目录
基础概念介绍
今夜闲来无事,忽然想到最近准备复习一下Verilog语法,所以就侃一侃状态机吧!
状态机根据状态的数量可以分为两种:有限状态机(Finite State Machine)和无限状态机(Infinite State Machine),本篇文章主要讨论有限状态机的一段式,二段式,和三段式的写法。
状态机是一种思想方法,往往我们自己就是一台状态机器。
我们常常说“唉,女友又和我吵架了,所以我最近不在状态呀”来解释自己最近情绪低落,工作不积极的原因。
从状态机的角度来说就是从 “积极状态” 转变到了“消极状态” ,这就是所谓状态切换,状态切换的条件是“女朋友和我分手了”,对应的人在不同状态也会有不同的表现,这就是状态机的输出。
然后过了几天,当我们又找到新的女朋友时,我们又会回到积极的状态,如此往复。
从上我们可以看出,其实状态机特别适合用来描述具有先后顺序或者逻辑规律的事情,这就是状态机的本质。
状态机的要素
- 状态,用来划分逻辑顺序和时序规律
- 输入,状态改变的条件
- 输出,某个状态特定发生的事件
FSM的分类
这两者的区别就在于,如果Mealy和女朋友吵架时还被女朋友打了,他会生气,如果没有被打,他会心里难过,但Moore这个人吧和女朋友吵架了无论有没有被打,只会因为吵架而心里难过。
Verilog描述状态机的方法
好了,扯淡时间结束,下面我们聊一点干货!
上面的状态转移图来自HDLBits,这是一个很号的Verilog语法练习网站。描述的是一个两状态的Moore型状态机(输出只与状态有关,与输入无关)
一段式描述
所谓一段式描述就是将状态转移和状态输出还有状态转移判断都写在一个always块里
// Note the Verilog-1995 module declaration syntax here:
module top_module(clk, reset, in, out);
input clk;
input reset; // Synchronous reset to state B
input in;
output out;//
reg out;
parameter A=1'b1,B=1'b0;
reg present_state;
always @(posedge clk or posedge reset) begin
if (reset) begin
present_state <= B;
out <= 1'b1;
end else begin
case (present_state)
A:begin
if(in) begin
present_state <= A; //状态转移
out <=1'b0; //状态输出
end
else begin
present_state <= B;
out <= 1'b1;
end
end
B:begin
if(in) begin
present_state <= B;
out <=1'b1;
end
else begin
present_state <= A;
out <= 1'b0;
end
end
endcase
end
end
endmodule
这样当然能够成功描述出状态转移图所示的状态机,但是因为输入进入时序电路是异步的,所以状态转移判断的电路应该写成组合逻辑,写成一段式相当于把组合逻辑和时序逻辑混写 这样不符合 Verilog组合逻辑和时序逻辑分开描述的代码风格,同时我个人理解:
一段式状态机会延长关键路径,降低状态机性能。
两段式描述
两段式描述的显著特点有两个:
- 状态转移为时序电路
- 次态判断和状态输出为组合电路
// Note the Verilog-1995 module declaration syntax here:
module top_module(clk, reset, in, out);
input clk;
input reset; // Synchronous reset to state B
input in;
output out;//
reg out;
parameter A = 1'b1,B=1'b0;
// Fill in state name declarations
reg present_state, next_state;
always@(posedge clk)begin //状态转移
if(reset == 1'b1)
present_state <= B;
else
present_state <= next_state;
end
always @(*) begin
case (present_state) //次态判断,组合逻辑
A: if(in)
next_state = A;
else
next_state = B;
B:if(in)
next_state = B;
else
next_state = A;
default:
next_state = B;
// Fill in state transition logic
endcase
case (present_state) //输出,组合逻辑
A: out = 1'b0;
B: out = 1'b1;
// Fill in output logic
endcase
end
endmodule
小技巧:两段式状态机的模板为两个always,一个为 状态转移(@clk)、输出与次态判断(@*) ,以及两个状态 present_state 与 next_state
但是两段式的状态机也有缺点,那就是输出为组合逻辑,容易产生毛刺,因此有条件最好在输出的位置插入一个寄存器。
三段式描述
三段式是在两段式的基础上解决了组合逻辑输出毛刺的问题。
- 状态转移(时序)
- 次态判断(组合)
- 电路输出(时序)
这时总会有个聪明哥站出来说上一句:“哎呀,你把电路输出写成时序逻辑那么输出要延后了呀!”
请大家试想一下,如果我们电路输出的组合逻辑是根据现态来决定输出的,并且我们暂定状态在每个时钟周期,从A到B再到A这样循环。当state为A时,组合逻辑输入使得next_state为B,下一次时钟沿来临时,next_state将被传递给present_state,此时如果输出的时序逻辑模块是根据现态判断输出(即case语句是判断present_state),那么在B状态的起始,输出的内容将为A状态的输出,并且直到下次时钟沿来临才会改变。
因此,电路输出将根据next_state来决定。
// Note the Verilog-1995 module declaration syntax here:
module top_module(clk, reset, in, out);
input clk;
input reset; // Synchronous reset to state B
input in;
output out;//
reg out;
parameter A = 1'b1,B=1'b0;
// Fill in state name declarations
reg present_state, next_state;
always@(posedge clk)begin //状态转移
if(reset == 1'b1)
present_state <= B;
else
present_state <= next_state;
end
always @(*) begin//次态判断,组合逻辑
case (present_state)
A: if(in)
next_state = A;
else
next_state = B;
B:if(in)
next_state = B;
else
next_state = A;
default:
next_state = B;
// Fill in state transition logic
endcase
end
always@(posedge clk)begin //电路输出,时序逻辑
if(reset)
out <= 1'b1;
else
begin
case(next_state)
A: out <= 1'b0;
B: out <= 1'b1;
default:
out <= 1'b1;
endcase
end
end
endmodule
小技巧:三段式状态机的要点:两个状态(present_state)与三个always(状态转移@(posedge clk)、电路输出@(posedge clk)、次态判断always@(*))
结语
正所谓,天下武功,唯快不破同样,天下代码,唯练不破。状态机是一种方法论,不仅仅存在于时序电路,更是在编程算法和现实生活中广为存在,可以说,万物本质皆为状态机,我们要保留一颗融会贯通的心,这样学习才能得心应手,游刃有余,好吧今天的成语大会到此结束!