上节内容讲解了状态机的基本理论、分类以及在数字电路,软件方面的建模及应用,最后以简单的例子对比常规VHDL程序设计状态机程序之间的设计关系。可以看出在时序电路的设计钟,特别是VHDL语言的描述中,尽管没有显式的使用状态机的描述方式,状态机在时序电路的设计中几乎无处不在的。多数时序电路都可以以状态机的方式进行描述。本节内容就状态机的几个要点做比较详细的介绍。
1.状态机状常用的几个名词介绍
-
时钟(clk)
由于状态机在数字逻辑设计中,是时序电路的一个部分,只有在时钟的驱动下才能按节拍运行,因此时钟是状态机关键因素。后面将以clk的代表时钟。
-
寄存器(register)
在VHDL程序设计中,时序电路是由register 和clock共同实现的,因此register 必不可少。
-
状态(state)
以S代表状态, cs(current state)表示现态(当前状态),ns(next state)表示次态(下一个状态)。
-
输入(input)
一般情况下状态的转换由现态和输入共同决定,输入不是必须的,如上节内容的例1,就没有输入,只是由clk和状态决定状态的条状和输出。
-
方程
表述状态转换或输出与现态与输入之间的关系。如图1,描述摩尔机的三个方程,分别为现态方程,次态方程与输出方程,后面结合摩尔机与梅利机进行详细描述。在时序电路中方程的表象形式为赋值语句。
2. 状态机的分析与实现
-
摩尔机
上节内容中虽然在状态机分类中提到摩尔机以及梅利机两种常用类型,但并没有做详细介绍。下面就这两种状态机作比较详细的介绍。
- 摩尔机:输出只与状态有关,与输入没有关系,这种状态机称为摩尔机。其方程描述如图1。

图1 摩尔机方程
图2 摩尔机时序逻辑图
在图1中, 方程ns=M{inputs, cs }表示,根据现态cs和输入input决定次态ns, 这个方程由组合逻辑实现。输出output=M{CS }仅由现态cs决定。由现态cs生成次态ns的关系描述可以由PROCESS过程中由if语句或case语句控制实现。
例1: 10进制计数器及数码管译码显示状态机的描述
变量定义如下:
时钟: clk : in std_logic
状态: count : std_logic_vector(3 downto 0)
输出 :SEVEN_SEG : out std_logic_vector(6 downto 0)
程序如下:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.std_logic_unsigned.all;
entity count_dis is
port(
clk : in std_logic;
rst : in std_logic;
SEVEN_SEG : out std_logic_vector(6 downto 0)
);
end count_dis;
architecture Behavioral of count_dis is
signal count : std_logic_vector(3 downto 0):= X"0";
begin
PROCESS(clk,rst)
BEGIN
IF(rst = '1') THEN
count <= X"0";
ELSIF rising_edge(clk) THEN
IF (count = X"9") THEN
count <= X"0";
ELSE
count <= count + 1;
END IF;
END IF;
END PROCESS;
PROCESS(count)
BEGIN
CASE count IS
WHEN X"0" => SEVEN_SEG <= "1000000";
WHEN X"1" => SEVEN_SEG <= "1111001";
WHEN X"2" => SEVEN_SEG <= "0100100";
WHEN X"3" => SEVEN_SEG <= "0110000";
WHEN X"4" => SEVEN_SEG <= "0011001";
WHEN X"5" => SEVEN_SEG <= "0010010";
WHEN X"6" => SEVEN_SEG <= "0000010";
WHEN X"7" => SEVEN_SEG <= "1111000";
WHEN X"8" => SEVEN_SEG <= "0000000";
WHEN X"9" => SEVEN_SEG <= "0010000";
WHEN X"A" => SEVEN_SEG <= "0001000";
WHEN X"B" => SEVEN_SEG <= "0000011";
WHEN X"C" => SEVEN_SEG <= "1000110";
WHEN X"D" => SEVEN_SEG <= "0100001";
WHEN X"E" => SEVEN_SEG <= "0000110";
WHEN X"F" => SEVEN_SEG <= "0001110";
WHEN OTHERS => SEVEN_SEG <= "1111111";
END CASE;
END PROCESS;
end Behavioral;
从上面的例子可以看出状态转换是在PROCESS过程中由if语句实现的,看看如何和上面的方程对应的。
- ns在状态(count)为9时 ns=0; 在状态为0–8时, ns=count+1,组合逻辑方程,
- cs=ns(在clk的上升沿驱动下),这里的cs实际上就是count,时序逻辑,
- 输出 SEVEN_SEG 由case语句实现(组合逻辑)
上面的状态机程序也可以改为例2方式实现,
例2 用cs 与ns 的方式显示描述10进制计数器的状态机描述。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.std_logic_unsigned.all;
entity count_dis is
port(
clk : in std_logic;
rst : in std_logic;
SEVEN_SEG : out std_logic_vector(6 downto 0)
);
end count_dis;
architecture Behavioral of count_dis is
signal count : std_logic_vector(3 downto 0):= X"0";
signal ns : std_logic_vector(3 downto 0):= X"0";
begin
ns <= X"0" when (count = X"9") else
count + 1;
PROCESS(clk,rst)
BEGIN
IF(rst = '1') THEN
count <= X"0";
ELSIF rising_edge(clk) THEN
count <= ns;
END IF;
END PROCESS;
PROCESS(count)
BEGIN
CASE count IS
WHEN X"0" => SEVEN_SEG <= "1000000";
WHEN X"1" => SEVEN_SEG <= "1111001";
WHEN X"2" => SEVEN_SEG <= "0100100";
WHEN X"3" => SEVEN_SEG <= "0110000";
WHEN X"4" => SEVEN_SEG <= "0011001";
WHEN X"5" => SEVEN_SEG <= "0010010";
WHEN X"6" => SEVEN_SEG <= "0000010";
WHEN X"7" => SEVEN_SEG <= "1111000";
WHEN X"8" => SEVEN_SEG <= "0000000";
WHEN X"9" => SEVEN_SEG <= "0010000";
WHEN X"A" => SEVEN_SEG <= "0001000";
WHEN X"B" => SEVEN_SEG <= "0000011";
WHEN X"C" => SEVEN_SEG <= "1000110";
WHEN X"D" => SEVEN_SEG <= "0100001";
WHEN X"E" => SEVEN_SEG <= "0000110";
WHEN X"F" => SEVEN_SEG <= "0001110";
WHEN OTHERS => SEVEN_SEG <= "1111111";
END CASE;
END PROCESS;
end Behavioral;
仿真图如下所示:

从上例的描述可以清楚的看出状态机的现态cs(count)及次态ns的实现。而且从本例可以看出,输出仅仅是状态的译码,与输入无关,因此是典型的摩尔机。当然ns的描述也可以在PROCESS过程中由case语句实现。如例3:
例3,
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.std_logic_unsigned.all;
entity count_dis is
port(
clk : in std_logic;
rst : in std_logic;
SEVEN_SEG : out std_logic_vector(6 downto 0)
);
end count_dis;
architecture Behavioral of count_dis is
signal count : std_logic_vector(3 downto 0):= X"0";
signal ns : std_logic_vector(3 downto 0):= X"0";
begin
PROCESS(COUNT)
BEGIN
CASE count IS
WHEN X"0" | X"1" | X"2" | X"3" | X"4" | X"5" | X"6" | X"7" | X"8" => ns <= count + 1;
WHEN X"9" => ns <= X"0";
WHEN OTHERS => ns <= X"0";
END CASE;
END PROCESS;
PROCESS(clk,rst)
BEGIN
IF(rst = '1') THEN
count <= X"0";
ELSIF rising_edge(clk) THEN
count <= ns;
END IF;
END PROCESS;
PROCESS(count)
BEGIN
CASE count IS
WHEN X"0" => SEVEN_SEG <= "1000000";
WHEN X"1" => SEVEN_SEG <= "1111001";
WHEN X"2" => SEVEN_SEG <= "0100100";
WHEN X"3" => SEVEN_SEG <= "0110000";
WHEN X"4" => SEVEN_SEG <= "0011001";
WHEN X"5" => SEVEN_SEG <= "0010010";
WHEN X"6" => SEVEN_SEG <= "0000010";
WHEN X"7" => SEVEN_SEG <= "1111000";
WHEN X"8" => SEVEN_SEG <= "0000000";
WHEN X"9" => SEVEN_SEG <= "0010000";
WHEN X"A" => SEVEN_SEG <= "0001000";
WHEN X"B" => SEVEN_SEG <= "0000011";
WHEN X"C" => SEVEN_SEG <= "1000110";
WHEN X"D" => SEVEN_SEG <= "0100001";
WHEN X"E" => SEVEN_SEG <= "0000110";
WHEN X"F" => SEVEN_SEG <= "0001110";
WHEN OTHERS => SEVEN_SEG <= "1111111";
END CASE;
END PROCESS;
end Behavioral;
例3是典型的3段式状态机。至于如何使用一段式,二段式,三段式状态机,下节内容将会详细讲解。
-
梅利机
梅利机与摩尔机不同,其输出不仅与状态有关还与输入有关。梅利机的描述方程如图2

图3
从图2可以看出,梅利机与摩尔机的惟一区别就在于输出。

图4
梅利机的描述如下面的例子所示,在状态S0时系统的输出也由输入与状态共同决定,这种状态机是典型的梅利机。梅利机中的输出可以是寄存器输出,也可以是组合逻辑输出。一般为了输出稳定都在clk的边沿下锁存输出。
TYPE FSM_ST is (S0,S1);
signal state: FSM_ST;
PROCESS(clk)
BEGIN
IF rising_edge(clk) THEN
IF(rst = '1') THEN
outputx <= ...;
state <= ...;
ELSE CASE (state) IS
WHEN S0 =>
IF (inputx = '1') THEN
outputx <= ...;
state <= ...;
END IF;
WHEN S1 => ...;
WHEN OTHERS =>
outputx <= ...;
state <= ...;
END CASE;
END IF;
END IF;
END PROCESS;
例4. 利用mealy机实现CPU指令的读取、译码、寄存器读取、执行、回写等过程。
有许多CPU的运行是由状态机实现的。本文举例的CPU运行的基本过程是指令读取、译码、寄存器读取、执行、寄存器(存储器)回写。这5个过程也可以发展为流水线的方式实现,也就成了5级流水线CPU架构,流水线架构依然离不开状态机的控制。

图5
在图5中,由状态机控制完成整个流程,步骤如下
- 状态1: 首先产生指令存储器的地址(ADDR),跳转到状态2
- 状态2:读取指令(input),跳转到状态3
- 状态3:指令输出(output),指令译码,跳转到状态4
- 状态4:寄存器读取,跳转到状态5
- 状态5:指令执行,跳到状态6,
- 状态6:判断是否需要寄存器回写,生成新的指令地址,返回状态2。
假设系统复位后的首地址为32’h8000_0000,指令为32位,程序如下:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.std_logic_unsigned.all;
Library UNISIM;
use UNISIM.vcomponents.all;
entity CPU_run is
port(
clk : in std_logic;
rst : in std_logic;
--从CPU的执行阶段,获得下条指令的地址
addr_in : in std_logic_vector(31 downto 0);
instr : in std_logic_vector(31 downto 0);
addr_r : out std_logic_vector(31 downto 0);
instr_r : out std_logic_vector(31 downto 0);
dec_r : out std_logic;
reg_rd_r : out std_logic;
exe_r : out std_logic; --执行指示
wr_b_r : out std_logic; --write back
wr_b_need : in std_logic --write back need,由译码产生
);
end CPU_run;
architecture Behavioral of CPU_run is
TYPE FSM_ST IS (S0,S1,S2,S3,S4,S5,S6);
signal cpu_run_st : FSM_ST;
begin
PROCESS(clk,rst)
BEGIN
IF (rst = '1') THEN
addr_r <= X"80000000";
instr_r <= X"ffffffff";
dec_r <= '0';
reg_rd_r <= '0';
exe_r <= '0';
wr_b_r <= '0';
cpu_run_st <= S0;
ELSE
CASE cpu_run_st IS
WHEN S0 => --start , addr_r 设为32'h8000_0000,初始化
addr_r <= X"80000000";
instr_r <= X"ffffffff";
dec_r <= '0';
reg_rd_r <= '0';
exe_r <= '0';
wr_b_r <= '0';
cpu_run_st <= S1;
WHEN S1 => --等待一个时钟,一般存储器输出会有一个时钟的delay
dec_r <= '0';
reg_rd_r <= '0';
exe_r <= '0';
wr_b_r <= '0';
cpu_run_st <= S2;
WHEN S2 => --指令输出到译码器 , 指令输出
instr_r <= instr;
cpu_run_st <= S3;
WHEN S3 => --decode stage,译码阶段
cpu_run_st <= S4;
dec_r <= '1';
WHEN S4 => --寄存器读取
cpu_run_st <= S5;
dec_r <= '0';
reg_rd_r <= '1';
WHEN S5 => --执行阶段
reg_rd_r <= '0';
exe_r <= '1';
cpu_run_st <= S6;
WHEN S6 => --回写
exe_r <= '0';
cpu_run_st <= S1;
addr_r <= addr_in;
IF(wr_b_need = '1') THEN --wr_b_need 由指令译码产生
wr_b_r <='1';
ELSE
wr_b_r <= '0';
END IF;
WHEN OTHERS => cpu_run_st <= S0;
END CASE;
END IF;
END PROCESS;
end Behavioral;
从上面的程序可以看出,指令输出不仅和状态有关,同时输出由输入指令(instr)决定,如wr_b_need是由输入指令(instr)产生的信号,决定wr_b_r输出是否有效,因此是典型的mealy机。
-
摩尔机与梅利机比较
下面以一个例子说明摩尔机与梅利机的不同,如例5:
例5:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.std_logic_unsigned.all;
Library UNISIM;
use UNISIM.vcomponents.all;
entity edgeDetector is
port(
clk : in std_logic;
rst : in std_logic;
level : in std_logic;
Mealy_tick : out std_logic;
Moore_tick : out std_logic
);
end edgeDetector;
architecture Behavioral of edgeDetector is
TYPE MEALY_FSM_ST IS (ZERO_MEALY,ONE_MEALY);
signal Mealy_cs,Mealy_ns : MEALY_FSM_ST;
TYPE MOORE_FSM_ST IS (ZERO_MOORE,EDGE_MOORE,ONE_MOORE);
signal Moore_cs, Moore_ns : MOORE_FSM_ST;
begin
--current state <= next state , first stage
PROCESS(clk,rst)
BEGIN
IF (rst = '1') THEN
Mealy_cs <= ZERO_MEALY;
Moore_cs <= ZERO_MOORE;
ELSE
Mealy_cs <= Mealy_ns;
Moore_cs <= Moore_ns;
END IF;
END PROCESS;
--Mealy state machine , second stage
PROCESS(Mealy_cs, level)
BEGIN
Mealy_ns <= Mealy_cs;
Mealy_tick <= '0';
CASE Mealy_cs IS
WHEN ZERO_MEALY =>
IF (level = '1') THEN
Mealy_ns <= ONE_MEALY;
Mealy_tick <= '1';
END IF;
WHEN ONE_MEALY =>
IF (level = '0') THEN
Mealy_ns <= ZERO_MEALY;
END IF;
END CASE;
END PROCESS;
--Moore state machine, second stage
PROCESS(Moore_cs, level)
BEGIN
Moore_ns <= Moore_cs;
Moore_tick <= '0';
CASE Moore_cs IS
WHEN ZERO_MOORE =>
IF (level = '1') THEN
Moore_ns <= EDGE_MOORE;
END IF;
WHEN EDGE_MOORE =>
Moore_tick <= '1';
IF (level = '1') THEN
Moore_ns <= ONE_MOORE;
ELSE
Moore_ns <= ZERO_MOORE;
END IF;
WHEN ONE_MOORE =>
IF (level = '0') THEN
Moore_ns <= ZERO_MOORE;
END IF;
END CASE;
END PROCESS;
end Behavioral;
仿真testbench如下所示(使用Verilog):
`timescale 1ns / 1ps
module tb(
);
reg clk = 0;
reg in = 0;
reg rst = 0;
wire Mealy_tick;
wire Moore_tick;
always #5 clk = ~clk;
initial
begin
rst = 1;
#200;
rst = 0;
#3 in = 1;
#12 in = 0;
#20 in = 1;
#10 in = 0;
#31 in = 1;
#2000;
$stop;
end
edgeDetector edgeDetector_inst
(
.clk(clk),
.rst(rst),
.level(in),
.Mealy_tick(Mealy_tick),
.Moore_tick(Moore_tick)
);
endmodule
仿真的波形图如图5,

图5
从图5可以看出Mealy_tick的输出紧随输入level,而摩尔机的输出Moore 机的输出是Moore_tick与clk的边沿对齐的,因此Moore 机的输出只与状态有关。上面的状态机是典型的二段式状态机,关于一段,二段,三段式状态机,下节内容将详细讨论。
3. 状态机中输入与状态的关系
从上面的几个例子可以看出,例1,2,3的状态跳转只与状态自身有关,而与输入无关,因此状态机的转移步伐是等间隔的。这种应用场景也是比较常见的,如钟表的计时,整点报时,等间隔流水作业的过程控制等。而更多的状态机的跳转是在输入的条件下进行的,这样就实现了跳转间隔不等长的情况,可以根据需要调整状态跳转的时间,灵活多变的时间分配可以处理更复杂的需求。如例4,例5所示。注意这里的输入可以是输入输出接口,也可能是实体模块内部组合逻辑产生的信号。
老师您好,刚刚在地铁上阅读了老师新发布的文章“状态机建模与VHDL程序设计”,阅读后对VHDL语言产生了浓厚的兴趣,本篇文章运用VHDL语言实现状态机模型,逻辑清晰,很有助于理解VHDL语言的语法及使用规则,很希望后期有关于VHDL语言基本语法的视频课程上线,谢谢老师!