有限状态机(Finite-State Machine,FSM),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。状态机不仅是一种电路的描述工具,而且也是一种思想方法,在电路设计的系统级和 RTL 级有着广泛的应用。
状态机类型
Verilog 中状态机主要用于同步时序逻辑的设计,能够在有限个状态之间按一定要求和规律切换时序电路的状态。状态的切换方向不但取决于各个输入值,还取决于当前所在状态。 状态机一般分为 2 类:Moore 状态机和 Mealy 状态机。
Moore 型状态机
Moore 型状态机的输出只与当前状态有关,与当前输入无关。
输出会在一个完整的时钟周期内保持稳定,即使此时输入信号有变化,输出也不会变化。输入对输出的影响要到下一个时钟周期才能反映出来。这也是 Moore 型状态机的一个重要特点:输入与输出是隔离开来的。
Mealy 型状态机
Mealy 型状态机的输出,不仅与当前状态有关,还取决于当前的输入信号。
Mealy 型状态机的输出是在输入信号变化以后立刻发生变化,且输入变化可能出现在任何状态的时钟周期内。因此,同种逻辑下,Mealy 型状态机输出对输入的响应会比 Moore 型状态机早一个时钟周期。
状态机设计流程
根据设计需求画出状态转移图,确定使用状态机类型,并标注出各种输入输出信号,更有助于编程。一般使用最多的是 Mealy 型 3 段式状态机,状态机的实现也分为两段式状态机和三段式状态机。
两段式状态机:
用两个always块来描述状态机,其中一个always模块采用同步时序来描述状态转移;另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律以及输出。两段式状态机要定义现态与次态,通过现态和次态的转换来实现时序逻辑。两段式状态机示例模板如下:
//两段式状态机第一个always块,描述当前状态的状态寄存器,时序逻辑
always @(posedge sys_clk)
begin
if(~sys_rst)
current_state <= S0;
else
current_state <= next_state;
end
//两段式状态机第二个always块,组合逻辑判断状态转移条件,描述状态转移规律与输出
always @(*)
begin
next_state = S0;
case (current_state)
S0: begin
out <= xxx;
if(...)
next_state = xxx;
else
next_state = xxx;
end
S1: begin
out <= xxx;
if(...)
next_state = xxx;
else
next_state = xxx;
end
...
default: ;
endcase
end
总结:两段式状态机将同步时序与组合逻辑分别放到不同的always块中实现。这样做的好处不仅仅是便于阅读、理解、维护,更重要的是利于综合器优化代码,利于用户添加合适的时序约束条件,但使用组合逻辑则无可避免的会引入毛刺问题。
三段式状态机:
在两个always块描述方法的基础上,使用3个always块。一个always块采用同步时序描述状态转移,一个always块采用组合逻辑判断转移条件,描述状态转移规律,另一个always块描述状态输出(可以组合逻辑输出,也可以时序逻辑输出)。三段式状态机示例模板如下:
//三段式状态机第一个always块,描述当前状态的状态寄存器,时序逻辑
always @(posedge sys_clk)
begin
if(~sys_rst)
current_state <= S0;
else
current_state <= next_state;
end
//三段式状态机第二个always块,组合逻辑判断状态转移条件,描述状态转移规律
always @(*)
begin
next_state = S0;
case (current_state)
S0: begin
if(...)
next_state = xxx;
else
next_state = xxx;
end
S1: begin
if(...)
next_state = xxx;
else
next_state = xxx;
end
...
default: ;
endcase
end
//三段式状态机第三个always块,描述输出
always @(posedge sys_clk)
begin
if(~sys_rst)
out <= xxx;
else
case (next_state)
S0: out <= xxx;
S1: out <= xxx;
...
default: ;
endcase
end
Moore状态机实现序列检测
对于Moore型状态机,序列检测1011的核心代码如下。
// Moore型三段式状态机第一个always块,同三段式实例模块
// Moore型三段式状态机第二个always块,描述下一状态的状态寄存器,总共有S0,S1,S2,S3,S4五个状态
always @(*)
begin
next_state = S0;
case (current_state)
S0: begin
if(data_in == 1'b1)
next_state = S1;
else
next_state = S0;
end
S1: begin
if(data_in == 1'b0)
next_state = S2;
else
next_state = S1;
end
S2: begin
if(data_in == 1'b1)
next_state = S3;
else
next_state = S0;
end
S3: begin
if(data_in == 1'b1)
next_state = S4;
else
next_state = S0;
end
S4: begin
if(data_in == 1'b0)
next_state = S2;
else
next_state = S1;
end
default: next_state = S0;
endcase
end
// Moore型三段式状态机第三个always块,描述输出,与输入信号无关
always @(posedge sys_clk)
begin
if(~sys_rst)
data_out <= 1'b0;
else if(current_state == S4)
data_out <= 1'b1;
else
data_out <= 1'b0;
end
//总结:三段式状态机的输出使用时序逻辑输出,避免了两段式状态机使用组合逻辑输出潜在的毛刺问题
Mealy状态机实现序列检测
对于Mealy型状态机,序列检测1011的核心代码如下。
// Mealy型三段式状态机第一个always块,同三段式实例模块
// Mealy型三段式状态机第二个always块,描述下一状态的状态寄存器,总共有S0,S1,S2,S3四个状态
always @(*)
begin
next_state = S0;
case (current_state)
S0: begin
if(data_in == 1'b1)
next_state = S1;
else
next_state = S0;
end
S1: begin
if(data_in == 1'b0)
next_state = S2;
else
next_state = S1;
end
S2: begin
if(data_in == 1'b1)
next_state = S3;
else
next_state = S0;
end
S3: begin
if(data_in == 1'b1)
next_state = S1;
else
next_state = S0;
end
default: next_state = S0;
endcase
end
// Mealy型三段式状态机第三个always块,描述输出,由现态和输入信号共同决定输出
always @(posedge sys_clk)
begin
if(~sys_rst)
data_out <= 1'b0;
else if(current_state == S3 && data_in == 1'b1)
data_out <= 1'b1;
else
data_out <= 1'b0;
end