Co-Simulation of C with Verilog in VCS

2024-05-08 06:18
文章标签 verilog co vcs simulation

本文主要是介绍Co-Simulation of C with Verilog in VCS,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Co-Simulation of C with Verilog in VCS

Introduction

Verilog PLI(编程语言接口)是一种从Verilog代码调用C或C ++函数的机制。

&#8195 在Verilog代码中调用的函数称为系统调用。 内置系统调用的示例是$ display,$ stop,$ random。PLI允许用户创建自定义系统调用,Verilog语法不允许我们做。 其中一些是:
- Power analysis
- Code coverage tools
- Modifying the Verilog simulation data structure - more accurate delays.
- Custom output displays.
- Co-simulation
- Design debug utilities
- Simulation analysis.
- C-model interface to accelerate simulation.
- Testbench modeling

为了实现PLI的这些应用,C代码应该可以访问Verilog simulator的内部数据结构。 为了方便这个Verilog PLI提供了所谓的acc 程序或简单的访问程序 。

有第二组程序,称为tf程序,或简单的任务和函数程序。tf和acc是PLI 1.0程序,非常广泛而且非常老。 Verilog 2001最新版本介绍的下一组例程称为vpi程序。 这些是小而清澈的PLI例程,因此是新版PLI 2.0。

您可以获得Verilog 2001 LRM或PLI 1.0 IEEE文档,了解提供的每个功能的详细信息。 Verilog IEEE LRM的写法是这样一种方式,即具有硬件背景的人可以理解。 如果您无法掌握上述IEEE文档,那么您可以购买图书部分列出的PLI书籍。

How PLI works

  1. 编写C/C++函数源代码
  2. 编译源代码生成共享文件(在Windows中既是*.DLL, UINX系统中*.so),比如在VCS仿真器中允许静态连接。
  3. verilog程序中调用C函数(通常是testbench中)
  4. 根据仿真器,在verilog代码编译过程中将C/C++函数细节传递给仿真器
  5. 一旦链接就像任何其他Verilog仿真一样运行仿真器
    @ Simulator|center

在仿真器执行Verilog代码期间,只要模拟器遇到用户定义的系统任务(以$开头),执行控制就被传递给PLI例程(C / C ++函数)

编写PLI应用程序

我们看到的例子太基础了,对于任何实际的目的都没有好处。 我们来考虑我们的臭名昭着的计数器示例,并在C中编写DUT参考模型和Checker,并将其链接到Verilog Testbench。 首先列出使用PLI编写C模型的要求。
- 意味着每当输入信号有变化时,调用C模型
- 意味着将Verilog代码中的变化信号(或任何其他信号)值转换为C代码
- 意味着从C代码驱动Verilog代码内的任何信号值

PLI应用程序规范

我们使用PLI定义我们臭名昭着的计数器测试平台的要求。 我们将把我们的PLI函数称为$counter_monitor。
- 用C实现计数器逻辑
- 用C语言实现checker逻辑
- 无论何时检查程序出现故障,终止仿真
@|center

调用C语言函数

在C写一个计数器是如此的酷,但是什么时候增加计数器值? 那么我们需要监视时钟信号的变化。每当时钟变化时,需要执行计数器功能。 这可以通过使用下面的例程来实现.
- 使用acc_vcl_add程序,其语法可以在Verilog PLI LRM中找到
acc_vcl_add程序基本上允许我们监视信号列表,并且每当监视信号变化时,它调用用户定义的功能(称为消费者C程序)。 VCL程序有四个参数:
- Handle to the monitored object
- Consumer C routine to call when the object value changes
- String to be passed to the consumer C routine
- Predefined VCL flags: vcl_verilog_logic for logic monitoring vcl_verilog_strength for strength monitoring
acc_vcl_add(net, display_net, netname, vcl_verilog_logic);

C源码基本部分

Counter_monitor是我们的C函数,它将从Verilog Testbench中调用。 对于任何其他C代码,我们需要包含特定于我们开发的应用程序的头文件。 在我们的例子中,我们需要包含acc程序包含文件。

访问程序acc_initialize初始化访问程序环境,并且必须在C语言应用程序调用任何其他访问例程之前调用。 在退出调用访问例程的C语言应用程序之前,还需要在程序结束时调用acc_close来退出访问例程环境。

// counter.c
#include "acc_user.h"handle clk ;handle reset ;handle enable ;handle dut_count ;void counter ();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 () {io_printf("Clock changed state\n");}

可以用handle访问verilog对象。handle是预定义的数据类型,它是指向设计层次结构中特定对象的指针。每个handle传达访问c程序有关可访问对象的唯一实例的信息; 信息是关于对象类型,以及如何和在何处查找关于对象的数据。但是我们如何传递特定的对象信息来处理? 那么我们可以通过多种方法来做到这一点,但是现在我们将把Verilog作为参数传递给$ counter_monitor:这些参数可以通过acc_handle_tfarg()例程在C程序中访问。 参数是代码中的数字。

所以clk = acc_handle_tfarg(1)基本上使clk的第一个参数的句柄通过; 同样,我们分配所有的手柄。 现在我们可以使用cc_vcl_add(clk,counter,null,vcl_verilog_logic)程序将clk添加到需要监视的信号列表中。 这里clk是handle(句柄), counter是当clk更改时执行的用户函数。

verilog源代码

以下是计数器示例的简单测试平台的代码。 我们使用下面的代码中所示的语法来调用C函数。 如果传递的对象是一个即时的,那么它应该在双引号内传递。 由于我们所有的对象都是网络或电线,所以没有必要在双引号内传递它们。

//pli_counter_tb.vmodule counter_tb();reg enable;reg reset;reg clk_reg;wire clk;wire [3:0] count;initial beginenable = 0;clk_reg = 0;reset = 0;$display("%g , Asserting reset", $time);#10  reset = 1;#10  reset = 0;$display ("%g, Asserting Enable", $time);#10  enable = 1;#55  enable = 0;$display ("%g, Deasserting Enable", $time);#1  $display ("%g, Terminating Simulator", $time);#1  $finish;endalways begin#5  clk_reg =  ! clk_reg;endassign clk = clk_reg;initial begin$counter_monitor (counter_tb.clk, counter_tb.reset, counter_tb.enable, counter_tb.count);endcounter U(.clk (clk),.reset (reset),.enable (enable),.count (count));endmodule
// counter.v
module counter(
input  wire                      clk,
input  wire                      reset,
input  wire                      enable,
output reg [3:0]                 count 
);
always@(posedge clk or negedge reset) beginif(~reset)count <= 4'b0000;else if(enable)count <= count + 1'b1;
end
endmodule

VCS Simulation
根据使用的仿真工具,编译和运行阶段各不相同。 在VCS运行上面的代码时输入:

vcs -full64 -R -P pli_counter.tab pli_counter_tb.v counter.v pli_full_example.c -CFLAGS "-g -I$VCS_HOME/`vcs -platform`/lib" +acc+3

您将获得以下输出:

0 , Asserting resetClock changed stateClock changed stateClock changed state20, Asserting EnableClock changed stateClock changed stateClock changed stateClock changed stateClock changed stateClock changed stateClock changed stateClock changed stateClock changed stateClock changed stateClock changed stateClock changed stateClock changed state85, Deasserting EnableClock changed state86, Terminating Simulator

完整C代码

所以现在我们看到当时钟发生变化时我们的函数被调用,我们可以编写计数器代码。 但是等等,有一个问题:每次计数器函数退出时,局部变量都会丢失它的值。 有几种方法可以保存变量状态。
- 将计数器变量声明为全局变量
- 使用tf_setworkarea()和tf_getworkarea()程序来存储和恢复局部变量的值。

由于我们只有一个变量,我们可以使用第一个解决方案。 即声明计数为全局变量。

要为计数器写入一个等效的模型,DUT需要时钟,复位,使能信号输入,代码检查器需要DUT计数的输出。需要使用PLI程序— acc_fetch_value(handle,”format”)—读取Verilog代码中的值。

但是返回的值是一个字符串,因此如果使用此程序读取多位向量信号,则需要将其转换为整数。 pli_conv是执行此转换的函数。 当DUT和TB计数值不匹配或换句话说,当模拟不匹配发生时,程序tf_dofinish()用于终止仿真。

#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 hereif (*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 hereif ((*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;
}

可以使用下面段落中所述的仿真器来编译和模拟上述代码。
- VCS
- Modelsim

VCS

使用VCS模拟器,您需要创建一个tab文件。 对于我们的示例,tab文件如下所示:

$counter_monitor call=counter_monitor acc=rw:*

这里$counter_monitor是用于Verilog代码的用户定义函数的名称,call = counter_monitor是在Verilog中调用$counter_monitor时调用的C函数。 acc = rw:告诉我们正在使用具有读和写访问模拟器内部数据的访问例程。 :表示这应适用于设计中的所有模块。

VCS PLI TAB FILE

PLI tab文件(也称为pli.tab文件)用于:
- 将用户定义的系统任务和系统功能与PLI应用程序中的功能相关联。 这使VCS MX能够在编译或执行系统任务或功能时调用这些功能。
- 限制由调试功能调用的PLI 1.0或PLI 2.0功能的范围和操作。

Syntax
$name PLI_specifications [access_capabilities]
其中,
$name: 指定用户自定义的函数或任务
PLI_specifications: 指定一个或多个规范,如C函数的名称(强制性),返回值的大小(仅限用户定义的系统函数),等等。
access_capabilities: 指定PLI应用程序中定义的功能的访问功能。 使用它来控制PLI 1.0或PLI 2.0功能访问设计层次结构的能力。

PLI_specifications

call=function
&#8195 指定在PLI应用程序中定义的函数的名称。这是强制的。
check=function
&#8195指定check函数的名称
misc=function 指
&#8195定misc函数的名称
data=integer
&#8195指定作为调用,检查和misc函数的第一个参数传递的值。 默认值为0
….(其他,见VCSMX UG)

access_capabilities

你可以在PLI tab文件中指定访问功能,原因如下:
- PLI函数与用户定义的系统任务或系统函数相关联。 为此,请在用户定义的系统任务或系统函数及其PLI规范的名称之后,在PLI tab文件中的一行上指定访问功能
- 对于VCS MX可以使用的调试特性。 为此,只需在PLI tab文件的一行中指定访问功能,而不需要相关的用户定义系统任务或系统函数

指定访问功能的格式如下:
acc=|+=|-=|:=capabilities:module_names[+]|%CELL|%TASK|*
其中,
- acc——一行开始位置指定访问功能的关键字
- =|+=|-=|:——添加、删除、更改指定功能的操作
- capabilities:
&#8195 逗号分隔的访问功能列表。 您可以在PLI规范中为功能指定的功能如下:
- r — read
- rw — read_write
- wn — write value to nets
- cbk — to be called when named objects change value
- …..(见 VCSMX UG)
- module_names —— 模块标识符(或名称)的逗号分隔列表
- + — 指定添加,删除或更改访问功能,不仅是指定模块的实例,还包括实例在指定模块的实例下分层实例
- %CELL%
&#8195 启用,禁用或更改PLI功能在“celldefine compiler指令”下编译的模块定义的所有实例以及Verilog库目录和库文件中的所有模块定义(如指定)中使用访问功能的能力 使用-y和-v分析选项)
- %TASK%
&#8195 启用,禁用或更改(取决于运营商)PLI功能在包含与PLI功能相关联的用户定义的系统任务或系统功能的模块定义的所有实例中使用访问功能的能力。
- *
&#8195 启用,禁用或更改(取决于操作员)PLI功能在整个设计中使用访问功能的能力。 使用通配符可能会严重阻碍VCS MX的性能。

VCS中用于编译代码的命令行选项有:

vcs -R -P pli_counter.tab pli_counter_tb.v counter.v pli_full_example.c -CFLAGS "-g -I$VCS_HOME/`vcs -platform`/lib" +acc+3

finial ouput:

 0 , Asserting reset10, dut_info : Counter is reset15, dut_info : Counter is reset20, Asserting Enable20, dut_info  : Expect value 0, Got value 030, dut_info  : Expect value 0, Got value 040, dut_info  : Expect value 1, Got value 150, dut_info  : Expect value 2, Got value 260, dut_info  : Expect value 3, Got value 370, dut_info  : Expect value 4, Got value 480, dut_info  : Expect value 5, Got value 585, Deasserting Enable86, Terminating Simulator

这篇关于Co-Simulation of C with Verilog in VCS的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/969542

相关文章

数字电路专题:verilog 阻塞赋值和非阻塞赋值

verilog 阻塞赋值 和 非阻塞赋值 “=”阻塞赋值, ”<=”非阻塞赋值。阻塞赋值为执行完一条赋值语句,再执行下一条,可理解为顺序执行,而且赋值是立即执行; 非阻塞赋值可理解为并行执行,不考虑顺序,在 always 块语句执行完成后,才进行赋值。 如下面的阻塞赋值: //代码如下:module top(din,a,b,c,clk);input din;input clk;out

systemverilog、verilog的部分常用内部函数

1. $ceil 作用:将给定的实数或浮点数向上取整。示例:$ceil(3.2) 返回 4。 2. $floor 作用:将给定的实数或浮点数向下取整。示例:$floor(3.9) 返回 3。 3. $value$plusargs 作用:从命令行读取传递给仿真器的参数。格式:$value$plusargs("格式", 变量),格式 用来匹配命令行的参数,变量 是用来存储匹配到的值。示例:$

Verilog语法+:和-:有什么用?

Verilog语法+:和-:主要用于位选择,可以让代码更简洁。 一、位选择基础 在Verilog中,位选择可以通过直接索引来实现,例如: reg [7:0] data; wire select_a; wire [2:0] select_b;   assign select_a = data[3]; assign select_b = data[2:0]; 二、+: 和 -: 语法

Verilog和Matlab实现RGB888互转YUV444

文章目录 一、色彩空间1.1 RGB色彩空间1.2 CMYK色彩空间1.3 YUV色彩空间 二、色彩空间转换公式2.1 RGB转CMYK2.2 CMYK转RGB2.3 RGB888转YUV4442.4 YUV444转RGB888 三、MATLAB实现RGB888转YUV4443.1 matlab代码3.2 matlab结果 四、Verilog实现RGB888转YUV444 一、

FPGA第 10 篇,Verilog 中的运算符和分支语句

前言 我们都知道 Verilog 作为一种硬件描述语言,不仅用于设计和仿真数字电路,还为开发者提供了强大的工具,用于控制和优化硬件的行为。其中运算符和分支语句是 Verilog 中的两大核心组成部分,它们负责执行逻辑操作、数学运算以及决定逻辑流的控制。 运算符 在 Verilog 中用于进行各种计算和逻辑操作,它们类似于软件编程中的运算符,但特定于硬件操作,涵盖了算术、逻辑、按位操作、移位操

verilog仿真激励

简介         本章节主要描述verilog激励仿真函数的介绍。 initial         主要针对寄存器初始化值,基本所有仿真都会使用到该语句,使用如下: initial beginsys_clk = 'd0; sys_rst_n = 'd0; #2000;sys_rst_n = 'd1; end repeat         重复有限次数地执行一段代码,使用如下:

Fourier TransformHilbert TransformRelated Function's MATLAB Simulation in Signal Processing

Fourier Transform:对于平稳信号有着明确的物理意义,频域反应各频率成分,可用于滤除高频噪声分量Hilbert  Transform:平稳信号分析,在信号分析和贷通信号中,理论和实用价值,SSB Walsh-Hadamand Transform:基于非正弦正交基的信号变换 ---First Of ALL---在数学与

verilog语法错误

1.写敏感列表always(@posedge or @negedge ext_rst_n),语法检查报错ERROR:HDLCompiler:806 - "E:\ISE14.6\Project\sp6\sp6ex1\source_code\sp6.v" Line 27: Syntax error near "(".仔细检查应为always @(posedge ext_clk_25m or neged

huggingface.co 无法访问问题换源解决

原因 Hugging Face是一家美国公司,专门开发用于构建机器学习应用的工具。该公司的代表产品是其为自然语言处理应用构建的transformers库,以及允许用户共享机器学习模型和数据集的平台。 既然是美国的公司,自然涉及到科学上网的问题, 但是科学上网并不稳定。 报错信息 requests.exceptions.ConnectionError: (MaxRetryError("HTTP

ubuntu,vivado HLS C simulation启动失败

在ubuntu18.04环境下安装了vivado2018.3版本,运行C simulation时失败,原因是缺少组件,解决办法: sudo apt-get install tofrodos gawk xvfb git libncurses5-dev tftpd zlib1g-dev zlib1g-dev:i386 libssl-dev flex bison chrpath socat autoc