引言:在編寫完HDL代碼后,往往需要通過仿真軟件Modelsim或者Vivadao自帶的仿真功能對HDL代碼功能進行驗證,此時我們需要編寫Testbench文件對HDL功能進行測試驗證。本文我們介紹寫Testbench編寫的一些要點。
1.Testbench文件結(jié)構(gòu)模板
編寫Testbench的目的是為了測試設(shè)計電路的功能、性能與設(shè)計的預(yù)期是否相符。驗證軟件功能通過包括以下步驟:
????? ? 產(chǎn)生合適的模擬激勵(波形):該激勵通常要覆蓋被測HDL模塊(黑盒或者稱作DUT模塊)所有可能得輸入狀態(tài);
????? ? 將產(chǎn)生的激勵加入到被測試模塊中并觀察其響應(yīng):即將DUT模塊例化到Testbench文件中,運行仿真軟測測試;
????? ? 將輸出響應(yīng)與期望值相比較:該步驟是驗證DUT功能較耗時的部分,需要仔細(xì)分析代碼功能是否達(dá)到預(yù)期設(shè)計,所有代碼段功能是否正常。
Testbench結(jié)構(gòu)一般模板如下:
?
module Test_bench_name();//通常無輸入無輸出 //01:信號或變量聲明定義 //--邏輯設(shè)計中輸入對應(yīng) reg 型 //--邏輯設(shè)計中輸出對應(yīng) wire 型 //02:使用 initial 或 always 語句產(chǎn)生激勵 //03:例化待測試DUT模塊 //04:監(jiān)控和比較輸出響應(yīng) endmodule2.時鐘激勵輸入示例
?
常見的時鐘有:50%占空比連續(xù)時鐘、固定周期數(shù)時鐘、非50%占空比時鐘,示例如下。
?
/*---------------------------------------------------------------- 時鐘激勵產(chǎn)生方法一:50%占空比時鐘 ----------------------------------------------------------------*/ parameter ClockPeriod=10; //參數(shù)化時鐘周期 initial begin clk_i=0; forever#(ClockPeriod/2) clk_i = ~clk_i; end /*---------------------------------------------------------------- 時鐘激勵產(chǎn)生方法二:50%占空比時鐘 ----------------------------------------------------------------*/ initial begin clk_i=0; end always #(ClockPeriod/2) clk_i=~clk_i; /*---------------------------------------------------------------- 時鐘激勵產(chǎn)生方法三:產(chǎn)生固定數(shù)量的時鐘脈沖 ----------------------------------------------------------------*/ parameter ClockPeriod=10; //參數(shù)化時鐘周期 initial begin clk_i=0; repeat(6) #(ClockPeriod/2) clk_i=~clk_i; end /*---------------------------------------------------------------- 時鐘激勵產(chǎn)生方法四:產(chǎn)生非占空比為 50%的時鐘 ----------------------------------------------------------------*/ parameter ClockPeriod=10; //參數(shù)化時鐘周期 initial begin clk_i=0; forever begin #((ClockPeriod/2)-2) clk_i=0; #((ClockPeriod/2)+2) clk_i=1; end end3.復(fù)位激勵輸入示例
?
復(fù)位輸入主要包括異步復(fù)位、同步復(fù)位,代碼示例如下。
?
/*---------------------------------------------------------------- 復(fù)位信號產(chǎn)生方法一:異步復(fù)位 ----------------------------------------------------------------*/ initial begin rst_n_i=1; #100; rst_n_i=0; #100; rst_n_i=1; end /*---------------------------------------------------------------- 復(fù)位信號產(chǎn)生方法二:同步復(fù)位 ----------------------------------------------------------------*/ initial begin rst_n_i=1; clk_i = 0; @(negedge clk_i) rst_n_i=0; #100; //固定時間復(fù)位 repeat(10) @(negedge clk_i); //固定周期數(shù)復(fù)位 @(negedge clk_i) rst_n_i=1; end always #5 clk_i=~clk_i; /*---------------------------------------------------------------- 復(fù)位信號產(chǎn)生方法三:復(fù)位任務(wù)封裝 ----------------------------------------------------------------*/ task reset; input [31:0] reset_time; //復(fù)位時間可調(diào),輸入復(fù)位時間 RST_ING=0; //復(fù)位方式可調(diào),低電平或高電平 begin rst_n=RST_ING; //復(fù)位中 #reset_time; //復(fù)位時間 rst_n_i=~RST_ING; //撤銷復(fù)位,復(fù)位結(jié)束 end endtask4.雙向口inout示例
/*---------------------------------------------------------------- 雙向信號inout 在 testbench 中定義為 wire 型變量 ----------------------------------------------------------------*/ reg sck; wire sda; //inout信號sda定義為wire型 reg sda_r; //inout 輸出定義為reg型 reg sda_en; assign sda_r = (sda_en) ? mosi : 1'bz; assign sda =sda_r;5.特殊信號設(shè)計
/*---------------------------------------------------------------- 特殊激勵信號產(chǎn)生描述一:輸入信號任務(wù)封裝 ----------------------------------------------------------------*/ task i_data; input [7:0] dut_data; begin@(posedge data_en); send_data=0; @(posedge data_en); send_data=dut_data[0]; @(posedge data_en); send_data=dut_data[1]; @(posedge data_en); send_data=dut_data[2]; @(posedge data_en); send_data=dut_data[3]; @(posedge data_en); send_data=dut_data[4]; @(posedge data_en); send_data=dut_data[5]; @(posedge data_en); send_data=dut_data[6]; @(posedge data_en); send_data=dut_data[7]; @(posedge data_en); send_data=1; #100; end endtask //調(diào)用方法:i_data(8'hXX); /*---------------------------------------------------------------- 特殊激勵信號產(chǎn)生描述二:多輸入信號任務(wù)封裝 ----------------------------------------------------------------*/ task more_input; input [7:0] a; input [7:0] b; input [31:0] times; output [8:0] c; begin repeat(times) //等待 times 個時鐘上升沿 @(posedge clk_i) c=a+b; //時鐘上升沿 a,b 相加 end endtask //調(diào)用方法:more_input(x,y,t,z);?//按聲明順序 /*---------------------------------------------------------------- 特殊激勵信號產(chǎn)生描述三:輸入信號產(chǎn)生,一次 SRAM 寫信號產(chǎn)生 ----------------------------------------------------------------*/ initial begin cs_n=1; //片選無效 wr_n=1; //寫使能無效 rd_n=1; //讀使能無效 addr=8'hxx; //地址無效 data=8'hzz; //數(shù)據(jù)無效 #100; cs_n=0; //片選有效 wr_n=0; //寫使能有效 addr=8'hF1; //寫入地址 data=8'h2C; //寫入數(shù)據(jù) #100; cs_n=1; wr_n=1; #10; addr=8'hxx; data=8'hzz; end /*---------------------------------------------------------------- 特殊激勵信號產(chǎn)生描述四:@與 wait ----------------------------------------------------------------*/ //@使用沿觸發(fā) //wait 語句都是使用電平觸發(fā) initial begin start=1'b1; wait(en=1'b1); #10; start=1'b0; end
?
6.仿真控制語句及系統(tǒng)任務(wù)描述
?
/*---------------------------------------------------------------- 仿真控制語句及系統(tǒng)任務(wù)描述 ----------------------------------------------------------------*/ $stop // 停止運行仿真,modelsim 中可繼續(xù)仿真 $stop(n) //帶參數(shù)系統(tǒng)任務(wù),根據(jù)參數(shù) 0,1或2不同,輸出仿真信息 $finish //結(jié)束運行仿真,不可繼續(xù)仿真 $finish(n) //帶參數(shù)系統(tǒng)任務(wù),根據(jù)參數(shù) 0,1或2不同,輸出仿真信息 //0:不輸出任何信息 //1:輸出當(dāng)前仿真時刻和位置 //2:輸出當(dāng)前仿真時刻、位置和仿真過程中用到的 memory 以及 CPU 時間的統(tǒng)計 $random //產(chǎn)生隨機數(shù) $random % n //產(chǎn)生范圍-n 到 n 之間的隨機數(shù) {$random} % n //產(chǎn)生范圍 0 到 n 之間的隨機數(shù) /*----------------------------------------------------------------7. 仿真終端顯示描述
/*---------------------------------------------------------------- 仿真終端顯示描述 ----------------------------------------------------------------*/ $monitor //仿真打印輸出, 打印出仿真過程中的變量,使其終端顯示 /*$monitor($time,,,"clk=%d reset=%d out=%d",clk,reset,out);*/ $display //終端打印字符串,顯示仿真結(jié)果等 /* $display(” Simulation start ! "); $display(” At time %t,input is %b%b%b,output is %b",$time,a,b,en,z); */ $time //返回 64 位整型時間 $stime //返回 32 位整型時間 $realtime //實行實型模擬時間8. 文本輸入方式
/*---------------------------------------------------------------- 文本輸入方式:$readmemb/$readmemh ----------------------------------------------------------------*/ //verilog 提供了讀入文本的系統(tǒng)函數(shù) $readmemb/$readmemh("<數(shù)據(jù)文件名>",<存儲器名>); $readmemb/$readmemh("<數(shù)據(jù)文件名>",<存儲器名>,<起始地址>); $readmemb/$readmemh("<數(shù)據(jù)文件名>",<存儲器名>,<起始地址>,<結(jié)束地址>); $readmemb:/*讀取二進制數(shù)據(jù),讀取文件內(nèi)容只能包含:空白位置,注釋行,二進制數(shù) 數(shù)據(jù)中不能包含位寬說明和格式說明,每個數(shù)字必須是二進制數(shù)字。*/ $readmemh:/*讀取十六進制數(shù)據(jù),讀取文件內(nèi)容只能包含:空白位置,注釋行,十六進制數(shù) 數(shù)據(jù)中不能包含位寬說明和格式說明,每個數(shù)字必須是十六進制數(shù)字。*/
?
審核編輯:黃飛
評論
查看更多