multi-language verification (五)UVM-SV通过DPI或者SC-UVM_ML库调用Matlab

前言

本篇将演示一个UVM-SV通过C/CPP作为胶合语言,调用Matlab的示例;并介绍两种方式,一种DPI封装为函数的方式,一种使用UVM-ML OA库,基于TLM通信的方式。

调用Matlab

Matlab是一款用于数据分析,算法开发和模型创建的软件;其提供众多的工具箱及交互式的操作,方便算法的开发。当使用Matalb作为RTL的算法model时,如何调用Matalb作为参考模型,是验证人员不得不解决的一点。

总结如下几种方式:

  1. 直接启动运行matlab程序,通过读写文件完成model的输入输出
    输入数据从文件中读取,载入到matlab,运行完成后再写到输出文件中;验证平台的激励也来自输入文件,RTL输出的数据写入到输出文件;对比matlab和RTL的输出文件;

  2. C/C++调用Matlab引擎
    engine.h中提供函数engEvalString,使得C/C++可直接调用Matlab引擎,运行.m文件;这种方式需要完整的安装Matlab程序,获取Matlab license,相当于后台运行Matlab。🔗Call MATLAB Functions from C++

  3. .m文件编译为.so,利用runtime库实现.so的调用
    利用mcc(Matlab Compiler)将.m文件编译为.so文件,C/CPP通过调用对应API完成和.so的数据交互;因为这种方式只依赖于Matlab Runtime(MCR)共享库,有很好的移植性,运行程序不需要安装完整的Matlab。
    CPP调用的API又分为三种:

在这里插入图片描述
4. 将.m文件转成.c文件
Matlab的dpigen命令支持将.m文件直接转化成.c文件,并提供相关DPI调用函数;但是.m.c对matlab的编写有一定要求,转化后的.c可读性不到,debug困难。🔗DPI Generation for MATLAB Code

综合对比上述几种方式,建议采用第三种;本篇将通过第三种方式的mwArray API,调用Matlab作为参考模型。

本篇演示的Matlab算法是RGB转YCvCr色域,效果如下图:
在这里插入图片描述
RGB888_to_YCbCr444.m文件:

function [Y_o,Cb_o,Cr_o]=RGB888_to_YCbCr444(R_i,G_i,B_i,cfg_struct)

   %[height,width] = size(R_i);
   height = cfg_struct.height;
   width  = cfg_struct.width;


   img_o = zeros(height,width,3);
   img_i = double(cat(3,R_i,G_i,B_i));

   for i = 1 : height
      for j= 1 : width
         img_o(i,j,1) = bitshift(( img_i(i,j,1)*76 + img_i(i,j,2)*150 + img_i(i,j,3)*29),-8);
         img_o(i,j,2) = bitshift((-img_i(i,j,1)*43 - img_i(i,j,2)*84 + img_i(i,j,3)*128 + 32768),-8);
         img_o(i,j,3) = bitshift(( img_i(i,j,1)*128 - img_i(i,j,2)*107 - img_i(i,j,3)*20 + 32768),-8);
      end
   end
   Y_o  = img_o(:,:,1);
   Cb_o = img_o(:,:,2);
   Cr_o = img_o(:,:,3);
end

RTL代码来自🔗crazybingo_VIP_RGB888_YCbCr444.v

Matlab Compiler的mcc命令将.m文件转为.so

${MATLAB_HOME}/bin/mcc -W cpplib:libRGB888_to_YCbCr444 RGB888_to_YCbCr444.m  -g -d output/

生成内容如下:
在这里插入图片描述
libRGB888_to_YCbCr444.h中包含可供C/CPP调用的API。

UVM_DPI_Matlab

DPI调用libRGB888_to_YCbCr444.h中的API,需要利用🔗mwArray API进行数据结构的转化;mwArray对应的头文件mclmcrrt.h,mclcppclass.h,runtime共享库libmwmclmcrrt.so

编译动态库命令:

CUR_DIR_PATH := $(shell echo $(CURDIR))

all: matlab_gen gen_lib

matlab_gen:
	${MATLAB_HOME}/bin/mcc -W cpplib:libRGB888_to_YCbCr444 RGB888_to_YCbCr444.m  -g -d output/

gen_lib:
	g++ -shared -fPIC -g -Wno-write-strings \
	-Ioutput -I${VCS_HOME}/include -Isrc_cpp -I${MATLAB_HOME}/extern/include \
	-lmwmclmcrrt -L${MATLAB_HOME}/runtime/glnxa64 \
	-lRGB888_to_YCbCr444 -Loutput \
	-Wl,-rpath=${CUR_DIR_PATH} -Wl,-rpath=${CUR_DIR_PATH}/output \
	./src_cpp/RGB888_to_YCbCr444_common.cpp \
	./src_cpp/RGB888_to_YCbCr444_dpi.cpp \
	-o libRGB888_to_YCbCr444_sim.so

clean:
	-rm -rf output *.so

SV DPI调用Matalb的数据传递路径: SV -> DPI-C -> CPP -> mwArray API -> libRGB888_to_YCbCr444 API

ref_model中调用DPI,计算结果:

`ifndef MY_MODEL__SV
`define MY_MODEL__SV
import "DPI-C" function chandle DPI_RGB888_to_YCbCr444_initialize(input chandle existhandle);
import "DPI-C" function void DPI_RGB888_to_YCbCr444_terminate(input chandle existhandle);
import "DPI-C" function void DPI_RGB888_to_YCbCr444(input chandle existhandle,
                                                   output int unsigned Y_dat_o[],
                                                   output int unsigned Cb_dat_o[],
                                                   output int unsigned Cr_dat_o[],
                                                   input int unsigned R_dat_o[],
                                                   input int unsigned G_dat_o[],
                                                   input int unsigned B_dat_o[],
                                                   input my_ctrl_s my_ctrl_struct
                                                   );
class my_model extends uvm_component;
   
   uvm_blocking_get_port #(my_transaction)  port;
   uvm_analysis_port #(my_transaction)  ap;

   extern function new(string name, uvm_component parent);
   extern function void build_phase(uvm_phase phase);
   extern virtual  task main_phase(uvm_phase phase);

   `uvm_component_utils(my_model)
endclass 

function my_model::new(string name, uvm_component parent);
   super.new(name, parent);
endfunction 

function void my_model::build_phase(uvm_phase phase);
   super.build_phase(phase);
   port = new("port", this);
   ap = new("ap", this);
endfunction

task my_model::main_phase(uvm_phase phase);
   chandle dpi_ch;
   my_transaction tr;
   my_transaction new_tr;
   super.main_phase(phase);
   dpi_ch = DPI_RGB888_to_YCbCr444_initialize(null);
   while(1) begin
      port.get(tr);
      new_tr = new("new_tr");
      new_tr.copy(tr);
      #10ns;
      DPI_RGB888_to_YCbCr444(dpi_ch,
                             new_tr.dat1,                       
                             new_tr.dat2,                       
                             new_tr.dat3,                       
                             tr.dat1,
                             tr.dat2,
                             tr.dat3,
                             tr.my_ctrl_struct);
      `uvm_info("my_model", "call matlab gen new tr:", UVM_LOW)
      new_tr.print();
      ap.write(new_tr);
   end
endtask
`endif

示例的完整代码可参考:🔗ML_verify_share

UVM_ML_Matlab

本节使用的UVM_ML-OA库介绍参考🔗multi-language verification (四)SystemC Verification、SystemC-SystemVeilog混仿

UVM_ML-OA安装
  1. tar -zxvf UVM_ML-1.13.0.tar.gz ; cd UVM_ML-1.13.0/ml
  2. source compile_vcs_lib.csh 生成动态库
#!bin/tcsh -f
setenv UVM_ML_HOME /home/holden/systemc_work/lib/UVM_ML-1.13.0
setenv OSCI_INSTALL /home/holden/systemc_work/lib/systemc-2.3.3
setenv SYSTEMC_HOME $OSCI_INSTALL
setenv OSCI_SRC $OSCI_INSTALL
setenv UVM_ML_CC g++
setenv UVM_ML_LD g++
setenv UVM_ML_SVDPI_DIR $VCS_HOME/include
setenv GET_OSCI_VERSION_EXTRA_FLAGS ""
source ./install_vcs.csh --clean --64bit --no-specman

运行完成后,打印出如下log:

***********************************
*
* Setup 
* using compiler version 9.2
* - for 64 bit
* - with debug information
* - with cleaning
* - with OSCI UVM-SC framework and adapter for OSCI 2.3.3
* - without IES NCSC UVM-SC framework and adapter
* - without Specman UVM-E adapter
* completed successfully
*
* The information about the environment variables 
* vital for running UVM-ML 
* may be found in /home/holden/systemc_work/lib/UVM_ML-1.13.0/ml/setup_64.csh (CSH format),
* in  /home/holden/systemc_work/lib/UVM_ML-1.13.0/ml/setup_64.sh (Bourne shell format)
* and in /home/holden/systemc_work/lib/UVM_ML-1.13.0/ml/setup_64.mk (Makefile format)
***********************************

find查找确认生成以下动态库:

holden@localhost.localdomain:/home/holden/systemc_work/lib/UVM_ML-1.13.0/ml 
>find . -name "*.so"
./libs/backplane/9.2/64bit/libuvm_ml_bp.so
./libs/osci/2.3.3/9.2/64bit/libuvm_sc_fw_osci.so
./libs/osci/2.3.3/9.2/64bit/libuvm_sc_ml_osci.so
./libs/osci/2.3.3/9.2/64bit/libuvm_osci.so
./libs/uvm_sv/9.2/64bit/libuvm_sv_ml.so
  1. cd examples/features/tlm1/sc_sv/sv_with_sc_ref_model
  2. make all运行sv_with_sc_ref_modelexmaple,这是一个sv TLM1调用sc当作ref_model的demo。
UVM_ML_HOME = /home/holden/systemc_work/lib/UVM_ML-1.13.0
SYSTEMC_HOME =  /home/holden/systemc_work/lib/systemc-2.3.3
all: clean create compile run

create:
	g++ -g -fPIC -o liball_osci_vcs.64.so \
	${UVM_ML_HOME}/ml/examples/features/tlm1/sc_sv/sv_with_sc_ref_model/sctop.cpp \
	-D_GLIBCXX_USE_CXX11_ABI=1 \
	-Xlinker -rpath -Xlinker ${UVM_ML_HOME}/ml/libs/osci/2.3.3/9.2/64bit \
	-Xlinker -Bsymbolic -L${UVM_ML_HOME}/ml/libs/osci/2.3.3/9.2/64bit \
	-luvm_sc_fw_osci -luvm_sc_ml_osci \
	-shared -I${SYSTEMC_HOME}/include -I${UVM_ML_HOME}/ml/adapters/uvm_sc \
	-I${UVM_ML_HOME}/ml/adapters/uvm_sc/osci -I${UVM_ML_HOME}/ml/frameworks/uvm/sc \
	-I${UVM_ML_HOME}/ml/examples/features/tlm1/sc_sv/sv_with_sc_ref_model

compile:
	vcs -o simv64 -top topmodule \
	-debug_access+all -kdb -lca -sverilog -timescale=1ns/1ns \
	-l comp.log -full64 \
	+define+USE_UVM_ML_RUN_TEST \
	-CFLAGS -DVCS \
	${UVM_ML_HOME}/ml/frameworks/uvm/sv/1.2-ml/src/dpi/uvm_dpi.cc \
	${UVM_ML_HOME}/ml/frameworks/uvm/sv/1.2-ml/src/uvm_pkg.sv \
	+incdir+${UVM_ML_HOME}/ml/frameworks/uvm/sv/1.2-ml/src \
	+incdir+${UVM_ML_HOME}/ml/frameworks/uvm/sv/1.2-ml/src/dpi \
	+incdir+${UVM_ML_HOME}/ml/examples/features/tlm1/sc_sv/sv_with_sc_ref_model \
	+incdir+${UVM_ML_HOME}/ml/adapters/uvm_sv \
	${UVM_ML_HOME}/ml/adapters/uvm_sv/uvm_ml_adapter.sv \
	${UVM_ML_HOME}/ml/examples/features/tlm1/sc_sv/sv_with_sc_ref_model/test.sv

run:
	./simv64 -l run.log \
	-sv_liblist ./bootstrap_file

clean:
	-rm -rf simv* csrc *.log *.so
RGB888_to_YCbCr444 UVM_ML-OA示例

SC侧的transaction:
do_pack(), do_unpack()函数分别对bit流打包、解包

//my_transaction.h
#ifndef MY_TRANSACTION_H
#define MY_TRANSACTION_H
#include <systemc.h>
#include <uvm.h>
using namespace uvm;
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using std::string;
using std::vector;
#include "RGB888_to_YCbCr444_common.h"

#define TIME_STRING sc_time_stamp().to_string().c_str()

class my_transaction : public uvm_object {
public:
   UVM_OBJECT_UTILS(my_transaction)
   cfg_struct cfg_s;
   std::vector<unsigned int> dat1;
   std::vector<unsigned int> dat2;
   std::vector<unsigned int> dat3;

// public methods defined for UVM
public:
   my_transaction() {}
   virtual ~my_transaction() {}
   virtual void do_print(std::ostream&) const;
   virtual void do_pack(uvm::uvm_packer&) const;
   virtual void do_unpack(uvm::uvm_packer&);
   virtual void do_copy(const uvm::uvm_object*);
   virtual bool do_compare(const uvm::uvm_object*) const;
}; // end of class my_transaction

inline bool operator == (const my_transaction & arg1, const my_transaction & arg2) {
   return arg1.do_compare(&arg2);
}

inline bool operator != (const my_transaction & arg1, const my_transaction & arg2) {
   return !(arg1 == arg2);
}

UVM_OBJECT_REGISTER(my_transaction)

#endif

//my_transaction.cpp
#include "my_transaction.h"
#include <iostream>
#include <algorithm>

void my_transaction::do_print(std::ostream& os) const {
   os << "{ ";
   os << "cfg_s.height = ";
   os << cfg_s.height << " ";
   os << "cfg_s.width = ";
   os << cfg_s.width << " ";
   os << " }";
   os << endl;
}

void my_transaction::do_pack(uvm::uvm_packer& packer) const {
   packer << cfg_s.height;
   packer << cfg_s.width;
   packer << dat1;
   packer << dat2;
   packer << dat3;
}

void my_transaction::do_unpack(uvm::uvm_packer& packer) {
   packer >> cfg_s.height;
   packer >> cfg_s.width;
   packer >> dat1;
   packer >> dat2;
   packer >> dat3;
}

void my_transaction::do_copy(const uvm::uvm_object* rhs) {
   const my_transaction* drhs = dynamic_cast<const my_transaction*>(rhs);
   if (!drhs) { cerr << "ERROR in do_copy" ; return ; }
   this->cfg_s.height = drhs->cfg_s.height;
   this->cfg_s.width = drhs->cfg_s.width;
   this->dat1 = drhs->dat1;
   this->dat2 = drhs->dat2;
   this->dat3 = drhs->dat3;
}

bool my_transaction::do_compare(const uvm::uvm_object* rhs) const {
   const my_transaction* drhs = dynamic_cast<const my_transaction*>(rhs);
   if (!drhs) { cerr << "ERROR in do_compare" ; return false ; }
   if (this->cfg_s.height != drhs->cfg_s.height) return false;
   if (this->cfg_s.width != drhs->cfg_s.width) return false;
   if (this->dat1 != drhs->dat1) return false;
   if (this->dat2 != drhs->dat2) return false;
   if (this->dat3 != drhs->dat3) return false;
   return true;
}

SV侧的transaction:
pack(),unpack()函数来自uvm_object_utils_begin宏注册的函数;

//my_transaction.sv
`ifndef MY_TRANSACTION__SV
`define MY_TRANSACTION__SV

typedef struct {
   rand int unsigned frame_height;
   rand int unsigned frame_width;
} my_ctrl_s;

class my_transaction extends uvm_sequence_item;

   rand int unsigned dat1[];
   rand int unsigned dat2[];
   rand int unsigned dat3[];
   rand my_ctrl_s my_ctrl_struct;

   constraint my_transaction_c {
      my_ctrl_struct.frame_width inside {[200:300]};
      my_ctrl_struct.frame_height inside {[100:200]};
      dat1.size == my_ctrl_struct.frame_width*my_ctrl_struct.frame_height;
      dat2.size == my_ctrl_struct.frame_width*my_ctrl_struct.frame_height;
      dat3.size == my_ctrl_struct.frame_width*my_ctrl_struct.frame_height;
      foreach(dat1[i]){
         dat1[i] inside {[0:8'hFF]};
         dat2[i] inside {[0:8'hFF]};
         dat3[i] inside {[0:8'hFF]};
      }
   }

   `uvm_object_utils_begin(my_transaction)
      `uvm_field_int (my_ctrl_struct.frame_height,UVM_ALL_ON | UVM_NOCOMPARE)
      `uvm_field_int (my_ctrl_struct.frame_width,UVM_ALL_ON | UVM_NOCOMPARE)
      `uvm_field_array_int (dat1,UVM_ALL_ON)
      `uvm_field_array_int (dat2,UVM_ALL_ON)
      `uvm_field_array_int (dat3,UVM_ALL_ON)
   `uvm_object_utils_end

   function new(string name = "my_transaction");
      super.new();
   endfunction

endclass
`endif

ref_model.cpp中调用libRGB888_to_YCbCr444 API:

class ref_model : public uvm_component,
                  tlm_analysis_port<my_transaction>
{
public:
   sc_port<tlm_analysis_if<my_transaction>, 2, SC_ONE_OR_MORE_BOUND> sc_ap;
   sc_export<tlm_analysis_if<my_transaction> > sc_imp;

   ref_model(sc_module_name nm) : uvm_component(nm)
                                  , sc_ap("sc_ap")
                                  , sc_imp("sc_imp")
   {
      sc_imp(*this);
   }

   UVM_COMPONENT_UTILS(ref_model)

   void build() {
      io_printf("[SC] %s: ref_model::build\n",TIME_STRING);
   }

   void write(const my_transaction& tr) {
      my_transaction out_tr;
      unsigned int frm_size;
      unsigned int *dat1_h;
      unsigned int *dat2_h;
      unsigned int *dat3_h;
      
      frm_size = tr.cfg_s.height*tr.cfg_s.width;
      dat1_h = (unsigned int*)malloc(sizeof(unsigned int)*frm_size);
      dat2_h = (unsigned int*)malloc(sizeof(unsigned int)*frm_size);
      dat3_h = (unsigned int*)malloc(sizeof(unsigned int)*frm_size);

      // call matlab API
      io_printf("[SC] %s: ref_model::before call matlab\n",TIME_STRING);
      init_RGB888_to_YCbCr444();
      RGB888_to_YCbCr444_main_process(
            dat1_h,
            dat2_h,
            dat3_h,
            &(tr.dat1[0]),
            &(tr.dat2[0]),
            &(tr.dat3[0]),
            &tr.cfg_s
            );
      io_printf("[SC] %s: ref_model::after call matlab\n",TIME_STRING);

      // construct out transaction
      out_tr.cfg_s.height = 0; // no use
      out_tr.cfg_s.width = 0; // no use
      out_tr.dat1 = std::vector<unsigned int>(dat1_h,(dat1_h+frm_size));
      out_tr.dat2 = std::vector<unsigned int>(dat2_h,(dat2_h+frm_size));
      out_tr.dat3 = std::vector<unsigned int>(dat3_h,(dat3_h+frm_size));

      sc_ap->write(out_tr);
      io_printf("[SC] %s: ref_model::after send out transaction\n",TIME_STRING);

      free(dat1_h);
      free(dat2_h);
      free(dat3_h);
      out_tr.dat1.clear();
      out_tr.dat2.clear();
      out_tr.dat3.clear();

   }

};
UVM_COMPONENT_REGISTER(ref_model)

my_env::connech_phase实现SC和SV的TLM连接:

function void my_env::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   .....
   void'(uvm_ml::connect(mdl.sc_ap.get_full_name(),{get_full_name(),".sctop.model.sc_imp"}));
   void'(uvm_ml::connect({get_full_name(),".sctop.model.sc_ap"},mdl.sc_imp.get_full_name()));
endfunction

SV UVM_ML-OA调用Matalb的数据传递路径:SV-> SV Port-> SC Export-> CPP -> mwArray API -> libRGB888_to_YCbCr444 API
结果返回路径则相反。

示例的完整代码可参考:🔗ML_verify_share

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

劲仔小鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值