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

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

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: