From Hamsterworks Wiki!
Please make sure that you have completed the earlier modules of FPGA course.
Having problems with your code? Do I need to change something? Want a reference solution to the project? Email me at "email@example.com", after removing the "nospam-" bit. I'll try to get back to you in a day or so.
Aim of module
- Create a VHDL test bench for your design
- Run your design in the Simulator
- Get some familiarity with the simulator
What is simulation?
When you debug software you are actually running the code on the processor, with access to the system resources such as the OS, memory, communication of file systems. Unlike debugging software, simulating a FPGA project doesn't run it on the actual hardware - the closest equivalent you may have experience with the simulation of a microcontroller in MPLAB or WinAVR.
Although no hardware is involved simulation is very powerful - it is very much like having the most powerful of logic analyse at your fingertips.
The initially confusing bit about simulation is that it requires another VHDL module to drives the input signals and receives the outputs from your design - a modulue that is called a "test bench". They are pretty easy to spot - the ENTITY declaration has no "IN" or "OUT" signals, just something like this:
ENTITY TestBench IS END TestBench;
Creating a test bench module
Here is how to create a test bench using the wizard in WebPack.
Right-click on the top level of the hierarchy and select to add a new source module into the project:
Select the "VHDL Test Bench" and assign it a name (I just add 'tb_' to the name of the component being tested), then click 'Next':
You will then need to select which component of the design you wish to test and then click 'Next':
A summary screen will be presented - review the details and then click "Finish'.
Breaking down of a Testbench module
You will have a new VHDL module and open it in source editor. Here is the resulting VHDL with most of the comments removed, to reduce its size.
LIBRARY ieee; USE ieee.std_logic_1164.ALL; ENTITY tb_Switches_LEDs IS END tb_Switches_LEDs; ARCHITECTURE behavior OF tb_Switches_LEDs IS COMPONENT Switches_LEDs PORT( switches : IN std_logic_vector(7 downto 0); LEDs : OUT std_logic_vector(7 downto 0); clk : IN std_logic ); END COMPONENT; --Inputs signal switches : std_logic_vector(7 downto 0) := (others => '0'); signal clk : std_logic := '0'; --Outputs signal LEDs : std_logic_vector(7 downto 0); -- Clock period definitions constant clk_period : time := 10 ns; BEGIN -- Instantiate the Unit Under Test (UUT) uut: Switches_LEDs PORT MAP ( switches => switches, LEDs => LEDs, clk => clk ); -- Clock process definitions clk_process :process begin clk <= '0'; wait for clk_period/2; clk <= '1'; wait for clk_period/2; end process; -- Stimulus process stim_proc: process begin wait for 100 ns; wait for clk_period*10; wait; end process; END;
This has a few more language structures that have not been seen so far. First is a component declaration, which defines the project that is being tested - much like a C function prototype:
COMPONENT Switches_LEDs PORT( switches : IN std_logic_vector(7 downto 0); LEDs : OUT std_logic_vector(7 downto 0); clk : IN std_logic ); END COMPONENT;
There is a "constant" declaration, which is of a "time" data type - this data type is exclusively used in simulation:
constant clk_period : time := 10 ns;
The next is creating an instance of the Switches_LEDs component, and attaching its signals to the signals withing the test bench:
uut: Switches_LEDs PORT MAP ( switches => switches, LEDs => LEDs, clk => clk );
And finally two processes that contain "wait" statements that controls the timing of signals within the simulation:
clk_process :process begin clk <= '0'; wait for clk_period/2; clk <= '1'; wait for clk_period/2; end process; -- Stimulus process stim_proc: process begin wait for 100 ns; wait for clk_period*10; wait; end process;
The first process ('clk_process') defines the clock signal - which will stay '0' for 5 nanoseconds, then flip to '1' for 5 nanoseconds - giving a 10ns (100MHz) clock. The seconds process ('stim_proc')is where you add statements to change the inputs of the unit under test - for example you could use "switches <= "11111111" to simulate the switches being turned on. When initially created all inputs (other than the clock signal) are set to '0'.
Being up front to avoid confusion the "wait for" statement with a time period it can not be realized inside an FPGA, and can only be used inside simulations. If you use this statement inside your design it will simulate perfectly well but you will not be able to implement your design in and FPGA.
Starting the simulation
From top to bottom, switch to "Simulation" view, select the desired test bench (you can have more than one), expand the "Processes" tree, and then double click on "Simulate behavioral model" - as a quirk, if you have just finished a simulation you may need to right-click on this and choose "Run all".
The simulation will be compiled, and then the simulator tool is launched. On start-up the simulator will simulate the first microsecond:
Using the simulator
From left to right you have the following panes:
- Instances and processes - the design hierarchy being simulated
- Objects - what signals are in the selected instance
- Waveform window - A list of signals being recorded, and a graphical display of their values over time.
Also, the default timescale is very small - 10 or so picoseconds. You can click the "zoom out" on the toolbar until you can see the clock signal ticking away:
As desired you can drag a signal from the "Objects" pane into the Waveform window, but as the signal has not yet been recorded you will need to click the "Reset" and then "Run for specified time" to get values displayed in the window. In this screenshot I've dragged "counter[29:0]" from UUT into the waveform window and rerun the simulation.
As you drag and click on the Waveform pane the value of that signal at that time is shown - unlike when debugging code you can trace backwards in time!
- Make some part of the design dependent on the state of one of the switches. Simulate the design after adding assignments to change the switch signal in the stimulus process.
- Right-click on some of the signals in the waveform window and explore the "radix" and cursor options.
- Click and drag over the waveform window to measure the duration of a signal from transition to transistion
- Click on the triangle to the left of a bus's name. What happens?
Points to ponder
- Does the simulation take into account the propagation delays inside the internal logic of the FPGA?
- If a signal changes at exactly the same time that clock signal's rising edge, what happens?
Ready to carry on?
Click here to carry on to the next module.