FSM时钟沿切换(下降沿改上升沿)导致计数器计数异常问题
问题背景
设计了如下Verilog模块test1,功能是仅让clk信号通过2个时钟周期,之后out_clk输出低电平:
module test1( input en, input clk, input burst, output out_clk ); reg [1:0] cpt = 0; localparam burst_beg = 0; localparam burst_end = 1; localparam burst_init = 2; reg [0:1] state = burst_init; assign out_clk = (cpt <= 1) && state != burst_init ? clk : 1'b0; always @(negedge clk) begin if (en) begin case (state) burst_init: begin if (burst) begin state <= burst_end; cpt <= 2; end end burst_beg: begin if (!burst) begin state <= burst_end; cpt <= 2; end else if (cpt == 1) begin cpt <= 2; end else if (cpt != 2)begin cpt <= 1; end end burst_end: begin if (burst) begin state <= burst_beg; cpt <= 0; end end default: ; endcase end end endmodule
同时设计了上升沿触发的计数器模块test2,每来一个时钟上升沿计数加1,顶层将test1的out_clk作为test2的时钟输入。预期每次burst时计数器增加2,但将test1的always块触发沿从negedge clk改为posedge clk后,计数器多增加了1次,需解析原因。
原因解析
触发沿切换导致时序对齐冲突
当test1用下降沿触发时,状态机和cpt的更新发生在clk下降沿,此时out_clk的组合逻辑赋值基于更新前的状态,下一个clk上升沿到来时out_clk已经是稳定的有效电平,test2只会捕获到2个有效上升沿。
改为上升沿触发后,状态机和cpt的更新与clk上升沿同步,out_clk作为组合逻辑会立即跟随状态变化。这就导致在同一个clk上升沿,test2先捕获到out_clk的上升沿(此时还是前一状态的有效电平),随后状态更新让out_clk立刻变化,额外产生的电平跳变被test2误判为有效沿,导致多计数。组合逻辑输出的时序毛刺
out_clk是直接将clk通过组合逻辑选通输出,当test1在clk上升沿更新状态和cpt时,out_clk会在同一个时钟周期内发生电平切换。比如当cpt从1更新为2时,out_clk会立刻从clk跳转为0,这个跳变如果发生在clk上升沿之后、下降沿之前,test2的时钟输入会出现额外的高低电平变化,被误捕获为上升沿。状态切换与时钟沿的重叠干扰
若burst信号变化与clk上升沿同步,状态机的切换会和时钟沿完全重叠。比如从burst_end切换到burst_beg时,cpt被设为0,out_clk立即允许clk通过,这个变化刚好发生在clk上升沿时刻,会让test2多捕获一次当前的上升沿,最终总计数比预期多1次。
总结
核心问题是上升沿触发的状态机更新与组合逻辑输出的out_clk在时序上重叠,导致test2的时钟输入出现额外有效沿或毛刺,最终多计数1次。下降沿触发时,状态更新和时钟沿错开,out_clk的变化不会和test2的采样沿冲突,因此符合预期。
内容的提问来源于stack exchange,提问作者Vladouch

