当前位置:   article > 正文

Verilog & Matlab 联合仿真_matlab verilog

matlab verilog

一、概述

         在进行仿真时,有时候一部分参考模型(reference model)来自于Matlab,这就需要通过某种方法调用并运行Matlab的参考模型。verilog并不支持直接调用Matlab,但是可以通过DPI接口调用C函数,而Matlab又预留了为C开放的API接口,因此在SV中调用Matlab可以通过如下步骤来实现:

  1. Verilog通过DPI调用C,为C中某些变量赋值
  2. 从verilog中传递来的变量通过适当的类型转换传递到Matlab中
  3.  C通过API启动Matlab并调用Matlab中的函数/模型
  4. Matlab函数运算结果返回C,并通过适当的数据类型转换返回给verilog

二、verilog 与 C 通信

1、数据格式转换  

        C中并没有SV中的一些变量类型,例如bit,reg等,因此SV与C通信首先需要将SV中的数据类型转化为C可以识别的类型,部分典型变量类型的对应关系如下所示(这些类型都定义在svdpi.h头文件中):

SystemVerilogC(input)C(output)
bytecharchar*
intintint*
realdoubledouble*
reg[N:0]/logic[N:0]const svLogicVecVal*svLogicVecVal*
bit[N:0]const svBitVecVal*svBitVecVal*
open array[]svOpenArrayHandlesvOpenArrayHandles

2、C侧代码编写

         C侧除了要包含一些要用到的基础的头文件(例如stdio.h)以外,还需要包含上述提到的svdpi.h头文件:

    #include "svdpi.h"

        该头文件中定义了SV与C通信的类型转换,以及C对这些数据类型的操作方法。

        另外,C侧代码并不一定需要main函数,Verilog仅把C代码当成task或function调用。例如,在C侧编写函数如下:

  1. int factorial(int i)
  2. {
  3. if(i <= 1) return 1;
  4. else return(i * factorial(i - 1));
  5. }

3、Verilog侧代码编写

        若想在Verilog中使用编写的C函数,则需要在进行导入(注意导入函数的可见范围):

import "DPI-C" function int factorial(input int i);

        之后便可以在可见范围内的module等地方使用该函数了,例如:

  1. module test;
  2. int result;
  3. ......
  4. initial begin
  5. result = factorial(5);
  6. ......
  7. end
  8. endmodule

 三、C 与 Matlab 通信

1、C侧代码编写

1)启动Matlab引擎

        C可以通过Matlab引擎指针来启动Matlab引擎,该引擎由Matlab软件包含的engine.h提供:

#include "engine.h"

        由于C启动Matlab需要用到Matlab引擎指针,之后便可以使用engOpen函数获取引擎指针,例如:

  1. Engine *ep;
  2. if(!(ep = engOpen("\0")))
  3. printf("\nCan't start Matlab engine!\n");

        engOpen函数原型如下: 

  1. #include "engine.h"
  2. Engine *engOpen(const char *startcmd);

        其参数为启动指令字符串,若在Windows环境下,则启动指令必须为空,在Linux环境下,启动指令为空时在当前主机启动,若启动指令为主机名,则在指定主机上启动,若为其它Matlab指令字符串,则Matlab会在启动时执行该指令。该函数返回Engine指针,若启动失败则返回NULL。

2)定义数据类型

        此外,Matlab中数据以矩阵的形式存储,因此还要包含定义矩阵类型以及操作方法的头文件matrix.h:

#include "matrix.h"

        Matlab中以矩阵形式存储数据,将C中的变量传递给Matlab(或反过来)时,需要定义Matlab能够识别的矩阵形式的变量,即mxArray类型,通常定义mxArray类型变量为指针变量,例如:

mxArray *mxarr_ptr = NULL;

        在定义完mxArray类型指针变量之后,可能还需要指定其大小和类型,需要用到mxCreateDoubleMatrix函数,该函数原型如下:

  1. #include "matrix.h"
  2. mxArray *mxCreateDoubleMatrix(mwSize m, mwSize n, mxComplexity ComplexFlag);

        其中,第一个参数和第二个参数代表创建m行n列的矩阵空间,第三个参数指定矩阵为实矩阵(mxREAL)还是复矩阵(mxCOMPLEX)。

3)C向Matlab传输数据

        结合前一节可以知道,若想向Matlab传递数据,首先应将C中普通类型的变量赋值到mxArray类型的变量中,此时需要用到mxSetPr函数,该函数原型如下:

  1. #include "matrix.h"
  2. void mxSetPr(mxArray *pm, double *pr);

        其中,第一个参数为mxArray类型的指针,第二个参数为double类型的指针。(在Matlab2018a版本以后,mxSetPr函数不再被建议使用,而应该使用mxSetDoubles函数)

        此外,当double类型指针未被分配空间时,则首先需要使用mxCalloc函数对其动态分配内存空间(注意不能使用calloc或malloc函数),其原型如下:

  1. #include "matrix.h"
  2. #include <stdlib.h>
  3. void *mxCalloc(mwSize n, mwSize size);

        其中,第一个参数为分配内存的单元数量,第二个参数为每个单元的大小(通常搭配sizeof函数使用),如果成功,函数返回动态内存的起始位置,否则返回NULL。

        接下来就需要将mxArray类型的变量传递至Matlab中,此时需要用到engPutVariable函数,该函数原型如下:

  1. #include "engine.h"
  2. int engPutVariable(Engine *ep, const char *name, const mxArray *pm);

        其中,第一个参数为引擎指针,第二个参数为Matlab中变量的名字,第三个参数为mxArray类型变量的指针,当操作成功时返回1,否则返回0

  需要注意的是,如果指定的名字在Matlab中不存在,则会在Matlab中创建该名字的变量并为其赋值,如果该名字已存在,则会将原变量替换为新变量,此外,传递的变量最大为2GB。

4)C调用Matlab程序

        Matlab提供了从C传递指令的engEvalString函数,其原型如下所示:

  1. #include "engine.h"
  2. int engEvalString(Engine *ep, const char *string);

        其中,第一个参数为引擎指针,第二个参数为指令字符串,若引擎关闭或指针为空则返回1,否则返回0,即使Matlab并不能识别该指令。

        通过该函数,我们便可以像在Matlab console中执行指令一样调用Matlab函数(.m文件),但需要注意的是,在调用函数或模型前,首先需要将Matlab的工作路径设置正确,例如:

  1. //--假设在/sv_matlab/demo/目录下存放有func_add.m文件,用于将两个数相加
  2. engEvalString("path('/sv_matlab/demo/',path);");
  3. //--在C中直接调用上述函数将两数相加,前提是Matlab中已经有a,b两个变量
  4. engEvalString("func_add(a,b)");

5)C从Matlab获取数据

        我们需要先从Matlab获取变量至C中的mxArray类型变量中,之后再将mxArray类型变量中的数据提取到C的普通类型变量中。首先,我们需要使用engGetVariable函数获取mxArray变量,该函数原型如下:

  1. #include "engine.h"
  2. mxArray *engGetVariable(Engine *ep, const char *name);

        其中,第一个参数为引擎指针,第二个参数为Matlab中变量名字的字符串,当指定的名称字符串不存在时,函数返回NULL,否则返回指向该变量的mxArray指针。

        之后,我们需要使用mxGetPr函数从mxArray类型变量中获取值,该函数原型如下:

  1. #include "matrix.h"
  2. mxDouble *mxGetPr(const mxArray *pm);

        其参数为mxArray类型变量的指针,如果指定的mxArray类型变量的指针为NULL,则返回函数NULL,否则返回指向mxArray中存储数据的指针。

6)释放内存

        在C和Matlab处理完相应的数据之后,可能需要为一些变量之前分配的空间释放其内存以防止内存泄漏。

        使用mxFree函数释放由mxCalloc分配的内存,其函数原型如下:

  1. #include "matrix.h"
  2. void mxFree(void *ptr);

        使用mxDestroyArray函数释放由mxCreateDoubleMatrix分配的内存,其函数原型如下:

  1. #include "matrix.h"
  2. void mxDestroyArray(mxArray *pm);

7)关闭Matlab引擎

         在处理完所有数据并释放完成内存后,可以使用前面提到的engEvalString函数来关闭Matlab引擎,例如:

engEvalString(ep,"close");

2、Matlab侧代码编写

        对于Verilog调用Simulink Model进行联合仿真的情况,其中一个方法就是,可以将Matlab中所有的变量,无论是输入还是输出,均打包成一个函数包,这样我们就可以通过engEvalString函数来调用整个模型。

四、Verilog & Matlab交互示例

        我们以从verilog中向Matlab中传递两个变量并将两个变量在Matlab中相加后返回给Verilog为例,再梳理一边该流程。首先是Verilog侧代码(这里用的是SV):

  1. `timescale 1ns/1ps
  2. import "DPI-C" function real func_add(const real a, const real b);
  3. module test;
  4. real val1;
  5. real val2;
  6. real result;
  7. initial begin
  8. val1 = 0.3;
  9. val2 = 1.1;
  10. result = func_add(val1, val2); //调用C函数
  11. $display("SV got result = %0f", result);
  12. #10;
  13. $finish();
  14. end
  15. endmodule

        然后是C侧代码:

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include "svdpi.h"
  4. #include "engine.h"
  5. #include "matrix.h"
  6. double func_add(double a, double b)
  7. {
  8. double *val1;
  9. double *val2;
  10. double *result;
  11. val1 = (double *)mxCalloc(1, sizeof(double));
  12. val2 = (double *)mxCalloc(1, sizeof(double));
  13. *val1 = a;
  14. *val2 = b;
  15. mxArray = *mxarr_val1 = mxCreateDoubleMatrix(1, 1, mxREAL);
  16. mxArray = *mxarr_val2 = mxCreateDoubleMatrix(1, 1, mxREAL);
  17. mxArray = *mxarr_result = NULL;
  18. Engine *ep;
  19. if(!(ep = engOpen("\0")))
  20. printf("\nCan't start Matlab engine!\n");
  21. else{
  22. engEvalString("path('/sv_matlab/demo/',path);");
  23. mxSetPr(mxarr_val1, val1);
  24. mxSetPr(mxarr_val2, val2);
  25. engPutVariable(ep, "a", mxarr_val1);
  26. engPutVariable(ep, "b", mxarr_val2);
  27. engEvalString(ep, "result = func_demo(a,b)");
  28. mxarr_result = engGetVariable(ep, "result");
  29. result = mxGetPr(mxarr_result);
  30. return *result;
  31. }
  32. }

        最后是Matlab侧代码:

  1. function result = func_demo(a, b)
  2. result = a + b;
  3. return

因为涉及到调用C函数,因此在跑仿真之前需要先对C文件进行编译,例在/sv_matlab/demo/c目录下有自己编写的C函数,则需要按如下方式对其编译:

  1. gcc demo/c/func_add.c -o demo/c/libdpi.so \
  2. -I /appl/tools/cadence/XCELIUM2009/tools.lnx86/inca/include/ \
  3. -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函数。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/668956
推荐阅读
相关标签
  

闽ICP备14008679号