目录
前言
本文将通过三个部分,状态机概念,状态机模型,以及状态机设计来向大家分享一下学习思路,同时以一个七分频器作为例子来简单实战
一、状态机概念
Verilog是并行执行的,
对于一些需要顺序执行的语句则需要借助状态机
示例:
8023密码锁
状态转换图
状态机(State Machine)
有限状态机(Finite State Machine):在有限个状态之间按照一定的规律转换的时序电路
二、状态机模型
状态机分为两类
Mealy型和Moore型,唯一的区别在于,Moore型输出仅有当前状态决定,不由输入决定
状态寄存器由一组触发器组成,用来记忆状态机当前所处的状态的改变,状态的改变只发生在时钟的跳变沿
Mealy型状态机
Moore型状态机
三、状态机设计
状态机设计可看成四段论,通过这样方法设计出来的状态机叫做三段式状态机
状态空间定义,状态跳转 ,下个状态判断,各个状态下的动作
1.状态空间定义
二进制编码
//define state space
parameter SLEEP = 2'b00;
parameter STUDY = 2'b01;
parameter EAT = 2'b10;
parameter AMUSE = 2'b11;
//initial variable
reg [1:0] current_state;
reg [1:0] next_state;
独热码:每个状态机只有一个寄存器置位,译码逻辑简单.推荐!
//define state space
parameter SLEEP = 4'b1000;
parameter STUDY = 4'b0100;
parameter EAT = 4'b0010;
parameter AMUSE = 4'b0001;
//initial variable
reg [3:0] current_state;
reg [3:0] next_state;
2.状态跳转(时序逻辑)
//transition
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) //_n表示低电平有效
current_state <= SLEEP;
else
current_state <= next_state;
end
3.下个状态判断(输入组合逻辑)
在组合逻辑中,always敏感信号表里需要包含所有case语句右边表达式中的变量以及if,else语句条件中的变量
也可以直接用*代替
always @(current_state or input_signals)
begin
case(current_state)
SLEEP:
begin
if(clock_alarm) next_state = STUDY;
else next_state = SLEEP;//组合逻辑,用阻塞赋值
end
STUDY:
begin
if (lunch_time) next_state = EAT;
else next_state = STUDY; //if_else要配对以避免latch的产生
end
EAT: ...; //笔者省略了部分代码,如需要,可在评论区问我
AMUSE: ...;
default: ...;
endcase
end
latch是一个边沿触发的锁存器,如果出现
1.if_else没配对
2.case语句没表达全
这两个问题,会导致电路出现锁存器,导致产生很多毛刺,影响电路性能
锁存器只在组合逻辑下产生
4.各个状态下的动作(输出组合逻辑)
//action
wire read_book;
assign read_book = (current_state == STUDY) ? 1'b1 : 1'b0;
//另一种表达方法
always @ (current_state)
begin
if(current_state == STUDY) read_book = 1;
else read_book = 0;//阻塞赋值
end
需要注意的是,有时候不一定是组合逻辑,后面加了一个输出寄存器之后就变成时序逻辑输出了;
好处是:
1.可以有效的滤去组合逻辑输出产生的毛刺;
2.可以有效的进行时序计算和约束;
3.另外对于总线形式的输出信号来说,容易使总线数据对齐,从而减少总线数据间的偏移,减少接受端数据采样出错的频率
实例:利用状态机来生成一段七分频器
module divider_7_fsm(
input sys_clk;
input sys_rst_n;
output reg clk_divide_7
);
//reg difine
reg [6:0] curr_st;
reg [6:0] next_st;
//wire difine
//parameter difine;
parameter WIDTH = 1;
//热独码
parameter s0 = 7'b0000_000;
parameter s1 = 7'b0000_001;
parameter s2 = 7'b0000_010;
parameter s3 = 7'b0000_100;
parameter s4 = 7'b0001_000;
parameter s5 = 7'b0010_000;
parameter s6 = 7'b0100_000;
always @(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n) curr_st <= 7'b0;
else next_st = curr_st;
end
//FSM state logic
always @(*) //*简化一下
begin
case(curr_st)
s0:
begin next_st = s1; end //阻塞赋值
s1 :
begin next_st = s2; end
s2 :
begin next_st = s3; end
s3 :
begin next_st = s4; end
s4 :
begin next_st = s5; end
s5 :
begin next_st = s6; end
s6 :
begin next_st = s0; end
endcase
end
//contron divide clock offset
always @(posedge sys_clk or sys_rst_n) //这里采用的是时序逻辑的写法
begin
if(sys_rst_n == 1'b0) clk_divide_7 <= 1'b0;
else if ((curr_st == s0) | (curr_st == s1) | (curr_st == s2) | (curr_st == s3)) clk_divide_7 <= 1'b0;
else if ((curr_st == s4) | (curr_st == s5) | (curr_st == s6)) clk_divide_7 <= 1'b1;
else;
end
endmodule