Summer'04 ECE 238 Labs






Lecture Notes





Old Labs






 



Lab 2 Tutorial - Introduction to Arithmetic and IP Cores

By now, you should be familiar with the following concepts, introduced in the first two labs.

  1. Using Karnaugh maps to derive the appropriate Sum-of-Products or Product-of-Sums terms for a given logic equation.
  2. Using code conversion to simplify complex logic.
  3. Using the Xilinx hardware development software in the creation and
    simulation of simple logic circuits.

In addition to the concepts listed above, this lab will introduce you to several new concepts, including the following:

  1. Using multiplexers to implement logic circuits of greater complexity.
  2. Using the Xilinx hardware development software to implement and simulate a circuit functionally equivalent to the multiplexer circuit you built and tested on the prototyping board.
  3. Developing test benches to simulate your designs using the ModelSim
    software.

As this lab will cover a substantial amount of material, you may wish to print out this tutorial so that you can quickly refer back to it during the course of the lab.

This tutorial will be broken down into a sequence of easy-to-follow steps. For the first part of this tutorial we will be concerned with the design and implementation of a binary subtractor circuit using Xilinx and VHDL.

Part I: Developing in VHDL -- The Binary Subtractor.

All serious hardware development done nowadays is done using various Hardware Design Languages (HDLs), such as Verilog, or VHDL. The HDL permits the engineer to describe highly complex logic systems for prototyping on programmable logic, such as an FPGA or a PLD/CPLD, or direct implementation on silicon using an ASIC (Application-Specific Integrated Circuit).

The hardware description language we will be using throughout this course is VHDL. This course is not a VHDL course. What you will recieve is a basic introduction to VHDL as it applies to basic digital logic design.

Creating a Project:

Before we can implement a binary subtractor using VHDL, first we need to create a project. Find and double-click the Project Navigator icon on the desktop. This will launch the Xilinx ISE software.
Once you have the Project Navigator window open, create a new project by clicking on the File menu, and selecting New Project. What should come up is a small window that looks like this:


Change the settings to match those in the dialog boxes. If you click on the Value boxes for each of the Property Names you will discover that each one has a drop-down menu. Select the appropriate Device Family, Device, and Design Flow values.

Once you click on Ok, you will find yourself back in Project Navigator. From here, you will need to create the VHDL code which is required to make the project work. There are two ways to do this. One will require that you be able to create the code from scratch. The other will create the ports and entities for you. However, before you can take the easy way, you should do the footwork at least once. Here, we will take you through the steps necessary to create a working VHDL project.

First, click on the New File icon on the icon bar (this would be the icon that looks like a blank sheet of paper.) On the right side of the screen, you will notice that a new window has been opened.

Basic VHDL -- Libraries and Entities.

The tutorial will break the code into parts in order to explain them. You can find the full code that you should paste into your project after the explanation..

library IEEE;
use IEEE.std_logic_1164.all;

These first two lines are library statements. In other programming languages, such as C/C++, this would be like the #include statements found at the beginning of a program.

When you write VHDL, what you are describing are actual instances of hardware you would be programming onto a PLD, or etching onto actual silicon in an ASIC. The two fundamental building blocks for each instance of hardware is the entity, and it's accompanying architecture. The entity is used to describe the inputs and outputs for the circuit, as defined in a port statement. The architecture statements, describe how the hardware is supposed to work. For our subtractor, we will need to define both the subtractor entity, and it's accompanying architecture.

entity subtraction is
port (
BROW_IN: in STD_LOGIC;
XY_IN: in STD_LOGIC_VECTOR (1 downto 0);
BROW_OUT: out STD_LOGIC;
DIV_OUT: out STD_LOGIC
);
end subtraction;

Here, we have defined the subtractor entity. Inside the entity is the port statement, which describes all the inputs and outputs to the device. A brief description follows:

in STD_LOGIC Single input pin.
in STD_LOGIC VECTOR (1 downto 0)-Two input pins combined into a bus.
out STD_LOGIC Single output pin.

With our hardware entity declared, we must now define it's behavior. As you analyze the case statement look at the truth table listed below it.

architecture subtraction_arch of subtraction is
begin
-- 4 to 1 multiplexer design with case construct
-- SEL: in STD_LOGIC_VECTOR(1 downto 0);
-- A, B, C, D:in STD_LOGIC;
-- MUX_OUT: out STD_LOGIC;
OUT_DIV:
process (XY_IN, BROW_IN)
begin
case XY_IN is
when "00" => DIV_OUT <= BROW_IN;
when "01" => DIV_OUT <= not(BROW_IN);
when "10" => DIV_OUT <= not(BROW_IN);
when "11" => DIV_OUT <= BROW_IN;
when others => DIV_OUT <= 'Z';
end case;
end process;

Recall the 4-to-1 MUX you learned about in the lab lecture. Here, our two control lines S1 and S2 would be XY_IN, and our output pin would be connected to DIV_OUT. With each case, we would select one of four possible valid inputs. However, we are not done yet. Remember that our subtractor has two outputs. We have defined only the first one. We now need to define the second output, the BROW_OUT. Before we do so, here is the truth table developed in the lecture notes:


Here, we have conveniently highlighted for you the BROW_OUT. Take a minute or two to work out the Karnaugh-map. Did you notice the following?

When X and Y are both true or false (00, or 11): The output is BROW_IN
When X is false, and Y is true (01): The output is true.
When X is true, and Y is false (10): The output is false.

With these hints in mind, examine the next bit of VHDL code. You will need to fill in the blank, denoted by five red stars. Here are a couple of hints:

A simple true is denoted by '1'
A simple false is denoted by '0'

-- 4 to 1 multiplexer design with case construct
-- SEL: in STD_LOGIC_VECTOR(1 downto 0);
-- A, B, C, D:in STD_LOGIC;
-- MUX_OUT: out STD_LOGIC;

OUT_BROW:
process (XY_IN, BROW_IN)
begin

case XY_IN is
when "00" => BROW_OUT <= *****;
when "01" => BROW_OUT <= *****;
when "10" => BROW_OUT <= *****;
when "11" => BROW_OUT <= *****;
when others => DIV_OUT <= 'Z';
end case;

end process;
end subtraction_arch;

Do you have the blanks filled in? Do they look like the following?

OUT_BROW:
process (XY_IN, BROW_IN)
begin

case XY_IN is
when "00" => BROW_OUT <= BROW_IN;
when "01" => BROW_OUT <= '1';
when "10" => BROW_OUT <= '0';
when "11" => BROW_OUT <= BROW_IN;
when others => DIV_OUT <= 'Z';
end case;

end process;

Here is the complete code to paste into your project:

library IEEE;
use IEEE.std_logic_1164.all;

entity subtraction is
port (
BROW_IN: in STD_LOGIC;
XY_IN: in STD_LOGIC_VECTOR (1 downto 0);
BROW_OUT: out STD_LOGIC;
DIV_OUT: out STD_LOGIC
);
end subtraction;

architecture subtraction_arch of subtraction is
begin
-- 4 to 1 multiplexer design with case construct
-- SEL: in STD_LOGIC_VECTOR(1 downto 0);
-- A, B, C, D:in STD_LOGIC;
-- MUX_OUT: out STD_LOGIC;
OUT_DIV:
process (XY_IN, BROW_IN)
begin

case XY_IN is
when "00" => DIV_OUT <= BROW_IN;
when "01" => DIV_OUT <= not(BROW_IN);
when "10" => DIV_OUT <= not(BROW_IN);
when "11" => DIV_OUT <= BROW_IN;
when others => DIV_OUT <= 'Z';
end case;

end process;

-- 4 to 1 multiplexer design with case construct
-- SEL: in STD_LOGIC_VECTOR(1 downto 0);
-- A, B, C, D:in STD_LOGIC;
-- MUX_OUT: out STD_LOGIC;
OUT_BROW:
process (XY_IN, BROW_IN)
begin

case XY_IN is
when "00" => BROW_OUT <= BROW_IN;
when "01" => BROW_OUT <= '1';
when "10" => BROW_OUT <= '0';
when "11" => BROW_OUT <= BROW_IN;
when others => DIV_OUT <= 'Z';
end case;

end process;
end subtraction_arch;

You will need to save this file. Click on the Save File icon. You will get a dialog box that looks like this:


Save your VHDL file as subtract.vhd.
If you have saved the file correctly, you should now notice that Xilinx has color-coded your VHDL code.


However, this VHDL file has not been added to your project. You will need to do that now. Click on the Project drop-down menu, and select Add Source . Xilinx will present you with the following dialog box:


Select the subtract.vhd file you created earlier and click Open. This will produce a new dialog box:


Select VHDL Module and click OK. You should now be back in Project Navigator. Now, notice that there have been some changes made. In the window marked Sources in Project, you should now see subtraction (subtract.vhd) listed as one of the sources. Now, in the Processes for Current Source window, you will now see three available processes: Synthesize, Implement Design , and Generate Programming File. To verify that your design works, double-click on Synthesize. If you have made no errors, then you will eventually see a green check mark next to Synthesize.

Now, we are only halfway done. We have a VHDL design that has correct syntax and style. Unfortunately, we have only the slightest clue that it is going to work the way we want it to. To remedy this, we will need to simulate the design, and in order to do this, we will need to add another VHDL file to your project, a ModelSim testbench.

Verifying Your Design -- Testbenches and Simulation .

You will need to click on the New File icon once again to create a new blank file. Writing a VHDL testbench will be a little different from writing a simple VHDL source file, as you will see later on. The tutorial will explain the code in sections and you can find the complete code after the explanation.

LIBRARY IEEE;
USE IEEE.std_logic_1164.all;
USE IEEE.STD_LOGIC_TEXTIO.ALL;
USE STD.TEXTIO.ALL;

--Declaration of ModelSim libraries used
LIBRARY UNISIM;
LIBRARY XILINXCORELIB;


ENTITY testbench IS
END testbench;

As you can see, in order to properly implement the testbench, we needed to include several more libraries. And note, for the testbench, the testbench entity is left blank.

ARCHITECTURE testbench_arch OF testbench IS

COMPONENT subtraction
PORT
(
BROW_IN : in STD_LOGIC;
XY_IN : in STD_LOGIC_VECTOR (1 downto 0);
BROW_OUT : out STD_LOGIC;
DIV_OUT : out STD_LOGIC
);
END COMPONENT;

What we have just defined is a component inside the testbench entity. This would be like a function in a C/C++ program, complete with it's own inputs and outputs. This is what we want to simulate. Notice that while we now have the component we want to simulate, we have nothing to simulate yet. This defined in the next few lines of code:

SIGNAL br_in : std_logic;
SIGNAL xy_in : std_logic_vector (1 downto 0);
SIGNAL br_out : std_logic;
SIGNAL div_out : std_logic;

These signals will show up when we run the simulation itself. We now need to define some of the basic simulation parameters, and assign which simulation signal is connected to which input or output on the component we wish to simulate. The following lines of code accomplish this.

--Defining the variable "CLK_PERIOD" to be
--equal to 20 nano seconds
constant CLK_PERIOD : time:= 20 ns;

BEGIN
--Unit under test is the module subtraction. Notice
--that it is not case sensitive
UUT : subtraction
--Defining external interface signals. This
--associates ports of the named entity with
--signals in the current architecture.
PORT MAP (
BROW_IN => br_in,
XY_IN => xy_in,
BROW_OUT => br_out,
DIV_OUT => div_out

);

Now we have assigned the inputs and outputs for our subtractor to the appropriate signals in the simulation, and we have defined a basic clock. The next section of code will define the behavior of each of the input signals in the simulation. All we will be doing here is cycling through all the possible combinations of inputs with each clock cycle.

--Two separate processes that provide for the two
--separate and distinct inputs. Both processes
--will run at the same time. A process is a collection
--of "sequential" statements executing in parallel with
--other concurrent statements or processes
--Clock for BR_IN
CLOCK: process
begin

br_in <= '0';
wait for CLK_PERIOD;
br_in <= '1';
wait for CLK_PERIOD;

end process;

--Clock for XY_IN
CLOCK2: process
begin

xy_in <= "00";
wait for CLK_PERIOD;
xy_in <= "01";
wait for CLK_PERIOD;
xy_in <= "10";
wait for CLK_PERIOD;
xy_in <= "11";
wait for CLK_PERIOD;

end process;

END testbench_arch;

Notice here that with each clock pulse, the simulation should cycle through all the possible input combinations.

Here is the complete testbench code.

LIBRARY IEEE;
USE IEEE.std_logic_1164.all;
USE IEEE.STD_LOGIC_TEXTIO.ALL;
USE STD.TEXTIO.ALL;

--Declaration of ModelSim libraries used
LIBRARY UNISIM;
LIBRARY XILINXCORELIB;


ENTITY testbench IS
END testbench;

ARCHITECTURE testbench_arch OF testbench IS

COMPONENT subtraction
PORT

(
BROW_IN : in STD_LOGIC;
XY_IN : in STD_LOGIC_VECTOR (1 downto 0);
BROW_OUT : out STD_LOGIC;
DIV_OUT : out STD_LOGIC
);

END COMPONENT;

SIGNAL br_in : std_logic;
SIGNAL xy_in : std_logic_vector (1 downto 0);
SIGNAL br_out : std_logic;
SIGNAL div_out : std_logic;

--Defining the variable "CLK_PERIOD" to be
--equal to 20 nano seconds
constant CLK_PERIOD : time:= 20 ns;

BEGIN
--Unit under test is the module subtraction. Notice
--that it is not case sensitive
UUT : subtraction
--Defining external interface signals. This
--associates ports of the named entity with
--signals in the current architecture.
PORT MAP

(
BROW_IN => br_in,
XY_IN => xy_in,
BROW_OUT => br_out,
DIV_OUT => div_out

);

 

--Two separate processes that provide for the two
--separate and distinct inputs. Both processes
--will run at the same time. A process is a collection
--of "sequential" statements executing in parallel with
--other concurrent statements or processes
--Clock for BR_IN
CLOCK: process
begin

br_in <= '0';
wait for CLK_PERIOD;
br_in <= '1';
wait for CLK_PERIOD;

end process;

--Clock for XY_IN
CLOCK2: process

begin

xy_in <= "00";
wait for CLK_PERIOD;
xy_in <= "01";
wait for CLK_PERIOD;
xy_in <= "10";
wait for CLK_PERIOD;
xy_in <= "11";
wait for CLK_PERIOD;

end process;

END testbench_arch;

Again, you will need to save this new file. You must name it subtract_tb.vhd. Th is serves to remind you that this file will be a ModelSim testbench.


Once you have saved the testbench file, you will need to add it to the project. Again, you should click on the Project drop-down menu, and select Add Source. When the dialog box appears, select your subtract_tb.vhd file and press Open. Then, the Choose Source Type dialog box will open. Be sure to select VHDL Test Bench. Click OK .


Now, you will be returned to Project Navigator. The subtract_tb.vhd file has been highlighted in the Sources in Project window. Additionally, you will see that in the Processes for Current Source window, something called ModelSim Simulator will appear. Expand it, and you will see four options:

Simulate Behavioral VHDL Model
Simulate Post-Translate VHDL Model
Simulate Post-Map VHDL Model
Simulate Post-Place & Route VHDL Model

Before we go on, notice that if you click on your subtract.vhd source in the Sources in Project window, you will see that all the options for that file will appear in the Processes for Current Source window, and vice-versa. Now, make sure that subtract_tb.vhd is highlighted in the first window, and double-click on Simulate Behavioral VHDL Model in the second window.

When you do that, Xilinx will launch the ModelSim software. After some time, you will find several new windows have been spawned on your Taskbar. Locate the one labeled wave - default and expand it.

Initially, it will be zoomed in far too much for you to see anything useful. To solve this problem, click on the Zoom drop-down menu, and select Zoom Full. You will now see the entire simulation from start to finish. Now, you can zoom in once or twice (using the magnifying glass with the '+' in the center) to see something useful.

You can now take the bold cyan line and drag it through the simulation, observing the inputs and outputs as they change over time. Take a moment to verify that the subtractor is working, that is, that the div_out and the br_out outputs match what you have on the Karnaugh-maps and truth tables.

Now, you will have a chance to modify the testbench. The current testbench, as neat as it is, will not give us quite the demonstration of the subtractor's functionality that we are looking for. For better results, we would want to cycle through all four possible combinations of the xy_in input while the br_in input is low, and then cycle through the four combinations again, with the br_in input being high this time.

Find the window marked ModelSim XE 5.6a and close it. This will cause you to exit ModelSim and return to Project Navigator. Click on the window containing your subtract_tb code . There, you must modify CLOCK: process so that instead of waiting for just one clock-cycle, it waits for four clock-cycles. (Hint: Like in C/C++, you can simply add *4 to each CLK_PERIOD in order to expand the number of clock-cycles it has to wait.) When you have done this, save the file and restart ModelSim.

 

alnz - Last update: June 21, 2004