从事逻辑设计的小伙伴对状态机这个词并不陌生,什么两段、三段状态机耳熟能详,摩尔、米利状态机型倒背如流。然而不得不承认的是读别人的RTL代码真的是一件痛苦的事情,那状态机可否更优雅的呈现呢?
状态机那些事儿
做逻辑设计的小伙伴都写过状态机,写法也都是大同小异,照着描述就OK了,看下下面这个状态机设计:
功能很简单,手动实现一个Verilog版的状态机并不复杂,无非这么来搞:
这只是一个简单的状态机,读代码的话相对来说还算容易,而当我们状态机较大时状态机的维护和阅读就有点儿费力了。
那么我的状态及能否这么来描述呢?
是否有一种读伪代码的感觉呢?
做真正的状态描述
很遗憾,上述写法并不是伪代码,而是可生成RTL电路的SpinalHDL代码结构。
在SpinalHDL里,其提供了StateMachine类来管理状态机逻辑:
val myFsm = new StateMachine{
其中提供了下面两个函数:
而针对状态机中的状态,SpinalHDL里封装提供了下面的工具辅助描述:
基于此,我们便可以方便快捷的去描述状态机,以一种更优雅的方式去实现状态机描述,而对于他人阅读来讲也是相当OK的。
等等,还有更好玩儿的。
在SpinalHDL里,定义了四种可以声明状态的类型:
- State(基本类型)
- StateDelay
- stateFsm
- stateParallelFsm
一探究竟~
StateDelay
StateDelay新增了一个whenCompleted方法,包含了State的所有属性,而新增的whenCompleted方法中的逻辑内容会在进入该状态延迟指定cycle后进行执行,如下例所示:
val stateG : State = new StateDelay(cyclesCount=40){
whenCompleted{
goto(stateH)
}
}
stateG定义为StateDelay,因此状态机将会在进入该状态10个时钟周期后执行whenCompleted中的内容,即跳转到stateH。
StateFsm
StateFsm主要用于状态机嵌套,允许我们在一个状态机中等待另一个状态机动作的完成。如下例所示:
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
}
}
}
stateC被声明为StateFsm,因此进入该状态后会执行internalFsm中的内容。等待internalFsm状态机执行结束后,stateC中的whenCompleted中的内容将会被执行,状态跳转至stateD。
StateParallelFsm
相较于StateFsm,StateParallelFsm中允许同时执行多个状态机,等所有状态机都跳转执行完毕后whenCompleted中的内容方会执行:
val stateD = new StateParallelFsm (internalFsmA(), internalFsmB()){
whenCompleted{
goto(stateE)
}
}
这里状态机跳转到stateD后,将会执行internalFsmA、internalFsmB两个状态机,当两个状态机都执行完毕退出时,whenCompleted中的内容将会被执行,跳转至stateE。
写在最后
SpinalHDL里状态机的描述相较于Verilog更具有伪代码般的阅读体验,阅读这种代码对于我们来讲相对更容易理解些,想想维护同事的Verilog代码,是否有很挠头的赶脚~。
当然,在转换成RTL代码时,状态机的样子还是和我们手写的差不多的。
END
作者:玉骐
原文链接:https://mp.weixin.qq.com/s/DkeW\_jxepbQpXX8y2v0JtA
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注Spinal FPGA专栏。