Verilog寄存器行为异常求助:uut_1输出为何与write_flag同步更新?
问题解析:Verilog寄存器时序与仿真事件逻辑
你遇到的问题核心是对Verilog仿真中同一时间点的事件执行顺序理解不到位,再加上阻塞、非阻塞赋值的行为差异导致的。
1. 为什么uut_1的寄存器和write_flag同步更新?
看测试平台initial块里这段代码:
@(posedge clk) d_in = 1; write_flag_a = 1;
@(posedge clk)等时钟上升沿一到,仿真就进入同一个时间步。- 你用的是阻塞赋值(
=),这种赋值会立刻把变量改成新值,在这个时间步里马上生效。 - 而你的设计模块
reg_behve里的always @(posedge clk)块,也是在这个时钟上升沿触发执行的。这时候write_flag_a和d_in已经是新值了,所以寄存器d_out_r会直接在当前这个时钟沿就更新,看起来和write_flag_a同步变高。
2. 为什么uut_2的寄存器是延迟一个周期更新?
再看write_flag_b的生成代码:
always @(posedge clk) begin write_flag_b <= write_flag_a; end
- 这里用的是非阻塞赋值(
<=),这种赋值不会立刻改变量值,而是把更新动作放到一个专门的队列里,要等当前时间步里所有阻塞赋值、触发的always块都跑完,才会更新变量。 - 当
write_flag_a在某个时钟沿被设为1时,write_flag_b要等这个时间步结束后才会变成1。所以下一个时钟沿到来时,write_flag_b才生效,设计里的寄存器这时候才会捕获d_in的值,表现为延迟一个周期更新,这才是你原本预期的寄存器行为。
怎么让uut_1也符合预期时序?
有两种简单方法:
- 方法一:把测试平台里的阻塞赋值改成非阻塞赋值:
这样变量更新会被延迟到当前时间步结束后,设计模块的@(posedge clk) d_in <= 1; write_flag_a <= 1;always块在当前时钟沿看到的还是旧值,下一个时钟沿才会触发寄存器更新。 - 方法二:提前一个时钟周期设置信号,确保时钟沿到来时信号已经稳定:
d_in = 1; write_flag_a = 1; @(posedge clk)
关键要点记一下
- 阻塞赋值(
=):立刻改值,同一时间步里先执行的赋值会影响后续代码,适合组合逻辑。 - 非阻塞赋值(
<=):不立刻改值,延迟到当前时间步所有事件完成后再更新,适合时序电路的寄存器赋值,能避免仿真里的竞争问题。 - 时钟边沿触发的
always块,是在时钟沿所在的时间步执行的,这时候同一时间步的阻塞赋值已经完成,非阻塞赋值还没生效。
内容的提问来源于stack exchange,提问作者LouisGG
相关产品推荐
相关产品推荐

