三种不同状态机写法

一段式状态机:

 1 reg[3:0]  cs, ns;
 2 always @(posedge clk or negedge rst_n) begin
 3     if (!rst_n) begin
 4         cs    <=  IDLE;
 5         cmd <=  3'b111;
 6     end
 7     else begin
 8         case (cs)
 9             IDLE :   if (wr_req) begin cs <= WR_S1; cmd <= 3'b011; end
10                 else if (rd_req) begin cs <= RD_S1; cmd <= 3'b011; end
11                 else             begin cs <= IDLE;  cmd <= 3'b111; end
12             WR_S1:               begin cs <= WR_S2; cmd <= 3'b101; end
13             WR_S2:               begin cs <= IDLE;  cmd <= 3'b111; end
14             RD_S1:   if (wr_req) begin cs <= WR_S2; cmd <= 3'b101; end
15                      else        begin cs <= RD_S2; cmd <= 3'b110; end
16             RD_S2:   if (wr_req) begin cs <= WR_S1; cmd <= 3'b011; end
17                      else        begin cs <= IDLE;  cmd <= 3'b111; end
18             default :                  cs <= IDLE;
19         endcase
20     end
21 end

两段式状态机:

 1 reg[3:0]  cs, ns;
 2 //----------   时序逻辑   ------------------
 3 always @(posedge clk or negedge rst_n) begin
 4     if (!rst_n)
 5         cs    <=  IDLE;
 6     else 
 7         cs    <=  ns;
 8 end
 9 //----------   组合逻辑   ------------------
10 always @(*) begin
ns = IDLE;
11 case (cs) 12 IDLE : if (wr_req) begin ns = WR_S1; cmd = 3'b011; end 13 else if (rd_req) begin ns = RD_S1; cmd = 3'b011; end 14 else begin ns = IDLE; cmd = 3'b111; end 15 WR_S1: begin ns = WR_S2; cmd = 3'b101; end 16 WR_S2: begin ns = IDLE; cmd = 3'b111; end 17 RD_S1: if (wr_req) begin ns = WR_S2; cmd = 3'b101; end 18 else begin ns = RD_S2; cmd = 3'b110; end 19 RD_S2: if (wr_req) begin ns = WR_S1; cmd = 3'b011; end 20 else begin ns = IDLE; cmd = 3'b111; end 21 default : ns = IDLE; 22 endcase 23 end

三段式状态机:

 1 reg[3:0]  cs, ns;
 2 //----------   时序逻辑   ------------------
 3 always @(posedge clk or negedge rst_n) begin
 4     if (!rst_n)
 5         cs    <=  IDLE;
 6     else 
 7         cs    <=  ns;
 8 end
 9 //----------   组合逻辑   ------------------
10 always @(*) begin
ns = IDLE; //组合逻辑块中出现的信号需要初始化
11 case (cs) //现态 12 IDLE : if (wr_req) begin ns = WR_S1; end 13 else if (rd_req) begin ns = RD_S1; end 14 else begin ns = IDLE; end 15 WR_S1: begin ns = WR_S2; end 16 WR_S2: begin ns = IDLE; end 17 RD_S1: if (wr_req) begin ns = WR_S2; end 18 else begin ns = RD_S2; end 19 RD_S2: if (wr_req) begin ns = WR_S1; end 20 else begin ns = IDLE; end 21 default : ns = IDLE; 22 endcase 23 end 24 //---------- 时序逻辑 ------------------ 25 always @(posedge clk or negedge rst_n) begin 26 if (!rst_n) 27 cmd <= 3'b011; 28 else begin 29 case (ns) //次态 30 IDLE : if (wr_req) begin cmd <= 3'b011; end 31 else if (rd_req) begin cmd <= 3'b011; end 32 else begin cmd <= 3'b111; end 33 WR_S1: begin cmd <= 3'b101; end 34 WR_S2: begin cmd <= 3'b111; end 35 RD_S1: if (wr_req) begin cmd <= 3'b101; end 36 else begin cmd <= 3'b110; end 37 RD_S2: if (wr_req) begin cmd <= 3'b011; end 38 else begin cmd <= 3'b111; end 39 default : ; 40 endcase 41 end 42 end

三种写法对比:

    (1)一段式状态机不利于维护(简单状态机可以用);

    (2)两段式状态机是常见写法,时序逻辑进行状态切换,时序逻辑实现各个输入、输出以及状态判断,利于维护,不过组合逻辑容易出现毛刺等常见问题;

    (3)三段式状态机推荐写法,代码易维护,时序逻辑输出解决了两段式写法种组合逻辑的毛刺问题,但是耗费资源多一些且第三段 always 如果判断条件是 cs 从输入到输出比一段式和两段式会延时一个时钟周期。

注意:

  1. 三段式并不是一定要写为3个always块,如果状态机更复杂,就不止3段了。
  2. 三段always模块中,第一个和第三个always模块是同步时序always模块,用非阻塞赋值(“ <= ”);第二个always模块是组合逻辑always模块,用阻塞赋值(“ = ”)。
  3. 第二部分为组合逻辑always模块,为了抑制warning信息,对于always的敏感列表建议采用always@(*)的方式。
  4. 第二部分,组合逻辑always模块,里面判断条件一定要包含所有情况!可以用else保证包含完全。
  5. 第二部分,组合逻辑电平要维持超过一个clock,仿真时注意。
  6. 需要注意:第二部分case中的条件应该为当前态(cs)。
  7. 第三部分case中的条件一般为次态(ns),当然也可以当前态(cs),根据需求,若为当前态(cs)则延时一个时钟周期。
  8. 编码原则,binary和gray-code适用于触发器资源较少,组合电路资源丰富的情况(CPLD),对于FPGA,适用one-hot code。这样不但充分利用FPGA丰富的触发器资源,还因为只需比较一个bit,速度快,组合电路简单。

 =================================================================

补充:(2019.12.30)

如上图所示,SignalASignalB是状态机的输出信号;

SignalA与输入有关(mealy型FSM),可以看出SignalACurrentState改变标志信号,由状态转移条件和当前状态有关,此信号如果用三段式FSM则无法表示,

虽然也可以另加组合逻辑实现:

assign  SignalA_1 = (状态转移条件) && (CurrentState == IDLE)    // 其中一个状态转移标志

assign  SignalA_1 = (CurrentState != NextState) && (CurrentState == IDLE   // 其中一个状态转移标志

assign  SignalA_1 = ( |(CurrentState ^ NextState) ) && (CurrentState == IDLE   // 其中一个状态转移标志

但是就不如二段式FSM简洁清晰、易于维护。

二段式和三段式相比

1.三段式做到了同步寄存器输出,消除了组合逻辑带来的不稳定与毛刺隐患;

2.三段式更利于时序路径分组,一般来说在FPGA/CPLD等可编程逻辑器件上综合与布线效果更好;

3.二段式比三段式代码结构简单;

4.如果的输出逻辑过于复杂,二段式比三段式更好,三段式时序上更紧张或无法正确输出;

Mealy机:

(1)二段式FSM

寄存器输出滞后于组合逻辑值一个时钟周期;

(2)三段式FSM

=================================================================

补充:(2020.01.01)

 案例分析:两段式状态机不可能完成的任务

源码copy如下:

  1 //`include "define.v"
  2 module fsm_2duan( clk,rst_n, din,dout);
  3 
  4 input       clk;
  5 input       rst_n;   
  6 input       din;
  7 output[3:0] dout;  
  8 
  9 parameter   IDLE  = 3'd0;
 10 parameter   STA1  = 3'd1;
 11 
 12 
 13 `ifdef  FSM_1
 14 //---------------------------------------------------
 15 //  一段式写法
 16 //---------------------------------------------------
 17 reg[2:0] cstate;
 18 reg[3:0] cnt;
 19 
 20 always @(posedge clk or negedge rst_n)
 21     if(!rst_n) 
 22         cstate <= IDLE;
 23     else begin
 24         case(cstate)
 25             IDLE: begin
 26                     cnt <= 4'd0;
 27                     if(din) 
 28                         cstate <= STA1;
 29                     else 
 30                         cstate <= IDLE;       
 31                 end
 32             STA1: begin
 33                     cnt <= cnt+1'b1;
 34                     if(cnt == 4'd10) 
 35                         cstate <= IDLE;
 36                     else 
 37                         cstate <= STA1;
 38                 end
 39             default: cstate <= IDLE;
 40         endcase
 41     end
 42 
 43 `elsif FSM_3
 44 //---------------------------------------------------
 45 // 三段式写法  
 46 //---------------------------------------------------
 47 reg[2:0] cstate,nstate;
 48 reg[3:0] cnt;
 49 
 50 always @(posedge clk or negedge rst_n)
 51     if(!rst_n) 
 52         cstate <= IDLE;
 53     else 
 54         cstate <= nstate;
 55 
 56 always @(cstate or din or cnt) begin
 57     case(cstate)
 58         IDLE:   if(din) 
 59                     nstate = STA1;
 60                 else 
 61                     nstate = IDLE;    
 62         STA1:   if(cnt == 4'd10) 
 63                     nstate = IDLE;
 64                 else 
 65                     nstate = STA1;
 66         default: nstate = IDLE;
 67     endcase
 68 end
 69 
 70 always @(posedge clk or negedge rst_n)
 71     if(!rst_n) 
 72         cnt <= 4'd0;
 73     else begin
 74         case(nstate)
 75             IDLE:   cnt <= 4'd0;
 76             STA1:   cnt <= cnt+1'b1;
 77             default: ;
 78         endcase
 79     end
 80 
 81 `elsif FSM_2C
 82 //---------------------------------------------------
 83 //  改进的两段式写法
 84 //---------------------------------------------------
 85 reg[2:0] cstate,nstate;
 86 reg[3:0] cnt;
 87 reg      clr_cnt_flag, add_cnt_flag;
 88 
 89 always @(posedge clk or negedge rst_n)
 90     if(!rst_n) 
 91         cstate <= IDLE;
 92     else 
 93         cstate <= nstate;
 94 
 95 always @(cstate or din or cnt) begin
 96     // 
 97     nstate       = IDLE;
 98     clr_cnt_flag = 1'b0;
 99     add_cnt_flag = 1'b0;
100    //
101     case(cstate)
102         IDLE: begin
103                 if(din) begin
104                     nstate = STA1;
105                     add_cnt_flag = 1'b1;
106                 end
107                 else 
108                     nstate = IDLE;    
109             end
110         STA1: begin
111                 add_cnt_flag = 1'b1;
112                 if(cnt >= 4'd10) begin
113                     nstate = IDLE;
114                     add_cnt_flag = 1'b0;
115                     clr_cnt_flag = 1'b1;
116                 end
117                 else 
118                     nstate = STA1;
119             end
120         default: nstate = IDLE;
121     endcase
122 end
123 
124 always @(posedge clk or negedge rst_n)
125     if(!rst_n) 
126         cnt <= 4'd0;
127     else if (clr_cnt_flag) 
128         cnt <= 4'd0;
129     else if (add_cnt_flag)
130         cnt <= cnt + 4'd1;
131     
132 
133 `else
134 //---------------------------------------------------
135 //  两段式写法
136 //---------------------------------------------------
137 reg[2:0] cstate,nstate;
138 reg[3:0] cnt;
139 
140 always @(posedge clk or negedge rst_n)
141     if(!rst_n) 
142         cstate <= IDLE;
143     else 
144         cstate <= nstate;
145 
146 always @(cstate or din or cnt) begin
147     case(cstate)
148         IDLE: begin
149                 cnt = 4'd0;
150                 if(din) 
151                     nstate = STA1;
152                 else 
153                     nstate = IDLE;    
154             end
155         STA1: begin
156                 cnt = cnt+1'b1;
157                 if(cnt == 4'd10) 
158                     nstate = IDLE;
159                 else 
160                     nstate = STA1;
161             end
162         default: nstate = IDLE;
163     endcase
164 end
165 `endif
166 
167 assign dout = cnt;
168 endmodule
View Code

1.这里我们先用 Quartus13.1 对这三种情况进行编译综合,分析编译log和布线后的电路;

一段式:

    (1)编译综合log没有异常;

    (2)生成的实际电路如下图:

            一段式中 cstate和cnt都生成了D触发器,其他为组合逻辑。

二段式:

    (1)编译综合log有异常,报出了组合逻辑反馈;

            (其实我们从二段式代码分析也能看出在组合逻辑的always块中,当 cnt == 4'd10 时 nstate状态将不确定);

    (2)生成的实际电路如下图:

            二段式中 只有cstate综合成了D触发器,其他(注意cnt也是组合逻辑)为组合逻辑。

三段式:

    (1)编译综合log没有异常;

    (2)生成的实际电路如下图:

            一段式中 cstate和cnt都生成了D触发器,其他为组合逻辑,与一段式区别仅在组合逻辑上。

2.用 VCS 对三种情况进行前仿真;

    因为没有进行代码检查,VCS编译时也不会报warning;只好看仿真结果;

一段式:

二段式:

改进二段式:(把原来的组合逻辑计数器改为时序计数器)

三段式:

总结:

1.不要用一段式;

2.根据需求选用二段式和三段式

   (1)FPGA之类的一般选三段式且独热编码,稳定可靠,不在乎那一点触发器资源;

   (2)ASIC尽量选二段式(或者组合逻辑的三段式),减小面积,且用为伪格雷码减少状态出错概率;

   (3)尽量选择摩尔型状态机,更加安全;

=================================================================

补充:(2020.07.04)

 摩尔机和米利机:

对比 摩尔型有限状态机 米利型有限状态机
性质 输出值仅由其当前状态确定 输出与当前状态和输入都有关
状态图 每个节点(状态)都标有输出值 每个弧(过渡)都标有输出值
输入反应 可能需要更多逻辑来将状态解码为输出 , 在时钟边沿之后更多的门延迟 机器对输入的反应更快,在相同的周期内反应 ,不需要等待时钟
安全性 更安全,输出在时钟边沿变化(总是在一个周期后) 输入更改可能会在逻辑完成后立即导致输出更改,当两台机器互连时可能出现问题,如果不小心,可能会发生异步反馈。

 

 ----------------------------------------------------------

参考:

《深入浅出玩转FPGA_第二版》P27 - P34

verilog 三段式状态机的技巧

[资料] 彻底搞懂状态机(一段式、两段式、三段式)!一个实例,三种方法对比看!!!(程序)

《Verilog HDL高级数字设计(第二版)》P182

《轻松成为设计高手 - Verilog HDL实用精解》P145 - P164

原文地址:https://www.cnblogs.com/yllinux/p/8641634.html