We would like to write this post for our friends who wants to create a simple FPGA Project with Xilinx ISE.
Xilinx ISE as a software package containing a graphical IDE, design entry tools, a simulator, a synthesizer (XST) and implementation tools. Limited version of Xilinx ISE (WebPack) can be downloaded for free from the Xilinx website.
It is not mandatory to use Xilinx software for all tasks (for example, synthesis can be done with Synplify, simulation - with Modelsim etc.), but it is the easier option to start off.
The information in this article applies to Xilinx ISE version 9.2.03i, but other versions (since 8.x) shouldn't be very different. If your version is older than 8.x, you'd better upgrade.
Creating a project
To create a project, start a Project Navigator and select File->New Project. You will be asked for project name and folder. Leave "top-level source type" as HDL.
Now we should choose a target device (we will use a Spartan-3A xc3s50a device as an example) as well as set up some other options:
The Project Navigator window contains a sidebar, which is on the left side by default. The upper part of this sidebar lists all project files, and the lower part lists tasks that are applicable for the file selected in the upper part.
Now, let's add a new source file to our project. We'll start from a simple 8-bit counter, which adds 1 to its value every clock cycle. This counter will have the following ports:
- CLK - input clock signal;
- CLR - input asynchronous clear signal (set counter value to 0);
- DOUT - output counter value (8-bit bus).
We'll define our counter as a VHDL module. VHDL language will be covered in more details in further chapters.
To create a new source file, choose "Create New Source" task and select "VHDL module" source type. The name of our module will becounter.vhd. Then you will be asked which module to associate the testbench with; choose counter.
Let's write the following code in counter.vhd:
entity counter is
Port ( CLK : in STD_LOGIC;
CLR : in STD_LOGIC;
DOUT : out STD_LOGIC_VECTOR (7 downto 0));
architecture Behavioral of counter is
signal val: std_logic_vector(7 downto 0);
process (CLK,CLR) is
if CLR='1' then
elsif rising_edge(CLK) then
ISE inserted library and ports declarations automatically, we only need to write an essential part of VHDL description (inside thearchitecture block).
To check VHDL syntax, select "Synthesize - XST => Check Syntax" task for our module.
In order to check that our code works as intended, we need to define input signals and check that output signals are correct. It can be done by creating a testbench.
To create a testbench for our counter, select "Create New Source" task, choose "VHDL Test Bench" module type and name it, for instance, counter_tb.vhd.
VHDL test bench is written in VHDL, just like a hardware device description. The difference is that a testbench can utilize some additional language constructs that aren't synthesizable and therefore cannot be used in real hardware (for example wait statements for delay definition).
In order for testbench file to be visible, choose "Behavioral Simulation" in the combobox in the upper part of the sidebar.
ISE automatically generates most of the testbench code, we need only to add our "stimulus":
ENTITY counter_tb_vhd IS
ARCHITECTURE behavior OF counter_tb_vhd IS
-- Component Declaration for the Unit Under Test (UUT)
CLK : IN std_logic;
CLR : IN std_logic;
DOUT : OUT std_logic_vector(7 downto 0)
SIGNAL CLK : std_logic := '0';
SIGNAL CLR : std_logic := '0';
SIGNAL DOUT : std_logic_vector(7 downto 0);
-- Instantiate the Unit Under Test (UUT)
uut: counter PORT MAP(
CLK => CLK,
CLR => CLR,
DOUT => DOUT
-- Clock generation
wait for 5 ns;
wait for 5 ns;
tb : PROCESS
wait for 100 ns;
wait; -- will wait forever
We have added the clock generation process (which generates 100MHz frequency clock) and reset stimulus.
Now select a test bench source file and apply "Xilinx ISE Simulator => Simulate Behavioral Model" task. We should get something like this:
It can be seen that our counter works properly.
The next step is to convert our VHDL code into a gate-level netlist (represented in the terms of the UNISIM component library, which contains basic primitives). This process is called "synthesis". By default Xilinx ISE uses built-in synthesizer XST (Xilinx Synthesis Technology).
In order to run synthesis, one should select "Synthesis/Implementation" in the combobox in the upper part of the sidebar, select a top-level module and apply a "Synthesize - XST" task. If the code is correct, there shouldn't be any pproblems during the synthesis.
Synthesizer report contains many useful information. There is a maximum frequency estimate in the "timing summary" chapter. One should also pay attention to warnings since they can indicate hidden problems.
After a successful synthesis one can run "View RTL Schematic" task (RTL stands for register transfer level) to view a gate-level schematic produced by a synthesizer:
Notice that an RTL schematic in question contains only one primitive: a counter, which is directly an element from the UNISIM library.
Synthesizer output is stored in NGC format.
Implementation design flow
- Translate - convert NGC netlist (represented in the terms of the UNISIM library) to NGD netlist (represented in the terms of the SIMPRIM library). The difference between these libraries is that UNISIM is intended for behavioral simulation, and SIMPRIM is a physically-oriented library (containing information about delays etc.) This conversion is performed by the program NGDBUILD and is rather straightforward. The main reason for it to be included is to convert netlist generated by different design entry methods (e.g. schematic entry, different synthesizers etc.) into one unified format.
- Map is a process of mapping the NGD netlist onto the specific resources of the particular device (logic cells, RAM modules, etc.) This operation is performed by the MAP program with resutls being stored in NCD format. For Virtex-5 MAP also does placement (see below).
- Place and route - as can be inferred from its name, this stage is responsible for the layout. It performs placement (logic resources distribution) and routing (connectivity resources distribution). Place and route is performed by a PAR program. For Virtex-5 devices, though, placement is performed by MAP program (and routing still by PAR program). The output of PAR is stored, again, in NCD format.
Constraints are very important during the implementation. They define pin assignments, clocking requirements and other parameters influencing implementation. Constraints are stored in UCF format (user constraints file).
In order to add constraints one need to add a new source (using "Create New Source" task) and choose "Implementation constraints file" source type. UCF file is a text file that can be directly edited by a user, however, simple consraints can be defined with graphical interface.
When a constraints file is selected in the upper part of the sidebar, the specific tasks become available. These include "Create Timing Constraints" and "Assign Package Pins".
For example, if we specify a frequency requirement on CLK as 100 MHz, the corresponding section of the constraints file will be:
NET "CLK" TNM_NET = CLK;
TIMESPEC TS_CLK = PERIOD "CLK" 100 MHz;
When timing requirements are specified in the constraints file, the implementation tools will strive to meet them (and report an error in the case it can't be met).
Package pins constraints must also be set (according to the board layout).
MAP program converts UCF constraints to the PCF format which is later used by PAR.
There are also synthesis constraints stored in XCF files. They are used rarely and shouldn't be confused with implementation constraints.
Programming file generation
After placement and routing, a file should be generated that will be loaded into the FPGA device to program it. This task is performed by a BITGEN program.
The programming file has .bit extension.
The programming file is loaded to the FPGA using iMPACT.