Inside an Automated State Machine

April 15, 2014

I was pleased to see that the generated code was reasonably easy to understand

Last time I looked at Fizzim, an open source tool for generating state machines for synthesis into an FPGA. If you recall, I laid out a simple state machine that put out a specific output on a trigger:

The machine has five states: Idle (the default, indicated by the double circle), S1, S1delay, S2, and S2delay. There is an output Q. Here’s the reason each state appears:

  • Idle — Wait for trigger input T
  • S1 — Trigger the output and load the counter with 5
  • S1delay — Hold the output asserted and decrement the counter to zero
  • S2 — Turn the output off and load the counter with 8
  • S2delay — Keep the output off and decrement the counter to zero

The result is that a T input causes a 6 time tick assert and then won’t retrigger again for another 9 clock ticks. You can experiment with the code in any Verilog simulator, including the online EDA Playground.

There are many options that can control the output of Fizzim, but I wanted to example some bits of the generated code to see how readable it was. Here’s the first part, which is hardly surprising:

module test (
  output reg Q,
  input wire T,
  input wire clk,
  input wire reset 

That certainly defines the module. I selected one hot encoding (that is, each state is represented by one bit). The state bits and some variables appear next:

// state bits
  idle    = 3'b000, 
  S1      = 3'b001, 
  S1delay = 3'b010, 
  S2      = 3'b011, 
  S2delay = 3'b100; 

  reg [2:0] state;
  reg [2:0] nextstate;
  reg [4:0] ctr1;
  reg [4:0] ctr2;

You can probably guess that state is the current state vector and nextstate is what will be the next state. If you aren’t too familiar with Verilog, the 3’b notation means the following are 3 binary digits. The ctr1 and crt2 variables I defined to hold the counters for S1Delay and S2Delay (the numbers in brackets define how many bits are in each; 5 bits in the case of the two counters). There is a bit of code somewhat further down that handles the state change and reset logic:

// sequential always block
  always @(posedge clk or negedge reset) begin
    if (!reset)
      state <= idle;
      state <= nextstate;

Before that, though, there’s code to work out the next state. Because of some options I set, the code will assume the next state is the same as the current state:

always @* begin
    nextstate = state; // default to hold value because implied_loopback is set
    Q = 0; // default
    case (state)
      idle   : begin
        if (T) begin
          nextstate = S1;
      S1     : begin
        Q = 1;
          nextstate = S1delay;
. . .

That makes sense. The idle state waits for a T to go to S1. However, it isn’t clear how S1Delay handles decrementing the counter if you are looking at just this block of code:

   S1delay: begin
        Q = 1;
        if (ctr1==0) begin
          nextstate = S2;

The counter decrement happens in a separate “datapath” block:

// datapath sequential always block
  always @(posedge clk or negedge reset) begin
    if (!reset) begin
      ctr1[4:0] <= 4'b0;
      ctr2[4:0] <= 4'b0;
    else begin
      ctr1[4:0] <= 0; // default
      ctr2[4:0] <= 0; // default
      case (nextstate)
        S1     : begin
          ctr1[4:0] <= 5;
     S1delay: begin
          ctr1[4:0] <= ctr1-1;
. . .

The rest of the code just handles the other states. There is also an optional piece that converts state names to text for simulation (which doesn’t work for the wave viewer on EDA Playground, but works great for Modelsim or GTKWave).

Granted, for a small state machine like this, there isn’t much motivation to use a tool like this other than the diagrams make for nice documentation. However, I was pleased to see that the generated code was reasonably easy to understand. Of course, if you really wanted a change, you ought to go change the Fizzim diagram and regenerate the output.

Fizzim will also generate VHDL, if you ask it to do so. I have a track record of looking at code generation tools, thinking they are cool, putting them away, and then hand-coding things anyway. However, as code-generation tools go, Fizzim seems pretty sane and with a little practice could even be more efficient since you can focus on the logic flow instead of the details.

I’ve been on a bit of an FPGA kick for the last few weeks talking about Zynq, test bed generation, and state machines. But I’ve been tinkering with some very cool embedded CPU toys, so I think I’ll put the FPGA toys to the back of the workbench for a little while and turn back to programming next time.