之前在使用Verilog做FPGA項目中、以及其他一些不同的場合下,零散的寫過一些練手性質(zhì)的testbench文件,開始幾次寫的時候,每次都會因為一些基本的東西沒記住、寫的很不熟練,后面寫的時候稍微熟練了一點(diǎn)、但是整體編寫下來比較零碎不成體系,所以在這里簡要記錄一下一般情況下、針對小型的verilog模塊進(jìn)行測試時所需要使用到的testbench文件的編寫要點(diǎn)。
本文主要參考了在網(wǎng)上找到的Lattice公司的“A Verilog HDL Test Bench Primer”手冊中的有關(guān)內(nèi)容。謝謝!
模塊實例化、reg&wire聲明、initial和always塊的使用
需要測試的模塊(Verilog-module)被稱為DUT(Design Under Test),在testbench中需要對一個或者多個DUT進(jìn)行實例化。
Testbench中的頂層module不需要定義輸入和輸出。
Testbench中連接到DUT instance的輸入的為reg類型、連接到DUT instance的輸出的為wire類型。
對于DUT的inout類型變量,在testbench中需要分別使用reg、wire類型的變量進(jìn)行調(diào)用。
例如,對于下面這樣一個待測試module:
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; end assign DATA = (READ_WRITE == 1) ? 2'bZ : LATCH_OUT; endmodule
為其設(shè)計的testbench文件可以是:
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 begin read_writet = 1; data_in = 11; #50 read_writet = 0; end endmodule
和普通的Verilog模塊中一樣、使用assign對wire類型的變量進(jìn)行賦值。
需要留意的一點(diǎn)是:對于沒有在代碼中賦初始值的變量,wire類型變量被初始化為Z、reg類型變量被初始化為X。
always和initial是兩種對reg變量進(jìn)行操作的串行控制塊。每個initial和always塊都會在仿真開始時同時開始運(yùn)行。
常見的,可以利用它們生成模塊所需的時鐘和復(fù)位信號,如下:
‘timescale 1 ns / 100 ps reg clk_50, rst_l; initial begin $display($time, " << Starting the Simulation >>"); clk_50 = 1’b0; // at time 0 rst_l = 0; // reset is active #20 rst_l = 1’b1; // at time 20 release reset end always #10 clk_50 = ~clk_50; // every ten nanoseconds invert
首行定義了時間單位/時間精度。時間單位為1ns,這樣生成的clk_50時鐘周期就是20ns、也就是頻率為50MHz。
復(fù)位信號rst_l在初始為0復(fù)位態(tài)、在20ns之后為1解除復(fù)位。
仿真中的停止、變量監(jiān)視和輸出
有兩種仿真控制函數(shù):$finish和$stop。其中,$finish任務(wù)用于終止仿真并跳出仿真器;$stop任務(wù)則用于中止仿真。在Modelsim中,$stop任務(wù)則是返回到交互模式。
如果需要監(jiān)視仿真中某個變量的變化情況,可以使用$monitor函數(shù):
$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);
每當(dāng)變量列表中的任一變量發(fā)生變化,就會產(chǎn)生輸出。
如果需要在仿真控制臺屏幕打印輸出,可以使用$display函數(shù):
$display($time, "<< count = %d - Turning OFF count enable >>",cnt_out);
任務(wù)Task的用法
可以將一組重復(fù)性的或者相關(guān)的命令組合到一起構(gòu)成一個任務(wù)。
任務(wù)通常可以在initial或者always塊中被調(diào)用。
一個任務(wù)可以擁有輸入、輸出、以及inouts,也可以包含計時或延時元素。
以一個在FPGA上實現(xiàn)的簡單SPI接口為例。外部設(shè)備為主、FPGA為從,命令一共32bit,構(gòu)成為“1位讀寫命令字(1讀0寫)+14位地址+1位NO CARE+16位數(shù)據(jù)”,片選信號拉低之后通信開始,時序如下圖:
數(shù)據(jù)流由外設(shè)到FPGA時(FPGA為接收),外設(shè)在SCLK的下降沿更新MOSI;FPGA在SCLK的上升沿將MOSI上的值抓取到移位寄存器。
當(dāng)FPGA為發(fā)送方時,F(xiàn)PGA在SCLK的下降沿更新MISO線上的輸出,外設(shè)在SCLK的上升沿將MISO上的值抓取過來。
外設(shè)可以通過該SPI接口訪問FPGA內(nèi)部生成的寄存器。
當(dāng)對FPGA上的spi模塊進(jìn)行讀測試時,外設(shè)發(fā)給FPGA的讀指令為:
{1'b1,address,1'b0,data(讀取到的16bit數(shù)據(jù))}
為此編寫的任務(wù)spi_read可以是:
task spi_read; input[13:0] address; output[15:0] data; reg [31:0] output_register; reg [15:0]input_register; integer i; begin $display("time:%t----------------task spi_read",$time ); #100; spi_clk = 1'b0; spi_csn = 1'b1; spi_mosi =1'b0; output_register = {1'b1,address,1'b0,16'd0}; $display("time:%t,testbench read output_register: %h,",$time,output_register ); $display("time:%t,testbench read address: %h",$time,address ); spi_csn = 1'b1; for(i = 0 ; i < 16 ; i=i+1) begin spi_csn = 1'b0; spi_clk = 1'b0; spi_mosi = output_register[31-i]; #100; spi_clk = 1'b1; #100; end for(i = 0 ; i < 16 ; i=i+1) begin spi_csn = 1'b0; spi_clk = 1'b0; #100; spi_clk = 1'b1; input_register[15-i] = spi_miso; #100; end spi_csn = 1'b1; data = input_register; $display("time:%t,testbench spi_read read data: %h,",$time,input_register ); $display("time:%t----------------",$time ); #100; end endtask
(其中仿真的時間單位為1ns,spi時鐘頻率為10MHz)
示例及匯總
根據(jù)前述內(nèi)容,自我總結(jié)一般簡單的testbench文件的結(jié)構(gòu)形式可以是如下:
`timescale 1 ns / 1 ns module testbench_module_top; reg reg …… wire wire …… //reset and clock definition initial begin …end initial begin …end //actual testing flows initial begin //variables initialization a = b = … task_1(var_1… var_N) … task_N(var_1… var_N) $stop; …end //dut module instance module_top U1 ( .var1(), .var2(), … .varN() ) //necessary control logic for testbench module test flow always@(...) //tasks definition task task_1; input …; output …; …… //action flow …… endtask …… task task_N; …… endtask endmodule
-
FPGA
+關(guān)注
關(guān)注
1629文章
21736瀏覽量
603419 -
模塊
+關(guān)注
關(guān)注
7文章
2707瀏覽量
47476 -
Verilog
+關(guān)注
關(guān)注
28文章
1351瀏覽量
110101 -
HDL
+關(guān)注
關(guān)注
8文章
327瀏覽量
47390 -
DUT
+關(guān)注
關(guān)注
0文章
189瀏覽量
12386
原文標(biāo)題:Testbench文件編寫紀(jì)要(Verilog)
文章出處:【微信號:zhuyandz,微信公眾號:FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論