• FPGA:什么是状态机?状态机的结构?状态机怎么用?


    前言

    状态机在学习FPGA初期就经常遇到,有必要写一篇笔记整理一下

    正文

    一、什么是状态机

    为啥要用状态机

    由于FPGA是并行执行,但当我想要处理有先后顺序的问题,这个时候就需要用状态机来解决

    有几种状态机让我选

    Moore状态机Mealy状态机(记得B站数电老师讲到过这两个词语,先挖个坑)

    这两个让我怎么选

    Moore状态机:输出只和当前状态有关,与输入无关。
    Mealy状态机:输出和输入、当前状态有关(常用
    相同点:状态的跳转只和输入有关

    二、状态机的结构

    由于一段式状态机、两段式状态机、三段式状态机中,三段式状态机是最常用的,我就暂时只记录三段式状态机的相关内容


    2.1 状态编码

    首先要将状态机的所有状态进行编码,这个编码只需要保证没有重复编码即可

    parameter IDLE       =    3'd0;
    parameter HALF_1     =    3'd1;
    parameter ONE        =    3'd2;
    parameter HALF_3     =    3'd3;
    
    • 1
    • 2
    • 3
    • 4

    2.2 第一段:时序逻辑

    第一段是时序逻辑: 描述状态转换,格式固定

    always@(posedge Clk or negedge Rst_n)begin
    if(Rst_n == 1'b0)
    	state <= IDLE;
    else 
    	state <= next_state;
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.3 第二段:组合逻辑

    第二段是组合逻辑
    状态转移条件:只考虑状态之间的跳转

    always@(*)
        case(state)
            S0:
            	if()
            		next_state <= S1;
    	endcase
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.4 第三段:时序逻辑

    第三段是时序逻辑:输出状态

    always@(posedge Clk or negedge Rst_n)begin
    if(Rst_n == 1'b0)
        output<= 1'b0;
    else if(((state == S0) && (input== 1'b1)) || ((state == S1) && (input == 1'b1)))//输出由输入+状态共同决定
        output<= 1'b1;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    三、状态机怎么用?

    3.1 可乐贩卖机

    目标:可乐贩卖机;饮料单价 2 元,该售卖机只能接受 0.5 元、1 元的硬币,考虑找零和出货

    思路:首先需要确定选什么作为状态,这里无非就只有可乐和钱,我一定是选钱
    明确输入、输出、状态跳变,画出状态转移图

    状态机选型:三段式状态机


    设计文件

    module fsm_3
    (
    input Clk,
    input Rst_n,
    input pi_input_5,
    input pi_input_10,
    
    output reg po_out_money,
    output reg po_out_water
    );
    
    parameter IDLE       =    3'd0;
    parameter HALF_1     =    3'd1;
    parameter ONE        =    3'd2;
    parameter HALF_3     =    3'd3;
    
    reg [3:0] state        ;
    reg [3:0] next_state    ;
    
    // 第一段
    always@(posedge Clk or negedge Rst_n)begin
    if(Rst_n == 1'b0)
    	state <= IDLE;
    else 
    	state <= next_state;
    end
    
    // 第二段
    always@(*)begin
    case(state)
        IDLE: 
            case({pi_input_5, pi_input_10})
            2'b10: next_state<= HALF_1;
            2'b01: next_state<= ONE;
            default: next_state<= IDLE;
            endcase            
        HALF_1: 
            case({pi_input_5, pi_input_10})
            2'b10: next_state<= ONE;
            2'b01: next_state<= HALF_3;
            default: next_state<= HALF_1;
            endcase
        ONE: 
            case({pi_input_5, pi_input_10})
            2'b10: next_state<= HALF_1;
            2'b01: next_state<= IDLE;
            default: next_state<= ONE;
            endcase
        HALF_3: 
            case({pi_input_5, pi_input_10})
            2'b10: next_state<= IDLE;
            2'b01: next_state<= IDLE;
            default: next_state<= HALF_3;
            endcase            
                            
        endcase
    end
    
    // 第三段
    always@(posedge Clk or negedge Rst_n)begin
    if(Rst_n == 1'b0)
        po_out_water <= 1'b0;
    else if(((state == HALF_3) && (pi_input_5 == 1'b1)) || ((state == ONE) && (pi_input_10 == 1'b1)))
        po_out_water <= 1'b1;
    else if((state == HALF_3) && (pi_input_10 == 1'b1)) begin
        po_out_water <= 1'b1;
        po_out_money <= pi_input_5;
        end
    else begin
        po_out_water <= 1'b0;
        po_out_money <= 1'b0;
    end    
    end
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    测试文件

    `timescale 1ns/1ns
    
    module tb_fsm_3;
    reg clk;
    reg Rst_n;
    reg in_1;
    reg in_2;
    reg[1:0] in[2:0];
    wire out_money;
    wire out_water;
    
    initial begin 
    in[0]=0;
    in[1]=1;
    in[2]=2;
    clk = 1'b0;
    Rst_n = 1'b0;
    #10;
    Rst_n = 1'b1;
    in_1 = 1'b0;
    in_2 = 1'b0;
    
    end
     
    always #10 clk = ~ clk;
    
    always@(posedge clk or negedge Rst_n)begin
    if(Rst_n == 1'b0)begin
        in_1 <= 1'b0;
        in_2 <= 1'b0;
        end
    else begin
        {in_1,in_2}<=in[{$random}%3];
        end
     
    end
    
    fsm_3 fsm_3_inst
    (
    .Clk(clk),
    .Rst_n(Rst_n),
    .pi_input_5(in_1),
    .pi_input_10(in_2),
    
    .po_out_money(out_money),
    .po_out_water(out_water)
    );
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    3.2 序列检测器(序列可重叠)

    序列检测器是个啥:检测输入的一串二进制代码中,是否存在与事先设定的码组一致的代码串,检测电路输出高电平,否则输出低电平

    序列检测器用在什么地方:检测通信系统中的同步码、提取所需信号

    要做的事:使用状态机检测出一串数字序列中的指定6位数据:101011
    在这里插入图片描述
    在这里插入图片描述

    状态检测序列
    S0IDLE
    S1检测1
    S2检测10
    S3检测101
    S4检测1010
    S5检测10101
    S6检测101011

    在这里插入图片描述


    《关于状态需要返回去东山再起》

    是否需要返回:不满足检测序列就需要返回
    前面几位能用:有几位满足检测序列顺序
    自己能否作为起点:自己和序列第1 位相同就能作为起点
    在这里插入图片描述


    设计文件

    module seq_1010110
    (
    input clk                    ,
    input rst_n                    ,
    input data                    ,
    
    output reg flage
    );
    
    reg [2:0]state ;// 下面对状态编码用了1-7,所以这里需要用3位的位宽
    reg [2:0]next_state;
    
    parameter S0 = 3'd1;// IDLE 
    parameter S1 = 3'd2;
    parameter S2 = 3'd3;
    parameter S3 = 3'd4;
    parameter S4 = 3'd5;
    parameter S5 = 3'd6;
    parameter S6 = 3'd7;
    
    always@(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)
        state <= S0;
    else 
        state <= next_state;
    end
    
    always@(*)
        case(state)
            S0:
            if(data == 1)
                next_state <= S1;
            else 
                next_state <= S0;
            S1:
            if(data == 0)
                next_state <= S2;
            else 
                next_state <= S0;
            S2:
            if(data == 1)
                next_state <= S3;
            else 
                next_state <= S0;
            S3:
            if(data == 0)
                next_state <= S4;
            else 
                next_state <= S1;
            S4:
            if(data == 1)
                next_state <= S5;
            else 
                next_state <= S0;
            S5:
            if(data == 1)
                next_state <= S6;
            else 
                next_state <= S4;
            S6:
            if(data == 0)
                next_state <= S2;
            else 
                next_state <= S1;
            
        endcase 
    
    always@(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)
        flage <= 1'b0;    
    else if(state == S6 && data == 0) // Mealy状态机:输出由输入和状态共同决定   
        flage <= 1'b1;
    else 
        flage <= 1'b0;        
    end    
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    测试文件

    `timescale 1ns/1ps
    
    module sequence1010110_tb();
    reg                            clk;
    reg                            rst_n;
    reg                            data;
    
    wire                           flag;
    reg             [15:0]        ascii_state;//用了2位字符S0-S6,1位字符8bit,所以这里位宽是16
    wire             [2:0]            tb_state;
    
    assign tb_state = seq_1010110_inst.state;
    
    always@(*)begin // 在波形图上能看到用S表示的状态ascii_state
      case(tb_state)
            3'd1    :    ascii_state = "S0";
            3'd2    :    ascii_state = "S1";
            3'd3    :    ascii_state = "S2";
            3'd4    :    ascii_state = "S3";
            3'd5    :    ascii_state = "S4";
            3'd6    :    ascii_state = "S5";
            3'd7    :    ascii_state = "S6";
            default :  ascii_state = "NO";
      endcase
    end
    
    initial begin 
     
     clk = 1'b0;
     rst_n = 1'b0;
     data = 1'b0;
     #10;
     rst_n = 1'b1;
     end
     always #10 clk = ~ clk;
     
     
     always begin
     #20;
     data = 1'b1;
     #20;
     data = 1'b0;
      #20;
     data = 1'b1;
      #20;
     data = 1'b0;
      #20;
     data = 1'b1;
      #20;
     data = 1'b1;
      #20;
     data = 1'b0;
      #20;
     data = 1'b1;
     
     end
    
        seq_1010110 seq_1010110_inst(
            .clk                 (clk),
            .rst_n               (rst_n),
            .data                (data),
    
            .flage                    (flag)
        );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    波形图

    怎么看波形:
    重点看state和data,next_state只是用来给state传递数据的,可以不用重点看
    重点看flag在什么时候被拉高,除了要在时钟上升沿,还需要注意是否需要比 (state == S6 && data == 0) 慢一拍
    在这里插入图片描述

  • 相关阅读:
    Go 什么是循环依赖
    1991年至2008年澳大利亚每月的抗糖尿病药物补贴时间序列分析
    WordPress站点如何实现发布文章即主动推送到百度快速收录和普通收录?
    单例设计模式
    JVM优化之 -Xss -Xms -Xmx -Xmn 参数设置
    R语言读取(加载)txt格式数据为dataframe、查询dataframe中存在缺失值的行
    解决AndroidStudio 提交代码到github失败的问题
    如何使用 NestJs、PostgreSQL、Redis 构建基于用户设备的授权验证
    使用 NIO 模式简单模拟一个 Tomcat 运行原理
    1. pip和conda的区别
  • 原文地址:https://blog.csdn.net/m0_46830519/article/details/125477976