Inside an Automated State Machine
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),
S2delay. There is an output
Q. Here's the reason each state appears:
Idle— Wait for trigger input
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 parameter 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
crt2 variables I defined to hold the counters for
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; else state <= nextstate; end
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; end end S1 : begin Q = 1; begin nextstate = S1delay; end end . . .
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; end end
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; end else begin ctr1[4:0] <= 0; // default ctr2[4:0] <= 0; // default case (nextstate) S1 : begin ctr1[4:0] <= 5; end S1delay: begin ctr1[4:0] <= ctr1-1; end . . .
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.