FPGA 13 状态机学习(字符串状态机)
模块结构:
主要功能: 设计了一个判断 “ Hello ” 字符串判断的状态机,当一个数据流中出现了 ”Hello“字符串 ,那么 OUT 的输出电平就会翻转
实现(设计)流程:内部设计一个状态机,当时钟上升沿到来的时候,接收data[7:0]的数据流,连续判断是否接收到 ‘Hello’,只要数据流中出现 ‘Hello ’字符,那么输出的OUT的电平进行翻转,否则电平状态不发生改变。
注:设计状态机时,每一个状态有个单独的判断标志码(编码),我们在实现状态机的使用的是case(state): 语句,所以进入状态判断时,需要根据输入来判断是否进入到下一个状态,或者时回归到空闲状态。
注: 在设计状态机时,可以看到,这个是基于一个 case + if --else 语句来实现状态机功能的,case:语句来判断当前处于某种状态,if–else 来执行state 状态跳转和执行当前状态下所需要执行的语句。
实验目的 : 了解基本的状态机在FPGA的使用。区分一段、二段、三段式状态机的区别。后续有(专题:状态机)
数据判断状态机的设计基本框图 :
Hello.v 文件
module Hello(Clk,Rst_n,data,led);
input Clk;//50M
input Rst_n;//低电平复位
input [7:0]data;
output reg out;
localparam
CHECK_H = 5'b0_0001,
CHECK_e = 5'b0_0010,
CHECK_la = 5'b0_0100,
CHECK_lb = 5'b0_1000,
CHECK_o = 5'b1_0000;
reg[4:0]state;
//状态机表示状态的 3种编码方式:
// 1、独热码 6个状态的独热码状态编码为:000001,000010,000100,001000,010000,100000
// 独热码优势 :大大简化解码方式,效率高,但是占用更多的寄存器资源,可以看的一个状态占用 1个位.
// 2、二进制编码 6个状态的独热码状态编码为:000,001,010,011,100,101
// 3、格雷码 格雷码特点: 0000 0001 0011 0010 0110 ,每个状态和相邻的只有一二进制是变化的,其余保持不变
// 状态机分类 :
// 一段式状态机 (本次学习使用)
// 两段式状态机
// 三段式状态机 (效率最高、收敛性最好)
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
out <= 1'b1; //设置初始值为高电平
state <= CHECK_H; //设置 初始状态机的状态 state <= CHECK_H;
end
else begin
case(state) //判断 state 的值
CHECK_H: //如果现在是 CHECK_H 的状态
if(data == "H") //判断此时 data 接收到的数据是否是 ‘H’
state <= CHECK_e; //是 ,则进入下一个状态 CHECK_e
else
state <= CHECK_H; //不是,回到最初 CHECK_H 的状态
CHECK_e:
if(data == "e")
state <= CHECK_la;
else
state <= CHECK_H;
CHECK_la:
if(data == "l")
state <= CHECK_lb;
else
state <= CHECK_H;
CHECK_lb:
if(data == "l")
state <= CHECK_o;
else
state <= CHECK_H;
CHECK_o:
begin
state <= CHECK_H; //到了最后一个 CHECK_o状态,
//不论怎么样,都需要将状态返回到最初的CHECK_H 状态
if(data == "o") //接收到最后一个 是字符 ‘ o ’,表示已经成功接收到一个完整的字符串
out <= ~led; //输出结果翻转
else
out <= led; //保持该信号
end
default:state <= CHECK_H;
endcase
end
endmodule
/*
CHECK_o : //进入第5个状态条件
if(data == "o") begin
led <= ~led ;
state <= CHECK_H ;
end
else
state <= CHECK_H ;
default : //其它非正常状态
state <= CHECK_H ;
*/
Hello_tb.v 文件
`timescale 1ns/1ns
`define clock_period 20
module Hello_tb ;
reg Clk ;
reg Rst_n ;
reg [7:0]ASCII;
wire led;
Hello Hello_0(
.Clk(Clk) ,
.Rst_n(Rst_n) ,
.data(ASCII) ,
.out(led)
);
initial Clk = 1 ;
always#(`clock_period/2) Clk = ~Clk ;
initial begin
//信号初始化
Rst_n = 0;
ASCII = 0;
#(`clock_period * 200);
Rst_n = 1;
#(`clock_period * 200 +1);
// forever : 循环调用,在仿真的时候设置仿真时间即可
forever begin
ASCII = "I";
#(`clock_period);
ASCII = "A";
#(`clock_period);
ASCII = "M";
#(`clock_period);
ASCII = "X";
#(`clock_period);
ASCII = "i";
#(`clock_period);
ASCII = "a";
#(`clock_period);
ASCII = "o";
#(`clock_period);
ASCII = "M";
#(`clock_period);
ASCII = "e";
#(`clock_period);
ASCII = "i";
#(`clock_period);
ASCII = "g";
#(`clock_period);
ASCII = "e";
#(`clock_period);
ASCII = "H";
#(`clock_period);
ASCII = "E";
#(`clock_period);
ASCII = "M";
#(`clock_period);
ASCII = "l";
#(`clock_period);
ASCII = "H";
#(`clock_period);
ASCII = "E";
#(`clock_period);
ASCII = "L";
#(`clock_period);
ASCII = "L";
#(`clock_period);
ASCII = "O";
#(`clock_period);
ASCII = "H";
#(`clock_period);
ASCII = "e";
#(`clock_period);
ASCII = "l";
#(`clock_period);
ASCII = "l";
#(`clock_period);
ASCII = "o";
#(`clock_period);
ASCII = "l";
end
end
endmodule
本次主要学习和了解的是关于状态机器的学习,
主要是如何从当前状态转移到下一个状态,并且知道如何进行判断进入下一状态,状态机错误后该如何去解决这一情况。
另外一个,就是再测试脚本文件中的 forever 语句的功能,我们通过设置仿真文件的的时间让其运行的时间停止,否则就会不断进行循环。