rockeric.com
在 SV及UVM接口應用篇之四:Matlab及Simulink模型與UVM的混合仿真 一文中,我們談到的是利用Matlab提供的C函數接口來在後臺啟動Matlab的引擎繼而直接執行M算法模型函數。在執行過程中,M模型函數傳遞也可以通過C一側映射的數據類型,由C一側傳入以及獲取最終的數據運算結果。
實際上,這種運行方式也可以變得複雜起來,例如由C一側調用Matlab提供的系統函數接口,不再只是單純地啟動Matlab引擎,還可以將部分或者全部以往由M實現的函數處理功能轉而由C函數去實現。這種更深入的交互方式可以在Matlab官網給的實例中獲得詳盡內容,也有不少的Matlab教材在講與C互動的部分中有講解。
https://ww2.mathworks.cn/help/
https://ww2.mathworks.cn/help/matlab/calling-matlab-engine-from-c-programs-1.html
只是在實際中,我們發現,對於Matlab算法模型的驗證對於Matlab工具的依賴性並沒有那麼多。意思是說,往往這些模型在Matlab環境開發的時候就已經穩定了,而將它們代入到C環境繼而到UVM環境中時,我們不再需要做額外的只有Matlab才能提供的數據處理。簡而言之就是,我們希望通過保持算法功能的條件下,讓Matlab算法模型先轉化為C模型,繼而在UVM中通過DPI-C直接對C模型進行調用。調用的方式也不會有什麼變化,即給轉化以後的C模型傳入大量的數據,接下來交由C模型進行運算,最後再將C模型的運算結果交給UVM,作為參考數據,用以後來與RTL模型輸出數據的比對。
選擇這樣處理M模型有一些明顯的優勢:
通過Matlab提供的轉換方式,可以將M函數「無損」地轉換為C函數。
這種轉換方式使得接下來在調用C模型時,不再需要像之前一樣在後臺啟動Matlab引擎,可以間接降低仿真對於聯合仿真的多語言之間的同步要求和性能損耗(Matlab引擎在後臺運行消耗更多資源)。
將原本需要的UVM-C-Matlab的語言同步接口問題簡化為UVM-C的典型DPI-C問題,更加便於調試。
接下來,我們就這一種方法給出一個示例,希望對於那些遇到了類似Matlab算法模型驗證問題的同學可以提供一些參考。關於本文的示例代碼,大家也可以通過【閱讀原文】,在路科的官網下載這個小例程。
我們這裡提供了一個對數組進行簡單處理的M函數mtfun。
function tch_out = mtfun (tch_in) %tch_in = [1;2;3]; fprintf('Matlab function mtfun:: tch_in data \n'); fprintf('%d \n', tch_in); tch_out = [tch_in;tch_in;]; fprintf('Matlab function mtfun:: tch_out data \n'); fprintf('%d \n', tch_out);end這個函數的功能即是增長作為輸入參數的數組,例如,如果tch_in是[1;2;3],那麼輸出結果即為[1;2;3;1;2;3]。用這個一個簡單的函數來模擬算法模型常常會對輸入的多個數組進行處理,接下來將運算結果也作為數組輸出的場景。按照本節處理M模型的方式,我們將按照例程中的指導文件README來展開M模型的嵌入流程。安裝Matlab,並且設置有關環境變量,即$LD_LIBRARY_PATH和$PATH。setenv LD_LIBRARY_PATH /opt/MATLAB/R2016a/bin/glnxa64:/opt/MATLAB/R2016a/sys/os/glnxa64:$LD_LIBRARY_PATHset path=(/opt/MATLAB/R2016a/bin $path)alias matlab "/opt/MATLAB/R2016a/bin/matlab"第2步
在轉換M模型之前,需要啟動必要的Matlab應用。
第3步
利用mcc編譯器,將M模型編譯轉換為C的動態庫。在這一步,上面的M模型mtfun函數即被轉換為C模型,該模型被編譯到動態庫libmtfun.so中。
mcc -W lib:libmtfun -T link:lib mtfun.m
第4步
可以在C一側的函數中調用libmtfun中的mtfun函數,這一步是C庫之間的調用,已經與Matlab引擎脫離關係。這裡我們省略了UVM通過DPI-C接口調用C函數,而主要是在說明,一旦將M模型轉換為C動態庫,即可實現C域中的函數調用和結果返回。
這裡給出了C的「盒子」(wrapper)函數,用來演示,如果調用在libmtfun庫中的函數mtfun,並且傳入數組,同時得到返回的數組。這一簡單示例即能夠在接下來被嵌入到UVM環境時,進一步與UVM環境完成數組的傳遞,最終實現將M模型轉換為C模型,最後給套上「盒子」,嵌入到UVM環境中用來做參考模型。
#include <stdlib.h>#include <stdio.h>#include <string.h>#include "mex.h"#include "libmtfun.h"
int mtcall(int *cin, int in_size, int **cout, int *out_size) { int i; mxArray *min, *mout;、
if( ! mclInitializeApplication(NULL, 0) ) { printf("Could not initialized application. \n"); return 1; } if( ! libmtfunInitialize() ) { printf("Could not initialized the library. \n"); return 1; }
min = mxCreateNumericMatrix(in_size, 1, mxINT32_CLASS, mxREAL); memcpy((char*)mxGetPr(min), (char*)cin, sizeof(int)*in_size);
printf("mtlab function call started \n"); mlfMtfun(1, &mout, min); printf("mtlab function call finished \n");
printf("mtlab mxarray copy started \n"); *out_size = mxGetM(mout) * mxGetN(mout); printf("out_size = %d \n", *out_size); *cout = (int*) malloc(sizeof(int)* (*out_size)); memcpy((char*)(*cout), (char*)mxGetPr(mout), sizeof(int)* (*out_size)); printf("mtlab mxarray copy finished \n");
mxDestroyArray(min); mxDestroyArray(mout);
libmtfunTerminate(); mclTerminateApplication();}
int main() { int *cin, *cout; int in_size = 5, out_size, i;
cin = (int*) malloc(sizeof(int)*in_size); for(i=0; i<in_size; i++) *(cin+i) = 2*i;
mtcall(cin, in_size, &cout, &out_size);
printf("mtfun cout result: \n"); for(i=0; i<out_size; i++) { printf("cout[%d] is %d \n", i, *(cout+i)); }}在C盒子函數mtcall中的實現中,這裡不再對Matlab提供的C編程接口做詳細解釋,同學們可以在給出的Matlab技術支持網頁中找到所有詳細的函數和對應的功能。從應用角度出發,路桑這裡給出一些在實現過程中要注意的細節(也是可能掉進的坑):
mclInitializeApplication和libmtfunInitialize這兩個函數調用很重要,沒有它們,就無法順利從libmtfun中調用mtfun函數。在調用mtfun傳遞數組前,需要使用Matlab提供的創建數組的方式mxCreateNumericMatrix,只有這樣,數據形式才能夠對應,在C數組和Matlab數組的數據映射處理中,我們分別採用了mxCreateNumericMatrix,memcpy,和malloc。在調用了mlfMtfun(M模型的mtfun函數轉換後的C庫中的函數名,在libmtfun.h中聲明),還需要最後調用mxDestroyArray來釋放min和mout所指向的、不再使用的Matlab數組空間。與啟動函數對應的是調用兩個終結函數mclTerminateApplication和libmtfunTerminate,用來結束盒子函數mtcall與mlfMtfun函數之間的數據交換,繼而最終釋放整個的mltMtfun函數空間。調用這兩個函數可以確保盒子函數mtcall在調用之後可以正常退出。接下來,我們通過在C一側的main函數來模擬UVM通過DPI-C接口來調用盒子函數mtcall,即傳遞一些數組和控制變量,接下來通過mlfMtfun的運算,最後將數據結果通過盒子函數傳出。
所以第4步需要完成C函數(mtcall,main)與M轉換得來的動態庫libmtfun.so之間的連結和調用。在該命令行中需要注意的是,有不少需要加載的預定義的Matlab系統函數庫,例如-leng -lmx -lmex。gcc -m64 -I/opt/MATLAB/R2016a/extern/include -L/opt/MATLAB/R2016a/bin/glnxa64 -L. -lmtfun -leng -lmx -lmex mtcall.c -o mtcall為了能夠使得在C函數運行時找到libmtfun.so動態庫,需要更新環境變量$LD_LIBRARY_PATH。setenv LD_LIBRARY_PATH ./:$LD_LIBRARY_PATHmain函數在調用盒子函數mtcall的過程中,傳遞了C數組。mtcall在調用mlfMtfun時轉換了對應的Matlab類型數組並且傳遞,在獲得了新的Matlab類型數組指針以後,又將內容複製到了新的C數組。
從仿真列印信息中我們可以得到:mtlab function call started Matlab function mtfun:: tch_in data 0 2 4 6 8 Matlab function mtfun:: tch_out data 0 2 4 6 8 0 2 4 6 8 mtlab function call finished mtlab mxarray copy started out_size = 10 mtlab mxarray copy finished mtfun cout result: cout[0] is 0 cout[1] is 2 cout[2] is 4 cout[3] is 6 cout[4] is 8 cout[5] is 0 cout[6] is 2 cout[7] is 4 cout[8] is 6 cout[9] is 8在實際項目中,當我們一開始嘗試繞過傳統的在後臺啟動Matlab引擎方式的時候,我們服務的客戶對此還有過猶豫,他們擔心轉換後的內容可能有功能偏差,同時也對於大型的Matlab模型在轉換後的C函數執行效率低,可能會影響動態仿真時的性能。不過在最後採用了這種新方案以後,仿真性能並沒有出現明顯的下降,且由於仿真過程中可能會頻繁地調用M模型轉換後的函數,這種語言接口本身經過優化的DPI-C調用方式也要優於在UVM-C-Matlab之間的數據間接同步方式。考慮到目前做算法驗證的場景越來越多,希望這一篇Matlab算法模型嵌入UVM的應用能夠像立秋後的雨一樣,帶給夏日一些清涼。SV及UVM接口應用篇之一:DPI接口和C測試(上)
SV及UVM接口應用篇之二:DPI接口和C測試(下)
SV及UVM接口應用篇之三:SystemC與UVM的TLM通信
SV及UVM接口應用篇之四:Matlab及Simulink模型與UVM的混合仿真
SV及UVM接口應用篇之五(終):腳本語言與UVM的交互
SV與UVM接口應用篇之六:開闢後臺C服務線程