状态机概念
硬件设计很讲究并行设计思想,虽然verilog描述的电路大多是并行的,但是对于实际的工程应用中,往往需要设计硬件来实现一些具有顺序的工作,就需要用到状态机的思想。
![](https://img-blog.csdnimg.cn/img_convert/0b9557af052bb6fdb51210d1fa1cd035.png)
状态机:有限状态机(Finite State Machine),简称FSM。
状态机是数字系统设计中非常重要的组成部分,状态机的设计对系统的高可靠性、高速性有着至关重要的作用。
状态机由多个相互跳转的状态组成,用于对具有逻辑顺序和时序规律的事件进行描述,在任意时刻,状态机只能处于某一个状态;状态机的功能可以发分解为两个部分:第一部分根据外部输入实现状态转移;第二部分根据特定状态和输入来驱动输出。
状态机可以将待实现的复杂功能分解在各个状态中,使得整个系统的控制流程的实现变得简单化。
状态机有四个要素:现态、次态、输入、输出;
现态:状态机当前所处的状态;
次态:根据现态、输入、状态转移条件所得到的状态机将要跳转至的新状态;
输入:一般指外部事件,当一个外部事件发生后,状态机便会根据状态转移函数发生响应;
输出:由现态或现态和输入共同决定的,但是有时某一状态或者某一输入改变,输出并不一定会发生变化,带来的仅仅是状态迁移而已。
状态机模型
摩尔型状态机:
![](https://img-blog.csdnimg.cn/img_convert/776e5bb02289ad5abd782b83bf7e33eb.png)
米勒型状态机:
![](https://img-blog.csdnimg.cn/img_convert/7f7fad25fe75ed990d8039597beb2f85.png)
由于摩尔型状态机的输出来自组合逻辑,可能会产生毛刺,从而产生不利的影响;
而米勒型状态机由于输入与输出有关,且输出也是组合逻辑,输出时序不佳,限制了系统的工作频率;
更好的方法时把摩尔型状态机和米勒型状态机混合起来使用,也即输出既和状态有关又与输入有关,采用时序逻辑输出。
寄存器型输出:具有更好的时序性能。
注意:一个健壮的状态机应当具备初始化状态或默认状态;即芯片上电或复位之后,状态机能够自动将所有判断条件复位,并进入初始状态。
大多数FPGA都有GSR(全局复位信号),当FPGA上电后,GSR有效,对所有寄存器、RAM等单元复位/置位。
![](https://img-blog.csdnimg.cn/img_convert/b30099b859e4dff357a5071e48172c27.png)
状态机描述方法
状态机描述方法:一段式、两段式、三段式
一段式状态机:所有的状态变化以及输出变化都写在一个always块中在该always块中既描述状态的同步转移,又描述状态的输入条件和输出。
两段式状态机:用两个always块来描述状态机。其中一个模块采用同步时序逻辑描述状态转移,另一个模块采用组合逻辑判断状态转移条件。它需要两个状态——现态和次态,然后通过现态和次态的转换来实现时序逻辑。输出采用组合逻辑。
三段式状态机:用三个always块来描述状态机。其中一个模块采用同步时序逻辑描述状态转移,另一个模块采用组合逻辑判断状态转移条件(注意和两段式的区别)。第三个模块描述状态的输出(既可以用组合逻辑也可以用时序逻辑)。
可见,三段式描述法是最简单的。
状态机编码方法
状态机编码方法:独热码编码(只有一位是高电平),自然二进制编码,格雷码编码。
![](https://img-blog.csdnimg.cn/img_convert/caf56c7f8584f7f16cd14cbd7280accb.png)
![](https://img-blog.csdnimg.cn/img_convert/0cbf1ae93feafe615e6ef8a621df2bae.png)
FPGA提供的触发器资源比组合逻辑资源多,所以使用独热码编码较好,节省组合逻辑资源,简化了比较逻辑,状态译码方便。
![](https://img-blog.csdnimg.cn/img_convert/c798e5e2222fc13575e8131a43331201.png)
总结:
推荐使用独热码编码方式与三段式描述方式设计状态机。
1.三段always块中,第一个和第三个(多个)always块都是同步时序逻辑,用非阻塞(<=)赋值;第二个always块是组合逻辑,用阻塞赋值(=) ;
2.第二部分为组合逻辑always块,建议敏感列表使用always@(*)的方式;
3.第二部分组合逻辑always块中的判断条件一定要包含所有情况;
4.第二部分组合逻辑always块中的case中的条件应该为当前状态-state c;
5.状态转移的条件可以在第二段组合逻辑always块之外单独设计。
序列检测设计
基于状态机的概念,设计下面一个状态图:
此状态图是要在一段序列101101101101中检测出1101的序列,可重复检测3次1101。
![](https://img-blog.csdnimg.cn/img_convert/b731b4e50e3178f036153eb38b423085.png)
设计随机输入一段序列,这个序列我们可以用按键去传达,比如这里,由于序列是二进制序列只有0个1组成,因此设计两位按键用高位和低位分别表示是否输出1/0,高位表示是否输出1,低位表示是否输出0。输出dout则规定4位,检测是否有1101的序列输出。
设计代码如下:
module detect (
input clk ,
input rst_n ,
input [1:0] key_in ,
output reg [3:0] dout
);
//参数定义
parameter S1 = 4'b0001 ,//检测‘1’
S2 = 4'b0010 ,//检测‘1’
S3 = 4'b0100 ,//检测‘0’
S4 = 4'b1000 ;//检测‘1’
//状态机信号
reg [3:0] state_c ;
reg [3:0] state_n ;
wire s12s2 ;
wire s22s3 ;
wire s22s1 ;
wire s32s4 ;
wire s42s2 ;
wire s42s1 ;
//第一段 时序逻辑 状态的转移
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state_c <= S1 ;
end
else begin
state_c <= state_n ;
end
end
//第二段 组合逻辑 描述状态转移的规律
always @(*) begin
case (state_c)
S1:begin
if(s12s2)
state_n = S2 ;
else
state_n = state_c ;
end
S2:begin
if(s22s3)
state_n = S3 ;
else if(s22s1)
state_n = S1 ;
else
state_n = state_c ;
end
S3:begin
if(s32s4)
state_n = S4 ;
else
state_n = state_c ;
end
S4: begin
if(s42s2)
state_n = S2 ;
else if(s42s1)
state_n = S1 ;
else
state_n = state_c ;
end
default:state_n = S1 ;
endcase
end
//状态之间的条件(状态之间有多少箭头就有多少条件)
assign s12s2 = state_c == S1 && (key_in[1]);//1
assign s22s3 = state_c == S2 && (key_in[1]);//1-1
assign s22s1 = state_c == S2 && (key_in[0]);//1-0
assign s32s4 = state_c == S3 && (key_in[0]);//1-1-0
assign s42s2 = state_c == S4 && (key_in[1]);//1-1-0-1
assign s42s1 = state_c == S4 && (key_in[0]);//1-1-0-0
//dout
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
dout <= 'd0;
end
else if (s42s2) begin
dout <= dout + 1'b1; //输出需要的结果1101
end
end
endmodule
测试文件:
`timescale 1ns/1ps
module detect_tb();
reg clk ;
reg rst_n ;
reg [1:0] key ;//两个按键0/1表示的是这两个按键是否按下,如0/1表示低位按键按下而高位按键没按下
wire [3:0] dout ;
//参数
parameter CYCLE = 20 ;//50M时钟
//模块例化
detect u_detect(
.clk (clk ),
.rst_n (rst_n ),
.key_in (key ),
.dout (dout)
);
//激励
initial begin //产生时钟
clk = 1'b1 ;
forever begin
#(CYCLE/2);
clk = ~clk ;
end
end
integer i;
initial begin
rst_n = 1'b1;
#(CYCLE);
rst_n = 1'b0;
key = 2'b00;
#(CYCLE*2);
#2;
rst_n = 1'b1;
#(CYCLE*4)
for (i = 0 ;i < 10 ; i = i + 1 ) begin
key[0] = {$random}%2 ;
#(CYCLE);
key = 2'b00;
#(CYCLE);
key[1] = {$random}%2 ;
#(CYCLE);
key = 2'b00;
#(CYCLE*2);
end
key = 2'b00;
#(CYCLE*10);
$stop;
end
endmodule
仿真分析:
![](https://img-blog.csdnimg.cn/img_convert/4d90671a06b80e77993c708ceefc39b3.png)
仿真结果显示,此段随机序列没有一个符合要求的1101序列。