录
一、何为状态机
- 状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作、完成特定操作的控制中心。有限状态机简写为FSM(Finite State Machine),主要分为2大类:
第一类,若输出只和状态有关而与输入无关,则称为摩尔(Moore)型状态机。
第二类,输出不仅和状态有关而且和输入有关系,则称为米莉(Mealy)型状态机。
二、状态机状态检测
2.1 问题描述
- 根据以下描述功能用verilog编写一段代码,并用状态机来实现该功能。
(1)状态机:实现一个测试过程,该过程包括启动准备状态、启动测试、停止测试、查询测试结果、显示测试结果、测试结束返回初始化6个状态;用时间来控制该过程,90秒内完成该过程;
(2)描述状态跳转时间;
(3)编码实现。
2.2 工程创建
① 首先在相应的文件夹下建立一个名为state_show(可自行命名)的文件,里面分别创建prj(存放工程的文件)、src(存放Verilog代码源文件)、sim(存放仿真文件)、tcl(引脚配置文件),除此之外还可以建立ip(存放知识产权的文件)等…
②打开Quartus ||(我的版本是18.1),新建工程
file——New——New Quartus Prime Preject——OK
然后next,到如下图页面:
③两个next后,根据自己的开发版型号进行选择,笔主这里是:
④下一页面如下
- 然后finish,创建工程完成。
2.3 代码
- 新建 verilog 文件,
File->New->Verilog HDL File
- 注意保存时,要保存在src中,命名要与Verilog 语句中一致
2.3.1 计时器模块
module time_count(
input clk, //50M时钟
input rst_n, //复位信号
output reg sec_15//15s
);
parameter MAX_NUM = 30'd749_999_999;//最大数15s,750_000_000-1次
reg [29:0] cnt_15;//计数寄存器
//15s计时器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_15 <= 30'd0;
end
else if(cnt_15 == MAX_NUM)begin
cnt_15 <= 30'd0;
end
else begin
cnt_15 <= cnt_15 + 1'd1;
end
end
//15s脉冲信号
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sec_15 <= 1'b0;
end
else if(cnt_15 == MAX_NUM)begin
sec_15 <= 1'b1;
end
else begin
sec_15 <= 1'b0;
end
end
endmodule
2.3.2 状态切换模块
module state_change(
input clk ,
input rst_n ,
input wire sec_15
);
reg [2:0] cstate; //现态
reg [2:0] nstate; //次态
//状态划分
parameter S0 = 0;//初始化
parameter S1 = 1;//启动准备状态
parameter S2 = 2;//启动测试
parameter S3 = 3;//停止测试
parameter S4 = 4;//查询测试结果
parameter S5 = 5;//显示测试结果
//第一段:现态跟随次态
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cstate <= S0;//复位键被按下,当前状态设置为初始化
else
cstate <= nstate; //下一次状态赋值给当前状态
end
//第二段:组合逻辑,阻塞赋值
always@(*)begin
if(!rst_n)
begin
nstate = S0;
end
else
case(cstate)
S0: begin
if(sec_15 == 1'b1)
nstate = S1;
else
nstate = nstate;
end
S1: begin
if(sec_15 == 1'b1)
nstate = S2;
else
nstate = nstate;
end
S2: begin
if(sec_15 == 1'b1)
nstate = S3;
else
nstate = nstate;
end
S3: begin
if(sec_15 == 1'b1)
nstate = S4;
else
nstate = nstate;
end
S4: begin
if(sec_15 == 1'b1)
nstate = S5;
else
nstate = nstate;
end
S5: begin
if(sec_15 == 1'b1)
nstate = S0;
else
nstate = nstate;
end
default: nstate = S0;
endcase
end
endmodule
2.3.3 顶层文件模块
module state_top(
input clk,
input rst_n
);
wire sec_15;
//实例化计时器模块
time_count inst_time_count(
.clk (clk ),
.rst_n (rst_n ),
.sec_15 (sec_15)
);
//实例化状态切换模块
state_change inst_state_change(
.clk (clk ),
.rst_n (rst_n ),
.sec_15 (sec_15)
);
endmodule
2.4 原理图
三、检测10010串的状态机
3.1 问题描述
- 画出可以检测10010串的状态图, 并用verilog编程实现之。
3.2 原理图示
3.3 创建工程
- 同上2.2
3.4 代码
3.4.1 按键消抖模块
module key_debounce(
input clk,
input rst_n,
input key,
output key_value, //按键稳定信号
output flag //抖动结束标识(0表示未结束,1表示结束)
);
parameter TIME_20MS = 20'd1_000_000; //20ms
reg [19:0] cnt_20ms;
reg key_value_r;
reg flag_r;
reg key_reg; //保存前一次取的key值
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_20ms <= 20'd0;
key_reg <= 1'b1;
end
else begin
key_reg <= key;
if(key_reg == 1'b1 && key == 1'b0)
cnt_20ms <= TIME_20MS;
else if(cnt_20ms <= 20'd0)
cnt_20ms <= 20'd0;
else
cnt_20ms <= cnt_20ms -20'd1;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
flag_r <= 1'b0;
else if(cnt_20ms == 20'd1)
flag_r <= 1'b1;
else
flag_r <= 1'b0;
end
assign flag = flag_r;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
key_value_r <= 1'b1;
else if(cnt_20ms == 20'd1)
key_value_r <= ~key;
else
key_value_r <= key_value_r;
end
assign key_value = key_value_r;
endmodule
3.4.2 状态机模块
module state(
input clk,
input rst_n,
input [1:0] key,
output wire [3:0] led
);
parameter MAX_TIME = 26'd49_999_999;//1s
parameter TIME_200MS = 24'd9_999_999; //200ms
reg [2:0] cstate; //现态
reg [2:0] nstate; //次态
parameter S0 = 0;//初始状态
parameter S1 = 1;//1状态
parameter S2 = 2;//10状态
parameter S3 = 3;//100状态
parameter S4 = 4;//1001状态
parameter S5 = 5;//10010状态
reg [3:0] led_r;
reg [25:0] cnt_1s;
reg [23:0] cnt_200ms;
//1s计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_1s <= 26'd0;
else if(cnt_1s == MAX_TIME)
cnt_1s <= 26'd0;
else
cnt_1s <= cnt_1s + 26'd1;
end
//200ms计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_200ms <= 24'd0;
else if(cnt_200ms == TIME_200MS)
cnt_200ms <= 24'd0;
else
cnt_200ms <= cnt_200ms + 24'd1;
end
//第一段
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cstate <= S0;
else
cstate <= nstate;
end
//状态切换模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
begin
nstate <= S0;
end
else
case(cstate)
S0: begin
if(key[1] )
nstate <= S1;
else if(key[0])
nstate <= S0;
else
nstate <= nstate;
end
S1: begin
if(key[0] )
nstate <= S2;
else if(key[1])
nstate <= S0;
else
nstate <= nstate;
end
S2: begin
if(key[0] )
nstate <= S3;
else if(key[1])
nstate <= S0;
else
nstate <= nstate;
end
S3: begin
if(key[1] )
nstate <= S4;
else if(key[0] )
nstate <= S0;
else
nstate <= nstate;
end
S4: begin
if(key[0] )
nstate <= S5;
else if(key[1] )
nstate <= S0;
else
nstate <= nstate;
end
S5: begin
if(cnt_1s == MAX_TIME)
nstate <= S0;
else
nstate <= S5;
end
default: nstate <= S0;
endcase
end
//第三段:跟随状态输出
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
led_r <=4'b0000;
else
case(cstate)
S5:begin
if(cnt_200ms == TIME_200MS)
led_r <= ~led_r;
else
led_r <= led_r;
end
default: led_r <= 4'b0000;
endcase
end
assign led = led_r;
endmodule
3、顶层模块
module state_top(
input wire clk,
input wire rst_n,
input wire [1:0] key,
output wire [3:0] led
);
wire [1:0] flag;
wire [1:0] key_value;
//实例化按键KEY0
key_debounce inst_key_debounce(
.clk (clk ),
.rst_n (rst_n ),
.key (key[0] ),
.flag (flag[0] ),
.key_value(key_value[0])
);
//实例化按键KEY1
key_debounce inst_key_debounce1(
.clk (clk ),
.rst_n (rst_n ),
.key (key[1] ),
.flag (flag[1] ),
.key_value(key_value[1])
);
//实例化状态切换模块
state inst_state(
.clk (clk ),
.rst_n(rst_n ),
.key ({key_value[1]&&flag[1],key_value[0]&&flag[0]}),
.led (led)
);
endmodule
3.5 原理图
总结
- 状态机分为一段式、两段式、三段式,一般建议用三段式最好。三段式第一段主要就是现态跟随次态,第二段是不同的状态及切换条件等,第三段就是不同状态的输出。
- 状态机部分不算很难,懂了就简单,只是有时候写得可能会有些混,写之前可以画个流程图梳理一下。