非同步複位同步釋放在實際項目中的應用

非同步複位同步釋放在實際項目中的應用

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

Tags: