State machines are one of the basic building blocks of FPGA design, I commonly use finite state machines to execute logic such as custom serial interfaces, input signal debouncing, and event timing as a few examples.
Finite state machines (FSM) in particular, are a representation of sequential logic circuits/systems, meaning that their output depends on both the current inputs and the current state of the system. FSMs are made up of a set states that take a certain set of inputs, and provides outputs based on set rules for how to move from state to state.
The core concept of FSMs remain the same in FPGA development regardless of which HDL language you are using: VHDL or Verilog. However, if you only know one language, the other can look quite alien at first glance. This tutorial will demonstrate the same simple finite state machine in both Verilog and VHDL to demonstrate the basic syntactical and mechanical differences between the two HDL languages.
The idea of this project is to hopefully act as an initial translation guide from one HDL language to the other for users as a sort of quick start in picking up the opposite one. Verilog just so happened to be what my digital logic class at university was teaching in, but when I got out into the real world, the need suddenly arose for me to take on projects in VHDL. So I understand the need to be able to pick up the opposite HDL language quickly.
Block Diagram of State MachineFor demonstration purposes I threw together this very basic FSM, that simply sets the first output signal high, then checks the current state of the given input signal. Depending on if the given input signal is high or low, the state machine sets one of two output signals high to indicate the current state of the input signal. After setting the output signal representing the current state of the input signal, the state machine sets another output signal high indicating that it has completed.
The state machine will stay in this "done" state until the reset signal resets it.
Translating the FSM into its Verilog version, we start with the keyword module at the beginning, followed by the instance name of the state machine (which is verilog_sm in this case). The term endmodule indicates the end of the code specific to the state machine in a file, so all of the code for the state machine goes in-between these two lines.
module verilog_sm();
endmodule
In the parenthesis after the instance name of the state machine is where all of the inputs and outputs of the state machine are being declared. As you can see, we start with the clock and reset signals, which are both inputs to the state machine declared with the keyword input.
The first output signal, output_sig0, is an output that the state machine itself will be assigning a value to. Thus, it also needs to be declared with the keyword reg after the keyword output since it is being translated as a register in the hardware a value is being set in. This also applies to the other output signals, output_sig1,output_sig2, and done_sig.
module verilog_sm(
input clk,
input reset,
output reg output_sig0,
input input_sig0,
output reg output_sig1,
output reg output_sig2,
output reg done_sig
);
endmodule
After the input and output signals are declared in the parenthesis, any other signals needed for the logic in the FSM and are contained solely within the FSM need to be declared.
The first thing is the register to hold the value of the next state to move to in the FSM. I called it state_register for clarity. Since it's not an input or output to the Verilog module, it only needs the keyword reg in its declaration. If the signal were not having a specific value the FSM was setting it to, the keyword used to declare it would instead be wire. Also, since there are a total of seven states (as seen in the block diagram above) that means with register will need to hold a value ranging fro zero to 6.
How many bits wide a register is directly correlates to how great of a value (in binary) it can hold. For example, a register that is only one bit wide can only represent up to decimal value one since a single bit in binary can only be a zero or a one. A two-bit wide register can represent up to decimal value three, since 00 = 0, 01 = 1, 10 = 2, and 11 = 3. A three-bit wide register can represent up to decimal value seven, since 000 = 0, 001 = 1, 010 = 2, 011 = 3, 100 = 4, 101 = 5, 110 = 6, and 111 = 7. The mathematical equation to represent this is: 2^(number of bits) = X, where the largest decimal value held = X - 1.
So going back to the three-bit register, 2^3 = 8 and the largest decimal value that can be held is 8 - 1 = 7. Again, with the two-bit register, 2^2 = 4 and the largest decimal value that can be held is 4 - 1 = 3. And the single bit register, 2^1 = 2, with the largest value that can be held is 2 - 1 = 1.
In this demonstration, since the largest value the state register will need to hold is 6, a 3-bit register is required. While a larger register could be used, it is wise to use the minimum size possible to conserve hardware resources in the FPGA, as well as minimize the number of possible unused states. To make the register 3 bits wide in Verilog, the size is declared between the keyword reg and the register name using brackets and a colon, so in this case:
reg [2:0] state_register;
After the declaration of internal signals of the FSM, the constants assigned to each state in the state machine are declared. Using the keyword parameter, I'm assigning constant values to a state name. For instance, state number zero, I'm calling init since it's the initial state the FSM is starting in. This is more of a personal style choice, it is perfectly fine to skip this and simply use the decimal or binary values for each state. I have just found that it makes the code a lot more readable to future me to have state names that hint at the high level function of the state.
module verilog_sm(
input clk,
input reset,
output reg output_sig0,
input input_sig0,
output reg output_sig1,
output reg output_sig2,
output reg done_sig
);
// declare the register to hold the state number for the FSM
// 7 states total in the FSM, so needs to be at least 3 bits
reg [2:0] state_register;
// FSM states
parameter init = 3'd0;
parameter set_out0_high = 3'd1;
parameter wait_one_state = 3'd2;
parameter check_in0 = 3'd3;
parameter set_out1_high = 3'd4;
parameter set_out2_high = 3'd5;
parameter set_done_high = 3'd6;
endmodule
After making all of the necessary declarations, we're read to get into the meat & potatoes of the state machine functionality in Verilog. Using an always @ block with the input clock and reset signals in the sensitivity list, it starts with an if-else statement that handles setting the state register value to 0 to start it back in the initial state upon reset.
The else part of the if-else statement contains a case statement that defines each state in the state register according to its decimal value. As you can probably start to notice, Verilog uses the keywords begin and end to enclose logic within certain elements. For instance there is the begin and end to enclose the if-else statement within the always @ block. Then both the if and the else have their own begin and end to enclose their own logic, such as the case statement in the else statement.
always @ (posedge clk or negedge reset)
begin
if (reset == 1'b0)
begin
end
else
begin
case (state_register)
endcase
end
end
So our Verilog FSM looks like this so far:
module verilog_sm(
input clk,
input reset,
output reg output_sig0,
input input_sig0,
output reg output_sig1,
output reg output_sig2,
output reg done_sig
);
// declare the register to hold the state number for the FSM
// 7 states total in the FSM, so needs to be at least 3 bits
reg [2:0] state_register;
// FSM states
parameter init = 3'd0;
parameter set_out0_high = 3'd1;
parameter wait_one_state = 3'd2;
parameter check_in0 = 3'd3;
parameter set_out1_high = 3'd4;
parameter set_out2_high = 3'd5;
parameter set_done_high = 3'd6;
always @ (posedge clk or negedge reset)
begin
if (reset == 1'b0)
begin
// reset throws the FSM back to the init state
state_register <= init;
end
else
begin
case (state_register)
// define each state here
init :
set_out0_high :
wait_one_state :
check_in0 :
set_out1_high :
set_out2_high :
set_done_high :
endcase
end
end
endmodule
As mentioned before, the init state is the initial state the FSM is starting in. So it performs actions such as clearing all of the output signals by setting them to zero. Since the next state according to the block diagram above is state one, the init state also sets the state register value to one. Thus on the next clock cycle, since the state register value will be set to one, it will go to state value one in the case statement (assuming the reset signal is not causing a reset).
State one sets the first output signal to high, thus the name set_out1_high, before setting the state register value to the next state according to the block diagram above, which is state value 2.
State value 2 is effectively a no operation state to allow for any outside logic to see the output signal set by the previous state one. Thus its only action is to set state register value to the next state according to the block diagram above, state value 3.
State value 3 is where the current value of the input signal is being checked, and based upon its value, the state machine will move to the next state to set either output signal 1 high (state value 4) or output signal 2 high (state value 5).
State values 4 and 5 are setting output signal 1 and output signal 2 high respectively, the next state for either of them is the last state in the FSM (state value 6).
Finally the last state will set the done signal high then remain in that state until the next reset by continually setting the state register to its own value, 6.
This leaves our Verilog state machine looking like this:
module verilog_sm(
input clk,
input reset,
output reg output_sig0,
input input_sig0,
output reg output_sig1,
output reg output_sig2,
output reg done_sig
);
reg [2:0] state_register;
// FSM states
parameter init = 3'd0;
parameter set_out0_high = 3'd1;
parameter wait_one_state = 3'd2;
parameter check_in0 = 3'd3;
parameter set_out1_high = 3'd4;
parameter set_out2_high = 3'd5;
parameter set_done_high = 3'd6;
always @ (posedge clk or negedge reset)
begin
if (reset == 1'b0)
begin
output_sig0 <= 1'b0;
output_sig1 <= 1'b0;
output_sig2 <= 1'b0;
state_register <= init;
end
else
begin
case (state_register)
init : //state 0
begin
output_sig0 <= 1'b0;
output_sig1 <= 1'b0;
output_sig2 <= 1'b0;
done_sig <= 1'b0;
state_register <= set_out0_high;
end
set_out0_high : //state 1
begin
output_sig0 <= 1'b1;
state_register <= wait_one_state;
end
wait_one_state : // state 2
begin
state_register <= check_in0;
end
check_in0 : //state 3
begin
if (input_sig0 == 1'b1)
begin
state_register <= set_out1_high;
end
else
begin
state_register <= set_out2_high;
end
end
set_out1_high : //state 4
begin
output_sig1 <= 1'b1;
state_register <= set_done_high;
end
set_out2_high : //state 5
begin
output_sig2 <= 1'b1;
state_register <= set_done_high;
end
set_done_high : //state 6
begin
done_sig <= 1'b1;
state_register <= set_done_high;
end
endcase
end
end
endmodule
VHDL State MachineThe VHDL version of this simple state machine looks quite different, but overall, executes the same functionality as its Verilog counterpart of the function block diagram. To help with correlation between the languages, I've maintained signal names with common elements such as signal names, state names, and register names.
Every VHDL file starts with the entity. The entity is where all of the external inputs and outputs of the state machine are defined. I titled this state machine vhdl_sm the title verilog_sm that I gave the Verilog state machine module. The entity block must be blocked at the end with end [entity name];. So in this case endvhdl_sm;
The port are listed listed in parenthesis after the keyword Port in the VDHL entity block. Followed by ": [in or out] signaltype;"
The two main data types your VHDL port signals can be are:
- STD_LOGIC = a single bit scalar that can represent 0, 1, Z (high impedance), W (weak, unreadable signal value), L (weak 0, needs a pulldown), H (weak 1, needs a pullup), - (don't care), U (uninitialized), or X (unknown, multiple drivers)
- STD_LOGIC_VECTOR = an array of STD_LOGIC values to represent a single larger value with a certain index. It is a vector of STD_LOGIC elements.
Since all of the input and output signals of this state machine are only one bit wide, they are all of the type STD_LOGIC:
entity vhdl_sm is
Port (clk : in STD_LOGIC;
reset : in STD_LOGIC;
output_sig0 : out STD_LOGIC;
input_sig0 : out STD_LOGIC;
output_sig1 : out STD_LOGIC;
output_sig2 : out STD_LOGIC;
done_sig : out STD_LOGIC );
end vhdl_sm;
After the VHDL entity block, is the VHDL architecture to defined the actual logic of the VHDL file, which in our case is the state machine. The VHDL architecture is given a name and is blocked at the end with end [architecture name];. In this case, I named the VHDL architecture FSM, and the end is blocked by end FSM;. It is also tied to the entity block by architecture [architecture name]of[entity name] is in its first line.
architecture FSM of vhdl_sm is
begin
end FSM;
After the declaration line of the VHDL architecture and before the begin specifier, the states of our FSM with type state_register is ([list of states]);
The signal state is then declared as the state_register type with the following line signal state : state_register;. This is in contrast to the way we handled the state register in Verilog where we assigned a constant decimal value to each state and had to make the register wide enough to hold the maximum decimal value. Here, we made state_register its own type with the possible values being the state names themselves.
Then between the begin and end FSM; lines, the actual logic of our FSM is placed.
architecture FSM of vhdl_sm is
type state_register is (init, set_out0_high, wait_one_state, check_in0, set_out1_high, set_out2_high, set_done_high);
signal state : state_register;
begin
process(clk, reset)
begin
end process;
end FSM;
Replacing the always @ (posedge clk or negedge reset) block in the Verilog code is the process(clk, reset) block.
Again, if the reset signal is active (which it is an active low reset for this demo) this will set the state machine to the init state. Then the rest of the time, at every rising clock edge, a case statement of all possible states in the FSM occurs where the logic for each state is placed in each case.
Each state (ie case) sets/checks its respective values, then sets the state register to what the next state should be for the next rising clock edge. All following the exact same logic as before from the functional block diagram.
entity vhdl_sm is
Port (clk : in STD_LOGIC;
reset : in STD_LOGIC;
output_sig0 : out STD_LOGIC;
input_sig0 : in STD_LOGIC;
output_sig1 : out STD_LOGIC;
output_sig2 : out STD_LOGIC;
done_sig : out STD_LOGIC );
end vhdl_sm;
architecture FSM of vhdl_sm is
type state_register is (init, set_out0_high, wait_one_state, check_in0, set_out1_high, set_out2_high, set_done_high);
signal state : state_register;
begin
process(clk, reset)
begin
if (reset = '0') then
state <= init;
elsif rising_edge(clk) then
case state is
when init =>
output_sig0 <= '0';
output_sig1 <= '0';
output_sig2 <= '0';
done_sig <= '0';
state <= set_out0_high;
when set_out0_high =>
output_sig0 <= '1';
state <= wait_one_state;
when wait_one_state =>
state <= check_in0;
when check_in0 =>
if (input_sig0 = '1') then
state <= set_out1_high;
else
state <= set_out2_high;
end if;
when set_out1_high =>
output_sig1 <= '1';
state <= set_done_high;
when set_out2_high =>
output_sig2 <= '1';
state <= set_done_high;
when set_done_high =>
done_sig <= '1';
state <= set_done_high;
end case;
end if;
end process;
end FSM;
ConclusionNaturally, the Verilog and VHDL code written here is by far not the only way to code a finite state machine in either language. There are endless circumstances and reasons to code state machines different in both Verilog and VHDL, but that is a whole other much longer post. Hopefully this helps any of you stuck trying to break the ice from one language to the other!
Comments