有限状态机(Finite-State Machine,FSM),又成为有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。笔者常在电机控制、通信协议解析等应用场景下应用FSM。 本文所讲的是基于硬件描述语言Verilog HDL的有限状态机的编写技巧及规范。众所周知FPGA以其并行性和可重构性为世人所知,而在当今的电子世界,基本所有的器件都是串行的,所以作为控制单元或者是可编程单元的FPGA需要进行并行转串行与外界进行通信、控制等,而有限状态机以其简单实用、结构清晰而恰如其分的充当着这个角色。
状态机的本质是对具有逻辑顺序或时序规律事件的一种描述方法。这个论断的最重要的两个词就是“逻辑顺序”和“时序逻辑”,这两点就是状态机所要描述的核心和强项,换言之,所有具有逻辑顺序和时序规律的事情都适合用状态机描述。
状态机的两种应用思路。第一种思路,从状态变量入手。如果一个电路具有时序逻辑或者逻辑顺序,我们就可以自然而然地规划出状态,从这些状态入手,分析每个状态的输入,状态转移和输出,从而完成电路功能;第二种思路,需要首先明确电路的输出关系,这些输出相当于状态的输出,回溯规划每个状态,和状态转移条件与状态输入。两种思路殊途同归,都要通过使用状态机的目的来达到控制某部分电路的目的,完成某种具有逻辑顺序或时序规律的电路设计。
状态机不仅仅是一种电路描述工具,更是一种思想方法,而且状态机HDL语言表达方式比较规范,且有章可循,所以用状态机思想进行逻辑设计得到了广泛的应用,对各种负责设计都套用状态机的设计理念,从而提高设计的效率和稳定性。由于是一种思想方法,所以对C 语言等的算法逻辑也有很好的借鉴作用。
状态机的基本要素:状态、输入和输出。
根据状态机的输出是否与输入条件相关,可以将状态机分为两大类:摩尔(Moore)型状态机和米勒(Mealy)型状态机。
根据状态机的数量是否为有限个,可将状态机分为有限状态机(Finite State Machine,FSM)和无限状态机(Infinite State Machine,ISM)。逻辑设计中一般所涉及的状态都是有限的,并且需要尽可能避免未知状态,所以我们所说的状态机都是特指有限状态机,用FSM表示。
在逻辑设计中,状态机的基本描述方式有三种,分别是:状态转移图、状态转移列表、HDL 语言描述。
状态转移图是状态机描述的最自然的方式。状态转移图经常设计规划阶段定义逻辑功能时使用,也可以在分析代码中状态机的时候使用,通过图形化的方式非常有助于理解设计意图。Intel Quartus II中就集成了这个功能。
另外值得一提的是目前有一些EDA工具支持状态转移图作为逻辑设计输入,例如StateCAD 等。在该工具中设计者只要画出状态转移图就可以自动的将其翻译成HDL语言代码,而且翻译出来的代码规范、可读性较好、可综合、易维护。StateCAD还能自动检测状态机的完备性和正确性,对状态转移图中的冗余状态、自锁状态、歧义定义和不完备状态机等隐含错误都会报警,并协助设计者更正错误。最后StateCAD灰自动生成设计的测试激励,并调用仿真程序,验证状态机的正确性,这个测试激励甚至可在后仿真中使用。总之,StateCAD 提供了状态机的输入、翻译、检测、优化和测试等一条龙的服务,使状态机的设计变得安全、可靠、快速、便捷。这类自动转状态转移图为HDL源代码的工具对设计、分析一些规模较小的状态机非常有效,但是由于自动翻译的代码过于程式化,效率不是最高,所以对于较大规模的逻辑设计,一般还是推荐使用HDL语言直接描述。
状态转移列表是用列表的方式描述状态机,是数字逻辑电路中常用的设计方法之一,经常被用于对状态化简,对于可编程逻辑设计,由于可用逻辑资源比较丰富,而且状态机编码要考虑设计的稳定性,安全性等因素,所以并不经常使用状态转移列表优化状态。
使用HDL语言描述状态机有一定的灵活性,但绝不是天马行空,而是有章可循。通过一些规范的描述方法,可以使HDL语言描述的状态机更安全、稳定、高效、易于维护。
状态机描述时关键是要描述清楚状态机的三要素,即如何进行状态转移、状态的输出、状态转移是否和输入条件相关等。具体描述时方法各种各样,有的设计者习惯将整个状态机写到1个always 模块里面,在该模块中既描述状态转移又描述状态的输入和输出,这种写法一般被称为一段式FSM 描述方法;还有一种写法是用2 个always模块,其中一个always模块采用同步时序描述状态转移,另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律,这种写法被称为两段式FSM描述方法;还有一种写法是在两段式描述方法基础上发展出来的,这种写法使用3个always模块,一个always模块采用同步时序描述状态转移,第二个采用组合逻辑判断状态转移条件,描述状态转移规律,第三个always模块使用同步时序电路描述每个状态的输出,这种写法我们成为三段式写法。
我们一般使用具有同步时序方式的设计,以提高设计的稳定性,消除毛刺。状态机实现后,一般来说,状态转移部分是同步时序电路而状态的转移条件的判断是组合逻辑。两段式之所以比一段式编码合理,就在于两段式编码将同步时序和组合逻辑分别放到不同的always模块中实现,这样做的好处不仅仅是便于阅读、理解、维护,更重要的是利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。而一段式FSM 描述不利于时序约束、更能更改、调试等,而且不能很好的表示米勒型FSM的输出,容易写出Latches锁存器,导致逻辑功能错误。
在一般两段式描述中,为了便于描述当前状态的输出,习惯将当前状态的输出用组合逻辑实现。但是这种组合逻辑仍然有产生毛刺的可能性,而且不利于约束,不利于综合其和布局布线器实现高性能的设计。因此如果设计运行额外的一个时钟街拍的插入latency,则要求尽量对状态机的输出用寄存器寄存一拍。但是很多实际情况不允许插入一个寄存节拍,此时则可以通过三段式描述方法进行解决。三段式与两段式相比,关键在于根据状态转移规律,在上一个状态根据输入条件判断出当前状态的输出,从而在不插入额外时钟节拍的前提下,实现了寄存器输出。
作为一个示例我们将用一段式、两段式、三段式分别描述一个典型的米勒型状态机。共有4 个状态:IDLE、S1、S2、ERROR;输入信号为时钟CLK,低电平一部复位信号RST_n,输入信号i1、i2,输出信号为o1、o2和err,状态的输出如下表:
State | Condition |
---|---|
IDLE | { o1, o2, err } = 3’b000 |
S1 | { o1, o2, err } = 3’b100 |
S2 | { o1, o2, err } = 3’b010 |
ERROR | { o1, o2, err } = 3’b111 |
//1-paragraph method to describe FSM
//Describe state transition, state output, input condition in 1 always block
module state1
(
input CLK,
input RST_n,
input i1,
input i2,
output reg [ 0: 0]
);