相關閱讀
Verilog基礎https://blog.csdn.net/weixin_45791458/category_12263729.html?spm=1001.2014.3001.5482
????????作為一個硬件描述語言,Verilog HDL常常需要使用語句描述并行執行的電路,但其實在仿真器的底層,這些并行執行的語句是有先后順序的,然而Verilog標準并沒有將這些事件調度的順序定死,而是給予了仿真器廠商一定的自由去實現自己的產品,這就導致了設計者如果不遵循一定的編程習慣,會導致意想不到的仿真結果,下面是一些相關的規則。
1、不要在兩個及以上的always或initial結構中對同一個變量賦值
? ? ? ??當兩個以上的過程結構同時執行時,它們之間的執行順序是不一定的,Verilog標準只規定了在一個順序塊(begin end)中的所有語句是按照其先后順序執行的,但并沒有規定同一個時間執行的多個結構的執行順序,下面舉例詳細說明。
//例1
`timescale 1ns/1ps
module test();initial $display("The initial_0 execute");initial $display("The initial_1 execute");initial $display("The initial_2 execute");
endmodule
? ? ? ? 在上例中,三個initial結構在0ns時同時執行,仿真器執行他們的順序是不定的,但測試表明,許多仿真器都選擇順序執行這三個initial結構,如下所示。
對于Mentor Modelsim SE,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execute對于Aldec Riviera Pro,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execute對于Cadence Xcelium,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execute對于Mentor Questa,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execute對于Synopsys VCS,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execute對于Icarus Verilog,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execute
? ? ? ? ?雖然看起來仿真器的行為都是一致的,但其實這只是一個巧合,下面的例子就體現出了各種仿真器對同一段代碼之間的不同行為。
//例2
`timescale 1ns/1ps
module test();reg a;initial #5 a = 1;initial @(a) $display("The initial_0 execute");initial @(a) $display("The initial_1 execute");initial @(a) $display("The initial_2 execute");
endmodule
????????這段代碼的三個initial結構都對事件a敏感,所以在5ns時,三個initial結構同時被觸發,而他們的執行順序是不定的,如下所示。
對于Mentor Modelsim SE,輸出結果為
The initial_2 execute
The initial_1 execute
The initial_0 execute對于Aldec Riviera Pro,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execute對于Cadence Xcelium,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execute對于Mentor Questa,輸出結果為
The initial_2 execute
The initial_1 execute
The initial_0 execute對于Synopsys VCS,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execut對于Icarus Verilog,輸出結果為
The initial_2 execute
The initial_1 execute
The initial_0 execute
? ? ? ? 下面的代碼中仍然體現了同一時間的多個結構的執行順序是不定的,即使觸發這些結構的事件在同一時間是有前后關系的。
//例3
`timescale 1ns/1ps
module test();reg a, b, c;initial begin#5;a = 1;b = 1;c = 1;endalways @(a) $display("The initial_0 execute");always @(b) $display("The initial_1 execute");always @(c) $display("The initial_2 execute");
endmodule
? ? ? ? 在5ns時,a、b、c先后被賦值為1,Verilog標準保證了begin end塊中的賦值順序從上到下。但由這些賦值語句觸發的其他always結構呢?是否也會按照a、b、c的順序執行呢?
對于Mentor Modelsim SE,輸出結果為
The initial_2 execute
The initial_1 execute
The initial_0 execute對于Aldec Riviera Pro,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execute對于Cadence Xcelium,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execute對于Mentor Questa,輸出結果為
The initial_2 execute
The initial_1 execute
The initial_0 execute對于Synopsys VCS,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execut對于Icarus Verilog,輸出結果為
The initial_0 execute
The initial_1 execute
The initial_2 execute
????????依然可以注意到不同仿真器之間的差異,為什么不都是按照a賦值,always @(a)執行,b賦值,always @(b)執行,c賦值,always @(c)執行的順序執行呢?還是那句話,Verilog標準只規定了一個順序塊(begin end)中的所有語句是按照其先后順序執行的,并沒有保證其他行為。當a賦值后,always @(a)被調度了,但他不一定會在b賦值之前執行,有可能在執行完b賦值、c賦值之后再來執行always @(a),此時always @(b)和always @(c)也應該在隊列中,他們執行的先后順序是標準沒有保證的(但需要注意的是,最基本的調度者和被調度者的先后關系是無法改變的,即always @(a)一定在a賦值后,b、c亦然)。下面是一個經典的兩個結構交錯執行的例子。
//例4
`timescale 1ns/1ps
module test();reg a;wire b;initial begin#1;a = 1;#1;a = 0;$display("b is %b", b);endassign b = a;endmodule
? ? ? ? ?當時間來到1ns,a被賦值為1,同時觸發assign結構,對b連續賦值為1。1ns后,a被更改為0,緊接著打印b的值,結果是1還是0呢?
對于Mentor Modelsim SE,輸出結果為
b is 1對于Aldec Riviera Pro,輸出結果為
b is 1對于Cadence Xcelium,輸出結果為
b is 1對于Mentor Questa,輸出結果為
b is 1對于Synopsys VCS,輸出結果為
b is 0對于Icarus Verilog,輸出結果為
b is 0
????????上面的結果顯示,仿真器可能在執行了a=0的賦值后立刻執行被調度的assign連續賦值,再返回initial結構執行顯示語句,也可能繼續執行下面的顯示語句,再去執行被調度的assign連續賦值。
? ? ? ? 這就給了我們一個啟示,在改變了一個全組合邏輯的電路的輸入后,緊接著顯示輸出,可能無法得到更新后的輸出。一個更好的做法是給$display語句一定的延時,這樣就能確保在執行$display前,連續賦值已經執行完畢,因為不同時間的語句執行是不會產生競爭的。
//例5
`timescale 1ns/1ps
module test();reg a;wire b;initial begin#1;a = 1;#1;a = 0;#0.01 $display("b is %b", b);endassign b = a;endmodule