8.1 状态机的基本概念
有限状态机(FSM)是为时序逻辑电路设计创建的特殊模型技术。
下图是一个状态机示意图,其下半部分是时序逻辑电路,上半部分是组合逻辑电路。
- 组合逻辑电路包含两部分输入:一部分是pr_state(present state,当前状态),另一部分是实际的外部输入信号。组合逻辑电路送出两部分信号:nx_state(next state,下一个状态)和实际的电路输出信号。
- 时序逻辑电路包含3个输入信号(clock,reset和nx_state)以及一个输出信号(pr_state)。且因为所有的寄存器都放在这一部分,所以clock和reset都与这部分相连。
状态机的分类:
- 米里(Mealy)型状态机:状态机的输出信号不仅与电路的当前状态有关,还与当前输入有关。
- 摩尔(Moore)型状态机:状态机的当前输出仅仅由当前状态决定。
从VHDL代码编写的角度出发:
- 对于时序逻辑电路部分,应该在PROCESS内部进行描述(PROCESS内部的代码是按照顺序执行的);
- 对于组合逻辑电路部分,则不需要。因为顺序代码既可以综合生成时序逻辑,也可以生成组合逻辑,故组合逻辑电路部分也可以在PROCESS中实现。
PS:
- clock和reset通常应该出现在用来实现时序逻辑电路的PROCESS的敏感信号列表中(除非不使用reset或使用同步reset,或用WAIT来代替IF)。当reset有效时,pr_state将强制回到系统的初始状态;当reset无效时,每当出现时钟的上升沿,寄存器将储存nx_state,并通过pr_state反馈给组合逻辑电路。
- 虽然理论上任何时序电路都可以建立状态机模型,但这并不总是一种高效的电路实现方式,如果不加区别地追求建立电路的状态机模型,可能会使代码更加冗长和容易出错。譬如,对于简单的计数器,就不需要使用状态机来实现。如果电路要完成的任务或要实现的功能可以进行完整清晰的排列和分类,那么应该首选使用状态机来实现电路。设计这类电路时,通常会在ARCHITECTURE的开始部分插入一个用户定义的枚举数据类型,其中应包含所有可能出现的系统状态。
8.2 设计风格#1
- 设计风格#1的状态机模板
LIBRARY ieee;
USE ieee.std_logic_1164.all;
----------------------------------------
ENTITY <entity_name> IS
PORT (reset: IN STD_LOGIC;
clock: IN STD_LOGIC;
input: IN <data_type>;
output: OUT <data_type>);
END <entity_name>;
-----------------------------------
ARCHITECTURE <arch_name> OF <entity_name> IS
TYPE state IS (state0,state1,state2,state3...); --模板需包括一个用户自定义的枚举类型,所有可能的状态都列入其中。
SIGNAL pr_state,nx_state: state;
BEGIN
---------------Lower section------------ --时序逻辑部分
PROCESS (reset,clock)
BEGIN
IF (reset = '1') THEN
pr_state <= state0;
ELSIF (clock'EVENT AND clock = '1') THEN
pr_state <= nx_state;
END IF;
END PROCESS;
时序逻辑部分设计风格的优点是:
①时序电路的设计方法基本上是标准的;
②占用的寄存器数量较少。上面的代码综合得到的寄存器的数量等于对FSM所有状态进行编码所需的位数,如果使用默认的(二进制)编码方式,则需要的寄存器数量是[log2\N],其中N是总的状态数,[ ]是取大于等于以2为底N的对数的最小整数。
----------Upper section----------- --组合逻辑部分
PROCESS (input,pr_state)
BEGIN
CASE pr_state IS
WHEN state0 =>
output <= <value>;
IF (input = ...) THEN
nx_state <= state1;
ELSIF...
END IF;
WHEN state1 =>
output <= <value>;
IF (input = ...) THEN
nx_state <= state2;
ELSIF...
END IF;
WHEN state2 =>
output <= <value>;
IF (input = ...) THEN --若是无条件切换状态,则去掉"if"的判断
nx_state <= state3;
ELSIF...
END IF;
...
END CASE;
END PROCESS;
END <arch_name>;
组合逻辑部分设计风格的优点是:
①对输出端口赋值和确定状态机的下一个状态;
②遵循了采用顺序代码设计组合逻辑电路的基本要求,即所有输入信号都必须出现在PROCESS的敏感信号列表中,并且所有的输入输出信号组合都必须完整列出;
③这段代码中由于没有任何信号的赋值是通过其他某个信号的跳变来触发的,所以不会生成寄存器。
- 设计风格#2的状态机模板
LIBRARY ieee;
USE ieee.std_logic_1164.all;
-------------------------------------
ENTITY <ent_name> IS
PORT (reset: IN STD_LOGIC;
clock: IN STD_LOGIC;
input: IN <data_type>;
output: OUT <data_type>);
END <ent_name>;
-----------------------------------
ARCHITECTURE <arch_name> OF <ent_name> IS
TYPE states IS (state0,state1,state2,state3,...);
SIGNAL pr_state,nx_state:states;
SIGNAL temp: <data_type>;
BEGIN
------------Lower section------ --时序逻辑部分
PROCESS (reset,clock)
BEGIN
IF (reset = '1') THEN
pr_state <= state0;
ELSIF (clock'EVENT AND clock = '1') THEN
output <= temp; --辅助信号temp在时钟上升沿赋值给了输出
pr_state <= nx_state;
END IF;
END PROCESS;
----------Upper section----------- --组合逻辑部分
PROCESS (input,pr_state)
BEGIN
CASE pr_state IS
WHEN state0 =>
temp <= <value>;
IF (input = ...) THEN
nx_state <= state1;
ELSIF...
END IF;
WHEN state1 =>
temp <= <value>;
IF (input = ...) THEN
nx_state <= state2;
ELSIF...
END IF;
WHEN state2 =>
temp <= <value>;
IF (input = ...) THEN
nx_state <= state3;
ELSIF...
END IF;
...
END CASE;
END PROCESS;
END <arch_name>;
设计风格#2的特点是,引入了内部信号temp,这个信号将输出结果储存起来,只有当所需的时钟边沿到来时才将它赋给输出端口。另外,整个电路生成了两个触发器,其中一个用来为状态机进行状态编码,另一个用来储存输出。
- 设计风格#1与设计风格#2对比
设计风格#1 | 设计风格#2 | |
触发器 | 1个触发器,进行状态编码,里面包含的寄存器的数量为[log2\N]。 | 2个触发器,状态编码和储存输出,包含的寄存器的数量为[log2\N]+输出寄存器。 |
输出 | 只取决于当前所处的状态。 | 同步输出,即只有当所需的时钟边沿到来时输出端口才会被赋值。 |
例8.5 交通灯控制器(双状态机互切)
例8.6 信号发生器(输出信号在时钟的两个边沿都变化)
例8.6 信号发生器(输出信号在时钟的两个边沿都变化)
例9.5 自动售货机控制(多输入判定不同状态)
例9.8 一个7段显示器的应用例题(用计数器设定不同状态的停留时长)
8.3 状态机编码风格
寄存器数量 | 优点 | 缺点 | |
二进制编码 | n个寄存器可以对2^n个状态进行编码 | 需要的寄存器数量最少 | 需要更多的外部辅助逻辑,且速度较慢 |
独热编码(OneHot) | n个寄存器对应n个状态 | 需要最少的辅助逻辑并具有最快的速度 | 寄存器数量最多 |
双热编码 | n个寄存器可以对C(2/n)=n(N-1)/2个状态进行编码 | 介于前两者之间 | 介于前两者之间 |