MicroZed Chronicles: Zynq MPSoC Inter Processor Interrupts and Messaging
When you have a complex heterogeneous system like the Zynq MPSoC, there is often a need to communicate between processors. We have previously examined one method that can be used for communication before when we looked at OpenAMP.
However, OpenAMP can come with an overhead and we do not always need to use a complex solution, especially if only simple communication is required, e.g the passing of short messages. In this case, using an inter processor interrupt and a short message buffer is much more efficient than using a shared memory and polling based approach.
Within the Zynq MPSoC, several inter processor interrupts are provided which enable interrupts and communication between:
Application Processing Unit (1 IPI)
Real Time Processing Unit (2 IPI)
Platform Management Unit (4 IPI)
Programmable Logic (4 IPI)
With the exception of the PMU IPIs that are hardwired, the remaining seven interrupts can be modified from their default assignment as they are distributed to all interrupt controllers.
The default IPI assignment can be changed by selecting the advanced mode within Vivado Re Customize IP on MPSoC core. The advanced configuration tab enables the assignment of the IPI to be completed, if a different mapping is preferred. For the example we will look at in this blog, I used the default assignment.
To facilitate the transfer of messages in both directions, there are seven sets of transmit and receive buffers. One buffer set for each IPI within the APU, RPU and PL, while the APU has one set buffer set shared between all four IPI.
Each set of buffers consists of eight transmit and eight receive registers for a total of 128 buffers. Each buffer is capable of storing up to 23 Bytes, not a significant number of bytes but still very useful.
When it comes to configuring and monitoring IPIs within our system, each IPI has six register — two are used to trigger a interrupt and four are used in the response.
As a demonstration of how we can use IPI, I created an example in which the APU interrupts the RPU. Once the interrupt has been asserted, messages are communicated in both directions.
To use the IPI in our software, we can make use of the xipipsu.h API as we would with any other peripheral within the Zynq MPSoC.
We can use these libraries in the A53 to configure drive the interrupt and transmit data, while in the R5 we use it obtain the data and send a response.
In the code snippet above you can find the IPI target within the Xparameters.h.
The message is written into one of two buffers, the message buffer or the response buffer the definition XIPIPSU_BUF_TYPE_MSG or XIPIPSU_BUF_TYPE_RESP, respectively define which buffer is to be read or written from.
In this example we poll for the IPI ACK, before we try and read the response from the response buffer.
Of course as the IPI is an interrupt within the R5, we must configure the R5 use the correct IPI interrupt and define the interrupt handler.
It is within the interrupt handler in the R5 that we use the xipipsu.h API to respond to the interrupt.
When I ran this on my Ultra96, this is the response I received when I configured the A53 to transmit 32 bytes of 0x55555555 and the R5 responds with 32 bytes of 0xAAAAAAAA.
All told, IPI are very simple yet powerful to get up and running on our Zynq MPSoC.