You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Verilog中always块内对同一变量两次非阻塞赋值引发异常的原因及解决方法

问题分析与解决方案

一、问题的根本原因

你遇到的问题核心在于Verilog非阻塞赋值(<=)的执行机制。

在使用非阻塞赋值的always @(posedge clk)块中,所有赋值操作遵循固定流程:

  1. 时钟沿触发时,先采样所有赋值语句右边表达式的当前值(也就是时钟沿到来前的变量状态);
  2. 整个always块执行完毕后,再统一更新所有左边的变量。

回到你的代码:

always @(posedge clk) begin
    buffer[119:112] <= data_in; // 采样data_in当前值,标记要更新buffer的这8位
    buffer <= buffer << 8;      // 同时采样buffer旧值,计算左移结果,标记要更新整个buffer
end

当块执行完成后,系统要更新buffer时,会发现两个针对同一变量的赋值请求:一个是更新部分位,另一个是更新整个变量。此时整个变量的赋值会覆盖部分位的赋值——最终buffer会被设置为左移后的旧值,之前写入[119:112]的操作完全被覆盖,自然看不到data_in的写入效果。

哪怕调换两行代码的顺序,结果也不会变:非阻塞赋值的采样是同时进行的,左移用的依然是旧buffer的值,整体赋值还是会覆盖部分赋值。

二、实现目标功能的正确写法

我们分两种场景给出解决方案,你可以根据实际需求选择:

写法1:严格实现“写入指定段→左移”的逻辑

要避免非阻塞赋值的覆盖问题,你需要先构造出最终要赋值给buffer的完整值,然后只做一次非阻塞赋值:

always @(posedge clk) begin
    // 用阻塞赋值临时构造最终状态
    reg [199:0] temp_buffer;
    temp_buffer = buffer;               // 先复制旧buffer的值
    temp_buffer[119:112] = data_in;     // 写入指定段
    temp_buffer = temp_buffer << 8;     // 执行左移操作
    // 最后用非阻塞赋值更新正式buffer
    buffer <= temp_buffer;
end

这里用阻塞赋值(=)在块内立即更新临时变量,最后一次性用非阻塞赋值更新buffer,既符合时序逻辑要求,又避免了多赋值覆盖的问题。

写法2:如果需求是“左移buffer,将data_in补到空出的低位”(常见移位寄存器场景)

如果你的真实意图是实现一个移位寄存器——每次时钟沿把buffer左移8位,再把新的data_in放到最低8位,代码可以更简洁:

always @(posedge clk) begin
    buffer <= {buffer[191:0], data_in}; // 把旧buffer的高192位左移,拼接新data_in到低位
end

这种写法直接通过拼接操作构造出新的buffer值,一次非阻塞赋值完成操作,既高效又符合Verilog最佳实践。


内容的提问来源于stack exchange,提问作者S.SaeedJazaeri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 17:28:13