DPI基础知识

2023-05-16

DPI 数据类型映射

当SystemVerilog与C环境交互时,会发生数据交换。 数据需要在两端以完全相同的方式解释,否则通信将失败。 SystemVerilog和C之间的数据交换通常使用DPI-C接口完成,该接口标准化类型对应关系和基本API(另请参见仿真器安装路径下的svdpi.h)。

大多数SystemVerilog数据类型在C语言中具有直接的对应关系,而其他(例如,4值类型,数组)需要DPI-C定义的类型和API。 查看下表完整的数据类型映射关系。

SystemVerilogC (input )C (output )
bytecharchar*
shortintshort intshort int*
intintint*
longintlong long intlong int*
shortrealfloatfloat*
realdoubledouble*
stringconst char*char**
string [N]const char**char**
bitsvBit or unsigned charsvBit* or unsigned char*
Logic, regsvLogic or unsigned charsvLogic* or unsigned char*
bit[N:0]const svBitVecVal*svBitVecVal*
reg[N:0], logic[N:0]const svLogicVecVal*svLogicVecVal*
unsized array[]const svOpenArrayHandlesvOpenArrayHandle
chandleconst void*void*

一个简单的例子:

#include <stdio.h>
#include <stdlib.h>
#include "svdpi.h"
int add() {
  int a = 10, b = 20;
  a = a + b;

  io_printf("Addition Successful and Result = %d\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n", a);
  return a;
}
module tb_dpi;

  import "DPI-C" function int add();
  import "DPI-C" function int sleep(input int secs);
  int j;
  
  initial
  begin
    $display("Entering in SystemVerilog Initial Block");
    #20
    j = add();
    $display("Value of J = %d", j);
    $display("Sleeping for 3 seconds with Unix function");
    sleep(3);
    $display("Exiting from SystemVerilog Initial Block");
    #5 $finish;
  end
  
endmodule

如上面的例子所示,就是这么简单,C代码是C代码,SV代码中只需要使用关键字import将C代码的中的函数导入即可。上一章讲到SystemVerilog代码与C语言之间的分离基于使用函数作为SV中的自然封装单元,对SV代码来说C中的函数是黑盒,是透明的。

DPI 各种使用方式

  • 通过import定义的函数可以在任何位置
  • 在Verilog模块中
  • 在SystemVerilog interface中
  • 在SystemVerilog package中
  • 在SystemVerilog“编译单元”中
  • import声明必须具有参数的原型
  • 必须与C函数中的参数数完全匹配
  • 必须指定兼容的数据类型
  • 可以在多个位置导入相同的C功能
  • 每个原型必须完全相同
  • 更好的方法是在包中定义一个导入

C函数可以作为Verilog任务或函数导入
import "DPI-C" function real sin(real in); //sin function in C math lib
import "DPI-C" task file_write(input string data, output reg status);

1. 从C向SV导入函数作为一个函数(function)使用

#include <math.h>
#include <svdpi.h>
 
double Sin(double r)
{
        return sin(r);
}
module incremental_call_test;
 
 	`define PAI 3.14159265358979323846;

    import "DPI-C"  pure function real cos (real n1);
    import "DPI-C"  pure function real sinh (real n2);
 
    initial begin
               
    	for(int i=0;i<=90;i++)
        	$display("cos=%f sinh=%f",cos(i*`PAI/180),sinh(i*`PAI/180));
       
    end
	
endmodule

顺便说一句,在上面的例子中,在C端函数中调用sin运行时,用户定义Sin函数是有点多余。可以直接从SystemVerilog调用这些C标准库的函数,例如cos函数的import。

2. 从C向SV导入函数作为一个任务(task)使用

import "DPI-C" task file_write(input string data, output reg status);

export "DPI-C" function sv_inc;
export "DPI-C" task delay_task;

3. 从SV导出函数(function)到C程序

#include "stdio.h" 
#include "svdpi.h" 

extern void export_func(void); 

void import_func() 
{ 
export_func(); 
} 
program main; 

export "DPI-C" function export_func; 
import "DPI-C" function void import_func(); 

function void export_func(); 
$display("SV: Hello from SV "); 
endfunction 

initial 
begin 
import_func(); 
end 

endprogram

4. 从SV导出任务(task)到C程序

#include "svdpi.h"
#include "vcsuser.h"
extern void delay_task();
extern long long int get_sv_time(); 

void import_task()
{
 
        io_printf("C:  current time = %ld #10 waiting\\\\n",get_sv_time());
        delay_task_by_parameter(10);

        io_printf("C: I'am back. current time = %ld 5 CLK waiting\\\\n",get_sv_time());
        wait_n_clks(5);

        io_printf("C: I'am back. current time = %ld event trigger waiting\\\\n",get_sv_time());
        wait_trigger();

        io_printf("C: I'am back. current time = %ld level High waiting\\\\n",get_sv_time());
        wait_level_high();
        io_printf("C: I'am back. current time = %ld C import task end\\\\n",get_sv_time());
 
}
`timescale 1ns/1ns
module export_test1;
 
   import "DPI-C"  context task import_task();
   export "DPI-C"  function  get_sv_time;
   export "DPI-C"  task delay_task_by_parameter;
   export "DPI-C"  task wait_n_clks;
   export "DPI-C"  task wait_trigger;
   export "DPI-C"  task wait_level_high;
 
   event ev;
   reg level=0;   
 
   function longint get_sv_time();
      return $time;
   endfunction
 
   reg clk=0;
   always #1 clk=~clk;
 
   task delay_task_by_parameter( input longint d);
      #(d);
   endtask;
 
   task wait_n_clks( input int n);
      repeat(n) begin
         @(negedge clk);
      end
   endtask
 
   task wait_trigger();
      @(ev);
   endtask
 
   task wait_level_high();
      wait(level==1);
   endtask
 
   initial begin
      #1;
      fork
         import_task();
         #40 level=1;
         repeat(21) #2 $display("......SV is running. current time = %2d",$time);
         #30 ->ev;
      join
      $display("fork join. %d",$time);
      $finish();
   end
 
endmodule

在上面例子中,SV和C都只使用了函数,但是不能从函数调用任务与DPI相同。与verilog一样,只能编写任务以处理延迟和等待事件,因此在这种情况下,这是任务。相反,如果不等待延迟或事件,使用函数有利于提高速度。函数的实现使用C堆栈帧,在许多实现中,它比任务更快,因为不需要线程处理(上下文保存)。在SystemVerilog中,如上例所示,可以使用不带返回值的void函数。因此如果没有延迟或等待事件,则应使用函数。

5. 互相调用的情况(TODO: c中调用sv函数,sv中调用C中含有sv函数的函数)

6 绑定别名

module gloval_name_space;
 
    import "DPI-C"  context task sv_import_task ();
	
endmodule
module gloval_name_space1;
 
    import "DPI-C"  context C_task1=task sv_import_task ();
 
endmodule
 
module gloval_name_space2;
 
    import "DPI-C"  context C_task2=task sv_import_task ();
 
endmodule

从C的命名空间是全局可见的。 SV sv_import_task中的声明变为等于C的链接名称。
对于所有模块,名称必须是唯一的。 C的链接是全局性的。
因此,
上面是一种别名C链接名称的方法。 SV上的名称相同,但在C上,使用别名C_task1和C_task2执行链接。
(sv_import_task是不可见的)

同理,在export的函数中也可使用别名:

export "DPI-C" f_plus = function f ; // "f" exported as "f_plus"
export "DPI-C" function f; // "f" exported under its own name

参数传递和返回值

在C和SV中有两种传递参数的方法:

  • 按值传递:被调用者函数将使用来自调用者的参数的副本
  • 通过引用传递:被调用者函数将使用来自调用者的参数的指针/引用

如果函数正在更改其参数的值,则仅当参数通过引用传递时,更改才会在函数外部可见。 当参数按值传递时,对函数内部完成的参数的任何更改都不会在其外部可见。

在SystemVerilog中,按值或按引用传递由参数方向确定。
在C中,通过值或引用传递是由参数类型是否为指针确定的。
默认情况下,SV和C都按值传递参数。

导入的C函数参数可以作为输入,输出或inout(双向)

  • 输入的行为就像在调用时复制到C函数中一样,C函数不应修改输入参数
  • 当函数返回时,输出的行为就像复制到Verilog中一样
  • Inouts的行为就像在调用中复制一样,并在返回时复制出来

除非另有说明,否则假定参数为输入

SV提供了丰富的数据类型可以作为参数:

  • void, byte, shortint, int, longint, real, shortreal, chandle, time, integer, and string
  • Scalar values of type bit and logic
  • Packed arrays, structs, and unions composed of types bit and logic
  • Types constructed from the supported types with the help of the constructs: struct , union , Unpacked array , typedef

也提供了丰富的返回值数据类型:

  • void, byte, shortint, int, longint, real, shortreal, chandle, and string
  • Scalar values of type bit and logic
  • Restrictions apply for import and export functions

DPI 导入函数的分类

Pure, Context and Generic C Functions

  • Pure C函数
    作为pure函数,函数的结果必须仅仅依赖于通过形参传递进来的数值。Pure函数的优点在于仿真器可以执行优化以改进仿真性能。Pure函数不能使用全局或者静态变量,不能执行文件I/O操作,不能访问操作系统环境变量,不能调用来自Verilog PLI 库的函数。只有没有输出或者inout的非void函数(必须有return值)可以被指定成pure。Pure函数不能作为Verilog任务导入。下面的例子声明了一个导入的C函数为pure函数:
    import "DPI" pure function real sin(real in); // function in C math library

  • Context C函数
    context C函数明确函数声明所在工作域的Verilog的层次。可以是void函数,可以有输出和inout参数,可以从C库调用函数(用于文件I/O等),可以调用PLI库中的许多函数,这使得被导入的C函数能够调用来自PLI或者VPI库的函数,从而DPI函数可以充分利用PLI的优势特性,比如写仿真器的log文件以及Verilog源代码打开的文件。context任务声明的样例如下:
    import "DPI" context task print(input int file_id, input bit [127:0] data);

  • Generic C函数
    本文把那些既没有明确声明为pure,也没有声明为context的函数称为generic函数(SystemVerilog标准没有给除了pure或context之外的函数特定的称呼)。generic C函数可以作为Verilog函数或者Verilog任务导入。任务或者函数可以由输入、输出以及inout的参数。函数可以有一个返回值,或者声明为void。generic C函数不允许调用Verilog PLI函数,不能访问除了参数以外的任何数据,只能修改这些参数。

注意! 正确的声明导入的函数为pure还是context是用户的责任。缺省情况下,DPI函数假定是generic函数。调用一个不正确声明成pure的C函数可能返回不正确或者不一致的结果,导致不可预测的运行时错误,甚至于让仿真崩溃。
同样,如果一个C函数访问Verilog PLI库或者其他API库,却没有声明为context函数,会导致不可预见的仿真结果甚至仿真崩溃。

常用数据类型映射

  • SV byte -> C char
// SV
import "DPI-C" function void compute_byte(input byte i_value, output byte result);
import "DPI-C" function byte get_byte(input byte i_value);
// C
void compute_byte(const char i_value, char* result);
char get_byte(const char i_value);
  • SV shortint -> C short int
// SV
import "DPI-C" function void compute_shortint(input shortint i_value, output shortint result);
import "DPI-C" function shortint get_shortint(input shortint i_value);
// C
void compute_shortint(const short int i_value, short int* result);
short int get_shortint(const short int i_value);
  • SV int -> C int
// SV
import "DPI-C" function void compute_int(input int i_value, output int result);
import "DPI-C" function int get_int(input int i_value);
// C
void compute_int(const int i_value, int* result);
int get_int(const int i_value);
  • SV longint -> C long int
// SV
import "DPI-C" function void compute_longint(input longint i_value, output longint result);
import "DPI-C" function longint get_longint(input longint i_value);
// C
void compute_longint(const long int i_value, long int* result);
long int get_longint(const long int i_value);
  • SV real -> C double
// SV
import "DPI-C" function void compute_real(input real i_value, output real result);
import "DPI-C" function real get_real(input real i_value);
// 
void compute_real(const double i_value, double* result);
double get_real(const double i_value);
  • SV string -> C char*
// SV
import "DPI-C" function void compute_string(input string i_value, output string result);
import "DPI-C" function string get_string(input string i_value);
// C
void compute_string(const char* i_value, char** result);
char* get_string(const char* i_value);
  • SV chandle -> C void*
// SV
import "DPI-C" function void compute_chandle(output chandle result);
import "DPI-C" function chandle get_chandle();
import "DPI-C" function void call_chandle(input chandle i_value, output int result);
// C
void compute_chandle(void** result);
void** get_chandle();
void call_chandle(const void* i_value, int* o_value);
  • SV bit -> C bit
// SV
import "DPI-C" function void compute_bit(input bit i_value, output bit result);
import "DPI-C" function bit get_bit(input bit i_value);
// C
void compute_bit(const svBit i_value, svBit* result);
svBit get_bit(const svBit i_value);
  • SV bit[n:0] -> C svBitVecVal
// SV
import "DPI-C" function void compute_bit_vector(input bit[`BIT_ARRAY_SIZE - 1 : 0] i_val, output bit[`BIT_ARRAY_SIZE - 1 : 0] result);
import "DPI-C" function bit[`BIT_ARRAY_SIZE - 1 : 0] get_bit_vector(input bit[`BIT_ARRAY_SIZE - 1 : 0] i_val);
// C
void compute_bit_vector(const svBitVecVal* i_value, svBitVecVal* result);
svBitVecVal get_bit_vector(const svBitVecVal* i_value);
  • SV logic -> C svLogic
// SV
import "DPI-C" function void compute_logic(input logic i_value, output logic result);
import "DPI-C" function logic get_logic(input logic i_value);
// C
void compute_logic(const svLogic i_value, svLogic* result);
svLogic get_logic(const svLogic i_value);

SV reg -> C svLogic

// SV
import "DPI-C" function void compute_reg(input reg i_value, output reg result);
import "DPI-C" function reg  get_reg(input reg i_value);
// C
void compute_reg(const svLogic i_value, svLogic* result);
svLogic get_reg(const svLogic i_value);
  • SV logic[n:0] -> C svLogicVecVal
// SV
import "DPI-C" function void compute_logic_vector(input logic[`LOGIC_ARRAY_SIZE - 1 : 0] i_val, output logic[`LOGIC_ARRAY_SIZE - 1 : 0] result, input int asize);
// C
svLogicVecVal*  get_logic_vector(const svLogicVecVal* i_value, int asize);
  • SV reg[n:0] -> C svLogicVecVal
// SV
import "DPI-C" function void compute_reg_vector(input reg[`REG_ARRAY_SIZE - 1 : 0] i_val, output reg[`REG_ARRAY_SIZE - 1 : 0] result, input int asize);
// C
void compute_reg_vector(const svLogicVecVal* i_value, svLogicVecVal* result, int asize);
  • SV int[] -> C svOpenArrayHandle
// SV
import "DPI-C" function void compute_unsized_int_array(input int i_value[], output int result[]);
// C
void compute_unsized_int_array(const svOpenArrayHandle i_value, svOpenArrayHandle result);
  • SV struct -> C struct
// SV
`define BIT_ARRAY_SIZE 16
typedef struct {
	byte aByte;
	int anInt;
	bit aBit;
	longint aLongInt;
	bit[`BIT_ARRAY_SIZE-1:0] aBitVector;
} dpi_c_ex_s;
import "DPI-C" function void compute_struct(input dpi_c_ex_s i_value, output dpi_c_ex_s result);
// C
typedef struct dpi_c_ex_s {
	char aChar;
	int anInt;
	svBit aBit;
	long int aLongInt;
	svBitVecVal aBitVector;
} dpi_c_ex_s;
void compute_struct(const dpi_c_ex_s* i_value, dpi_c_ex_s* output);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

DPI基础知识 的相关文章

  • XXapp产品过程思考:

    本人在团队中本为项目经理 xff0c 临时需求 xff0c 客串产品经理成为新鸟 xff0c 没吃过猪肉 xff0c 没见过猪跑 xff0c 完全自己想象猪该如何跑 xff0c 以下为本人在研发这款app的一点心得 xff0c 仅供大家参考
  • AttributeError: module ‘pymunk‘ has no attribute ‘constraint‘ 解决方案

    问题分析 出现这个问题是因为当前版本的pymunk的版本过高所致 xff08 我的版本是6 4 0 xff09 xff0c 换用更低版本的pymunk即可 解决方案 pip install pymunk 61 61 5 5 0 参考链接 P
  • linux 网络 device not managed 上不了网

    Ubuntu 12 04 xff0c 本来无线网络连接好好的 xff0c 但是在重启了一次unity界面之后 xff0c 网络连接那个地方就一直显示一个扇形 xff0c 点开后的下拉列表里面一个无线网络都看不见 xff0c 发现 34 Wi
  • 开发板不能下载内核 内核下载问题解答

    首先出现 1 39 serverip 39 not set 这时在 uboot gt setenv serverip 192 168 1 103 这是我虚拟机的ip xff0c 大家对应设置即可 2 ERROR 96 ipaddr 39 n
  • 2013 吊水壶瀑布 长春 周边 旅游 攻略 双阳

    话说没车想去长春大的吊水壶时间很苦逼的事情 xff0c 一下为本人历经千辛万苦总结的去吊水壶的攻略 xff0c 绝对在网上独一无二 xff0c 而且不是自驾游 楼主全靠乘车问路 xff0c 不信有人会比我的攻略更省rmb啦 xff0c 下面
  • javaScript、PHP连接外卖小票机打印机方案(佳博、芯烨等)

    前言 xff1a 目前开发需要用到电脑直接连接外卖小票机打印小票 xff0c 查阅各种资料 xff0c 终于解决了这个问题 效果 xff1a PHP JavaScript直接连接小票机并且自动出票 支持的小票机 xff1a 目前测试可以的有
  • adb 操作指令详解

    ADB xff0c 即 Android Debug Bridge xff0c 它是 Android 开发 测试人员不可替代的强大工具 xff0c 也是 Android 设备玩家的好玩具 注 xff1a 有部分命令的支持情况可能与 Andro
  • typeAliases用法简介

    1 作为别名使用 在mybatis config xml中对Books类取别名 xff1a lt typeAliases gt lt typeAlias alias 61 34 Books 34 type 61 34 com niu poj
  • 获取文件的contentType

    获取文件类型 64 param fileName 文件名 64 return public static String getContentType String fileName Optional lt MediaType gt medi
  • PCL vtkAtomic.h(358): error C2872: “detail”: 不明确的符号

    错误 xff1a 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 C2872 detail 不明确的符号 TestCarModel d softwareinstallpath pcl1 8 1 3rdparty vtk includ
  • You must set CMAKE_CUDA_ARCHITECTURES to e.g. ‘native’, ‘all-major’, ‘70’

    colmap 编译报错 CMake Error at CMakeLists txt 255 span class token punctuation span message span class token punctuation spa
  • 基于Open3D的点云处理2-Open3D的IO与数据转换

    三维数据类型 点云 某个坐标系下的点数据集 xff0c 每个点包括三维坐标X xff0c Y xff0c Z 颜色 分类值 强度值 时间等信息 xff1b 储存格式 xff1a pts LAS PCD xyz asc ply等 xff1b
  • ModuleNotFoundError: No module named ‘gym_minigrid‘ 解决方案

    问题描述 如题 xff0c 今天在执行以下代码时出现了题目中的bug xff1a from gym minigrid wrappers import FullyObsWrapper 查询了https www roseindia net an
  • 基于Open3D的点云处理3-可视化

    可视化接口 API open3d span class token punctuation span visualization span class token punctuation span draw geometries span
  • 基于Open3D的点云处理4-旋转、平移、缩放

    三维变换主要包括 xff1a 平移 旋转 缩放 在open3d中 xff0c 针对三维对象的变换主要有translate rotate scale和transform Translate 平移 Rotate 旋转 Scale 缩放 Tran
  • python-opencv图像处理(3)cv2.copyMakeBorder()

    padding 不难发现 xff0c 用3 3的核对一副6 6的图像进行卷积 xff0c 得到的是4 4的图 xff0c 图片缩小了 xff01 那怎么办呢 xff1f 我们可以把原图扩充一圈 xff0c 再卷积 xff0c 这个操作叫pa
  • CNN卷积层、池化层、全连接层

    卷积神经网络是通过神经网络反向传播自动学习的手段 xff0c 来得到各种有用的卷积核的过程 卷积神经网络通过卷积和池化操作 xff0c 自动学习图像在各个层次上的特征 xff0c 这符合我们理解图像的常识 人在认知图像时是分层抽象的 xff
  • C/C++数组的大小最大能有多大?

    直接定义一个数组 xff0c 如a SIZE 这个是分配的静态空间 xff0c 在栈上 xff08 局部变量 xff09 或全局静态区 xff08 全局变量 xff09 上分配的 xff0c 一般栈的内存是1M到2M xff0c 所以静态分
  • 激活函数(5)ELU函数、PReLU函数

    ELU函数 ELU函数公式和曲线如下图 ELU函数是针对ReLU函数的一个改进型 xff0c 相比于ReLU函数 xff0c 在输入为负数的情况下 xff0c 是有一定的输出的 xff0c 而且这部分输出还具有一定的抗干扰能力 这样可以消除
  • PCL学习:基于VFH描述子的聚类识别与位姿估计(1)

    VFH是非常强大的描述子之一 其主要用于识别和估计点集的聚类 这里的聚类是指3D点的集合 常常代表着一个特殊目标对象或场景的一部分 由分割或提取算法获得 在本节我们的目标不是提供一个最终的识别工具 而是根据各个聚类的场景及其对应的6自由度位

随机推荐