状态机(State machine)

一、简介(Introduction)

在SpinalHDL中用户可以像在VHDL/Verilog中一样定义状态机, 通过使用计数和Swith/Case语句。但是在SpinalHDL中也可以使用专用的语法。

下方的状态机可以被如下代码实现:

fsm_simple

格式A:

import spinal.lib.fsm._

class TopLevel extends Component {
  val io = new Bundle {
    val result = out Bool()
  }

  val fsm = new StateMachine {
    val counter = Reg(UInt(8 bits)) init (0)
    io.result := False

    val stateA : State = new State with EntryPoint {
      whenIsActive(goto(stateB))
    }
    val stateB : State = new State {
      onEntry(counter := 0)
      whenIsActive {
        counter := counter + 1
        when(counter === 4) {
          goto(stateC)
        }
      }
      onExit(io.result := True)
    }
    val stateC : State = new State {
      whenIsActive(goto(stateA))
    }
  }
}

格式B:

import spinal.lib.fsm._

class TopLevel extends Component {
  val io = new Bundle {
    val result = out Bool()
  }

  val fsm = new StateMachine{
    val stateA = new State with EntryPoint
    val stateB = new State
    val stateC = new State

    val counter = Reg(UInt(8 bits)) init (0)
    io.result := False

    stateA
      .whenIsActive(goto(stateB))

    stateB
      .onEntry(counter := 0)
      .whenIsActive {
        counter := counter + 1
        when(counter === 4) {
          goto(stateC)
        }
      }
      .onExit(io.result := True)

    stateC
      .whenIsActive(goto(stateA))
  }
}

Verilog:

module MyTopLevel (
  input               io_cond0,
  input               io_cond1,
  output              io_flag,
  output     [7:0]    io_state,
  output reg          io_result,
  input               clk,
  input               reset
);
  localparam fsm_enumDef_BOOT = 2'd0;
  localparam fsm_enumDef_stateA = 2'd1;
  localparam fsm_enumDef_stateB = 2'd2;
  localparam fsm_enumDef_stateC = 2'd3;

  wire                fsm_wantExit;
  reg                 fsm_wantStart;
  wire                fsm_wantKill;
  reg        [7:0]    fsm_counter;
  reg        [7:0]    counter;
  reg        [1:0]    fsm_stateReg;
  reg        [1:0]    fsm_stateNext;
  wire                _zz_when_StateMachine_l233;
  wire                _zz_when_StateMachine_l233_1;
  wire                when_MyTopLevel_l54;
  wire                when_StateMachine_l233;
  wire                when_StateMachine_l249;
  `ifndef SYNTHESIS
  reg [47:0] fsm_stateReg_string;
  reg [47:0] fsm_stateNext_string;
  `endif


  `ifndef SYNTHESIS
  always @(*) begin
    case(fsm_stateReg)
      fsm_enumDef_BOOT : fsm_stateReg_string = "BOOT  ";
      fsm_enumDef_stateA : fsm_stateReg_string = "stateA";
      fsm_enumDef_stateB : fsm_stateReg_string = "stateB";
      fsm_enumDef_stateC : fsm_stateReg_string = "stateC";
      default : fsm_stateReg_string = "??????";
    endcase
  end
  always @(*) begin
    case(fsm_stateNext)
      fsm_enumDef_BOOT : fsm_stateNext_string = "BOOT  ";
      fsm_enumDef_stateA : fsm_stateNext_string = "stateA";
      fsm_enumDef_stateB : fsm_stateNext_string = "stateB";
      fsm_enumDef_stateC : fsm_stateNext_string = "stateC";
      default : fsm_stateNext_string = "??????";
    endcase
  end
  `endif

  assign fsm_wantExit = 1'b0;
  always @(*) begin
    fsm_wantStart = 1'b0;
    case(fsm_stateReg)
      fsm_enumDef_stateA : begin
      end
      fsm_enumDef_stateB : begin
      end
      fsm_enumDef_stateC : begin
      end
      default : begin
        fsm_wantStart = 1'b1;
      end
    endcase
  end

  assign fsm_wantKill = 1'b0;
  always @(*) begin
    io_result = 1'b0;
    if(when_StateMachine_l233) begin
      io_result = 1'b1;
    end
  end

  assign _zz_when_StateMachine_l233 = (fsm_stateReg == fsm_enumDef_stateB);
  assign _zz_when_StateMachine_l233_1 = (fsm_stateNext == fsm_enumDef_stateB);
  always @(*) begin
    fsm_stateNext = fsm_stateReg;
    case(fsm_stateReg)
      fsm_enumDef_stateA : begin
        fsm_stateNext = fsm_enumDef_stateB;
      end
      fsm_enumDef_stateB : begin
        if(when_MyTopLevel_l54) begin
          fsm_stateNext = fsm_enumDef_stateC;
        end
      end
      fsm_enumDef_stateC : begin
        fsm_stateNext = fsm_enumDef_stateA;
      end
      default : begin
      end
    endcase
    if(fsm_wantStart) begin
      fsm_stateNext = fsm_enumDef_stateA;
    end
    if(fsm_wantKill) begin
      fsm_stateNext = fsm_enumDef_BOOT;
    end
  end

  assign when_MyTopLevel_l54 = (fsm_counter == 8'h04);
  assign when_StateMachine_l233 = (_zz_when_StateMachine_l233 && (! _zz_when_StateMachine_l233_1));
  assign when_StateMachine_l249 = ((! _zz_when_StateMachine_l233) && _zz_when_StateMachine_l233_1);
  always @(posedge clk or posedge reset) begin
    if(reset) begin
      fsm_counter <= 8'h0;
      fsm_stateReg <= fsm_enumDef_BOOT;
    end else begin
      fsm_stateReg <= fsm_stateNext;
      case(fsm_stateReg)
        fsm_enumDef_stateA : begin
        end
        fsm_enumDef_stateB : begin
          fsm_counter <= (fsm_counter + 8'h01);
        end
        fsm_enumDef_stateC : begin
        end
        default : begin
        end
      endcase
      if(when_StateMachine_l249) begin
        fsm_counter <= 8'h0;            //这会产生有优先级的选择器,该情况优先级更高
      end
    end
  end


endmodule

二、状态机(StateMachine)

StateMachine是基本类(class), 它管理FSM的逻辑。

val myFsm = new StateMachine {
  // 定义状态
}

StateMachine还提供了一些访问器(accessors):

命名 返回类型 描述
isActive(state) Bool 当状态机处于所给定的状态时返回true
isEntering(state) Bool 当状态机要进入所给定的状态时返回true
  1. 入口点(Entry point)

    通过扩展EntryPoint特征, 可以将状态定义为状态机的入口点:

    val stateA = new State with EntryPoint
    

    或者通过使用val stateA = new State with EntryPoint

    val stateA = new State
    setEntry(stateA)
    
  2. 转换(Transitions)

    • 转换由goto(nextState)表示, 它使得状态机在下一个周期转换到nextState

    • exit()将状态机在下一个周期转换为boot状态(boot state)(或者, 在StateFsm中, 退出当前嵌套状态机)。

三、状态(States)

多种状态可以被使用:

  • State (最基本的)

  • StateDelay

  • StateFsm

  • StateParallelFsm

它们每个都提供以下函数来定义与它们相关的逻辑:

命名 描述
state.onEntry {
yourStatements
}
yourStatements在状态机未处于state时应用, 并将在下一个周期处于state
state.onExit {
yourStatements
}
yourStatements在状态机处于state时应用, 并将在下一个周期处于其他状态
state.whenIsActive {
yourStatements
}
yourStatements在状态机处于state时应用
state.whenIsNext {
yourStatements
}
yourStatements在状态机将在下一个周期处于state时执行(即使已经是state状态)

``state.隐含在一个new State`块中:

fsm_stateb

val stateB : State = new State {
  onEntry(counter := 0)
  whenIsActive {
    counter := counter + 1
    when(counter === 4) {
      goto(stateC)
    }
  }
  onExit(io.result := True)
}
  1. 状态延迟(StateDelay)

    StateDelay允许创建一个状态, 该状态在whenCompleted{…}语句执行之前等待固定数量的周期。首选的使用方法是:

    val stateG : State = new StateDelay(cyclesCount=40) {
        whenCompleted {
            goto(stateH)
        }
    }   
    

    同样可以用一行代码来写:

    val stateG : State = new StateDelay(40) { whenCompleted(goto(stateH)) }
    
  2. 状态中的有限状态机(StateFsm)

    StateFsm允许描述包含嵌套状态机的状态。当嵌套状态机完成(退出)时, whenCompleted{…}执行。

    例如:

    // 内部的FSM按如下定义
    val stateC = new StateFsm(fsm=internalFsm()) {
        whenCompleted {
            goto(stateD)
        }
    }
    
    def internalFsm() = new StateMachine {
        val counter = Reg(UInt(8 bits)) init (0)
    
        val stateA : State = new State with EntryPoint {
            whenIsActive {
                goto(stateB)
            }
        }
    
        val stateB : State = new State {
            onEntry (counter := 0)
            whenIsActive {
                when(counter === 4) {
                    exit()
                }
                counter := counter + 1
            }
        }
    }
    

    在上面的示例中, exit()使状态机跳转到boot状态(内部隐藏状态)。这将通知StateFsm其内部状态机已完成。

  3. 状态中的并行有限状态机(StateParallelFsm)

    StateParallelFsm允许处理多个嵌套状态机。当所有嵌套状态机完成时, whenCompleted{…}执行。

    例如:

    val stateD = new StateParallelFsm (internalFsmA(), internalFsmB()) {
        whenCompleted{
            goto(stateE)
        }
    }
    
  4. 关于入口状态的注释(Notes about the entry state)

    上面定义的进入状态的方式使得在重置复位和第一个时钟采样之间, 状态机处于启动状态。只有在第一次时钟采样之后, 定义的进入状态才有效。这允许正确地进入entry状态(应onEntry中的语句), 并允许嵌套状态机。

    虽然它很有用, 但也可以绕过该特性, 直接让状态机引导(boot)到用户状态。

    为此, 使用makeInstantEntry()而不是定义一个new State。这个函数返回boot状态, 在复位后直接激活。

    注意:该状态的onEntry只会在它从另一个状态转换到这个状态时调用, 而不会在启动期间调用。

    注意:在仿真过程中, boot状态总是命名为Boot.

    例如:

    // 状态序列: IDLE, STATE_A, STATE_B, ...
    val fsm = new StateMachine {
    // IDLE在仿真中被命名为BOOT
        val IDLE = makeInstantEntry()
        val STATE_A, STATE_B, STATE_C = new State
    
        IDLE.whenIsActive(goto(STATE_A))
        STATE_A.whenIsActive(goto(STATE_B))
        STATE_B.whenIsActive(goto(STATE_C))
        STATE_C.whenIsActive(goto(STATE_B))
    }
    //  状态序列 : BOOT, IDLE, STATE_A, STATE_B, ...
    val fsm = new StateMachine {
        val IDLE, STATE_A, STATE_B, STATE_C = new State
        setEntry(IDLE)
    
        IDLE.whenIsActive(goto(STATE_A))
        STATE_A.whenIsActive(goto(STATE_B))
        STATE_B.whenIsActive(goto(STATE_C))
        STATE_C.whenIsActive(goto(STATE_B))
    }