圖二示意了一個簡單的時序(sequential)流水線寄存器。
從例5到例8列舉了一個工程師可能選用的4種使用阻塞賦值為它建模的方案。
module pipeb1 (q3, d, clk);
output [7:0] q3;
input [7:0] d;
input clk;
reg [7:0] q3, q2, q1;
always @(posedge clk) begin
q1 = d;
q2 = q1;
q3 = q2;
end
endmodule
Example 5 - Bad blocking-assignment sequential coding style #1
(外注:綜合報告:WARNING: Signal <q1> is assigned but never used.
WARNING:Signal <q2> is assigned but never used.)
在例5里面,接連的“阻塞賦值”命令將使得輸入D連續(xù)地覆蓋所有寄存器輸出(在下一個posedge clk到來時)。即在每一個clk邊沿,輸入值被無延遲地傳到q3的輸出。這很明顯并沒有建立一個流水線而只是為一個寄存器建模------實際綜合結(jié)果將是上面的圖3。
module pipeb2 (q3, d, clk);
output [7:0] q3;
input [7:0] d;
input clk;
reg [7:0] q3, q2, q1;
|
q3 = q2;
q2 = q1;
q1 = d;
end
endmodule
Example 6 - Bad blocking-assignment sequential coding style #2 - but it works!
上面的pipeb2里面,阻塞賦值被仔細地安排了次序以使得行為仿真正確。這種建模同樣也可以得到正確的綜合結(jié)果。(外注:Found 3-bit shift register for signal <q3>.
Summary: inferred 8 Shift register(s). )
在下面的例3里,“阻塞賦值”被安排在不同的always塊里面。這樣Verilog標準允許以任意的次序 來仿真執(zhí)行3個always塊-------這也許會使得該流水線仿真結(jié)果產(chǎn)生錯誤,因為這產(chǎn)生了Verilog競爭條件。由不同的always塊執(zhí)行順序會產(chǎn)生不同的結(jié)果。盡管這樣,它的綜合結(jié)果將是正確的! 這就意味著綜合前仿真和綜合后仿真不匹配。Pipeb4或者其它的類似always塊同樣也許會產(chǎn)生仿真與綜合不匹配的結(jié)果------綜合結(jié)果是對的,但是仿真結(jié)果也許不正確。(外注:pipeb4只是又顛倒了一下次序,對實際仿真次序卻不產(chǎn)生決定作用.)
module pipeb3 (q3, d, clk);
output [7:0] q3;
input [7:0] d;
input clk;
reg [7:0] q3, q2, q1;
always @(posedge clk) q1=d;
always @(posedge clk) q2=q1;
always @(posedge clk) q3=q2;
endmodule
Example 7 - Bad blocking-assignment sequential coding style #3
module pipeb4 (q3, d, clk);
output [7:0] q3;
input [7:0] d;
input clk;
reg [7:0] q3, q2, q1;
always @(posedge clk) q2=q1;
always @(posedge clk) q3=q2;
always @(posedge clk) q1=d;
endmodule
Example 8 - Bad blocking-assignment sequential coding style #4
假如每一個上面的例子都改用“非阻塞賦值”那么將會都能得到正確的仿真結(jié)果,并綜合出想要的流水線邏輯。
module pipen1 (q3, d, clk);
output [7:0] q3;
input [7:0] d;
input clk;
reg [7:0] q3, q2, q1;
always @(posedge clk) begin
q1 <= d;
q2 <= q1;
q3 <= q2;
end
endmodule
Example 9 - Good nonblocking-assignment sequential coding style #1
module pipen2 (q3, d, clk);
output [7:0] q3;
input [7:0] d;
input clk;
reg [7:0] q3, q2, q1;
always @(posedge clk) begin
q3 <= q2;
q2 <= q1;
q1 <= d;
end
endmodule
Example 10 - Good nonblocking-assignment sequential coding style #2
module pipen3 (q3, d, clk);
output [7:0] q3;
input [7:0] d;
input clk;
reg [7:0] q3, q2, q1;
always @(posedge clk) q1<=d;
always @(posedge clk) q2<=q1;
always @(posedge clk) q3<=q2;
endmodule
Example 11 - Good nonblocking-assignment sequential coding style #3
module pipen4 (q3, d, clk);
output [7:0] q3;
input [7:0] d;
input clk;
reg [7:0] q3, q2, q1;
always @(posedge clk) q2<=q1;
always @(posedge clk) q3<=q2;
always @(posedge clk) q1<=d;
endmodule
Example 12 - Good nonblocking-assignment sequential coding style #4
從上面的流水線編碼風(fēng)格例子可以看出:
僅一個“阻塞賦值”的描述可以保證仿真正確。
三個“阻塞賦值”的描述可以得到正確綜合結(jié)果。
四個“非阻塞賦值”描述都可以保證仿真結(jié)果正確。
四個“非阻塞賦值”描述都可以得到正確綜合結(jié)果。(原文這一條是“阻塞賦值”大概是有誤?)
雖然,如果限制在一個always塊里面,并小心地組織好一個always塊里面阻塞賦值的次序(外注:一個always塊里面的幾個“阻塞賦值”是按照陳述的次序串行仿真執(zhí)行的,綜合執(zhí)行次序也是?)同樣可能會正確地為流水線建模;但是另一方面,我們可以很容易地使用“非阻塞賦值”來為上面的流水線建模------它們既可以正確仿真也可以正確綜合。
9.0 阻塞賦值 & 簡單例子
有許多將Verilog和Verilog綜合的書,它們舉了很多成功地利用“阻塞賦值”為一些簡單的時序電路建模的小例子。例13是一個在大多數(shù)Verilog書本里用來為一個觸發(fā)器(flip-flop)建模的例子(這是簡單而有缺陷的阻塞賦值建模,但是它確實可以工作):
module dffb (q, d, clk, rst);
output q;
input d, clk, rst;
reg q;
always @(posedge clk)
if (rst) q = 1'b0;
else q = d;
endmodule
Example 13 - Simple flawed blocking-assignment D-flipflop model - but it works!
如果工程師們想把所有的模塊(module)都集中到一個always里面描述,“阻塞賦值”可以用來正確地為所需要的邏輯建模、仿真和綜合。但是不幸的是這個原因?qū)е铝讼矚g在其它情況下也使用“阻塞賦值”的習(xí)慣,并且更復(fù)雜的時序always塊將會產(chǎn)生競爭條件------在前面已經(jīng)詳細闡述過。
module dffx (q, d, clk, rst);
output q;
input d, clk, rst;
reg q;
always @(posedge clk)
if (rst) q <= 1'b0;
else q <= d;
endmodule
Example 14 - Preferred D-flipflop coding style with nonblocking assignments
應(yīng)該努力養(yǎng)成使用“非阻塞賦值”為 所有的 時序邏輯建模的習(xí)慣------象上面的例14一樣------即使是為了對付任何一個簡單的模塊。
下面考慮一下一個稍微復(fù)雜的時序邏輯,一個線性反饋移位寄存器(Linear Feedback shift-Register)或稱之為LFSR。
10.0 為時序反饋建模 (Sequential feedback modeling)
一個LFSR是一種帶反饋環(huán)路(feedback loop)的時序邏輯。反饋環(huán)路(feedback loop)為工程師們帶來了一個難題使得他們試圖使用細心組織次序的“阻塞賦值”來為它正確建模,如下面的例子:
module lfsrb1 (q3, clk, pre_n);
output q3;
input clk, pre_n;
reg q3, q2, q1;
wire n1;
assign n1 = q1 ^ q3;
always @(posedge clk or negedge pre_n)
if (!pre_n) begin
q3 = 1'b1;
q2 = 1'b1;
q1 = 1'b1;
end
else begin
q3 = q2;
q2 = n1;
q1 = q3;
end
endmodule
Example 15 - Non-functional LFSR with blocking assignments(外注:綜合報告―――>
Register <q1> equivalent to <q3> has been removed
Found 1-bit register for signal <q3>.
Found 1-bit xor2 for signal <n1>.
Found 1-bit register for signal <q2>.)
Summary:
inferred 2 D-type flip-flop(s).
沒有辦法通過調(diào)整描述次序的方法來正確建模除非引入一個臨時的變量(
外注:例如引入“wire n2”――>
module xxxxx (q3, clk, pre_n);
output q3;
input clk, pre_n;
reg q3, q2, q1;
wire n1,n2;
assign n1 = q1 ^ q3;
assign n2 = q3;
always @(posedge clk or negedge pre_n)
if (!pre_n) begin
q3 = 1'b1;
q2 = 1'b1;
q1 = 1'b1;
end
else begin
q3 = q2;
q2 = n1;
q1 = n2;
end
endmodule
這樣可以得到正確的綜合結(jié)果:
Found 1-bit register for signal <q3>.
Found 1-bit xor2 for signal <n1>.
Found 1-bit register for signal <q1>.
Found 1-bit register for signal <q2>.
Summary:
inferred 3 D-type flip-flop(s).)。
可以通過把所有賦值弄到一個等式的方式(one-line equations)來避免使用臨時變量,例如下面的例16所示。但是現(xiàn)在編碼顯得更難于理解尤其當涉及的表達式更大更長時,編寫代碼和調(diào)試都變得比較困難,因此不鼓勵使用這種風(fēng)格。
module lfsrb2 (q3, clk, pre_n);
output q3;
input clk, pre_n;
reg q3, q2, q1;
always @(posedge clk or negedge pre_n)
if (!pre_n) {q3,q2,q1} = 3'b111;
else {q3,q2,q1} = {q2,(q1^q3),q3};
endmodule
Example 16 - Functional but cryptic LFSR with blocking assignments
如果把例15和例16的阻塞賦值(blocking assignment)都替換為非阻塞賦值(nonblocking assignment),如下面例17和18所示,那么所有的仿真都將如我們對一個LFSR所期望的那樣。
module lfsrn1 (q3, clk, pre_n);
output q3;
input clk, pre_n;
reg q3, q2, q1;
wire n1;
assign n1 = q1 ^ q3;
always @(posedge clk or negedge pre_n)
if (!pre_n) begin
q3 <= 1'b1;
q2 <= 1'b1;
q1 <= 1'b1;
end
else begin
q3 <= q2;
q2 <= n1;
q1 <= q3;
end
endmodule
Example 17 - Functional LFSR with nonblocking assignments
module lfsrn2 (q3, clk, pre_n);
output q3;
input clk, pre_n;
reg q3, q2, q1;
always @(posedge clk or negedge pre_n)
if (!pre_n) {q3,q2,q1} <= 3'b111;
else {q3,q2,q1} <= {q2,(q1^q3),q3};
endmodule
Example 18 - Functional but cryptic LFSR with nonblocking assignments
根據(jù)8.0段例子pipeline和10.0段例子LFSR,我們推薦對所有時序邏輯建模時使用非阻塞賦值(nonblocking assignment)。相似的分析也將顯示出對latch建模時使用非阻塞賦值(nonblocking assignment)是最安全的。
#1: 當為時序邏輯建模,使用“非阻塞賦值”。
#2: 當為鎖存器(latch)建模,使用“非阻塞賦值”。
11.0 組合邏輯―使用阻塞賦值(blocking assignment)
用Verilog可以有很多種方法為組合邏輯建模,但是當使用always塊來為組合邏輯建模時,應(yīng)該使用阻塞賦值(blocking assignment)。
如果在某個always塊里面只有一個賦值(表達),那么使用阻塞或者非阻塞賦值都可以正確工作。但是如果您對養(yǎng)成好的編碼習(xí)慣有興趣的話,還是要“總是用阻塞賦值對組合邏輯建模”。
一些設(shè)計師建議非阻塞賦值不應(yīng)該只為編寫時序邏輯,它也可以用來編寫組合邏輯。當然對于簡單的組合邏輯always塊這是可以的,但是對于在一個always塊里面含有多個賦值陳述,例如例19含有and-or的陳述,使用了不含延遲(delay)的非阻塞賦值會造成仿真不正確,或者要使仿真正確您需要另外的添加敏感事件列表(sensitivity list entries),和“多登入路徑”(multiple passes)來貫穿always 塊以使得仿真正確。接下來的問題是從仿真需要多長時間來看,這是低效率的(外注:即降低仿真的performance)。
例19的y輸出建立在3個依次執(zhí)行的陳述上(外注:tmp1 <= a & b; tmp2 <= c & d; y <= tmp1 | tmp2;)。由于非阻塞賦值的LHS變量值更新是在對RHS表達式估值之后,所以tmp1和tmp2的值仍然是該always塊上一個登入口的值而不是在這一個仿真時間步(simulation time step)結(jié)束時被更新的值。因此y的值將受舊的tmp1和tmp2影響,而不是這次掃描過的always塊內(nèi)被更新的值。
module ao4 (y, a, b, c, d);
output y;
input a, b, c, d;
reg y, tmp1, tmp2;
always @(a or b or c or d) begin
tmp1 <= a & b;
tmp2 <= c & d;
y <= tmp1 | tmp2;
end
endmodule
Example 19 - Bad combinational logic coding style using nonblocking assignments
例20與例19是一樣的,不同之處在于tmp1和tmp2被添加到事件列表中去了。如第7段(section 7.0)中所述,在“非阻塞賦值更新事件隊列”中當非阻塞賦值更新LHS變量時,always塊將會“自觸發(fā)”并使用最新的tmp1和tmp2來更新y輸出?,F(xiàn)在y輸出值正確了因為增加使用了兩條“登入路徑”(two passes)貫穿整個always塊。使用更多的“登入路徑”來貫穿always塊等于降低仿真器的性能,因此如果可以有合理的一些代碼變化可以取代這種用法的話,就盡量避免這種用法。
module ao5 (y, a, b, c, d);
output y;
input a, b, c, d;
reg y, tmp1, tmp2;
always @(a or b or c or d or tmp1 or tmp2) begin
tmp1 <= a & b;
tmp2 <= c & d;
y <= tmp1 | tmp2;
end
endmodule
Example 20 - Inefficient multi-pass combinational logic coding style with nonblocking assignments
發(fā)展一個好的習(xí)慣可以避免使用“多登入路徑”(multiple passes)貫穿always塊,即使用阻塞賦值為組合邏輯建模。
module ao2 (y, a, b, c, d);
output y;
input a, b, c, d;
reg y, tmp1, tmp2;
always @(a or b or c or d) begin
tmp1 = a & b;
tmp2 = c & d;
y = tmp1 | tmp2;
end
endmodule
Example 21 - Efficient combinational logic coding style using blocking assignments
例21與例19一樣,不同之處只在于用阻塞賦值替代了非阻塞賦值。這保證了在一個“登入路徑”貫穿always后y輸出的正確(guarantee that the y-output assumes the correct value after only one pass through the always block?)。因此有下面的編碼方針:
#3: 當用always塊為組合邏輯建模,使用“阻塞賦值”