目录
一、状态机概述
1、什么是状态机
状态机是有限状态机(FSM)的简称,是现实事物运行规则抽象而成的一个数学模型。状态机的本质就是对具有逻辑顺序或时序规律事件的一种描述方法,所有具有逻辑顺序和时序规律的事情都适合用状态机描述。
FSM顾名思义,状态数目是有限的,状态的数目可以用表示状态的位数确定,采用n位二进制编码的状态机的状态数目最多是种。
同步FSM在数字系统种应用广泛,其特点是具有有限个状态,且状态的转移由时钟驱动。
2、FSM分类
FSM有两种基本类型:Mealy状态机和Moore状态机。他们的区别在于:Mealy状态机的下一级状态和输出取决于当前状态和当前输入;Moore状态机的下一状态取决于当前状态和当前输入,但是输出只取决于当前状态。这两类FSM的下一状态和输出都是由组合逻辑电路形成的。
3、通用状态机建模规范
分为一段式、两段式与三段式状态机,可以根据状态机always块数目对状态机进行初步区分,具体区分还是要判断状态机是否按照具体的建模规则描述。
一段式:将状态的同步转移、状态输出与状态的输入条件都写在同一个always模块里面。
不建议使用,原因在于:(1)这种描述方法不符合将时序逻辑与组合逻辑分开描述的Coding Style;(2)所有功能都在同一Always块中,在描述状态转移的同时需要考虑状态输出,代码比较冗余,可读性较差。(3)不利于维护修改,不利于综合器和布局布线器对设计进行优化。
两段式:使用两个always 模块,其中一个always 模块采用同步时序的方式描述状态转移,而另一个模块采用组合逻辑的方式判断状态转移条件,描述状态转移规律。
两段式状态机符合将时序逻辑与组合逻辑分开描述的Coding Style,利于工具进行综合与布局布线,但也有缺点:其输出一般采用组合逻辑描述,而组合逻辑输出易产生毛刺等不稳定因素。解决方式是可以在两段式输出之后插入一个额外的寄存器,进行寄存器打拍操作来有效的消除毛刺带来的问题。这种解决方式会使得输出延迟一个时钟,具体问题具体分析。
三段式:使用3个always模块,一个always 模块采用同步时序的方式描述状态转移,一个采用组合逻辑的方式判断状态转移条件,描述状态转移规律,第三个always 模块使用同步时序电路描述每个状态的输出。
第三个进程采用下一状态决定电路输出,是为了保证三段式状态机输出时延与两段式相等。在没有时延要求的情况下,建议使用当前状态决定电路输出,提高状态机的稳定性。
其实选择使用现态还是次态作为第三段的输出判断,本质上是决定使用Moore型和Mealy型的问题,当选择Moore型时,输出仅和状态有关,所以采用次态来决定输出,可以使得输出严格与状态在时序上具有理论的一致性,选择Mealy型时,本身就是基于现态和输入决定输出,所以使用现态决定输出,会更加符合思维上的认知。但是在真实的HDL设计中没有这么明确的要求,设计者需要根据时延的需求完成自己的设计,但是要注意考虑到避免用一个输入同时去作为次态和输出的决定条件。推荐还是使用现态+输入的方式作为第三段输出的判决条件。
二、状态机代码实例
1、可乐机
每次只能投入 1 枚 1 元硬币,且每瓶可乐卖 3 元钱,即投入 3 个硬币就可以让可乐机出可乐,如果投币不够 3 元想放弃投币需要按复位键,否则之前投入的钱不能退回。
输入信号:
clk:系统时钟
rst_n:低电平有效的复位信号
money:投币结果,高电平为投币1元,低电平为未投币
输出信号:
cola:低电平为无可乐输出,高电平为有可乐输出
下面给出状态转移图,由于FSM可以分为Moore和Mealy型,所以可以给出的状态转移图也有2种,这两种在绘制的时候会稍有不同,一般数电的课程会明确其规范性,但是还是经常会出现混淆的情况:
1.1一段式
(1)一段式Moore型
//一段式FSM示例
//采用的是Moore型的FSM
//--------------------<模块及端口定义>---------------------
module cola_FSM(
input clk, //时钟输入
input rst_n, //复位信号
input money, //是否投币输入
output reg cola); //是否有可乐输出
//--------------------<状态机参数定义>---------------------
//独热码编码的优点是每次状态跳转只有两个bit位发生跳变,动态功耗较低。同时独热码编码译码电路比较简单。缺点是用的寄存器位宽比二进制编码会大一点。
parameter IDLE = 4'b0001;
parameter ONE = 4'b0010;
parameter TWO = 4'b0100;
parameter THREE = 4'b1000;
//--------------------<一段式状态定义>---------------------
reg [3:0] state; // 状态定义
//---------------------<一段式状态机>---------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n) begin
state <= IDLE;
cola <= 1'b0;
end
else case (state)
IDLE: begin
cola <= 1'b0;
state <= money ? ONE : IDLE;
end
ONE : begin
cola <= 1'b0;
state <= money ? TWO : ONE ;
end
TWO : begin
cola <= 1'b0;
state <= money ? THREE : TWO ;
end
THREE:begin
cola <= 1'b0;
state <= money ? ONE : IDLE;
end
default:begin
cola <= 1'b0;
state <= money ? ONE : IDLE;
end
endcase
end
endmodule
(2)一段式Mealy型
//一段式FSM示例
//采用的是Mealy型的FSM
//--------------------<模块及端口定义>---------------------
module cola_FSM(
input clk, //时钟输入
input rst_n, //复位信号
input money, //是否投币输入
output reg cola); //是否有可乐输出
//--------------------<状态机参数定义>---------------------
//独热码编码的优点是每次状态跳转只有两个bit位发生跳变,动态功耗较低。同时独热码编码译码电路比较简单。缺点是用的寄存器位宽比二进制编码会大一点。
parameter IDLE = 3'b001;
parameter ONE = 3'b010;
parameter TWO = 3'b100;
//--------------------<一段式状态定义>---------------------
reg [2:0] state; // 状态定义
//---------------------<一段式状态机>---------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n) begin
state <= IDLE;
cola <= 1'b0;
end
else case (state)
IDLE: begin
cola <= 1'b0;
state <= money ? ONE : IDLE;
end
ONE : begin
cola <= 1'b0;
state <= money ? TWO : ONE ;
end
TWO : begin
cola <= money ? 1'b1 : 1'b0;
state <= money ? IDLE : TWO ;
end
default:begin
cola <= 1'b0;
state <= money ? ONE : IDLE;
end
endcase
end
endmodule
1.2二段式
(1)二段式Moore型
//二段式FSM示例
//采用的是Moore型的FSM
//--------------------<模块及端口定义>---------------------
module cola_FSM(
input clk, //时钟输入
input rst_n, //复位信号
input money, //是否投币输入
output reg cola); //是否有可乐输出
//--------------------<状态机参数定义>---------------------
//独热码编码的优点是每次状态跳转只有两个bit位发生跳变,动态功耗较低。同时独热码编码译码电路比较简单。缺点是用的寄存器位宽比二进制编码会大一点。
parameter IDLE = 4'b0001;
parameter ONE = 4'b0010;
parameter TWO = 4'b0100;
parameter THREE = 4'b1000;
//--------------------<二段式状态定义>---------------------
reg [3:0] state; // 当前状态定义
reg [3:0] next_state; // 次态定义
//---------------------<二段式状态机>---------------------
//同步时序描述状态转移
always@(posedge clk or negedge rst_n)begin
if(!rst_n) state <= IDLE;
else state <= next_state;
end
//组合逻辑描述状态转移条件,并给出输出
always@(*)begin
case (state)
IDLE: begin
cola = 1'b0;
next_state = money ? ONE : IDLE;
end
ONE : begin
cola = 1'b0;
next_state = money ? TWO : ONE ;
end
TWO : begin
cola = 1'b0;
next_state = money ? THREE : TWO ;
end
THREE:begin
cola = 1'b1;
next_state = money ? ONE : IDLE;
end
default:begin
cola = 1'b0;
next_state = money ? ONE : IDLE;
end
endcase
end
endmodule
(2)二段式Mealy型
//二段式FSM示例
//采用的是Mealy型的FSM
//--------------------<模块及端口定义>---------------------
module cola_FSM(
input clk, //时钟输入
input rst_n, //复位信号
input money, //是否投币输入
output reg cola); //是否有可乐输出
//--------------------<状态机参数定义>---------------------
//独热码编码的优点是每次状态跳转只有两个bit位发生跳变,动态功耗较低。同时独热码编码译码电路比较简单。缺点是用的寄存器位宽比二进制编码会大一点。
parameter IDLE = 3'b001;
parameter ONE = 3'b010;
parameter TWO = 3'b100;
//--------------------<二段式状态定义>---------------------
reg [2:0] state; // 当前状态定义
reg [2:0] next_state; // 下一状态定义
//---------------------<二段式状态机>---------------------
//同步时序描述状态转移
always@(posedge clk or negedge rst_n)begin
if(!rst_n) state <= IDLE;
else state <= next_state;
end
//组合逻辑描述状态转移条件,并给出输出
always@(*)begin
case (state)
IDLE: begin
cola = 1'b0;
next_state = money ? ONE : IDLE;
end
ONE : begin
cola = 1'b0;
next_state = money ? TWO : ONE ;
end
TWO : begin
cola = money ? 1'b1 : 1'b0;
next_state = money ? IDLE : TWO ;
end
default:begin
cola = 1'b0;
next_state = money ? ONE : IDLE;
end
endcase
end
endmodule
1.3三段式
(1)三段式Moore型
//三段式FSM示例
//采用的是Moore型的FSM
//--------------------<模块及端口定义>---------------------
module cola_FSM(
input clk, //时钟输入
input rst_n, //复位信号
input money, //是否投币输入
output reg cola); //是否有可乐输出
//--------------------<状态机参数定义>---------------------
//独热码编码的优点是每次状态跳转只有两个bit位发生跳变,动态功耗较低。同时独热码编码译码电路比较简单。缺点是用的寄存器位宽比二进制编码会大一点。
parameter IDLE = 4'b0001;
parameter ONE = 4'b0010;
parameter TWO = 4'b0100;
parameter THREE = 4'b1000;
//--------------------<三段式状态定义>---------------------
reg [3:0] state; // 当前状态定义
reg [3:0] next_state; // 次态定义
//---------------------<三段式状态机>---------------------
//同步时序描述状态转移
always@(posedge clk or negedge rst_n)begin
if(!rst_n) state <= IDLE;
else state <= next_state;
end
//组合逻辑描述状态转移条件
always@(*)begin
case (state)
IDLE: begin
next_state = money ? ONE : IDLE;
end
ONE : begin
next_state = money ? TWO : ONE ;
end
TWO : begin
next_state = money ? THREE : TWO ;
end
THREE:begin
next_state = money ? ONE : IDLE;
end
default:begin
next_state = money ? ONE : IDLE;
end
endcase
end
//同步时序逻辑描述输出
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cola <= 1'b0;
else
cola <= (state == THREE) ? 1'b1 : 1'b0;
end
endmodule
(2)三段式Mealy型
//三段式FSM示例
//采用的是Mealy型的FSM
//--------------------<模块及端口定义>---------------------
module cola_FSM(
input clk, //时钟输入
input rst_n, //复位信号
input money, //是否投币输入
output reg cola); //是否有可乐输出
//--------------------<状态机参数定义>---------------------
//独热码编码的优点是每次状态跳转只有两个bit位发生跳变,动态功耗较低。同时独热码编码译码电路比较简单。缺点是用的寄存器位宽比二进制编码会大一点。
parameter IDLE = 3'b001;
parameter ONE = 3'b010;
parameter TWO = 3'b100;
//--------------------<三段式状态定义>---------------------
reg [2:0] state; // 当前状态定义
reg [2:0] next_state; // 次态定义
//---------------------<三段式状态机>---------------------
//同步时序描述状态转移
always@(posedge clk or negedge rst_n)begin
if(!rst_n) state <= IDLE;
else state <= next_state;
end
//组合逻辑描述状态转移条件
always@(*)begin
case (state)
IDLE: begin
next_state = money ? ONE : IDLE;
end
ONE : begin
next_state = money ? TWO : ONE ;
end
TWO : begin
next_state = money ? IDLE : TWO ;
end
default:begin
next_state = money ? ONE : IDLE;
end
endcase
end
//同步时序逻辑描述输出
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cola <= 1'b0;
else
cola <= ((state == TWO) && money) ? 1'b1 : 1'b0;
end
endmodule
2、序列检测器
序列检测器是学习状态机设计中非常经典的一个问题,虽然可以使用移位寄存器的方式实现(甚至使用移位寄存器的方式更加简单),但是这里作为状态机的熟悉和练习,还是使用状态机的方式进行一个实现。
使用三段式状态机设计一个序列检测器,当检测到序列“101110”时,输出结果为1,否则输出为0。
由于我们一般推荐使用三段式的状态机,所以后面的内容将都采用三段式进行描述。
首先照例给出状态转移图,由于本人习惯性设计Mealy型状态机,所以后续的设计都将采用Mealy型,Moore型可以根据上述给出可乐机部分给出的示例自行设计实现。
根据状态转移图,我们就可以给出HDL代码:
//-------------------------------<模块及输入输出定义>-------------------------
module top(
input clk,
input rst_n,
input data_in,
output reg data_out);
//------------------------------<状态参数定义>-------------------------------
parameter s1 = 6'b000001;
parameter s2 = 6'b000010;
parameter s3 = 6'b000100;
parameter s4 = 6'b001000;
parameter s5 = 6'b010000;
parameter s6 = 6'b100000;
//-----------------------------<现态与次态定义>------------------------------
reg [5:0]state;
reg [5:0]next_state;
//-----------------------------<三段式状态机设计(Mealy型)>-----------------
//同步时序逻辑描述状态转移
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
state <= s1;
else
state <= next_state;
end
//组合逻辑描述状态转移条件
always@(*)
begin
case(state)
s1:
next_state = data_in ? s2 : s1;
s2:
next_state = data_in ? s2 : s3;
s3:
next_state = data_in ? s4 : s1;
s4:
next_state = data_in ? s5 : s3;
s5:
next_state = data_in ? s6 : s3;
s6:
next_state = data_in ? s2 : s3;
default :
next_state = s1;
endcase
end
//同步时序逻辑实现输出
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
data_out <= 1'b0;
else
data_out <= ((state == s6) && (!data_in)) ? 1'b1: 1'b0;
end
endmodule