非同步復位同步釋放在實際專案中的應用

吃豆熊發表於2022-04-12

非同步復位同步釋放在實際專案中的應用

1 引言

最近看了“How do I reset my FPGA?”和一些時序分析的內容,發現之前ov5640影像採集專案中的幾個問題:

問題1:用了全域性時鐘復位,全域性復位一般具有高扇出(需要驅動的後級邏輯訊號多),因為它需要擴充套件到設計中的每一個觸發器。這樣會消耗大量的佈線資源,對器件的利用率和時序效能造成不利影響。

問題2:設計全採用了非同步復位,對毛刺敏感且復位結束會處於亞穩態。

問題3:在locked1,locked2為低時,時鐘是不穩定的,此時送入後續模組的是不穩定的時鐘,觸發器可能出現功能錯誤。

那麼我們是否需要重構全部的程式碼呢?其實也沒有必要。重構程式碼很麻煩,需要對所有18個模組進行程式碼修改,然後重新模擬十八次。

這裡我們選擇在頂層模組進行復位模組區域性化的劃分,若實現送入每一個模組的都是非同步復位同步釋放後的復位訊號,保證訊號已經同步,本質上也是一樣的。而且打兩拍之後的復位訊號送到時,一方面訊號對毛刺不敏感,不會受到脈衝干擾,另外一方面,也相當於復位訊號延後了一個或兩個clock,最後一個clock時兩個locked訊號均已穩定,保證系統正常工作。

2 程式碼

2.1 時鐘模組

首先,我們需要確定專案中各模組工作的時鐘域。此處有一點注意事項,我們pll2中的輸入時鐘為pll1生成的100Mhz時鐘而非外部時鐘,因此呼叫ip核時需要選用"no buffer"否則會報錯。我們呼叫的兩個pll程式碼如下:

//復位模組復位訊號
assign  rst_n = locked1 && locked2 && sys_rst_n;
//時鐘模組1例化
clk_gen clk_gen_inst
(
    .clk_125m            (clk_125m           ),     // output sdram clk
    .clk_shift_125m      (clk_shift_125m     ),     // output sdram output clk
    .clk_50m             (clk_50m            ),     // output ov5640 clk
    .clk_100m            (clk_100m           ),     // output pll2 clk
    .clk_24m             (clk_24m            ),     // output ov5640 output clk
    .reset               (~pll_rst_n1        ),     // input 
    .locked              (locked1            ),     // output
    .sys_clk             (sys_clk            )      // input sys_clk
);
//時鐘模組2例化
clk_gen_hdmi clk_gen_hdmi_inst
(
    .clk_74m             (clk_74m            ),     // output vga clk
    .clk_371m            (clk_371m           ),     // output hdmi tmds clk
    .reset               (~pll_rst_n2        ),     // input 
    .locked              (locked2            ),     // output 
    .clk_100m            (clk_100m           )      // input clk_100m pll1 output clk_100Mhz
);

此處我們會發現一個問題,我們後續模組的復位訊號是在時鐘穩定的情況下基礎上生成的,而實際上pll本身也有復位訊號,為了保障整個工程的穩定性,我們需要對pll的復位訊號也進行非同步復位同步釋放

圖1 結果時序圖

2.2 復位模組

下面是我們設定的復位模組程式碼,在時鐘穩定後, 將工作時鐘,外部輸入復位訊號送入模組後,在各模組時鐘下同步後的復位訊號輸出至各模組

那麼這裡有個問題,如果在時鐘沒穩定前,如果有復位訊號輸入本模組會不會出現亞穩態的問題呢,實際上是不會的,我們可以看復位模組的復位訊號,在時鐘訊號沒穩定下,locked1,locked2常為0,不存在訊號變化也就不存在亞穩態的問題了。

復位模組復位訊號程式碼如下:

//復位模組復位訊號
assign  rst_n = locked1 && locked2 && sys_rst_n;

復位模組程式碼如下:

//**************************************************************************
// *** 名稱 : sys_reset.v
// *** 作者 : 吃豆熊
// *** 日期 : 2021-4-1
// *** 描述 : 非同步復位同步釋放模組
//**************************************************************************

module sys_reset
//========================< 埠 >==========================================
(
    input   wire                sys_clk,                  //系統時鐘
    input   wire                pll_clk1, 
    input   wire                pll_clk2, 
    input   wire                vga_clk,  
    input   wire                sdram_clk,
    input   wire                hdmi_clk, 

    input   wire                sys_rst_n,                //外界輸入復位
    input   wire                rst_n,                    //兩級時鐘pll穩定後復位

    output   wire               pll_rst_n1,               //第一級pll復位    非同步復位同步釋放後復位
    output   wire               pll_rst_n2,               //第二級pll復位
    output   wire               camera_rst_n,             //攝像頭模組復位
    output   wire               vga_rst_n,                //vga模組復位
    output   wire               sdram_rst_n,              //sdram模組復位
    output   wire               hdmi_rst_n                //hdmi模組復位
);

//========================< 訊號 >==========================================
//第一級pll復位
reg                         pll_rst_n_reg1;
reg                         pll_rst_n_reg2;
//第二級pll復位
reg                         pll_rst_n_reg3;
reg                         pll_rst_n_reg4;
//攝像頭復位
reg                         camera_rst_n_reg1;
reg                         camera_rst_n_reg2;
//sdram復位
reg                         sdram_rst_n_reg1;
reg                         sdram_rst_n_reg2;
//vga復位
reg                         vga_rst_n_reg1;
reg                         vga_rst_n_reg2;
//hdmi復位
reg                         hdmi_rst_n_reg1;
reg                         hdmi_rst_n_reg2;

//==========================================================================
//==    訊號生成
//==========================================================================
//第一級pll復位訊號
always@(posedge pll_clk1 or negedge sys_rst_n )begin
    if(sys_rst_n == 1'b0)begin
        pll_rst_n_reg1 <= 1'b0;
        pll_rst_n_reg2 <= 1'b0;
   end
  else begin
        pll_rst_n_reg1 <= 1'b1;
        pll_rst_n_reg2 <= pll_rst_n_reg1;
    end
end
 assign pll_rst_n1 = pll_rst_n_reg2;
//第二級pll復位訊號
 always@(posedge pll_clk2 or negedge sys_rst_n )begin
    if(sys_rst_n == 1'b0)begin
        pll_rst_n_reg3 <= 1'b0;
        pll_rst_n_reg4 <= 1'b0;
   end
  else begin
        pll_rst_n_reg3 <= 1'b1;
        pll_rst_n_reg4 <= pll_rst_n_reg3;
    end
end
 assign pll_rst_n2 = pll_rst_n_reg4;

//攝像頭復位訊號
always@(posedge pll_clk2 or negedge rst_n )begin
    if(rst_n == 1'b0)begin
        camera_rst_n_reg1 <= 1'b0;
        camera_rst_n_reg2 <= 1'b0;
   end
  else begin
        camera_rst_n_reg1 <= 1'b1;
        camera_rst_n_reg2 <= camera_rst_n_reg1;
    end
end
 assign camera_rst_n = camera_rst_n_reg2;

 //sdram復位訊號
always@(posedge sdram_clk or negedge rst_n )begin
    if(rst_n == 1'b0)begin
        sdram_rst_n_reg1 <= 1'b0;
        sdram_rst_n_reg2 <= 1'b0;
   end
  else begin
        sdram_rst_n_reg1 <= 1'b1;
        sdram_rst_n_reg2 <= sdram_rst_n_reg1;
    end
end
 assign sdram_rst_n = sdram_rst_n_reg2;

 //vga復位訊號
always@(posedge vga_clk or negedge rst_n )begin
    if(rst_n == 1'b0)begin
        vga_rst_n_reg1 <= 1'b0;
        vga_rst_n_reg2 <= 1'b0;
   end
  else begin
        vga_rst_n_reg1 <= 1'b1;
        vga_rst_n_reg2 <= vga_rst_n_reg1;
    end
end
 assign vga_rst_n = vga_rst_n_reg2;

 //hdmi復位訊號
always@(posedge hdmi_clk or negedge rst_n )begin
    if(rst_n == 1'b0)begin
        hdmi_rst_n_reg1 <= 1'b0;
        hdmi_rst_n_reg2 <= 1'b0;
   end
  else begin
        hdmi_rst_n_reg1 <= 1'b1;
        hdmi_rst_n_reg2 <= hdmi_rst_n_reg1;
    end
end
 assign hdmi_rst_n = hdmi_rst_n_reg2;

endmodule

最後是我們的頂層模組例化程式碼:

//區域性復位劃分模組
sys_reset sys_reset_inst
(
    .sys_clk             (sys_clk            ),
    .pll_clk1            (sys_clk            ),
    .pll_clk2            (clk_100m           ),
    .vga_clk             (clk_74m            ),
    .sdram_clk           (clk_125m           ),
    .hdmi_clk            (clk_74m            ),
    //輸入復位       
    .sys_rst_n           (sys_rst_n          ),
    .rst_n               (rst_n              ),
    //輸出非同步復位同步釋放後復位訊號    
    .pll_rst_n1          (pll_rst_n1         ),
    .pll_rst_n2          (pll_rst_n2         ),
    .camera_rst_n        (camera_rst_n       ),
    .vga_rst_n           (vga_rst_n          ),
    .sdram_rst_n         (sdram_rst_n        ),
    .hdmi_rst_n          (hdmi_rst_n         )
);

隨後我們就可以將輸出的復位訊號輸入各個模組使用,且理論上由於均為wire型,因此與在每個模組內進行同步毫無區別。同時也避免了需要在頂層模組進行時鐘復位訊號的同步,保證了頂層模組的簡潔。

---

原創教程,轉載請註明出處吃豆熊-非同步復位同步釋放

參考資料:深入淺出玩轉fpga

相關文章