状态机的组成其实比较简单,要素大致有三个:输入,输出,还有状态。
状态机描述时关键是要描述清楚前面提高的几个状态机的要素,即如何进行状态转移;每个状态的输出是什么;状态转移是否和输入条件相关等。
有人习惯将整个状态机写到一个always模块里面,在该模块中同时描述了状态转移,又描述状态的输入和输出。这种写法一般被称为一段式FSM描述方法; 还有一种写法是用2个always模块,其中一个always模块采用同步时序描述状态转移;另一个采用组合逻辑判断状态转移描述状态转移规律,这种写法 被称为两段式FSM描述;还有一种是在两段式基础之上发展出来的,这种写法使用3个always模块,一个always模块采用同步时序描述状态转移;第 二个采用组合逻辑判断状态转移条件,描述状态转移规律,第三个always模块使用同步时序电路描述每个状态的输出,这种写法成为三段式。
一般推荐后两种方法,也就是两段式和三段式。其原因为:FSM和其他设计一样,最好使用同步时序,以便提高设计的稳定性,消除毛刺。状态转移部分一般是同 步时序电路而状态的转移条件的判断是组合逻辑,之所以两段式比一段式更为合理,就在于它将时序逻辑和组合逻辑分别放到不同的always程序快中实现,这 样一来便于理解和阅读,二来利于综合器又换代码,更有利于用户添加合适的时序约束条件,利于布局布线器实现设计。
在两段式中,当前状态的输出是用组合逻辑实现的,这样的实现是有产生毛刺的可能性的,而且不利于约束。同样的原因,某些需要引入输出反馈的设计中,两段式会引入组合逻辑的反馈,使综合失败,这是要极力避免的。
三段式与两段式相比,关键在于根据状态转移规律,在上一状态根据输入条件判断当前状态的输出,从而在不出入额外的时钟节拍的前提下,实现了寄存器输出。
一个三段式的状态机大致如下:
module state (nrst,clk,sig1,sig2,out1,out2); input nrst,clk; input sig1,sig2; output out1,out2; reg out1,out2; reg [2:0] NS,CS; parameter [2:0] IDLE = 3'b000, S1 = 3'b001, S2 = 3'b010; //第一段,状态转换always @(posedge clk or negedge nrst) if(!nrst) CS<= IDLE; else CS<= NS; //第二段,组合逻辑判断always @(nrst or CS or sig1 or sig2) begin NS = 3'bx;//给出默认值 case(CS) IDLE: begin if (sig1......) NS = IDLE; else NS = S1; end S1: begin if(.........) NS = S1; else NS = xxxx;
S2: ..... endcase end //第三段,FSM输出always @(posedge clk or negedge nrst) if (!nrst) {out1,out2}<= 2'b00;else begin {out1,out2}<= 2'b00; case (NS) IDLE:......
S1: endcase end endmodule
两段式其实就是将上述的第二段,第三段合一。
三段式写法可概括为如下图:
注意:一三段之中用<=,第二段中用=。