State Machine Coding Styles for Synthesis

Absract

前几天遇到DE2—Camera中关于状态机中的问题,不解,后询问志伟学长后才恍然大悟,所以发现状态机还得好好学学。

今天看了Clifford E. Cummings大师的论文《State Machine Coding Styles for Synthesis》,写的相当好,再结合真无双大师的博文,对学到的状态机做个总结。

Introduction

A finite state machine has the general structureshown in Figure 1.

state machine

State Machine Classification

There are two types of state machines as classified by the types of outputs generated from each.
The first is the Moore State Machine where the outputs are only a function of the present state.
The second is the Mealy State Machine where one or more of the outputs are a function of thepresent state and one or more of the inputs.

State Assignments

Guideline:make state assignment using parameters with symbolic state names.

parameter [2:0] // synopsys enum code
IDLE = 3'd0,
S1 = 3'd1,
S2 = 3'd2,
S3 = 3'd3,
ERROR = 3'd4;
Example 1 - Parameter definitions for binary encoding

也能够用`define来定义状态参数,但是`define创建的是全局定义,与`define不同,parameter属于定义他们的module。这样就允许一个design拥有多个FSM可以重用这些状态参数。

Two-Always Block State Machine

由Figure 1可以发现总共有三个模块:Next state logic,State register,Output logic.用两个always模块,所以我们可以将Next state logic和State register合一,或者State register和Output logic合一,亦或Next state logic与Output logic合一。

但综合来看,Next state logic与State register合一最合适。

/* 
 (C) OOMusou 2011 http://oomusou.cnblogs.com
 
 Filename    : simple_fsm_moore_2_always_0_cs_ns_good.v
 Synthesizer : Quartus II 8.1
 Description : 2 always block for moore fsm (GOOD)
 Release     : Jun.05,2011 1.0
 */
 
 module simple_fsm (
   clk,
   rst_n,
   w_i,
   z_o
 );
 
 input  clk;
 input  rst_n;
 input  w_i;
 output z_o;
 
 parameter IDLE = 2'b00;
 parameter S0   = 2'b01;
 parameter S1   = 2'b10;‎
 
 reg [1:0] curr_state;
 reg z_o;
 
 // state reg + next state logic
 always@(posedge clk or negedge rst_n)
   if (~rst_n) curr_state <= IDLE;
   else
     case (curr_state)
       IDLE    : if (w_i) curr_state <= S0;
                 else     curr_state <= IDLE;
       S0      : if (w_i) curr_state <= S1;
                 else     curr_state <= IDLE;
       S1      : if (w_i) curr_state <= S1;
                 else     curr_state <= IDLE;
       default :          curr_state <= IDLE;
     endcase
     
 // output logic
 always@(*)
   case (curr_state)
     IDLE    : z_o = 1'b0;
     S0      : z_o = 1'b0;
     S1      : z_o = 1'b1;
     default : z_o = 1'b0;
   endcase
   
 endmodule
 

State register和Output logic合一

/* 
 (C) OOMusou 2011 http://oomusou.cnblogs.com
 
 Filename    : simple_fsm_moore_2_always_1_cs_ol_ng.v
 Synthesizer : Quartus II 8.1
 Description : 2 always block for moore fsm (NO GOOD)
 Release     : Jun.05,2011 1.0
 */
 
 module simple_fsm (
   clk,
   rst_n,
   w_i,
   z_o
 );
 
 input  clk;
 input  rst_n;
 input  w_i;
 output z_o;
 
 parameter IDLE = 2'b00;
 parameter S0   = 2'b01;
 parameter S1   = 2'b10;
 
 reg [1:0] curr_state;
 reg [1:0] next_state;
 reg z_o;
 
 // state reg + output logic
 always@(posedge clk or negedge rst_n)
   if (~rst_n) {curr_state, z_o} <= {IDLE, 1'b0};
   else begin
     curr_state <= next_state;
     
     case (next_state)
       IDLE    : z_o <= 1'b0;
       S0      : z_o <= 1'b0;
       S1      : z_o <= 1'b1;
       default : z_o <= 1'b0;
     endcase
   end
 
 // next state logic    
 always@(*)
   case (curr_state)
     IDLE    : if (w_i) next_state = S0;
               else     next_state = IDLE;
     S0      : if (w_i) next_state = S1;
               else     next_state = IDLE;
     S1      : if (w_i) next_state = S1;
               else     next_state = IDLE;
     default :          next_state = IDLE;
   endcase    
 
 endmodule
这种写法的最大问题是:output logic要用next state去判断。 因为假如仍然使用curr_state去判断,则会延迟一个clock(这是在时序电路中,用的是nonblocking),提前使用next_state则能避免这一个延迟。这个地方很容易出错,所以不推荐这种方法。 
 
Next state logic与Output logic合一
/* 
 (C) OOMusou 2011 http://oomusou.cnblogs.com
 
 Filename    : simple_fsm_moore_2_always_2_ns_ol_ng.v
 Synthesizer : Quartus II 8.1
 Description : 2 always block for moore fsm (NO GOOD)
 Release     : Jun.05,2011 1.0
 */
 
 module simple_fsm (
   clk,
   rst_n,
   w_i,
   z_o
 );
 
 input  clk;
 input  rst_n;
 input  w_i;
 output z_o;
 
 parameter IDLE = 2'b00;
 parameter S0   = 2'b01;
 parameter S1   = 2'b10;
 
 reg [1:0] curr_state;
 reg [1:0] next_state;
 reg z_o;
 
 // state reg
 always@(posedge clk or negedge rst_n)
   if (~rst_n) curr_state <= IDLE;
   else        curr_state <= next_state;
     
 // next state logic + output logic 
 always@(*)
   case (curr_state)
     IDLE    : if (w_i)   {next_state, z_o} = {S0  , 1'b0};
               else       {next_state, z_o} = {IDLE, 1'b0};
     S0      : if (w_i)   {next_state, z_o} = {S1  , 1'b0};
               else       {next_state, z_o} = {IDLE, 1'b0};
     S1      : if (w_i)   {next_state, z_o} = {S1  , 1'b1}; // always output 1'b1
               else       {next_state, z_o} = {IDLE, 1'b1}; // always output 1'b1
     default :            {next_state, z_o} = {IDLE, 1'b0};
   endcase
 
 endmodule

这种方法增加了程序的复杂度,所以也不推荐。

One-Always Block State Machine

这种FSM在修改和调试上比two-always困难。

always@(posedge clk or negedge rst_n)
  if (~rst_n) {curr_state, z_o} <= {IDLE, 1'b0};
  else
    case (curr_state)
      IDLE    : if (w_i) {curr_state, z_o} <= {S0,   1'b0};
                else     {curr_state, z_o} <= {IDLE, 1'b0};
      S0      : if (w_i) {curr_state, z_o} <= {S1,   1'b1}; //?
                else     {curr_state, z_o} <= {IDLE, 1'b0};
      S1      : if (w_i) {curr_state, z_o} <= {S1,   1'b1};
                else     {curr_state, z_o} <= {IDLE, 1'b0}; //?
      default :          {curr_state, z_o} <= {IDLE, 1'b0};
    endcase
 

根据前面的经验,output logic只与当前的state有关,并且是组合逻辑。

S0 : if (w_i) {curr_state, z_o} <= {S1, 1'b1}

在S0中, 输出必须提前一个clk让z_o等于1,因为这是在nonblocking中,不然就不和当前state有关,而有一个delay了。

同理在else {curr_state, z_o} <= {IDLE, 1'b0};中也得提前为0.

Note: output assignments inside of a sequential always block cannot be Mealy outputs.

推荐的FSM写法

比较推荐使用前面所说的第一种two-always的写法,并且给output logic使用时序电路,增加一个延迟,而非采用逻辑电路。

moore fsm

/* 
 (C) OOMusou 2011 http://oomusou.cnblogs.com
 
 Filename    : simple_fsm_moore_2_always_0_cs_ns_good.v
 Synthesizer : Quartus II 8.1
 Description : 2 always block for moore fsm (GOOD)
 Release     : Jun.05,2011 1.0
 */
 
 module simple_fsm (
   clk,
   rst_n,
   w_i,
   z_o
 );
 
 input  clk;
 input  rst_n;
 input  w_i;
 output z_o;
 
 parameter IDLE = 2'b00;
 parameter S0   = 2'b01;
 parameter S1   = 2'b10;
 
 reg [1:0] curr_state;
 reg z_o;
 
 // state reg + next state logic
 always@(posedge clk or negedge rst_n)
   if (~rst_n) curr_state <= IDLE;
   else
     case (curr_state)
       IDLE    : if (w_i) curr_state <= S0;
                 else     curr_state <= IDLE;
       S0      : if (w_i) curr_state <= S1;
                 else     curr_state <= IDLE;
       S1      : if (w_i) curr_state <= S1;
                 else     curr_state <= IDLE;
       default :          curr_state <= IDLE;
     endcase
     
 // output logic
 always@(posedge clk or negedge rst_n)
   if (~rst_n)
     z_o <= 1'b0;
   else
     case (curr_state)
       IDLE    : z_o <= 1'b0;
       S0      : z_o <= 1'b0;
       S1      : z_o <= 1'b1;
       default : z_o <= 1'b0;
     endcase
   
 endmodule

由于output logic采用的是时序电路,所以curr_state在两个always中传输过程中增加了一个delay。当上面// state reg + next state logic 模块中curr_state为S0时,output logic中的curr_state仍旧停留在IDLE中。所以延迟了一个clk.

Reference

真OO无双:http://www.cnblogs.com/oomusou/archive/2011/06/05/fsm_coding_style.html

Clifford E. Cummings,State Machine Coding Styles for Synthesis.

posted on 2012-05-16 21:34  宕夏  阅读(926)  评论(0编辑  收藏  举报

导航