Latch 的含義
◆鎖存器(Latch),是電平觸發的存儲單元,數據存儲的動作取決于輸入時鐘(或者使能)信號的電平值。僅當鎖存器處于使能狀態時,輸出才會隨著數據輸入發生變化。
當電平信號無效時,輸出信號隨輸入信號變化,就像通過了緩沖器;當電平有效時,輸出信號被鎖存。激勵信號的任何變化,都將直接引起鎖存器輸出狀態的改變,很有可能會因為瞬態特性不穩定而產生振蕩現象。
鎖存器示意圖如下:
◆觸發器(flip-flop),是邊沿敏感的存儲單元,數據存儲的動作(狀態轉換)由某一信號的上升沿或者下降沿進行同步的(限制存儲單元狀態轉換在一個很短的時間內)。
觸發器示意圖如下:
◆寄存器(register),在 Verilog 中用來暫時存放參與運算的數據和運算結果的變量。一個變量聲明為寄存器時,它既可以被綜合成觸發器,也可能被綜合成 Latch,甚至是 wire 型變量。
但是大多數情況下我們希望它被綜合成觸發器,但是有時候由于代碼書寫問題,它會被綜合成不期望的 Latch 結構。
◆Latch 的主要危害有:1)輸入狀態可能多次變化,容易產生毛刺,增加了下一級電路的不確定性;2)在大部分 FPGA 的資源中,可能需要比觸發器更多的資源去實現 Latch 結構;3)鎖存器的出現使得靜態時序分析變得更加復雜。
Latch 多用于門控時鐘(clock gating)的控制,設計時一般應當避免 Latch 的產生。
if 結構不完整
◆組合邏輯中,不完整的 if - else 結構,會產生 Latch。
例如下面的模型,if 語句中缺少 else 結構,系統默認 else 的分支下寄存器 q 的值保持不變,即具有存儲數據的功能,所以寄存器 q 會被綜合成 Latch 結構。
module module1_Latch1(
input data,
input en ,
output reg q) ;
always @(*) begin
if (en) q = data ;
end
endmodule
◆避免此類 Latch 的方法主要有 2 種,一種是補全 if-else 結構,或者對信號賦初值。
例如,上面模型中的 always 語句,可以改為以下兩種形式:
// 補全條件分支結構
always @(*) begin
if (en) q = data ;
else q = 1'b0 ;
end
//賦初值
always @(*) begin
q = 1'b0 ;
if (en) q = data ; //如果en有效,改寫q的值,否則q會保持為0
end
◆但是在時序邏輯中,不完整的 if - else 結構,不會產生 Latch,例如下面模型。
這是因為,q 寄存器具有存儲功能,且其值在時鐘的邊沿下才會改變,這正是觸發器的特性。
module module1_ff(
input clk ,
input data,
input en ,
output reg q) ;
always @(posedge clk) begin
if (en) q <= data ;
end
endmodule
◆在組合邏輯中,當條件語句中有很多條賦值語句時,每個分支條件下賦值語句的不完整也是會產生 Latch。
其實對每個信號的邏輯拆分來看,這也相當于是 if-else 結構不完整,相關寄存器信號缺少在其他條件下的賦值行為。例如:
module module1_Latch11(
input data1,
input data2,
input en ,
output reg q1 ,
output reg q2) ;
always @(*) begin
if (en) q1 = data1 ;
else q2 = data2 ;
end
endmodule
這種情況也可以通過補充完整賦值語句或賦初值來避免 Latch。例如:
always @(*) begin
//q1 = 0; q2 = 0 ; //或在這里對 q1/q2 賦初值
if (en) begin
q1 = data1 ;
q2 = 1'b0 ;
end
else begin
q1 = 1'b0 ;
q2 = data2 ;
end
end
case 結構不完整
case 語句產生 Latch 的原理幾乎和 if 語句一致。在組合邏輯中,當 case 選項列表不全且沒有加 default 關鍵字,或有多個賦值語句不完整時,也會產生 Latch。例如:
module module1_Latch2(
input data1,
input data2,
input [1:0] sel ,
output reg q ) ;
always @(*) begin
case(sel)
2'b00: q = data1 ;
2'b01: q = data2 ; //缺少 default 選項
endcase
end
endmodule
消除此種 Latch 的方法也是 2 種,將 case 選項列表補充完整,或對信號賦初值。
補充完整 case 選項列表時,可以羅列所有的選項結果,也可以用 default 關鍵字來代替其他選項結果。
例如,上述 always 語句有以下 2 種修改方式。
always @(*) begin
case(sel)
2'b00: q = data1 ;
2'b01: q = data2 ;
default: q = 1'b0 ;
endcase
end
always @(*) begin
case(sel)
2'b00: q = data1 ;
2'b01: q = data2 ;
2'b10, 2'b11 :
q = 1'b0 ;
endcase
end
原信號賦值或判斷
在組合邏輯中,如果一個信號的賦值源頭有其信號本身,或者判斷條件中有其信號本身的邏輯,則也會產生 Latch。因為此時信號也需要具有存儲功能,但是沒有時鐘驅動。此類問題在 if 語句、case 語句、問號表達式中都可能出現,例如:
//signal itself as a part of condition
reg a, b ;
always @(*) begin
if (a & b) a = 1'b1 ; //a - > Latch
else a = 1'b0 ;
end
//signal itself are the assigment source
reg c;
wire [1:0] sel ;
always @(*) begin
case(sel)
2'b00: c = c ; //c - > Latch
2'b01: c = 1'b1 ;
default: c = 1'b0 ;
endcase
end
//signal itself as a part of condition in “? expression”
wire d, sel2;
assign d = (sel2 && d) ? 1'b0 : 1'b1 ; //d - > Latch
避免此類 Latch 的方法,就只有一種,即在組合邏輯中避免這種寫法,信號不要給信號自己賦值,且不要用賦值信號本身參與判斷條件邏輯。
例如,如果不要求立刻輸出,可以將信號進行一個時鐘周期的延時再進行相關邏輯的組合。上述第一個產生 Latch 的代碼可以描述為:
reg a, b ;
reg a_r ;
always (@posedge clk)
a_r <= a ;
always @(*) begin
if (a_r & b) a = 1'b1 ; //there is no Latch
else a = 1'b0 ;
end
敏感信號列表不完整
如果組合邏輯中 always@() 塊內敏感列表沒有列全,該觸發的時候沒有觸發,那么相關寄存器還是會保存之前的輸出結果,因而會生成鎖存器。
這種情況,把敏感信號補全或者直接用 always@(*) 即可消除 Latch。
小結
總之,為避免 Latch 的產生,在組合邏輯中,需要注意以下幾點:
1)if-else 或 case 語句,結構一定要完整
2)不要將賦值信號放在賦值源頭,或條件判斷中
3)敏感信號列表建議多用 always@(*)
-
FPGA設計
+關注
關注
9文章
428瀏覽量
26489 -
寄存器
+關注
關注
31文章
5325瀏覽量
120045 -
Verilog
+關注
關注
28文章
1345瀏覽量
109994 -
鎖存器
+關注
關注
8文章
905瀏覽量
41448 -
觸發器
+關注
關注
14文章
1996瀏覽量
61056
發布評論請先 登錄
相關推薦
評論