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 0×10 = 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.

Tagged with:  

Tutorial Overview

Sometimes we have an .ngc file from CORE Generator (or some other source) that we would like to bring into EDK as a peripheral. This project is a simple example of integrating a blackbox design into a peripheral generated by the Peripheral Wizard. We will first create a blackbox multiplier using the Xilinx CORE Generator and then we will use the generated .ngc file in our 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 blackbox 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 with CORE Generator

Follow these steps to generate the Multiplier.

  1. Leaving XPS running and your project open, from the “Start” menu, open Xilinx CORE Generator.
  2. Select “File->New Project”.
  3. Click “Browse” and select an appropriate location for a Coregen project. Within a Coregen project, you can create several cores that don’t necessarily relate to each other. Just select the folder where you normally place your projects, for example “C:\ML505\Projects”, and create a sub-folder called “Cores”. Open this folder and click “OK”.
  4. You will be asked for the specifications of the FPGA you are using. All the cores you generate under this CORE Generator project file will be customized for the FPGA you specify here. Under the “Part” tab, select these options: Family “Virtex5″, Device “xc5vlx50t”, Package “ff1136″, Speed grade “-1″. Click “OK”. Note: If you are not using the ML505 board, these specifications may not apply to you. You will have to enter the details corresponding to the specific FPGA that you are using.

  5. When you have created your CORE Generator project, click on the “View by Function” tab to get a list of cores that you are able to generate.
  6. Open “Math Functions->Multipliers” and double-click on “Multiplier”.
  7. A dialog box should open to allow you to select the features of the Multiplier you want. For “Multiplier Type” select “Parallel Multiplier”. Select “Signed” for both port inputs and give them a width of 16 bits. Click “Next”.
  8. For “Multiplier Construction” select “Use Mults”. In the “Optimization Options”, select “Speed Optimized”.
  9. Click “Finish”. Your Multiplier core will be generated and CORE Generator will display a list of all the generated files. Close this window, and close CORE Generator.

Modify the Peripheral

When we create a peripheral with read and write FIFOs, the Peripheral Wizard generates code in the user_logic.vhd file that loops the data from the write FIFO to the read FIFO. This makes it easy to test for the first time because one can write data to the peripheral and verify that it was received by reading back the same data. In our case, the loop-back design reduces the number of modifications we need to make because we want to do the same thing, only we want to first pass the data through the multiplier before putting it through to the read FIFO. Now we will add code in our peripheral template to instantiate a multiplier core and 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 Peripheral

Now we will use the Peripheral Wizard again, but this time using the import function.

  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. As usual we tick “HDL source files” and in this case, to include our .ngc file, we will also tick “Netlist files”. Now 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, 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. Now we should arrive at the “Netlist Files” page where we are asked to locate any netlist files or black-box components that are instantiated in our peripheral. Click “Select Files” and navigate your way to the CORE Generator project folder “Cores” that you created earlier. You will find a file called “multiplier.ngc”. Select the file and click “Open”. The file should now be listed on the “Netlist Files” page. Click “Next”.
  14. Click “Finish”.

When you include .ngc files in a peripheral design as we just did, the Peripheral Wizard does some things behind-the-scenes that are important to know. Firstly, it makes a copy of the .ngc file and places it in the “pcores\my_multiplier_v1_00_a\netlist” folder (if that folder doesn’t exist, it creates one). Secondly, it creates a file called “my_multiplier_v2_1_0.bbd” in the “pcores\my_multiplier_v1_00_a\data” folder with the following contents:

############################################################
## Filename:          C:\ML505\Projects\MultiplierNGC\pcores-
/my_multiplier_v1_00_a/data/my_multiplier_v2_1_0.bbd
## Description:       Black Box Definition
## Date:              Mon Feb 11 16:53:42 2008 (by Create a-
nd Import Peripheral Wizard)
############################################################

Files
############################################################
multiplier.ngc

Finally, the Peripheral Wizard adds these two lines to the “my_multiplier_v2_1_0.mpd” file in the “pcores\my_multiplier_v1_00_a\data” folder:

OPTION STYLE = MIX
OPTION RUN_NGCBUILD = TRUE

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 and save the file.
#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 0×10 = 16!

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

In the next tutorial, Integrating a VHDL Design into a Peripheral, we show how to integrate a custom VHDL design into the Peripheral Wizard templates.

Tagged with:  

Tutorial Overview

In this tutorial we will create a simple project that uses our own IP peripheral (instead of using the XPS General Purpose IO peripheral provided by Xilinx) to read from the DIP switches and write to the LEDs. The software application will display the DIP switch values on the LED outputs and also send the DIP switch values to the UART.

Any custom logic (IP) that you design must connect to the PLB to communicate with the Microblaze processor. When connected to the PLB, the IP becomes part of the memory map accessible to the Microblaze. The IP will have a base address and a high address signifying where in the memory map it resides, and how much of the memory map it occupies. The Microblaze interacts with the IP as though it were part of the memory.

In this example, we will use the Peripheral Wizard to create a basic IP peripheral that connects to the PLB and implements one 32 bit register. Writing to the peripheral through the PLB will allow us to change the contents of the register. The outputs of the register will drive the LEDs. We will modify the core so that reading from it through the PLB will return the DIP switch settings. The diagram below illustrates the peripheral and connections.

Figure: Peripheral for controlling the LEDs and reading the DIP switches

Note that another possibility for this design is to use another register for passing the DIP switch values through to the OPB handler. In this design, only one register was used to keep the design simple and to minimize the changes to the template.

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. The Base System Builder can produce sample software applications to run on the Microblaze and test the memory and peripherals. For this project we will not need them as we will create our own. Un-tick “Memory Test” and “Peripheral Test”. 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 Peripheral

We now create our custom peripheral using the Peripheral Wizard.

  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_peripheral” 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 “User logic software register” and “Include data phase timer”, un-tick everything else and click “Next”.
     
  7. On the “Slave Interface” page, click “Next”.
     
  8. On the “User S/W Register” page, choose “1″ for the number of software accessible registers. As the Microblaze is a 32-bit processor, we will choose 32-bits for the size of the register, even though we only need 8-bits for the 8 LEDs.
     
  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 and we can modify them to suit our application.

Modify the Peripheral

The template created by the Peripheral Wizard implements a 32-bit register that we can read and write to via the PLB. When the peripheral is instantiated, we could access the register by reading and writing to the base address of the peripheral. We need to modify this functionality slightly, because of two things:

  1. We want to connect the outputs of the register to the LEDs – to achieve this we need to create an output port on the peripheral and connect it to the output of the register.
  2. We want to make all reads from the peripheral return the values of the DIP switches – to achieve this we need to create an input port on the peripheral and make it return these values on every read request.

Follow these steps to modify the peripheral:

  1. Select from the menu “File->Open” and browse to “pcores\my_peripheral_v1_00_a\hdl\vhdl” from the project folder. This folder contains two source files that describe our peripheral: “my_peripheral.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.
  2. Open the file “my_peripheral.vhd”. We will need to add two ports to this source code, one for the LEDs and one for the DIP switches.
  3. Find the line of code that says “–ADD USER PORTS BELOW THIS LINE” and add these two lines of code:
  LED_Data : out std_logic_vector(0 to 7);
  DIP_Data : in std_logic_vector(0 to 7);
  1. Find the line of code that says “–MAP USER PORTS BELOW THIS LINE” and add these two lines of code:
  LED_Data => LED_Data,
  DIP_Data => DIP_Data,
  1. Save and close the file.
  2. Open the file “user_logic.vhd”. We will need to modify this source code to include the two new ports and to modify the behaviour.
  3. Find the line of code that says “–ADD USER PORTS BELOW THIS LINE” and add the following two lines of code.
  LED_Data : out std_logic_vector(0 to 7);
  DIP_Data : in std_logic_vector(0 to 7);
  1. Find the line of code that says “–USER logic implementation added here” and add the following line of code below. This connects the LED port to the first 8 bits of the register output.
  LED_Data <= slv_reg0(24 to 31);
  1. Find the line of code that says “IP2Bus_Data <= slv_ip2bus_data when slv_read_ack = ’1′ else (others => ’0′);” and replace it with the code below. Now, when the peripheral is read on the OPB, it will always return the DIP switch settings. As the bus is 32 bits long, we just fill the unused bits with zeros using the “others” keyword.
  IP2Bus_Data(0 to 23) <= (others=>'0');
  IP2Bus_Data(24 to 31) <= DIP_Data(0 to 7);
  1. Save and close the file.

Import the Peripheral

Now we will use the Peripheral Wizard again, but this time using the import function.

  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_peripheral”. 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. 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_peripheral_v1_00_a\data” and select the “my_peripheral_v2_1_0.pao” file. Click “Next”.
     
  7. On the HDL analysis information page, 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 “Identify Interrupt Signals” page, un-tick “Select and configure interrupts” and click “Next”. We don’t need interrupts for our design.
     
  12. On the “Parameter Attributes” page, click “Next”.
  13. On the “Port Attributes” page, click “Next”.
  14. Click “Finish”.

Now we have a peripheral that is ready to use and it should be accessible through the “IP Catalog->Project Repository” in the XPS interface. Note that although we can access it through the IP Catalog, other projects will not find it there because it is only associated with our project, as we specified in the Peripheral Wizard.

Create an Instance of the Peripheral

Now we are ready to create an instance of the peripheral into our project which can then be downloaded into the FPGA and tested by using simple code running on the Microblaze.

  1. From the “IP Catalog” find the “my_peripheral” IP core in the “Project Local pcores” group. Right click on the core and select “Add IP”.
     
  2. From the “System Assembly View” using the “Bus Interface” filter, connect the “my_peripheral_0″ to the “mb_plb” (PLB) bus.
     
  3. Click on the “Ports” filter and open the “my_peripheral_0″ tree. There will be two ports listed “LED_Data” and “DIP_Data”. Type in a net name for each of them. The net name can be anything you like, but for this project let us call them “LED_Data” and “DIP_Data” for simplicity. For each net, use the option “Make External”. If you then click open the “External Ports” tree, you should see these nets listed.
     
  4. Click on the “Addresses” filter. Change the “Size” for “my_peripheral_0″ to 64K. Then click “Generate Addresses”.

We have now created an instance of the peripheral in our design.

Link the External Ports to FPGA Pins

Now we have made the LED and DIP ports external however we need to link them to specific pins on the FPGA. We will use the schematic for the ML505 to get the correct pin numbers for each of the LEDs and DIP switches. Then we modify the User Constraints File (UCF) to include these pin assignments.

  1. Click the “Project” tab and double click on the UCF file to open it.
  2. Find the comment “## IO Devices constraints” and add the following lines of code below it:
#### Module LEDs_8Bit constraints

Net LED_Data_pin<0> LOC = AE24;
Net LED_Data_pin<0> IOSTANDARD=LVCMOS18;
Net LED_Data_pin<0> PULLDOWN;
Net LED_Data_pin<0> SLEW=SLOW;
Net LED_Data_pin<0> DRIVE=2;
Net LED_Data_pin<1> LOC = AD24;
Net LED_Data_pin<1> IOSTANDARD=LVCMOS18;
Net LED_Data_pin<1> PULLDOWN;
Net LED_Data_pin<1> SLEW=SLOW;
Net LED_Data_pin<1> DRIVE=2;
Net LED_Data_pin<2> LOC = AD25;
Net LED_Data_pin<2> IOSTANDARD=LVCMOS18;
Net LED_Data_pin<2> PULLDOWN;
Net LED_Data_pin<2> SLEW=SLOW;
Net LED_Data_pin<2> DRIVE=2;
Net LED_Data_pin<3> LOC = G16;
Net LED_Data_pin<3> IOSTANDARD=LVCMOS25;
Net LED_Data_pin<3> PULLDOWN;
Net LED_Data_pin<3> SLEW=SLOW;
Net LED_Data_pin<3> DRIVE=2;
Net LED_Data_pin<4> LOC = AD26;
Net LED_Data_pin<4> IOSTANDARD=LVCMOS18;
Net LED_Data_pin<4> PULLDOWN;
Net LED_Data_pin<4> SLEW=SLOW;
Net LED_Data_pin<4> DRIVE=2;
Net LED_Data_pin<5> LOC = G15;
Net LED_Data_pin<5> IOSTANDARD=LVCMOS25;
Net LED_Data_pin<5> PULLDOWN;
Net LED_Data_pin<5> SLEW=SLOW;
Net LED_Data_pin<5> DRIVE=2;
Net LED_Data_pin<6> LOC = L18;
Net LED_Data_pin<6> IOSTANDARD=LVCMOS25;
Net LED_Data_pin<6> PULLDOWN;
Net LED_Data_pin<6> SLEW=SLOW;
Net LED_Data_pin<6> DRIVE=2;
Net LED_Data_pin<7> LOC = H18;
Net LED_Data_pin<7> IOSTANDARD=LVCMOS25;
Net LED_Data_pin<7> PULLDOWN;
Net LED_Data_pin<7> SLEW=SLOW;
Net LED_Data_pin<7> DRIVE=2;

#### Module DIP_Switches_8Bit constraints

Net DIP_Data_pin<0> LOC=U25;
Net DIP_Data_pin<0> IOSTANDARD=LVCMOS18;
Net DIP_Data_pin<0> PULLDOWN;
Net DIP_Data_pin<0> SLEW=SLOW;
Net DIP_Data_pin<0> DRIVE=2;
Net DIP_Data_pin<1> LOC=AG27;
Net DIP_Data_pin<1> IOSTANDARD=LVCMOS18;
Net DIP_Data_pin<1> PULLDOWN;
Net DIP_Data_pin<1> SLEW=SLOW;
Net DIP_Data_pin<1> DRIVE=2;
Net DIP_Data_pin<2> LOC=AF25;
Net DIP_Data_pin<2> IOSTANDARD=LVCMOS18;
Net DIP_Data_pin<2> PULLDOWN;
Net DIP_Data_pin<2> SLEW=SLOW;
Net DIP_Data_pin<2> DRIVE=2;
Net DIP_Data_pin<3> LOC=AF26;
Net DIP_Data_pin<3> IOSTANDARD=LVCMOS18;
Net DIP_Data_pin<3> PULLDOWN;
Net DIP_Data_pin<3> SLEW=SLOW;
Net DIP_Data_pin<3> DRIVE=2;
Net DIP_Data_pin<4> LOC=AE27;
Net DIP_Data_pin<4> IOSTANDARD=LVCMOS18;
Net DIP_Data_pin<4> PULLDOWN;
Net DIP_Data_pin<4> SLEW=SLOW;
Net DIP_Data_pin<4> DRIVE=2;
Net DIP_Data_pin<5> LOC=AE26;
Net DIP_Data_pin<5> IOSTANDARD=LVCMOS18;
Net DIP_Data_pin<5> PULLDOWN;
Net DIP_Data_pin<5> SLEW=SLOW;
Net DIP_Data_pin<5> DRIVE=2;
Net DIP_Data_pin<6> LOC=AC25;
Net DIP_Data_pin<6> IOSTANDARD=LVCMOS18;
Net DIP_Data_pin<6> PULLDOWN;
Net DIP_Data_pin<6> SLEW=SLOW;
Net DIP_Data_pin<6> DRIVE=2;
Net DIP_Data_pin<7> LOC=AC24;
Net DIP_Data_pin<7> IOSTANDARD=LVCMOS18;
Net DIP_Data_pin<7> PULLDOWN;
Net DIP_Data_pin<7> SLEW=SLOW;
Net DIP_Data_pin<7> DRIVE=2;
  1. Save and close the file.

Create a Software Application

Now we need to create a simple software application to test the peripheral. The program simply needs to read and write to the peripheral through the PLB.

  1. Click on the “Applications” tab and double click “Add Software Application Project”.
  2. For the name, type “TestApp”. Select the “microblaze_0″ processor. Click “OK”.
  3. Right-click on “Default: microblaze_0_bootloop” and select “Mark to Initialize BRAMs” so that this application does not get executed on the Microblaze. It should now have a red cross through its icon.
  4. Right-click on “Project: TestApp” and select “Mark to Initialize BRAMs” to select our application for execution on the Microblaze. It should now not have a red cross through its icon.
  5. Select from the menu File->New, and choose “Text file”. Copy the code below into this new file and save the file as “TestApp.c” in a new folder called “TestApp” within the project folder.
#include "xparameters.h"
#include "my_peripheral.h"

void * my_peripheral_p;
Xuint32 my_peripheral;

int main (void) {

  unsigned int DataRead;
  unsigned int OldData;

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

  // Check and get the device address
  my_peripheral_p = XPAR_MY_PERIPHERAL_0_BASEADDR;
  XASSERT_NONVOID(my_peripheral_p != XNULL);
  my_peripheral = (Xuint32) my_peripheral_p;

  OldData = (unsigned int) 0xffffffff;
  while(1){
    // Read the state of the DIP switches
    DataRead = MY_PERIPHERAL_mReadSlaveReg0(my_peripheral, 0);
    // Send the data to the UART if the settings change
    if(DataRead != OldData){
      xil_printf("DIP Switch settings: 0x%2X\r\n", DataRead);
      // Set the LED outputs to the DIP switch values
      MY_PERIPHERAL_mWriteSlaveReg0(my_peripheral, 0, DataRead);
      // Record the DIP switch settings
      OldData = DataRead;
    }
  }
}

 

  1. Right-click on “Sources” under the “Project: TestApp” tree and select “Add Existing Files”. Select the file we just saved.

The software application simply creates a pointer to the peripheral and makes reads and writes to the peripheral through the pointer. We can obtain the address of the peripheral by looking in the “System Assembly View” using the “Addresses” filter, but in this example, we use the “XPAR_MY_PERIPHERAL_0_BASEADDR” definition given in the “xparameters.h” file which is created automatically by XPS. By using this definition, instead of a fixed value, we don’t have to modify our code each time we modify the hardware and addresses.

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. We can now download our hardware description and software application to the FPGA. From the XPS software, select “Device Configuration->Download Bitstream”.

When the application is running and Hyperterminal is open, the DIP switch settings should be seen on the LEDs and on the Hyperterminal screen. Change the DIP switches to see the message change. The Hyperterminal output should look similar to the screen shot below:

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

In the next tutorial, Timer with Interrupts, we develop our own timer peripheral so that we can make the LEDs flash.

Tagged with:  

Tutorial Overview

In the previous example, we created a project using the BSB and all of the work related to the hardware design was done by the BSB. In this example, we will create the same simple project, but this time we will add the GPIO for the LEDs manually. This way we will learn the process of adding extra peripherals to our design and we will also better understand the hardware design features of XPS.

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

What you will learn

As in the previous example, we will create the initial project using the BSB and add software applications. In addition, we will step through:

  1. How to add a standard IP peripheral without the BSB
  2. How to add constraints to your design to make links to external hardware (eg. LEDs)

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″ and “DIP_Switches_8Bit” 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. The Base System Builder can produce sample software applications to run on the Microblaze and test the memory and peripherals. For this project we will not need them as we will create our own. Un-tick “Memory Test” and “Peripheral Test”. 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 a Simple Application

Now that the basic project is made, we can create a simple software application to run on the Microblaze to test the DIP switches.

  1. Select the Applications tab. You will see a software application already there called “Default: microblaze_0_bootloop”. The purpose of this application is to occupy the Microblaze in the case that there is no other software application in the design. It simply keeps the Microblaze running in an endless loop.
  2. Double click “Add Software Application”.
     
  3. For the project name type “DIPTest”. For the processor select “microblaze_0″. Click “OK”.
     
  4. From the Applications tab, right click on the software application “Default: microblaze_0_bootloop” and select “Mark to Initialize BRAMs”. The icon will now have a small red cross signifying that it will not run on the Microblaze.
  5. Right click on the software application we just created “Project: DIPTest” and select “Mark to Initialize BRAMs”. The red cross on the icon of our software application should have been removed signifying that it will run on the Microblaze.
     
  6. From the menu select File->New, then select “Text file”. Copy the following code into the new file. Save the file as “DIPTest.c” in a folder called “DIPTest” within the project folder.
#include "xparameters.h"
#include "xbasic_types.h"
#include "xgpio.h"
#include "xstatus.h"

XGpio GpioInput;

int main (void) {

  Xuint32 status;
  Xuint32 DataRead;
  Xuint32 OldData;

  // Initialize the GPIO driver so that it's ready to use,
  status = XGpio_Initialize(&GpioInput,
                         XPAR_DIP_SWITCHES_8BIT_DEVICE_ID);
  if (status != XST_SUCCESS)
    return XST_FAILURE;
  // Set the direction for all signals to be inputs
  XGpio_SetDataDirection(&GpioInput, 1, 0xFFFFFFFF);

  // Initialize the old data register
  OldData = 0xFF;

  while(1){
    // Read the state of the DIP switches
    DataRead = XGpio_DiscreteRead(&GpioInput, 1);

    // Send the data to the UART if the settings change
    if(DataRead != OldData){
      xil_printf("DIP Switch settings: 0x%2X\r\n", DataRead);
      OldData = DataRead;
    }
  }
  return 0;
}
  1. In the “Project:DIPTest” tree, right click on “Sources” and select “Add existing files”. Select the “DIPTest.c” file that we just created and click “OK”.
     
  2. Right click on the “Project: DIPTest” application and select “Generate Linker Script” and click “Generate”.
     
  3. Select “Software->Generate Libraries and BSPs” from the menu.
  4. We can now download our hardware description and software application to the FPGA. Select “Device Configuration->Download bitstream”. When the application is running and Hyperterminal is open, the DIP switch settings should be seen. Change the DIP switches to see the UART message change.

The simple C program that we added to this project simply reads the DIP switch settings and sends a message with that data to the UART.

Add the GPIO for the LEDs

Now we will manually add the second GPIO for the LEDs.

  1. In the IP Catalog tab, open the “General Purpose IO” tree.
  2. Right click on “XPS General Purpose IO” and select “Add IP”.
     
  3. In the “System Assembly View” using the “Bus Interface” filter, you will see the new GPIO instance called “xps_gpio_0″. Click on the instance and rename it to “LEDs_8Bit”. XPS automatically modifies the “system.mss” and “system.mhs” files to include the new IP details.
  4. Connect the instance to the PLB bus by opening the “LEDs_8Bit” tree and selecting “mb_plb” for the “SPLB” bus connection.
     
  5. Double click on the “LEDs_8Bit” instance. We can modify the instance parameters using this dialog box. Type “8″ for the “GPIO Data Channel Width” and click OK.
     
  6. Select the “Ports” filter and open the “LEDs_8Bit” tree.
  7. The “GPIO_IO” net should display “No Connection”. Click on “No Connection” and type “fpga_0_LEDs_8Bit_GPIO_IO” to give the net a name.
     
  8. Click the net again to bring down the drop-down menu and select “Make External”. The newly created net should now be in the “External Ports” tree. XPS automatically modifies the “system.mhs” file to include the new external port.
  9. Select the “Addresses” filter.
  10. Click the “LEDs_8Bit” size drop-down menu and select 64K. Then click “Generate Addresses”. XPS automatically reconfigures the memory map and gives the LEDs GPIO a base address and a high address. It also automatically modifies the “system.mhs” file to update the IP address details.

We have now created an instance of the GPIO peripheral in our design.

Modify the Constraints (.UCF) File

The remaining details to include in the hardware description are the external pin connections of the LEDs. This is specified in the “system.ucf” file which is up to the user to modify according to the external hardware configuration (ie. How the FPGA pins are wired up on the ML505 board). Follow these steps to modify the constraints file:

  1. Click the Project tab, open the “Project files” tree and double click the UCF file to open it.
     
  2. Find the comment “## IO Devices constraints” and add the following lines of code below it:
#### Module LEDs_8Bit constraints

Net fpga_0_LEDs_8Bit_GPIO_IO_pin<0> LOC = AE24;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<0> IOSTANDARD=LVCMOS18;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<0> PULLDOWN;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<0> SLEW=SLOW;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<0> DRIVE=2;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<1> LOC = AD24;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<1> IOSTANDARD=LVCMOS18;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<1> PULLDOWN;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<1> SLEW=SLOW;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<1> DRIVE=2;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<2> LOC = AD25;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<2> IOSTANDARD=LVCMOS18;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<2> PULLDOWN;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<2> SLEW=SLOW;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<2> DRIVE=2;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<3> LOC = G16;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<3> IOSTANDARD=LVCMOS25;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<3> PULLDOWN;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<3> SLEW=SLOW;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<3> DRIVE=2;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<4> LOC = AD26;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<4> IOSTANDARD=LVCMOS18;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<4> PULLDOWN;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<4> SLEW=SLOW;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<4> DRIVE=2;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<5> LOC = G15;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<5> IOSTANDARD=LVCMOS25;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<5> PULLDOWN;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<5> SLEW=SLOW;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<5> DRIVE=2;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<6> LOC = L18;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<6> IOSTANDARD=LVCMOS25;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<6> PULLDOWN;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<6> SLEW=SLOW;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<6> DRIVE=2;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<7> LOC = H18;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<7> IOSTANDARD=LVCMOS25;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<7> PULLDOWN;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<7> SLEW=SLOW;
Net fpga_0_LEDs_8Bit_GPIO_IO_pin<7> DRIVE=2;

 

Modify the Software Application

Now the hardware definitions are complete and we must modify the software application to test the hardware.

  1. Create a new software application called “LEDTest” and create a new folder for it called “LEDTest” in the project directory.
  2. From the menu select File->New and copy the code below into the new file. Save the file as “LEDTest.c” in the “LEDTest” folder.
#include "xparameters.h"
#include "xbasic_types.h"
#include "xgpio.h"
#include "xstatus.h"

XGpio GpioOutput;
XGpio GpioInput;

int main (void) {

  Xuint32 status;
  Xuint32 DataRead;
  Xuint32 OldData;

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

  // Initialize the GPIO driver so that it's ready to use,
  status = XGpio_Initialize(&GpioOutput,
                                 XPAR_LEDS_8BIT_DEVICE_ID);
  if (status != XST_SUCCESS)
    return XST_FAILURE;
  // Set the direction for all signals to be outputs
  XGpio_SetDataDirection(&GpioOutput, 1, 0x0);

  // Initialize the GPIO driver so that it's ready to use,
  status = XGpio_Initialize(&GpioInput,
                         XPAR_DIP_SWITCHES_8BIT_DEVICE_ID);
  if (status != XST_SUCCESS)
    return XST_FAILURE;
  // Set the direction for all signals to be inputs
  XGpio_SetDataDirection(&GpioInput, 1, 0xFFFFFFFF);

  OldData = 0xFFFFFFFF;
  while(1){
    // Read the state of the DIP switches
    DataRead = XGpio_DiscreteRead(&GpioInput, 1);

    // Send the data to the UART if the settings change
    if(DataRead != OldData){
      xil_printf("DIP Switch settings: 0x%2X\r\n", DataRead);
      // Set the GPIO outputs to the DIP switch values
      XGpio_DiscreteWrite(&GpioOutput, 1, DataRead);
      // Record the DIP switch settings
      OldData = DataRead;
    }
  }
}
  1. Add the “LEDTest.c” file into the “Project: LEDTest” source files by right-clicking on “Source” and selecting “Add Existing Files”.
  2. Select “Mark to Initialize BRAMs” for the “Project: LEDTest” application and deselect it for the “Project: DIPTest” application.
  3. Generate the linker script for the “Project: LEDTest” application.

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. We can now download our hardware description and software application to the FPGA. From the XPS software, select “Device Configuration->Download Bitstream”.

The simple C program that we added to this project simply reads the DIP switch settings and sends a message with that data to the UART. It also writes the same values to the LEDs. When the application is running and Hyperterminal is open, the DIP switch settings should be seen on the LEDs and on the Hyperterminal screen. Change the DIP switches to see the message change.

After a few changes in the DIP switches, the Hyperterminal output should look similar to the screen shot below:

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

In the next tutorial, Create a Peripheral Using the Peripheral Wizard, we develop our own custom peripheral for reading the DIP switches and writing to the LEDs.

Tagged with:  

Overview

In this tutorial we will improve on the Timer peripheral developed in Create a Simple Timer Peripheral. The improvement is achieved by enabling the peripheral to generate an interrupt when the timer expires. The PowerPC then processes the interrupt through an interrupt handler function which gets called whenever the interrupt occurs. In this example, the interrupt handler function will switch the state of the LEDs and reset the timer.

Figure: The Timer peripheral

The timer will use two registers, one to store the delay period and the other for starting, stopping and checking if the timer has expired. We will call the first register the delay register and the second register the control register. The delay register will be set by the software application to determine how long of a delay it requires. This value will remain in the delay register unchanged and the software application will be able to read this value if for some reason it needs to. The control register will only use the first two bits, being bit 0 and bit 1. Bit 0 will be read-only and will be used by the timer peripheral to signal to the software application that the timer has expired. Bit 1 will be read and writeable, and it will be used by the software application to make the timer “run”. When a “1″ is written to this bit, the timer will start counting down from the delay value. When a “0″ is written to this bit, the timer will reset.

This tutorial contains screenshots to guide you through the entire implementation process. You can click on the images to view a higher resolution when necessary.

Are you using EDK 10.1?

Try the updated version of this tutorial based on the Virtex-5 FPGA on the ML505 board: Timer with Interrupts

Create the Basic Project

Follow these steps to create the basic project:

  1. Open XPS and from the dialog box, select “Base System Builder wizard” and OK.
  2. Create a new folder for the project and select it using “Browse”. In “Advanced Options” tick “Use repository paths” and select the “C:\XUPV2P\lib” folder using “Browse”. Click “OK”.
     
  3. Tick “I would like to create a new design” and click “Next”.
  4. Select “Xilinx” as the board vendor. Select “XUP Virtex II Pro Development System” as the board name. Select “C” as the board revision. Click “Next”.
     
  5. Tick “PowerPC” and click “Next”.
     
  6. Select all clock frequencies to be 100MHz. Select “No debug”. Click “Next”.
     
  7. In selecting the Additional IO Interfaces, tick “RS232_Uart_1″ and “LEDs_4Bit”. Untick everything else.



     
  8. When Adding Internal Peripherals, select 64KB for the “plb_bram_if_cntlr_1″ and click “Next”.
     
  9. Select “RS232_Uart_1″ for both STDIN and STDOUT. Un-tick “Memory Test” and leave “Peripheral Test” ticked. Click “Next”.
     
  10. On the “Configure Peripheral Test Application” page, select “plb_bram_if_cntlr_1″ for the Instruction, Data, Stack and Heap memories. Click “Next”.
     
  11. Click “Generate”.
  12. Click “Finish”.
  13. Tick “Start using Platform Studio” and click “OK”.

Create the Timer Peripheral

We now create our Timer peripheral using the Peripheral Wizard.

  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. Type “my_timer” for the peripheral name. Click “Next”.
     
  5. Select “On-chip Peripheral Bus” (OPB) and click “Next”.
  6. The Peripheral Wizard can generate our VHDL template to include many different features. Tick “User logic interrupt support” and “User logic S/W register support”. Un-tick everything else and click “Next”.
     
  7. On the “Interrupt Service” page, leave the defaults and click “Next”.
     
  8. Choose “2″ for the number of software accessible registers. Choose 32-bits for the size of the registers, as we will use a 32-bit timer.
     
  9. On the “IP Interconnect” page we can customize our connection to the OPB 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 and we can modify them to include the code for the timer.

Modify the Timer Peripheral

Now we want to add a timer to this peripheral template and connect it up with the registers that the Peripheral Wizard created for us.

  1. Select from the menu “File->Open” and look in the project folder.
  2. Open the folders: “pcores\my_timer_v1_00_a\hdl\vhdl”. This folder contains two source files that describe our peripheral “my_timer.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 include our timer code.
  4. Find the line of code that says “–USER signal declarations added here” and add the following lines of code just below.
-- Timer signals and components

signal timer_count : std_logic_vector(0 to C_DWIDTH-1);
signal timer_expired : std_logic;
signal timer_run : std_logic;
  1. Find the line of code that says “–USER logic implementation added here” and add the following lines of code just below.
-- Timer connections
timer_run <= slv_reg1(1);

-- Timer process - times the delay between bursts
process (Bus2IP_Clk, Bus2IP_Reset)
begin
  -- if the peripheral is told to reset, then reset the timer
  if Bus2IP_Reset = '1' then
    timer_count <= (others => '0');
    timer_expired <= '1';
  -- otherwise, if there is a clock event, run the timer
  elsif Bus2IP_Clk'event and Bus2IP_Clk = '1' then
    -- if the timer is not running, then reset the timer
    if timer_run = '0' then
      timer_count <= slv_reg0;
      timer_expired <= '0';
    -- if the timer count is not zero then decrease the count
    elsif timer_count /= 0 then
      timer_count <= timer_count - 1;
      timer_expired <= '0';
    -- otherwise, the timer has expired
    else
      timer_expired <= '1';
    end if;
  end if;
end process;

-- Interrupt connections
IP2Bus_IntrEvent(0) <= timer_expired;
  1. Find the line of code that says “when “01″ => slv_ip2bus_data <= slv_reg1″ and replace it with the three lines of code below. Now, when the timer control register is read from, bit 0 will represent the timer_expired signal.
when "01" =>
  slv_ip2bus_data(1 to C_DWIDTH-1) <= slv_reg1(1 to C_DWIDTH-1);
  slv_ip2bus_data(0) <= timer_expired;
  1. Now we need to remove the example code that generates interrupts. The peripheral wizard automatically generates some VHDL code to generate interrupts in our “user_logic.vhd” file. As we use the “timer_expired” signal to generate interrupts, we must delete the example code. Comment out or delete all the code between the line “– Example code to generate user logic interrupts” and the line “IP2Bus_IntrEvent <= interrupt;” inclusively.
  2. Save and close the file.

Import the Timer Peripheral

Now we will use the Peripheral Wizard again, but this time using the import function.

  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_timer”. 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. 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_timer_v1_00_a\data” and select the “my_timer_v2_1_0.pao” file. Click “Next”.
     
  7. On the HDL analysis information page, click “Next”. The wizard will mention if any errors are found in the design.
  8. On the Bus Interfaces page, tick “OPB Slave” and click “Next”.
     
  9. On the “SOPB: Port” page, click “Next”.
  10. On the “SOPB: Parameter” page, click “Next”.
  11. On the “Identify Interrupt Signals” page, leave the defaults and click “Next”.
     
  12. On the “Parameter Attributes” page, click “Next”.
  13. On the “Port Attributes” page, click “Next”.
  14. Click “Finish”.

The timer peripheral is now ready to use and it should be accessible through the “IP Catalog->Project Repository” in the XPS interface. Note that although we can access it through the IP Catalog, other projects will not find it there because it is only associated with our project, as we specified in the Peripheral Wizard.

Create an Instance of the Peripheral

Now we are ready to create an instance of the peripheral into our project which can then be downloaded into the FPGA and tested by using simple code running on the PowerPC.

  1. From the “IP Catalog” find the “my_timer” 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_timer_0″ to the OPB bus.
     
  3. Click on the “Ports” filter. Click on the “+” for “my_timer_0″ to view its ports.
    Click on the “Net” field for the “IP2INTC_Irpt” port. Type “my_timer_irq” in this field and press “Enter”.
     
  4. Still within the “Ports” filter, click on the “+” for the “ppc405_0″ to view the PowerPC ports. Click on the “Net” field for the “EICC405EXTINPUTIRQ” port. Type “my_timer_irq” in this field and press “Enter”. This connects the timer interrupt request signal to the interrupt request input of the PowerPC.
  5. Click on the “Addresses” filter. Change the “Size” for “my_timer_0″ to 64K. Then click “Generate Addresses”.

Now we have created an instance of the Timer peripheral in our design and we have connected its interrupt request signal to the external interrupt request input of the PowerPC. This completes our hardware connections for interrupt capability.

Modify the Software Application

The software application will contain the main program and the interrupt handler function to be called when an interrupt occurs. It must create the interrupt vector table which tells the PowerPC what code to run when an interrupt is generated. It also must enable the Timer peripheral to generate interrupts, and the PowerPC to respond to interrupts.

  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 and save the file.
#include "xparameters.h"
#include "xbasic_types.h"
#include "xgpio.h"
#include "xstatus.h"
#include "my_timer.h"
#include "xexception_l.h"

#define TIMER_RESET   0x00000000
#define TIMER_RUN     0x40000000
#define TIMER_EXPIRED 0x80000000
#define TIMER_HALFSEC 0x02FAF080

XGpio GpioOutput;
Xuint32 my_timer;
unsigned int *my_timer_p =
         (unsigned int *) XPAR_MY_TIMER_0_BASEADDR;

//----------------------------------------------------
// INTERRUPT HANDLER FUNCTION
//----------------------------------------------------
void MY_TIMER_Intr_Handler(void * baseaddr_p)
{
  static Xuint32 led_data;
  Xuint32 baseaddr;
  Xuint32 IpStatus;

  baseaddr = (Xuint32) baseaddr_p;

  // Get the timer interrupt status
  IpStatus = MY_TIMER_mReadReg(baseaddr,
                      MY_TIMER_INTR_ISR_OFFSET);

  // If timer caused the interrupt then switch LEDs
  if (IpStatus){
    // If LEDs are ON then turn OFF and vice versa
    led_data = led_data ^ 0xF;
    XGpio_DiscreteWrite(&GpioOutput, 1, led_data);
    // Reset the timer and hence the interrupt
    MY_TIMER_mWriteSlaveReg1(baseaddr, TIMER_RESET);
    MY_TIMER_mWriteSlaveReg1(baseaddr, TIMER_RUN);
  }
}

//----------------------------------------------------
// MAIN FUNCTION
//----------------------------------------------------
int main (void)
{
  Xuint32 status;

  // Clear screen
  xil_printf("%c[2J",27);
  xil_printf("  Timer Project from ");
  xil_printf("http://www.fpgadeveloper.com\r\n");
  xil_printf("  --------------------------------");
  xil_printf("-----------------\r\n\r\n");

  //----------------------------------------------------
  // INITIALIZE INTERRUPT VECTOR TABLE
  //----------------------------------------------------
  xil_printf("  - Initializing interrupt vector table\r\n");
  // Initialize exception handling
  XExc_Init();
  XExc_RegisterHandler(XEXC_ID_NON_CRITICAL_INT,
    (XExceptionHandler)MY_TIMER_Intr_Handler,
  (void *)XPAR_MY_TIMER_0_BASEADDR);

  //----------------------------------------------------
  // INITIALIZE THE TIMER PERIPHERAL
  //----------------------------------------------------
  xil_printf("  - Initializing the timer peripheral\r\n");
  // Check that the my_timer peripheral exists
  XASSERT_NONVOID(my_timer_p != XNULL);
  my_timer = (Xuint32) my_timer_p;
  // Load the delay register with the delay time of 0.5s
  MY_TIMER_mWriteSlaveReg0(my_timer, TIMER_HALFSEC);

  //----------------------------------------------------
  // SETUP THE LEDs
  //----------------------------------------------------
  xil_printf("  - Setting up the LEDs\r\n");
  // Initialize the GPIO driver
  status = XGpio_Initialize(&GpioOutput,
                            XPAR_LEDS_4BIT_DEVICE_ID);
  if (status != XST_SUCCESS)
    return XST_FAILURE;
  // Set the direction for all signals to be outputs
  XGpio_SetDataDirection(&GpioOutput, 1, 0x0);
  // Turn the LEDs ON
  XGpio_DiscreteWrite(&GpioOutput, 1, 0x0);

  //----------------------------------------------------
  // ENABLE INTERRUPTS
  //----------------------------------------------------
  xil_printf("  - Enabling interrupts\r\n");
  // Enable timer interrupts
  MY_TIMER_EnableInterrupt(my_timer_p);
  // Enable PowerPC non-critical interrupts
  XExc_mEnableExceptions(XEXC_NON_CRITICAL);

  // Start the timer
  xil_printf("  - Starting the timer\r\n\r\n");
  MY_TIMER_mWriteSlaveReg1(my_timer, TIMER_RUN);

  xil_printf("    SUCCESS!\r\n");
  while(1){
  }
}
  1. Save and close the file.

Use the Default Linker Script

As mentioned earlier, the PowerPC requires an interrupt vector table to know what code to run when an interrupt occurs. The vector table resides in memory reserved by the linker script. The custom linker script for the “TestApp_Peripheral” application does not include a vector table, so we need to switch to the default linker script.

  1. From the “Applications” tab and within “Project: TestApp_Peripheral”, right-click on “Compiler Options” and select “Set Compiler Options”.
     
  2. Tick “Use default linker script” and click “OK”.

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 XUPV2P board.
  3. From the XPS software, select “Device Configuration->Download Bitstream”.

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

The LEDs should be flashing once every second. The timer peripheral is set for a delay period of half a second. Each call to the interrupt handler changes the state of the LEDs and resets the timer for another half a second. The result is that the LEDs remain ON for half a second and OFF for half a second.

The PowerPC has only one input for non-critical interrupt requests and in this project, we used it for our Timer peripheral. In a more complex design with multiple interrupts sources, we need to use an interrupt controller to multiplex all the interrupt request signals into one. This is the topic of the next tutorial.

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

Tagged with:  

Peripherals FAQ

On February 13, 2008, in Version 8.2, Xilinx Platform Studio (XPS), by Jeff

When I make changes to a peripheral, what is the most efficient way to re-build the project? How can I avoid having to re-build the entire project?

Sometimes we are developing a peripheral and we have to re-build and test it several times over. In this situation, we don’t have to “Clean Hardware” and re-build the entire project from scratch every time. Instead, we can make use of an option in the .mpd file of the peripheral in development.

  1. First make sure your modified .vhd files are all saved.
  2. Select “File->Open” and browse to the “data” folder of your modified peripheral. Select the .mpd file and click “Open”.
  3. In the .mpd file there will be a line saying “OPTION CORE_STATE = ACTIVE”. Change the “ACTIVE” to “DEVELOPMENT” and save the file.
  4. Now select “Project->Rescan User Repositories”.

Now every time that you select “Device Configuration->Download Bitstream”, XPS will check if you have made changes to the peripheral and re-build it into the project if necessary.

There is an exception to the above case and that is if you have modified the ports of your peripheral. In this case, you must use the Peripheral Wizard to Import your peripheral, change the .mpd file and then run “Project->Rescan User Repositories” again. Remember, every time that you run the Peripheral Wizard, it will re-write your .mpd file, so each time you will have to change the “ACTIVE” back to “DEVELOPMENT”. For a more detailed description of the Import process, see the tutorial Create a Peripheral Using the Peripheral Wizard.

How can I copy a peripheral from one project into another?

If you developed a peripheral in one project and you want to use it in another, you just have to copy all the peripheral files and use “Project->Rescan User Repositories”.

  1. In Windows Explorer, browse to the “pcores” folder of the project where you have a peripheral you want to copy.
  2. Select and copy the folder of the peripheral you want to copy.
  3. Paste this folder into the “pcores” folder of the project in which you want to use the peripheral.
  4. Now do the same thing for the “drivers” folder. Note that not all peripherals have drivers, so this folder may not exist for the peripheral you want to copy.
  5. Open the project in XPS and select “Project->Rescan User Repositories”.

Now the peripheral should appear in the “IP Catalog” of the project.

Tagged with:  

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.

For a more powerful example of integrating a VHDL design into a peripheral, see Creating an Aurora Transceiver, in which we create a peripheral from the Aurora core source files generated by CORE Generator.

This tutorial contains screenshots to guide you through the entire implementation process. You can click on the images to view a higher resolution when necessary.

Are you using EDK 10.1?

Try the updated version of this tutorial based on the Virtex-5 FPGA on the ML505 board: Integrating a VHDL Design into a Peripheral

Create the Basic Project

Follow these steps to create the basic project:

  1. Open XPS and from the dialog box, select “Base System Builder wizard” and OK.
  2. Create a new folder for the project and select it using “Browse”. In “Advanced Options” tick “Use repository paths” and select the “C:\XUPV2P\lib” folder using “Browse”. Click “OK”.
     
  3. Tick “I would like to create a new design” and click “Next”.
  4. Select “Xilinx” as the board vendor. Select “XUP Virtex II Pro Development System” as the board name. Select “C” as the board revision. Click “Next”.
     
  5. Tick “PowerPC” and click “Next”.
     
  6. Select all clock frequencies to be 100MHz. Select “No debug”. Click “Next”.
     
  7. In selecting the Additional IO Interfaces, leave “RS232_Uart_1″ ticked and un-tick everything else.



     
  8. When Adding Internal Peripherals, select 64KB for the “plb_bram_if_cntlr_1″ and click “Next”.
     
  9. 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. Tick “Start using Platform Studio” and click “OK”.

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. Type “my_multiplier” for the peripheral name. Click “Next”.
     
  5. Select “On-chip Peripheral Bus” (OPB) and click “Next”.
     
  6. The Peripheral Wizard can generate our VHDL template to include many different features. We will only need “FIFO”. Tick that option, un-tick everything else and click “Next”.
     
  7. For the FIFO Service settings, leave the defaults ticked: Include read FIFO, Include write FIFO, Use packet mode, Use vacancy calculation. Choose 32-bits for the width of the FIFOs, and choose a depth of 512.
     
  8. On the “IP Interconnect” page we can customize our connection to the OPB but we will leave everything as is for simplicity. Click “Next”.
  9. 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.
  10. 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”.
  11. 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. 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 “OPB Slave” and click “Next”.
     
  9. On the “SOPB: Port” page, click “Next”.
  10. On the “SOPB: 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 Repository” 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 OPB 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, temp);
  }

  // pop data out from read packet FIFO
  for(i = 0; i < 4; i++){
    temp = MY_MULTIPLIER_mReadFromFIFO(baseaddr);
    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 XUPV2P 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 0×10 = 16!

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

For a more powerful example of integrating a VHDL design into a peripheral, see Creating an Aurora Transceiver. In this tutorial, we create a peripheral to allow serial communication at 1.5Gbps between two XUPV2P boards connected by a SATA cable.

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

Tagged with:  

Overview

Sometimes we have an .ngc file from CORE Generator (or some other source) that we would like to bring into EDK as a peripheral. This project is a simple example of integrating a blackbox design into a peripheral generated by the Peripheral Wizard. We will first create a blackbox multiplier using the Xilinx CORE Generator and then we will use the generated .ngc file in our 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 blackbox designs into peripherals.

This tutorial contains screenshots to guide you through the entire implementation process. You can click on the images to view a higher resolution when necessary.

Are you using EDK 10.1?

Try the updated version of this tutorial based on the Virtex-5 FPGA on the ML505 board: Integrating a Blackbox into a Peripheral

Create the Basic Project

Follow these steps to create the basic project:

  1. Open XPS and from the dialog box, select “Base System Builder wizard” and OK.
  2. Create a new folder for the project and select it using “Browse”. In “Advanced Options” tick “Use repository paths” and select the “C:\XUPV2P\lib” folder using “Browse”. Click “OK”.
     
  3. Tick “I would like to create a new design” and click “Next”.
  4. Select “Xilinx” as the board vendor. Select “XUP Virtex II Pro Development System” as the board name. Select “C” as the board revision. Click “Next”.
     
  5. Tick “PowerPC” and click “Next”.
     
  6. Select all clock frequencies to be 100MHz. Select “No debug”. Click “Next”.
     
  7. In selecting the Additional IO Interfaces, leave “RS232_Uart_1″ ticked and un-tick everything else.



     
  8. When Adding Internal Peripherals, select 64KB for the “plb_bram_if_cntlr_1″ and click “Next”.
     
  9. 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. Tick “Start using Platform Studio” and click “OK”.

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. Type “my_multiplier” for the peripheral name. Click “Next”.
     
  5. Select “On-chip Peripheral Bus” (OPB) and click “Next”.
     
  6. The Peripheral Wizard can generate our VHDL template to include many different features. We will only need “FIFO”. Tick that option, un-tick everything else and click “Next”.
     
  7. For the FIFO Service settings, leave the defaults ticked: Include read FIFO, Include write FIFO, Use packet mode, Use vacancy calculation. Choose 32-bits for the width of the FIFOs, and choose a depth of 512.
     
  8. On the “IP Interconnect” page we can customize our connection to the OPB but we will leave everything as is for simplicity. Click “Next”.
  9. 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.
  10. 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”.
  11. Click “Finish”. Now our templates are created.

Create the Multiplier with CORE Generator

Follow these steps to generate the Multiplier.

  1. Leaving XPS running and your project open, from the “Start” menu, open Xilinx CORE Generator.
  2. Select “File->New Project”.
  3. Click “Browse” and select an appropriate location for a Coregen project. Within a Coregen project, you can create several cores that don’t necessarily relate to each other. Just select the folder where you normally place your projects, for example “C:\XUPV2P\Projects”. Click “OK”.
  4. CORE Generator will ask you if you want to create the “coregen” folder. Click “OK”.
  5. You will be asked for the specifications of the FPGA you are using. Under the “Part” tab, select these options: Family “Virtex2P”, Device “xc2vp30″, Package “ff896″, Speed grade “-7″. Click “OK”.
  6. When you have created your Coregen project, click on the “View by Function” tab to get a list of cores that you are able to generate.
  7. Open “Math Functions->Multipliers” and double-click on “Multiplier”.
  8. A dialog box should open to allow you to select the features of the Multiplier you want. For “Multiplier Type” select “Parallel Multiplier”. Select “Unsigned” for both port inputs and give them a width of 16 bits.
     
  9. Click “Finish”. Your Multiplier core will be generated and CORE Generator will display a list of all the generated files. Close this window, and close CORE Generator.

Modify the Peripheral

When we create a peripheral with read and write FIFOs, the Peripheral Wizard generates code in the user_logic.vhd file that loops the data from the write FIFO to the read FIFO. This makes it easy to test for the first time because one can write data to the peripheral and verify that it was received by reading back the same data. In our case, the loop-back design reduces the number of modifications we need to make because we want to do the same thing, only we want to first pass the data through the multiplier before putting it through to the read FIFO. Now we will add code in our peripheral template to instantiate a multiplier core and 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_v9_0
  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_v9_0
  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 Peripheral

Now we will use the Peripheral Wizard again, but this time using the import function.

  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. As usual we tick “HDL source files” and in this case, to include our .ngc file, we will also tick “Netlist files”. Now 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, click “Next”. The wizard will mention if any errors are found in the design.
  8. On the Bus Interfaces page, tick “OPB Slave” and click “Next”.
     
  9. On the SOPB: Port page, click “Next”.
  10. On the SOPB: Parameter page, click “Next”.
  11. On the “Parameter Attributes” page, click “Next”.
  12. On the “Port Attributes” page, click “Next”.
  13. Now we should arrive at the “Netlist Files” page where we are asked to locate any netlist files or black-box components that are instantiated in our peripheral. Click “Select Files” and navigate your way to the Coregen project folder “coregen” that you created using CORE Generator earlier. You will find a file called “multiplier_v9_0_mult_gen_v9_0_xst_1.ngc”. Rename this file to “multiplier_v9_0.ngc”, then select the file and click “Open”. The file should now be listed on the “Netlist Files” page. Click “Next”.
     
  14. Click “Finish”.

When you include .ngc files in a peripheral design as we just did, the Peripheral Wizard does some things behind-the-scenes that are important to know. Firstly, it makes a copy of the .ngc file and places it in the “pcores\my_multiplier_v1_00_a\netlist” folder (if that folder doesn’t exist, it creates one). Secondly, it creates a file called “my_multiplier_v2_1_0.bbd” in the “pcores\my_multiplier_v1_00_a\data” folder with the following contents:

############################################################
## Filename:          C:\XUPV2P\Projects\Multiplier5\pcores-
/my_multiplier_v1_00_a/data/my_multiplier_v2_1_0.bbd
## Description:       Black Box Definition
## Date:              Mon Feb 11 16:53:42 2008 (by Create a-
nd Import Peripheral Wizard)
############################################################

Files
############################################################
multiplier_v9_0.ngc

Finally, the Peripheral Wizard adds these two lines to the “my_multiplier_v2_1_0.mpd” file in the “pcores\my_multiplier_v1_00_a\data” folder:

OPTION STYLE = MIX
OPTION RUN_NGCBUILD = TRUE

The multiplier peripheral should now be accessible through the “IP Catalog->Project Repository” 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 OPB 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 and save the file.
#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, temp);
  }

  // pop data out from read packet FIFO
  for(i = 0; i < 4; i++){
    temp = MY_MULTIPLIER_mReadFromFIFO(baseaddr);
    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 XUPV2P 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 0×10 = 16!

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

For an example of integrating a black-box FIFO into a peripheral, see Creating an Aurora Transceiver. In this tutorial, we create an Aurora peripheral to allow serial communication at 1.5Gbps between two XUPV2P boards connected by a SATA cable.

In the next tutorial, Integrating a VHDL Design into a Peripheral, we show how to integrate a custom VHDL design into the Peripheral Wizard templates.

Tagged with:  

Overview

In this project, we will add code to a peripheral template generated by the Peripheral Wizard to create a simple timer. The timer peripheral will be used by the PowerPC to make the LEDs flash with a fixed period.

Figure: The Timer peripheral

The timer will use two registers, one to store the delay period and the other for starting, stopping and checking if the timer has expired. We will call the first register the delay register and the second register the control register. The delay register will be set by the software application to determine how long of a delay it requires. This value will remain in the delay register unchanged and the software application will be able to read this value if for some reason it needs to. The control register will only use the first two bits, being bit 0 and bit 1. Bit 0 will be read-only and will be used by the timer peripheral to signal to the software application that the timer has expired. Bit 1 will be read and writeable, and it will be used by the software application to make the timer “run”. When a “1″ is written to this bit, the timer will start counting down from the delay value. When a “0″ is written to this bit, the timer will reset.

This tutorial contains screenshots to guide you through the entire implementation process. You can click on the images to view a higher resolution when necessary.

Create the Basic Project

Follow these steps to create the basic project:

  1. Open XPS and from the dialog box, select “Base System Builder wizard” and OK.
  2. Create a new folder for the project and select it using “Browse”. In “Advanced Options” tick “Use repository paths” and select the “C:\XUPV2P\lib” folder using “Browse”. Click “OK”.
     
  3. Tick “I would like to create a new design” and click “Next”.
  4. Select “Xilinx” as the board vendor. Select “XUP Virtex II Pro Development System” as the board name. Select “C” as the board revision. Click “Next”.
     
  5. Tick “PowerPC” and click “Next”.
     
  6. Select all clock frequencies to be 100MHz. Select “No debug”. Click “Next”.
     
  7. In selecting the Additional IO Interfaces, tick “RS232_Uart_1″ and “LEDs_4Bit”. Untick everything else.



     
  8. When Adding Internal Peripherals, select 64KB for the “plb_bram_if_cntlr_1″ and click “Next”.
     
  9. Select “RS232_Uart_1″ for both STDIN and STDOUT. Un-tick “Memory Test” and leave “Peripheral Test” ticked. Click “Next”.
     
  10. On the “Configure Peripheral Test Application” page, select “plb_bram_if_cntlr_1″ for the Instruction, Data, Stack and Heap memories. Click “Next”.
     
  11. Click “Generate”.
  12. Click “Finish”.
  13. Tick “Start using Platform Studio” and click “OK”.

Modify the Software Application

The software application that the BSB has created for us will test the LEDs by making them flash one at a time. We want to modify this application so that it makes all the LEDs flash at the same time.

  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 and save the file.
#include "xparameters.h"
#include "xbasic_types.h"
#include "xgpio.h"
#include "xstatus.h"

XGpio GpioOutput;

int main (void) {

  Xuint32 status;
  unsigned int i;

  // Initialize the GPIO driver so that it's ready to use,
  status = XGpio_Initialize(&GpioOutput,
                                  XPAR_LEDS_4BIT_DEVICE_ID);
  if (status != XST_SUCCESS)
    return XST_FAILURE;
  // Set the direction for all signals to be outputs
  XGpio_SetDataDirection(&GpioOutput, 1, 0x0);

  while(1){
    // Turn OFF the LEDs
    XGpio_DiscreteWrite(&GpioOutput, 1, 0xF);
    // Wait for a period
    for(i = 0; i < 0x0F00000; i++){}
    // Turn ON the LEDs
    XGpio_DiscreteWrite(&GpioOutput, 1, 0x0);
    // Wait for a period
    for(i = 0; i < 0x0F00000; i++){}
  }
}
  1. From the menu select “Device Configuration->Download Bitstream”.

The bitstream will be created and downloaded into the FPGA. When the application runs, you will see the LEDs on the XUPV2P board flashing.

If you read through the code, you will notice that we have used simple “for” loops to implement delays between turning the LEDs on and turning them off. Without the delays, the LEDs would flash more quickly than the human eye can see. The main problems with using these “for” loops are: (1) we can’t be very precise about the delay time because it depends on how quickly the processor completes the “for” loop which depends on how many instructions per second it can perform and whether any interrupt routines are executed during that time, (2) the main program can’t do anything during the time that we wait.

For these reasons, instead of using the PowerPC to calculate the delays, it is more convenient to use an external timer module. An independent timer peripheral can count-down the delay that we specify and will not be influenced by other processes occurring at the same time. To check the timer, the PowerPC has the choice of either (1) polling the timer peripheral to find out when the delay period has expired or (2) receive an interrupt when the delay period has expired. In this tutorial, we will use the polling method for simplicity. The interrupt method is explored in the tutorial Timer with Interrupts.

Create the Timer Peripheral

We now create our Timer peripheral using the Peripheral Wizard.

  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. Type “my_timer” for the peripheral name. Click “Next”.
     
  5. Select “On-chip Peripheral Bus” (OPB) and click “Next”.
  6. The Peripheral Wizard can generate our VHDL template to include many different features. Tick “User logic S/W register support”. Un-tick everything else and click “Next”.
     
  7. Choose “2″ for the number of software accessible registers. Choose 32-bits for the size of the registers, as we will use a 32-bit timer.
     
  8. On the “IP Interconnect” page we can customize our connection to the OPB but we will leave everything as is for simplicity. Click “Next”.
  9. 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.
  10. 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”.
  11. Click “Finish”. Now our templates are created and we can modify them to include the code for the timer.

Modify the Peripheral

Now we want to add a simple timer to this peripheral template and connect it up with the registers that the Peripheral Wizard created for us.

  1. Select from the menu “File->Open” and look in the project folder.
  2. Open the folders: “pcores\my_timer_v1_00_a\hdl\vhdl”. This folder contains two source files that describe our peripheral “my_timer.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 include our timer code.
  4. Find the line of code that says “–USER signal declarations added here” and add the following lines of code just below.
-- Timer signals and components

signal timer_count : std_logic_vector(0 to C_DWIDTH-1);
signal timer_expired : std_logic;
signal timer_run : std_logic;
  1. Find the line of code that says “–USER logic implementation added here” and add the following lines of code just below.
-- Timer connections
timer_run <= slv_reg1(1);

-- Timer process - times the delay between bursts
process (Bus2IP_Clk, Bus2IP_Reset)
begin
  -- if the peripheral is told to reset, then reset the timer
  if Bus2IP_Reset = '1' then
    timer_count <= (others => '0');
    timer_expired <= '1';
  -- otherwise, if there is a clock event, run the timer
  elsif Bus2IP_Clk'event and Bus2IP_Clk = '1' then
    -- if the timer is not running, then reset the timer
    if timer_run = '0' then
      timer_count <= slv_reg0;
      timer_expired <= '0';
    -- if the timer count is not zero then decrease the count
    elsif timer_count /= 0 then
      timer_count <= timer_count - 1;
      timer_expired <= '0';
    -- otherwise, the timer has expired
    else
      timer_expired <= '1';
    end if;
  end if;
end process;
  1. Find the line of code that says “when “01″ => slv_ip2bus_data <= slv_reg1″ and replace it with the three lines of code below. Now, when the timer control register is read from, bit 0 will represent the timer_expired signal.
when "01" =>
  slv_ip2bus_data(1 to C_DWIDTH-1) <= slv_reg1(1 to C_DWIDTH-1);
  slv_ip2bus_data(0) <= timer_expired;
  1. Save and close the file.

Import the Timer Peripheral

Now we will use the Peripheral Wizard again, but this time using the import function.

  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_timer”. 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. 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_timer_v1_00_a\data” and select the “my_timer_v2_1_0.pao” file. Click “Next”.
     
  7. On the HDL analysis information page, click “Next”. The wizard will mention if any errors are found in the design.
  8. On the Bus Interfaces page, tick “OPB Slave” and click “Next”.
     
  9. On the SOPB: Port page, click “Next”.
  10. On the SOPB: Parameter page, click “Next”.
  11. On the “Parameter Attributes” page, click “Next”.
  12. On the “Port Attributes” page, click “Next”.
  13. Click “Finish”.

The timer peripheral is now ready to use and it should be accessible through the “IP Catalog->Project Repository” in the XPS interface. Note that although we can access it through the IP Catalog, other projects will not find it there because it is only associated with our project, as we specified in the Peripheral Wizard.

Create an Instance of the Timer Peripheral

Now we are ready to create an instance of the peripheral into our project which can then be downloaded into the FPGA and tested by using simple code running on the PowerPC.

  1. From the “IP Catalog” find the “my_timer” 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_timer_0″ to the OPB bus.
     
  3. Click on the “Addresses” filter. Change the “Size” for “my_timer_0″ to 64K. Then click “Generate Addresses”.

Now we have created an instance of the Timer peripheral in our design.

Modify the Software Application

Now the timer peripheral is in our project and ready for us to use. We can now modify our software application to use the timer to produce the delays between turning the LEDs on and turning them off.

In the code below, to interact with the timer peripheral, we are using the “my_timer” driver that was created by the peripheral wizard and located in the project folder at “\drivers\my_timer”. The include file “my_timer.h” provides us with simple functions for reading and writing to the registers that we created in our peripheral. In general, you should always check the peripheral driver source code whenever you create a peripheral because it often contains useful functions for the features you included in the 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 and save the file.
#include "xparameters.h"
#include "xbasic_types.h"
#include "xgpio.h"
#include "xstatus.h"
#include "my_timer.h"

#define TIMER_RESET   0x00000000
#define TIMER_RUN     0x40000000
#define TIMER_EXPIRED 0x80000000
#define TIMER_HALFSEC 0x02FAF080

XGpio GpioOutput;
unsigned int *my_timer_p =
              (unsigned int *) XPAR_MY_TIMER_0_BASEADDR;

int main (void) {

  Xuint32 my_timer;
  Xuint32 status;

  // Check that the my_timer peripheral exists
  XASSERT_NONVOID(my_timer_p != XNULL);
  my_timer = (Xuint32) my_timer_p;
  // Load the delay register with the delay time of 0.5s
  MY_TIMER_mWriteSlaveReg0(my_timer, TIMER_HALFSEC);

  // Initialize the GPIO driver
  status = XGpio_Initialize(&GpioOutput,
                                  XPAR_LEDS_4BIT_DEVICE_ID);
  if (status != XST_SUCCESS)
    return XST_FAILURE;
  // Set the direction for all signals to be outputs
  XGpio_SetDataDirection(&GpioOutput, 1, 0x0);

  while(1){
    // Turn OFF the LEDs
    XGpio_DiscreteWrite(&GpioOutput, 1, 0xF);
    // Start the timer and wait for it to expire
    MY_TIMER_mWriteSlaveReg1(my_timer, TIMER_RUN);
    while(!(MY_TIMER_mReadSlaveReg1(my_timer) & TIMER_EXPIRED)){}
    // Reset the timer
    MY_TIMER_mWriteSlaveReg1(my_timer, TIMER_RESET);
    // Turn ON the LEDs
    XGpio_DiscreteWrite(&GpioOutput, 1, 0x0);
    // Start the timer and wait for it to expire
    MY_TIMER_mWriteSlaveReg1(my_timer, TIMER_RUN);
    while(!(MY_TIMER_mReadSlaveReg1(my_timer) & TIMER_EXPIRED)){}
    // Reset the timer
    MY_TIMER_mWriteSlaveReg1(my_timer, TIMER_RESET);
  }
}

 

Download and Test the Project

  1. Turn on the XUPV2P board.
  2. From the XPS software, select “Device Configuration->Download Bitstream”.

The bitstream will be created and downloaded into the FPGA. When the application runs, you will see the LEDs on the XUPV2P board flashing once every second. The timer peripheral is set for a delay period of half a second. Every time the timer expires, the state of the LEDs is inverted so that the LEDs remain ON for half a second and OFF for half a second.

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

In the next tutorial, Integrating a Black Box into a Peripheral, we show how to integrate a netlist (.NGC) file into a peripheral. For an improved version of the timer peripheral using interrupts, see Timer with Interrupts.

Tagged with:  

Overview

In this tutorial we will create a simple project that uses our own IP core (instead of using the General Purpose IO core provided by Xilinx) to read from the DIP switches and write to the LEDs. The software application will display the DIP switch values on the LED outputs and also send the DIP switch values to the UART.

Any IP core must connect to the OPB (or PLB) to communicate with the PowerPC. When connected to the OPB, the IP core becomes part of the memory map accessible to the PowerPC. The IP core will have a base address and a high address signifying where in the memory map it resides, and how much of the memory map it occupies. The PowerPC interacts with the IP core as though it were part of the memory.

In this example, we will use the Peripheral Wizard to create a basic IP core that connects to the OPB and implements one 32 bit register. Writing to the IP core through the OPB will allow us to change the contents of the register. The outputs of the register will drive the LEDs. We will modify the core so that reading from it through the OPB will return the DIP switch settings. The diagram below illustrates the peripheral and connections.

Figure: Peripheral for controlling the LEDs and reading the DIP switches

Note that another possibility for this design is to use another register for passing the DIP switch values through to the OPB handler. In this design, only one register was used to keep the design simple and to minimize the changes to the template.

This tutorial contains screenshots to guide you through the entire implementation process. You can click on the images to view a higher resolution when necessary.

Are you using EDK 10.1?

Try the updated version of this tutorial based on the Virtex-5 FPGA on the ML505 board: Create a Peripheral using the Peripheral Wizard

Create the Basic Project using BSB

Follow these steps to create the basic project:

  1. Open XPS and from the dialog box, select “Base System Builder wizard” and OK.
  2. Create a new folder for the project and select it using “Browse”. In “Advanced Options” tick “Use repository paths” and select the “C:\XUPV2P\lib” folder using “Browse”. Click “OK”.
  3. Tick “I would like to create a new design” and click “Next”.
  4. Select “Xilinx” as the board vendor. Select “XUP Virtex II Pro Development System” as the board name. Select “C” as the board revision. Click “Next”.
     
  5. Tick “PowerPC” and click “Next”.
     
  6. Select all clock frequencies to be 100MHz. Select “No debug”. Click “Next”.
     
  7. In selecting the Additional IO Interfaces, leave “RS232_Uart_1″ ticked and un-tick everything else.



     
  8. When Adding Internal Peripherals, select 64KB for the “plb_bram_if_cntlr_1″ and click “Next”.
     
  9. Select “RS232_Uart_1″ for both STDIN and STDOUT. Un-tick “Memory Test” and “Peripheral Test”. Click “Next”.
     
  10. Click “Generate”.
  11. Click “Finish”.
  12. Tick “Start using Platform Studio” and click “OK”.

Create the Peripheral

We now create our custom peripheral using the Peripheral Wizard.

  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. Type “my_peripheral” for the peripheral name. Click “Next”.
     
  5. Select “On-chip Peripheral Bus” (OPB) and click “Next”.
     
  6. The Peripheral Wizard can generate our VHDL template to include many different features. We will only need “User logic S/W register support”. Tick that option, un-tick everything else and click “Next”.
     
  7. Choose “1″ for the number of software accessible registers. As the PowerPC is a 32-bit processor, we will choose 32-bits for the size of the register, even though we only need 4-bits for the 4 LEDs.
     
  8. On the “IP Interconnect” page we can customize our connection to the OPB but we will leave everything as is for simplicity. Click “Next”.
  9. 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.
  10. 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”.
  11. Click “Finish”. Now our templates are created and we can modify them to suit our application.

Modify the Peripheral

The template created by the Peripheral Wizard implements a 32-bit register that we can read and write to via the OPB. When the peripheral is instantiated, we could access the register by reading and writing to the base address of the peripheral. We need to modify this functionality slightly, because of two things:

  1. We want to connect the outputs of the register to the LEDs – to achieve this we need to create an output port on the peripheral and connect it to the output of the register.
  2. We want to make all reads from the peripheral return the values of the DIP switches – to achieve this we need to create an input port on the peripheral and make it return these values on every read request.

Follow these steps to modify the peripheral:

  1. Select from the menu “File->Open” and browse to “pcores\my_peripheral_v1_00_a\hdl\vhdl” from the project folder. This folder contains two source files that describe our peripheral: “my_peripheral.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.
  2. Open the file “my_peripheral.vhd”. We will need to add two ports to this source code, one for the LEDs and one for the DIP switches.
  3. Find the line of code that says “–ADD USER PORTS BELOW THIS LINE” and add these two lines of code:
  LED_Data : out std_logic_vector(0 to 3);
  DIP_Data : in std_logic_vector(0 to 3);
  1. Find the line of code that says “–MAP USER PORTS BELOW THIS LINE” and add these two lines of code:
  LED_Data => LED_Data,
  DIP_Data => DIP_Data,
  1. Save and close the file.
  2. Open the file “user_logic.vhd”. We will need to modify this source code to include the two new ports and to modify the behaviour.
  3. Find the line of code that says “–ADD USER PORTS BELOW THIS LINE” and add the following two lines of code.
  LED_Data : out std_logic_vector(0 to 3);
  DIP_Data : in std_logic_vector(0 to 3);
  1. Find the line of code that says “–USER logic implementation added here” and add the following line of code below. This connects the LED port to the first 4 bits of the register output.
  LED_Data <= slv_reg0(28 to 31);
  1. Find the line of code that says “IP2Bus_Data <= slv_ip2bus_data;” and replace it with the code below. Now, when the peripheral is read on the OPB, it will always return the DIP switch settings. As the bus is 32 bits long, we just fill the unused bits with zeros using the “others” keyword.
  IP2Bus_Data(0 to 27) <= (others=>'0');
  IP2Bus_Data(28 to 31) <= DIP_Data(0 to 3);
  1. Save and close the file.

Import the Peripheral

Now we will use the Peripheral Wizard again, but this time using the import function.

  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_peripheral”. 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. 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_peripheral_v1_00_a\data” and select the “my_peripheral_v2_1_0.pao” file. Click “Next”.
     
  7. On the HDL analysis information page, click “Next”. The wizard will mention if any errors are found in the design.
  8. On the Bus Interfaces page, tick “OPB Slave” and click “Next”.
     
  9. On the SOPB: Port page, click “Next”.
  10. On the SOPB: Parameter page, click “Next”.
  11. On the “Identify Interrupt Signals” page, un-tick “Select and configure interrupts” and click “Next”. We don’t need interrupts for our design.
     
  12. On the “Parameter Attributes” page, click “Next”.
  13. On the “Port Attributes” page, click “Next”.
  14. Click “Finish”.

Now we have a peripheral that is ready to use and it should be accessible through the “IP Catalog->Project Repository” in the XPS interface. Note that although we can access it through the IP Catalog, other projects will not find it there because it is only associated with our project, as we specified in the Peripheral Wizard.

Create an Instance of the Peripheral

Now we are ready to create an instance of the peripheral into our project which can then be downloaded into the FPGA and tested by using simple code running on the PowerPC.

  1. From the “IP Catalog” find the “my_peripheral” 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_peripheral_0″ to the OPB bus.
     
  3. Click on the “Ports” filter and open the “my_peripheral_0″ tree. There will be two ports listed “LED_Data” and “DIP_Data”. Type in a net name for each of them. The net name can be anything you like, but for this project let us call them “LED_Data” and “DIP_Data” for simplicity. For each net, use the option “Make External”. If you then click open the “External Ports” tree, you should see these nets listed.
     
  4. Click on the “Addresses” filter. Change the “Size” for “my_peripheral_0″ to 64K. Then click “Generate Addresses”. You might ask why 64K when we only have one 32 bit register to address. If you enter 32 bits as a size and then click “Generate Addresses”, XPS automatically changes the size to 64K and calculates the addresses accordingly.

We have now created an instance of the peripheral in our design.

Link the External Ports to FPGA Pins

Now we have made the LED and DIP ports external however we need to link them to specific pins on the FPGA. We will use the schematic for the XUPV2P to get the correct pin numbers for each of the LEDs and DIP switches. Then we modify the User Constraints File (UCF) to include these pin assignments.

  1. Click the “Project” tab and double click on the UCF file to open it.
  2. Find the comment “## IO Devices constraints” and add the following lines of code below it:
#### Module DIPSWs_4Bit constraints

Net DIP_Data_pin<0> LOC=AC11;
Net DIP_Data_pin<0> IOSTANDARD = LVCMOS25;
Net DIP_Data_pin<1> LOC=AD11;
Net DIP_Data_pin<1> IOSTANDARD = LVCMOS25;
Net DIP_Data_pin<2> LOC=AF8;
Net DIP_Data_pin<2> IOSTANDARD = LVCMOS25;
Net DIP_Data_pin<3> LOC=AF9;
Net DIP_Data_pin<3> IOSTANDARD = LVCMOS25;

#### Module LEDs_4Bit constraints

Net LED_Data_pin<0> LOC=AC4;
Net LED_Data_pin<0> IOSTANDARD = LVTTL;
Net LED_Data_pin<0> SLEW = SLOW;
Net LED_Data_pin<0> DRIVE = 12;
Net LED_Data_pin<1> LOC=AC3;
Net LED_Data_pin<1> IOSTANDARD = LVTTL;
Net LED_Data_pin<1> SLEW = SLOW;
Net LED_Data_pin<1> DRIVE = 12;
Net LED_Data_pin<2> LOC=AA6;
Net LED_Data_pin<2> IOSTANDARD = LVTTL;
Net LED_Data_pin<2> SLEW = SLOW;
Net LED_Data_pin<2> DRIVE = 12;
Net LED_Data_pin<3> LOC=AA5;
Net LED_Data_pin<3> IOSTANDARD = LVTTL;
Net LED_Data_pin<3> SLEW = SLOW;
Net LED_Data_pin<3> DRIVE = 12;
  1. Save and close the file.

Create a Software Application

Now we need to create a simple software application to test the peripheral. The program simply needs to read and write to the peripheral through the OPB.

  1. Click on the “Applications” tab and double click “Add Software Application Project”.
  2. For the name, type “TestApp”. Select the “ppc405_0″ processor. Click “OK”.
  3. Right-click on “Default: ppc405_0_bootloop” and select “Mark to Initialize BRAMs” so that this application does not get executed on the PowerPC. It should now have a red cross through its icon.
  4. Right-click on “Project: TestApp” and select “Mark to Initialize BRAMs” to select our application for execution on the PowerPC. It should now not have a red cross through its icon.
  5. Select from the menu File->New. Copy the code below into this new file and save the file as “TestApp.c” in a new folder called “TestApp” within the project folder.
#include "xparameters.h"

unsigned int *my_peripheral =
              (unsigned int *) XPAR_MY_PERIPHERAL_0_BASEADDR;

int main (void) {

  unsigned int DataRead;
  unsigned int OldData;

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

  OldData = (unsigned int) 0xffffffff;
  while(1){
    // Read the state of the DIP switches
    DataRead = *my_peripheral;
    // Send the data to the UART if the settings change
    if(DataRead != OldData){
      xil_printf("DIP Switch settings: 0x%X\r\n", DataRead);
      // Set the LED outputs to the DIP switch values
      *my_peripheral = DataRead;
      // Record the DIP switch settings
      OldData = DataRead;
    }
  }
}

 

  1. Right-click on “Sources” under the “Project: TestApp” tree and select “Add Existing Files”. Select the file we just saved.

The software application simply creates a pointer to the peripheral and makes reads and writes to the peripheral through the pointer. We can obtain the address of the peripheral by looking in the “System Assembly View” using the “Addresses” filter, but in this example, we use the “XPAR_MY_PERIPHERAL_0_BASEADDR” definition given in the “xparameters.h” file which is created automatically by XPS. By using this definition, instead of a fixed value, we don’t have to modify our code each time we modify the hardware and addresses.

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 XUPV2P board.
  3. We can now download our hardware description and software application to the FPGA. From the XPS software, select “Device Configuration->Download Bitstream”.

When the application is running and Hyperterminal is open, the DIP switch settings should be seen on the LEDs and on the Hyperterminal screen. Change the DIP switches to see the message change. The Hyperterminal output should look similar to the screen shot below:

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

In the next tutorial, Create a Simple Timer Peripheral, we develop our own timer peripheral so that we can make the LEDs flash.

Tagged with: