状态机

简介

在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,并且优先级高于状态。

状态编码

默认情况下,FSM状态向量将使用(针对Verilog或VHDL)生成RTL的语言/工具的本地编码进行编码。可以通过使用 setEncoding(...) 函数覆盖此默认设置,该方法接收 SpinalEnumEncoding 或类型为 (State, BigInt) 的可变参数以进行自定义编码。

使用 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 中应用语句),并支持嵌套状态机。

虽然它很有用,但也可以绕过该功能并直接让状态机启动到用户状态。

为此,请使用 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 sequance : 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))
}