什么是状态机?
根据夏宇闻老师的原文:"状态机是CPU的控制核心,用于产生一系列的控制信号,启动或停止某些部件.CPU何时进行读指令来读写I/O端口及RAM区等操作,都是由状态机来控制的.状态机的当前状态,由变量state(状态)记录,state的值就是当前这个指令周期中已经过的时钟数(从零记起). "
说的简单些,状态机就是以时钟为触发信号,内含状态判断的机制,例如,我们的日常工作学习流程就可以是一个[状态机]:
早晨7:30~8:30是起床及工作前的准备.这就是一个状态我们可以用一个标志位来代替,而起床这个状态可以由起床闹钟唤醒,这个闹钟即为此状态的判断(闹钟响[起床],闹钟没响[不起床]).
8:30~21:30是工作学习时间,这是另一个状态,此状态由今天是否为工作日决定(为工作日[工作],为休息日[不工作].
21:30~23:00是睡前准备时间,此为又一状态,此状态以是否加班作为判断(不加班[睡前准备],加班[继续工作]).
23:00~7:30即为睡眠时间,此状态可由是否困倦为判断(困了[睡觉],没有困[继续玩耍]).
以上即为一个含判断,以时钟为触发的状态机,此时我们人作为"CPU".
下面写一个简单的fpga状态机:以时钟为触发,以输入字符串"Hello"作为条件,当满足条件时,控制led灯反转.
代码如下:
module hello(Clk, Rst_n, data, led);
input Clk; //50M
input Rst_n; //低电平有效
input [7:0]data; //8位位宽数据
output reg led;
// 多个状态定义
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; //状态寄存器
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
led <= 1'b1; //led为1时熄灭
state <= CHECK_H;
end
else begin
case(state)
CHECK_H:
if(data == "H")
state <= CHECK_e;
else
state <= 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:
// if(data == "o")begin
// state <= CHECK_H;
// led = ~led;
// end
// else
// state <= CHECK_H;
//改:
begin
state <= CHECK_H;
if(data == "o")
led = ~led;
end
default:
state <= CHECK_H;
endcase
end
endmodule
其中,CHECK_H等为逻辑状态(其实就是一个标志位),变量state为状态记录(作为状态值判断),当状态连续命中而最终确认得到字符串"Hello"时,led灯翻转,否则保持原样.RTL Viewer如下:
testbench如下:
`timescale 1ns/1ns
`define clock_period 20
module hello_tb();
reg Clk;
reg Rst_n;
reg[7:0] data;
wire led;
hello hello0(
.Clk(Clk),
.Rst_n(Rst_n),
.data(data),
.led(led)
);
//产生系统时钟
initial Clk = 1;
always #(`clock_period/2) Clk = ~Clk;
initial begin
Rst_n = 0;
data = 0;
#(`clock_period*20) Rst_n = 1;
#(`clock_period*50)
forever begin
data = "i";
#(`clock_period)
data = "b";
#(`clock_period)
data = "H";
#(`clock_period)
data = "e";
#(`clock_period)
data = "i";
#(`clock_period)
data = "d";
#(`clock_period)
data = "i";
#(`clock_period)
data = "d";
#(`clock_period)
data = "H";
#(`clock_period)
data = "E";
#(`clock_period)
data = "i";
#(`clock_period)
//one Hello
data = "H";
#(`clock_period)
data = "e";
#(`clock_period)
data = "l";
#(`clock_period)
data = "l";
#(`clock_period)
data = "o";
#(`clock_period)
//two Hello
data = "H";
#(`clock_period)
data = "e";
#(`clock_period)
data = "l";
#(`clock_period)
data = "l";
#(`clock_period)
data = "o";
#(`clock_period)
data = "d";
#(`clock_period)
data = "H";
#(`clock_period)
data = "E";
#(`clock_period)
data = "i";
end
end
endmodule
因为随机生成"Hello"的机会渺茫,所以人为定义了两个,让这组data输入一直产生,在仿真设置里设置10ns结束,得到波形如下:
放大一个输出段:
可以看到一个led翻转发生在第一个"Hello"完成时,第二个翻转为紧跟的第二个"Hello"输入完成时,实现了利用"Hello"输入控制led灯翻转的状态机设计 .
为了便于观察,可在data信号产生前的延迟+1,仿真效果如下:
可以看到led在"hello"中"o"的下一个 时钟沿才发生翻转,即捕获到"o"信号之后发生翻转.
以上即为状态机的内容,作为初识,我认为状态机可以理解为一系列的case,当条件满足时,CPU执行特定的任务:)