状态机

简介

在SpinalHDL中,您可以像在VHDL/Verilog中一样,通过使用枚举和switch/case语句来定义状态机。但在SpinalHDL中,您还可以使用专门的语句。

下面的状态机在随后的示例中实现:

../../_images/fsm_simple.svg

样式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))
  }
}

StateMachine

StateMachine 是基类,它管理FSM的逻辑。

val myFsm = new StateMachine {
  // Definition of states
}

StateMachine 还提供了一些访问器:

名称

返回类型

描述

isActive(state)

Bool

当状态机处于给定状态时返回 True

isEntering(state)

Bool

当状态机进入给定状态时返回 True

入口点

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

val stateA = new State with EntryPoint

或者使用 setEntry(state)

val stateA = new State
setEntry(stateA)

转换

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

  • exit() 使状态机在下一个周期处于启动(boot)状态(或者,在 StateFsm 中,退出当前的嵌套状态机)。

这两个函数可以在状态定义中使用(见下文),或使用 always { yourStatements } ,这将始终应用 yourStatements,并且优先级高于状态。

状态编码

By default the FSM state vector will be encoded using the native encoding of the language/tools the RTL is generated for (Verilog or VHDL). This default can be overridden by using the setEncoding(...) method which either takes a SpinalEnumEncoding or varargs of type (State, BigInt) for a custom encoding.

使用 SpinalEnumEncoding
val fsm = new StateMachine {
  setEncoding(binaryOneHot)

  ...
}
使用自定义编码
val fsm = new StateMachine {
  val stateA = new State with EntryPoint
  val stateB = new State
  ...
  setEncoding((stateA -> 0x23), (stateB -> 0x22))
}

警告

当使用 graySequential 枚举编码时,不会进行任何检查以验证FSM转换是否只在状态向量中产生单比特的变化。编码是根据状态定义的顺序完成的,设计者必须确保仅在需要时进行有效的转换。

状态

可以使用多种类型的状态:

  • State (基础状态)

  • StateDelay

  • StateFsm

  • StateParallelFsm

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

名称

描述

state.onEntry {
  yourStatements
}

当状态机不在 state 状态,并且在下一个周期将处于 state 状态时,执行 yourStatements

state.onExit {
  yourStatements
}

当状态机在 state 状态时执行 yourStatements ,并且在下一个周期将处于另一个状态

state.whenIsActive {
  yourStatements
}

当状态机在 state 状态时执行 yourStatements

state.whenIsNext {
  yourStatements
}

当状态机在下一个周期处于 state 状态时, yourStatements 被执行(即使它已经处于该状态)

state. 隐含在 new State 块中:

../../_images/fsm_stateb.svg
val stateB : State = new State {
  onEntry(counter := 0)
  whenIsActive {
    counter := counter + 1
    when(counter === 4) {
      goto(stateC)
    }
  }
  onExit(io.result := True)
}

StateDelay(状态延迟)

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

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

也可以写成一行:

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

StateFsm

StateFsm 允许您描述一个包含嵌套状态机的状态。当嵌套状态机完成(退出)时,执行 whenCompleted { ... } 中的语句。

这是一个StateFsm定义的示例:

// internalFsm is a function defined below
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() 使状态机跳转到启动状态(内部隐藏状态)。这将通知 StateFsm 其内部状态机已经完成。

StateParallelFsm

StateParallelFsm 允许您处理多个嵌套状态机。当所有嵌套状态机完成时,执行 whenCompleted { ... } 中的语句。

示例:

val stateD = new StateParallelFsm (internalFsmA(), internalFsmB()) {
  whenCompleted {
    goto(stateE)
  }
}

关于入口状态的注释

上面定义入口状态的方式使得在复位和第一次时钟采样之间,状态机处于启动状态。只有在第一次时钟采样之后,定义的入口状态才会变为活动状态。这保证了能正确进入入口状态(在 onEntry 中应用语句),并支持嵌套状态机。

While it is useful, it is also possible to bypass that feature and directly having a state machine booting into a user state.

为此,请使用 makeInstantEntry() 而不是定义 new State 。该函数返回启动状态,复位后直接激活。

备注

该状态的 onEntry 仅在从另一个状态转换到该状态时调用,而不是在启动期间。

备注

在仿真过程中,启动状态始终命名为 BOOT

示例:

// State sequance: IDLE, STATE_A, STATE_B, ...
val fsm = new StateMachine {
  // IDLE is named BOOT in simulation
  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))
}
//  State sequence : 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))
}

Notes about using state value

In cases that users want to retrieve the state value for purpose, where state value could be accessed by stateReg. However, the stateReg is not initialized during elaboration of state machine, so any access of stateReg directly could cause error. Use the postBuild method as below can solve this problem.

//  After or inside the fsm's definition.
fsm.postBuild{
  io.status := fsm.stateReg.asBits //io.status is the signal user want to assigned to.
}