參考博客: https://blog.csdn.net/u014070258/article/details/90052426
原題(卓勝微電子2020)
時鍾輸入clk, sel為時鍾控制信號,sel=0輸出clk, sel = 1 輸出clk的四分頻,要求異步復位,保持時鍾信號的完整性。
實現思路
-
毛刺產生的根本原因:是切換控制信號sel相對於時鍾信號可以在任何時間里發生改變,本質是切換信號為異步信號。
-
解決辦法:任何時鍾的高電平狀態下的切換都需要避免。當兩個輸入時鍾之間是倍數關系時,插入下降沿觸發觸發器確保切換只發生在本時鍾為低電平時候,引入輸出反饋確保另外一路的時鍾為低電平時候發生切換。
Verilog代碼
`timescale 1ns / 1ps
module clk_switch(
input clk,
input rstn,
input sel,
output clk_o
);
// 產生4分頻時鍾
reg [1:0] cnt;
wire clk_div4;
always @(posedge clk or negedge rstn) begin
if(!rstn) cnt <= 2'd0;
else cnt <= cnt + 1'b1;
end
assign clk_div4 = cnt[1];
// 時鍾切換電路: sel=0輸出clk, sel=1輸出clk_div4
reg clk_en;
reg clk_div4_en;
always @(negedge clk or negedge rstn) begin
if(!rstn) clk_en <= 1'b0;
else clk_en <= ~sel & ~clk_div4_en;
end
always @(negedge clk_div4 or negedge rstn) begin
if(!rstn) clk_div4_en <= 1'b0;
else clk_div4_en <= sel & ~clk_en;
end
// 時鍾輸出電路
assign clk_o = (clk_en & clk) | (clk_div4_en & clk_div4);
endmodule
測試激勵
`timescale 1ns / 1ps
module clk_switch_tb;
// Inputs
reg clk;
reg rstn;
reg sel;
// Outputs
wire clk_o;
// Instantiate the Unit Under Test (UUT)
clk_switch uut (
.clk(clk),
.rstn(rstn),
.sel(sel),
.clk_o(clk_o)
);
initial begin
// Initialize Inputs
clk = 0;
rstn = 0;
sel = 0;
// Wait 100 ns for global reset to finish
#500;
rstn = 1;
#400;
sel = 1;
#210;
sel = 0;
#315;
sel = 1;
#205;
sel = 0;
// Add stimulus here
end
always #20 clk=~clk;
endmodule
仿真波形

亞穩態問題
因為sel為異步信號,為了降低亞穩態,可以再加一級寄存器來幫助穩定數據,然后將數據傳遞到下一級。首級寄存器正沿觸發,次級寄存器負沿觸發。
考慮亞穩態的代碼
reg clk_reg1,clk_en;
reg clk_div4_reg1,clk_div4_en;
// 消亞穩態
always @(posedge clk or negedge rstn) begin
if(!rstn) clk_reg1 <= 1'b0;
else clk_reg1 <= ~sel & ~clk_div4_en;
end
always @(posedge clk_div4 or negedge rstn) begin
if(!rstn) clk_div4_reg1 <= 1'b0;
else clk_div4_reg1 <= sel & ~clk_en;
end
// 本時鍾低電平切換
always @(negedge clk or negedge rstn) begin
if(!rstn) clk_en <= 1'b0;
else clk_en <= clk_reg1;
end
always @(negedge clk_div4 or negedge rstn) begin
if(!rstn) clk_div4_en <=1'b0;
else clk_div4_en <= clk_div4_reg1;
end
assign clk_o = (clk_en & clk) | (clk_div4_en & clk_div4);