012---状态机的基本知识

1. 摘要

文章为学习记录。主要介绍状态机概述、状态转移图、状态编码、状态机写法、状态机代码示例。

2. 状态机概述

状态机 (Finite State Machine),也称为同步有限 状态机,用于描述有先后顺序或时序规律的事情。

"同步 ":状态机中所有的状态跳转都是在时钟的作用下进行的。

"有限 ":状态的个数是有限的。
Moore 型状态机 :最后的输出只和当前状态有关而与输入无关。
Mealy 型状态机: 最后的输出不仅和当前状态有关还和输入有关。

3. 状态转移图

状态转移图能够表达出状态机的状态状态跳转的条件

状态转移图三要素:

(a)输入:根据输入可以确定是否需要进行状态跳转以及输出,是影响状态机系统执行过程的重要驱动力;

(b)输出:根据当前时刻的状态以及输入确定,是状态机系统最终要执行的动作;

(c)状态:根据输入和上一状态决定当前时刻所处的状态,是状态机系统执行的一个稳定的过程。
输入有多少种情况,每个状态的跳转就有多少种情况。

常见的状态转移图如下图所示。

4. 状态编码

以3个状态数为例:

(a) 独热码:3'b001,3'b010,3'b100; 使用的寄存器资源多,组合逻辑资源少。

(b) 二进制码:2'b00,2'b01,2'b10;使用的寄存器资源少,组合逻辑资源多。

(c)格雷码:2'b00,2'b01,2'b11;使用的寄存器资源少,组合逻辑资源多。其相邻状态转换时只有1bit不同。

状态编码方式如下表所示。

5. 状态机写法

一段式:在一段状态机中使用时序逻辑既描述状态的转移,也描述数据的输出;

二段式:在第一段状态机中使用时序逻辑描述状态转移,在第二段状态机中使用组合逻辑描述数据的输出;

三段式:在第一段状态机中采用时序逻辑描述状态转移,在第二段在状态机中采用组合逻辑判断状态转移条件描述状态转移规律,在第三段状态机中描述状态输出,可以用组合电路输出,也可以时序电路输出;

新二段式:使用两个均采用时序逻辑的 always 块。第一个 always 块描述状态的转移为第一段状态机,第二个 always 块描述数据的输出为第二段状态机(如果我们遵循一个 always 块只描述一个变量的原则,如果有多个输出时第二段状态机就可以分为多个always 块来表达)。

6. 状态机代码示例

复制代码
module  complex_fsm
(
    input   wire    sys_clk         ,   //系统时钟50MHz
    input   wire    sys_rst_n       ,   //全局复位
    input   wire    pi_money_one    ,   //投币1元
    input   wire    pi_money_half   ,   //投币0.5元
                    
    output  reg     po_money        ,   //po_money为1时表示找零
                                        //po_money为0时表示不找零
    output  reg     po_cola             //po_cola为1时出可乐
                                        //po_cola为0时不出可乐
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
//只有五种状态,使用独热码
parameter   IDLE     = 5'b00001;
parameter   HALF     = 5'b00010;
parameter   ONE      = 5'b00100;
parameter   ONE_HALF = 5'b01000;
parameter   TWO      = 5'b10000;

//reg   define
reg     [4:0]   state;

//wire  define
wire    [1:0]   pi_money;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//pi_money:为了减少变量的个数,我们用位拼接把输入的两个1bit信号拼接成1个2bit信号
//投币方式可以为:不投币(00)、投0.5元(01)、投1元(10),每次只投一个币
assign pi_money = {pi_money_one, pi_money_half};

//第一段状态机,描述当前状态state如何根据输入跳转到下一状态
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state <= IDLE;  //任何情况下只要按复位就回到初始状态
    else	case(state)
                IDLE    : if(pi_money == 2'b01)   //判断一种输入情况
                              state <= HALF;
                          else    if(pi_money == 2'b10)//判断另一种输入情况
                              state <= ONE;
                          else
                              state <= IDLE;
    
                HALF    : if(pi_money == 2'b01)
                              state <= ONE;
                          else    if(pi_money == 2'b10)
                              state <= ONE_HALF;
                          else
                              state <= HALF;
    
                ONE     : if(pi_money == 2'b01)
                              state <= ONE_HALF;
                          else    if(pi_money == 2'b10)
                              state <= TWO;
                          else
                              state <= ONE;
    
                ONE_HALF: if(pi_money == 2'b01)
                              state <= TWO;
                          else    if(pi_money == 2'b10)
                              state <= IDLE;
                          else
                              state <= ONE_HALF;
    
                TWO     : if((pi_money == 2'b01) || (pi_money == 2'b10))
                              state <= IDLE;
                          else
                              state <= TWO;
        //如果状态机跳转到编码的状态之外也回到初始状态
                default :       state <= IDLE;
            endcase

//第二段状态机,描述当前状态state和输入pi_money如何影响po_cola输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_cola <= 1'b0;
    else    if((state == TWO && pi_money == 2'b01) || (state == TWO && 
          pi_money == 2'b10) || (state == ONE_HALF && pi_money == 2'b10))
        po_cola <= 1'b1;
    else
        po_cola <= 1'b0;

//第二段状态机,描述当前状态state和输入pi_money如何影响po_money输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n ==	1'b0)
        po_money <= 1'b0;
    else if((state == TWO) && (pi_money == 2'b10))
        po_money <= 1'b1;
    else
        po_money <= 1'b0;

endmodule
相关推荐
~请叫我小祸害~21 分钟前
在 C# 中使用 Dapper 查询数据并导出 Excel
开发语言·数据库·c#·excel
美好的事情总会发生33 分钟前
PECL(Positive Emitter-Coupled Logic)电平详解
嵌入式硬件·硬件工程·智能硬件
张志翔的博客38 分钟前
RK3588 openssl-3.4.1 编译安装
开发语言·后端·scala
m0_555762901 小时前
qt style-sheet样式不起作用问答
开发语言·qt
啊吧怪不啊吧1 小时前
C++相关基础概念之入门讲解(上)
c语言·开发语言·c++
破刺不会编程1 小时前
Linux中的权限
linux·运维·服务器·开发语言
灏瀚星空1 小时前
Python JSON模块详解:从入门到高级应用
开发语言·经验分享·笔记·python·json
azaz_plus1 小时前
C++ priority_queue 堆
开发语言·c++·stl··priority_queue
上官美丽1 小时前
单一责任原则在Java设计模式中的深度解析
java·开发语言·设计模式
橙序研工坊1 小时前
Java基础语法练习42(基本绘图-基本的事件处理机制-小坦克的绘制-键盘控制坦克移动)
java·开发语言