状态机设计

读大学时学习数字电路,老师会告诉我们,时序电路和组合电路的根本区别就在于,时序电路包含记忆器件(锁存器或触发器)。有了记忆器件,电路的输出就不单单决定于当前输入,同时也跟记忆器件存储的先前信息有关。记忆器件存储的信息可以理解为电路的状态,如果这些状态的数量是有限的,我们就可以利用它们来构建有限状态机(FSM)。

根据输出条件的不同,状态机分为Moore型(输出仅跟状态有关)和Mealy型(输出跟状态和输入都有关)。

在Moore型状态机中,输出只跟状态有关,而状态即寄存器存储的信息(可以理解为FF的Q端),所以说输出相对于输入总是有1 clock cycle的延时,于是我们管这种叫同步状态机。由于输出只由状态驱动(FF/Q),所以对下一级寄存器来说,它是一个同步信号。(严格来说,并不能完全避免输出的冒险,因为多bit状态如果不是Grey或One-Hot编码,也会存在竞争;但只要满足timing closure,冒险就不会传递到下一级寄存器)

在Mealy型状态机中,输出跟状态和输入都有关,当输入变化时,输出也会立即变化,所以我们管这种叫异步状态机。如果输入是异步信号(虽然它不应该是),那么输出也将是异步信号,对后级寄存器来说就存在亚稳态风险;正确的做法是状态机的输入都应该同步到本地时钟域,这样Mealy型的输出也是同步信号(跟Moore型一样,不能完全避免冒险),而且不存在1 clock cycle的延时。

下图为全同步设计的Mealy状态机:

不管哪种状态机,如果后级电路对毛刺敏感,最好都在输出端加一级寄存器。前面提到,只要满足timing closure,毛刺就不会传递。

Glitch Free Moore型状态机:

Glitch Free Mealy型状态机:

对比两种类型的状态机,Moore的输出逻辑简单,但是状态译码器比较复杂;Mealy输出逻辑复杂,但是状态数量较少,而且可以做到无延时输出。两者没有优劣之分,而且在实际设计中,往往会混用两种类型,具体问题要具体分析。

在代码层面,状态机有一段式、两段式、三段式的写法,本文推荐三段式写法,因为不管站在综合工具还是代码维护的角度,三段式都更有优势。所谓三段式,就是将状态转移、状态条件和输出逻辑分成三个always块,下面是Verilog Template:

// FSM Template
module  state_machine_template(
    input               clk,
    input               rstn,
    input               input1,
    input               input2,
    input               ...,
    output              output1,
    output              output2,
    output              ...
);

localparam  [STATE_NUM-1: 0]    // Here using binary code
    S0  = 0,    // Use meaningful parameters instead in your design
    S1  = 1,
    S2  = 2,
    ... ;
    
reg     [STATE_NUM-1: 0]    current_state, next_state;  // next_state is actually the output of state condition combinational logics

// State transition
always @(posedge clk or negedge rstn)
begin
    if(!rstn)
        current_state   <= S0;
    else
        current_state   <= next_state;
end

// State condition combinational logics
always @(*)
begin
    case(current_state)
        S0:
            if(<input1 condition is true>)
                next_state  = S1;
            else if(<input2 condition is true>)
                next_state  = S2;
            else
                next_state  = S0;
        S1:
            if(<input3 condition is true>)
                next_state  = S2;
            else if(<input4 condition is true>)
                next_state  = S0;
            else
                next_state  = S1;
        S2:
            ...
            
        default:
            next_state  = current_state/S0/S1/...;  // carefully deal with default case to avoid dead state
    endcase
end

`ifdef MOORE_TYPE
// Moore combinational outputs
always @(*)
begin
    case(current_state)
        S0: 
        begin
            output1     = <value>;
            output2     = <value>;
            ...
        end
        S1:
        begin
            output1     = <value>;
            output2     = <value>;
            ...
        end
        ...
        default:
        begin
            output1     = <value>;
            output2     = <value>;
            ...
        end
    endcase
end

// Moore sequential outputs
always@(posedge clk or negedge rstn)
begin
    if(!rstn)
    begin
        output1_r   <= <default_value>;
        output2_r   <= <default_value>;
        ...
    end
    else
    begin
        output1_r   <= output1;
        output2_r   <= output2;
        ...
    end
end

`else   // MEALY_TYPE
// Mealy combinational outputs
always @(*)
begin
    case(current_state)
        S0: 
        begin
            if(<condition1>)
            begin
                output1     = <value>;
                output2     = <value>;
                ...
            end
            else if(<condition2>)
            begin
                output1     = <value>;
                output2     = <value>;
                ...
            end
            else
            begin
                output1     = <value>;
                output2     = <value>;
                ...
            end
        end
        ...
        default:
        begin
            output1     = <value>;
            output2     = <value>;
            ...
        end
    endcase
end

// Mealy sequential outputs
always@(posedge clk or negedge rstn)
begin
    if(!rstn)
    begin
        output1_r   <= <default_value>;
        output2_r   <= <default_value>;
        ...
    end
    else
    begin
        output1_r   <= output1;
        output2_r   <= output2;
        ...
    end
end
`endif


endmodule

需要注意,状态机一般用于控制信号(control path),而数据信号(data path)最好与之分隔开来,这样代码清晰,而且便于进行低功耗设计。

最后讨论一下如何提升状态机的性能。复杂的状态机可能有几十个状态和输入条件,如果不做优化,其状态译码逻辑将非常复杂,极有可能成为设计中的关键路径。一般我们通过打断组合逻辑并插入pipeline reg来提升电路性能,但状态机的译码电路是不能加入流水的,怎么办?思路是拆分组合逻辑。比如我们可以对状态和输出进行分类,按类别将大状态机切割成若干个小状态机,这时候输入也被分组后去触发相应的小状态机;再比如状态机中经常用到计数器,直接将它作为条件嵌入状态机中也会拖累性能,我们可以用它产生类似enable这样的信号去触发状态转移,就能简化状态译码电路。当然,性能优化往往伴随着面积增大,所以也不能一味地去拆分组合逻辑。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值