先前我们所看的例子太基础也没有实际用途。让我们看计数器的例子,写一个DUT的参考模型以及用C写的Checker程序,并把它链入到Verilog的Testbench。首先列出我们要用PLI写C模型的要求。
- 调用C模型的方法,当输入信号有任何变化时;
- 获得verilog代码中改变了的信号值或者任何C代码内部的信号的方法
- 通过C代码驱动Verilog内部值
VerilogPLI提供一系列满足以上要求的程序(函数)。
PLI 应用的SPEC
我们定义使用PLI的计数器testbench的要求。 该PLI函数我们称作$counter_monitor
- 用C实现一个计数器逻辑
- 用C实现一个检测逻辑
- 当checker失败时,终止仿真
调用C函数
用C写计数器非常简单,但是什么时候我们要增加计数器的值呢?所以我们就需要监测时钟信号的变化(顺便说一句,通过verilog代码驱动复位和时钟信号是个好习惯)一旦时钟发生变化,计数器函数就要被执行。这可以通过以下的函数实现:
使用acc_vcl_add程序。该函数的语法可以查阅Verilog PLI LRM基本上acc_vcl_add程序能检测许多信号,一旦任何一个信号改变就会调用用户定义的函数(也就是定制的C程序)。VCL程序有4个自变量:
- 需要监测对象的句柄
- 用户C程序,当对象值变化就会调用它
- 传递给C程序的字符串
- 预定义的VCL标志:vcl_verilog_logic -- 逻辑监测
vcl_verilog_strength -- 强度监测
acc_vcl_add(net, display_net, netname, vcl_verilog_logic);
C代码 - 基础
Counter_monitor是我们在verilog testbench中将要调用的C函数。就像任何其他的C代码,我们需要包含针对我们所要开发的特定的应用的头文件。我们这里,需要包含acc 程序include文件。
access函数 acc_initialize 初始化access程序的环境,在程序调用任何的access程序前必须要调用的。在退出调用access程序的C应用程序前,在程序的最后必须要退出access程序环境,调用acc_close。
1 |
#include "acc_user.h" |
|
2 |
|
|
3 |
handle clk ; |
|
4 |
handle reset ; |
|
5 |
handle enable ; |
|
6 |
handle dut_count ; |
|
7 |
void counter (); |
|
8 |
|
|
9 |
void counter_monitor() { |
|
10 |
|
acc_initialize(); |
11 |
|
clk = acc_handle_tfarg(1); |
12 |
|
reset = acc_handle_tfarg(2); |
13 |
|
enable = acc_handle_tfarg(3); |
14 |
|
dut_count = acc_handle_tfarg(4); |
15 |
|
acc_vcl_add(clk,counter,null,vcl_verilog_logic); |
16 |
|
acc_close(); |
17 |
} |
|
18 |
|
|
19 |
void counter () { |
|
20 |
|
io_printf( "Clock changed staten" ); |
21 |
} |
为了能accessing verilog对象,我们使用句柄。句柄是指预定义的数据类型,它是是设计层次中某个对象的指针。每个句柄传递给access函数有关唯一可以accessible对象的类型信息,以及如何哪里能找到有关此对象的信息。但是,我们如何把某个特定对象的信息给句柄呢?我们可以通过许多途径,但是现在我们用verilog采用参数传递的方式给$counter_monitor, 这些参数在C程序中可以采用acc_handle_tfarg()函数获得,这里自变量和代码中一样是数字。
因此clk = acc_handle_tfarg(1) 使得clk作为第一参数传递的句柄。类似我们给所有的句柄赋值。现在我们可以把clk加入我们需要检测的信号列表中,acc_vcl_add(clk,counter,null,vcl_verilog_logic)。这里clk是句柄,counter是当clk发生变化时,需要执行的用户函数。
函数counter()就不作任何解释了,它是类似于hello world简单的代码。
Verilog 代码
下面是计数器例子的简单的testbench。We call the C-function using the syntax shown in code below. If object thats been passed is a instant, then it should be passed inside double quotes. Since all our objects are nets or wires, there is no need to pass them inside double quote.
1 |
module counter_tb(); |
|||
2 |
reg enable; |
|||
3 |
reg reset; |
|||
4 |
reg clk_reg; |
|||
5 |
wire clk; |
|||
6 |
wire [3:0] count; |
|||
7 |
|
|||
8 |
initial begin |
|||
9 |
|
enable = 0; |
||
10 |
|
clk_reg = 0; |
||
11 |
|
reset = 0; |
||
12 |
|
$display( "%g , Asserting reset"
|