🔍 問題本質
在一個 always @(posedge clk)
塊中,所有的代碼都是順序執行的。但這不意味著它就像軟件一樣“一條一條執行”,因為最終是電路!電路是并行存在的!
Verilog 是硬件描述語言(HDL),你寫的 if
看起來像判斷語句,實際上是用來描述硬件行為的。比如賦值,就是描述電路連線和寄存器觸發器之間的連接與更新方式。
🌰例子一:多個 if 控制不同變量(并行)
always @(posedge clk) beginif (a) x <= 1;if (b) y <= 2;
end
? 分析:
-
x
和y
是兩個不同的變量(信號) -
if (a)
和if (b)
雖然寫在一個 always 里,但它們控制的是不同的變量 -
所以它們最終在硬件上是并行存在的,誰滿足條件誰賦值,互不干擾
🎯 打比方:
你想象有兩個電工:
-
電工A:看到
a=1
就把燈x
打開 -
電工B:看到
b=1
就把燈y
點亮
兩個電工各干各的,不會打架。
🌰例子二:多個 if 控制同一個變量(后面會覆蓋前面)
always @(posedge clk) beginif (a) x <= 1;if (b) x <= 2;
end
?? 注意:
-
現在兩個
if
都是對x
賦值 -
如果
a=1
,b=1
,那么兩個賦值都觸發,但最后一個有效
💥 為什么?
Verilog 順序描述 => 最后一個賦值會“覆蓋”前面那個 所以:
-
如果
a=1
,b=1
,x <= 2
-
如果
a=1
,b=0
,x <= 1
-
如果
a=0
,b=1
,x <= 2
🎯 打比方:
還是兩個電工:
-
電工A先來,把燈
x
調到1 -
電工B后到,把燈
x
調到2
最后你看到燈 x
是2,電工A的動作被“覆蓋”了
?
這里如果有疑惑,可以看這篇博客:
【verilog】多個 if 控制同一個變量(后面會覆蓋前面)非阻塞賦值真的并行嗎?
🌰例子三:中間插入打拍語句(可以)
always @(posedge clk) beginif (a)x <= 1;x_d1 <= x; // 打拍:讓x的值走一拍if (b)x <= 2;
end
?? 這個時候 x <= 1
先執行,但后面又 x <= 2
,會不會有問題?
會被覆蓋!除非你做數據隔離或選擇性賦值
🌰例子四:多個 if else + case 都是并行行為的體現
always @(posedge clk) beginif (sel == 0)x <= 1;else if (sel == 1)x <= 2;case(sel)2: x <= 3;3: x <= 4;endcase
end
😵? 分析:
-
如果
sel == 2
,那么:上面 if-else 不觸發,case 觸發x <= 3
-
如果
sel == 1
,if-else 觸發x <= 2
,case 不觸發
但是注意,如果你寫多個能影響同一個變量的賦值語句,它們中間的優先級以代碼順序為準
? 結論總結
情況 | 行為 | 說明 |
---|---|---|
多個 if 控制不同變量 | 并行 | 誰條件滿足誰執行,互不干擾 |
多個 if 控制同一個變量 | 順序 | 后面賦值覆蓋前面 |
if/else + case 控制同一變量 | 順序+可能沖突 | 后面的 case 會覆蓋前面 if 的賦值 |
插入打拍語句 | 合法 | 不影響并行結構,但要注意覆蓋風險 |
多個嵌套 if | 按照語句順序 | 但并不表示電路是串行運行,是條件表達上的判斷順序 |
😎 建議寫法
為了代碼清晰和避免莫名其妙的賦值沖突:
-
推薦使用
if-else if-else
結構統一處理一個變量的所有情況 -
或者先保存中間變量,最后統一賦值
-
打拍信號最好和主賦值語句分開塊寫,減少混亂