logic a ;
logic b ;
this.inf = inf ;
endfunction
inf.a =a ;
endtask
VHDL (VHSIC-HDL, Very High-Speed Integrated Circuit Hardware Description Language) is a hardware description language used in electronic des...
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.
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:
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:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity counter is
Port ( CLK : in STD_LOGIC;
CLR : in STD_LOGIC;
DOUT : out STD_LOGIC_VECTOR (7 downto 0));
end counter;
architecture Behavioral of counter is
signal val: std_logic_vector(7 downto 0);
begin
process (CLK,CLR) is
begin
if CLR='1' then
val<="00000000";
elsif rising_edge(CLK) then
val<=val+1;
end if;
end process;
DOUT<=val;
end Behavioral;
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":
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_unsigned.all;
USE ieee.numeric_std.ALL;
ENTITY counter_tb_vhd IS
END counter_tb_vhd;
ARCHITECTURE behavior OF counter_tb_vhd IS
-- Component Declaration for the Unit Under Test (UUT)
COMPONENT counter
PORT(
CLK : IN std_logic;
CLR : IN std_logic;
DOUT : OUT std_logic_vector(7 downto 0)
);
END COMPONENT;
--Inputs
SIGNAL CLK : std_logic := '0';
SIGNAL CLR : std_logic := '0';
--Outputs
SIGNAL DOUT : std_logic_vector(7 downto 0);
BEGIN
-- Instantiate the Unit Under Test (UUT)
uut: counter PORT MAP(
CLK => CLK,
CLR => CLR,
DOUT => DOUT
);
-- Clock generation
process is
begin
CLK<='1';
wait for 5 ns;
CLK<='0';
wait for 5 ns;
end process;
tb : PROCESS
BEGIN
CLR<='1';
wait for 100 ns;
CLR<='0';
wait; -- will wait forever
END PROCESS;
END;
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.
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.
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.
When ModelSim is automatically lunched within the ISE environment it just displays the top entity level signals in the Wave View window. However, to either facilitate debugging tasks or check specific behavior of lower level components most of the time internal signals also need to be displayed in the Wave View window of ModelSim. Moreover, coloring, ordering and grouping signals is especially useful in complex designs. Hence, having one or several custom views and invoking them automatically will help the verification job.
Scripts files (.do) created in ISE
When ModelSim is launched from the Xilinx ISE, Project Navigator automatically creates a .do script file that contains all of the necessary commands to compile the .vhd source files, load, start and run the
simulation in ModelSim.
The .do script file created by Project Navigator has different extensions based on the type of simulation launched. For instance, for a Behavioral (functional) Simulation these are the three files created:
<TestBenchName>.fdo ModelSim commands for behavioral simulation
<TestBenchName>.udo ModelSim user commands
<TestBenchName_wave>.fdo ModelSim Wave window format commands for behavioral simulation
A detail of the ModelSim commands in a <TestBenchName>.fdo script file created by Project Navigator, when running a Behavioral Simulation, is described below:
The <TestBenchName>.fdo script file is regenerated each time a behavioral simulation is launched. Therefore, any modification done in this file will be automatically overwriting. Still, in this script file there is a command line, do{<TestBenchName_wave>.fdo}, that invokes the <TestBenchName_wave>.fdo script file. This script file never is neither modified nor overwritten by Project Navigator. Hence, the best file that can be used to customize the Wave window format in the ModelSim environment is the <TestBenchName_wave>.fdo script file. By default, this file has the following structure:
The ‘add wave *’ command means that, by default, all the top level entity signals will be displayed in the Wave view of ModelSim. Likewise, when running simulation after translating, mapping or PAR processes, similar script files are created. A detail of the script files created in each process is showed below:
Simulation Post-translate
<TestBenchName>.ndo ModelSim commands for post-translate simulation
<TestBenchName>.udo ModelSim user commands
<TestBenchName_wave>.ndo ModelSim waves format commands for post-translate simulation
Simulation Post-Map Process
<TestBenchName>.mdo ModelSim commands for post-map simulation
<TestBenchName>.udo ModelSim user commands
<TestBenchName_wave>.mdo ModelSim waves format commands for post-map simulation
Simulation Post-PAR
<TestBenchName>.tdo ModelSim commands for post-par simulation
<TestBenchName>.udo ModelSim user commands
<TestBenchName_wave>.tdo ModelSim waves format commands for post-par simulation
In general, the script file <TestBenchName_wave>.*do will be the file to be modified to customize the Wave window in the ModelSim environment in any of the different simulation cases. As it was detailed above, the “add wave *” command states to display just the top level entity signals. However; to facilitate debugging tasks most of the time internal signals also need to be displayed in the Wave window. Moreover, coloring, ordering and grouping the signals in the Wave window is especially useful in complex designs.
Customization Process
The process to customize the Wave window in ModelSim when running the simulation flow in Xilinx ISE environment is detailed below:
1) Create a new ISE project or just open one already created. Write your VHDL code, create your test bench and execute either the functional, post-translate, post-map or post-par simulation of your design, which will open ModelSim simulation tool.
2) Within the Wave window, ModelSim offers several methods to customize the view.
3) Once you are done adding signal, dividers and changing colors, it is then necessary to save this new customized Wave window format to be able to use it again:
4) Go back to the ISE Project Manager window.
5) There are a couple of options to load automatically the saved wave.do file (item 3.b) when running a simulation from Project Navigator. As it was explained before, the easiest way is to modify the <TestBenchName_wave.fdo> file. Hence, open to edit the <TestBenchName_wave.fdo> file that resides in the ISE Project Directory. Remove or comment out (by using the # character) the command line “add wave *” and then add a new command line that will invoke the customized waveform; by writing “do wave.do” (or whatever name you use when saving the Wave window format). If you have saved the wave.do file in a directory different than the ISE Project Directory, you will need to write the complete path where the file wave.do resides. Below, there are two examples of how the <TestBenchName_wave.fdo> file would look like, one for a complete directory path, and the other with a relative path:
Once finishing with the modifications, save the customized <TestBenchName_wave.fdo> file.
6) From now on for every behavioral simulation to be executed, Project Navigator will use the custom wave.do waveform format when launching ModelSim.
Even though it has been explained the process to customize the behavioral simulation, similar steps have to be followed, for instance, for customizing the post-place and route simulation. The file to be changed in this case is the <TestBenchName_wave>.tdo (as it was explained before) and just follows all the steps detailed above for the behavioral simulation.
More on Customization
Besides of modifying the <TestBenchName_wave.fdo> file for customizing the Wave window format, another ModelSim commands can also be added with other purposes. For instance, one useful command for designs with a large amount of internal signals is the following: log –r /*
This command will log all the data objects in the design. For example, if after running the simulation you find out you would like to see an internal signal not currently displayed in the Wave view, you just need to select the signal you want to add, drag and drop it in the Wave view. Then, its respective waveform will immediately be displayed.
Without the log command, if that particular internal signal is not in the list of signals in the wave.do script, it is not logged; therefore no waveform will be displayed for that signal. In this case, it will be necessary to add the signal to the Wave window, save the wave.do again and then rerun the simulation.
The drawback of the log command is that it could make the simulation much slower since it needs to log all the signals of the design. One point to keep in mind is the fact that internal signals after PAR usually do not keep the same name as before the PAR. This means that the wave.do saved for functional simulation, may or may not be able to display all the internal signals when doing a post-PAR simulation.
One solution for this problem is to create another customized wave.do for the post-PAR simulation naming it something like wave_par.do. Then, change the <TestBenchName_wave.tdo> file as explained in point 5 above. Another helpful point to know is the fact that it is possible to rerun the simulation without closing ModelSim, avoiding returning to Project Navigator.
For instance, if you find an error when running the simulation in one of the .vhd source files, you can edit the VHDL file, still keeping open ModelSim (even you can use ModelSim text editor), save the modifications of the VHDL file and rerun the simulation by typing do {TestBenchName.fdo} and pressing enter in the transcript window (bottom window) of ModelSim. You can also use the up-arrow key in the transcript window to go through all the executed commands until you find the do {TestBenchName.fdo} command, and then just press enter to execute it.
At a low level of abstraction, a protocol is often most easily understood as a state machine. Design criteria can also easily be expressed in terms of desirable or undesirable protocol states and state transitions. In a way, the protocol state symbolizes the assumptions that each process in the system makes about the others. It defines what actions a process is allowed to take, which events it expects to happen, and how it will respond to those events.
The Xcell Journel Issue 81 gives some important tips on implementing state machines in your FPGA
Link to Xcell Journel issue 81
Sounds Interesting !!!!!
The idea is to devise a “micro-environment’’ that mimics the human brain. Researchers hope to study neurodegenerative conditions such as Alzheimer’s disease, strokes and concussions. The eventual goal is to study the effects of drugs and vaccines on the brain.
Draper, a spinoff from the Massachusetts Institute of Technology (MIT), and USF are using embryonic cells from rats, but researchers plan to use human cells in the future. The brain-on-a-chip combines several technologies, including an emerging field called microfluidics.
Microfluidics deals with the control of fluids in devices. Tiny chip-like devices using microfluidics are used in many applications, such as cell sorting and detection, gene analysis, inkjet print heads, lab-on-a-chip units and point-of-care diagnostic tools. Meanwhile, lab-on-a-chip, and a related field, organ-on-a-chip (i.e. brain-on-a-chip), are systems that integrate various functions in a chip-like format. Some, but not all, lab-on-a-chip systems use microfluidics.
In Verilog there’s a bit shifter operator, which isn’t used a lot, since FPGA designers prefer to state exact bit vectors. But sometimes bit shifting makes the code significantly more readable. Too bad that Xilinx’ XST synthesizer doesn’t get it right in a specific case.
Namely, the following statement is perfectly legal:
always @(posedge clk)
reduce <= 1 + (end_offset >> (6 + rcb_is_128_bytes - format_shift) );
But it turns out that Xilinx ISE 13.2 XST synthesizer gets confused by the calculation of the shift rate, and creates something wrong. I can’t even tell what it did, but it was wrong.
So the rule is simple: It’s fine to have the shift number being a register (even combinatoric) or a wire, but no inline calculations. So this is fine:
always @(format_shift or rcb_is_128_bytes)
if (rcb_is_128_bytes)
case (format_shift)
0: shifter <= 7;
1: shifter <= 6;
default: shifter <= 5;
endcase
else
case (format_shift)
0: shifter <= 6;
1: shifter <= 5;
default: shifter <= 4;
endcase
always @(posedge clk)
reduce <= 1 + (end_offset >> shifter );
(assuming that format_shift goes from zero to 2).
Actually, I would bet that it’s equally fine to calculate the number of shifts and put the result in a wire. I went for the case statement hoping that the synthesizer will take the hint that not all values that fit into the registers are possible, and will hence avoid implementing impossible shift values.
Needless to say, I know about this because something went horribly wrong all of the sudden. I believe XST version 12.2 handled the shift calculation OK. And then people ask me why I don’t like upgrades.
In Verilog there’s a bit shifter operator, which isn’t used a lot, since FPGA designers prefer to state exact bit vectors. But sometimes bit shifting makes the code significantly more readable. Too bad that Xilinx’ XST synthesizer doesn’t get it right in a specific case.
Namely, the following statement is perfectly legal:
always @(posedge clk)
reduce <= 1 + (end_offset >> (6 + rcb_is_128_bytes - format_shift) );
But it turns out that Xilinx ISE 13.2 XST synthesizer gets confused by the calculation of the shift rate, and creates something wrong. I can’t even tell what it did, but it was wrong.
So the rule is simple: It’s fine to have the shift number being a register (even combinatoric) or a wire, but no inline calculations. So this is fine:
always @(format_shift or rcb_is_128_bytes)
if (rcb_is_128_bytes)
case (format_shift)
0: shifter <= 7;
1: shifter <= 6;
default: shifter <= 5;
endcase
else
case (format_shift)
0: shifter <= 6;
1: shifter <= 5;
default: shifter <= 4;
endcase
always @(posedge clk)
reduce <= 1 + (end_offset >> shifter );
(assuming that format_shift goes from zero to 2).
Actually, I would bet that it’s equally fine to calculate the number of shifts and put the result in a wire. I went for the case statement hoping that the synthesizer will take the hint that not all values that fit into the registers are possible, and will hence avoid implementing impossible shift values.
Needless to say, I know about this because something went horribly wrong all of the sudden. I believe XST version 12.2 handled the shift calculation OK. And then people ask me why I don’t like upgrades.