赞
踩
在进行仿真时,有时候一部分参考模型(reference model)来自于Matlab,这就需要通过某种方法调用并运行Matlab的参考模型。verilog并不支持直接调用Matlab,但是可以通过DPI接口调用C函数,而Matlab又预留了为C开放的API接口,因此在SV中调用Matlab可以通过如下步骤来实现:
C中并没有SV中的一些变量类型,例如bit,reg等,因此SV与C通信首先需要将SV中的数据类型转化为C可以识别的类型,部分典型变量类型的对应关系如下所示(这些类型都定义在svdpi.h头文件中):
SystemVerilog | C(input) | C(output) |
---|---|---|
byte | char | char* |
int | int | int* |
real | double | double* |
reg[N:0]/logic[N:0] | const svLogicVecVal* | svLogicVecVal* |
bit[N:0] | const svBitVecVal* | svBitVecVal* |
open array[] | svOpenArrayHandle | svOpenArrayHandles |
C侧除了要包含一些要用到的基础的头文件(例如stdio.h)以外,还需要包含上述提到的svdpi.h头文件:
#include "svdpi.h"
该头文件中定义了SV与C通信的类型转换,以及C对这些数据类型的操作方法。
另外,C侧代码并不一定需要main函数,Verilog仅把C代码当成task或function调用。例如,在C侧编写函数如下:
- int factorial(int i)
- {
- if(i <= 1) return 1;
- else return(i * factorial(i - 1));
- }
若想在Verilog中使用编写的C函数,则需要在进行导入(注意导入函数的可见范围):
import "DPI-C" function int factorial(input int i);
之后便可以在可见范围内的module等地方使用该函数了,例如:
- module test;
- int result;
- ......
- initial begin
- result = factorial(5);
- ......
- end
- endmodule
C可以通过Matlab引擎指针来启动Matlab引擎,该引擎由Matlab软件包含的engine.h提供:
#include "engine.h"
由于C启动Matlab需要用到Matlab引擎指针,之后便可以使用engOpen函数获取引擎指针,例如:
- Engine *ep;
- if(!(ep = engOpen("\0")))
- printf("\nCan't start Matlab engine!\n");
engOpen函数原型如下:
- #include "engine.h"
- Engine *engOpen(const char *startcmd);
其参数为启动指令字符串,若在Windows环境下,则启动指令必须为空,在Linux环境下,启动指令为空时在当前主机启动,若启动指令为主机名,则在指定主机上启动,若为其它Matlab指令字符串,则Matlab会在启动时执行该指令。该函数返回Engine指针,若启动失败则返回NULL。
此外,Matlab中数据以矩阵的形式存储,因此还要包含定义矩阵类型以及操作方法的头文件matrix.h:
#include "matrix.h"
Matlab中以矩阵形式存储数据,将C中的变量传递给Matlab(或反过来)时,需要定义Matlab能够识别的矩阵形式的变量,即mxArray类型,通常定义mxArray类型变量为指针变量,例如:
mxArray *mxarr_ptr = NULL;
在定义完mxArray类型指针变量之后,可能还需要指定其大小和类型,需要用到mxCreateDoubleMatrix函数,该函数原型如下:
- #include "matrix.h"
- mxArray *mxCreateDoubleMatrix(mwSize m, mwSize n, mxComplexity ComplexFlag);
其中,第一个参数和第二个参数代表创建m行n列的矩阵空间,第三个参数指定矩阵为实矩阵(mxREAL)还是复矩阵(mxCOMPLEX)。
结合前一节可以知道,若想向Matlab传递数据,首先应将C中普通类型的变量赋值到mxArray类型的变量中,此时需要用到mxSetPr函数,该函数原型如下:
- #include "matrix.h"
- void mxSetPr(mxArray *pm, double *pr);
其中,第一个参数为mxArray类型的指针,第二个参数为double类型的指针。(在Matlab2018a版本以后,mxSetPr函数不再被建议使用,而应该使用mxSetDoubles函数)
此外,当double类型指针未被分配空间时,则首先需要使用mxCalloc函数对其动态分配内存空间(注意不能使用calloc或malloc函数),其原型如下:
- #include "matrix.h"
- #include <stdlib.h>
- void *mxCalloc(mwSize n, mwSize size);
其中,第一个参数为分配内存的单元数量,第二个参数为每个单元的大小(通常搭配sizeof函数使用),如果成功,函数返回动态内存的起始位置,否则返回NULL。
接下来就需要将mxArray类型的变量传递至Matlab中,此时需要用到engPutVariable函数,该函数原型如下:
- #include "engine.h"
- int engPutVariable(Engine *ep, const char *name, const mxArray *pm);
其中,第一个参数为引擎指针,第二个参数为Matlab中变量的名字,第三个参数为mxArray类型变量的指针,当操作成功时返回1,否则返回0
需要注意的是,如果指定的名字在Matlab中不存在,则会在Matlab中创建该名字的变量并为其赋值,如果该名字已存在,则会将原变量替换为新变量,此外,传递的变量最大为2GB。
Matlab提供了从C传递指令的engEvalString函数,其原型如下所示:
- #include "engine.h"
- int engEvalString(Engine *ep, const char *string);
其中,第一个参数为引擎指针,第二个参数为指令字符串,若引擎关闭或指针为空则返回1,否则返回0,即使Matlab并不能识别该指令。
通过该函数,我们便可以像在Matlab console中执行指令一样调用Matlab函数(.m文件),但需要注意的是,在调用函数或模型前,首先需要将Matlab的工作路径设置正确,例如:
- //--假设在/sv_matlab/demo/目录下存放有func_add.m文件,用于将两个数相加
- engEvalString("path('/sv_matlab/demo/',path);");
- //--在C中直接调用上述函数将两数相加,前提是Matlab中已经有a,b两个变量
- engEvalString("func_add(a,b)");
我们需要先从Matlab获取变量至C中的mxArray类型变量中,之后再将mxArray类型变量中的数据提取到C的普通类型变量中。首先,我们需要使用engGetVariable函数获取mxArray变量,该函数原型如下:
- #include "engine.h"
- mxArray *engGetVariable(Engine *ep, const char *name);
其中,第一个参数为引擎指针,第二个参数为Matlab中变量名字的字符串,当指定的名称字符串不存在时,函数返回NULL,否则返回指向该变量的mxArray指针。
之后,我们需要使用mxGetPr函数从mxArray类型变量中获取值,该函数原型如下:
- #include "matrix.h"
- mxDouble *mxGetPr(const mxArray *pm);
其参数为mxArray类型变量的指针,如果指定的mxArray类型变量的指针为NULL,则返回函数NULL,否则返回指向mxArray中存储数据的指针。
在C和Matlab处理完相应的数据之后,可能需要为一些变量之前分配的空间释放其内存以防止内存泄漏。
使用mxFree函数释放由mxCalloc分配的内存,其函数原型如下:
- #include "matrix.h"
- void mxFree(void *ptr);
使用mxDestroyArray函数释放由mxCreateDoubleMatrix分配的内存,其函数原型如下:
- #include "matrix.h"
- void mxDestroyArray(mxArray *pm);
在处理完所有数据并释放完成内存后,可以使用前面提到的engEvalString函数来关闭Matlab引擎,例如:
engEvalString(ep,"close");
对于Verilog调用Simulink Model进行联合仿真的情况,其中一个方法就是,可以将Matlab中所有的变量,无论是输入还是输出,均打包成一个函数包,这样我们就可以通过engEvalString函数来调用整个模型。
我们以从verilog中向Matlab中传递两个变量并将两个变量在Matlab中相加后返回给Verilog为例,再梳理一边该流程。首先是Verilog侧代码(这里用的是SV):
- `timescale 1ns/1ps
- import "DPI-C" function real func_add(const real a, const real b);
- module test;
- real val1;
- real val2;
- real result;
- initial begin
- val1 = 0.3;
- val2 = 1.1;
- result = func_add(val1, val2); //调用C函数
- $display("SV got result = %0f", result);
- #10;
- $finish();
- end
- endmodule
然后是C侧代码:
- #include <stdlib.h>
- #include <stdio.h>
- #include "svdpi.h"
- #include "engine.h"
- #include "matrix.h"
- double func_add(double a, double b)
- {
- double *val1;
- double *val2;
- double *result;
-
- val1 = (double *)mxCalloc(1, sizeof(double));
- val2 = (double *)mxCalloc(1, sizeof(double));
- *val1 = a;
- *val2 = b;
- mxArray = *mxarr_val1 = mxCreateDoubleMatrix(1, 1, mxREAL);
- mxArray = *mxarr_val2 = mxCreateDoubleMatrix(1, 1, mxREAL);
- mxArray = *mxarr_result = NULL;
- Engine *ep;
-
- if(!(ep = engOpen("\0")))
- printf("\nCan't start Matlab engine!\n");
- else{
- engEvalString("path('/sv_matlab/demo/',path);");
- mxSetPr(mxarr_val1, val1);
- mxSetPr(mxarr_val2, val2);
- engPutVariable(ep, "a", mxarr_val1);
- engPutVariable(ep, "b", mxarr_val2);
- engEvalString(ep, "result = func_demo(a,b)");
- mxarr_result = engGetVariable(ep, "result");
- result = mxGetPr(mxarr_result);
- return *result;
- }
- }
最后是Matlab侧代码:
- function result = func_demo(a, b)
- result = a + b;
- return
因为涉及到调用C函数,因此在跑仿真之前需要先对C文件进行编译,例在/sv_matlab/demo/c目录下有自己编写的C函数,则需要按如下方式对其编译:
- gcc demo/c/func_add.c -o demo/c/libdpi.so \
- -I /appl/tools/cadence/XCELIUM2009/tools.lnx86/inca/include/ \
- -std=c99 -fPIC -shared -leng -lmx -lmex
其中参数:
-o 指定生成的结果文件,结果文件名为libdpi.so,SV将默认查找这个库内的C函数
-I 指定编译包含的头文件库(这里要用到包含svdpi.h的路径),由仿真工具提供,这里以XCELIUM为例
-fPIC 告诉编译器产生与位置无关代码
-shared 表示生成库能被别的程序链接
-leng -lmx 和 -lmex 编译matlab库所需的参数,主要用于编译和链接
在编译完成之后,便可以启动SV的仿真了,如果C函数没有发生变化,则重复SV侧仿真无需再次编译C函数。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。