状态机(State Machine)
有限状态机(Finite State Machine,简称FSM)。
在有限个状态之间按一定规律转换的时序电路。
状态机例子:密码锁的设计(状态转移图)
状态机模型
Mealy 状态机(输出与输入有关)
Moore状态机(输出与输入无关)
状态寄存器由一组触发器组成,用来记忆状态机当前所处的状态,状态的改变只发生在时钟的跳变沿。
状态是否改变、如何改变,取决于组合逻辑F的输出,F是当前状态和输入信号的函数。
状态机的输出是由输出组合逻辑G提供的,G也是当前状态的输入信号的函数。
状态机设计
1、状态空间定义
2、状态跳转
3、下个状态判断
4、各个状态下的动作
1、状态空间定义
方法一:
//define state space
parameter SLEEP = 2'b00;
parameter STUDY = 2'b01;
parameter EAT = 2'b10;
parameter AMUSE = 2'b11;
//internal 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;
//internal variable
reg [3:0] current_state;
reg [3:0] next_state;
2、状态跳转(时序逻辑)
//transition
always @(posedge clk or negedge rst_n) begin//敏感列表:时钟信号以及复位信号边沿的组合
if(!rst_n)
current_state <= SLEEP; //使用非阻塞赋值
else
current_state <= next_state;
end
3、下个状态判断(组合逻辑)
//next state decision
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;
end
EAT: ...;
AMUSE::...;
default:...;
endcase
end
If/else要配对以避免latch的产生
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 divider7_fsm(
//input ports
input sys_clk,
input sys_rst_n,
//output ports
output reg clk_divide_7
);
//reg define
reg [6:0] curr_st;
reg [6:0] next_st;
//parameter define
parameter WIDTH = 1;
//one hot code design,各个状态的定义
parameter S0 = 7'b0000000;
parameter S1 = 7'b0000001;
parameter S2 = 7'b0000010;
parameter S3 = 7'b0000100;
parameter S4 = 7'b0001000;
parameter S5 = 7'b0010000;
parameter S6 = 7'b0100000;
always @(posedge sys_clk or megedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
curr_st <= 7'b0;
else
curr_st <= next_st;
end
//FSM state logic
always @(*) degin
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
default:next_st = S0;
endcase
end
//control divide clock offset
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
clk_divide_7 <= 1'b0;
end
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