有限状态机(FSM,Finite-State Machine)是数字设计中的基本构建块,数字设计中很多状态逻辑都可以抽象为FSM。这一部分将对FSM的Chisel实现进行详细介绍,除了基本有限状态机(Moore有限状态机)以外,还会学习Mealy有限状态机和并将二者进行对比。这一篇文章就首先介绍Moore有限状态机。
有限状态机,即FSM,可以描述一组状态(states)和状态之间的条件状态转移(state transitions)。FSM有一个初始状态,会在复位的时候设置。而FSM还有个名字,叫作同步时序电路,是不是听起来很熟悉,没错,所有的同步时序电路本质上都可以抽象为有限状态机。
一个FSM的实现需要有三个部分:
原则上来说,任何包含一个用于存储状态的寄存器或其他存储器元素的数字电路都可以用单个有限状态机来描述。然而这么做并不现实,比如你要想用单个FSM来描述我们的电脑那就太离谱了。所以这一部分还是介绍单个FSM,而下一部分则会介绍用过小的FSM的组合构建更大的通信FSM。
基本有限状态机也叫Moore有限状态机,下图是FSM的示意图:
如图所示,寄存器包含了当前的状态state
,下个状态计算逻辑Next state logic
或根据当前state
和输入in
来计算下一个状态nextState
,在下一个周期,nextState
就变成了state
。输出逻辑Output logic
会计算输出out
,由于这里输出仅依赖于当前状态,一次你这种状态机叫作基本有限状态机,也叫做Moore机。
状态转换图(State Diagram)可视化地描述FSM的行为,在状态转换图中,每个状态都描绘为以状态名为标签的圆圈,而状态转换用状态之间的箭头来表示。执行某个转换的条件会在状态转换的箭头以标签的形式给出。下图就是一个FSM的状态转换图的例子:
这个FSM有三个状态:green
、orange
和red
,表示不同等级的警告。FSM从green
状态开始,如果一个bad event
来了,警告等级就会转换为orange
,如果再发生了第二个bad event
,那就进一步转换为red
,此时我们希望可以响铃,ring bell
是FSM唯一的输出,我们把输出加到red
状态上,如果接受到了clear
信号,那警告等级就会重置为green
。
尽管状态转换图可以可视化地描述FSM的功能,看起来很舒服,也很容易掌握,但状态转换表用于写代码的时候写起来更快。下表就上面警告FSM的状态转换表:
表中列举了不同当前状态和输入值对应的下一个状态以及当前状态对应的输出。原则上讲,我们需要把所有状态的所有可能性都列举出来,那这个表的话就会有
3
×
4
=
12
3\times 4=12
3×4=12行。但我们可以简化一些,比如如果bad event
发生的时候,clear
信号就忽视掉了,也就是说bad event
比clear
有更高的优先级。输出列是有一些重复的,如果我们的FSM更大或者有更多的输出,那我们可以画两个表格,一个用于下个状态逻辑,另一个用于输出逻辑。
在设计好警告等级FSM之后,我们就可以用Chisel代码写出来了,完整代码如下:
// package my.hello
import chisel3._
import chisel3.util._
class SimpleFsm extends Module {
val io = IO(new Bundle {
val badEvent = Input(Bool())
val clear = Input(Bool())
val ringBell = Output(Bool())
})
// FSM的三种状态
val green :: orange :: red :: Nil = Enum(3)
// 状态寄存器
val stateReg = RegInit(green)
// 状态转换逻辑
switch (stateReg) {
is (green) {
when(io.badEvent) {
stateReg := orange
}
}
is (orange) {
when(io.badEvent) {
stateReg := red
} .elsewhen(io.clear) {
stateReg := green
}
}
is (red) {
when(io.clear) {
stateReg := green
}
}
}
// 输出逻辑
io.ringBell := stateReg === red
}
object MyModule extends App {
println(getVerilogString(new SimpleFsm()))
}
下面解释一下代码:
Enum
类型和switch
控制指令,因此需要导入import chisel3.util._
;Bool
类型;IO
的Bundle
里面,这个结构前面说过很多次了;Enum
类,还用了符号化的名字表示状态:val green :: orange :: red :: Nil = Enum(3)
(当前版本的ChiselEnum
用的还是二进制编码,如果想用独热编码可以自己定义Chisel常量);::
进行连接,Nil
表示列表的结尾,然后一个ChiselEnum
类型的实例被赋值到这个状态列表;val stateReg = RegInit(green)
;switch
结构,覆盖了所有的状态,在每个is
分支里面,我们都基于输入,通过赋新的状态给状态寄存器来实现下个状态逻辑;red
的时候就ringing bell
,这个没啥好说的。需要注意的是,我们没有引入一个为寄存器引入一个next_state
信号,因为在Verilog和VHDL里面也是这么干的。在Verilog和VHDL里面,寄存器都是用特殊语法描述的,不能通过一个组合逻辑块被赋值和重新赋值。因此,额外的信号通过组合逻辑块计算后直接连接到寄存器的输入上就完事了。不过在Chisel里面寄存器是个基本类型,是可以随便在组合逻辑块里面用的。
这一篇文章介绍了基本有限状态机(FSM),即Moore机,对应的是输出只和当前状态有关的有限状态机,下一篇我们将会学习输出依赖于输入的状态机,即Mealy有限状态机,通过上升沿检测的例子来阐明,进一步地还会比较Moore机和Mealy机的区别。