?
7.5??仿真測(cè)試文件(Testbench)的設(shè)計(jì)方法
7.5.1??測(cè)試文件的用途
隨著設(shè)計(jì)量和復(fù)雜度的不斷增加,數(shù)字設(shè)計(jì)驗(yàn)證變得越來(lái)越難,所消耗的成本也越來(lái)越高。面對(duì)這種挑戰(zhàn),驗(yàn)證工程師必須依靠相應(yīng)的驗(yàn)證工具和方法才行。對(duì)于大型的設(shè)計(jì),比如上百萬(wàn)門的設(shè)計(jì)驗(yàn)證,工程師必須使用一整套規(guī)范的驗(yàn)證工具;而對(duì)于較小的設(shè)計(jì),使用具有HDL?testbench的仿真器是一個(gè)不錯(cuò)的選擇。
Testbench已經(jīng)成為了HLL(High-Level-Language,高級(jí)語(yǔ)言)設(shè)計(jì)標(biāo)準(zhǔn)驗(yàn)證方法。一般來(lái)說(shuō),它能夠完成下面一些任務(wù)。
·??實(shí)例化DUT(Design?Under?Test,被測(cè)設(shè)計(jì))。
·??通過(guò)為模塊添加測(cè)試向量對(duì)DUT進(jìn)行仿真。
·??通過(guò)終端或波形窗口提供仿真結(jié)果。
·??比較實(shí)際輸出與期望輸出的差異。
一般來(lái)說(shuō),Testbench使用工業(yè)標(biāo)準(zhǔn)VHDL或者Verilog?HDL語(yǔ)言來(lái)描述。簡(jiǎn)單的Testbench通過(guò)調(diào)用用戶設(shè)計(jì)的功能模塊,然后進(jìn)行仿真。較為復(fù)雜的Testbench還包括一些其他的功能,比如包含特定的激勵(lì)向量或者進(jìn)行實(shí)際輸出與期望輸出的比較等。
如圖7.32所示是一個(gè)標(biāo)準(zhǔn)的HDL驗(yàn)證流程,圖中表達(dá)了上述的Testbench功能。
圖7.32??HDL?Testbench仿真流程
?
由于Testbench是使用VHDL或者Verilog?HDL編寫(xiě)的,因此Testbench的驗(yàn)證流程是跨平臺(tái)的。同時(shí)由于這兩種語(yǔ)言是標(biāo)準(zhǔn)的非私有語(yǔ)言,所以使用它們進(jìn)行驗(yàn)證的流程可以在未來(lái)的設(shè)計(jì)中繼續(xù)使用。
下面講解Testbench的設(shè)計(jì)方法。
7.5.2??測(cè)試文件設(shè)計(jì)方法
由于測(cè)試文件只是在仿真時(shí)使用,因此在綜合中對(duì)RTL語(yǔ)言的語(yǔ)法限制在測(cè)試文件中并不存在。相反地,測(cè)試文件可以編寫(xiě)得更加通俗,所有的行為結(jié)構(gòu)語(yǔ)法都可以使用,使之更加容易理解。所有的測(cè)試文件都包含了如表7.1所示的結(jié)構(gòu)。
表7.1 測(cè)試文件的共同結(jié)構(gòu)
VHDL |
Verilog?HDL |
實(shí)體和結(jié)構(gòu)聲明(Entity,Architecture) |
模塊聲明(Module) |
信號(hào)聲明(Signal) |
信號(hào)生命(Signal) |
頂層模塊實(shí)例化 |
頂層模塊實(shí)例化 |
激勵(lì)向量 |
激勵(lì)向量 |
?
正如所述,測(cè)試文件除了包含這些共有的結(jié)構(gòu)意外,還完成一些額外的工作。例如顯示結(jié)果、嵌入的誤碼檢測(cè)等。下面通過(guò)一些例子來(lái)展現(xiàn)測(cè)試文件中常用的結(jié)構(gòu)。
1.產(chǎn)生測(cè)試時(shí)鐘
仿真必須產(chǎn)生時(shí)鐘才能進(jìn)行,只有在推進(jìn)的仿真時(shí)鐘中才可以判定輸出的結(jié)果是否符合設(shè)計(jì)的要求。交互時(shí)鐘是硬件設(shè)計(jì)語(yǔ)言里面最容易實(shí)現(xiàn)的時(shí)鐘,下面通過(guò)Verilog語(yǔ)言的例子來(lái)說(shuō)明如何生成測(cè)試時(shí)鐘。
Parameter ClockPeriod = 10;???????? // 聲明時(shí)鐘周期常量
//時(shí)鐘生成方法1:
???? initial begin
????????? forever Clock = #(ClockPeriod / 2) ~ Clock;
???? end
//時(shí)鐘生成方法2:
???? initial begin
????????? always #(ClockPeriod / 2) Clock = ~Clock;
???? end
在兩種實(shí)現(xiàn)方法中,都是用了initial塊語(yǔ)句。在方法1中,使用了forever語(yǔ)句,是最常用的產(chǎn)生時(shí)鐘的方法。在方法2中,使用過(guò)了always語(yǔ)句,同樣也實(shí)現(xiàn)了時(shí)鐘的生成。兩種方法都產(chǎn)生了周期為10?的時(shí)鐘波形。
2.提供激勵(lì)源
為了得到Testbench的驗(yàn)證結(jié)果,必須為DUT提供激勵(lì)向量。并行激勵(lì)模塊常常被用來(lái)為測(cè)試文件提供必要的激勵(lì)。有兩種不同的方法來(lái)實(shí)現(xiàn)并行的激勵(lì):一種是絕對(duì)時(shí)間激勵(lì),另一種是相對(duì)時(shí)間激勵(lì)。
在絕對(duì)時(shí)間激勵(lì)中,所有的仿真時(shí)間值都是相對(duì)仿真時(shí)間零點(diǎn)定義的。而在相對(duì)時(shí)間激勵(lì)中,一般會(huì)提供初始化值,然后等待事件來(lái)觸發(fā)激勵(lì)向量。兩種方法都可以根據(jù)設(shè)計(jì)者的需要在同一個(gè)測(cè)試文件中使用。
下面分別是兩種方法產(chǎn)生激勵(lì)的例子。
(1)絕對(duì)時(shí)間。
initial?begin
?????Reset?=?1;????????????????//仿真時(shí)間零點(diǎn)激勵(lì)
?????Load?=?0;?????????????????//仿真時(shí)間零點(diǎn)激勵(lì)
?????Count_UpDn?=?0;??????????//仿真時(shí)間零點(diǎn)激勵(lì)
?????#100?Reset?=?0;??????????//絕對(duì)時(shí)間100激勵(lì)
?????#20?Load?=?1;????????????//絕對(duì)時(shí)間120激勵(lì),相對(duì)上一個(gè)時(shí)間點(diǎn)20
?????#20?Count_UpDn?=?1;?????//絕對(duì)時(shí)間140激勵(lì),相對(duì)上一個(gè)時(shí)間點(diǎn)20
end
(2)相對(duì)時(shí)間。
always?@?(posedge?clock)
?????TB_Count?<=?TB_Count?+?1;????//絕對(duì)時(shí)間的遞增
initial?begin
?????if?(TB_Count?<=?5)????begin????//觸發(fā)事件,產(chǎn)生下列激勵(lì)
???????????Reset?=?1;
???????????Load?=?0;
???????????Count?_UpDn?=?0;
???????????end
?????else?begin????????????????//觸發(fā)事件,產(chǎn)生下列激勵(lì)
???????????Reset?=?0;
???????????Load?=?1;
???????????Count_UpDn?=?1;
?????end
end
initial?begin
?????if?(Count?==?1100)?begin????//觸發(fā)事件,產(chǎn)生歸零激勵(lì),并顯示結(jié)果
???????????Count_UpDn?<=?0;
???????????$display("Terminal?Count?Reached,?now?counting?down.");
???????????end
end
值得注意的是,Verilog?HDL語(yǔ)言的initial模塊之間是并行執(zhí)行的,但是initial模塊內(nèi)部是順序執(zhí)行的。也就是說(shuō),測(cè)試文件的激勵(lì)順序在仿真時(shí)間零點(diǎn)同時(shí)啟動(dòng)并行模塊,然后根據(jù)各個(gè)模塊的內(nèi)部激勵(lì)順序產(chǎn)生激勵(lì)向量。
3.結(jié)果輸出
測(cè)試文件通過(guò)關(guān)鍵詞$display和$monitor來(lái)實(shí)現(xiàn)結(jié)果的輸出。下面是使用Verilog?HDL語(yǔ)言實(shí)現(xiàn)在終端上顯示結(jié)果的例子:
//?在終端中打印信號(hào)的ASCII值
initial?begin
??????$timeformat(-9,1,"ns",12);?????????????????????????????????//設(shè)置輸出時(shí)鐘格式
??????$display("?Time?Clk?Rst?Ld?SftRg?Data?Sel");???????????//顯示輸入的字符串
??????$monitor("%t?%b?%b?%b?%b?%b?%b",????????????????????????//設(shè)置輸出信號(hào)的格式
??????$realtime,?clock,?reset,?load,?shiftreg,?data,?sel);?//指定輸出的信號(hào)
end
$display是將函數(shù)內(nèi)部雙引號(hào)中的字符串輸出在終端中。而$monitor則不同,因此它的輸出是事件驅(qū)動(dòng)的。在例子中,$monitor信號(hào)列表中的$realtime信號(hào)變化會(huì)觸發(fā)終端顯示事件的發(fā)生,該信號(hào)被設(shè)計(jì)者對(duì)應(yīng)到仿真時(shí)間中,每次$monitor的觸發(fā)將會(huì)把信號(hào)列表中的信號(hào)值顯示在終端中。
$monitor語(yǔ)句中的“%”用于定義信號(hào)列表中信號(hào)的輸出格式。例如,%t將信號(hào)按照時(shí)間格式輸出,%b將信號(hào)按照二進(jìn)制格式輸出。另外,Verilog?HDL語(yǔ)言還提供了其他的輸出格式,比如%h為十六進(jìn)制輸出,%d為十進(jìn)制輸出,%o為八進(jìn)制輸出等。更為詳細(xì)的格式輸出定義可以參看Verilog參考手冊(cè)。
?
7.5.3??測(cè)試常用語(yǔ)句
常用的Verilog測(cè)試用結(jié)構(gòu)語(yǔ)句,比如$monitor、$display和$time在上面已經(jīng)介紹過(guò)了。下面再來(lái)介紹一些其他的常用語(yǔ)句。
1.force/release
force和release語(yǔ)句可以用來(lái)強(qiáng)制對(duì)執(zhí)行過(guò)程中的寄存器或網(wǎng)絡(luò)型信號(hào)量賦值。這兩條語(yǔ)句共同完成一個(gè)強(qiáng)制賦值的過(guò)程。當(dāng)一個(gè)被force的信號(hào)被release以后,這個(gè)信號(hào)將會(huì)保持當(dāng)時(shí)的狀態(tài)直到下一個(gè)賦值語(yǔ)句產(chǎn)生為止。
下面舉個(gè)例子來(lái)說(shuō)明這兩條語(yǔ)句的使用。
module?testbench;
?????...
?????initial?begin
??????????reset?=?1;????????????????//在仿真時(shí)間零點(diǎn)將reset激勵(lì)為1
??????????force?DataOut?=?101;????//在仿真時(shí)間零點(diǎn)強(qiáng)制使DataOut為101,并保持
??????????#25?reset?=?0;???????????//在仿真絕對(duì)時(shí)間25將reset激勵(lì)為0
??????????#25?release?DataOut;????//在仿真絕對(duì)時(shí)間50釋放
??????????????????????????????????????//?DataOut值將保持直至下一個(gè)對(duì)它的賦值語(yǔ)句
??????????...
??????????end
endmodule
2.a(chǎn)ssign/deassign
assign/deassign語(yǔ)句與force/release語(yǔ)句相類似,不過(guò)assign/deassign語(yǔ)句只能對(duì)設(shè)計(jì)中的寄存器型信號(hào)賦值。它們常常被用來(lái)設(shè)置輸入值。
下面是這兩個(gè)語(yǔ)句的例子。
module?testbench;
?????...
?????initial?begin
??????????reset?=?1;??????????????//絕對(duì)時(shí)間零點(diǎn)對(duì)reset賦值1
??????????DataOut?=?101;
??????????#25?reset?=?0;?????????//絕對(duì)時(shí)間25對(duì)reset賦值0
??????????release?DataOut;
??????????...
?????end
?????initial?begin
??????????#20?assign?reset?=?1;?//此條語(yǔ)句覆蓋之前的賦值語(yǔ)句(即絕對(duì)時(shí)間零點(diǎn)的賦值)
??????????#25?reset?=?0;??????????//絕對(duì)時(shí)間45對(duì)reset賦值0
??????????#50?release?reset;?????//絕對(duì)時(shí)間95釋放reset信號(hào)
endmodule
3.timescales
timescale語(yǔ)句用于定義測(cè)試文件的單位時(shí)間,同時(shí)也對(duì)仿真的精度有影響。它的語(yǔ)法定義如下:
‘timescale?reference_time/precision
其中,reference_time是單位時(shí)間的度量,precision決定了仿真的推進(jìn)延遲精度,同時(shí)也設(shè)置了仿真的推進(jìn)步進(jìn)單位。下面是timescale語(yǔ)句的使用范例:
‘timescale?1?ns?/?1?ps???????????//度量參考為1ns,精度為1ps
module?testbench;
?????...
?????initial?begin
??????????#5?reset?=?1;????????????//?5個(gè)仿真時(shí)間延遲,相當(dāng)于5×1ns?=?5ns?的仿真時(shí)間
??????????#10?reset?=?0;
??????????...
??????????end
?????initial?begin
??????????//display語(yǔ)句將在每一個(gè)仿真推進(jìn)步進(jìn)中執(zhí)行,也就是1ps執(zhí)行一次
??????????$display?(“%d?,?Reset?=?%b”,?$time,?reset);
??????????end
endmodule
應(yīng)該注意的是,如果仿真中使用了時(shí)間延遲值,那么仿真的精度應(yīng)大于最小的延遲值。例如仿真中使用了9ps的仿真時(shí)間延遲,那么仿真的推進(jìn)步進(jìn)精度必須為1ps來(lái)保證9ps的延遲。
4.Reading?Memory?Initialization?Files
Verilog?HDL提供了$readmemb和$readmemh命令來(lái)讀取ASCII文件,用于初始化memory的內(nèi)容。這兩個(gè)語(yǔ)句可以用于初始化FPGA中由IP?Core生成的存儲(chǔ)器宏模塊,例如RAM、ROM等。
下面是利用這個(gè)語(yǔ)句對(duì)Xilinx的實(shí)例(design_instance)進(jìn)行初始化的例子。
$readmemb?(“<design.mif>”,?design_instance);
其中,mif文件是Xilinx的Core?Generator建立的對(duì)存儲(chǔ)器進(jìn)行初始化的文件。用戶也可以自己編寫(xiě)這個(gè)文件的內(nèi)容。