一、状态机分类
1、Moore型状态机:
状态机的变化只与当前状态有关
2、Mearly型状态机:
状态机的变化不仅与当前状态有关,还与输入有关
二、状态机写法分类
1、一段式
把状态跳转逻辑,状态机状态和状态机结果都放到一个always块里。
在信号少的情况下容易理解,信号多容易造成出错,且复杂。
2、二段式
把状态跳转逻辑用always组合逻辑编写,状态机状态用always时许逻辑编写,状态机结果用assign组合逻辑输出。
结果输出用组合逻辑容易出现毛刺现象。
3、三段式
把状态跳转逻辑用always组合逻辑编写,状态机状态用always时序逻辑编写,状态机结果用always组合逻辑输出。
描述方式使得FSM做到了同步寄存器输出,消除了组合逻辑输出不稳定与毛刺隐患,更有利于时序稳定
三、状态编码方式
1、二进制编码:
parameter IDLE = 3'b001;
parameter FILTER_DOWN = 3'b010;
parameter HOLD = 3'b011;
parameter FILTER_UP = 3'b100;
2、格雷码:
parameter IDLE = 3'b001;
parameter FILTER_DOWN = 3'b011;
parameter HOLD = 3'b010;
parameter FILTER_UP = 3'b110;
3、独热码:(可提高电路速度和可靠性)
parameter IDLE = 4'b0001;
parameter FILTER_DOWN = 4'b0010;
parameter HOLD = 4'b0100;
parameter FILTER_UP = 4'b1000;
四、三段式状态机设计方法
1、状态空间定义
2、状态跳转(时序逻辑)
3、下一个状态判断(组合逻辑)
4、各个状态下的判断(组合逻辑)
五、三段式状态机编写
1、题目分析
本次设计使用三段式状态机进行编写,实现按键消抖同时控制led亮灭,消抖原理如下:
前沿抖动以及后沿抖动持续时间约为5-10ms,稳定状态持续时间位20-40ms
2、设计分析
当前沿抖动时,定时器开始计数,待20ms之后读取按键状态,进行状态跳转。当后沿抖动时,定时器开始计数,待20ms之后读取按键状态,进行状态跳转。
3、状态图
4、三段式状态机编写
(1)确认输入输出
输入:系统时钟,复位信号,按键信号
输出:led标志信号
`timescale 1ns / 1ps
//
module key_shake(
input sys_clk,
input sys_rst_n,
input key,
output reg led
);
(2)状态编码
本次采用独热码
parameter IDLE = 4'b0001; //led = 1,key_flag = 1
parameter FILTER_DOWN = 4'b0010; //key_flag = 0
parameter HOLD = 4'b0100; //led = 0,key_flag = 1
parameter FILTER_UP = 4'b1000; //key_flag = 0
(3)其他关键信号以及标准定义
parameter cntmax = 15'b100_1110_0001_1111; //20ms
reg [3:0] current_state;
reg [3:0] next_state;
reg [14:0] cnt;
reg key_flag;
reg cnt_flag;
(4)状态机编写
第一段:
状态切换,使用时序逻辑实现
///
//状态转换
always @ (posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
current_state <= IDLE;
end
else begin
current_state <= next_state;
end
end
第二段:
根据当前状态确定下一状态,使用组合逻辑实现
/
//转移条件
always @ ( * )begin
case(current_state)
IDLE:begin
if( !key && key_flag)begin //kry_flag = 1
next_state = FILTER_DOWN;
end
else begin
next_state = IDLE;
end
end
FILTER_DOWN:begin
if( cnt_flag )begin //key_flag = 0 cnt_flag = 1
if(!key )begin//
next_state = HOLD;
end//
else begin//
next_state = IDLE;//
end//
end
else begin
next_state = FILTER_DOWN;
end
end
HOLD:begin
if(!key && key_flag)begin //key_flag = 1
next_state = FILTER_UP;
end
else begin
next_state = HOLD;
end
end
FILTER_UP:begin
if(cnt_flag )begin //key_flag =
if(!key )begin //
next_state = IDLE;
end//
else begin//
next_state = HOLD;//
end//
end
else begin
next_state = FILTER_UP;
end
end
default:begin
next_state = IDLE;
end
endcase
end
第三段:
确认输出状态,使用组合逻辑实现
//状态输出
always @ ( posedge sys_clk )begin
case(current_state)
IDLE:begin
led = 1'b1;
// cnt = 15'b0;
if(key)begin
key_flag = 1'b1;
end
end
FILTER_DOWN:begin
if( cnt == cntmax )begin
cnt_flag = 1'b1;
cnt = 15'b0;
end
else begin
cnt_flag = 1'b0;
cnt = cnt + 15'b1;
end
if(!key)begin
key_flag = 1'b0;
end
end
HOLD:begin
led = 1'b0;
// cnt = 15'b0;
if(key)begin
key_flag = 1'b1;
end
end
FILTER_UP:begin
if( cnt == cntmax )begin
cnt_flag = 1'b1;
cnt = 15'b0;
end
else begin
cnt_flag = 1'b0;
cnt = cnt + 15'b1;
end
if(!key)begin
key_flag = 1'b0;
end
end
default:begin
led = 1'b1;
cnt = 15'b0;
if(key)begin
key_flag = 1'b0;
end
end
endcase
end
endmodule
六、ila文件编写
///
ila_0 your_instance_name (
.clk(sys_clk), // input wire clk
.probe0(sys_rst_n), // input wire [0:0] probe0
.probe1(key), // input wire [0:0] probe1
.probe2(led), // input wire [0:0] probe2
.probe3(key_flag), // input wire [0:0] probe3
.probe4(cnt_flag), // input wire [0:0] probe4
.probe5(current_state), // input wire [3:0] probe5
.probe6(next_state), // input wire [3:0] probe6
.probe7(cnt) // input wire [14:0] probe7
);
七、综合代码(可直接复制)
`timescale 1ns / 1ps
//
module key_shake(
input sys_clk,
input sys_rst_n,
input key,
output reg led
);
parameter IDLE = 4'b0001; //led = 1,key_flag = 1
parameter FILTER_DOWN = 4'b0010; //key_flag = 0
parameter HOLD = 4'b0100; //led = 0,key_flag = 1
parameter FILTER_UP = 4'b1000; //key_flag = 0
parameter cntmax = 15'b100_1110_0001_1111; //20ms
reg [3:0] current_state;
reg [3:0] next_state;
reg [14:0] cnt;
reg key_flag;
reg cnt_flag;
///
ila_0 your_instance_name (
.clk(sys_clk), // input wire clk
.probe0(sys_rst_n), // input wire [0:0] probe0
.probe1(key), // input wire [0:0] probe1
.probe2(led), // input wire [0:0] probe2
.probe3(key_flag), // input wire [0:0] probe3
.probe4(cnt_flag), // input wire [0:0] probe4
.probe5(current_state), // input wire [3:0] probe5
.probe6(next_state), // input wire [3:0] probe6
.probe7(cnt) // input wire [14:0] probe7
);
///
//状态转换
always @ (posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
current_state <= IDLE;
end
else begin
current_state <= next_state;
end
end
/
//转移条件
always @ ( * )begin
case(current_state)
IDLE:begin
if( !key && key_flag)begin //kry_flag = 1
next_state = FILTER_DOWN;
end
else begin
next_state = IDLE;
end
end
FILTER_DOWN:begin
if( cnt_flag )begin //key_flag = 0 cnt_flag = 1
if(!key )begin//
next_state = HOLD;
end//
else begin//
next_state = IDLE;//
end//
end
else begin
next_state = FILTER_DOWN;
end
end
HOLD:begin
if(!key && key_flag)begin //key_flag = 1
next_state = FILTER_UP;
end
else begin
next_state = HOLD;
end
end
FILTER_UP:begin
if(cnt_flag )begin //key_flag =
if(!key )begin //
next_state = IDLE;
end//
else begin//
next_state = HOLD;//
end//
end
else begin
next_state = FILTER_UP;
end
end
default:begin
next_state = IDLE;
end
endcase
end
//状态输出
always @ ( posedge sys_clk )begin
case(current_state)
IDLE:begin
led = 1'b1;
// cnt = 15'b0;
if(key)begin
key_flag = 1'b1;
end
end
FILTER_DOWN:begin
if( cnt == cntmax )begin
cnt_flag = 1'b1;
cnt = 15'b0;
end
else begin
cnt_flag = 1'b0;
cnt = cnt + 15'b1;
end
if(!key)begin
key_flag = 1'b0;
end
end
HOLD:begin
led = 1'b0;
// cnt = 15'b0;
if(key)begin
key_flag = 1'b1;
end
end
FILTER_UP:begin
if( cnt == cntmax )begin
cnt_flag = 1'b1;
cnt = 15'b0;
end
else begin
cnt_flag = 1'b0;
cnt = cnt + 15'b1;
end
if(!key)begin
key_flag = 1'b0;
end
end
default:begin
led = 1'b1;
cnt = 15'b0;
if(key)begin
key_flag = 1'b0;
end
end
endcase
end
endmodule
八、实验结果
1、ila截图
(1)按键下降沿
(2)按键上升沿
九、总结
1、了解消抖原理
2、了解状态机编写方法
3、学习描绘状态转换图
4、学习状态机编写逻辑