Verdvana

空持千百偈 不如吃茶去

有限状态机

02 Sep 2019 » FPGA, Verilog HDL

1 前言

        任何一个复杂的数字电路功能都可以通过对其输入输出行为的描述来进行准确的描述。通常单纯的输入输出行为描述是不能够完全描述特定的复杂行为,因为会经常出现输入相同而李树输入不同导致的输出不同的情况,如果想要避免上述情况发生,就必须记忆历史记录。因此,通过存储器加上组合电路就能够完整地描述任何复杂的数字电路功能。

        在数字逻辑设计中,这种建模方式被称为有限状态机(Finite State Machine,FSM)。它的描述为:将要输出的结果是当前状态以及当前输入的组合。


2 有限状态机的设计思想

        有限状态机是由一组状态(State)、一个起始状态(Start State)、一组输入和根据输入及现有状态转换为下一个状态的转换函数(Transition)组成的计算模型:

1         上图中的寄存器可以存储关于过去的信息,它反映从系统开始到现在时刻输入的变化;而通过组合电路,可以实现对输入信号做特定的动作响应。


3 有限状态机的设计

3.1 编码

        状态机的棉麻主要目的是为了定义参数,从而增强程序的可读性,状态机的编码有多种:顺序码(二进制)、格雷码(Gray)、独热编码(One-hot)。此外还有Johnson码和Nova三态码。但常见的主要是前三种。

        采用格雷码,可以减少相邻状态瞬变的次数,有时若不能在所有状态中采用格雷码,则应在状态矢量中增加触发器的数量以减少开关的次数。另一种方法是使用独热编码,该编码方式使用一组码元,每一个码元仅有一位有效。虽然该编码使用的触发器较多,却可减少组合逻辑的使用,在带多个输出且每个输出是几个状态的函数的状态机更是如此。

3.2 状态机的复位

        状态机的复位分成同步复位和异步复位。同步复位是指复位要与分频后的时钟信号同步,触发信号仅为分配后的时钟信号;而异步复位是指复位与分频后的时钟信号和复位信号都参与触发。其实就是在触发复位函数的敏感列表中是否把复位信号作为触发信号。

        另外,由于电路的外部干扰等,状态机存在进入未知状态的情况,此时需要对状态机自动复位,而添加看门狗电路就是最佳选择。

3.3 状态机的条件选择

        状态机的跳转是状态机的核心部分,状态机的条件跳转是控制整个状态机在状态之间的切换,从而决定输出的情况。

3.4 状态机的输出

        有限状态机的设计核心就是为了准确描述输入与输出之间的关系。对于这一步,目前有两种描述风格:

  • Moore型:输出只与当前状态有关;
  • Mealy型:输出不仅与当前状态有关,还与输入有关。

        由于Mealy型状态机的输出与输入有关,输出信号中很容易出现毛刺,所以建议读者使用Moore型状态机进行描述。

3.5 有限状态机的设计步骤

  • 对描述的逻辑进行抽象,得到状态转换图;
  • 状态化简。有效降低电路实现的复杂度;
  • 状态分配,就是对每一个状态分配一个寄存器值;
  • 选定触发器的类型并求出状态方程、驱动方程和输出方程;
  • 按照方程得出逻辑图。

4 状态机的三种描述风格

        FSM有多种描述风格,包括一段式描述、二段式描述和三段式描述。每种描述都有优缺点,都有适用的场合,但从芯片设计实践角度看,最佳的描述方法是三段式描述

4.1 一段式描述

        一段式描述的风格是将状态译码、状态寄存和输出放在一个always块中。这种描述方式的优点是代码简单,缺点是代码维护与修改非常困难:

module FSM_style1(
input clk,
input rst_n,
input in1,
input in2,
input in3,
output reg out1,
output reg out2,
output reg out3
);
reg[3:0] state;
parameter state0 = 4'b0001,state1 = 4'b0010,
          state2 = 4'b0100,state3 = 4'b1000;
always @(posedge clk or negedge rst_n)
  if(!rst_n)
    state <= state0;
  else
  case(state)
    state0: begin
      if(in1) begin
        state<=state1;
        out1 <= 1;
      end
      else state<=state0;
    end
    state1: begin
      state<=state2;
      out2 <= 1;
    end
    state2: begin
      if(in2) begin
        state<=state3; out3 <= 1;
      end
      else state<=state0;
    end
    state3: begin
      if(in3) state<=state0;
      else begin
        state<=state0;out3 <= 1;
      end
    end
    default:
    begin
      state <= state0; out1 =0;out2=0;out3=0;
    end
  endcase

endmodule

4.2 二段与三段式描述

        二段式描述的代码由两个always块构成,其中一个always块用于完成状态寄存(时序逻辑),另一个always块则用于把状态译码和状态输出两个组合逻辑放在一起。

        三段式描述的代码则将状态译码、状态寄存和输出分别放在三个allways块中,相对二段式描述的逻辑层次更清晰:

module FSM_style3(
input clk,
input rst_n,
input in1,
input in2,
input in3,
output reg out1,
output reg out2,
output reg out3
);
reg[3:0] state,next_state;
parameter state0 = 4'b0001, state1 = 4'b0010,
          state2 = 4'b0100, state3 = 4'b1000;
//第一段 组合电路用于状态译码
always @(state or in1 or in2 or in3) 
  case(state)
    state0:if(in1) //根据条件,选择目标跳转状态
        next_state<=state1;
      else
        next_state <= state0;
    state1: next_state<=state2;
    state2: if(in2)
        next_state<=state3;
      else
        next_state <= state0;
    state3: if(in3)
        next_state<=state0;
      else
        next_state <= state3;
    default:
      next_state <= state0;
  endcase

//第二段:更新状态寄存器
always @(posedge clk or posedge rst_n)
  if(!rst_n)
    state <= state0;
  else
    state <= next_state;
//第三段:利用状态寄存器输出控制结果
always @(state)
begin
  //首先产生默认值,后续再改写,防止锁存器产生
  {out1,out2,out3}=3'b000;
  case(state)
    state1: {out1,out2,out3}=3'b100;
    state2: {out1,out2,out3}=3'b110;
    state3: {out1,out2,out3}=3'b111;
  endcase
end
endmodule

4.3 FSM编码风格小结

        对于有限状态机的描述风格,业内公认的结论如下:

  • 一段式的状态机从功能上说没有错误,但可读性差,这种风格的状态机不能被综合工具很好的识别,同时也很难进行优化;
  • 二段式状态机的代码将组合逻辑和时序逻辑分开,具有较好的可读性,更有利于综合工具对状态机的优化;
  • 三段式状态机的代码除了具有二段式的优点,还可以对输出进行寄存,可以有效地滤除毛刺,提高工作效率。

        此外,建议对状态机的各个状态使用localparam或parameter,将状态定义为有意义的符号名。


        告辞。