Le vent se lève, il faut tenter de |

大浪淘沙、

园龄:7年1个月粉丝:50关注:19

日常记录(64)DPI

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;
}

结果

ans is 120
ans is 120

参数方向与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代码

  1. chandle数据类型是专用于DPI的,表示一个C、C++指针。
  2. bit[6:0]和svBitVecVal对应到SV和C。bit对应到svBit。
  3. 功能上实现了上升沿进行计数,下降沿进行置位,避免冲突。
点击查看代码
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代码

  1. 实现了一个计数器的功能实现,使用malloc的方式建立一个空间,创建了一个静态的计数器数据,并返回。delete用于释放空间。counter7用于实现7位的计数功能。
  2. svdpi.h的声明,才使得svBitVecVal等数据类型可以使用,而malloc.h本用于malloc函数,,veriuser.h本用于io_printf函数,可能被vcs自动编译了。
  3. 编译后的vc_hdrs.h文件中包括了C的头文件了。
点击查看代码
#include <svdpi.h>
/* #include <malloc.h> */
/* #include <veriuser.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

四状态数值对应

  1. 单比特的logic 对应svLogic的两位(aval和bval),aval为低位,bval为高位。00表示0,01表示1,10表示z,11表示x。
  2. 向量的logic如[31:0]等对应svLogicVecVal结构体,其中包括了bval和aval,取出对应到各个bit,进行组合确定四状态的值。

C++调用

SV代码

  1. 和上面基本一致,但是改了函数名部分
  2. 还是调用的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++代码

  1. 实现了一个C++的类,其中包括了一个内部计数的cnt。
  2. 外部又使用了静态方法,实现了和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代码

  1. svOpenArrayHandle用于获取开放数组句柄
  2. 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] = 90abcdef
bpack[2][0][0] = 90
C:b64[1]=1
C:b64[2]=1234567890abcdef
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
// Pack the class properties into a struct
function RGB_T pack();
pack.r = r; pack.g = g; pack.b = b;
endfunction : pack
// Unpack a struct into the class properties
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()); // Create random pixel
pixel.display("\nSV: before "); // Print it
pstruct = pixel.pack(); // Convert to a struct
invert(pstruct); // Call C to invert bits
pixel.unpack(pstruct); // Unpack struct to class
pixel.display("SV: after "); // Print it
end
end
endprogram

C代码

定义了结构体和SV对应。然后传值。

点击查看代码
typedef struct {
unsigned char b, g, r; // x86 big-endian
/* unsigned char r, g, b; // SPARC format */
} *p_rgb;
void invert(p_rgb rgb) {
rgb->r = ~rgb->r; // Invert the color values
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; // No type or args
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();
}

结果:

C: c_display
SV: block

上下文与作用域

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() {
// Print the current scope
io_printf("\nC: c_display called from scope %s\n",
svGetNameFromScope(svGetScope()));
// Set a new scope
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);
}
#!/usr/bin/perl
print "Perl: Hello world!\n" ;
exit (3)
posted @   大浪淘沙、  阅读(1072)  评论(0编辑  收藏  举报
历史上的今天:
2020-02-26 java学习与应用(5.3)--Spring
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示