異步複位同步釋放在實際項目中的應用
異步複位同步釋放在實際項目中的應用
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


