状态机
简介
在SpinalHDL中,您可以像在VHDL/Verilog中一样,通过使用枚举和switch/case语句来定义状态机。但在SpinalHDL中,您还可以使用专门的语句。
下面的状态机在随后的示例中实现:
样式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
还提供了一些访问器:
名称 |
返回类型 |
描述 |
---|---|---|
|
|
当状态机处于给定状态时返回 |
|
|
当状态机进入给定状态时返回 |
入口点
通过扩展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.
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.onExit {
yourStatements
}
|
当状态机在 |
state.whenIsActive {
yourStatements
}
|
当状态机在 |
state.whenIsNext {
yourStatements
}
|
当状态机在下一个周期处于 |
state.
隐含在 new State
块中:
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))
}