Verilog & Matlab 联合仿真

一、概述

         在进行仿真时,有时候一部分参考模型(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侧编写函数如下:

int factorial(int i)
{
    if(i <= 1) return 1;
    else return(i * factorial(i - 1));
}

3、Verilog侧代码编写

        若想在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 通信

1、C侧代码编写

1)启动Matlab引擎

        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。

2)定义数据类型

        此外,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)。

3)C向Matlab传输数据

        结合前一节可以知道,若想向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。

4)C调用Matlab程序

        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)");

5)C从Matlab获取数据

        我们需要先从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中存储数据的指针。

6)释放内存

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

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

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

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

#include "matrix.h"
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):

`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函数。

 

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
MATLABVerilog联合仿真是一种将MATLABVerilog两种仿真工具结合起来进行仿真的方法。MATLAB是一种高级的数学、信号处理和系统设计软件,而Verilog是一种硬件描述语言,常用于数字电路的设计和仿真。 在MATLABVerilog联合仿真中,可以利用MATLAB的高级算法和仿真工具进行系统级建模和分析,并将设计结果以Verilog的形式输出,进而进行数字电路级的仿真验证。 首先,将设计需求和规格在MATLAB中进行系统级建模。利用MATLAB提供的信号处理工具箱、控制系统工具箱等,可以方便地进行信号处理和系统级设计。在这个阶段,可以利用MATLAB进行算法验证、参数调整和性能评估等。 然后,将MATLAB中的设计结果转化为Verilog代码。MATLAB提供了一些工具和函数可以直接将MATLAB中的设计结果转化为Verilog相关的描述,例如HDL Coder。 最后,使用Verilog进行数字电路级的仿真验证Verilog是一种硬件描述语言,可以用于描述数字电路的结构和功能。通过将MATLAB中的设计结果转化为Verilog代码,并利用Verilog仿真工具,可以进行数字电路级的仿真验证,以验证设计的正确性和性能约束。 总的来说,MATLABVerilog联合仿真可以充分发挥两者的优势,实现从系统级设计到数字电路级仿真的全过程。这种联合仿真方法在数字电路设计和验证中具有很大的应用潜力,可以提高设计效率和准确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沧海一升

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值