为何Verilog 4位行波进位加法器输入非X却输出X?
4位行波进位加法器半加器always块未触发问题分析
尝试基于两个半加器构建全加器,进而搭建4位行波进位加法器。半加器和全加器单独测试正常,但级联为4位加法器后,测试时出现异常:当输入a=0、b=0、cin=0时,5ns时刻第二个全加器(f1)的第二个半加器(h1)输入均为0,但内部temp寄存器及输出仍为X,对应的always块未执行更新。
各模块代码如下:
半加器代码
module HA (input a, b, output reg cout, output reg sum); reg [1:0] temp; always @ (a or b) begin temp = a + b; #2 cout = temp[1]; #1 sum = temp[0]; end endmodule
全加器代码
module FA(input a, b, cin, output cout, sum); wire c1, c2; wire s; HA h0 (.a(a), .b(b), .cout(c1), .sum(s)); HA h1 (.a(s), .b(cin), .cout(c2), .sum(sum)); or o1 (cout, c1, c2); endmodule
4位行波进位加法器代码
module PAdder(input [3:0] a, input [3:0] b, input cin, output [3:0] sum, output cout); wire [2:0] c; FA f0 (a[0], b[0], cin, c[0], sum[0]); FA f1 (a[1], b[1], c[0], c[1], sum[1]); FA f2 (a[2], b[2], c[1], c[2], sum[2]); FA f3 (a[3], b[3], c[2], cout, sum[3]); endmodule
测试平台代码
module PAddertb; reg [3:0] a; reg [3:0] b; reg cin; wire [3:0] sum; wire cout; integer i; PAdder p0(a, b, cin, sum, cout); initial begin $monitor("a=%d(%0b) b=%d(%0b) cin=%0b cout=%0b sum=%d(%0b)", a, a, b, b, cin, cout, sum, sum); for (i = 0; i < 1; i = i + 1) begin {a, b, cin} = i; #20; end end endmodule
问题原因分析
带延迟的
always块导致时序叠加与事件阻塞:
半加器使用带固定延迟(#2、#1)的always块实现输出赋值,这种写法仅适用于单模块行为仿真,级联时会引发事件触发的时序冲突。以f1的h1为例:- 0ns时测试平台完成赋值,
f1的h0输入触发always块,其输出s在1ns从X变为0,f0的进位c[0]在2ns从X变为0。 h1的always块在1ns(s变化)时第一次触发,此时c[0]仍为X,计算得到temp=0+X=X,随后进入延迟等待,直到4ns才完成本次always块执行。- 2ns时
c[0]变为0,触发h1的always块新事件,但该事件会被当前正在执行的always块阻塞,需等到4ns后才会再次触发。因此在5ns时刻,第二次always块执行尚未完成temp的赋值,导致temp和输出仍为X。
- 0ns时测试平台完成赋值,
reg类型输出的初始值问题:
半加器的cout、sum及内部temp均为reg类型,初始值默认是X。若always块未及时触发更新,这些寄存器会保持X状态,直到触发完成赋值。
解决方案
将半加器改为连续赋值的组合逻辑实现,避免使用带延迟的always块,这样输入变化时输出会立即更新,不会出现事件阻塞和时序延迟问题:
module HA (input a, b, output cout, output sum); // 用连续赋值实现半加器逻辑,无延迟 assign sum = a ^ b; // 异或实现和 assign cout = a & b; // 与实现进位 endmodule
这种写法符合组合逻辑的Verilog规范,级联时不会出现时序冲突,能保证输入稳定后输出及时更新,彻底解决X值问题。
内容的提问来源于stack exchange,提问作者Amir Kooshky
相关产品推荐
相关产品推荐

