| ||
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
第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); // create same size array as C side memcpy((char*)mxGetPr(min), (char*)cin, sizeof(int)*in_size); // copy C array to Matlab array printf("mtlab function call started \n"); mlfMtfun(1, &mout, min); // Call matlab function 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)); // allocate M*N sized int array memcpy((char*)(*cout), (char*)mxGetPr(mout), sizeof(int)* (*out_size)); // copy Matlab array to C array 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; // input C array initialization mtcall(cin, in_size, &cout, &out_size); // call C wrapper function printf("mtfun cout result: \n"); for(i=0; i<out_size; i++) { // print C array result printf("cout[%d] is %d \n", i, *(cout+i)); }}在C盒子函数mtcall中的实现中,这里不再对Matlab提供的C编程接口做详细解释,同学们可以在给出的Matlab技术支持网页中找到所有详细的函数和对应的功能。从应用角度出发,路桑这里给出一些在实现过程中要注意的细节(也是可能掉进的坑):
gcc -m64 -I/opt/MATLAB/R2016a/extern/include -L/opt/MATLAB/R2016a/bin/glnxa64 -L. -lmtfun -leng -lmx -lmex mtcall.c -o mtcall
从仿真打印信息中我们可以得到:
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
SV及UVM接口应用篇之三:SystemC与UVM的TLM通信
SV及UVM接口应用篇之四:Matlab及Simulink模型与UVM的混合仿真