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

本文详细介绍了FPGA中的状态机概念,包括Moore和Mealy状态机的区别,并通过三段式状态机结构进行说明。文中通过可乐贩卖机和序列检测器两个实例,展示了状态机在解决顺序控制问题中的应用,帮助读者深入理解状态机的设计与实现。
摘要由CSDN通过智能技术生成

前言

状态机在学习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;

2.2 第一段:时序逻辑

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

always@(posedge Clk or negedge Rst_n)begin
if(Rst_n == 1'b0)
	state <= IDLE;
else 
	state <= next_state;
end

2.3 第二段:组合逻辑

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

always@(*)
    case(state)
        S0:
        	if()
        		next_state <= S1;
	endcase

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;

三、状态机怎么用?

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
测试文件
`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

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
测试文件
`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
波形图

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值