RA8 Inter-Processor Communication (IPC) Usage

RA8 Inter-Processor Communication (IPC) Usage

Renesas RA8M2, RA8D2, RA8T2, and RA8P1 are available as dual-core MCUs with CM85 and CM33 cores. Inter-Processor Communication (IPC) peripheral is designed to allow data exchanges between the two cores. This post explains several methods for exchanging data on RA8P1 with IPC peripheral and Flexible Software Package (FSP). A sample project is also provided.

A Bit About IPC Peripheral and FSP APIs

IPC peripheral supports:

  • Non Maskable Interrupt (NMI) event notifications

  • Maskable Interrupt (MI) event notifications or 4-byte data exchange through FIFOs

  • Shared memory between the two CPUs with semaphores for data synchronization and messages longer than 4 bytes.

FSP support is provided by the following APIs.

  • Following are IPC FSP Module APIs. You must add FSP IPC Module to your project to use these APIs.

Functions

Description

R_IPC_Open

Configure an IPC instance

R_IPC_MessageSend

Send the message to IPC-Message FIFO

R_IPC_EventGenerate

Generate IPC maskable interrupt to another core

R_IPC_CallbackSet

Updates the user callback and has option of providing memory for callback structure

R_IPC_Close

Closes the IPC driver

  • Following are IPC APIs in BSP. These APIs come with the BSP and you do not need to add FSP IPC Module.

Functions

Description

R_BSP_IpcNmiEnable

Assign the user callback for IPC-NMI

R_BSP_IpcSemaphoreTake

Take hardware semaphore

R_BSP_IpcSemaphoreGive

Release hardware semaphore

R_BSP_IpcNmiRequestSet

Trigger non-maskable interrupt to another core

For more details, see RA8 dual core MCU User Manuals and FSP Documentation.

Sample Project

The sample project runs on EK-RA8P1 and uses FSP v6.3.0. You may want to download and import the sample project before you continue with the rest of this post.

By default, FSP configures CPU0 as CM85 and CPU1 as CM33 core. CPU0 sends SW1 and SW2 push button press events to CPU1. Upon reception of these events, CPU1 toggles LED1 and LED2 respectively. These examples show how to use NMI and MI event and data exchange for simple messages.

As for CPU1, it randomly selects well-known movie quotations (just to have some fun) from a list and send them to CPU0 every second. Received quotations are printed to the virtual debug port. This is an example of more complex data communication between the CPUs with more than 4 bytes or simple events (no data).

To see the virtual debug port output, connect debugger cable to your PC.  Use Windows Device Manager to find the enumerated COM port number.  Configure your terminal program to connect this port at 115,200, 8-bit, no parity, 1 stop bit. 

Method 1

Method 1 uses an NMI to send an event to another CPU.  In this sample project, CPU0 sends SW1 push button press events with NMI request as shown below.  The R_BSP_IpcNmiRequestSet API can be called from an interrupt service routine (ISR).  In this case, it is called from g_sw1_cb function that services the SW1 push button interrupts.

image-20260128-202614.png
CPU0 - SW1 Callback Function

To receive this NMI request, CPU1 setups an ISR by R_BSP_IpcNmiEnable API. The g_nmi_ipc_cb function toggles the LED1 within the ISR context since the event processing is short. The usage of R_BSP_IpcNmiEnable API and the processing in the ISR are shown below.

image-20260128-203105.png
CPU1 - Enable NMI and Setup Callback Function
image-20260128-203302.png
CPU1 - NMI Callback Function

There is no need to add the FSP IPC Module for generating and handling NMIs.  The required APIs are provided by the BSP Module.  Note that only one specific event can be sent with NMIs.  In our sample project, it is the SW1 push button press event. 

Method 2

This is an example of using MI and exchanging a 4-byte of data by dedicated IPC FIFOs. You need to add the FSP IPC Module to use IPC FIFO message method. The IPC FIFO can transmit only 4 bytes of data. However, semaphore synchronization and exclusive control are not needed with this method.

SW2 push button press event on CPU0 generates an interrupt and it is processed in g_sw2_cb function that sets a global flag.

image-20260128-223912.png
CPU0 - SW2 Callback Handler

The event is processed in the main loop of CPU0. A simple “true” (or numerical value of 1) is sent to CPU1.

image-20260128-224306.png
CPU0 - Processing of SW2 Event

At CPU1, an MI is generated upon reception of the FIFO message and a registered callback function is called. Callback function is configured through FSP Configuration tool. In this sample project, the name of the ISR callback function is g_ipc1_cb and the event type is PC_EVENT_MESSAGE_RECEIVED.

image-20260128-225223.png
CPU1 - IPC Callback Function

In the callback function, a global flag is set for processing of the received message in the main loop of CPU1 and LED2 is toggled although it could have been processed in the callback function. Remember that callback functions are run in an interrupt context and therefore, certain precautions must be taken.

image-20260128-230100.png
CPU1 - Processing of SW2 Event

Method 3

So far we have seen two communication methods between the CPUs. NMIs and MIs with FIFO. First method is for communicating events without data and the second method is sending a 4-byte message.

What if we need to send more than 4-byte data? This is done by using a memory shared between both CPUs. Because data is shared by both CPUs, semaphore synchronization and exclusive access to the memory is required. You can implement your own solution for this or use the IPC hardware semaphores and FSP APIs. The important point is, however, independent of the techniques you use, it is up to the programmer to make sure the shared memory access by the CPUs is mutually exclusive.

This sample project allocates a 1KB shared memory. This is done through FSP Configuration tool in the Solution Project. The partition name of the shared memory is RAM_SHARED_MEM.

image-20260128-230955.png
Solution Project - Shared Memory Partition

Notes about user defined memory partitions:

  • When you build your CPU0 or CPU1 projects, you will see a generated linker file named fsp_gen.ld in the Debug folder. The partition will be named the same as in the FSP Configurator e.g. RAM_SHARED_MEM in this example. This a user defined memory partition and it will be located at the end of fsp_gen.ld file. You do not need to make any changes to the linker file. This is just an FYI.

image-20260128-231614.png
Generated Linker File - User Defined Memory Partition
  • You must define a variable placed in this shared memory only in one of the CPUs. Otherwise, you will see memory overflow errors. The example below shows the definition of shared_mem array in CPU0.

image-20260128-232002.png
CPU0 - Shared Memory Definition
  • You can access this memory from the other CPU by declaring a pointer to the shared memory. An example is given below. CPU1 accesses the shared memory through p_shared_mem pointer.

image-20260128-232312.png
CPU1 - Referencing to the Shared Memory
  • The information of the start address and the size of the shared memory partition are provided in the bsp_linker_info.h file in the Debug folder.

Notes about semaphore usage:

  • To use semaphores, you must include the FSP IPC Semaphore Module 

  • Configure the semaphore name and its number through the FSP Configuration tool

  • There are 16 semaphores available in RA8 dual core MCUs.

To demonstrate the shared memory method, the sample project randomly selects a movie quotation from a list and sends it from CPU1 to CPU0 every second. The quotation list is an array of strings and named g_messages.

image-20260129-183613.png
CPU1 - Movie Quotations

When it is time to select a message and send it, CPU1 takes the semaphore configured, writes the selected message to the shared memory and generates an interrupt to CPU0.

image-20260129-165202.png
CPU1 - Transmit Message by Writing to Shared Memory

At CPU0, the ISR calls g_ipc0_cb function with event type IPC_EVENT_IRQ1. A global flag is set for further processing in the main loop of CPU0.

image-20260129-165517.png
CPU0 - IPC Callback Function for Shared Memory Message

In the main loop of CPU0, the received message is copied from the shared memory into a local buffer. Then it is printed to the virtual debug port and displayed on a terminal program. CPU0 also generates a MI back to CPU1 to complete the message exchange handshake.

image-20260129-170225.png
CPU0 - Processing of Shared Memory Message Reception

Messages sent by CPU1 are displayed by CPU0 on a terminal program.

image-20260129-170640.png
CPU0 - Prints Out Messages Received from CPU1

When CPU1 receives the IPC event type IPC_EVENT_IRQ1, it gives the semaphore back. It is important that a semaphore taken by a CPU must be given back the same CPU. This will guarantee exclusive access to the shared memory.

image-20260129-170849.png
CPU1 - Gives Back the Semaphore