用2选1MUX搭建4选1MUX输出延迟的原因及敏感列表修改疑问
always @(sel)会出现延迟,改成@(*)就正常? 让我来拆解一下你遇到的这个问题,核心原因其实是手动编写的敏感列表不完整,导致仿真时组合逻辑的更新没有被及时触发。
先看原代码的问题所在
你的4选1 MUX模块里的上层always块是这么写的:
always @(sel) begin if(sel[1] == 0) out <= w0; else if (sel[1] == 1) out <= w1; end
这个always块的作用是根据sel[1]选择下层两个2选1 MUX的输出w0或w1,赋值给out。但你只把sel加入了敏感列表——这意味着只有当sel整体发生变化时,这个always块才会执行。
但这里有个关键的点:w0和w1的更新是由sel[0]触发的(下层MUX的敏感列表是sel[0])。当sel[0]变化时,w0/w1会更新,但这个变化不会触发上层的always块(除非sel整体也变化了)。更糟的是,就算sel整体变化了,因为非阻塞赋值<=的特性,w0/w1的更新会在当前仿真周期的delta延迟后才完成,此时上层always块已经执行过了,读取的是w0/w1的旧值,自然就出现了1个时间单位的延迟。
@(*)为什么能解决问题?
@(*)是Verilog提供的自动敏感列表语法,它会自动扫描always块中所有在赋值右侧出现的变量,把它们全部加入敏感列表。对于你的这个always块来说,@(*)会自动包含sel[1]、w0、w1这三个变量。
这样一来,触发always块执行的条件就完整了:
- 当
sel(包括sel[0]或sel[1])变化时,会触发执行; - 当
w0或w1更新时(下层MUX输出变化),也会立刻触发always块重新执行,此时就能读取到最新的w0/w1值,直接更新out,自然就不会有延迟了。
补充一个验证小技巧
如果你不想用@(*),也可以手动把所有依赖的变量都写进敏感列表,比如改成:
always @(sel[1], w0, w1) begin if(sel[1] == 0) out <= w0; else if (sel[1] == 1) out <= w1; end
这样修改后,效果和@(*)完全一致,也能解决延迟问题——这也进一步印证了是敏感列表缺失变量导致的问题。
最后提个最佳实践
写组合逻辑的always块时,强烈推荐用@(*)(或者SystemVerilog里的always_comb),手动编写敏感列表很容易遗漏变量,不仅会导致仿真行为异常,甚至可能让综合工具生成不符合预期的电路。
内容的提问来源于stack exchange,提问作者youngjae san

