基于JK触发器的BCD计数器复位异常问题求助
BCD计数器复位异常问题:复位至4而非0的排查与修复
问题描述
使用SystemVerilog将JK触发器配置为T型触发器(J=K=1)实现BCD计数器,目标是从0计数到9后复位至0。计划通过JK触发器的clr输入,在计数值达到4'b1010时复位触发器,但实际运行时,计数值到4'b1010后,计数器复位到4'b0100(即4)而非0。排查后怀疑第三个触发器的always_ff块中存在条件同时触发的情况。
代码实现
顶层设计模块(bcdCounter)
module bcdCounter( input logic clk, input logic rst, output logic Q0, output logic Q1, output logic Q2, output logic Q3, output [3:0] out ); assign out = {Q3,Q2,Q1,Q0}; logic clr; assign clr = ~(Q3 & Q1); // $monitor("the value of clr is %0d and Q3 is %d and Q1 is %d", clr,Q3,Q1); jkFlipFlop flipFlop1(.J(1'b1), .K(1'b1), .rst(rst), .clk(clk), .clr(clr), .Q(Q0)); jkFlipFlop flipFlop2(.J(1'b1), .K(1'b1), .rst(rst), .clk(Q0), .clr(clr), .Q(Q1)); jkFlipFlop flipFlop3(.J(1'b1), .K(1'b1), .rst(rst), .clk(Q1), .clr(clr), .Q(Q2)); jkFlipFlop flipFlop4(.J(1'b1), .K(1'b1), .rst(rst), .clk(Q2), .clr(clr), .Q(Q3)); endmodule
JK触发器模块(jkFlipFlop)
module jkFlipFlop( input logic J, input logic K, input logic rst, input logic clk, input logic clr, output logic Q ); always_ff @(negedge clk, negedge clr ,posedge rst) begin if (rst) begin Q <= 1'b0; end else if(clr == 1'b0) begin Q <= 1'b0; end else if(clr != 1'b0 )begin case ({J,K}) 2'b00: Q <= Q; 2'b01: Q <= 1'b0; 2'b10: Q <= 1'b1; 2'b11: Q <= ~Q; endcase end end endmodule
测试平台模块(bcdTB)
module bcdTB(); logic clk,rst,Q0,Q1,Q2,Q3; logic [3:0] out; bcdCounter bcdCount(.clk(clk), .rst(rst), .Q0(Q0), .Q1(Q1), .Q2(Q2), .Q3(Q3),.out(out)); always #40 clk = ~clk; initial begin clk = 1'b0; rst = 1'b0; #2 rst = 1'b1; #2 rst = 1'b0; #1000 $finish; end endmodule
仿真波形

问题原因
- 异步清零与时钟事件的竞争:触发器的
always_ff敏感列表同时包含negedge clk和negedge clr,当计数到9(4'b1001)时,下一个系统时钟沿触发Q0翻转,进而触发Q1翻转,此时Q3 & Q1为1,clr从1变为0(negedge clr)。这个异步清零事件与部分触发器的时钟事件(如Q1的跳变)几乎同时发生,导致触发器的行为出现竞争,部分触发器未正确执行清零操作(如第三个触发器Q2未被清零)。 - 清零逻辑的覆盖范围问题:当前
clr = ~(Q3 & Q1)会在所有Q3和Q1同时为1的状态(如4'b1010、4'b1011等)触发清零,但异步清零的时序不确定性导致部分触发器未响应。
解决方案
方案1:改用同步清零(推荐)
将异步清零改为同步清零,即移除clr从敏感列表,仅在时钟沿判断清零条件,避免异步事件竞争。修改后的JK触发器代码如下:
module jkFlipFlop( input logic J, input logic K, input logic rst, input logic clk, input logic clr, output logic Q ); always_ff @(negedge clk, posedge rst) begin if (rst) begin Q <= 1'b0; end else if(clr == 1'b0) begin Q <= 1'b0; end else begin case ({J,K}) 2'b00: Q <= Q; 2'b01: Q <= 1'b0; 2'b10: Q <= 1'b1; 2'b11: Q <= ~Q; endcase end end endmodule
同时调整顶层的清零逻辑,确保仅在计数值达到4'b1010时触发同步清零:
// 替换原clr赋值 assign clr = ~(out == 4'b1010);
方案2:优化异步清零的时序逻辑
如果坚持使用异步清零,需确保清零信号的变化不会与触发器的时钟沿竞争。可以通过调整清零条件,在计数值达到9时提前生成清零信号,避免4'b1010状态的出现:
// 顶层模块中替换clr赋值 assign clr = ~(out == 4'b1001);
这样在计数到9时就触发异步清零,跳过4'b1010状态,避免竞争问题。
内容的提问来源于stack exchange,提问作者Asurii
相关产品推荐
相关产品推荐

