06. 时序逻辑与阻塞/非阻塞赋值
时序逻辑用寄存器保存过去的信息。同步设计中,绝大多数状态只在指定时钟沿更新。

D 触发器模板
跳转到“D 触发器模板”reg q;
always @(posedge clk) begin q <= d;endposedge clk 表示上升沿触发。每个上升沿到来时,触发器采样 d;随后 q 更新并保持到下一个有效边沿。
同步复位与异步复位
跳转到“同步复位与异步复位”同步高有效复位:
always @(posedge clk) begin if (rst) q <= 1'b0; else q <= d;end只有时钟上升沿才检查 rst。
异步低有效复位:
always @(posedge clk or negedge rst_n) begin if (!rst_n) q <= 1'b0; else q <= d;endrst_n 下降沿可立即让寄存器复位,不必等待时钟。解除异步复位通常需要遵守器件和工程的同步释放策略,避免恢复/撤除时间问题。不要凭个人习惯混用复位极性和风格,应遵循项目约定与目标器件建议。
非阻塞赋值为什么适合寄存器
跳转到“非阻塞赋值为什么适合寄存器”
非阻塞赋值的直观模型:
- 在时钟沿读取所有右值旧状态;
- 安排左值更新;
- 当前过程计算结束后统一提交。
always @(posedge clk) begin stage1 <= data_in; stage2 <= stage1; stage3 <= stage2;end这会生成三级流水线,每个周期数据前进一步。若错误使用阻塞赋值,仿真中同一块后句可能读到刚更新的新值,三个阶段会像被“穿透”,不再表达预期流水行为。
计数器
跳转到“计数器”module counter #( parameter WIDTH = 8) ( input wire clk, input wire rst_n, input wire enable, output reg [WIDTH-1:0] count);
always @(posedge clk or negedge rst_n) begin if (!rst_n) count <= {WIDTH{1'b0}}; else if (enable) count <= count + 1'b1; end
endmoduleenable=0 时没有 else 是否会产生锁存器?不会。这个块是带时钟的时序过程,未赋值明确表示触发器保持当前状态,正是需要的行为。
带终值脉冲的计数器
跳转到“带终值脉冲的计数器”module pulse_counter #( parameter WIDTH = 8, parameter [WIDTH-1:0] LIMIT = 8'd99) ( input wire clk, input wire rst_n, output reg [WIDTH-1:0] count, output reg tick);
always @(posedge clk or negedge rst_n) begin if (!rst_n) begin count <= {WIDTH{1'b0}}; tick <= 1'b0; end else begin tick <= 1'b0; if (count == LIMIT) begin count <= {WIDTH{1'b0}}; tick <= 1'b1; end else begin count <= count + 1'b1; end end end
endmodule每个正常周期先安排 tick <= 0,到终值时再安排 tick <= 1。同一过程块中,同一寄存器最后执行的非阻塞赋值获胜,因此 tick 只保持一个周期。
一个寄存器只由一个时序过程驱动
跳转到“一个寄存器只由一个时序过程驱动”不要这样写:
always @(posedge clk) if (set) flag <= 1'b1;
always @(posedge clk) if (clear) flag <= 1'b0;flag 有两个过程驱动,优先级不明确,也通常无法综合为普通单个触发器。合并到同一块并明确优先级:
always @(posedge clk) begin if (clear) flag <= 1'b0; else if (set) flag <= 1'b1;end时钟不是普通数据
跳转到“时钟不是普通数据”避免用普通逻辑随意生成门控时钟:
assign gated_clk = clk & enable; // 可能产生毛刺和时钟偏斜优先使用时钟使能:
always @(posedge clk) begin if (enable) q <= d;end需要真正门控时钟时,应使用目标器件的专用时钟资源和规范流程。
时序并不只看功能
跳转到“时序并不只看功能”综合后的寄存器之间存在组合路径:
源寄存器 Q → 组合逻辑 → 目标寄存器 D时钟周期必须容纳时钟到 Q 延迟、组合逻辑延迟、布线延迟和建立时间。代码功能正确但组合路径太长,仍可能无法在目标频率运行。优化方法包括减少逻辑级数、增加流水级、使用专用资源和正确约束时钟。
时序逻辑审查清单
跳转到“时序逻辑审查清单”- 是否只在一个时钟边沿更新;
- 是否统一使用非阻塞赋值;
- 复位极性、同步/异步语义是否一致;
- 复位是否给所有必要状态确定值;
- 一个寄存器是否只有一个过程驱动;
- 位宽是否足够,终值是否可达;
- 跨时钟域信号是否经过专门同步设计。
练习
跳转到“练习”- 写一个带同步高有效复位和使能的 8 位寄存器。
- 写一个 4 位循环计数器,从 0 数到 9 后回到 0。
- 用三个寄存器实现三级延迟,预测输入脉冲经过几个时钟到达输出。
- 解释为什么时钟过程缺少
else是保持寄存器,而组合过程缺少else会推导锁存器。