In this article, I’m going to explain how to simulate digital circuits using the ISim software, which is part of the ISE Design Suite.
ISim is the simulator provided by Xilinx, and if you have the ISE software installed on your computer, this simulator is also installed alongside it. You can use it to simulate your circuits.
More...
Types of Digital Circuit Simulation
Before diving into the simulation itself, let’s talk a bit about the different types of simulation.
When you want to simulate a digital circuit using one of these simulation tools, you generally have two types of simulation available to you:
The first type is Functional Simulation, also known as Behavioral Simulation.
The second type is Timing Simulation.
In functional simulation, your goal is to verify the logical behavior of your circuit.
For example, if you’ve designed an adder, you want to check whether the addition operation is performed correctly or not.
In functional simulation, all delays from operators and functions are considered to be zero.
For instance, if your circuit contains an AND gate, when one of the inputs to the gate changes, the result is propagated to the output in zero time. But in reality, that’s not how things work.
In practice, every electronic or digital component has what’s called a propagation delay, which causes the effect of the input to appear at the output after some delay.
For example, in the case of the AND gate, if you change one of its inputs, the output might change after, say, one nanosecond. This delay is called propagation delay.
In timing simulation, these propagation delays are also taken into account.
So, as you might guess, timing simulation is used for later stages of the design process, such as after synthesis, placement, and routing.
Once you’ve completed synthesis, the actual components inside the FPGA are used for implementation, and each of these components has a propagation delay.
In timing simulation, these propagation delays are also considered.
For example, when simulating an AND gate, after changing the input, the output changes according to the propagation delay, which could vary depending on the FPGA you are using. It might be, for example, one nanosecond or some other value.
Now, to explore simulation using the ISim software, let’s use an example.
I’ll use the same priority encoder example that we discussed in detail in the previous article.
Simulating a Priority Encoder Using ISim
As you probably remember from the last article, an encoder determines its output based on which of its inputs is active.
In the normal operation of an encoder, exactly one of the inputs should be “1” at any given time. However, if more than one input is “1,” the encoder might not function correctly.
That’s why the priority encoder was introduced.
In a priority encoder, if more than one input is “1,” you can specify whether the priority should be given to the higher-index bit or the lower-index bit.
For example, in the following priority encoder, you see that inputs W1 and W3 are simultaneously 1.

A priority encoder with inputs W1 and W3 simultaneously set to 1.
Now, if we say this is a priority encoder that assigns priority to the higher-index bit, which is W3, then since W3 is 1, it's considered as the active input, and W1 will be ignored.
The output will then be determined accordingly.

The truth table for a 4-to-2 priority encoder that assigns priority to the higher-index.
Creating a Testbench for VHDL Simulation
To simulate your circuit using VHDL in ISim, the first thing you need to do is create a special file called a testbench.
A testbench file is itself a VHDL file, but it's a special VHDL file where the VHDL module you’ve written is instantiated, and you can assign the desired values to the inputs of your module.
Keep in mind that the testbench file is only used for simulation and cannot be implemented.
Now, let’s see how we can simulate the priority encoder module we designed in the last article using ISim.
Here, you can see the priority encoder code that we discussed in detail in the previous article.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity Encoder is port ( w : in std_logic_vector(3 downto 0); y : out std_logic_vector(1 downto 0) ); end Encoder; architecture Behavioral of Encoder is begin y <= "11" when w(3) = '1' else "10" when w(2) = '1' else "01" when w(1) = '1' else "00"; end Behavioral;
If you have any questions about this code, you can refer to the previous article to clarify them before you proceed with the simulation.
To simulate this code, you first need to create a testbench file. A testbench, as I mentioned earlier, is a VHDL file, but it’s a specific type of VHDL file.
Steps to Create a Testbench in ISim
To create it, go to the hierarchy section, right-click, and select New Source:

First step of creating a testbench file in ISim software.
Among the file types you can create, select VHDL Test Bench and give it a name.

Select VHDL Test Bench and give it a name.
I personally have a convention for organizing my files and making them easier to access later - I name testbench files with the name of the module I want to simulate and add _tb at the end.
For example, now the module I want to use is the Priority_Encoder module, so I'll name the testbench file "Priority_Encoder_tb" where tb stands for testbench.
Click Next, proceed to the next page, and here you’ll be asked which of your modules you want to create the testbench for.

Here you’re asked which of your modules you want to create the testbench for.
As you can see in the hierarchy section, there are several files in the project, but when creating a testbench, you can only select one module at a time.
So here it's asking which module I want to create this testbench for. We want to create it for the priority encoder module, so we select that and click Next, and finally click Finish.

Now, a testbench file has been created.
LIBRARY ieee; USE ieee.std_logic_1164.ALL; -- Uncomment the following library declaration if using -- arithmetic functions with Signed or Unsigned values --USE ieee.numeric_std.ALL; ENTITY Encoder_tb IS END Encoder_tb; ARCHITECTURE behavior OF Encoder_tb IS -- Component Declaration for the Unit Under Test (UUT) COMPONENT Encoder PORT( w : IN std_logic_vector(3 downto 0); y : OUT std_logic_vector(1 downto 0) ); END COMPONENT; --Inputs signal w : std_logic_vector(3 downto 0) := (others => '0'); --Outputs signal y : std_logic_vector(1 downto 0); -- No clocks detected in port list. Replace <clock> below with -- appropriate port name constant <clock>_period : time := 10 ns; BEGIN -- Instantiate the Unit Under Test (UUT) uut: Encoder PORT MAP ( w => w, y => y ); -- Clock process definitions <clock>_process :process begin <clock> <= '0'; wait for <clock>_period/2; <clock> <= '1'; wait for <clock>_period/2; end process; -- Stimulus process stim_proc: process begin -- hold reset state for 100 ns. wait for 100 ns; wait for <clock>_period*10; -- insert stimulus here wait; end process; END;
I won’t go into too much detail about this file, but there’s an important point to note here.
Testbench File Considerations for Combinational Circuit Simulations
The ISE software creates the testbench file assuming that you want to simulate a sequential circuit with a clock signal.
As a result, it includes some lines related to the clock. If you’re only simulating a combinational circuit, as in this example, you’ll need to remove these clock-related lines.
These lines contain the word clock, so it’s easy to find them.
For example, in line 31, the word clock appears, so this line should be deleted.
Similarly, lines 41 to 48, which generate the system clock for the simulation, can also be removed since our system doesn’t use a clock.
Additionally, there’s another clock-related line at line 57 that should be deleted as well.
Once all these lines are removed, save the file.
LIBRARY ieee; USE ieee.std_logic_1164.ALL; -- Uncomment the following library declaration if using -- arithmetic functions with Signed or Unsigned values --USE ieee.numeric_std.ALL; ENTITY Encoder_tb IS END Encoder_tb; ARCHITECTURE behavior OF Encoder_tb IS -- Component Declaration for the Unit Under Test (UUT) COMPONENT Encoder PORT( w : IN std_logic_vector(3 downto 0); y : OUT std_logic_vector(1 downto 0) ); END COMPONENT; --Inputs signal w : std_logic_vector(3 downto 0) := (others => '0'); --Outputs signal y : std_logic_vector(1 downto 0); -- No clocks detected in port list. Replace <clock> below with -- appropriate port name BEGIN -- Instantiate the Unit Under Test (UUT) uut: Encoder PORT MAP ( w => w, y => y ); -- Stimulus process stim_proc: process begin -- hold reset state for 100 ns. wait for 100 ns; -- insert stimulus here wait; end process; END;
As mentioned earlier, the testbench file instantiates your module and allows you to assign values to its inputs.
In this case, our module has only one input, W, which is a 4-bit signal because we’re simulating a 4-to-2 priority encoder.
Now we need to assign values to W. But where do we do that?
Assigning Input Values in the Testbench
If you scroll down in the testbench file, you’ll see a section labeled insert stimulus here.
This is where you specify the values for your inputs.
So, let’s assign a value to W. I’ll write W followed by a simple assignment operator.
For example, I’ll assign it the value “0110”. This means W0 is 0, W1 is 1, W2 is 1, and W3 is 0.
w <= "0110";
Since this priority encoder prioritizes the higher-index bit, you can probably guess what the output will be.
The highest-index bit that’s 1 is bit 2, so we expect the output to be the binary representation of 2.
Now, let’s run the simulation and see if that’s the case.
Running the First Simulation and Interpreting Results
After creating and saving the testbench file, to run the simulation, go to the Design section above the hierarchy pane, where you’ll see two options: Implementation and Simulation.

Simulation section and the list of testbenches.
First, select Simulation for the simulation process. Here, you’ll see all testbenches in our project listed.
Our testbench is Priority_Encoder_tb. Click on it, and in the lower panel, you’ll see the option to launch the ISim software.

The option to launch the ISim software is shown.
As I mentioned earlier, ISim is Xilinx’s simulation software, and if you’ve installed the ISE software, ISim is installed along with it on your computer.
Before running the simulation, you can double-click on the Behavioral Check Syntax option to ensure there are no syntax errors in your testbench code.

Double-click on this Behavioral Check Syntax option to ensure there are no syntax errors in your testbench code.
Fortunately, there are no errors here.

There are no errors in the testbench code.
After that, you can double-click on Simulate Behavioral Model to launch ISim software from within ISE.

Launch ISim software from within ISE.
Well, the ISim software launches and this black section is the part that shows you the signals.

Since we’re simulating a priority encoder, we have a 4-bit input, W, and a 2-bit output, Y.
Here, you can see the input value “0110” that we assigned in the testbench. As expected, the output is the binary representation of 2, which you can see bellow.

By default, ISim simulates for a duration of 1 microsecond each time you launch it.
You can see that the simulation ends at the 1-microsecond mark.

By default, the simulation ends at the 1-microsecond mark.
Initially, the simulation might not be fully visible on the screen, but you can click the Zoom to Full View icon to view the entire simulation from 0 to 1 microsecond.

Click the Zoom to Full View icon to view the entire simulation.
If you want to run the simulation for a longer time, you can specify the desired duration in the field that is shown bellow.

To run the simulation for a longer time, you can specify the desired duration on the toolbar.
For example, I’ll set it to 2 microseconds. Then, click the Run for the time specified on the toolbar icon to extend the simulation by 2 microseconds.
Now, the simulation runs up to 3 microseconds.

To view the entire simulation again, click the Zoom to Full View icon to see the simulation from 0 to 3 microseconds.
If you want to zoom in on a specific section, you can click on it to place a cursor there and use the Zoom In option. Each click will enlarge that section further.

Click to enlarge a specific section.
Modifying Inputs and Relaunching the Simulation
Now, you might wonder what to do if you want to change the input.
One way is to go back to the ISE software and assign a new input value in the testbench.
For example, let’s change W to “1001.”
In this case, we expect the output to be 3 because the highest-index bit that’s “1” is bit 3.
However, we can’t write two consecutive assignments without a time gap because, otherwise, only the last one will be considered.
We need to introduce a delay between these two assignments to W, which we can do using the Wait for statement.
For example, I’ll copy the line 43, paste it right after the first assignment to W in the testbench code, and add a Wait for 50 nanoseconds statement between the two assignments.
This means that after assigning W the value “0110,” we’ll wait 50 nanoseconds before changing it to “1001.”
-- Stimulus process stim_proc: process begin -- hold reset state for 100 ns. wait for 100 ns; -- insert stimulus here W <= "0110"; wait for 50 ns; W <= "1001"; wait; end process;
As you can see, there’s also a default 100-nanosecond delay at the start of the simulation, during which the input is 0.
After 100 nanoseconds, the input changes to “0110,” and then 50 nanoseconds later, it changes to “1001.”
Now, let’s save the updated testbench file and return to the simulator.
Here, you can click the Relaunch button to rerun the simulation with the updated testbench.

Click the Relaunch button to rerun the simulation with the updated testbench.
Now, the simulation runs for 1 microsecond, and you can clearly see the changes.
As specified in the testbench code, the input was 0 for the first 100 nanoseconds. Then, at 100 nanoseconds, it changed to “0110.”
After 100 nanoseconds, we changed the input to “0110,” with an output of 2. After 50 nanoseconds, at the 150-nanosecond mark, the input changed to “1001,” and as expected, the output became 3.

The simulation results with the updated testbench.
As you can see, working with this software is very straightforward, and it allows you to perform various types of simulations for your circuits.
The features provided by this software make it easy to test and verify the behavior of the modules you’ve designed.