DPI部分和so文件和.a文件相互区分,
so是编译成动态库以后,使用sv_lib、sv_root命令,在运行时使用,获取到API;
.a文件是静态链接后,使用-P xxx.tab xxx.a文件,在编译时候使用,生成的simv,是PLI,有$符号;
DPI这部分,是直接将c文件当成普通的sv文件,放在之前,直接编译到simv。
最简单的例子
SV代码
由sv代码调用C代码,import
表示从外部导入,即将外部的C代码导入到这里、
function int factorial(input int i)
表示的是目前的代码调用的函数
factorial = function int my_factorial(input int i)
表示目前的调用函数是my_factorial,而外部对应的C函数为factorial。这里的重命名方法可以防止一些C和SV的关键字冲突。
import "DPI-C" function int factorial(input int i);
import "DPI-C" factorial = function int my_factorial(input int i);
program test_dpi ();
initial begin
int ans = factorial(5 );
int ans2 = my_factorial(5 );
$display ("ans is %0d" , ans);
$display ("ans is %0d" , ans2);
end
endprogram : test_dpi
C代码
int factorial (int n) {
if (n==1 )
return 1 ;
else
return factorial (n-1 ) * n;
}
结果
参数方向与C库调用
SV代码
默认参数方向是input类型的,在output类型的变量过程中,被调用的C语言注明了指针。若不注明为指针,则输出值的sum为0。另外不支持ref类型。
在调用的sin函数过程中,是c库的内部函数,因此直接可以使用而不用在C代码中实现。
import "DPI-C" function int addmul(input int a, b, output int sum);
import "DPI-C" function real sin(input real r);
module taa12();
initial begin
int sum;
int ans = addmul(2 , 3 , sum);
$display ("mul is %d, sum is %d" , ans, sum);
end
initial begin
$display ("sin(0)=%f" , sin(3 .1415926 /2 ));
end
endmodule
C代码
int addmul(int a, int b, int * sum )
{
*sum = a + b;
return a*b;
}
输出结果
mul is 6 , sum is 5
sin(0 )=1.000000
数据类型传递
SV代码
chandle数据类型是专用于DPI的,表示一个C、C++指针。
bit[6:0]和svBitVecVal对应到SV和C。bit对应到svBit。
功能上实现了上升沿进行计数,下降沿进行置位,避免冲突。
点击查看代码
import "DPI-C" function chandle counter_new();
import "DPI-C" function void delete(input chandle inst);
import "DPI-C" function void counter7(input chandle inst, output bit [6 :0 ] out, input bit [6 :0 ] in, input bit reset, load);
program automatic test ();
initial begin
bit [6 :0 ] o1, o2, i1, i2;
bit reset, load, clk1;
chandle inst1, inst2;
inst1 = counter_new();
inst2 = counter_new();
fork
forever begin
#10 clk1= ~clk1;
end
forever begin
@(posedge clk1) begin
counter7(inst1, o1, i1, reset, load);
counter7(inst2, o2, i2, reset, load);
end
end
join_none
reset = 0 ;
load = 0 ;
i1 = 120 ;
i2 = 10 ;
@(negedge clk1);
load = 1 ;
@(negedge clk1);
load = 0 ;
#300 ;
delete(inst1);
delete(inst2);
$finish ;
end
endprogram : test
C代码
实现了一个计数器的功能实现,使用malloc的方式建立一个空间,创建了一个静态的计数器数据,并返回。delete用于释放空间。counter7用于实现7位的计数功能。
svdpi.h的声明,才使得svBitVecVal等数据类型可以使用,而malloc.h本用于malloc函数,,veriuser.h本用于io_printf函数,可能被vcs自动编译了。
编译后的vc_hdrs.h文件中包括了C的头文件了。
点击查看代码
#include <svdpi.h>
typedef struct {
unsigned char cnt;
} c7;
void * counter_new () {
c7* c = (c7*) malloc (sizeof (c7));
c->cnt = 0 ;
return c;
}
void delete (c7 *inst)
{
free (inst);
}
void counter7 (c7 *inst, svBitVecVal *count, const svBitVecVal *i, const svBit reset, const svBit load)
{
if (reset){
inst->cnt = 0 ;
}else if (load) {
inst->cnt = *i;
}else {
inst->cnt ++;
}
inst->cnt &=0x7f ;
*count = inst->cnt;
io_printf ("C: cnt, i, reset, load, %d, %d, %d, %d\n" , *count, *i, reset, load);
}
输出结果
点击查看代码
C: cnt, i, reset, load, 1, 120, 0, 0
C: cnt, i, reset, load, 1, 10, 0, 0
C: cnt, i, reset, load, 120, 120, 0, 1
C: cnt, i, reset, load, 10, 10, 0, 1
C: cnt, i, reset, load, 121, 120, 0, 0
C: cnt, i, reset, load, 11, 10, 0, 0
C: cnt, i, reset, load, 122, 120, 0, 0
C: cnt, i, reset, load, 12, 10, 0, 0
C: cnt, i, reset, load, 123, 120, 0, 0
C: cnt, i, reset, load, 13, 10, 0, 0
C: cnt, i, reset, load, 124, 120, 0, 0
C: cnt, i, reset, load, 14, 10, 0, 0
C: cnt, i, reset, load, 125, 120, 0, 0
C: cnt, i, reset, load, 15, 10, 0, 0
C: cnt, i, reset, load, 126, 120, 0, 0
C: cnt, i, reset, load, 16, 10, 0, 0
C: cnt, i, reset, load, 127, 120, 0, 0
C: cnt, i, reset, load, 17, 10, 0, 0
C: cnt, i, reset, load, 0, 120, 0, 0
C: cnt, i, reset, load, 18, 10, 0, 0
C: cnt, i, reset, load, 1, 120, 0, 0
C: cnt, i, reset, load, 19, 10, 0, 0
C: cnt, i, reset, load, 2, 120, 0, 0
C: cnt, i, reset, load, 20, 10, 0, 0
C: cnt, i, reset, load, 3, 120, 0, 0
C: cnt, i, reset, load, 21, 10, 0, 0
C: cnt, i, reset, load, 4, 120, 0, 0
C: cnt, i, reset, load, 22, 10, 0, 0
C: cnt, i, reset, load, 5, 120, 0, 0
C: cnt, i, reset, load, 23, 10, 0, 0
C: cnt, i, reset, load, 6, 120, 0, 0
C: cnt, i, reset, load, 24, 10, 0, 0
C: cnt, i, reset, load, 7, 120, 0, 0
C: cnt, i, reset, load, 25, 10, 0, 0
四状态数值对应
单比特的logic 对应svLogic的两位(aval和bval),aval为低位,bval为高位。00表示0,01表示1,10表示z,11表示x。
向量的logic如[31:0]等对应svLogicVecVal结构体,其中包括了bval和aval,取出对应到各个bit,进行组合确定四状态的值。
C++调用
SV代码
和上面基本一致,但是改了函数名部分
还是调用的C的方法。但是对面的C的方法内部又调用了CPP
点击查看代码
import "DPI-C" counter7_new=function chandle counter_new();
import "DPI-C" function void counter7(
input chandle inst,
output bit [6 :0 ] cnt,
input bit [6 :0 ] in,
input bit reset, load
);
program automatic test ();
initial begin
bit [6 :0 ] o1, i1;
bit reset, load, clk1;
chandle inst1, inst2;
inst1 = counter_new();
fork
forever begin
#10 clk1= ~clk1;
end
forever begin
@(posedge clk1) begin
counter7(inst1, o1, i1, reset, load);
end
end
join_none
reset = 0 ;
load = 0 ;
i1 = 120 ;
@(negedge clk1);
load = 1 ;
@(negedge clk1);
load = 0 ;
#300 ;
$finish ;
end
endprogram : test
C++代码
实现了一个C++的类,其中包括了一个内部计数的cnt。
外部又使用了静态方法,实现了和C++类的连接。其中的this用法什么的都和sv相同。
点击查看代码
#include <svdpi.h>
#include <veriuser.h>
class Counter7
{
public :
Counter7 ();
void counter7_signal (svBitVecVal *cnt, svBitVecVal *i, svBit reset, svBit load) ;
private :
unsigned char cnt;
};
Counter7::Counter7 ()
{
cnt=0 ;
}
void Counter7::counter7_signal (svBitVecVal *cnt, svBitVecVal *i, svBit reset, svBit load)
{
if (reset) {
this ->cnt=0 ;
}else if (load) {
this ->cnt=*i;
}else {
this ->cnt++;
}
this ->cnt &= 0x7F ;
*cnt = this ->cnt;
}
extern "C"
{
void * counter7_new ()
{
return new Counter7;
}
void counter7 (void * inst, svBitVecVal *cnt, svBitVecVal *i, svBit reset, svBit load)
{
Counter7 *c7 = (Counter7 *)inst;
c7->counter7_signal (cnt, i, reset, load);
io_printf ("cnt %0d, i %0d, reset %0d, load %0d\n" , *cnt, *i, reset, load);
}
}
开放数组
SV代码
传入了一个定长数组data[20]
点击查看代码
mport "DPI-C" function void fib_oa(output bit [31 :0 ] data[]);
program taa ();
bit [31 :0 ] data[20 ];
initial begin
fib_oa(data);
foreach (data[i]) begin
$display (i, data[i]);
end
end
endprogram : taa
C代码
svOpenArrayHandle用于获取开放数组句柄
svGetArrayPtr用于获取实际的数组元素指针
点击查看代码
#include <svdpi.h>
void fib_oa(const svOpenArrayHandle data_oa)
{
int i, *data ;
data = (int *) svGetArrayPtr(data_oa );
data [0] = 1;
data [1] = 1;
for (i = 2 ; i < 20 ; i++) {
data [i] = data [i-1] + data [i-2];
}
}
实际结果
点击查看代码
0 1
1 1
2 2
3 3
4 5
5 8
6 13
7 21
8 34
9 55
10 89
11 144
12 233
13 377
14 610
15 987
16 1597
17 2584
18 4181
19 6765
其它方法
开放数组除了上面使用的svOpenArrayHandle和svGetArrayPtr以外,还有很多方法。
svLeft、svRight、svLow、svHigh获取数组左边界、右边界、下界、上界。
svIncrement、svSize、svDimension、svSizeOfArray获取方向、大小、维数、字节量
svGetArrElemPtr获取元素指针。
压缩的开放数组
SV代码
这里的压缩数组中,倒序的9:1,在foreach中也是倒序的。
点击查看代码
import "DPI-C" function void view_pack(input bit [63 :0 ] b64[]);
program test ();
bit [1 :0 ][0 :3 ][6 :-1 ] bpack [9 :1 ];
initial begin
foreach (bpack[i]) begin
bpack[i] = i;
end
bpack[2 ]=64'h12345678_90abcdef ;
foreach (bpack[i]) begin
$info ("%0d value %0d" , i, bpack[i]);
end
$display ("bpack[2][0] = %h" , bpack[2 ][0 ]);
$display ("bpack[2][0][0] = %h" , bpack[2 ][0 ][0 ]);
view_pack(bpack);
end
endprogram : test
C代码
其中的svGetArrElemPtr1用于获取压缩的开放数组h第1维的第i个元素。
可以替换为svGetArrElemPtr。
svGetArrElemPtr2、svGetArrElemPtr3分别为获取第二维、第三维的元素。
点击查看代码
#include <svdpi.h>
void view_pack (const svOpenArrayHandle h)
{
int i;
for (i = svLow (h, 1 ); i< svHigh (h,1 ); i++) {
io_printf ("C:b64[%0d]=%llx\n" , i, *(long long int *)svGetArrElemPtr1 (h,i));
}
}
运行结果
点击查看代码
Info: "taa12-6.sv" , 13 : test: at time 0
9 value 9
Info: "taa12-6.sv" , 13 : test: at time 0
8 value 8
Info: "taa12-6.sv" , 13 : test: at time 0
7 value 7
Info: "taa12-6.sv" , 13 : test: at time 0
6 value 6
Info: "taa12-6.sv" , 13 : test: at time 0
5 value 5
Info: "taa12-6.sv" , 13 : test: at time 0
4 value 4
Info: "taa12-6.sv" , 13 : test: at time 0
3 value 3
Info: "taa12-6.sv" , 13 : test: at time 0
2 value 1311768467294899695
Info: "taa12-6.sv" , 13 : test: at time 0
1 value 1
bpack[2 ][0 ] = 90 abcdef
bpack[2 ][0 ][0 ] = 90
C: b64[1 ]=1
C: b64[2 ]=1234567890 abcdef
C: b64[3 ]=3
C: b64[4 ]=4
C: b64[5 ]=5
C: b64[6 ]=6
C: b64[7 ]=7
C: b64[8 ]=8
$finish at simulation time 0
传递结构
SV代码
定义了压缩的结构RGB_T,注意pack的返回方式是使用函数名的方式,该函数名指代了返回值的结构体。
将结构体传入C的invert函数方法中。实现传值。
点击查看代码
typedef struct packed { bit [ 7 :0 ] r, g, b; } RGB_T;
import "DPI-C" function void invert(inout RGB_T pstruct);
program automatic test;
class RGB;
rand bit [ 7 :0 ] r, g, b;
function void display(string prefix="" );
$display ("%sRGB=%x,%x,%x" , prefix, r, g, b);
endfunction : display
function RGB_T pack();
pack.r = r; pack.g = g; pack.b = b;
endfunction : pack
function void unpack(RGB_T pstruct);
r = pstruct.r ; g = pstruct.g ; b = pstruct.b ;
endfunction : unpack
endclass : RGB
initial begin
RGB pixel;
RGB_T pstruct;
pixel = new ;
repeat (5 ) begin
assert (pixel.randomize ());
pixel.display ("\nSV: before " );
pstruct = pixel.pack ();
invert(pstruct);
pixel.unpack (pstruct);
pixel.display ("SV: after " );
end
end
endprogram
C代码
定义了结构体和SV对应。然后传值。
点击查看代码
typedef struct {
unsigned char b, g, r;
} *p_rgb;
void invert (p_rgb rgb) {
rgb-> r = ~rgb-> r;
rgb-> g = ~rgb-> g;
rgb-> b = ~rgb-> b;
io_printf ("C: Invert rgb=%02x,%02x,%02x\n" ,
rgb-> r, rgb-> g, rgb-> b);
}
结果
点击查看代码
SV: before RGB=b9,6c,a7
C: Invert rgb=46,93,58
SV: after RGB=46,93,58
SV: before RGB=5b,80,8a
C: Invert rgb=a4,7f,75
SV: after RGB=a4,7f,75
SV: before RGB=3a,56,33
C: Invert rgb=c5,a9,cc
SV: after RGB=c5,a9,cc
SV: before RGB=07,52,59
C: Invert rgb=f8,ad,a6
SV: after RGB=f8,ad,a6
SV: before RGB=e0,4c,64
C: Invert rgb=1f,b3,9b
SV: after RGB=1f,b3,9b
$finish at simulation time 0
纯导入与关联导入
纯导入
使用pure关键字在import "DPI-C" 之后。不访问任何全局变量,静态变量,不对文件读写,不进行进程操作等。默认,输出仅依赖于输入。
关联导入
使用context关键字在import "DPI-C" 之后。需要记录上下文。
简单导出
使用export将函数导出给C调用,无需说明返回值、参数,只说明是function,以及function名。
SV代码
module block;
import "DPI-C" context function void c_display();
export "DPI-C" function sv_display;
initial c_display();
function void sv_display();
$display ("SV: block" );
endfunction
endmodule : block
C代码
void c_display () {
io_printf ("C: c_display\n");
sv_display ();
}
结果:
上下文与作用域
SV代码
两个module,让block先行,然后保存当前作用域,打印。
第二个module在打印的时候,由于作用域被第一个给改变了,
因此在执行C中调用的SV的时候,调用了第一个module的sv_display。
点击查看代码
module block;
import "DPI-C" context function void c_display();
import "DPI-C" context function void save_my_scope();
export "DPI-C" function sv_display;
function void sv_display();
$display ("SV: %m" );
endfunction : sv_display
initial begin
save_my_scope();
c_display();
end
endmodule : block
module top;
import "DPI-C" context function void c_display();
export "DPI-C" function sv_display;
function void sv_display();
$display ("TSV: %m" );
endfunction : sv_display
block b1();
initial #1 c_display();
endmodule : top
C代码
svScope 是全局的量,表示SV的作用域值。
svGetScope用于获取当前对应SV的作用域。
svSetScope用于设置当前对应SV的作用域。
点击查看代码
#include <svdpi.h>
extern void sv_display () ;
svScope my_scope;
void save_my_scope () {
my_scope = svGetScope();
}
void c_display () {
io_printf("\nC: c_display called from scope %s\n" ,
svGetNameFromScope(svGetScope()));
svSetScope(my_scope);
io_printf("C: calling %s.sv_display\n" ,
svGetNameFromScope(svGetScope()));
sv_display();
}
运行结果
第一次调用过程中,
点击查看代码
C: c_display called from scope top.b1
C: calling top.b1.sv_display
SV: top.b1.sv_display
C: c_display called from scope top
C: calling top.b1.sv_display
SV: top.b1.sv_display
与其它语言交互
SV代码,注明:+script=tpl.pl,注意perl的位置。
import "DPI-C" function int call_perl(string s);
program automatic perl_test;
int ret_val;
string script;
initial begin
if (!$test$plusargs("script" )) begin
$display ("No +script switch found" );
$finish ;
end
$value$plusargs ("script=%s" , script);
$display ("SV: Running '%0s'" , script);
ret_val = call_perl(script);
$display ("SV: Perl script returned %0d" , ret_val );
end
endprogram : perl_test
C的system和Verilog的$system,调用外部的程序执行。
#include "vc_hdrs.h"
#include <stdlib.h>
#include <wait.h>
int call_perl (const char * command) {
int result = system (command);
return WEXITSTATUS (result);
}
print "Perl: Hello world!\n" ;
exit (3 )
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2020-02-26 java学习与应用(5.3)--Spring