Finite State Machines (FSM), are very important in hardware design. They represent systems that transition through a set of internal states. These transitions are defined in terms of both external inputs and the current internal state. A change to an external input causes a change to the internal state. State changes sometimes also trigger an output change.
Most interesting software and hardware systems involve finite state machines in some form. Most often finite state machines act as controllers for a digital sub-system. Example uses include modules implementing communication protocols, button de-bounce, and video output controllers. In fact, even devices as complex as a CPU or a microcontroller (MCU) is often simply a very large and complex finite state machine.
For this article I am going to create an extremely simple FSM to illustrate the concept. There will be only two states: On and Off, and the circuit will start in the Off state. The current state will directly drive an output connected to an LED. The center pushbutton will serve as the external input. Pushing the button will update the state between on and off – in other words it will toggle the LED. Very simple indeed, but it will be sufficient to illustrate the basic features of an FSM.
Design Constraints
In the constraints file, I uncomment the following signals: clk, sw[0], led[0], and btnC. For convenience I will rename sw[0] to rst and led[0] to led. Below is the complete constraints (XDC) file for the project.
## This file is a general .xdc for the Basys3 rev B board
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project
## Clock signal
set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk]
## Switches
set_property PACKAGE_PIN V17 [get_ports rst]
set_property IOSTANDARD LVCMOS33 [get_ports rst]
## LEDs
set_property PACKAGE_PIN U16 [get_ports led]
set_property IOSTANDARD LVCMOS33 [get_ports led]
##Buttons
set_property PACKAGE_PIN U18 [get_ports btnC]
set_property IOSTANDARD LVCMOS33 [get_ports btnC]
## Configuration options, can be used for all designs
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]
If you’ve been reading previous posts, for example my post on Pulse Width Modulation, you will have noted that I very often use Vivado Behavioral Simulations along with a test-bench to validate my designs. There are many good reasons to prefer simulation over hardware synthesis during development. I touch on some of them in Test Bench Data Files, and Test Bench for Verilog Behavioral Simulation.
However for this project I will not do that. Instead I will synthesize this project directly on the Basys-3 hardware. Why? For reasons that will become clear later I want to be able to use the pushbutton interactively and observe the results.
Code Details
There are a few traits that all, or nearly all, FSM’s have in common. Those are:
- State definitions
- State variables
- Logic for updating the current state
- Logic for calculating the next state
Let’s see how that looks in Verilog. Starting below on lines 9 and 10 you can see that I define two states: state_off and state_on using localparam. And then on lines 13 and 14 I declare two state variables as reg type: rState and rNextState. These registers hold the current and next state values respectively.
module fsm(
input clk,
input rst,
input btnC,
output led
);
// state declarations
localparam state_off = 0;
localparam state_on = 1;
// state variables
reg rState;
reg rNextState;
// sequential logic to update the state register
always @(posedge clk, posedge rst)
begin
if (rst)
rState <= state_off;
else
rState <= rNextState;
end
// Combinational logic to calculate the next state
always @*
begin
rNextState= rState;
if (btnC) begin
case (rState)
state_off:
rNextState= state_on;
default:
rNextState= state_off;
endcase
end
end
// output assignment
assign led = rState;
endmodule
Starting on line 17 is an always block that defines the sequential logic used to update the current state (rState). The logic in this case is quite simple. If rst is raised the current state is set to state_off. Otherwise, on each clk tick we set the current state (rState) to the value of the next state (rNextState). Remember that we normally use non-blocking assignments in sequential logic.
Then on line 26 is the start of another always block. This block defines combinational logic which computes the next state based on the current state and any external inputs. Again the logic is quite simple. The next state defaults to the current state. However, if the center button is pressed (btnC), then next state is set to state_on if the current state is state_off. Otherwise the next state is set to state_off.
And finally on line 42 we have a continuous assignment that connects the current state (rState) to our LED output.
You might be wondering why I used default instead of having a case for state_on. The answer is two-fold. First, in this project we have only two states, so if rState is not state_off, then it must be state_on. The second reason has to do with ensuring correct and efficient Verilog synthesis for case statements. We need to ensure that we handle every possible input value in our case statements, and one safe way of doing that is to always make sure that we include a default case.
Why does the LED flicker?
If you go ahead and run this project on hardware, you may notice something interesting. As you press the center pushbutton the LED may flicker. Why is that? The answer has to do with something called contact bounce. Buttons are mechanical devices and as such there are small vibrations that can take place when the mechanical switch opens or closes. As the vibrations occur, the contacts may open and close rapidly introducing noise on the output. These vibrations settle out quickly, typically within a few milliseconds.
However this noise it very important if you have logic, like a state machine, that is driven by these inputs. It is precisely the contact noise on our button input that causes the LED to flicker. And this is why I chose not to simulate this project. A simulation models ideal switch behavior and so will not produce contact bounce. I wanted to experience contact bound in order to illustrate the problem. Properly dealing with contact bounce is a real-world practicality for hardware and software designers.
There are a number of strategies for addressing contact bounce. You may or may not be surprised to know that FSM’s are one of the solutions often employed to “debounce” inputs from mechanical switches, buttons, relays, etc.
In the meantime, there is a nice article on dealing with contact bounce here. And this is an important enough topic that I will be covering it in some detail in an upcoming post.
And that’s it for now. As always, the source code for this project is on github.
Please Like and Share these posts on social media. And remember that if you have feedback, questions or suggestions regarding this post, please leave a comment!