Menu Close

状态机建模与VHDL程序设计–摩尔机与梅利机

上节内容讲解了状态机的基本理论、分类以及在数字电路,软件方面的建模及应用,最后以简单的例子对比常规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所示。注意这里的输入可以是输入输出接口,也可能是实体模块内部组合逻辑产生的信号。

 

Posted in FPGA, FPGA 教材教案, Quartus II, Quartus II, VHDL, VHDL, Vivado, Vivado, 开发工具, 开发工具, 开发语言, 教材与教案, 文章, 编程语言

1 Comment

  1. 8班王飞飞

    老师您好,刚刚在地铁上阅读了老师新发布的文章“状态机建模与VHDL程序设计”,阅读后对VHDL语言产生了浓厚的兴趣,本篇文章运用VHDL语言实现状态机模型,逻辑清晰,很有助于理解VHDL语言的语法及使用规则,很希望后期有关于VHDL语言基本语法的视频课程上线,谢谢老师!

发表回复

相关链接