本文詳細(xì)闡述了在一個testbench中,應(yīng)該如何使用阻塞賦值與非阻塞賦值。首先說結(jié)論,建議在testbench中,對時鐘信號(包括分頻時鐘)使用阻塞賦值,對其他同步信號使用非阻塞賦值。
下面是一個簡單的D觸發(fā)器模塊,本文將針對它的testbench進(jìn)行討論。
moduleDff(inputclk, rst_n, data_in,outputregdata_out); always@(posedgeclk,negedgerst_n)begin if(!rst_n) data_out <=?1'b0; ? ? ? ?else? ? ? ? ? ? ?data_out <= data_in; ? ?endendmodule
在Verilog仿真時,仿真波形與真實(shí)波形是有一定差距的,這體現(xiàn)在同步信號的改變與時鐘沿一直是對齊的,而真實(shí)情況下,數(shù)據(jù)信號在時鐘沿后需要延遲一段時間才會發(fā)生改變。
體現(xiàn)在上面的模塊中就是數(shù)據(jù)信號data_in的改變是與時鐘信號clk同步的,data_out的改變也是與時鐘信號clk同步的。
圖1展示了一個簡單的仿真波形,其中信號data_in和信號data_out的改變都與時鐘沿同步,需要注意的是在時鐘沿處,信號data_out得到的是信號data_in在時鐘沿處的原值,而不是改變后的值。
圖1 仿真波形
也就是說,一個更加真實(shí)的波形可能如圖2所示。
圖2 真實(shí)波形
下面給出圖1所示波形的testbench。
moduleDff_t; regrst_n =1; regclk =0; regdata_in =0; wiredata_out; Dff dff(.clk(clk),.rst_n(rst_n),.data_in(data_in),.data_out(data_out)); //時鐘產(chǎn)生 alwaysbegin #10clk = ~clk; end //異步復(fù)位信號 initialbegin #3rst_n =0; #3rst_n =1; end //同步數(shù)據(jù)輸入 initialbegin #10data_in <=?1; ? ? ? ? #20?data_in <=?0; ? ? ? ? #20?data_in <=?1; ? ? ? ? #20?data_in <=?0; ? ?end? ? ?endmodule
其中時鐘信號和異步復(fù)位信號使用了阻塞賦值,而數(shù)據(jù)信號使用了非阻塞賦值。如果不是這樣,就無法保證產(chǎn)生如圖1所示的仿真波形,下面將分別討論。
1、如時鐘信號使用非阻塞賦值,數(shù)據(jù)信號也使用非阻塞賦值
moduleDff_t; regrst_n =1; regclk =0; regdata_in =0; wiredata_out; Dff dff(.clk(clk),.rst_n(rst_n),.data_in(data_in),.data_out(data_out)); //時鐘產(chǎn)生 alwaysbegin #10clk <= ~clk; ? ?end? ? ??//異步復(fù)位信號? ? ?initial?begin? ? ? ? ?#3?rst_n =?0; ? ? ? ? #3?rst_n =?1; ? ?end? ? ??//同步數(shù)據(jù)輸入? ? ?initial?begin? ? ? ? ?#10?data_in <=?1; ? ? ? ? #20?data_in <=?0; ? ? ? ? #20?data_in <=?1; ? ? ? ? #20?data_in <=?0; ? ?end? ? ?endmodule
圖3 錯誤的波形(一種可能)
此時進(jìn)行仿真,可能會出現(xiàn)圖3所示的錯誤波形,信號data_out得到的是信號data_in在時鐘沿處改變后的值。
拿第一個時鐘上升沿即10ns時舉例,此時時鐘信號clk被非阻塞賦值,同時data_in被非阻塞賦值。首先說明非阻塞賦值的過程,非阻塞賦值是分兩步進(jìn)行的,第一步是將賦值號<=右表達(dá)式求值,在當(dāng)前仿真時間的所有賦值和非阻塞賦值右表達(dá)式求值(活躍事件)完成后,再進(jìn)行第二步,即非阻塞賦值的賦值(非阻塞賦值的賦值順序由求值順序決定),即非阻塞賦值分為兩步:求值與賦值,后文僅使用“賦值”一詞代表非阻塞賦值中的賦值這個步驟,注意其與阻塞賦值的區(qū)別。
由于initial結(jié)構(gòu)和always結(jié)構(gòu)是并行的,因此無法確定哪一個非阻塞賦值的右表達(dá)式求值是先進(jìn)行的,但可以確定的是,信號clk的賦值和信號data_in的賦值以某種先后順序被調(diào)度到之后(非阻塞賦值更新區(qū))執(zhí)行。當(dāng)進(jìn)行第二步時,clk的賦值和data_in的賦值都從非阻塞賦值更新區(qū)激活到活躍事件區(qū)執(zhí)行,此時有多種執(zhí)行方式:
1、如果clk的賦值先執(zhí)行(即之前clk非阻塞賦值右表達(dá)式先求值),則其又觸發(fā)了@(posedge clk),接著是執(zhí)行data_out非阻塞賦值右表達(dá)式求值,還是執(zhí)行data_in的賦值,是不確定的,它們都是活躍事件。如果先執(zhí)行data_out非阻塞賦值右表達(dá)式求值,則data_out得到的是data_in的舊值即0;如果先執(zhí)行data_in的賦值,則data_out得到的是data_in的新值即1(圖3可能就是這種情況)。
2、如果data_in的賦值先執(zhí)行(即之前data_in非阻塞賦值右表達(dá)式先求值),則最后data_out得到的一定是data_in的新值即1(圖3可能就是這種情況)。
2、如時鐘信號使用阻塞賦值,數(shù)據(jù)信號也使用阻塞賦值
moduleDff_t; regrst_n =1; regclk =0; regdata_in =0; wiredata_out; Dff dff(.clk(clk),.rst_n(rst_n),.data_in(data_in),.data_out(data_out)); //時鐘產(chǎn)生 alwaysbegin #10clk = ~clk; end //異步復(fù)位信號 initialbegin #3rst_n =0; #3rst_n =1; end //同步數(shù)據(jù)輸入 initialbegin #10data_in =1; #20data_in =0; #20data_in =1; #20data_in =0; end endmodule
圖4 錯誤的波形(一種可能)
此時進(jìn)行仿真,可能會出現(xiàn)圖4所示的錯誤波形,信號data_out得到的是信號data_in在時鐘沿處改變后的值。
拿第一個時鐘上升沿即10ns時舉例,此時時鐘信號clk被阻塞賦值,同時data_in被阻塞賦值。由于initial結(jié)構(gòu)和always結(jié)構(gòu)是并行的,因此無法確定哪一個阻塞賦值是先進(jìn)行的,此時有多種執(zhí)行方式。
1、如果clk的阻塞賦值先進(jìn)行,則其又觸發(fā)了@(posedge clk),接著是執(zhí)行data_out非阻塞賦值右表達(dá)式求值,還是執(zhí)行data_in的阻塞賦值,是不確定的,它們都是活躍事件。如果先執(zhí)data_out非阻塞賦值右表達(dá)式求值,則data_out首先得到的是data_in的舊值即0;如果先執(zhí)行data_in的阻塞賦值,則則data_out得到的是data_in的新值即1(圖4可能就是這種情況)。
2、如果data_in的阻塞賦值先進(jìn)行則最后data_out得到的一定是data_in的新值即1(圖4可能就是這種情況)。
3、如時鐘信號使用非阻塞賦值,數(shù)據(jù)信號使用阻塞賦值
moduleDff_t; regrst_n =1; regclk =0; regdata_in =0; wiredata_out; Dff dff(.clk(clk),.rst_n(rst_n),.data_in(data_in),.data_out(data_out)); //時鐘產(chǎn)生 alwaysbegin #10clk <= ~clk; ? ?end? ? ??//異步復(fù)位信號? ? ?initial?begin? ? ? ? ?#3?rst_n =?0; ? ? ? ? #3?rst_n =?1; ? ?end? ? ??//同步數(shù)據(jù)輸入? ? ?initial?begin? ? ? ? ?#10?data_in =?1; ? ? ? ? #20?data_in =?0; ? ? ? ? #20?data_in =?1; ? ? ? ? #20?data_in =?0; ? ?end? ? ?endmodule
圖5 錯誤的波形
此時進(jìn)行仿真,一定會出現(xiàn)圖5所示的錯誤波形,信號data_out得到的是信號data_in在時鐘沿處改變后的值。
拿第一個時鐘上升沿即10ns時舉例,此時時鐘信號clk被非阻塞賦值,同時data_in被阻塞賦值。由于initial結(jié)構(gòu)和always結(jié)構(gòu)是并行的,因此無法確定是非阻塞賦值的右表達(dá)式求值先進(jìn)行還是阻塞賦值先進(jìn)行,但是阻塞賦值一定是在非阻塞賦值的賦值前進(jìn)行的(根據(jù)非阻塞賦值的定義),所以不管有多少種執(zhí)行方式,此時只有一種結(jié)果。
1、data_out得到的一定是data_in的新值即1(圖5就是這種情況)。
4、時鐘信號使用阻塞賦值,數(shù)據(jù)信號使用非阻塞賦值
moduleDff_t; regrst_n =1; regclk =0; regdata_in =0; wiredata_out; Dff dff(.clk(clk),.rst_n(rst_n),.data_in(data_in),.data_out(data_out)); //時鐘產(chǎn)生 alwaysbegin #10clk = ~clk; end //異步復(fù)位信號 initialbegin #3rst_n =0; #3rst_n =1; end //同步數(shù)據(jù)輸入 initialbegin #10data_in <=?1; ? ? ? ? #20?data_in <=?0; ? ? ? ? #20?data_in <=?1; ? ? ? ? #20?data_in <=?0; ? ?end? ? ?endmodule
最后分析正確的testbench,拿第一個時鐘上升沿即10ns時舉例,此時時鐘信號clk被阻塞賦值,同時data_in被非阻塞賦值。由于initial結(jié)構(gòu)和always結(jié)構(gòu)是并行的,因此無法確定是非阻塞賦值的右表達(dá)式求值先進(jìn)行還是阻塞賦值先進(jìn)行。
1、如果clk的阻塞賦值先進(jìn)行,則其又觸發(fā)了@(posedge clk),接著是執(zhí)行data_out非阻塞賦值右表達(dá)式求值,還是執(zhí)行data_in非阻塞賦值右表達(dá)式求值,是不確定的,它們都是活躍事件。但是可以肯定的是,data_out得到的一定是data_in的舊值,因?yàn)榉亲枞x值的賦值一定在所有非阻塞賦值的求值后進(jìn)行(根據(jù)非阻塞賦值的定義)。
2、如果data_in非阻塞賦值右表達(dá)式求值先進(jìn)行,則在之后clk阻塞賦值進(jìn)行后,其又觸發(fā)了@(posedge clk),接著執(zhí)行data_out非阻塞賦值右表達(dá)式求值,但求值時是使用data_in的舊值,因?yàn)榉亲枞x值的賦值一定在所有非阻塞賦值的求值后進(jìn)行(根據(jù)非阻塞賦值的定義)。
來源:https://blog.csdn.net/weixin_45791458/article/details/137046594
-
仿真
+關(guān)注
關(guān)注
51文章
4202瀏覽量
135002 -
Verilog
+關(guān)注
關(guān)注
28文章
1363瀏覽量
111356 -
阻塞賦值
+關(guān)注
關(guān)注
0文章
10瀏覽量
9213 -
非阻塞賦值
+關(guān)注
關(guān)注
0文章
11瀏覽量
10052
原文標(biāo)題:在testbench中使用阻塞賦值和非阻塞賦值的區(qū)別
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設(shè)計(jì)論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
Verilog語言中阻塞和非阻塞賦值的不同
Verilog中阻塞賦值和非阻塞賦值的正確使用
【技巧分享】FPGA至簡設(shè)計(jì)-阻塞賦值與非阻塞賦值
阻塞賦值和非阻塞賦值的用法一篇文章就夠了
verilog中阻塞賦值和非阻塞賦值到底有什么區(qū)別

IEEE Verilog阻塞賦值和非阻塞賦值的區(qū)別

VerilogHDL語言:清阻塞賦值和非阻塞賦值
簡述阻塞賦值和非阻塞賦值的可綜合性

評論