This tutorial will provide a basic overview of state machine design using Behavioral Verilog. It will simply consist of an example block of code for a generic state machine with explanations following.
A silly little state machine is designed that, for the purpose of being generic, very briefly and simply represents a growing tree. Here is a list of the inputs and outputs, along with the state graph that the code will be representing. Following these is the module's Verilog code. The code is explained afterwards.
Inputs | |
---|---|
Plant | Planting the seed |
Water | Watering the plant |
Sun | Giving the plant sunlight |
Reset | Start over |
Outputs | |
getFruit | Getting fruit from the tree |
getSeeds | Getting more seeds from the tree |
Done | Cycle is over |
It is assumed that if no listed transition conditions are met, the state stays where its at and does not transition.
module state_machine (clock, reset, plant, water, sun, harvest, getFruit, getSeeds, done); input clock, reset, plant, water, sun, harvest; output getFruit, getSeeds, done; // stores the state reg[1:0] state; reg[1:0] next_state; // state parameters parameter seed = 2'd0; parameter sprout = 2'd1; parameter tree = 2'd2; parameter dead = 2'd3; always @(posedge clock) if (reset) state <= seed; else state <= next_state; // setting the state always @(*) case (state) seed: if (plant) next_state <= sprout; else // Keep the state the same // This is required because the Nexys 4 apparently // will reset the register if it is not given a value next_state <= state; sprout: if (water && sun) next_state <= tree; else next_state <= dead; tree: if (harvest) next_state <= seed; else // Keep the state the same // This is required because the Nexys 4 apparently // will reset the register if it is not given a value next_state <= state; default: next_state <= seed; endcase // note that the always block ends here // setting the outputs // moore outputs assign getFruit = state == tree; assign done = state == dead; //mealy output assign getSeeds = (state == tree) && harvest; endmodule
As you can see, inputs and outputs as declared in a state machine module just like they would be in any Verilog module.
reg[1:0] state creates the register that will store the state. This must have enough bits to store all the states. In this example there are 4 different states thus state only needs 2 bits.
reg[1:0] next_state creates a register that stores the next state.
parameter is similar to const in C. This simply creates a constant variable with the value given. This allows you to encode your states with names. It is not necessary to do this, but it makes it incredibly easy to manage your states. If you don't do this, you'll have to remember which state encoding corresponds to each state, and when you transition states you'll have to store a number directly like state ⇐ 2'b10
. It is highly recommended that you use parameters.
always @(posedge clock) is the block in which all the transitions occur. Note that begin
and end
are not necessary because this always block only contains one statement.
always @(*) is an always block that will always be operating. It is essentially combination logic but an easier way to code it. Setting the next state inside an always block like this allows states that don't need a clock cycle to be passed through. This will be particularly helpful in the last lab of this class, where the decode state should not take up a clock cycle.
case (state) is a switch statement. It acts just like a switch case
statement in C. case
looks at the value that is passed to it, in this example state
, and then executes the option that state
is equal to. So if state
is equal to seed
, it will execute if (plant) state ⇐ sprout;
. If state
doesn't match any of the given options, default
is executed. In this case default
is used to give the state machine an initial state even if reset
is not asserted. Case does not need a begin but it needs an endcase.
else state <= dead; under state sprout
doesn't need to be an else if
statement because there are only 2 transitions leaving that state, and the second is actually an inversion of the first using DeMorgan's Law!
default: is the case that is selected if no others are met.
The outputs are described in lines of combinational logic at the end. You can set the outputs inside the always block, but the outputs must then be made registers and its harder to find problems in your code as statements for a single output will be spread out all over.
There are multiple ways to set an output to 1 given a condition. The first output line, assign getFruit = state == tree? 1 : 0; tests state == tree
using a ternary operator. However, the statement state == tree
will return a 1 or a 0 anyway, so it can be used on its own without a ternary operator as is done in the third output, assign done = state == dead;. This can only be done on single bit outputs.
The outputs getFruit and done are both only moore outputs, they only depend on the current state. This can be seen from the fact that their logic only depends on what state
is. getSeeds, however, is a mealy output as it depends on the transition. This is done by checking the current state and the transition condition and then and'ing them together.