State Machines Lab
State Machines Lab
Introduction
In this lab, you will create the CNTRL_FSM submodule as part of the Calculator project (Figure
18b-1). You will also perform RTL verification on the module.
Note: You can describe this functional block either as a multi-process or single-process FSM. In
either case, the outputs should be registered. Start with the multi-process version. If time permits,
you can complete code for the single-process FSM.
Objectives
After completing this lab, you will be able to:
Write VHDL code to describe a synchronous state machine diagram
Describe a state machine by using both a multi-process and single-process approach
Procedure
NOTE: Toolwire is the default platform for running labs. Use R:\ for all directory
references.
This lab comprises two primary steps: You will write code for the FSM and verify functionality by
using RTL simulation. If time permits, you will write and verify code for a single-process FSM.
For each procedure within a primary step, there are general instructions (indicated by the
symbol). These general instructions only provide a broad outline for performing the procedure.
Below these general instructions, you will find accompanying step-by-step directions and
illustrated figures that provide more detail for performing the procedure. If you feel confident
about completing a procedure, you can skip the step-by-step directions and move on to the next
general instruction.
Note: If you are not using Toolwire to perform this lab, all software programs, files, and projects
will be located on the C:\ drive instead of R:\.
For each procedure within a primary step, there are general instructions (indicated by
the symbol). These general instructions only provide a broad outline for
performing the procedure. Below these general instructions, you will find
accompanying step-by-step directions and illustrated figures that provide more detail
for performing the procedure. If you feel confident about completing a procedure,
you can skip the step-by-step directions and move on to the next general instruction.
Open the existing My_Class_Labs project within the ISE™ software, located in
the R:\training\vhdl\labs directory.
Select Start Programs Xilinx ISE 9.1i Project Navigator to launch the Project
Navigator
By default, the ISE software should start with the last open project as the current project.
!
Important: Unless otherwise noted, you must label your project and HDL source files as
instructed, or simulation may not work properly in the Toolwire environment.
!
In the New Source dialog box, select VHDL Module, enter CNTRL_FSM in the File name
field, and click Next to open the table-based entity wizard
Enter the port data for the intended module with the information in Figure 18b-1
Note that the ports of the FSM utilize the user-defined data type DATA_FRAME, as described
in the MY_RECORD composite data type.
Between the keyword architecture and begin statements, define an enumerated type called
STATE_TYPE, with the values as shown in Figure 18b-2
Declare the signals CURR_STATE and NEXT_STATE for a multi-process FSM (or STATE for
a single process) of the type STATE_TYPE
Make certain that within the S4_DONE state, the condition to remain in that state is explicitly
coded
ADDR: The output signal ADDR needs to be incremented and tested within the
FSM, which necessitates creating an internal signal which can be updated within the
process and drive the output port within the clocked process—for example, ADDR_I.
However, you also want to avoid inferred latches in the circuit. A recommended VHDL
coding technique is to declare two internal signals that will mirror each other. One signal
(ADDR_Q, for example) is assigned within the clocked process, which keeps the logic
fully synchronous. Under normal operating conditions, it registers the value of the
internal signal ADDR_I.
That same synchronous signal drives both the physical output port in the clocked process
and the mirrored internal signal in the combinatorial process. An example of this
approached is shown in the solution in the Answers section.
To ensure that the FSM parks in the S4_DONE state at the correct point, pay careful
attention to when the ADDR_I signal is updated; that is, what its value is for a given
clock cycle.
DATA_FRAME: You will need an input port of this type to drive the output
ports of the module with the individual components (sub-elements) of the DATA_FRAME
input vector.
Create individual output assignment statements from this signal to the respective
output ports; that is, A_IN <= DATA_FRAME.A_IN ;
Each of the other modules contains an EN signal. This FSM needs to assert the
MEM_EN, ALU_EN, and COMP_EN control signals at the appropriate intervals.
After ADDR_I signal reaches 7 (“111”), the FSM should park in the S4_DONE
state until, and unless, a RESET occurs.
You can either build a testbench to verify the functionality of the FSM as a
standalone module, or you can combine one or more of the other submodules in
this level of hierarchy. In the next exercise, this will be the explicit requirement.
You can use the same testbench and input stimulus to verify either the multi-
process or single-process versions.
Select Project New Source
In the New Source dialog box, select VHDL Test Bench and enter CNTRL_FSM_TB in the
File name field. Remember to include the CALC1_PAK file
Depending on whether or not you are using any of the other submodules at this stage, your
testbench and input stimulus should focus on the following:
Are the MEM_EN, ALU_EN and COMP_EN signals asserted at the proper time?
When ADDR_I reaches its upper limit, does the FSM remain in the S4_DONE
state?
You can get started on the final exercise by using the same OPCODEs in your
testbench that will be loaded into the MEM submodule for the upcoming
SIMPLE_CALC exercise.
You recorded this in Table 16b-3 in the “Arithmetic Logic Unit” lab.
Create a free-running input clock that operates at 50 MHz
Assuming that no errors are detected (if this is not the case, you must correct all errors before
proceeding), the script completes and you can view the simulation results. You are also in the
interactive tool mode.
In the SIM Hierarchy VIEW window, select the lower-level component, which should be
labeled UUT
The window shows the signals within that module (at that level, within that region)
Select the current-state signal, which holds the value of the current-state register
Drag the current-state signal into the Waveform window. It should now be listed along with
the signals in the top level
From this point, you can restart and then rerun the simulation in interactive mode (Figure
18b-3). The literal values for CURR_STATE should be visible.
The operation of the FSM entity was straightforward. However, the steps that you took
demonstrate the basics that would be necessary for a FSM of any complexity.
Finally, you built a simple testbench to verify the functionality of the module. Verifying each
submodule before you attempt the broader simulation is recommended.
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
use work.CALC1_PAK.all;
entity CNTRL_FSM is
port ( DATA_FRAME : in MY_RECORD;
CLK : in std_logic;
RESET : in std_logic;
A_IN : out std_logic_vector(3 downto 0);
B_IN : out std_logic_vector(3 downto 0);
C_IN : out std_logic;
OP_CODE : out std_logic_vector(3 downto 0);
EXP : out std_logic_vector(3 downto 0);
ADDR : out std_logic_vector ( 2 downto 0);
COMP_EN : out std_logic;
MEM_EN : out std_logic;
ALU_EN : out std_logic );
end entity CNTRL_FSM;
ADDR <= ADDR_Q; -- used to update output port from synchronous internal signal
ADDR_I <= ADDR_Q; -- use the registered ADDR_Q to keep ADDR fully synchronous
end case;
end RTL;
END CNTRL_FSM_TB_vhd;
COMPONENT CNTRL_FSM
PORT(
DATA_FRAME : IN MY_RECORD;
CLK : IN std_logic;
RESET : IN std_logic;
A_OP : OUT std_logic_vector(3 downto 0);
B_OP : OUT std_logic_vector(3 downto 0);
C_IN : OUT std_logic;
OP_CODE : OUT std_logic_vector(3 downto 0);
EXP : OUT std_logic_vector(3 downto 0);
MEM_EN : OUT std_logic;
ALU_EN : OUT std_logic ;
COMP_EN : OUT std_logic ;
ADDR : OUT std_logic_vector(2 downto 0) );
END COMPONENT;
tb : PROCESS
BEGIN
DATA_FRAME <= ("1000", "0100","0000",'0',"0000");
wait for 100 ns;
DATA_FRAME <= ("1000", "0100","0101",'0',"0000");
END TEST;