一段式状态机:
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 从输入到输出比一段式和两段式会延时一个时钟周期。
注意:
- 三段式并不是一定要写为3个always块,如果状态机更复杂,就不止3段了。
- 三段always模块中,第一个和第三个always模块是同步时序always模块,用非阻塞赋值(“ <= ”);第二个always模块是组合逻辑always模块,用阻塞赋值(“ = ”)。
- 第二部分为组合逻辑always模块,为了抑制warning信息,对于always的敏感列表建议采用always@(*)的方式。
- 第二部分,组合逻辑always模块,里面判断条件一定要包含所有情况!可以用else保证包含完全。
- 第二部分,组合逻辑电平要维持超过一个clock,仿真时注意。
- 需要注意:第二部分case中的条件应该为当前态(cs)。
- 第三部分case中的条件一般为次态(ns),当然也可以当前态(cs),根据需求,若为当前态(cs)则延时一个时钟周期。
- 编码原则,binary和gray-code适用于触发器资源较少,组合电路资源丰富的情况(CPLD),对于FPGA,适用one-hot code。这样不但充分利用FPGA丰富的触发器资源,还因为只需比较一个bit,速度快,组合电路简单。
=================================================================
补充:(2019.12.30)
如上图所示,SignalA和SignalB是状态机的输出信号;
SignalA与输入有关(mealy型FSM),可以看出SignalA是CurrentState改变标志信号,由状态转移条件和当前状态有关,此信号如果用三段式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
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 HDL高级数字设计(第二版)》P182
《轻松成为设计高手 - Verilog HDL实用精解》P145 - P164