zoukankan      html  css  js  c++  java
  • 三种不同状态机写法

    一段式状态机:

     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

  • 相关阅读:
    Go 语言简介(下)— 特性
    Array.length vs Array.prototype.length
    【转】javascript Object使用Array的方法
    【转】大话程序猿眼里的高并发架构
    【转】The magic behind array length property
    【转】Build Your own Simplified AngularJS in 200 Lines of JavaScript
    【转】在 2016 年做 PHP 开发是一种什么样的体验?(一)
    【转】大话程序猿眼里的高并发
    php通过token验证表单重复提交
    windows 杀进程软件
  • 原文地址:https://www.cnblogs.com/yllinux/p/8641634.html
Copyright © 2011-2022 走看看