October 18, 200813 minutes
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:
Create the Basic Project
Follow these steps to create the basic project:
RS232_Uart_1 ticked and un-tick everything else.
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”.
Create the Peripheral
We now create our custom peripheral using the Peripheral Wizard.
my_peripheral for the peripheral name. Click “Next”.
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:
Follow these steps to modify the peripheral:
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.my_peripheral.vhd. We will need to add two ports to this source code, one for the LEDs and one for the DIP switches.--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);--MAP USER PORTS BELOW THIS LINE and add these two lines of code: LED_Data => LED_Data,
DIP_Data => DIP_Data,user_logic.vhd. We will need to modify this source code to include the two new ports and to modify the behaviour.--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);--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);
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);Import the Peripheral
Now we will use the Peripheral Wizard again, but this time using the import function.
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”.
pcores\my_peripheral_v1_00_a\data and select the my_peripheral_v2_1_0.pao file. Click “Next”.
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.
my_peripheral IP core in the “Project Local pcores” group. Right click on the core and select “Add IP”.
my_peripheral_0 to the mb_plb (PLB) bus.
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.
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.
#### 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;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.
microblaze_0 processor. Click “OK”.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.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;
}
}
}
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
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.