打印
[FPGA]

Testbench文件

[复制链接]
2233|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
模块实例化、reg&wire声明、initial和always块的使用
需要测试的模块(Verilog-module)被称为DUT(Design Under Test),在testbench中需要对一个或者多个DUT进行实例化。
Testbench中的顶层module不需要定义输入和输出。
Testbench中连接到DUT instance的输入的为reg类型、连接到DUT instance的输出的为wire类型。
对于DUT的inout类型变量,在testbench中需要分别使用reg、wire类型的变量进行调用。
例如,对于下面这样一个待测试module:
[url=] [/url]
module bidir_infer (DATA, READ_WRITE);input READ_WRITE ;inout [1:0] DATA ;reg [1:0] LATCH_OUT ;always @ (READ_WRITE or DATA) begin    if (READ_WRITE == 1)        LATCH_OUT <= DATA;endassign DATA = (READ_WRITE == 1) ? 2'bZ : LATCH_OUT;endmodule[url=] [/url]

为其设计的testbench文件可以是:
[url=] [/url]
module test_bidir_ver;reg read_writet;reg [1:0] data_in;wire [1:0] datat, data_out;bidir_infer uut (datat, read_writet);assign datat = (read_writet == 1) ? data_in : 2'bZ;assign data_out = (read_writet == 0) ? datat : 2'bZ;initial beginread_writet = 1;data_in = 11;#50 read_writet = 0;endendmodule[url=] [/url]

和普通的Verilog模块中一样、使用assign对wire类型的变量进行赋值。
需要留意的一点是:对于没有在代码中赋初始值的变量,wire类型变量被初始化为Z、reg类型变量被初始化为X。
always和initial是两种对reg变量进行操作的串行控制块。每个initial和always块都会在仿真开始时同时开始运行。
常见的,可以利用它们生成模块所需的时钟和复位信号,如下:
[url=] [/url]
‘timescale 1 ns / 100 psreg clk_50, rst_l;initialbegin$display($time, " << Starting the Simulation >>");clk_50 = 1’b0; // at time 0rst_l = 0; // reset is active#20 rst_l = 1’b1; // at time 20 release resetendalways#10 clk_50 = ~clk_50; // every ten nanoseconds invert[url=] [/url]

首行定义了时间单位/时间精度。时间单位为1ns,这样生成的clk_50时钟周期就是20ns、也就是频率为50MHz。
复位信号rst_l在初始为0复位态、在20ns之后为1解除复位。
仿真中的停止、变量监视和输出
有两种仿真控制函数:$finish和$stop。其中,$finish任务用于终止仿真并跳出仿真器;$stop任务则用于中止仿真。在Modelsim中,$stop任务则是返回到交互模式。
如果需要监视仿真中某个变量的变化情况,可以使用$monitor函数:
$monitor($time, " clk_50=%b, rst_l=%b, enable_l=%b, load_l=%b, count_in=%h, cnt_out=%h, oe_l=%b, count_tri=%h", clk_50, rst_l, enable_l, load_l, count_in, cnt_out, oe_l, count_tri);
每当变量列表中的任一变量发生变化,就会产生输出。
如果需要在仿真控制台屏幕打印输出,可以使用$display函数:
$display($time, "<< count = %d - Turning OFF count enable >>",cnt_out);


任务Task的用法
可以将一组重复性的或者相关的命令组合到一起构成一个任务。
任务通常可以在initial或者always块中被调用。
一个任务可以拥有输入、输出、以及inouts,也可以包含计时或延时元素。
以一个在FPGA上实现的简单SPI接口为例。外部设备为主、FPGA为从,命令一共32bit,构成为“1位读写命令字(1读0写)+14位地址+1位NO CARE+16位数据”,片选信号拉低之后通信开始,时序如下图:
数据流由外设到FPGA时(FPGA为接收),外设在SCLK的下降沿更新MOSI;FPGA在SCLK的上升沿将MOSI上的值抓取到移位寄存器。
当FPGA为发送方时,FPGA在SCLK的下降沿更新MISO线上的输出,外设在SCLK的上升沿将MISO上的值抓取过来。
外设可以通过该SPI接口访问FPGA内部生成的寄存器。
当对FPGA上的spi模块进行读测试时,外设发给FPGA的读指令为:
{1'b1,address,1'b0,data(读取到的16bit数据)}
为此编写的任务spi_read可以是:
View Code
(其中仿真的时间单位为1ns,spi时钟频率为10MHz)
示例及汇总
根据前述内容,自我总结一般简单的testbench文件的结构形式可以是如下:
[url=] [/url]
`timescale 1 ns / 1 nsmodule testbench_module_top;reg reg……wirewire……//reset and clock definitioninitial beginendinitial beginend//actual testing flowsinitial begin //variables initializationa = b = …task_1(var_1… var_N)…task_N(var_1… var_N)$stop;…end//dut module instancemodule_top U1(.var1(),.var2(),….varN())//necessary control logic for testbench module test flowalways@(...)//tasks definitiontask task_1;input …;output …;……//action flow……endtask……task task_N;……endtaskendmodule[url=] [/url]



使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:这个社会混好的两种人:一是有权有势,二是没脸没皮的。

1050

主题

11299

帖子

26

粉丝