Verilog 避免Latch

2022-05-18 09:58 更新

關(guān)鍵詞:觸發(fā)器,鎖存器

Latch 的含義

鎖存器(Latch),是電平觸發(fā)的存儲單元,數(shù)據(jù)存儲的動作取決于輸入時鐘(或者使能)信號的電平值。僅當(dāng)鎖存器處于使能狀態(tài)時,輸出才會隨著數(shù)據(jù)輸入發(fā)生變化。

當(dāng)電平信號無效時,輸出信號隨輸入信號變化,就像通過了緩沖器;當(dāng)電平有效時,輸出信號被鎖存。激勵信號的任何變化,都將直接引起鎖存器輸出狀態(tài)的改變,很有可能會因為瞬態(tài)特性不穩(wěn)定而產(chǎn)生振蕩現(xiàn)象。

鎖存器示意圖如下:


觸發(fā)器(flip-flop),是邊沿敏感的存儲單元,數(shù)據(jù)存儲的動作(狀態(tài)轉(zhuǎn)換)由某一信號的上升沿或者下降沿進行同步的(限制存儲單元狀態(tài)轉(zhuǎn)換在一個很短的時間內(nèi))。

觸發(fā)器示意圖如下:


寄存器(register),在 Verilog 中用來暫時存放參與運算的數(shù)據(jù)和運算結(jié)果的變量。一個變量聲明為寄存器時,它既可以被綜合成觸發(fā)器,也可能被綜合成 Latch,甚至是 wire 型變量。但是大多數(shù)情況下我們希望它被綜合成觸發(fā)器,但是有時候由于代碼書寫問題,它會被綜合成不期望的 Latch 結(jié)構(gòu)。

Latch 的主要危害有:

  • 輸入狀態(tài)可能多次變化,容易產(chǎn)生毛刺,增加了下一級電路的不確定性;
  • 在大部分 FPGA 的資源中,可能需要比觸發(fā)器更多的資源去實現(xiàn) Latch 結(jié)構(gòu);
  • 鎖存器的出現(xiàn)使得靜態(tài)時序分析變得更加復(fù)雜。

Latch 多用于門控時鐘(clock gating)的控制,一般設(shè)計時,我們應(yīng)當(dāng)避免 Latch 的產(chǎn)生。

if 結(jié)構(gòu)不完整

組合邏輯中,不完整的 if - else 結(jié)構(gòu),會產(chǎn)生 latch。

例如下面的模型,if 語句中缺少 else 結(jié)構(gòu),系統(tǒng)默認(rèn) else 的分支下寄存器 q 的值保持不變,即具有存儲數(shù)據(jù)的功能,所以寄存器 q 會被綜合成 latch 結(jié)構(gòu)。

module module1_latch1(
    input       data,
    input       en ,
    output reg  q) ;
   
    always @(*) begin
        if (en) q = data ;
    end

endmodule

避免此類 latch 的方法主要有 2 種,一種是補全 if-else 結(jié)構(gòu),或者對信號賦初值。

例如,上面模型中的always語句,可以改為以下兩種形式:

    // 補全條件分支結(jié)構(gòu)    
    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 結(jié)構(gòu),不會產(chǎn)生 latch,例如下面模型。

這是因為,q 寄存器具有存儲功能,且其值在時鐘的邊沿下才會改變,這正是觸發(fā)器的特性。

module module1_ff(
    input       clk ,
    input       data,
    input       en ,
    output reg  q) ;
   
    always @(posedge clk) begin
        if (en) q <= data ;
    end

endmodule

在組合邏輯中,當(dāng)條件語句中有很多條賦值語句時,每個分支條件下賦值語句的不完整也是會產(chǎn)生 latch。

其實對每個信號的邏輯拆分來看,這也相當(dāng)于是 if-else 結(jié)構(gòu)不完整,相關(guān)寄存器信號缺少在其他條件下的賦值行為。例如:

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 結(jié)構(gòu)不完整

case 語句產(chǎn)生 Latch 的原理幾乎和 if 語句一致。在組合邏輯中,當(dāng) case 選項列表不全且沒有加 default 關(guān)鍵字,或有多個賦值語句不完整時,也會產(chǎn)生 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 ;
        endcase
    end

endmodule

當(dāng)然,消除此種 latch 的方法也是 2 種,將 case 選項列表補充完整,或?qū)π盘栙x初值。

補充完整 case 選項列表時,可以羅列所有的選項結(jié)果,也可以用 default 關(guān)鍵字來代替其他選項結(jié)果。

例如,上述 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

原信號賦值或判斷

在組合邏輯中,如果一個信號的賦值源頭有其信號本身,或者判斷條件中有其信號本身的邏輯,則也會產(chǎn)生 latch。因為此時信號也需要具有存儲功能,但是沒有時鐘驅(qū)動。此類問題在 if 語句、case 語句、問號表達式中都可能出現(xiàn),例如:

    //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 的方法,就只有一種,即在組合邏輯中避免這種寫法,信號不要給信號自己賦值,且不要用賦值信號本身參與判斷條件邏輯。

例如,如果不要求立刻輸出,可以將信號進行一個時鐘周期的延時再進行相關(guān)邏輯的組合。上述第一個產(chǎn)生 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@() 塊內(nèi)敏感列表沒有列全,該觸發(fā)的時候沒有觸發(fā),那么相關(guān)寄存器還是會保存之前的輸出結(jié)果,因而會生成鎖存器。

這種情況,把敏感信號補全或者直接用 always@(*) 即可消除 latch。

小結(jié)

總之,為避免 latch 的產(chǎn)生,在組合邏輯中,需要注意以下幾點:

  1. if-else 或 case 語句,結(jié)構(gòu)一定要完整
  2. 不要將賦值信號放在賦值源頭,或條件判斷中
  3. 敏感信號列表建議多用 always@(*)

點擊這里下載源碼


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號