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

Verilog FSM全状态赋值仍出现锁存器推断的问题排查

Verilog FSM综合锁存器推断警告的原因及解决方法

问题背景

使用Verilog设计有限状态机(FSM),在Synopsys Design Vision中综合时出现锁存器推断警告。涉及finish、A_times_B_enable等信号,核心代码如下:

// control path
always @(*) begin
  casex (current_state)
    s0: begin
      read_addr_sel = 2'b10;
      size_count_sel = 2'b10;
      ABenable = 1'b0;
      A_times_B_enable = 1'b0;
      Accumulate_sel = 2'b10;
      finish = 1'b0;
      next_state = s0;
      if (go == 1'b1) next_state = s1;
    end
    s1: begin
      read_addr_sel = 2'b00;
      size_count_sel = 2'b10;
      ABenable = 1'b0;
      A_times_B_enable = 1'b0;
      Accumulate_sel = 2'b10;
      finish = 1'b0;
      next_state = s2;
    end
    s2: begin
      read_addr_sel = 2'b01;
      size_count_sel = 2'b10;
      ABenable = 1'b0;
      A_times_B_enable = 1'b0;
      Accumulate_sel = 2'b10;
      finish = 1'b0;
      next_state = s3;
    end
    s3: begin
      read_addr_sel = 2'b01;
      size_count_sel = 2'b00;
      ABenable = 1'b0;
      A_times_B_enable = 1'b0;
      Accumulate_sel = 2'b10;
      finish = 1'b0;
      next_state = s4;
    end
    s4: begin
      read_addr_sel = 2'b01;
      size_count_sel = 2'b01;
      ABenable = 1'b1;
      A_times_B_enable = 1'b0;
      Accumulate_sel = 2'b10;
      finish = 1'b0;
      next_state = s5;
    end
    s5: begin
      read_addr_sel = 2'b01;
      size_count_sel = 2'b01;
      ABenable = 1'b1;
      A_times_B_enable = 1'b1;
      Accumulate_sel = 2'b00;
      finish = 1'b0;
      next_state = s6;
    end
    s6: begin
      read_addr_sel = 2'b01;
      size_count_sel = 2'b01;
      ABenable = 1'b1;
      A_times_B_enable = 1'b1;
      Accumulate_sel = 2'b01;
      finish = 1'b0;
      next_state = s6;
      if (SizeCount == 16'b11) next_state = s7;
    end
    s7: begin
      read_addr_sel = 2'b10;
      size_count_sel = 2'b01;
      ABenable = 1'b1;
      A_times_B_enable = 1'b1;
      Accumulate_sel = 2'b01;
      finish = 1'b0;
      next_state = s7;
      if (SizeCount == 16'b01) next_state = s8;
    end
    s8: begin
      read_addr_sel = 2'b10;
      size_count_sel = 2'b10;
      ABenable = 1'b0;
      A_times_B_enable = 1'b1;
      Accumulate_sel = 2'b01;
      finish = 1'b0;
      next_state = s9;
    end
    s9: begin
      read_addr_sel = 2'b10;
      size_count_sel = 2'b10;
      ABenable = 1'b0;
      A_times_B_enable = 1'b0;
      Accumulate_sel = 2'b01;
      finish = 1'b0;
      next_state = s10;
    end
    s10: begin
      read_addr_sel = 2'b10;
      size_count_sel = 2'b10;
      ABenable = 1'b0;
      A_times_B_enable = 1'b0;
      Accumulate_sel = 2'b10;
      finish = 1'b1;
      next_state = s0;
    end
  endcase
end

原因分析

锁存器推断的核心原因是组合逻辑块中存在未被完全赋值的信号:

  • 代码中的casex语句没有定义default分支,当current_state取到未列出的状态值(比如状态编码的冗余状态、复位后的未知状态,或是casex匹配逻辑覆盖不全的情况)时,所有输出信号都不会被赋值。
  • 综合器会默认认为这些信号需要保持之前的值,因此推断出锁存器来实现“保持”功能。

解决方法

有两种可靠的方式避免锁存器推断,推荐使用第一种(更不易遗漏):

方法1:在组合逻辑块开头为所有信号赋默认值

在always @(*)块的最开始,给所有输出信号设置默认值,后续casex分支中只需要修改需要变化的信号值即可。这种方式能确保所有信号在任何情况下都有明确赋值。

修改后的代码示例:

// control path
always @(*) begin
  // 先给所有信号赋默认值
  read_addr_sel = 2'b10;
  size_count_sel = 2'b10;
  ABenable = 1'b0;
  A_times_B_enable = 1'b0;
  Accumulate_sel = 2'b10;
  finish = 1'b0;
  next_state = s0;

  casex (current_state)
    s0: begin
      // 只修改需要变化的信号,其余沿用默认值
      if (go == 1'b1) next_state = s1;
    end
    s1: begin
      read_addr_sel = 2'b00;
      next_state = s2;
    end
    s2: begin
      read_addr_sel = 2'b01;
      next_state = s3;
    end
    s3: begin
      read_addr_sel = 2'b01;
      size_count_sel = 2'b00;
      next_state = s4;
    end
    s4: begin
      read_addr_sel = 2'b01;
      size_count_sel = 2'b01;
      ABenable = 1'b1;
      next_state = s5;
    end
    s5: begin
      read_addr_sel = 2'b01;
      size_count_sel = 2'b01;
      ABenable = 1'b1;
      A_times_B_enable = 1'b1;
      Accumulate_sel = 2'b00;
      next_state = s6;
    end
    s6: begin
      read_addr_sel = 2'b01;
      size_count_sel = 2'b01;
      ABenable = 1'b1;
      A_times_B_enable = 1'b1;
      Accumulate_sel = 2'b01;
      next_state = s6;
      if (SizeCount == 16'b11) next_state = s7;
    end
    s7: begin
      read_addr_sel = 2'b10;
      size_count_sel = 2'b01;
      ABenable = 1'b1;
      A_times_B_enable = 1'b1;
      Accumulate_sel = 2'b01;
      next_state = s7;
      if (SizeCount == 16'b01) next_state = s8;
    end
    s8: begin
      read_addr_sel = 2'b10;
      A_times_B_enable = 1'b1;
      Accumulate_sel = 2'b01;
      next_state = s9;
    end
    s9: begin
      Accumulate_sel = 2'b01;
      next_state = s10;
    end
    s10: begin
      finish = 1'b1;
      next_state = s0;
    end
  endcase
end

方法2:为casex添加default分支

在casex的最后添加default分支,在该分支中为所有信号赋值(比如复位到初始状态的输出):

casex (current_state)
  // 原有状态分支...
  default: begin
    read_addr_sel = 2'b10;
    size_count_sel = 2'b10;
    ABenable = 1'b0;
    A_times_B_enable = 1'b0;
    Accumulate_sel = 2'b10;
    finish = 1'b0;
    next_state = s0;
  end
endcase

注意事项

  • 组合逻辑always @(*)块中,所有被赋值的信号必须在所有可能的执行路径上都有明确赋值,否则必然会触发锁存器推断。
  • 优先使用“默认值+分支修改”的方式,既简化代码,又能彻底避免遗漏赋值的问题。

内容的提问来源于stack exchange,提问作者valve

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 02:34:54