FSM I

Ryan Robucci

1. State Machines Basics

Mealy Machine

gen-mealy.svg

Moore Machine

gen-moore.svg

2. References

3. Implementation of Mealy and Moore Machines

20240326190932.png

4. State-Machine Implementation

20240326191122.png

  • All Finite State Machines can be partitioned into a combinatorial and sequential parts
  • Often these two pieces are coded in separate code blocks, but they may also be combined sometimes
  • Note: some simple register-specific operations such a synchronous reset are coded in either the combination or the sequential blocks, but are often coded in the sequential block to indicate selection of register configuration. An asynchronous clear should be part of the sequential block.

5. State-Machine Verilog Implementation

module FSM (clk, clear, x,y);

input logic clk, clear;
input logic [2:0] x;
output logic [1:0] y;

// Declare the symbolic names for states
          //Verilog: localparam [6:0] S1 = 7'b0000001,S2 = 7'b0000010, S7 = 7'b1000000;
  typedef enum int {S0,S1,S2} state_t;



// Declare current state and next state variables
  //Verilog: reg [2:0] CS,NS;
  state_t CS,NS;


always_ff @ (posedge clk or posedge clear) begin
  if (clear == 1'b1) CS <= S0;
  else CS <= NS;
end

always_comb begin //always @ (CS, x)
  case (CS)
    S0 : begin
         y = 2'b00;
         if (x[2] && ~x[1] && x[0])
           NS = S1;
         else if (x[2] && x[1] && ~x[0])
           NS = S2;
         else
           NS = S0;
    end
    S1 : begin
         y = 2'b10;

    ...
end

6. Mealy Implementation


module FSM (clk, clear, x,y);

input logic clk, clear;
input logic [2:0] x;
output logic [1:0] y;

// States
typedef enum int {S0,S1,S2} state_t;


// Declare current state and next state variables
state_t CS,NS;


always_ff @ (posedge CLOCK or posedge clear) begin
  if (clear == 1'b1) CS <= S1;
  else CS <= NS;
end

always_comb begin //always @ (CS, x)
  case (CS)
    S1 : begin
         y = {x[2] , ~x[1] && x[0]};
         if (x[2] && ~x[1] && x[0])
           NS = S2;
         else if (x[2] && x[1] && ~x[0])
           NS = S4;
         else
           NS = S1;
    end
    S2 : begin
         y = 2'b10;

    ...


  • every output bit must be well-defined for every case and input to avoid latches
  • for NS, every output bit is set in every case to avoid latches

7. SystemVerilog FSM State Register

Verilog

reg [7:0] CS,NS; //reg!=register
localparam S_init   = 8'b00000000;
localparam S_start0 = 8'b00000001;
localparam S_start1 = 8'b00000010;

SystemVerilog

enum int {S_init, S_start0,S_start1} state_t;
state_t CS,NS;

SystemVerilog enum:

  • meaningful enum symbols displayed in waveform viewers and loggers
  • Symbol accessed as string through member function name():

    $strobe("State %s %0d",CS.name(),CS);
    
  • When automatic FSM state reencoding is used, shouldn't put much human effort into providing encodings
    • using localparam to provide state encodings requires significant effort and careful use
    • later: discuss that with default settings, many synthesizers replace user-provided encodings for FSM states
    • therefore use of enum int seems appropriate when finally using synthesizer-generated encodings

8. "Registered Output Moore" and "Registered Output Mealy" Machines

🛰️ Registered Output FSM

  • FSM models generalize sequential circuits
  • registered-output logic is common and has advantages and disadvantages to be discussed later
  • learn code design patterns/styles for registered-output-logic

reg_output20240326194726.png

  • Additionally, code can reuse previous outputs for future output and state updates, meaning the new registers are part of the state of the system
  • In code, you can update outputs as needed in a given state and retain values by default if not assigned

20240326194805.png

  • ✏️ Note Formal Size of State Considering any output logic register to be part of the state (serving as "extended state register"), a "Registered Output Mealy" machine is technically a Moore Machine

9. Single Always Block Style for Registered Output Logic (Registered-Output Mealy)

always_ff @ (posedge CLOCK or posedge CLEAR) begin: FSM
  logic _temp;

  if (CLEAR == 1'b1) begin
    CS <= S1;
  end else begin 
    case (CS) 
      S1 : begin 
        _temp = ~x[1];                     //intermediate logic (blocking assignment)
        y  <= {x[2] , _temp & x[0]};       //register intent (non-blocking assignment)
        if (x[2] && _temp && x[0])  
          CS <= S2;                        //register intent (non-blocking assignment)
        else if (x[2] && x[1] && ~x[0]) 
          CS <= S4; 
        else 
          CS <= S1;
      end
      S2 : begin 
          y  <= {x[1] && ~x[1] , x[0]};   
         . 
         . 
         . 
  • Embedded combination circuit signal assignments ended with blocking assignment and thus encode "immediate" effect. All consumers of blocking assignment results should be within this code block. Even within the block, no consumer should rely on a value assigned from a blocking statement in a previous trigger event.
  • Remember, any variable written to inside an edge-triggered block can become a register regardless of the use of blocking or non-blocking…consider every output variable, combinational and sequential, in every case and branch of decision tree and make sure assignments are always made to avoid latches

10. Coding Style for State Machines

🛰️ Registered Output with Three-Always Block Style

  • Registered output can cause “cycle delays” so some output transitions need to be coded along with the state transition into a state rather than with the state they are supposed to coincide with (discuss output on prev. slide)
  • Sometimes this feels like you’re coding outputs in the “previous state” or coding output ahead of time to account for register delay. I refer to it as coding the output along with the state transition it coincides with. This leads to additional lines of code as you need to code each output logic possibly for every transition to a state rather than once per state
  • To avoid this “code bloat”, yet another approach is to code the registered outputs in a separate block. This leads to three blocks:
    • Combinatorial Next State Logic along with any combinatorial outputs
    • Sequential State Register
    • Sequential Registered Outputs according to the destination (next) state from the combinatorial Block.
  • Good contrasting examples can be found here: http://www.sunburst-design.com/papers/CummingsSNUG2003SJ_SystemVerilogFSM.pdf

11. Example State-Machine: An Arbitrator

Block Diagram

gen-arb.svg

Example Operation Timing Diagram

  • Assume peripherals need a start signal for two-clock cycles
    • in the next cycle they present busy as low
    • then in the following cycle after they present busy as high and hold it until it coincides with a new result

arb-wave.svg

Statemachine

  • Using more states simplifies output logic

20240326195939.png

always_comb begin
//defaults
startA=0;
startB=0;
...
unique0 case (CS)
  STARTA0: startA=1;
  STARTA1: startA=1;
  STARTB0: startB=1;
  STARTB1: startB=1;
endcase
...
  • Using fewer states requires more output logic and, in the code shown effectively embeds extended state information in additional registers

fewer_states.svg

always_ff @ (posedge clk) begin
   if (sel_capture_en) begin
     sel_capture <=sel;
   end
end

always_comb begin
  //defaults
  startA=0;
  startB=0;
  sel_capture_en = 0; 
  ...
  unique0 case (CS)
    INIT:   sel_capture_en = 1;
    START0: begin startA=~sel_capture; startB=sel_capture; end
    START1: begin startA=~sel_capture; startB=sel_capture; end
  endcase
  ...

Code (using registered Outputs With Single Always Block)

module control_with_state_machine2(
   input  logic clk,
   input  logic rst,
   output logic startA,     // start signal to   peripheral  A
   output logic startB,     // start signal to   peripheral  B
   input  logic busyA,      // busy  signal from peripheral  A
   input  logic busyB,      // busy  signal from peripheral  B
   output logic ready,      // ready signal to   primary
   input  logic sel0A1B,    // peripheral select
   input  logic start       // go    signal from primary
);

enum int {S_init, S_start0,S_start1} state_t
state_t CS,NS;


always_ff @ (posedge clk) begin
  if (rst == 1)
    CS<=S_init;
  else
    case (CS)
    S_init: begin
      if (start == 1) begin
        startA <= ~sel_0A1B;
        startB <=  sel_0A1B;
        sel_0A1B_saved<= sel_0A1B;
        CS<=S_start0;
      end else begin
        startA <= 0;
        startB <= 0;
        CS<=S_init;
      end
    end
    S_start0: begin
        //startA <= startA;
        //startB <= startB;
       CS<=S_start1;
    end
    S_start1: begin
        startA <= 0;
        startB <= 0;
        CS<=S_wait_for_result;
    end
    S_wait_for_result: begin
       automatic bit [15:0] _result16;
       startA <= 0;
       startB <= 0;
       if (~sel_0A1B_saved && ~busyA || ~sel_0A1B_saved && ~busyB) begin
          startCS<=S_result_H;
          if (~sel_0A1B_saved) begin
             _result16 = resultA;
          end else begin
             _result16 = resultB;
          end
          result <= {result[31:16],_result16}
       end
    end
    S_result_H: begin
       automatic bit [15:0] _result16; 
       if (~sel_0A1B_saved) begin
          _result16 = resultA;
       end else begin
          _result16 = resultB;
       end
       result <= {_result16,result[15:0]}
       ready <= 1;
    end
    endcase
end
endmodule

NOTE FSM to tackle Design Complexity

  • this is a good point to consider why we build state machines and coding hardware HDL vs software
  • a key advantage of a state machine for a logic designer is that the complexity of a large task can be broken down into smaller problems represented by the work done in each state
  • unfortunately the hardware designer must be critical of the synthesizer, because the value of hardware itself is in optimization more often than in software which is more often focused on function
  • this splits the mind of the designer and makes the overall design task more challenging and time-consuming
  • software optimization tools are mature and displace the need for humans to spend time on optimizations, while hardware design tools are maturing

Internal Timing Diagram for FSM

Place holder for in-class timing diagram. Key concepts:

  • Identification of implied combinatorial signals including register inputs (outputs and state (NS))
  • Arrival of input changes versus response timing
  • Coincidence of Code and Output Timing

timing-diagram-fsm.svg

12. Registered Outputs 3-Always-Block Style

module control_with_state_machine2(
   input  logic clk,
   input  logic rst,
   output logic startA,     //start signal to peripheral A
   output logic startB,     //start signal to peripheral B
   input  logic busyA,      //busy signal from peripheral A
   input  logic busyB,      //busy signal from peripheral B
   output logic ready,      //ready signal to primary
   input  logic sel_0A1B,    //go signal from primary
   input  logic start        //go signal from primary
);

enum int {S_init, S_start0,S_start1} state_t
state_t CS,NS;

assign ready = ~(sel_0A1B?busyB_n:busyA_n); // extra combinational logic outside state machine

  always_ff @ (posedge clk) begin
    if (rst == 1)
      CS<=S_init;
    else
      CS<=NS;
  end

  always_comb begin
     NS = CS;
     case (CS)
    S_init: begin
       if (go) begin
          NS = S_start0;
       end
    end
    S_start0:
      NS = S_start1;
    S_start1:
      NS = S_init;
      endcase
  end

  always_ff @ (posedge clk) begin
     case (NS)
       S_init:
         startA <= 0;   
         startB <= 0;   
       S_start0:
         startA <= ~selAnB;
         startB <= selAnB;
       S_start1:
         startA <= startA;  
         startB <= startB;  
      endcase
  end


endmodule
  • also need to consider appropriate resets and defaults logic

13. When is registered-output not appropriate?

  • Note that the specification outlined in the second following timing diagram could not be satisfied by registered output logic,
    • since values are passed through the interface intra-cycle, (within the same cycle, rather than only from one cycle to the next)

arb-wave.svg

arb-wave-comb.svg

14. Review: state variables

  • Explicit States are stored in the (primary) state register, but other registers for variables can exist serving as extended state variables
  • In-class examples given related to a controller, but more registers will be used later for complex computational machines
  • Technically, every register in a digital system is a part of the state. What variables you decide to think of as state in your state diagram and what is coded in the CS register and used in the case statement is up to you.
  • When there are many similar states, sometimes combining them in code and adding a register for a variable makes sense. This can reduce the state decoding and state logic and may make the code more maintainable and easier to read. however…
  • Testing plans must be considered accordingly.
    • More explicitly specified states can be favored for testing plans

15. Review

  • FSM Models generalize synchronous sequential logic circuits
  • Mealy Machine vs Moore Machine
  • Register-Output Logic
    • same-cycle/intra-cycle I/O transfer requires combinational logic pathway
  • Primary State Register vs Extended State Registers
    • Internally embedded state registers and output registers can extend the system state space
  • FSM coding in SystemVerilog

Author: Dr. Ryan Robucci

Created: 2025-09-25 Thu 13:00

Validate