从电路角度理解Verilog:为什么always里要用非阻塞赋值?for循环真的‘贵’吗?

张开发
2026/4/10 2:40:25 15 分钟阅读
从电路角度理解Verilog:为什么always里要用非阻塞赋值?for循环真的‘贵’吗?
从晶体管到触发器Verilog赋值语义的硬件本质解析当我们在Verilog中写下always (posedge clk)时实际上是在描述一个由时钟信号控制的物理电路。这种从代码到硅片的映射关系正是硬件描述语言与普通编程语言最本质的区别。本文将带您穿越抽象层次看看非阻塞赋值、for循环这些语法特性背后真实的硬件实现。1. 阻塞与非阻塞两种赋值方式的电路实现差异在Verilog仿真器中阻塞赋值()和非阻塞赋值()的行为差异显而易见前者立即生效后者在时间步结束时更新。但很少有人思考为什么综合后的电路会有不同1.1 阻塞赋值的组合逻辑特性考虑这个典型的组合逻辑例子always (*) begin a b c; d a | e; end综合器会将其转换为两级门电路一个AND门连接b和c一个OR门连接AND门输出和e这种级联式结构正是阻塞赋值立即生效特性的硬件表现。如果改用非阻塞赋值always (*) begin a b c; d a | e; end综合工具会产生两个独立的组合逻辑块可能导致仿真与硬件行为不一致。1.2 非阻塞赋值的时序元件映射时序逻辑中的非阻塞赋值对应着实际的存储元件。以下代码always (posedge clk) begin reg_a in_a; reg_b reg_a; end会被综合为两个级联的D触发器。关键点在于所有右侧的表达式都使用时钟沿前的值更新操作在时钟边沿后同时发生这解释了为何在同一个always块中a b; b a;能正确实现交换操作——它对应着两个并行工作的触发器而非软件中的临时变量交换。硬件视角非阻塞赋值建模的是时钟沿触发时所有寄存器同时采样输入、同时更新输出的物理特性2. 竞争条件的硅片真相原始文章中提到的复位竞争案例在实际芯片中表现为时钟偏移(clock skew)问题。当两个触发器的时钟信号存在微小延迟时时钟到达顺序reg1状态reg2状态最终稳态reg1先于reg2保持旧值采样reg1旧值保持原态reg2先于reg1采样reg2新值保持旧值状态交换这种不确定性源于物理布线导致的时钟信号传播延迟工艺变异引起的触发器建立/保持时间差异使用非阻塞赋值至少能保证仿真行为更接近实际硬件综合工具能正确推断时序约束避免RTL仿真与门级仿真的不一致3. for循环的硬件代价从代码到面积初学者常误以为for循环是高效的写法实则不然。Verilog中的循环本质上是结构复制器。3.1 循环展开的硬件代价这段看似简洁的代码for(i0; i4; ii1) begin out[i] in[i] enable[i]; end会被综合为4个独立的与门和4个触发器等效于out[0] in[0] enable[0]; out[1] in[1] enable[1]; out[2] in[2] enable[2]; out[3] in[3] enable[3];参数化设计时尤其需要注意循环次数在综合时必须是确定的每次迭代都产生独立的硬件实例面积随循环次数线性增长3.2 时序控制型替代方案对于需要迭代处理的设计更硬件友好的方式是显式实现状态机reg [1:0] state; always (posedge clk) begin case(state) 2d0: begin out[0] in[0] enable; state 2d1; end 2d1: begin out[1] in[1] enable; state 2d2; end // ...其他状态 endcase end这种设计复用相同的逻辑单元通过状态控制分时处理面积基本固定与处理次数无关4. assign与always的电路实现差异虽然assign和always块都可以描述组合逻辑但它们的硬件映射有微妙差别。4.1 连续赋值的门级表现简单的assign语句assign out (a b) | c;通常综合为单级组合逻辑而功能相同的always块always (*) begin out (a b) | c; end 可能因综合工具优化策略不同产生略有差异的门级网表。 ### 4.2 敏感列表的硬件含义 不完整的敏感列表 verilog always (a or b) begin out a b c; end会导致综合出锁存器因为工具必须保持c变化时out的旧值。这正是为什么推荐使用always (*)的深层原因——它确保综合出纯组合逻辑。5. 现代综合工具的优化边界了解综合器的工作方式有助于编写更高效的代码。以这个移位寄存器为例always (posedge clk) begin for(i1; i4; ii1) reg[i] reg[i-1]; reg[0] new_data; end优质的综合工具可能识别出这是移位操作将其优化为专用移位寄存器硬件单元如有触发器链带使能控制时钟门控优化但糟糕的编码风格会阻碍这些优化在循环中加入复杂条件判断混合使用阻塞和非阻塞赋值引入不必要的中间变量在Xilinx Vivado和Intel Quartus的实测中良好编写的代码可获得10-15%的逻辑资源节省更优的时序收敛特性更可预测的综合结果编写硬件描述代码时时刻记住你不是在写程序而是在画电路图。每个语法结构都对应着具体的硬件成本理解这种映射关系才能写出既高效又可靠的RTL代码。

更多文章