• 三种不同状态机写法


    一段式状态机:

     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

  • 相关阅读:
    【筛法求素数】Codeforces Round #426 (Div. 1) A. The Meaningless Game
    【构造】AtCoder Regular Contest 079 F
    【贪心】AtCoder Regular Contest 079 E
    【构造】AtCoder Regular Contest 079 D
    【树形dp】hdu6035 Colorful Tree
    【计算几何】【bitset】Gym
    【枚举】【高斯消元】Gym
    【矩阵乘法】Gym
    【枚举约数】Gym
    【置换群】【枚举约数】hdu6038 Function
  • 原文地址:https://www.cnblogs.com/yllinux/p/8641634.html
Copyright © 2020-2023  润新知