HDLBits 1~12 计数器仿真不匹配:为什么 c_load 在暂停时仍为高?
问题出在控制逻辑:代码在 Q>11 时会无条件拉高 c_load,没有考虑 enable=0 时计数器应保持当前值。因此,当 reset=0、enable=0、Q=12 时,代码输出 c_load=1。
另外,你的代码在不加载时将 c_d 设为 0,而给出的通过方案始终输出 1。题目会检查这些控制信号,所以除了 Q,还需要关注它们的差异。
为什么 reset 和 enable 都为 0,c_load 仍然为 1?
你的 case 判断的是:
case({{reset|(Q>11)},enable})
去掉不影响含义的内层花括号后,可以写成:
case ({reset | (Q > 11), enable})
拼接的两位分别是:
- 高位:
reset | (Q > 11)。 - 低位:
enable。
高位不仅包含 reset,还包含对 Q 的判断。假设当前状态是:
reset = 0;
enable = 0;
Q = 4'd12;
那么:
reset | (Q > 11) = 1'b1
此时 case 匹配 2'b10,执行:
v_ctrl = {1'b0, 1'b1, 4'b1};
结果是:
c_enable = 1'b0;
c_load = 1'b1;
c_d = 4'd1;
这与代码中的真值表一致。case 的高位不能直接当成 reset,因为它还包含 (Q > 11)。
为什么 c_enable 为 0 也阻止不了加载?
题目规定,count4 的同步加载优先级高于计数使能。它的行为可以用以下代码示意:
always @(posedge clk) begin
if (load)
Q <= d;
else if (enable)
Q <= Q + 1'b1;
end
这段代码用于说明题目给定的行为,不是提供的模块源码。
当 load=1 时,即使 enable=0,计数器也会在时钟上升沿加载 d。因此,原实现到达 12 后,即使外部 enable=0,仍会在下一个上升沿加载 1,无法停留在 12。
正确的回绕条件是什么?
复位和回绕需要分别处理:
reset=1时,无论enable是多少,都应在下一个时钟上升沿加载 1。reset=0时,只有enable=1且Q=12才应回绕到 1。reset=0、enable=0时,计数器应保持当前值,包括保持在 12。
对应的加载条件是:
assign c_load = reset | (enable & (Q == 4'd12));
对正常的 1~12 状态,两种实现的比较如下:
| reset | enable | 当前 Q | 原实现 c_load | 修正后 c_load |
|---|---|---|---|---|
| 1 | 任意 | 任意 | 1 | 1 |
| 0 | 0 | 1~11 | 0 | 0 |
| 0 | 0 | 12 | 1 | 0 |
| 0 | 1 | 1~11 | 0 | 0 |
| 0 | 1 | 12 | 1 | 1 |
原代码先把复位和终点判断合并为 reset | (Q > 11),后续真值表因此无法区分需要复位和已到终点但应暂停的情况。
c_d 为什么也需要调整?
你的代码在不加载时将 c_d 设为 0:
2'b00:v_ctrl = {1'b0,1'b0,4'b0};
2'b01:v_ctrl = {1'b1,1'b0,4'b0};
从计数器状态更新的角度看,load=0 时不会使用 d,所以这个差异不会直接改变该时钟沿的 Q。
但 c_d 也是题目要求检查的输出。给出的通过方案将它固定为:
assign c_d = 4'd1;
要与该方案的控制输出一致,c_d 应始终输出 1,不能仅因为它不影响当前计数值就将其视为任意值。
修正代码
module top_module (
input clk,
input reset,
input enable,
output [3:0] Q,
output c_enable,
output c_load,
output [3:0] c_d
);
wire wrap;
assign wrap = (Q == 4'd12);
assign c_d = 4'd1;
assign c_load = reset | (enable & wrap);
assign c_enable = enable & ~c_load;
count4 the_counter (
.clk(clk),
.enable(c_enable),
.load(c_load),
.d(c_d),
.Q(Q)
);
endmodule
其中,c_enable 在加载期间为 0,与给出的通过方案一致。虽然 count4 的加载优先级更高,但控制输出本身也会被检查,仍需按预期生成。
如何核对波形?
先在时钟上升沿通过 reset 将 Q 置为 1,再使能计数到 12,随后在下一个上升沿之前将 enable 置为 0。
正确实现应满足:
- 暂停期间,
Q=12、c_load=0、c_enable=0。 - 恢复
enable=1后,c_load=1,在下一个上升沿将Q加载为 1。 - 无论是否暂停,只要
reset=1,都在下一个上升沿将Q加载为 1。
同步复位意味着 Q 在时钟沿更新。c_load 由组合逻辑生成,可以随 reset、enable 或 Q 的变化而变化,无须等到时钟沿。
原材料没有提供实际波形,因此无法确定时间 440 的具体失配信号,但可以从代码直接确认上述逻辑差异。另外,粘贴代码中的 VerilogVerilog 若确实存在于源文件中,会造成语法错误,需要删除;它不能解释一段已经成功运行的仿真为什么出现上述控制结果。
备注:内容仅供参考。
