Integrating a VHDL Design into a Peripheral


Tutorial Overview

This tutorial is similar to the previous one titled: Integrating a Blackbox into a Peripheral however in this case, instead of integrating an .ngc file into a peripheral, 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 the same multiplier peripheral as was created in the previous tutorial.

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.

This tutorial contains screenshots to guide you through the entire implementation process. Click on the images to view a higher resolution.

Requirements

Before following this tutorial, you will need to do the following:

  • Buy an ML505/ML506/ML507 or XUPV5 board if you don’t already have one. Xilinx supplies the ML50x boards, but the best deal is the XUPV5 from Digilent. Click the Digilent link for more information.

Create the Basic Project

Follow these steps to create the basic project:

  1. Open XPS. From the dialog box, select “Base System Builder wizard” and OK.
  2. You will be asked to specify which folder to place the project. Click “Browse” and create a new folder for the project. Click “OK”.  
  3. We are given the choice to create a new project or to create one using the template of another project. Tick “I would like to create a new design” and click “Next”.
  4. On the “Select Board” page, select “Xilinx” as the board vendor. Then select the board you are using (eg. “Virtex 5 ML505 Evaluation Platform”). Select “1” as the board revision. Click “Next”. (Note: If you are using the XUPV5 (ML509) board, you might find that it is not listed. Select “Virtex 5 ML505 Evaluation Platform” instead.)  
  5. On the “Select Processor” page, we normally have a choice between using the PowerPC “hard” processor, or the Microblaze “soft” processor. Since the Virtex-5 does not contain any PowerPCs, we can only select Microblaze. Click “Next”.  
  6. On the “Configure Microblaze” page, select the clock frequency to be 125MHz. We will use the RS232 port for debugging rather than the JTAG, so select “No debug”. Click “Next”.  
  7. In selecting the Additional IO Interfaces, leave RS232_Uart_1 ticked and un-tick everything else.  
  8. On the “Add Internal Peripherals” page, click “Next”.  
  9. On the “Software Setup” page, select RS232_Uart_1 for both STDIN and STDOUT. Un-tick “Memory Test” and leave “Peripheral Test” ticked. Click “Next”.  
  10. Click “Generate”.
  11. Click “Finish”.
  12. If you are using the XUPV5 (ML509) board and you selected “Virtex 5 ML505 Evaluation Platform” in step 4 above, you must select the right Virtex-5 version in the project settings. Select “Project->Project Options” and set the Virtex-5 version to “XC5VLX110T”, package “FFG1136” and speed grade “-1”.

Create the Multiplier Peripheral

Follow these steps to create the multiplier peripheral.

  1. Select from the menu “Hardware->Create or Import Peripheral”. Click “Next”.
  2. Select “Create templates for a new peripheral” and click “Next”.  
  3. 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”.  
  4. On the “Name and Version” page, type my_multiplier for the peripheral name. Click “Next”.  
  5. On the “Bus Interface” page, select “Processor Local Bus” (PLB) and click “Next”.  
  6. 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”.  
  7. On the “Slave Interface” page, click “Next”.  
  8. 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 512.  
  9. On the “IP Interconnect” page we can customize our connection to the PLB but we will leave everything as is for simplicity. Click “Next”.
  10. On the “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.
  11. 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”.
  12. Click “Finish”. Now our templates are created.

Create the Multiplier core in VHDL

Follow these steps to create the Multiplier core:

  1. Select “File->New”. This will open a new text document that we will use for the multiplier VHDL source code.
  2. Copy and paste the following code into the new text document.
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;
  1. Save the file as multiplier.vhd in the pcores\my_multiplier_v1_00_a\hdl\vhdl folder.

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.

  1. 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”.
  2. 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
  1. Add the line lib my_multiplier_v1_00_a multiplier vhdl just above those two lines.
  2. Save the file.

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.

  1. Select from the menu “File->Open” and look in the project folder.
  2. 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.
  3. 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.
  4. Find the line of code that says --USER signal declarations added here and add the following lines of code just below.
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;
  1. Find the line of code that says --USER logic implementation added here and add the following lines of code just below.
multiplier_0 : multiplier
  port map (
    clk => Bus2IP_Clk,
    a => WFIFO2IP_Data(16 to 31),
    b => WFIFO2IP_Data(0 to 15),
    p => IP2RFIFO_Data);
  1. 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.
  2. Save and close the file.

Import the Multiplier Peripheral

Now we will use the Peripheral Wizard in Import mode.

  1. Select from the menu “Hardware->Create or Import Peripheral” and click “Next”.
  2. Select “Import existing peripheral” and click “Next”.  
  3. Select “To an XPS project”, ensure that the folder chosen is the project folder, and click “Next”.
  4. For the name of the peripheral, type 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”.  
  5. Now we are asked about the files that make up our peripheral. Tick “HDL source files” and click “Next”.  
  6. 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”.  
  7. 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.
  8. On the Bus Interfaces page, tick “PLB Slave” and click “Next”.  
  9. On the SPLB: Port page, click “Next”.
  10. On the SPLB: Parameter page, click “Next”.
  11. On the “Parameter Attributes” page, click “Next”.
  12. On the “Port Attributes” page, click “Next”.
  13. Click “Finish”.

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.

  1. From the “IP Catalog” find the my_multiplier IP core in the “Project Repository” group. Right click on the core and select “Add IP”.  
  2. From the “System Assembly View” using the “Bus Interface” filter, connect the my_multiplier_0 to the PLB bus.  
  3. Click on the “Addresses” filter. Change the “Size” for my_multiplier_0 to 64K. Then click “Generate Addresses”.

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.

  1. From the “Applications” tab, open “Sources” within the “Project: TestApp_Peripheral” tree. Open the TestApp_Peripheral.c source file.
  2. Replace all the code in this file with the following source.
#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 a Hyperterminal window with the required settings. For the correct settings, see Hyperterminal Settings.
  2. Turn on the ML505 board.
  3. From the XPS software, select “Device Configuration->Download Bitstream”.

The Hyperterminal output should look as shown in the image below:

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

The project folder for this tutorial can be downloaded in a compressed ZIP file MultiplierVHDL-EDK10-1.zip . Right-click on the link and select “Save Link As”.

In the next tutorial, Timer with Interrupts, we show how to create a peripheral that is capable of generating interrupts on the PowerPC.


See also