Standalone Peripheral Drivers (PL)
Introduction
In this exercise, we will run an application that makes use of the standalone peripheral drivers to access AXI peripherals residing in the Programmable Logic.
In order to do this, we need to follow the same procedure that we used in the Vitis Hello World Demo but using the ultra96v2_custom.xsa file exported from Vivado as the hardware when creating the platform. This hardware contains:
- AXI Timer from the Vivado Libraries.
- LED Driver created as a Custom AXI Peripheral.
When we do this, we will have the same peripherals we had in the Processing System only version, so we can repeat the same exercises, but now we have a bitstream for the programmable logic.
Software Projects
In order to not interfere with previous projects, we will choose the following names for the different projects:
- Platform: ultra96v2_custom_platform
- Application: ultra96v2_custom_hello
- System: ultra96v2_custom_hello_system
Application Debugging
At the time of debugging, we will see that our debug configuration automatically detects the PL bitstream and includes it in the booting process:
Boot Image Creation
In the same way, when creating a boot image from the system project, it will detect the bitstream and will create a new partition:
- Partition Type: datafile
- Destination Device: PL
Note that the bitstream should always be placed after the FSBL and the PMU FW partitions:
Application Code
Once we are up with playing with the Hello World template, we can play with the AXI peripherals:
- We will set a the AXI Timer so it counts 1 second and then generate an interruption.
- We configure an interrupt handling routine that:
- Prints a message on the UART with the seconds elapsed since the execution began.
- Toggle the PL LEDs by accessing to our custom AXI peripheral using low level 32 bit access to memory.
- Rearm the the timer to start the count to 1 second again.
In order to run this demo, replace the all of the code in the src/helloworld.c file inside the ultra96v2_custom_hello application with the following contents:
#include <stdio.h>
#include "platform.h"
#include "xtmrctr.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xscugic.h"
XScuGic InterruptController; /* Instance of the Interrupt Controller */
static XScuGic_Config *GicConfig; /* The configuration parameters of the controller */
// Trigger counter
int count = 0;
void Timer_InterruptHandler(void *data, u8 TmrCtrNumber)
{
printf("Interrupt handled, count: %d \n\r", count);
print("\r\n");
// Using the Xil_In32 and Xil_Out32 macros as basic low level drivers
// We will toggle the LEDs every time the interrupt is handled.
if (Xil_In32(XPAR_MY_LED_DRIVER_0_S00_AXI_BASEADDR) == 0)
Xil_Out32(XPAR_MY_LED_DRIVER_0_S00_AXI_BASEADDR, 0xFFFFFFFF);
else
Xil_Out32(XPAR_MY_LED_DRIVER_0_S00_AXI_BASEADDR, 0x00000000);
XTmrCtr_Stop(data,TmrCtrNumber);
XTmrCtr_Reset(data,TmrCtrNumber);
XTmrCtr_Start(data,TmrCtrNumber);
count++;
}
int SetUpInterruptSystem(XScuGic *XScuGicInstancePtr)
{
/*
* Connect the interrupt controller interrupt handler to the hardware
* interrupt handling logic in the ARM processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
XScuGicInstancePtr);
/*
* Enable interrupts in the ARM
*/
Xil_ExceptionEnable();
return XST_SUCCESS;
}
int ScuGicInterrupt_Init(u16 DeviceId,XTmrCtr *TimerInstancePtr)
{
int Status;
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
* */
GicConfig = XScuGic_LookupConfig(DeviceId);
if (NULL == GicConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&InterruptController, GicConfig,
GicConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Setup the Interrupt System
* */
Status = SetUpInterruptSystem(&InterruptController);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Connect a device driver handler that will be called when an
* interrupt for the device occurs, the device driver handler performs
* the specific interrupt processing for the device
*/
Status = XScuGic_Connect(&InterruptController,
XPAR_FABRIC_AXI_TIMER_0_INTERRUPT_INTR,
(Xil_ExceptionHandler)XTmrCtr_InterruptHandler,
(void *)TimerInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Enable the interrupt for the device and then cause (simulate) an
* interrupt so the handlers will be called
*/
XScuGic_Enable(&InterruptController, XPAR_FABRIC_AXI_TIMER_0_INTERRUPT_INTR);
return XST_SUCCESS;
}
int main()
{
XTmrCtr TimerInstancePtr;
int xStatus;
print("##### Application Starts #####\n\r");
print("\r\n");
xStatus = XTmrCtr_Initialize(&TimerInstancePtr,XPAR_AXI_TIMER_0_DEVICE_ID);
if(XST_SUCCESS != xStatus)
print("TIMER INIT FAILED \n\r");
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Set Timer Handler
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XTmrCtr_SetHandler(&TimerInstancePtr,
Timer_InterruptHandler,
&TimerInstancePtr);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Setting timer Reset Value (PL Clock is 100MHz, tick every 1 second)
// 100000000 decimal = 0x05F5E100 hex
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XTmrCtr_SetResetValue(&TimerInstancePtr,
0, //Change with generic value
0xFFFFFFFF - 0x05F5E100);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Setting timer Option (Interrupt Mode And Auto Reload )
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XTmrCtr_SetOptions(&TimerInstancePtr,
XPAR_AXI_TIMER_0_DEVICE_ID,
(XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION ));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//SCUGIC interrupt controller Initialization
//Registration of the Timer ISR
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
xStatus=ScuGicInterrupt_Init(XPAR_SCUGIC_0_DEVICE_ID,&TimerInstancePtr);
if(XST_SUCCESS != xStatus)
print(" :( SCUGIC INIT FAILED \n\r");
//Start Timer
XTmrCtr_Start(&TimerInstancePtr,0);
print("timer start \n\r");
//Wait For interrupt;
print("Wait for the Timer interrupt to trigger \r\n");
print("########################################\r\n");
print(" \r\n");
while(1) {}
cleanup_platform();
return 0;
}