0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

FPGA中测试文件编写中的激励仿真

电子工程师 来源:CSDN 作者:长弓的坚持 2021-04-02 18:27 次阅读

大家好,又到了每日学习的时间了,今天我们来聊一聊FPGA中测试文件编写的相关知识,聊一聊激励仿真

‍ 1. 激励的产生

对于testbench而言,端口应当和被测试的module一一对应。端口分为input,output和inout类型产生激励信号的时候,input对应的端口应当申明为reg, output对应的端口申明为wire,inout端口比较特殊,下面专门讲解。

1)直接赋值。

一般用initial块给信号赋初值,initial块执行一次,always或者forever表示由事件激发反复执行。

举例,一个module

module exam();

reg rst_n;

reg clk;

reg data;

initial

begin

clk=1‘b0;

rst=1’b1;

#10

rst=1‘b0;

#500

rst=1’b1;

end

always

begin

#10

clk=~clk;

end

大家应该注意到有个#符号,该符号的意思是指延迟相应的时间单位。该时间单位由timscale决定。一般在testbench的开头定义时间单位和仿真 精度,比如`timescale 1ns/1ps,前面一个是代表时间单位,后面一个代表仿真时间精度。以上面的例子而言,一个时钟周期是20个单位,也就是20ns。而仿真时间精度的概 念就是,你能看到1.001ns时对应的信号值,而假如timescale 1ns/1ns,1.001ns时候的值就无法看到。对于一个设计而言,时间刻度应该统一,如果设计文件和testbench里面的时间刻度不一致,仿真 器默认以testbench为准。一个较好的办法是写一个global.v文件,然后用include的办法,可以防止这个问题。

对于反复执行的操作,可写成task,然后调用,比如

task load_count;

input [3:0] load_value;

begin

@(negedge clk_50);

$display($time, “ 《《 Loading the counter with %h 》》”, load_value);

load_l = 1’b0;

count_in = load_value;

@(negedge clk_50);

load_l = 1’b1;

end

endtask //of load_count

initial

begin

load_count(4’hA); // 调用task

end

其他像forever,for,function等等语句用法类似,虽然不一定都能综合,但是用在testbench里面很方便,大家可以自行查阅参考文档

2) 文件输入

有时候,需要大量的数据输入,直接赋值的话比较繁琐,可以先生成数据,再将数据读入到寄存器中,需要时取出即可。用 $readmemb系统任务从文本文件中读取二进制向量(可以包含输入激励和输出期望值)。$readmemh 用于读取十六进制文件。例如:

reg [7:0] mem[1:256] // a 8-bit, 256-word 定义存储器mem

initial $readmemh ( “E:/readhex/mem.dat”, mem ) // 将.dat文件读入寄存器mem中

initial $readmemh ( “E:/readhex/mem.dat”, mem, 128, 1 ) // 参数为寄存器加载数据的地址始终

2. 查看仿真结果

对于简单的module来说,要在modelsim的仿真窗口里面看波形,就用add wave 。.命令

比如,testbench的顶层module名叫tb,要看时钟信号,就用add wave tb.clk

要查看所有信号的时候,就用 add wave /*

当然,也可以在workspace下的sim窗口里面右键单击instance来添加波形

对于复杂的仿真,免不了要记录波形和数据到文件里面去。

1)波形文件记录

常见的波形文件一般有两种,vcd和fsdb,debussy是个很好的工具,支持fsdb,所以最好是modelsim+debussy的组合

默认情况下,modelsim不认识fsdb,所以需要先装debussy,再生成fsdb文件。

$dumpfile和$dumpvar是verilog语言中的两个系统任务,可以调用这两个系统任务来创建和将指定信息导入VCD文件。

对于fsdb文件来说,对应的命令是fsdbDumpfile,dumpfsdbvars

(什么是VCD文件? 答:VCD文件是在对设计进行的仿真过程中,记录各种信号取值变化情况的信息记录文件。EDA工具通过读取VCD格式的文件,显示图形化的仿真波形,所以,可以把VCD文件简单地视为波形记录文件。)下面分别描述它们的用法并举例说明之。

$dumpfile系统任务:为所要创建的VCD文件指定文件名。

举例(“//”符号后的内容为注释文字):

initial

$dumpfile (“myfile.dump”); //指定VCD文件的名字为myfile.dump,仿真信息将记录到此文件

$dumpvar系统任务:指定需要记录到VCD文件中的信号,可以指定某一模块层次上的所有信号,也可以单独指定某一个信号。

典型语法为$dumpvar(level, module_name); 参数level为一个整数,用于指定层次数,参数module则指定要记录的模块。整句的意思就是,对于指定的模块,包括其下各个层次(层次数由 level指定)的信号,都需要记录到VCD文件中去。

举例:

initial

$dumpvar (0, top); //指定层次数为0,则top模块及其下面各层次的所有信号将被记录

initial

$dumpvar (1, top); //记录模块实例top以下一层的信号

//层次数为1,即记录top模块这一层次的信号

//对于top模块中调用的更深层次的模块实例,则不记录其信号变化

initial

$dumpvar (2, top); //记录模块实例top以下两层的信号

//即top模块及其下一层的信号将被记录

假设模块top中包含有子模块module1,而我们希望记录top.module1模块以下两层的信号,则语法举例如下:

initial

$dumpvar (2, top.module1); //模块实例top.module1及其下一层的信号将被记录

假设模块top包含信号signal1和signal2(注意是变量而不是子模块), 如我们希望只记录这两个信号,则语法举例如下:

initial

$dumpvar (0, top.signal1, top.signal2); //虽然指定了层次数,但层次数是不影响单独指定的信号的

//即指定层次数和单独指定的信号无关

我们甚至可以在同一个$dumpvar的调用中,同时指定某些层次上的所有信号和某个单独的信号,假设模块top包含信号signal1,同时包含有子模 块module1,如果我们不但希望记录signal1这个独立的信号,而且还希望记录子模块module1以下三层的所有信号,则语法举例如下:

initial

$dumpvar (3, top.signal1, top.module1); //指定层次数和单独指定的信号无关

//所以层次数3只作用于模块top.module1, 而与信号

top.signal1无关

上面这个例子和下面的语句是等效的:

initial

begin

$dumpvar (0, top.signal1);

$dumpvar (3, top.module1);

end

$dumpvar的特别用法(不带任何参数):

initial

$dumpvar; //无参数,表示设计中的所有信号都将被记录

最后,我们将$dumpfile和$dumpvar这两个系统任务的使用方法在下面的例子中综合说明,假设我们有一个设计实例,名为 i_design,此设计中包含模块module1,模块module1下面还有很多层次,我们希望对这个设计进行仿真,并将仿真过程中模块 module1及其以下所有层次中所有信号的变化情况,记录存储到名为mydesign.dump的VCD文件中去,则例示如下:

initial

begin

$dumpfile (“mydesign.dump”); //指定VCD文件名为mydesign.dump

$dumpvar (0, i_design.module1); //记录i_design.module1模块及其下面层次中所有模块的所有信号

end

对于生成fsdb文件而言,也是类似的

initial

begin

$fsdbDumpfile(“tb_xxx.fsdb”);

$fsdbDumpvars(0,tb_xxx);

end

2)文件输出结果

integer out_file; // out_file 是一个文件描述,需要定义为 integer类型

out_file = $fopen ( “ cpu.data ” ); // cpu.data 是需要打开的文件,也就是最终的输出文本

设计中的信号值可以通过$fmonitor, $fdisplay,$fwrite

其中$fmonitor只要有变化就一直记录,$fdisplay和$fwrite需要触发条件才记录

例子:

initial begin

$fmonitor(file_id, “%m: %t in1=%d o1=%h”, $time, in1, o1);

end

always@(a or b)

begin

$fwrite(file_id,“At time%t a=%b b=%b”,$realtime,a,b);

end

3 testbench的技巧

1)。如果激励中有一些重复的项目,可以考虑将这些语句编写成一个task,这样会给书写和仿真带来很大方便。例如,一个存储器的testbench的激励可以包含write,read等task。

2)。如果DUT中包含双向信号(inout),在编写testbench时要注意。需要一个reg变量来表示其输入,还需要一个wire变量表示其输出。

3)。如果initial块语句过于复杂,可以考虑将其分为互补相干的几个部分,用数个initial块来描述。在仿真时,这些initial块会并发运行。这样方便阅读和修改。

4)。每个testbench都最好包含$stop语句,用以指明仿真何时结束。

5)。加载测试向量时,避免在时钟的上下沿变化,比如数据最好在时钟上升沿之前变化,这也符合建立时间的要求。

4.一个简单的例子

module counter (clk, reset, enable, count);

input clk, reset, enable;

output [3:0] count;

reg [3:0] count;

always @ (posedge clk)

if (reset == 1‘b1) begin

count 《= 0;

end else if ( enable == 1’b1) begin

count 《= count + 1;

end

endmodule

testbench

module counter_tb;

reg clk, reset, enable;

wire [3:0] count;

counter U0 (

.clk (clk),

.reset (reset),

.enable (enable),

.count (count)

);

initial begin

clk = 0;

reset = 0;

enable = 0;

end

always

#5 clk = ! clk;

initial begin

$dumpfile (“counter.vcd”);

$dumpvars;

end

initial begin

$display(“ time, clk, reset, enable, count”);

$monitor(“‰d, ‰b, ‰b, ‰b, ‰d”,$time, clk,reset,enable,count);

end

initial

#100 $finish;

//Rest of testbench code after this line

endmodule

5 双向端口

芯片外部引脚很多都使用inout类型的,为的是节省管腿。一般信号线用做总线等双向数据传输的时候就要用到INOUT类型了。就是一个端口同时做输入和 输出。inout在具体实现上一般用三态门来实现。三态门的第三个状态就是高阻‘Z’。当inout端口不输出时,将三态门置高阻。这样信号就不会因为两端同时 输出而出错了,更详细的内容可以搜索一下三态门tri-state的资料

1 使用inout类型数据,可以用如下写法:

inout data_inout;

input data_in;

reg data_reg;//data_inout的映象寄存器

reg link_data;

assign data_inout=link_data?data_reg:1’bz;//link_data控制三态门

//对于data_reg,可以通过组合逻辑或者时序逻辑根据data_in对其赋值。通过控制link_data的高低电平,从而设置data_inout是输出数据还是处于高阻态,如果处于高阻态,则此时当作输入端口使用.link_data可以通过相关电路来控制。

2 编写测试模块时,对于inout类型的端口,需要定义成wire类型变量,而其它输入端口都定义成reg类型,这两者是有区别的。

当上面例子中的data_inout用作输入时,需要赋值给data_inout,其余情况可以断开。此时可以用assign语句实现:assign data_inout=link?data_in_t:1’bz;其中的link ,data_in_t是reg类型变量,在测试模块中赋值。

另外,可以设置一个输出端口观察data_inout用作输出的情况:

Wire data_out;

Assign data_out_t=(!link)?data_inout:1’bz;

else,in RTL

inout use in top module(PAD)

dont use inout(tri) in sub module

也就是说,在内部模块最好不要出现inout,如果确实需要,那么用两个port实现,到顶层的时候再用三态实现。理由是:在非顶层模块用双向口的话,该 双向口必然有它的上层跟它相连。既然是双向口,则上层至少有一个输入口和一个输出口联到该双向口上,则发生两个内部输出单元连接到一起的情况出现,这样在 综合时往往会出错。

对双向口,我们可以将其理解为2个分量:一个输入分量,一个输出分量。另外还需要一个控制信号控制输出分量何时输出。此时,我们就可以很容易地对双向端口建模。

例子:

CODE:

module dual_port (

inout_pin,

);

inout inout_pin;

wire inout_pin;

wire input_of_inout;

wire output_of_inout;

wire out_en;

assign input_of_inout = inout_pin;

assign inout_pin = out_en ? output_of_inout : 高阻;

endmodule

可见,此时input_of_inout和output_of_inout就可以当作普通信号使用了。

在仿真的时候,需要注意双向口的处理。如果是直接与另外一个模块的双向口连接,那么只要保证一个模块在输出的时候,另外一个模块没有输出(处于高阻态)就可以了。

如果是在ModelSim中作为单独的模块仿真,那么在模块输出的时候,不能使用force命令将其设为高阻态,而是使用release命令将总线释放掉

很多初学者在写testbench进行仿真和验证的时候,被inout双向口难住了。仿真器老是提示错误不能进行。下面是我个人对inout端口写 testbench仿真的一些总结,并举例进行说明。在这里先要说明一下inout口在testbench中要定义为wire型变量。

先假设有一源代码为:

module xx(data_inout , 。.);

inout data_inout;

assign data_inout=(! link)?datareg:1‘bz;

endmodule

方法一:使用相反控制信号inout口,等于两个模块之间用inout双向口互连。这种方法要注意assign 语句只能放在initial和always块内。

module test();

wire data_inout;

reg data_reg;

reg link;

initial begin

end

assign data_inout=link?data_reg:1’bz;

endmodule

方法二:使用force和release语句,但这种方法不能准确反映双向端口的信号变化,但这种方法可以反在块内。

module test();

wire data_inout;

reg data_reg;

reg link;

#xx; //延时

force data_inout=1‘bx; //强制作为输入端口

。..

#xx;

release data_inout; //释放输入端口

endmodule

很多读者反映仿真双向端口的时候遇到困难,这里介绍一下双向端口的仿真方法。一个典型的双向端口如图1所示。

其中inner_port与芯片内部其他逻辑相连,outer_port为芯片外部管脚,out_en用于控制双向端口的方向,out_en为1时,端口为输出方向,out_en为0时,端口为输入方向。

用Verilog语言描述如下:

module bidirection_io(inner_port,out_en,outer_port);

input out_en;

inout[7:0] inner_port;

inout[7:0] outer_port;

assign outer_port=(out_en==1)?inner_port:8’hzz;

assign inner_port=(out_en==0)?outer_port:8‘hzz;

endmodule

用VHDL语言描述双向端口如下:

library ieee;

use IEEE.STD_LOGIC_1164.ALL;

entity bidirection_io is

port ( inner_port : inout std_logic_vector(7 downto 0);

out_en : in std_logic;

outer_port : inout std_logic_vector(7 downto 0) );

end bidirection_io;

architecture behavioral of bidirection_io is

begin

outer_port《=inner_port when out_en=’1‘ else (OTHERS=》’Z‘);

inner_port《=outer_port when out_en=’0‘ else (OTHERS=》’Z‘);

end behavioral;

仿真时需要验证双向端口能正确输出数据,以及正确读入数据,因此需要驱动out_en端口,当out_en端口为1时,testbench驱动 inner_port端口,然后检查outer_port端口输出的数据是否正确;当out_en端口为0时,testbench驱动 outer_port端口,然后检查inner_port端口读入的数据是否正确。由于inner_port和outer_port端口都是双向端口(在 VHDL和Verilog语言中都用inout定义),因此驱动方法与单向端口有所不同。

验证该双向端口的testbench结构如图2所示。

这是一个self-checking testbench,可以自动检查仿真结果是否正确,并在Modelsim控制台上打印出提示信息。图中Monitor完成信号采样、结果自动比较的功能。

testbench的工作过程为

1)out_en=1时,双向端口处于输出状态,testbench给inner_port_tb_reg信号赋值,然后读取outer_port_tb_wire的值,如果两者一致,双向端口工作正常。

2)out_en=0时,双向端口处于输如状态,testbench给outer_port_tb_reg信号赋值,然后读取inner_port_tb_wire的值,如果两者一致,双向端口工作正常。

用Verilog代码编写的testbench如下,其中使用了自动结果比较,随机化激励产生等技术。

`timescale 1ns/10ps

module tb();

reg[7:0] inner_port_tb_reg;

wire[7:0] inner_port_tb_wire;

reg[7:0] outer_port_tb_reg;

wire[7:0] outer_port_tb_wire;

reg out_en_tb;

integer i;

initial

begin

out_en_tb=0;

inner_port_tb_reg=0;

outer_port_tb_reg=0;

i=0;

repeat(20)

begin

#50

i=$random;

out_en_tb=i[0]; //randomize out_en_tb

inner_port_tb_reg=$random; //randomize data

outer_port_tb_reg=$random;

end

end

//**** drive the ports connecting to bidirction_io

assign inner_port_tb_wire=(out_en_tb==1)?inner_port_tb_reg:8’hzz;

assign outer_port_tb_wire=(out_en_tb==0)?outer_port_tb_reg:8‘hzz;

//instatiate the bidirction_io module

bidirection_io bidirection_io_inst(.inner_port(inner_port_tb_wire),

.out_en(out_en_tb),

.outer_port(outer_port_tb_wire));

//***** monitor ******

always@(out_en_tb,inner_port_tb_wire,outer_port_tb_wire)

begin

#1;

if(outer_port_tb_wire===inner_port_tb_wire)

begin

$display(“ **** time=%t ****”,$time);

$display(“OK! out_en=%d”,out_en_tb);

$display(“OK! outer_port_tb_wire=%d,inner_port_tb_wire=%d”,

outer_port_tb_wire,inner_port_tb_wire);

end

else

begin

$display(“ **** time=%t ****”,$time);

$display(“ERROR! out_en=%d”,out_en_tb);

$display(“ERROR! outer_port_tb_wire != inner_port_tb_wire” );

$display(“ERROR! outer_port_tb_wire=%d, inner_port_tb_wire=%d”,

outer_port_tb_wire,inner_port_tb_wire);

end

end

endmodule

原文标题:简谈FPGA Verilog testbench

文章出处:【微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1603

    文章

    21328

    浏览量

    593267
  • 文件
    +关注

    关注

    1

    文章

    540

    浏览量

    24402

原文标题:简谈FPGA Verilog testbench

文章出处:【微信号:gh_9d70b445f494,微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    FPGA入门必备:Testbench仿真文件编写实例详解

    编写完HDL代码后,往往需要通过仿真软件Modelsim或者Vivadao自带的仿真功能对HDL代码功能进行验证,此时我们需要编写Testbench
    发表于 04-29 10:43 38次阅读

    在自己编写的.c文件,如何调用main.c已经定议的 \" hspi1 \"?

    , Rx_Data, 7,1000);//在mian.c中使用“ hspi1”没有问题 while(1) { } } 但在自己编写的 .c文件不能使用main.c 的 \" hsp
    发表于 04-24 07:49

    【分享】基于Easygo仿真平台的三电机实时仿真测试应用

    调用电机库的多种模块可直接实现多电机仿真功能,大大降低多电机仿真测试的门槛和设备投入与时间成本。基于EasyGo Machine电机库
    发表于 04-09 16:49

    多电机仿真篇丨双电机实时仿真测试应用

    国内虚拟研究平台多基于单电机设计,而实际工业多电机配合工作更为常见,如机器人、3D打印机等。多电机同步控制在工业自动化生产系统中广泛存在,但目前基于FPGA纳秒级实时仿真平台多为单电机设计,进行多
    发表于 03-19 16:13

    fpga仿真器是什么?它有哪些优势?

    FPGA仿真器是一种用于模拟FPGA(现场可编程门阵列)硬件行为的软件工具。它通过模拟FPGA内部的逻辑电路、时序和接口等,帮助工程师在FPGA
    的头像 发表于 03-15 15:15 280次阅读

    fpga仿真文件怎么写

    首先,你需要选择一个FPGA仿真软件,如ModelSim、Vivado、Quartus II等。这些软件都提供了强大的仿真功能,可以帮助你验证FPGA设计的正确性。
    的头像 发表于 03-15 14:00 213次阅读

    基于FPGA的线性插值-

    与调试等主要步骤。 考虑到本算法需要做多次的迭代运算,如果程序没有设计好,可能导致陷入死循环,导致无法输出的结果。此时就需要利用仿真测试查找逻辑上的问题。 FPGA通常可以通过编写
    发表于 11-23 23:09

    测试文件编写流程说明

    testbench是写输入激励的,是一种验证手段。
    的头像 发表于 08-19 10:58 429次阅读
    <b class='flag-5'>测试</b><b class='flag-5'>文件</b><b class='flag-5'>编写</b>流程说明

    使用nuclei stuio将软件程序编为.Verilog,在vivado仿真出现的问题求解

    胡哥好,请问我的项目提示string is not a constant是怎么回事,是这一句出的提示 在进行这个仿真时, 我将e203_hbirdv2文件的rtl和tb所有文件
    发表于 08-16 08:20

    sdk编写的c程序是如何变成dump文件的?

    各位前辈,sdk编写的c程序是如何变成dump文件的,他的转换过程大概是什么,还有就是转化后的dump文件的main部分为啥在波形
    发表于 08-11 07:21

    Verilog Testbench怎么写 Verilog Testbench文件编写要点

    熟练了一点、但是整体编写下来比较零碎不成体系,所以在这里简要记录一下一般情况下、针对小型的verilog模块进行测试时所需要使用到的testbench文件编写要点。
    的头像 发表于 08-01 12:44 1515次阅读
    Verilog Testbench怎么写 Verilog Testbench<b class='flag-5'>文件</b>的<b class='flag-5'>编写</b>要点

    别克君越控开关失灵,什么情况怎么解决

    开关
    YS YYDS
    发布于 :2023年06月23日 00:38:57

    Verilog仿真激励举例

    Verilog 代码设计完成后,还需要进行重要的步骤,即逻辑功能仿真仿真激励文件称之为 testbench,放在各设计模块的顶层,以便对模块进行系统性的例化调用进行
    的头像 发表于 06-02 11:35 1133次阅读
    Verilog<b class='flag-5'>仿真</b><b class='flag-5'>激励</b>举例

    FPGA初学者:分析FPGA仿真验证知识

    ,再次重新编译, 直到正确为止。 建立仿真,切换到图 10 的 Library,点开 work 前面的加号,右键测试文件(红圈),选择 Simulate without Optimi
    发表于 06-02 09:56

    【正点原子DFPGL22G开发板体验】ModelSim 的安装和使用 FPGA开发仿真

    PDS 和仿真软件 Modelsim 放在 B 盘),文件列 表如下图所示:使用 Modelsim 软件需要添加 License,请多多支持正版。至此,Modelsim 安装完成。学习,可以自动查找
    发表于 04-30 17:24