verilog PLI简介

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:*

运行结果如下:

 

posted @ 2015-12-19 22:23  柯西不是我  阅读(2609)  评论(0编辑  收藏  举报