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
相关推荐
Scott9999HH6 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社7 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海7 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖8 小时前
JDK 26 新特性详解
java·开发语言
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
千寻xun9 小时前
三、实战篇-NVME SSD控制之FPGA实现(一)
fpga开发·nvme ssd·nvme协议
人邮异步社区10 小时前
怎么把C语言学到精通?
c语言·开发语言
sramdram10 小时前
MCU微控制器OA打印机方案
单片机·嵌入式硬件·mcu·mcu微控制器
心平气和量大福大11 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai11 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang