非同步復位同步釋放在實際專案中的應用
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