您的位置:首页 > 其它

verilog PLI简介

2016-05-08 11:27 260 查看
0.简介
Verilog PLI(Programming Language Interface )是一种Verilog代码调用C/C++函数的机制。它能让Verilog像调用一些系统调用(如$display/$stop/$random)一样调用用户编写的C/C++函数。PLI可以完成如下功能:

功耗分析

代码覆盖率工具

修改Verilog仿真数据结构(如修改为更精确的延时,即sdf反标)

自定义输出显示

联合仿真

设计的调试功能

仿真分析

加速仿真的C模型接口

Testbench建模

为了完成上述功能,C代码需要能够访问Verilog的内部数据结构,因此Verilog PLI需要提供一些访问程序集(acc routines),此外Verilog PLI还提供了另外一组程序集:任务功能程序集(tf routines)。目前PLI有两个版本:PLI1.0和PLI2.0,PLI 2.0又叫VPI,是随着Verilog 2001一起发布的。

1.工作原理

1)C/C++编写函数

2)编译并产生共享库(.DLL in Windows .so in UNIX)。一些仿真器如VCS允许静态链接。

3)将C/C++函数细节在编译veriog代码是传递给编译器(称为链接linking)

4)一旦链接完成,就可以像运行其他的verilog仿真一样了。



当Verilog仿真器遇到用户定义的系统函数时,便将控制权交给PLI 程序(C/C++函数)

2.示例-Hello World

下面给出一个简单的示例,例子不涉及任何PLI标准函数(ACC,TF等),因此,只要使用对应仿真器连接C/C++函数即可。

hello.c:

#include <stdio.h>

void hello () {
printf ("\nHello World!\n");
}


hello_pli.v

module hello_pli ();

initial begin
$hello;
#10 $finish;
end

endmodule


使用如下VCS命令编译并运行:

vcs -R -P hello.tab hello_pli.v hello.c


hello.tab文件内容如下:

$hello call=hello


3.示例-PLI应用程序

下面一个示例将简单的调用PLI系统函数实现C/C++代码对Verilog代码的数据结构的访问,并将其应用在Testbench中实现对DUT的监视。

DUT设计为一个简单的16进制计数器,counter.v代码如下:

`timescale 1ns/10ps
module counter (
input clk,
input reset,
input enable,
output reg [3:0] count
);

always @(posedge clk or posedge reset)
begin
if(reset)
begin
count <=  4'b0;
end
else if(enable)
begin
count <= count+1'b1;
end
end
endmodule


编写Testbench代码 pli_counter_tb.v如下:

1 `timescale 1ns/10ps
2 module counter_tb();
3  reg enable;
4  reg reset;
5  reg clk_reg;
6  wire clk;
7  wire [3:0] dut_count;
8
9 initial begin
10   enable = 0;
11   clk_reg = 0;
12   reset = 0;
13   $display("%g , Asserting reset", $time);
14   #10 reset = 1;
15   #10 reset = 0;
16   $display ("%g, Asserting Enable", $time);
17   #10 enable = 1;
18   #55 enable = 0;
19   $display ("%g, Deasserting Enable", $time);
20   #1 $display ("%g, Terminating Simulator", $time);
21   #1 $finish;
22 end
23
24 always begin
25   #5 clk_reg = !clk_reg;
26 end
27
28 assign clk = clk_reg;
29 initial
30 begin
31     $dumpfile("wave.vcd");
32     $dumpvars;
33 end
34
35 initial begin
36   $counter_monitor (counter_tb.clk, counter_tb.reset, counter_tb.enable, counter_tb.dut_count);
37 end
38
39 counter U(
40 .clk (clk),
41 .reset (reset),
42 .enable (enable),
43 .count (dut_count)
44 );
45
46 endmodule


上述代码中35-37行调用用户自定义系统函数$counter_monitor,对信号进行监视并打印值,此系统函数使用C代码编写,并通过PLI进行调用。

C代码 pli_full_example.c如下:





#include "acc_user.h"

typedef char * string;
handle clk ;
handle reset ;
handle enable ;
handle dut_count ;
int count ;
int sim_time;
string high = "1";
void counter ();
int pli_conv (string in_string, int no_bits);

void counter_monitor() {
acc_initialize();
clk       = acc_handle_tfarg(1);
reset     = acc_handle_tfarg(2);
enable    = acc_handle_tfarg(3);
dut_count = acc_handle_tfarg(4);
acc_vcl_add(clk,counter,null,vcl_verilog_logic);
acc_close();
}

void counter () {
p_acc_value value;
sim_time = tf_gettime();
string i_reset = acc_fetch_value(reset,"%b",value);
string i_enable = acc_fetch_value(enable,"%b",value);
string i_count = acc_fetch_value(dut_count,"%b",value);
string i_clk = acc_fetch_value(clk, "%b",value);
int size_in_bits= acc_fetch_size (dut_count);
int tb_count = 0;
// Counter function goes here
if (*i_reset == *high) {
count = 0;
io_printf("%d, dut_info : Counter is reset\n", sim_time);
}
else if ((*i_enable == *high) && (*i_clk == *high)) {
if ( count == 15 ) {
count = 0;
} else {
count = count + 1;
}
}
// Counter Checker function goes checker logic goes here
if ((*i_clk != *high) && (*i_reset != *high)) {
tb_count = pli_conv(i_count,size_in_bits);
if (tb_count != count) {
io_printf("%d, dut_error : Expect value %d, Got value %d\n",
sim_time, count, tb_count);
tf_dofinish();
} else {
io_printf("%d, dut_info  : Expect value %d, Got value %d\n",
sim_time, count, tb_count);
}
}
}

// Multi-bit vector to integer conversion.
int pli_conv (string in_string, int no_bits) {
int conv = 0;
int i = 0;
int j = 0;
int bin = 0;
for ( i = no_bits-1; i >= 0; i = i - 1) {
if (*(in_string + i) == 49) {
bin = 1;
} else if (*(in_string + i) == 120) {
io_printf ("%d, Warning : X detected\n", sim_time);
bin = 0;
} else if (*(in_string + i) == 122) {
io_printf ("%d, Warning : Z detected\n", sim_time);
bin = 0;
} else {
bin = 0;
}
conv = conv + (1 << j)*bin;
j ++;
}
return conv;
}


View Code
因为C代码调用了PLI系统函数,所以需要包含头文件"acc_user.h",acc_user.h可以在vcs安装目录下找到,需要使用vcs命令参数指定头文件路径,

VCS命令如下:

vcs +v2k -R -P pli_counter.tab pli_counter_tb.v counter.v pli_full_example.c -CFLAGS "-g -l$VCS_HOME/linux/lib" +acc+3


-CFLAG 命令参数用于向C编译器传递C编译参数,因为C代码使用了访问程序集acc routines,还需要加上 +acc+3;+v2k是因为代码使用了verilog2001语法。

-P参数和上面例子一样,指定tab文件,不同的是,此时需要增加访问属性acc,r为读,w为写,pli_counter.tab如下:

$counter_monitor call=counter_monitor acc=rw:*


运行结果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: