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:
- Open XPS and from the dialog box, select “Base System Builder wizard” and OK.
- 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”.
- Tick “I would like to create a new design” and click “Next”.
- 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”.
- Tick “PowerPC” and click “Next”.
- Select all clock frequencies to be 100MHz. Select “No debug”. Click “Next”.
- In selecting the Additional IO Interfaces, leave
RS232_Uart_1
ticked and un-tick everything else. - When Adding Internal Peripherals, select 64KB for the
plb_bram_if_cntlr_1
and click “Next”. - Select
RS232_Uart_1
for both STDIN and STDOUT. Un-tick “Memory Test” and “Peripheral Test”. Click “Next”. - Click “Generate”.
- Click “Finish”.
- Tick “Start using Platform Studio” and click “OK”.
Create the Peripheral
We now create our custom peripheral using the Peripheral Wizard.
- Select from the menu “Hardware->Create or Import Peripheral”. Click “Next”.
- Select “Create templates for a new peripheral” and click “Next”.
- 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”.
- Type
my_peripheral
for the peripheral name. Click “Next”. - Select “On-chip Peripheral Bus” (OPB) and click “Next”.
- 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”.
- 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.
- On the “IP Interconnect” page we can customize our connection to the OPB but we will leave everything as is for simplicity. Click “Next”.
- 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.
- 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”.
- 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:
- 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.
- 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:
- 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
anduser_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. - 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. - 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);
- 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,
- Save and close the file.
- 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. - 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);
- 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);
- 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);
- Save and close the file.
Import the Peripheral
Now we will use the Peripheral Wizard again, but this time using the import function.
- Select from the menu “Hardware->Create or Import Peripheral” and click “Next”.
- Select “Import existing peripheral” and click “Next”.
- Select “To an XPS project”, ensure that the folder chosen is the project folder, and click “Next”.
- 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”. - Tick “HDL source files” and click “Next”.
- 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 themy_peripheral_v2_1_0.pao
file. Click “Next”. - On the HDL analysis information page, click “Next”. The wizard will mention if any errors are found in the design.
- On the Bus Interfaces page, tick “OPB Slave” and click “Next”.
- On the SOPB: Port page, click “Next”.
- On the SOPB: Parameter page, click “Next”.
- On the “Identify Interrupt Signals” page, un-tick “Select and configure interrupts” and click “Next”. We don’t need interrupts for our design.
- On the “Parameter Attributes” page, click “Next”.
- On the “Port Attributes” page, click “Next”.
- 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.
- From the “IP Catalog” find the
my_peripheral
IP core in the “Project Repository” group. Right click on the core and select “Add IP”. - From the “System Assembly View” using the “Bus Interface” filter, connect the
my_peripheral_0
to the OPB bus. - Click on the “Ports” filter and open the
my_peripheral_0
tree. There will be two ports listedLED_Data
andDIP_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 themLED_Data
andDIP_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. - 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.
- Click the “Project” tab and double click on the UCF file to open it.
- 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;
- 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.
- Click on the “Applications” tab and double click “Add Software Application Project”.
- For the name, type “TestApp”. Select the
ppc405_0
processor. Click “OK”. - 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. - 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.
- 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;
}
}
}
- 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
- Open a Hyperterminal window with the required settings. For the correct settings, see Hyperterminal Settings.
- Turn on the XUPV2P board.
- 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.