我们知道状态机需要添加default状态,避免状态机跳转到未知状态无法恢复。那么是不是只要增加了default状态机就完全没有风险了呢,可以看一下Designing Safe Verilog State Machines with Synplify这篇文章的介绍。

写状态机时需要检查状态机的跳转是否完整,是否有多余的状态。例如,用2bit表示以下三个状态,那么就会存在1个状态2'b11没有覆盖。

// 用2位表示3个状态:IDLE=2'b00, S1=2'b01, S2=2'b10
// 注意:2'b11 是未定义状态!

parameter    IDLE = 2'b00;
parameter    S1   = 2'b01;
parameter    S2   = 2'b10;

reg [1:0] current_state, next_state;

// 状态寄存器
always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        current_state <= IDLE;
    else
        current_state <= next_state;
end

// 次态逻辑(组合逻辑)
always_comb begin
    next_state = current_state; // 默认保持
    done = 1'b0;

    case (current_state)
        IDLE: begin
            if (go)
                next_state = S1;
        end
        S1: begin
            next_state = S2;
        end
        S2: begin
            done = 1'b1;
            next_state = IDLE;
        end
        // ❌ 缺少 default 或 2'b11 的处理!
    endcase
end

endmodule

这样做的目的是为了保证系统在收到扰动时不会挂死,比如时钟加扰或者高能粒子导致的bit翻转,让系统状态在某个时刻跳转到2‘b11这个状态,无法自动恢复到正常状态。在理论上,只要状态跳转的链路完整且合理,并且状态数与bit数满足幂次关系,是不会出现挂死的情况。一个满状态的状态机跳转示意图如下:

Snipaste_2026-02-01_23-57-09.png

在设计时,如果状态数不足,需要补充上default状态,此时等价于上述满状态的状态机,跳转示意图如下:

从表面上来看,上面的跳转图中每个状态都能走到并且跳转到下一状态,并不存在逻辑链路上的死路,按道理无论系统受到什么扰动都不会挂死的。但是实际上工具综合出来的电路并不一定完全按照我们的设计目的,还会综合时序、面积进行优化,这个时候就可能引入问题。以下面的状态机为例:

工具可能为了达到时序最优,将状态机编码更改为独热码。

module FSM1 (clk, in1, rst, out1);
    input clk, rst, in1;
    output [2:0] out1;

    `define s0 3'b000
    `define s1 3'b001
    `define s2 3'b010
    `define s3 3'b011
    `define s4 3'b100

    reg [2:0] out1;
    reg [2:0] state /* synthesis syn_encoding = "onehot" */;
    reg [2:0] next_state;

    always @(posedge clk or posedge rst)
        if (rst)
            state <= `s0;
        else
            state <= next_state;

    always @(state or in1)
        case (state)
            `s0: begin
                out1 <= 3'b000;
                if (in1)
                    next_state <= `s1;
                else
                    next_state <= `s0;
            end
            `s1: begin
                out1 <= 3'b001;
                if (in1)
                    next_state <= `s2;
                else
                    next_state <= `s1;
            end
            `s2: begin
                out1 <= 3'b010;
                if (in1)
                    next_state <= `s3;
                else
                    next_state <= `s2;
            end
            `s3: begin
                out1 <= 3'b011;
                if (in1)
                    next_state <= `s4;
                else
                    next_state <= `s3;
            end
            `s4: begin
                out1 <= 3'b100;
                if (in1)
                    next_state <= `s0;
                else
                    next_state <= `s4;
            end
            default: begin
                out1 <= 3'b000;
                next_state <= `s3;
            end
        endcase
endmodule

此时工具综合出来的电路如下图所示:

前级寄存器翻转后会让下级寄存器依次翻转,达到流水效果,组合逻辑大大减少,bit翻转也减少,降低峰值功耗。但是这种电路是无法处理其他异常状态的,例如全0状态,系统永远无法恢复。为了避免这种场景,我们需要设置安全状态机。

reg [2:0] state /* synthesis syn_encoding = "safe,onehot" */;

添加上述综合语句之后,综合工具会自动将状态机编码设置为onehot,同时给综合出来的电路添加复位功能,当检测到非法onehot编码之后,会自动复位整个状态机。

/* synthesis syn_preserve = 1 */

上述综合语句可以实现类似的效果,设置综合工具不对该信号做任何优化。

现在回到最初的问题,是不是满状态的状态机或者添加了default状态的状态机不会挂死呢?答案显然是否定的。

那么进一步问,是否设置了安全状态机就说明状态机不会挂死呢?答案仍然是否定的。

给状态机设置复位状态之后虽然可以在任何时刻下跳转到合法状态,但是可能还是会挂死在合法状态。例如正确的跳转过程是A - B - C - D - A,跳转条件依赖外部输入信号,外部输入信号又和内部有耦合。在状态突然从C跳转到非法状态又被复位到A状态时,可能外部无法给出合适的激励,让状态机正常跳转到B。

这些就需要开发人员在设计之初就考虑到了,综合工具无法避免。

最后介绍一下常见的几种综合工具:

Synplify和紫光同创PDS:

state_reg /* synthesis syn_encoding = "value" */ ;  state_reg表示状态机寄存器 

value 可选值如下:

default-自动选择编码方式。 

onehot-采用onehot编码方式。

gray-采用格雷码

sequential-采用自然码

safe-如果不能到达任一个状态时,让其回到复位态

state_reg /* synthesis syn_preserve = 1 */  ; state_reg表示状态机寄存器 

用在某些独立的寄存器上,或module(则相当于施加于module中的所有寄存器)上,使其在优化时保持不动。也可用于保持某个自动机在优化时不动。如果使用这个综合指令,工具会保留所有default状态。举个例子,如果3bit的状态机,只有2个合法状态,其他6个状态都是非法的,使用default代替,那么综合工具会直接保留6中状态,如果使用syn_encoding 综合语句,会对6个非法状态进行合并优化,资源更少。

Vivado:

Vivado Design Suite Properties Reference Guide UG912 (v2025.2) November 20, 2025

(* fsm_encoding = "one_hot" *) reg [7:0] my_state;

FSM_ENCODING controls encoding on the state machine. Typically, the Vivado tools choose an encoding protocol for state machines based on heuristics that do the best for the most designs. Certain design types work better with a specific encoding protocol.

Place FSM_ENCODING on the state machine registers. The legal values for this are one_hot, sequential, johnson, gray, user_encoding, and none. The auto value is the default, and allows the tool to determine best encoding. The user_encoding value tells the tool to infer a state machine, and use the encoding that you provide in the RTL.


(* fsm_safe_state = "reset_state" *) reg [7:0] my_state;

FSM_SAFE_STATE instructs Vivado synthesis to do the following:

Insert logic into the state machine that detects invalid states
Forces the state machine to transition to a valid state on the next clock cycle.
For example, a one-hot state machine forced into an invalid one-hot state such as 0101, can recover from the invalid state. Place the FSM_SAFE_STATE attribute on the state machine registers. You can set this attribute in either the RTL or in the XDC.

The values for FSM_SAFE_STATE are:

auto_safe_state
Uses Hamming-3 encoding for auto-correction for one bit flip.
reset_state
Forces the state machine into the reset state using Hamming-2 encoding detection for one bit flip.
power_on_state
Forces the state machine into the power-on state using Hamming-2 encoding detection for one bit flip.
default_state
Forces the state machine to default state specified in RTL, the same state specified in default branch of the case statement in Verilog. It can also be the state specified in the others branch of the case statement in VHDL. For this to work, a default or others state must be in the RTL.