Verilog FSM设计的学习心得(二)

Posted on 2012-04-15 14:10  freeny  阅读(1850)  评论(0)    收藏  举报

FSM设计过程中,常见的实现方法是one-always-block(一段式写法),two-always-block(两段式写法),three-always-block(三段式写法)。

1.一段式写法(不推荐,尽量避免此写法)

将整个状态机写在1个always模块中,在该模块中即描述状态转移,又描述状态的输入和输出。一段式描述方法将状态转移判断的组合逻辑和状态寄存器的时序逻辑混写在同一个always模块中,不符合将时序和组合逻辑分开描述的 Coding Style(代码风格) ,而且在描述当前状态时要考虑下个状态的输出,整个代码不清晰,不利于维护修改,并且不利于附加约束,不利于综合器和布局布线器对设计的优化。

一般来说,一段式代码长度会比两段式冗长大约 80%到 150%左右。

所以一段式 FSM描述是不推荐的FSM描述方式,设计过程中一定要避免。

image

图1 一段式FSM描述结构图

 

2.两段式写法(推荐使用)

使用2个always模块来描述状态机,其中一个always模块采用同步时序描述状态转移;另一个always模块采用组合逻辑判断状态转移条件,描述状态转移规律。输出在组合逻辑模块中与状态转移同时进行。

两段式思路建模结构清晰,描述简洁,便于约束,而且如果允许输出逻辑允许插入一个节拍,就可以通过插入输出寄存器改善输出逻辑的时序并避免组合逻辑的毛刺。

为了使FSM描述清晰简介,易于维护,易于附加时序约束,使综合器和布局布线器更好的优化设计,推荐使用两段式 FSM描述方法。

image

图2 两段式FSM描述结构图

 

3.三段式写法(推荐使用)

使用3个always模块,一个always模块采用同步时序描述状态转移;第二个always模块采用组合逻辑判断状态转移条件,描述状态转移规律;第三个always模块使用同步时序电路描述每个状态的输出。

三段式思路建模方法结构清晰,格式化的结构,解决了不改变时序要求的前提下用寄存器做状态输出的问题。

两段式 FSM 描述方法有一个明显的弱点就是其输出一般使用组合逻辑描述,而组合逻辑易产生毛刺等不稳定因素,不利于约束,不利于综合器和布局布线器实现高性能的设计,并且在 FPGA/CPLD 等逻辑器件中过多的组合逻辑会影响实现的速率(这点与 ASIC 设计不同) 。在两段式 FSM 描述方法中,如果时序允许插入一个额外的时钟节拍,则尽量在后级电路对FSM 的组合逻辑输出用寄存器寄存一个节拍,则可以有效地消除毛刺。但是很多情况下,设计并不允许额外的节拍插入(Latency) ,此时,解决之道就是采用 3 段式 FSM 描述方法。

三段式与两段式 FSM描述的最大区别在于两段式采用了组合逻辑输出,而三段式巧妙地根据下一状态的判断,用同步时序逻辑寄存 FSM的输出。

image

图3 三段式FSM描述结构图

 

三种FSM描述方法的比较

使用一段式建模 FSM 的寄存器输出的时候,必须要综合考虑现态在何种状态转移条件下会进入哪些次态,然后在每个现态的 case 分支下分别描述每个次态的输出;

而三段式建模描述 FSM 的状态机输出时,只需指定 case 敏感表为次态寄存器,然后直接在每个次态的 case 分支中描述该状态的输出即可,根本不用考虑状态转移条件。

 

三段式描述方法与两段式描述相比,虽然代码结构复杂了一些,但是换来的优势是使 FSM 做到了同步寄存器输出,消除了组合逻辑输出的不稳定与毛刺的隐患,而且更利于时序路径分组,一般来说在 FPGA/CPLD 等可编程逻辑器件上的综合与布局布线效果更佳。

a.三段式建模和一段式建模的关系

大家可以看到三段式与一段式的最大区别在于:

  • 使用一段式建模 FSM 的寄存器输出的时候,必须要综合考虑现态在何种状态转移条件下会进入哪些次态,然后在每个现态的 case 分支下分别描述每个次态的输出。
  • 三段式建模描述 FSM 的状态机输出时,只需指定 case 敏感表为次态寄存器,然后直接在每个次态的case 分支中描述该状态的输出即可,根本不用考虑状态转移条件。

 

对于简单的FSM,三段式建模的寄存器输出的优势还不是十分明显,但是对于复杂一些的FSM,三段式建模的优势就会十分显著。

 image

图4   三段式建模结构与一段式建模结构的关系图

b.两段式建模和三段式建模的关系

从代码上看,三段式建模的前两段与两段式建模完全相同,仅仅多了一段寄存器 FSM 输出。一般来说,使用寄存器输出可以改善输出的时序条件,还能避免组合电路的毛刺,所以是更为推荐的描述方式。

 

电路设计不是一成不变地,在某些情况下,两段式结构比三段式结构更有优势。

请大家再分析一下图2、图 3 的结构,细心的读者会发现,两段式用状态寄存器分割了两部分组合逻辑(状态转移条件组合逻辑和输出组合逻辑) ;

而三段式结构中,从输入到寄存器状态输出的路径上,要经过两部分组合逻辑(状态转移条件组合逻辑和输出组合逻辑) ,从时序上,这两部分组合逻辑完全可以看为一体。这样这条路径的组合逻辑就比较繁杂,该路径的时序相对紧张。

 

两段式建模中用状态寄存器分割了组合逻辑,而三段式将寄存器移到组合逻辑的最后端。如果寄存器前的组合逻辑过于复杂,势必会成为整个设计的关键路径,此时就不宜再使用三段式建模,而要使用两段式建模。   

解决两段式建模组合逻辑输出产生毛刺的方法是,额外的在 FSM 后级电路插入寄存器,调整时序,完成功能。

 

任何一种描述的优劣只是一般规律,而不是绝对性规律。例如一般来说不推荐一段式描述,但是如果 FSM 的结构十分简单,状态很少,状态转移条件和状态输出都十分简化,则使用一段式建模的效率很高。这些经验呢需要逐步积累,在实际运用中仔细体会。

需要注意的地方:

  1. 在两段式描述方法中,第一个always模块采用时序逻辑描述状态转移,使用”<=”(non-blocking)进行赋值;第二个always模块采用组合逻辑判断状态转移条件,敏感列表为当前状态“CS”、复位条件和输入条件,推荐使用Verilog-2001标准中的通配符(*)作为敏感列表,在敏感列表之后的语句中先写默认的下一状态“NS”的描述,次模块使用“=”(blocking)进行赋值。
  2. 虽然下一状态寄存器 NS 为寄存器类型,但是在两段式 FSM 的判断状态转移条件的always 模块中,实际上对应的真实硬件电路是纯组合逻辑电路。

  3. 三段式描述方法的前面两个always模块与两段式基本相同,不同之处在于第二个判断状态转移到的always模块中case语句判断的条件是当前状态”CS“,同样推荐使用敏感列表通配符”*“和阻塞赋值”=“进行操作;第三个always模块为同步时序输出,此处case语句判断条件是下一状态”NS“,推荐使用非阻塞赋值”<=“进行相关操作。
  4. 必须保证组合逻辑always模块中的敏感列表包含所有用到的状态。为避免敏感列表不完整造成的意外状况,推荐使用敏感列表的通配符“*”,即将组合逻辑always模块写成always @(*)或always @* 的形式。

以下为三种描述方法的代码示例:(以下面的状态转移图为例)

image

  • 一段式

module fsm_one_always
  (output reg gnt,
   input dly, done, req, clk, rst_n);

  parameter [1:0] IDLE  = 2'd0,
                              BBUSY = 2'd1,
                              BWAIT = 2'd2,
                              BFREE = 2'd3;

  reg [1:0] state;

  always @(posedge clk or negedge rst_n)
         if (!rst_n) begin
                 state <= IDLE;
                 gnt   <= 1'b0;
              end
        else begin
              state <= 2'bx;
              gnt   <= 1'b0;
              case (state)
                    IDLE : if (req) begin
                                 state <= BBUSY;
                                  gnt <= 1'b1;
                                 end
                             else              state <= IDLE;
                    BBUSY: if (!done) begin
                                     state <= BBUSY;
                                     gnt <= 1'b1;
                                  end
                               else if ( dly) begin
                                    state <= BWAIT;
                                    gnt <= 1'b1;
                                  end
                              else              state <= BFREE;
                      BWAIT: if ( dly) begin
                                      state <= BWAIT;
                                       gnt <= 1'b1;
                                      end
                                    else              state <= BFREE;
                      BFREE: if (req) begin
                                      state <= BBUSY;
                                      gnt <= 1'b1;
                                    end
                                 else              state <= IDLE;
                endcase
        end
endmodule

 

 

  • 两段式

module fsm_two_always
  (output reg gnt,
   input dly, done, req, clk, rst_n);

  parameter [1:0] IDLE  = 2'b00,
                              BBUSY = 2'b01,
                             BWAIT = 2'b10,
                             BFREE = 2'b11;

  reg [1:0] state, next;

  always @(posedge clk or negedge rst_n)
          if (!rst_n) state <= IDLE;
         else        state <= next;

  always @(state or dly or done or req) begin
        next = 2'bx;
        gnt  = 1'b0;
        case (state)
              IDLE :   if (req)       next = BBUSY;
                          else           next = IDLE;
              BBUSY: begin
                                gnt = 1'b1;
                                if (!done)     next = BBUSY;
                                else if ( dly) next = BWAIT;
                                else           next = BFREE;
                           end
             BWAIT: begin
                               gnt = 1'b1;
                               if (!dly)      next = BFREE;
                               else           next = BWAIT;
                          end
             BFREE:   if (req)       next = BBUSY;
                            else           next = IDLE;
        endcase
   end
endmodule

 

 

  • 三段式

module fsm_three_always
  (output reg gnt,
   input dly, done, req, clk, rst_n);

  parameter [1:0] IDLE  = 2'b00,
                               BBUSY = 2'b01,
                              BWAIT = 2'b10,
                              BFREE = 2'b11;

  reg [1:0] state, next;

  always @(posedge clk or negedge rst_n)
            if (!rst_n) state <= IDLE;
            else        state <= next;

  always @(state or dly or done or req) begin
           next = 2'bx;
           case (state)
           IDLE : if (req)       next = BBUSY;
                      else           next = IDLE;
            BBUSY: if (!done)     next = BBUSY;
                          else if ( dly) next = BWAIT;
                          else           next = BFREE;
             BWAIT: if (!dly)      next = BFREE;
                          else           next = BWAIT;
            BFREE: if (req)       next = BBUSY;
                         else           next = IDLE;
           endcase
    end

  always @(posedge clk or negedge rst_n)
           if (!rst_n) gnt <= 1'b0;
           else begin
                 gnt <= 1'b0;
                 case (next)
                       IDLE,  BFREE: ; // default outputs
                        BBUSY, BWAIT: gnt <= 1'b1;
                 endcase
        end
endmodule

 

参考资料:

吴继华.王诚《Verilog设计与验证》中的第6章:如何写好状态机

Clifford E. Cummings 《The Fundamentals of Efficient Synthesizable Finite State Machine Design using NC-Verilog and BuildGates》 http://www.sunburst-design.com/papers/

博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3