参考博客: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);