Inter Processor Interrupts and Message Buffers
- Introduction
- IPI Master configuration in Vivado
- Adding a new Domain for Cortex-R5
- Master-Slave Poll for Ack Demo
- Ping-Pong Demo
Introduction
The heterogeneous multiprocessor system uses the inter-processor interrupt (IPI) structure to exchange short interrupt-driven messages between processors in the system.
Processor communications include:
- Inter-Processor Interrupt (IPI) structure
- Memory buffers to exchange short private 32 Byte messages between eight IPI agents:
- PMU
- RPU
- APU
- Programmable Logic.
In a typical situation, the sender writes a 32-byte request message and generates an interrupt to the receiver. The receiver can write a response message and clear the interrupt that is observed by the sender.
The communications process uses both the IPI interrupt structure and the message buffers:
- 11x Interrupt channels
- Seven interrupts default to APU MPCore, RPU0, RPU1, and PL, but can be reprogrammed to any processor.
- Four interrupts are hardwired to the PMU interrupt controller, IPI channels.
- Message buffers provide exclusive communications between each sender and each
receiver:
- Seven sets of assignable message buffers.
- One set of buffers dedicated to the PMU.
- Each set has 8x request and 8x response buffers.
- Each buffer has a capacity of 32 Bytes.
NOTE: The IPI interrupts and message buffers are independent hardware functions that are associated by software programming.
IPI Master configuration in Vivado
The IPI master mapping can be configured in Vivado by accessing the advanced settings in the Zynq UltraScale+ Processing System re-customization menu:
The values in the image are the default ones, so we can use the ultra96v2_standalone.xsa to try the following examples.
Adding a new Domain for Cortex-R5
At any time, we can add new domains to a platform by clicking in the "+" icon in the platform design view:
When done, a dialog will appear in which we can specify the parameters for a new domain:
In order to perform the demos, we will need an additional standalone domain for the Cortex-R5 core 0:
Once done, we will need to modify the Board Support Package for the psu_cortexr5_0 in order to use the UART 1 from the Ultra96-V2.
Master-Slave Poll for Ack Demo
Cortex-A53[0] sends a single message and then use the poll for ack function to wait for a response from Cortex-R5[0].
Cortex-A53[0] Master
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xipipsu.h"
/* Test message length in words. Max is 8 words (32 bytes) */
#define TEST_MSG_LEN 8
/* Time out parameter while polling for response */
#define TIMEOUT_COUNT 100000
int main()
{
init_platform();
u32 Index;
u32 Status;
u32 MessageBuffer[TEST_MSG_LEN];
u32 ResponseBuffer[TEST_MSG_LEN];
XIpiPsu IpiInst;
XIpiPsu_Config *CfgPtr;
xil_printf("GL Research IPI with Polled Ack Demo\r\n");
/* Look Up the config data */
CfgPtr = XIpiPsu_LookupConfig(XPAR_XIPIPSU_0_DEVICE_ID);
/* Init with the Cfg Data */
XIpiPsu_CfgInitialize(&IpiInst, CfgPtr, CfgPtr->BaseAddress);
xil_printf("Send Message and Trigger to Cortex-R5[0] IPI and Poll for Ack\r\n");
for (Index = 0; Index < TEST_MSG_LEN; Index++) {
MessageBuffer[Index] = Index;
}
XIpiPsu_WriteMessage(&IpiInst, XPAR_XIPIPS_TARGET_PSU_CORTEXR5_0_CH0_MASK, MessageBuffer,TEST_MSG_LEN,XIPIPSU_BUF_TYPE_MSG);
XIpiPsu_TriggerIpi(&IpiInst, XPAR_XIPIPS_TARGET_PSU_CORTEXR5_0_CH0_MASK);
Status = XIpiPsu_PollForAck(&IpiInst, XPAR_XIPIPS_TARGET_PSU_CORTEXR5_0_CH0_MASK, TIMEOUT_COUNT);
if (XST_SUCCESS == Status) {
xil_printf("Received response from Cortex-R5[0]\r\n");
xil_printf("\r\n");
XIpiPsu_ReadMessage(&IpiInst, XPAR_XIPIPS_TARGET_PSU_CORTEXR5_0_CH0_MASK, ResponseBuffer,
TEST_MSG_LEN, XIPIPSU_BUF_TYPE_RESP);
xil_printf("Word[n] = 0xFFFFFFFF - Message[n] = Response[n]\r\n");
xil_printf("-----------------------------------------------\r\n");
for (Index = 0; Index < TEST_MSG_LEN; Index++) {
xil_printf("Word[%d] = 0xFFFFFFFF - 0x%08x = 0x%08x\r\n", Index, MessageBuffer[Index],
ResponseBuffer[Index]);
}
} else {
xil_printf("Time-Out Error while Polling for Ack\r\n");
}
cleanup_platform();
return 0;
}
Cortex-R5[0] Slave
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_exception.h"
#include "xscugic.h"
#include "xipipsu.h"
/* Test message length in words. Max is 8 words (32 bytes) */
#define TEST_MSG_LEN 8
XScuGic GicInst;
XIpiPsu IpiInst;
void IpiIntrHandler(void *XIpiPsuPtr);
static XStatus SetupInterruptSystem(XScuGic *IntcInstancePtr,XIpiPsu *IpiInstancePtr, u32 IpiIntrId) ;
int main()
{
init_platform();
XIpiPsu_Config *CfgPtr;
/* Look Up the config data */
CfgPtr = XIpiPsu_LookupConfig(XPAR_XIPIPSU_0_DEVICE_ID);
/* Init with the Cfg Data */
XIpiPsu_CfgInitialize(&IpiInst, CfgPtr, CfgPtr->BaseAddress);
/* Setup the GIC */
SetupInterruptSystem(&GicInst, &IpiInst, (IpiInst.Config.IntId));
/* Enable reception of IPIs from CortexA53_0 */
XIpiPsu_InterruptEnable(&IpiInst, XPAR_XIPIPS_TARGET_PSU_CORTEXA53_0_CH0_MASK);
/* Clear existing interrupt from CortexA53_0 */
XIpiPsu_ClearInterruptStatus(&IpiInst, XPAR_XIPIPS_TARGET_PSU_CORTEXA53_0_CH0_MASK);
while (1) {};
cleanup_platform();
return 0;
}
void IpiIntrHandler(void *XIpiPsuPtr)
{
/* This routine has to be fast enough to respond inside the timeout
* defined in the message sender application from Cortex-A53[0]
* E.g. if we try to print something, we may fail to to emit
* a response in the allowed time slot.
*/
u32 Index;
u32 MessageBuffer[TEST_MSG_LEN];
u32 ResponseBuffer[TEST_MSG_LEN];
XIpiPsu *InstancePtr = (XIpiPsu *) XIpiPsuPtr;
XIpiPsu_ReadMessage(InstancePtr,XPAR_XIPIPS_TARGET_PSU_CORTEXA53_0_CH0_MASK, MessageBuffer,
TEST_MSG_LEN, XIPIPSU_BUF_TYPE_MSG);
for (Index = 0; Index < TEST_MSG_LEN; Index++) {
ResponseBuffer[Index] = 0xFFFFFFFF - MessageBuffer[Index];
}
XIpiPsu_WriteMessage(InstancePtr,XPAR_XIPIPS_TARGET_PSU_CORTEXA53_0_CH0_MASK, ResponseBuffer,
TEST_MSG_LEN, XIPIPSU_BUF_TYPE_RESP);
XIpiPsu_ClearInterruptStatus(InstancePtr, XPAR_XIPIPS_TARGET_PSU_CORTEXA53_0_CH0_MASK);
}
static XStatus SetupInterruptSystem(XScuGic *IntcInstancePtr,XIpiPsu *IpiInstancePtr, u32 IpiIntrId)
{
u32 Status = 0;
XScuGic_Config *IntcConfig; /* Config for interrupt controller */
/* Initialize the interrupt controller driver */
IntcConfig = XScuGic_LookupConfig(XPAR_SCUGIC_0_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&GicInst, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Connect the interrupt controller interrupt handler to the
* hardware interrupt handling logic in the processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler, IntcInstancePtr);
/*
* 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(IntcInstancePtr, IpiIntrId,
(Xil_InterruptHandler) IpiIntrHandler, (void *) IpiInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/* Enable the interrupt for the device */
XScuGic_Enable(IntcInstancePtr, IpiIntrId);
/* Enable interrupts */
Xil_ExceptionEnable();
return XST_SUCCESS;
}
Ping-Pong Demo
Cortex-A53[0] and Cortex-R5[0] interchange messages until one of them leaves the match. No response channel is used, but the two independent IPI message channels.
Cortex-A53[0] Player
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_exception.h"
#include "xscugic.h"
#include "xipipsu.h"
#include "xipipsu_hw.h"
/* IPI device ID to use for this test */
#define TEST_CHANNEL_ID XPAR_XIPIPSU_0_DEVICE_ID
/* Test message length in words. Max is 8 words (32 bytes) */
#define TEST_MSG_LEN 8
/* Length for the Ping-Pong match */
#define TEST_MATCH_LEN 8
/* Interrupt Controller device ID */
#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID
XScuGic GicInst;
XIpiPsu IpiInst;
/* Buffers to store Test Data */
u32 MsgBuffer[TEST_MSG_LEN];
u16 Counter;
void IpiIntrHandler(void *XIpiPsuPtr);
static XStatus SetupInterruptSystem(XScuGic *IntcInstancePtr,XIpiPsu *IpiInstancePtr, u32 IpiIntrId) ;
void SendMessage(XIpiPsu *InstancePtr);
int main()
{
init_platform();
Counter = 0;
XIpiPsu_Config *CfgPtr;
xil_printf("GL Research IPI Ping-Pong Demo\r\n");
/* Look Up the config data */
CfgPtr = XIpiPsu_LookupConfig(TEST_CHANNEL_ID);
/* Init with the Cfg Data */
XIpiPsu_CfgInitialize(&IpiInst, CfgPtr, CfgPtr->BaseAddress);
/* Setup the GIC */
SetupInterruptSystem(&GicInst, &IpiInst, (IpiInst.Config.IntId));
/* Enable reception of IPIs from Cortex-R5 */
XIpiPsu_InterruptEnable(&IpiInst, XPAR_XIPIPS_TARGET_PSU_CORTEXR5_0_CH0_MASK);
/* Clear Interrupt from Cortex-R5*/
XIpiPsu_ClearInterruptStatus(&IpiInst, XPAR_XIPIPS_TARGET_PSU_CORTEXR5_0_CH0_MASK);
/* Call the send message routine */
SendMessage(&IpiInst);
while (1) {};
cleanup_platform();
return 0;
}
static XStatus SetupInterruptSystem(XScuGic *IntcInstancePtr,XIpiPsu *IpiInstancePtr, u32 IpiIntrId)
{
u32 Status = 0;
XScuGic_Config *IntcConfig; /* Config for interrupt controller */
/* Initialize the interrupt controller driver */
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&GicInst, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Connect the interrupt controller interrupt handler to the
* hardware interrupt handling logic in the processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler, IntcInstancePtr);
/*
* 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(IntcInstancePtr, IpiIntrId,
(Xil_InterruptHandler) IpiIntrHandler, (void *) IpiInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/* Enable the interrupt for the device */
XScuGic_Enable(IntcInstancePtr, IpiIntrId);
/* Enable interrupts */
Xil_ExceptionEnable();
return XST_SUCCESS;
}
void IpiIntrHandler(void *XIpiPsuPtr)
{
u32 Index;
XIpiPsu *InstancePtr = (XIpiPsu *) XIpiPsuPtr;
u32 IpiSrcMask; /**< Holds the IPI status register value */
IpiSrcMask = XIpiPsu_GetInterruptStatus(InstancePtr);
xil_printf("Cortex-A53[0] -> IPI STATUS = 0x%08X \r\n", IpiSrcMask);
u32 TmpBufPtr[TEST_MSG_LEN] = { 0 }; /**< Holds the received Message, later inverted and sent back as response*/
XIpiPsu_ReadMessage(InstancePtr, XPAR_XIPIPS_TARGET_PSU_CORTEXR5_0_CH0_MASK, TmpBufPtr,
TEST_MSG_LEN, XIPIPSU_BUF_TYPE_MSG);
XIpiPsu_ClearInterruptStatus(InstancePtr, XPAR_XIPIPS_TARGET_PSU_CORTEXR5_0_CH0_MASK);
xil_printf("Cortex-A53[0] -> Message Received:\r\n");
for (Index = 0; Index < TEST_MSG_LEN; Index++) {
xil_printf("- Word[%d] = 0x%08X \r\n", Index, TmpBufPtr[Index]);
}
SendMessage(InstancePtr);
}
void SendMessage(XIpiPsu *InstancePtr)
{
u32 Index;
if (Counter < TEST_MATCH_LEN) {
xil_printf("Cortex-A53[0] -> Ping: %d\r\n", Counter);
for (Index = 0; Index < TEST_MSG_LEN; Index++) {
MsgBuffer[Index] = 0xDAD00000 + Counter;
}
XIpiPsu_WriteMessage(InstancePtr, XPAR_XIPIPS_TARGET_PSU_CORTEXR5_0_CH0_MASK, MsgBuffer, TEST_MSG_LEN, XIPIPSU_BUF_TYPE_MSG);
XIpiPsu_TriggerIpi(InstancePtr, XPAR_XIPIPS_TARGET_PSU_CORTEXR5_0_CH0_MASK);
Counter++;
}
else {
xil_printf("Cortex-A53[0] -> left the match!\r\n");
}
}
Cortex-R5[0] Player
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_exception.h"
#include "xscugic.h"
#include "xipipsu.h"
#include "xipipsu_hw.h"
/* IPI device ID to use for this test */
#define TEST_CHANNEL_ID XPAR_XIPIPSU_0_DEVICE_ID
/* Test message length in words. Max is 8 words (32 bytes) */
#define TEST_MSG_LEN 8
/* Length for the Ping-Pong match */
#define TEST_MATCH_LEN 8
/* Interrupt Controller device ID */
#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID
XScuGic GicInst;
XIpiPsu IpiInst;
/* Buffers to store Test Data */
u32 MsgBuffer[TEST_MSG_LEN];
u16 Counter;
void IpiIntrHandler(void *XIpiPsuPtr);
static XStatus SetupInterruptSystem(XScuGic *IntcInstancePtr,XIpiPsu *IpiInstancePtr, u32 IpiIntrId) ;
void SendMessage(XIpiPsu *InstancePtr);
int main()
{
init_platform();
Counter = 0;
XIpiPsu_Config *CfgPtr;
/* Look Up the config data */
CfgPtr = XIpiPsu_LookupConfig(TEST_CHANNEL_ID);
/* Init with the Cfg Data */
XIpiPsu_CfgInitialize(&IpiInst, CfgPtr, CfgPtr->BaseAddress);
/* Setup the GIC */
SetupInterruptSystem(&GicInst, &IpiInst, (IpiInst.Config.IntId));
/* Enable reception of IPIs from all CortexA53_0 */
XIpiPsu_InterruptEnable(&IpiInst, XPAR_XIPIPS_TARGET_PSU_CORTEXA53_0_CH0_MASK);
/* Clear existing Interrupt from CortexA53_0 */
XIpiPsu_ClearInterruptStatus(&IpiInst, XPAR_XIPIPS_TARGET_PSU_CORTEXA53_0_CH0_MASK);
while (1) {};
cleanup_platform();
return 0;
}
void IpiIntrHandler(void *XIpiPsuPtr)
{
u32 Index;
XIpiPsu *InstancePtr = (XIpiPsu *) XIpiPsuPtr;
u32 IpiSrcMask; /**< Holds the IPI status register value */
IpiSrcMask = XIpiPsu_GetInterruptStatus(InstancePtr);
xil_printf("Cortex-R5[0] -> IPI STATUS = 0x%08X \r\n", IpiSrcMask);
u32 TmpBufPtr[TEST_MSG_LEN] = { 0 }; /**< Holds the received Message, later inverted and sent back as response*/
XIpiPsu_ReadMessage(InstancePtr, XPAR_XIPIPS_TARGET_PSU_CORTEXA53_0_CH0_MASK, TmpBufPtr,
TEST_MSG_LEN, XIPIPSU_BUF_TYPE_MSG);
XIpiPsu_ClearInterruptStatus(InstancePtr, XPAR_XIPIPS_TARGET_PSU_CORTEXA53_0_CH0_MASK);
xil_printf("Cortex-R5[0] -> Message Received:\r\n");
for (Index = 0; Index < TEST_MSG_LEN; Index++) {
xil_printf("- Word[%d] = 0x%08X \r\n", Index, TmpBufPtr[Index]);
}
SendMessage(InstancePtr);
}
static XStatus SetupInterruptSystem(XScuGic *IntcInstancePtr,XIpiPsu *IpiInstancePtr, u32 IpiIntrId)
{
u32 Status = 0;
XScuGic_Config *IntcConfig; /* Config for interrupt controller */
/* Initialize the interrupt controller driver */
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&GicInst, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Connect the interrupt controller interrupt handler to the
* hardware interrupt handling logic in the processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler, IntcInstancePtr);
/*
* 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(IntcInstancePtr, IpiIntrId,
(Xil_InterruptHandler) IpiIntrHandler, (void *) IpiInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/* Enable the interrupt for the device */
XScuGic_Enable(IntcInstancePtr, IpiIntrId);
/* Enable interrupts */
Xil_ExceptionEnable();
return XST_SUCCESS;
}
void SendMessage(XIpiPsu *InstancePtr)
{
u32 Index;
if (Counter < TEST_MATCH_LEN) {
xil_printf("Cortex-R5[0] -> Pong: %d\r\n", Counter);
for (Index = 0; Index < TEST_MSG_LEN; Index++) {
MsgBuffer[Index] = 0xBABE0000 + Counter;
}
XIpiPsu_WriteMessage(InstancePtr, XPAR_XIPIPS_TARGET_PSU_CORTEXA53_0_CH0_MASK, MsgBuffer, TEST_MSG_LEN, XIPIPSU_BUF_TYPE_MSG);
XIpiPsu_TriggerIpi(InstancePtr, XPAR_XIPIPS_TARGET_PSU_CORTEXA53_0_CH0_MASK);
Counter++;
}
else {
xil_printf("Cortex-R5[0] left the match!\r\n");
}
}