From VHDL to FPGA
#1: VHDL simulation
Jason Agron
University of Kansas
Enno Lübbers
University of Paderborn
jagron@ittc.ku.edu, enno.luebbers@upb.de
Field-Programmable Gate Arrays (FPGAs)
Fine-grained reconfigurable hardware
Gate-Array: regular structure of “logic cells”, connected
through an interconnection network
Configuration stored in SRAM, must be loaded on startup
Modern FPGAs provide lots of logic resources
Virtex-5: up to 330,000 logic cells
DSP blocks, multipliers, block RAM, ...
Manual design simply unmanageable
What is VHDL?
VHDL (Very high speed integrated logic Hardware
Description Language)
Developed in the 1980s and 1990s as a documentation language for
(digital) hardware as an alternative to complex manuals
Soon logic simulators appeared to simulate (= verify the behaviour)
these ‘documents’
Now mainly used as a design entry language for digital logic
synthesizers to generate hardware
document
= describe
simulate
= verify
Overview
VHDL as a modelling language
Modules
Testbenches
VHDL simulation
Tools
Xilinx ISE
ModelSim
VHDL synthesis
Tool Flow
Hardware
Spartan-3E development board
Tools
Xilinx ISE
Xilinx iMPACT
VHDL modules
my_module
A module can have
Ports
Inputs
Outputs
Parameters (“generics”)
This interface is described by an
entity my_module is
in
pu
ts
i_a
i_b
ou
tp
ut
s
o_c
o_d
parameters
G_MYPARAM
port (
i_a : in std_logic;
i_b : in std_logic;
o_c : out std_logic;
o_d : out std_logic
);
generic (
G_MYPARAM : integer
);
Functional description
The function of a module is defined
By a VHDL description of the module’s
behaviour, or
By assembling the module from other modules
(which is a description of its structure)
my_module
in
pu
ts
ou
tp
ut
s
architecture behavioural
of my_module is
begin
c <= a xor b;
d <= a and b;
end behavioural;
my_module
VHDL testbenches
To verify a modules operation, we need to
Stimulate the module’s inputs
Let the module process the inputs internally
Observe the outputs
i_a
i_b
o_c
o_d
G_MYPARAM
?
my_module_tb
!
Introduction to the Xilinx
Toolset: Project Navigator (ISE)
What is Project Navigator (ISE)?
ISE is a tool that is used for…
Organizing design files.
Running processes to implement a design.
Compilation
Simulation
Synthesis
ISE is an integrated development environment (IDE).
You can create/edit sources
Create tests
Configure implementation parameters
Optimization levels
IDEs: Hardware vs. Software
Software IDE
Sources describe programs and libraries.
Testing can be done via debugging/simulation.
Usually done by stepping through the code line by line.
Compilation results in an executable.
Hardware IDE.
Sources describe entities and libraries.
Blocks with I/O ports.
Testing is done via debugging/simulation.
Usually done by providing stimulus to input ports, and watching behavior of
internal state and output ports.
The result of simulation is a waveform and I/O.
Compilation (synthesis) results in a bitstream, not an executable.
A “program” for an FPGA.
How To Get Started
Open up ISE.
Create a new ISE project.
Select “File”, “New Project”
Select the proper FPGA target, language, etc.
Click “Next” repetitively, and then click “Finish”.
Setting Up the Project Parameters
Specify the location and name of the project.
Specify the parameters of the project.
FPGA target.
Synthesis tool.
Adding Sources to the Project
Add HDL sources to the
project.
First, Create a subdirectory within
the project for the HDL sources to
be placed in (maybe called
/src/hdl).
Now, add the sources to the
project.
Select the FPGA within the
“Sources in Project” window.
Right click and select “Add
Source” or “New source”.
Add existing sources and new
sources as needed.
NOTE: Some sources can
only be made visible by
altering the “Sources For:”
parameter!
What Can Be Simulated?
An entity cannot be
simulated!
It has I/O ports
What should the simulator do for
inputs?
It is like “simulating” a video game
console without a game or
controller.
A testbench can be simulated!
A testbench is a wrapper around
an entity.
It stimulates the input ports.
The testbench itself has no I/O
ports.
It acts as a “driver”.
Starting a Simulation
Select “Behavioral Simulation”
from the “Sources For:” menu
in the top-left.
This allows the user to see:
Project testbenches.
Project UUTs = Units Under Test.
Select the testbench that is to
be simulated.
The “Processes” menu should
now have a “Simulation”
option.
Expand this option and double
click on “Simulate Behavioral
Model”.
Simulation Output: Waveform
The output of simulation is usually a waveform.
A clock-cycle accurate representation of signal behavior in the system.
A simulator “executes” the testbench.
Allowing the testbench to “drive” the UUT.
The waveform shows the behavior of signals…
As input to the UUT.
Anatomy of a Testbench
A testbench must do the
following:
Instantiate a UUT.
Define the component.
Instantiate the component.
Provide stimulus to the UUT.
Input signals
Clocks
A testbench must not do the
following:
Must not have any I/O ports.
I/O can be done via special
print statements (screen I/O
and file I/O).
These are usually simulator
Anatomy of a Testbench - Clocks
Purpose of a clock:
Periodic signal used for synchronization.
Used as a “gating” mechanism for storage devices
Registers use a clock as a signal to store.
On “every” clock edge store a new value.
Edges can be rising, falling, or both.
The clock model above is encapsulated in a process
Example: Queue (FIFO)
FIFO = First In First Out.
Operations:
Enqueue (Add to queue)
Dequeue (Remove from queue)
Status:
Datentypen
VHDL bietet spezielle Datentypen und Operatoren zur
Hardwarebeschreibung
z.B. std_logic als digitales Signal mit den Zuständen
‚U‘ : nicht initialisiert
‚X‘ : unbekannt
‚0‘ : logisch 0
‚1‘ : logisch 1
‚Z‘ : hochohmig
‚W‘ : schwaches Signal (0 oder 1)
‚L‘ : schwaches Signal, wahrscheinlich 0
‚H‘ : schwaches Signal, wahrscheinlich 1
‚-‘ : Wert irrelevant (don‘t care)
Skalare Typen
integer, natural, unsigned...
Arrays
std_logic_vector, array of integer...
Benutzerdefinierte Datenstrukturen (records)
Entity
VHDL-Modelle bestehen aus Modulen, sogenannten Entities
ähnlich wie Klassendeklarationen
beschreiben Schnittstelle der Module
entity half_adder is
port ( x : in std_logic;
-- x input
y : in std_logic;
-- y input
s : out std_logic;
-- sum output
c : out std_logic
-- carry output
);
Architecture
Die Implementation einer „entity“ wird durch eine
„architecture“ definiert:
Hier handelt es sich um eine sogenannte
Verhaltensbeschreibung
architecture behavioural of half_adder is
begin
s <= x xor y;
c <= x and y;
Strukturbeschreibung
Aus zwei Halbaddierern lässt sich ein Volladdierer
zusammensetzen:
entity full_adder is
port (
x : in std_logic;
-- x input
y : in std_logic;
-- y input
c_in : in std_logic;
-- carry input
s : out std_logic;
-- sum output
c_out : out std_logic
-- carry output
);
end full_adder;
architecture structural of full_adder is
signal a, b, c : std_logic;
begin
ha1 : half_adder port map (x => x, y => y, s => a, c => b);
ha2 : half_adder port map (x => a, y => c_in, s => s, c => c);
c_out <= b or c;
Komplexe Operatoren
Zum Addieren zweier Zahlen in Binärdarstellung können
mehrere Volladdierer zusammengeschaltet werden
VHDL kennt aber auch arithmetische Operatoren
s <= a + b;
s <= a * b;
...
Außerdem lassen sich Funktionen definieren, z.B.
crc <= crc16(x);
Prozesse
Grundlegendes Konstrukt zur Verhaltensbeschreibung:
Prozesse werden „ausgeführt“, sobald sich eines der Signale in
der sensitivity list ändert
Getaktete (synchrone) Logik
rising_edge(clk) ⇔ clk‘event and clk = ‚1‘
process(clk, reset)
begin
if reset = ‚1‘ then
output <= ‚0‘;
elsif rising_edge(clk) then
output <= input + 1;
end if;
end process;
+
Register
1
input
output
clk
reset
clear
D
Q
Parallelität
In VHDL „passiert alles gleichzeitig“
signal a, b, c, d : std_logic;
process(clk)
begin
if rising_edge(clk) then
a <= input;
b <= a;
c <= b;
d <= c;
end if;
end process;
a
b
clk
0 1 2 3 4 5 6 7 8
input(0) input(1) input(2) input(3) input(4) input(5) input(6) ...
X input(0) input(1) input(2) input(3) input(4) input(5) ...
a
Registerinput
b
Registerc
Registerd
Registerclk
Signal-Zuweisung
Parallelität
Signal-Zuweisungen erfolgen “nicht-blockierend” (parallel)
Variablen-Zuweisungen erfolgen “blockierend” (sequentiell)
außerdem existieren Variablen nur innerhalb eines Prozesses
process(clk)
variable a, b, c, d : std_logic;
begin
if rising_edge(clk) then
a := input;
b := a;
c := b;
d := c;
end if;
end process;
process(clk)
variable a, b, c, d : std_logic;
begin
if rising_edge(clk) then
d := input;
end if;
end process;
⇔
Variablen-Zuweisung
Demonstration
reset
clk
bcd2seg
delay-counter
toplevel
disp
dp
an
display-counter
Daten
Dezimal-punkt
Chip
Select
50 MHz
Zähler auf
7-Segment-Anzeige
bcd2seg.vhd
-- $Id$
---- bcd2seg.vhd: BCD to 7-segment display encoder
---- Converts a BCD-coded signal to seven-segment-display
---- Author : Enno Luebbers <enno.luebbers@upb.de> -- Created: 24.04.2006
---- Major changes:
-- 24.04.2006 Enno Luebbers File created. --library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.NUMERIC_STD.ALL; entity bcd2seg is port (
i_bcd : in integer range 0 to 15; o_7seg : out std_logic_vector(6 downto 0) );
end bcd2seg;
architecture behavioral of bcd2seg is -- A -- --- F| |B -- | G | -- --- E| |C -- | D | --
---type slv_array is array(0 to 15) of std_logic_vector(6 downto 0); constant conversionTable : slv_array :=
( -- ABCDEFG "1111110", -- 0 "0110000", -- 1 "1101101", -- 2 "1111001", -- 3 "0110011", -- 4 "1011011", -- 5 "1011111", -- 6 "1110000", -- 7 "1111111", -- 8 "1111011", -- 9 "1110111", -- A "0011111", -- B "1001110", -- C "0111101", -- D "1001111", -- E "1000111" -- F ); begin o_7seg <= conversionTable(i_bcd);
counter.vhd
-- $Id$
---- counter.vhd: Counter / Generics example
---- Provides a simple up/down counter. Parameters can -- be set through generics
---- Author : Enno Luebbers <enno.luebbers@upb.de> -- Created: 24.04.2006
---- Major changes:
-- 24.04.2006 Enno Luebbers File created. --library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity counter is generic (
G_MAX : natural := 15; -- maximum range G_INIT : natural := 15; -- initial value G_UP : boolean := true -- count up? );
port (
i_clk : in std_logic; i_reset : in std_logic; i_enable : in std_logic;
o_value : out natural range 0 to G_MAX );
end counter;
architecture Behavioral of counter is signal value : natural range 0 to G_MAX; begin
-- check generics
assert G_MAX >= G_INIT report "G_INIT greater G_MAX" severity FAILURE;
count : process(i_clk, i_reset) begin
if i_reset = '1' then value <= G_INIT;
elsif rising_edge(i_clk) and i_enable = '1' then if G_UP then value <= value + 1; else value <= value - 1; end if; end if; end process; o_value <= value; end Behavioral;
toplevel.vhd
-- $Id$
---- toplevel.vhd: VHDL tutorial top level entity
---- Top level entity for Digilent Spartan-3 evaluation board
---- Author : Enno Luebbers <enno.luebbers@upb.de> -- Created: 24.04.2006
---- Major changes:
-- 24.04.2006 Enno Luebbers File created. --library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity toplevel is port ( clk : in std_logic; reset : in std_logic; -- LED display
disp : out std_logic_vector(6 downto 0); dp : out std_logic;
an : out std_logic_vector(3 downto 0) );
end toplevel;
architecture structural of toplevel is component bcd2seg
port (
component counter generic (
G_MAX : natural := 15; -- maximum range
G_INIT : natural := 15; -- initial value G_UP : boolean := true -- count up? );
port (
i_clk : in std_logic; i_reset : in std_logic; i_enable : in std_logic;
o_value : out natural range 0 to G_MAX );
end component;
constant DELAY_INIT : integer := 50000000; signal counterEnable : std_logic;
signal counterValue : integer range 0 to 15 := 0;
signal delay : integer range 0 to DELAY_INIT-1 := DELAY_INIT-1; signal displayData : std_logic_vector(6 downto 0);
begin delayCounter : counter generic map ( G_MAX => DELAY_INIT-1, G_INIT => DELAY_INIT-1, G_UP => false ) port map ( i_clk => clk, i_reset => reset, i_enable => '1', o_value => delay );
toplevel.vhd
displayCounter : counter generic map ( G_MAX => 15, G_INIT => 0, G_UP => true ) port map ( i_clk => clk, i_reset => reset, i_enable => counterEnable, o_value => counterValue ); encoder : bcd2seg port map ( i_bcd => counterValue, o_7seg => displayData );counterEnable <= '1' when delay = 0 else '0';
disp <= NOT displayData; -- the LED display is active-low driven dp <= '1';
an <= "1010"; end structural;