最近總是遇到systemverilog的賦值問題,檢視了一下手冊發現SV的賦值方式總的還是繼承了verilog的賦值方式,而且verilog賦值方面的資料比較多,所以就寫了先寫一篇關於verilog的賦值總結。
連續賦值
連續賦值就是一旦賦值,輸出將隨輸入改變而變化,一旦修改輸入則立刻體現在輸出上。
input a,b;
wire out;
assign out = a & b;
- 要求賦值左側必須為net型
- 關鍵詞assign
- 賦值過程類似於物理場景的導線連線,out將跟隨a異或b的電路變化而變化,一旦a或b有修改則立刻體現在輸出上。
- 連續賦值屬於併發執行,與書寫順序無關,不論在何時賦值都會在一開始執行。
- 對於同一引數多次賦值,視情況和強度而定,但不要進行該操作。
- 常用於組合邏輯
過程賦值
過程賦值只會在賦值時刻進行改變,其餘時間保持不變,而且根據使用的賦值符號=
和<=
,分為阻塞賦值和非阻塞賦值。
阻塞賦值 =
//例1
initial begin
ai = 4'd1 ; //(1)
ai = 4'd2 ; //(2)
//ai 最此刻為 2
#20 ; //(3)
ai = 4'd3 ; //(4)
bi = 4'd4 ; //(5)
value_blk = ai + bi ; //(6)
//value_blk 最終為7
#40;
end
- 要求賦值左側必須為reg、integer, real, time-variable, or memory(等非net型);
- 賦值過程在initial或always塊中;
- 阻塞賦值類似於C語言的賦值方式,後面的賦值會覆蓋掉前面的賦值,執行順序為1->2->...->6,類似軟體程式設計的賦值方式,隨時賦值隨時開始。
- 最終結果與書寫順序相關.
- 常用於testbench測試用例/組合邏輯.
非阻塞賦值 =>
//例1
initial begin
ai = 4'd1 ; //(1)
bi = 4'd2 ; //(2)
//ai和bi的初始值分別為1,2
#20 ; //(3)
//非阻塞賦值
ai <= 4'd3 ; //(4)
bi <= 4'd4 ; //(5)
value_nonblk <= ai + bi ; //(6)
#40;
//value_blk 最終為3
end
- 要求賦值左側必須為reg型
- 關鍵詞為initial或always
- 非阻塞賦值類似於物理電路中的時序電路,其中可以這樣理解程式碼執行順序,1->2->3之後其他的非阻塞賦值(4)(5)(6)不著急執行,而是列入到"事件佇列"中,一直存到#40需要被執行前,即下一個時刻需要執行前,(4)(5)(6)將會被同時執行,此時對於value_nonblk而言其對應的ai和bi依舊是1和2,因此結果為3,可用於時序電路建模。
- 最終結果與書寫順序相關
- 常用於時序邏輯
下面使用阻塞賦值和非阻塞分別描述構成的組合邏輯和時序邏輯。
module top_module(
input a, output reg out1, output reg out2);
always@(*) begin
out1 = a; //a的值直接到out1
out2 = out1; //當前out1直接賦給out2
end //也就是說a的值直接out2
endmodule
阻塞賦值在always(*)
塊下,直接綜合時形成了如下的組合邏輯電路。
module top_module(
input a, input clk, output reg out1, output reg out2);
always@(posedge clk) begin
out1 <= a; //a的值賦給out1
out2 <= out1; //此時左側的out1並不是a的大小,而是上一週期的值,因此這時是將上一週期的out1的值賦給out2
end
endmodule
而非阻塞賦值在always(posedge clk)
塊下,在綜合時形成了如下時序電路。
⚠️注意
雖然阻塞賦值always@(*) out_block = a & b;
和連續賦值assign out = a & b;
賦值方式不同,而且左側採用的分別為reg型別和net型別,但最終綜合出來卻是一樣的組合邏輯電路,都不會出現暫存器,這說明宣告的型別與合成的硬體無關。
同樣賦值過程並完全等同於硬體實現過程always@(posedge clk) a <= 1;
與always@(posedge clk) a = 1;
兩者也並沒有區別,a作為輸出都有暫存器的出現,也就是賦值方式與硬體實現過程並不相同。 這主要是Verilog語法的歷史遺留問題, 只採用宣告的型別和賦值方式很難區分,而systemverilog乾脆直接用logic代替了wire和reg兩種型別,對宣告的型別不做區分【難辦,難辦就別辦了~~】,因此瞭解綜合後的硬體電路,要重點關注語法本身是組合還是時序。
因此作為一個ICer,不能為了炫技而採用各種花裡胡哨的技巧,而是採用一些原則來保證自己的程式碼可以更便於觀察。因此為了更清晰的描述出確定的電路, 在使用非阻塞與阻塞賦值時最好遵循以下原則:
- 時序電路建模時,使用非阻塞賦值 ;
- 鎖存器電路建模時,使用非阻塞賦值 ;
- 使用always塊寫組合邏輯電路時,採用阻塞賦值;
- 在同一個always塊中同時建立時序和組合邏輯電路時,用非阻塞賦值 ;
- 在同一個always塊中不要同時使用非阻塞賦值和阻塞賦值 ;
- 不要在多個always塊中,為一個變數賦值;
- 用$strobe系統任務來顯示用非阻塞賦值的變數值;
- 在賦值時不要使用#0延遲【有時間了再去聊原因】;
遵循以上邏輯可以消除90%-100%的在模擬中產生的競爭冒險現象,有助於正確編寫可綜合硬體。
過程連續賦值
連續賦值透過assign語句驅動net型別變數,而過程賦值透過initial和always塊驅動reg型別的變數。這兩種驅動方式基本上可以構成常用的電路,但verilog又給出了一種賦值方式,即過程連續賦值,可以透過覆蓋現有的賦值,並在一定時期內驅動net、reg資料變數。
主要有兩對過程連續賦值:assign-deassign
和force-release
。但這種賦值方式最好不要用於設計,會使得程式碼難以理解,更多用於testbench,在測試時,為出現對應結果,將設計中某些必要的訊號強制賦為期待值。
assign-deassign
assign透過在有一段時間內覆蓋掉現有的過程賦值控制的reg變數的值。並在執行deassign之後,就會釋放掉過程連續賦值,但可以保持一段時間,直到其他賦值進行修改。
module assign_deassign_ex;
reg [3:0] d1;
initial begin
$monitor("At time T = %0t: d1 = %0d", $time, d1);
d1 = 5;
#20 d1 = 7;
end
initial begin
#5;
assign d1 = 3; //直接覆蓋掉d1從5->3
#5 deassign d1; //d1釋放之後,d1維持一段時間,在#20時刻從5變為7
$display("At time T = %0t: deassign d1", $time);
end
endmodule
//輸出結果
At time T = 0: d1 = 5
At time T = 5: d1 = 3
At time T = 10: deassign d1
At time T = 20: d1 = 7
force-release
force 和 release 語句透過在一段時間內覆蓋現有的過程、連續或過程連續賦值來控制 net 和 reg 資料型別變數值,force的許可權比assign要高,可以覆蓋掉其他的賦值方式。release同deassign一樣,可以釋放掉賦值,但對於過程賦值和過程連續賦值前可以保持前一個值。而對於net型別,直接恢復到前一個連續賦值的值。
module assign_deassign_ex;
reg [3:0] d1;
wire [3:0] d2;
assign d2 = 2;
initial begin
$monitor("At time T = %0t: d1 = %0d, d2 = %0d", $time, d1, d2);
d1 = 5;
#20 d1 = 7;
end
initial begin
#5;
$display("At time T = %0t: force d1 and d2", $time);
force d1 = 3; //強制將d1賦值為3
force d2 = 4; //d2賦值為4
#5 release d1; //d1可以保持為3,一直到#20恢復為7
release d2; //d2一旦釋放則恢復到2
$display("At time T = %0t: release d1 and d2", $time);
end
endmodule
//輸出
At time T = 0: d1 = 5, d2 = 2
At time T = 5: force d1 and d2
At time T = 5: d1 = 3, d2 = 4
At time T = 10: release d1 and d2
At time T = 10: d1 = 3, d2 = 2
At time T = 20: d1 = 7, d2 = 2
參考文獻
[1] Procedural continuous assignments - VLSI Verify-教程類
[2] Verilog Assignments-教程類
[3] Verilog 過程連續賦值-教程類
[4] How does SystemVerilog force work? -問答類