Joseph Tarango
Department of Computer Science and Engineering
Bourns College of Engineering
University of California, Riverside

Home
CS122A | Syllabus
Intro to FPGAs | Basics of VHDL | Intro to Xilinx | Turnin
Lab 1 | Lab 2 | Lab 3 | Lab 4 | Lab 5 | Project

Integrating a VHDL Design into a Peripheral

Tutorial Overview

In this tutorial we integrate one or more VHDL files. Sometimes we have a VHDL design that we developed in ISE, or some other program, and we would like to bring it into the EDK as a peripheral. In this tutorial, we will create a multiplier peripheral.

The multiplier will take in two 16 bit unsigned inputs and have a 32 bit unsigned output. A single 32 bit write to the peripheral will contain the two 16 bit inputs, separated by the lower and higher 16 bits. A single 32 bit read from the peripheral will contain the result from the multiplication of the two 16 bit inputs. Instead of registers, we will use a read and write FIFO for the interface with the software application. In this way, the peripheral write FIFO can be loaded with a number of multiplications to perform, and the results can be pushed into the read FIFO for the software application to retrieve at its convenience. Practically, this design does not serve much purpose, but it is a simple demonstration of integrating VHDL designs into peripherals.

Picking up where we left off from the Base System Builder

Create the Multiplier Peripheral

Follow these steps to create the multiplier peripheral.

  1. Select from the menu "Hardware->Create or Import Peripheral".
  2. Click "Next".
  3. Select "Create templates for a new peripheral" and click "Next".
  4. We must now decide where to place the files for the peripheral. They can be placed within this project, or they can be made accessible to other projects. Select "To an XPS project". Click "Next".
  5. On the "Name and Version" page, type "my_multiplier" for the peripheral name. Click "Next".
  6. On the "Bus Interface" page, select "Processor Local Bus" (PLB) and click "Next".
  7. On the "IPIF Services" page, we can make the Peripheral Wizard generate our VHDL template to include different features. Select "Read/Write FIFO" and "Include data phase timer", un-tick everything else and click "Next".
  8. On the "Slave Interface" page, click "Next".
  9. For the FIFO Service settings, leave the defaults ticked: Include read FIFO, Include write FIFO, Use packet mode, Use vacancy calculation. Choose a FIFO depth of 8.
  10. On the "IP Interconnect" page we can customize our connection to the PLB but we will leave everything as is for simplicity. Click "Next".
  11. On the "(OPTIONAL) Peripheral Simulation Support" page, we can specify if we want the wizard to create a simulation platform for our peripheral. Click "Next" without ticking the option to generate.
  12. After the "Peripheral Implementation Support" page, the wizard will generate all the template files for us. Tick "Generate ISE and XST project files" and "Generate template driver files". Click "Next".
  13. Click "Finish". Now our templates are created.
  14. Create the Multiplier core in VHDL

    Follow these steps to create the Multiplier core:

  15. Select "File->New". This will open a new text document that we will use for the multiplier VHDL source code.
  16. Copy and paste the following code into the new text document. Save the file as "multiplier.vhd" in the "pcores\my_multiplier_v1_00_a\hdl\vhdl" folder.
  17. library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_arith.all;
    use ieee.std_logic_unsigned.all;
    
    entity multiplier is
      port(
        clk : in std_logic;
        a   : in std_logic_vector(15 downto 0);
        b   : in std_logic_vector(15 downto 0);
        p   : out std_logic_vector(31 downto 0)
      );
    end multiplier;
    
    architecture IMP of multiplier is
    
    begin
      process (clk)
      begin
        if (clk'event and clk = '1') then
          p <= unsigned(a) * unsigned(b);
        end if;
      end process;
    end IMP;
    

    Modify the .PAO file

    The .pao file contains a list of all the source files that compose our peripheral. We use this list when we run the Peripheral Wizard in Import mode. Now that we have added another source file to the project ("multiplier.vhd"), we must include it in the .pao file.

  18. Select "File->Open" and browse to the "pcores\my_multiplier_v1_00_a\data" folder. Select the file "my_multiplier_v2_1_0.pao" and click "Open".
  19. At the bottom of this file you will see these two lines:
    lib my_multiplier_v1_00_a user_logic vhdl
    lib my_multiplier_v1_00_a my_multiplier vhdl
  20. Add the line "lib my_multiplier_v1_00_a multiplier vhdl" just above those two lines.
  21. Save the file.
  22. Now we can use this .pao file with the Peripheral Wizard and it will know to include the "multiplier.vhd" file in our design. Notice that the .pao file lists the source files in hierarchical order. Thus if you have a VHDL design consisting of multiple files, it is important to know the hierarchical order of your components. The components at the top of the chain are listed at the bottom of the file.

    Modify the Peripheral

    Now we will add code in our peripheral template to instantiate a multiplier core and we will connect it to the read and write FIFOs.

  23. Select from the menu "File->Open" and look in the project folder.
  24. Open the folders: "pcores\my_multiplier_v1_00_a\hdl\vhdl". This folder contains two source files that describe our peripheral "my_multiplier.vhd" and "user_logic.vhd". The first file is the main part of the peripheral and it implements the interface to the OPB. The second file is where we place our custom logic to make the peripheral do what we need it to do. This part is instantiated by the first file.
  25. Open the file "user_logic.vhd". We will need to modify this source code to instantiate the multiplier and connect it to the read and write FIFOs.
  26. Find the line of code that says "USER signal declarations added here" and add the following lines of code just below.
  27. component multiplier
       port(
          clk : in std_logic;
          a : in std_logic_vector(15 downto 0);
          b : in std_logic_vector(15 downto 0);
          p : out std_logic_vector(31 downto 0)
       );
    end component;
  28. Find the line of code that says "USER logic implementation added here" and add the following lines of code just below.
  29. multiplier_0 : multiplier
      port map (
        clk => Bus2IP_Clk,
        a => WFIFO2IP_Data(16 to 31),
        b => WFIFO2IP_Data(0 to 15),
        p => IP2RFIFO_Data
      );
  30. Find the line of code that says "IP2RFIFO_Data <= WFIFO2IP_Data;" and comment it out (or delete it). Now, the read FIFO will be loaded with data from the multiplier.
  31. Save and close the file.
  32. Import the Multiplier Peripheral

    Now we will use the Peripheral Wizard in Import mode.

  33. Select from the menu "Hardware->Create or Import Peripheral" and click "Next".

  34. Select "Import existing peripheral" and click "Next".
  35. Select "To an XPS project", ensure that the folder chosen is the project folder, and click "Next".
  36. For the name of the peripheral, select "my_multiplier". Tick "Use version" and select the same version number that we originally created. Click "Next". It will ask if we are willing to overwrite the existing peripheral and we should answer "Yes".

  37. Now we are asked about the files that make up our peripheral. Tick "HDL source files" and click "Next".
  38. Select "Use existing Peripheral Analysis Order file (*.pao)" and click "Browse". From the project folder, go to "pcores\my_multiplier_v1_00_a\data" and select the "my_multiplier_v2_1_0.pao" file. Click "Next".
  39. On the HDL analysis information page, if you scroll down, you will see the "multiplier.vhd" file listed third from the bottom. Click "Next". The wizard will mention if any errors are found in the design.
  40. On the Bus Interfaces page, tick "PLB Slave" and click "Next".
  41. On the SPLB: Port page, click "Next".
  42. On the SPLB: Parameter page, click "Next".
  43. On the Identify Interrupt Signals, un-tick "Select and configure interrupt" then click "Next"
  44. On the "Parameter Attributes" page, make sure "List User Parameters Only" is selected then click "Next".
  45. On the "Port Attributes" page, make sure "List User Ports Only" is selected then click "Next".
  46. Click "Finish".
  47. The multiplier peripheral should now be accessible through the "IP Catalog->Project Local pcores" in the XPS interface.

    Create an Instance of the Peripheral

    Follow these steps to create an instance of the peripheral in the project.

  48. From the "IP Catalog" find the "my_multiplier" IP core in the "Project Repository" group. Right click on the core and select "Add IP".
  49. A new window will open, click "OK". The dialogue is to select the address range, since we are not selecting a valid range we will get an error in the command line. Such as:
    WARNING:EDK:2137 - Peripheral my_multiplier_0 is not accessible from any processor in the system. Check Bus Interface connections and address parameters.

  50. From the "System Assembly View" using the "Bus Interface" filter, connect the "my_multiplier_0″ to the PLB bus.
  51. Click on the "Addresses" filter. Change the "Size" for "my_multiplier_0" to 64K. then click "Generate Addresses".
  52. Click "Generate Addresses" in the upper right hand corner of the window.

    The Console will give you something like this output:
    Address Map for Processor microblaze_0
      (0000000000-0x00007fff) dlmb_cntlr	dlmb
      (0000000000-0x00007fff) ilmb_cntlr	ilmb
      (0x81400000-0x8140ffff) LEDs_8Bit	mb_plb
      (0x81420000-0x8142ffff) DIP_Switches_4Bit	mb_plb
      (0x81440000-0x8144ffff) Buttons_4Bit	mb_plb
      (0x84000000-0x8400ffff) RS232_DCE	mb_plb
      (0x84400000-0x8440ffff) mdm_0	mb_plb
      (0xcde00000-0xcde0ffff) my_multiplier_0	mb_plb
    Generated Addresses Successfully
    				
  53. Now we have an instance of the multiplier peripheral in our project and our hardware design is complete.

    Modify the Software Application

    Now all we need to do is modify the software application to test our multiplier peripheral.

  54. From SDK create a new "Xilinx C Project" and Select "Empty Application" or  "Peripheral Tests" (It is recommended to start from "Peripheral Tests" because most of the other I/O pointers and libraries have already been setup). Add the files from the "C:\Spartan3EStarterBoard\drivers\my_multiplier_v1_00_a\src" to your new project "src" folder. If you are confused at this point you need to complete the SDK tutorial.
  55. Use this code to test the design.
#include "xparameters.h"
#include "xbasic_types.h"
#include "xstatus.h"
#include "my_multiplier.h"

Xuint32 *baseaddr_p = (Xuint32 *)XPAR_MY_MULTIPLIER_0_BASEADDR;

int main (void) {
  Xuint32 i;
  Xuint32 temp;
  Xuint32 baseaddr;

  // Clear the screen
  xil_printf("%c[2J",27);

  // Check that the peripheral exists
  XASSERT_NONVOID(baseaddr_p != XNULL);
  baseaddr = (Xuint32) baseaddr_p;

  xil_printf("Multiplier Test\n\r");

  // Reset read and write packet FIFOs to initial state
  MY_MULTIPLIER_mResetWriteFIFO(baseaddr);
  MY_MULTIPLIER_mResetReadFIFO(baseaddr);

  // Push data to write packet FIFO
  for(i = 1; i <= 4; i++ ){
    temp = (i << 16) + i;
    xil_printf("Wrote: 0x%08x \n\r", temp);
    MY_MULTIPLIER_mWriteToFIFO(baseaddr,0, temp);
  }

  // pop data out from read packet FIFO
  for(i = 0; i < 4; i++){
    temp = MY_MULTIPLIER_mReadFromFIFO(baseaddr,0);
    xil_printf("Read:  0x%08x \n\r", temp);
  }

  // Reset the read and write FIFOs
  MY_MULTIPLIER_mResetWriteFIFO(baseaddr);
  MY_MULTIPLIER_mResetReadFIFO(baseaddr);

  xil_printf("End of test\n\n\r");

  // Stay in an infinite loop
  while(1){
  }
}
  1. Save and close the file.

Download and Test the Project

  1. Open Cutecom the same as in Lab 3 Part 1 by typing "cutecom&" in a terminal. The set up options for the serial connection are: Device (/dev/ttyS0) Baud Rate (9600), Data bits (8), Stop bits (1) Parity (none), Open for (Reading/Writing).
  2. Turn on the Spartan-3E board.
  3. From the XPS software, select "Device Configuration->Download Bitstream".

The output should look as shown below:

Multiplier Test
Wrote: 0x00010001
Wrote: 0x00020002
Wrote: 0x00030003
Wrote: 0x00040004
Read: 0x00000001
Read: 0x00000004
Read: 0x00000009
Read: 0x00000010
End of test

Remember, those numbers are in hexadecimal so 0x10 = 16!