状态机定义及分类
什么是状态机:状态机通过不同的状态迁移来完成特定的逻辑操作(时序操作)
状态机的分类:Moore型状态机和Mealy型状态机
Moore型:状态机的变化只与当前的状态有关
Mealy型:状态机的变化不仅与当前的状态有关,还与输入有关
如何创建状态机:状态机的创建可以分为一段式,两段式和三段式:
一段式:所有的状态变化以及导致的输出变化都写在了一个always快中。在该模块中既描述状态转移,又描述状态的输入和输出。
两段式:用两个always模块来描述状态机。其中一个模块采用同步时序逻辑电路描述状态转移,另一个模块采用组合逻辑判断状态转移条件。它需要两个状态——现态和次态,然后通过现态和次态的转换来实现时序逻辑。
三段式:用三个always模块来描述状态机。其中一个模块采用同步时序逻辑电路描述状态转移,另一个模块采用组合逻辑判断状态转移条件(注意和两段式的区别)。第三个模块描述状态的输出(既可以用组合逻辑也可以用时序逻辑)
三段式状态机有什么优点?
为了说明这个问题,我们先来看另外一个问题:
(1)组合电路中的竞争与冒险
1、竞争与冒险产生的原因:在组合电路中信号的高低变化是需要一定的过渡时间的。当多路信号的电平发生变化时,在信号变化的瞬间,组合逻辑的输出也有先后顺序,并不是同步变化。比如000101要变化到00100 在变化的过程中可能会出现00110信号,被称为尖峰信号“毛刺”。时钟端口、清零和置位端口对毛刺信号十分敏感。(在数字电路中,信号由于经由不同路径传输达到某一汇合点的时间有先有后的现象,就称之为竞争;由于竞争现象所引起的电路输出发生瞬间错误的现象,就称之为冒险;)
2、解决竞争与冒险的方案
(1)采用格雷码取代普通的二进制计数器
(2) D触发器是一种比较传统的去除毛刺的方法。利用D触发器对输入信号的毛刺不敏感的特点(只要毛刺不出现在上升沿并且满足数据的建立和保持时间就不会对系统造成伤害),从而去除信号中的毛刺。
(2)三段式状态机的优点
1、一段式状态机只涉及时序电路,没有竞争与冒险,同时消耗逻辑比较少。但是如果状态非常多,一段式状态机显得比较臃肿,不利于维护。
2、两段式状态机:当一个模块采用时序(状态转移),一个模块采用组合时候(状态机输出),组合逻辑电路容易造成竞争与冒险;当两个模块都采用时序,可以避免竞争与冒险的存在,但是整个状态机的时序上会延时一个周期。
3、三段式状态机:三段式状态机在状态转移时采用组合逻辑电路+格雷码,避免了组合逻辑的竞争与冒险;状态机输出采用了同步寄存器输出,也可以避免组合逻辑电路的竞争与冒险;采用这两种方法极大的降低了竞争冒险。并且在状态机的采用这种组合逻辑电路+次态寄存器输出,避免了两段式状态机的延时一个周期(三段式状态机在上一状态中根据输入条件判断当前状态的输出,从而在不插入额外时钟节拍的前提下,实现寄存器的输出)。
灵活选择状态机,不一定要拘泥理论,怎样方面怎样来
三段式状态机模板:
module three_fsm(clk,rst_n,a,z);
input clk;
input rst_n;
input a;
output reg z;
reg [5:0] current_state;
reg [5:0] next_state;
parameter S0 = 6'b00_0001;
parameter S1 = 6'b00_0010;
parameter S2 = 6'b00_0100;
parameter S3 = 6'b00_1000;
parameter S4 = 6'b01_0000;
parameter S5 = 6'b10_0000;
//第一个进程,同步时序always模块,格式化描述次态寄存器迁移到现态寄存器
//相当于是一个D触发器
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
current_state <= S0;
else
current_state <= next_state;
end
//第二个进程,组合逻辑always模块,描述状态转移条件判断
always@(*)
begin
case(current_state)
S0: next_state = (a==1)?S1:S0;
S1: next_state = (a==0)?S2:S1;
S2: next_state = (a==0)?S3:S1;
S3: next_state = (a==1)?S4:S0;
S4: next_state = (a==0)?S5:S1;
S5: next_state = (a==0)?S3:S1;
default: next_state = S0;
endcase
end
//第三个进程,同步时序always模块,格式化描述次态寄存器输出
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
z = 0;
else
case(next_state)
S0: z = 0;
S1: z = 0;
S2: z = 0;
S3: z = 0;
S4: z = 0;
S5: z = 1;
default: z = 0;
endcase
end
endmodule