Introduction
Inter-Processor Communication (IPC) is a hardware-accelerated communication mechanism specifically designed for multi-core heterogeneous systems. It enables cross-core data interaction through shared SRAM, ensuring low-latency information transfer between multiple cores. Its core objective is to provide efficient collaborative capabilities for multi-core systems.
IPC Architecture
Hardware Architecture Features
The block diagram of IPC is illustrated in the following figure:
Symmetric Structure
Each CPU is equipped with an identical IPC module.
Full-Duplex Communication Model
Supports bidirectional, full-duplex communication.
Channel Isolation
Channels are designed with hardware-level resource isolation, ensuring data transmission on any channel does not block others.
Supports parallel multi-channel operations (e.g., CPUA can simultaneously transmit sensor data via Channel n and log information via Channel m).
Message Pool
A pre-allocated memory block where each IPC channel sends/receives messages through predefined fixed addresses in the pool.
Shared Memory
For messages larger than 4 bytes: The sender dynamically allocates a shared memory block, writes the memory address into the message pool, and the receiver copies the data locally before the sender releases the shared memory.
For messages ≤4 bytes: Directly write the message into the message pool.
NVIC Interrupt Generator
After data is ready in an IPC channel, writing to specific registers in the IPC block triggers a receive interrupt on the receiving end. Similarly, completing reception can trigger a transmission completion interrupt on the sender side.
Data Flow Model
Sender Process: [Shared Information] → Select available channel → Write to shared memory → Update channel register → Trigger remote interrupt → Remote core reads memory.
Receiver Process: Interrupt notification → Read channel register → Locate shared memory → Copy data locally → Process data → Clear interrupt flag.
IPC Definition
Channel
Concept: An IPC channel is a bidirectional, independent communication path between two IPC entities. Each direction contains multiple channels.
Purpose: Used to transmit send/receive events between two CPUs to achieve inter-core communication.
Component
IPC Configuration Table (IPC_INIT_TABLE)
Purpose: Defines all information for the entire transmission link, including data type, receive/transmit callback functions, interrupt handler parameters, transmission direction, and IPC channel number.
Usage: Users must place an instance of this structure in the IPC_TABLE_DATA_SECTION. The SDK will enable Tx/Rx interrupts for the corresponding channel and register interrupt handlers based on the structure’s members.
Structure Definition:
IPC_TABLE_DATA_SECTION typedef struct _IPC_INIT_TABLE_ { u32 USER_MSG_TYPE; /* Data type: IPC_USER_DATA (data less than 32 bits) or IPC_USER_POINT (address of data larger than 32 bits) */ void (*Rxfunc)(void *Data, u32 IrqStatus, u32 ChanNum); /* Receive interrupt callback function */ void *RxIrqData; /* Parameter for receive callback function */ void (*Txfunc)(void *Data, u32 IrqStatus, u32 ChanNum); /* Transmit completion interrupt callback function */ void *TxIrqData; /* Parameter for transmit callback function */ u32 IPC_Direction; /* Transmission direction */ u32 IPC_Channel; /* IPC channel number */ } IPC_INIT_TABLE, *PIPC_INIT_TABLE;
IPC Data Structure (IPC_MSG_STRUCT)
Purpose: In the message pool, each IPC channel has a fixed location to store the
IPC_MSG_STRUCT
structure, which is used to store and transmit data.Structure Definition:
typedef struct ipc_msg_struct { u32 msg_type; /* Data type, same as USER_MSG_TYPE */ u32 msg; /* 32-bit data to transmit or data address (for data larger than 32 bits) */ u32 msg_len; /* Data length */ u32 rsvd; /* Reserved field */ } IPC_MSG_STRUCT, *PIPC_MSG_STRUCT, ipc_msg_struct_t;
IPC Standard Usage
Communication Protocol Stack Registration
1.1 Channel Selection
File Path: ameba_ipccfg.h
Select and Define Channels:
Choose and customize channels by uncommenting and modifying their definitions. Example:
1/* Uncomment Channel n and add a functional description */ 2#define IPC_N2A_Channeln n /*!< User-defined channel: Sensor data transfer from KM0 to KM4 */ 3... 4//#define IPC_A2N_Channeln n /*!< User-defined channel: Sensor data transfer from KM4 to KM0 */
Confirm Transmission Direction:
1#define IPC_KM0_TO_KM4 0 // KM0 as sender, KM4 as receiver 2#define IPC_KM4_TO_KM0 1 // Reverse direction
Guidelines:
Each direction supports 16 physical channels; channel numbers must be within 0–15.
Avoid using system-reserved channels (e.g., those used by Wi-Fi/BT system components).
1.2 Define Receiver Callback Function
The receiver callback function reads messages from the message pool. For pointer-type messages, use
_memcpy()
to copy shared data to local memory. The SDK automatically clears interrupt flags after the handler exits.1uint32_t local_buffer[32] = {0}; 2 3/* Receive handler (interrupt context) */ 4void Rx_Channeln_Handler(void) { 5 PIPC_MSG_STRUCT msg = ipc_get_message(IPC_KM0_TO_KM4, IPC_N2A_Channeln); /* Retrieve message metadata */ 6 uint32_t *sensor_data = (uint32_t*)msg->msg; /* Extract data pointer */ 7 _memcpy(local_buffer, (void*)sensor_data, data_size); /* Copy data to local (data length pre-agreed) */ 8 rtos_sema_give(ipc_sema); /* Trigger task-level processing */ 9}
1.3 Register IPC Configuration Table
Register an IPC configuration table for each channel on the receiver side. The SDK automatically enables receive interrupts.
1 /* Channel configuration table */ 2IPC_TABLE_DATA_SECTION 3const IPC_INIT_TABLE ipc_chn_config = { 4 .msg_type = IPC_USER_POINT, // Message type: pointer (data > 32 bits) 5 .rx_func = Rx_Channeln_Handler, 6 .rx_irq_data = NULL, // No additional parameters 7 .tx_func = NULL, // Unidirectional transfer (no TX callback) 8 .tx_irq_data = NULL, 9 .direction = IPC_KM0_TO_KM4, // Direction identifier 10 .channel_num = IPC_N2A_Channeln // Channel macro 11};
Data Transmission and Reception
2.1. Sender Implementation
Call
ipc_send_message()
with direction, channel number, and message.Example: IPC sends a request from KM0 to KM4 via Channel n.
1 void send_sensor_data(void) { 2 IPC_MSG_STRUCT ipc_msg_temp; 3 sensor_buffer = read_sensor(); // Update shared memory 4 5 ipc_msg_temp.msg_type = IPC_USER_POINT; // Pointer-based transfer 6 ipc_msg_temp.msg = (u32)&sensor_buffer; 7 ipc_msg_temp.msg_len = 1; // Data length (bytes) 8 ipc_msg_temp.rsvd = 0; 9 10 /* Send shared data (SDK API) with timeout handling */ 11 ipc_send_message(IPC_KM0_TO_KM4, IPC_N2A_Channeln, &ipc_msg_temp); 12 }
2.2. Receiver Handling
The receiver thread monitors
ipc_sema
, processes data upon arrival, and proceeds to next steps.1 /* Interrupt context: Fast data capture */ 2void Rx_Channeln_Handler(void *data) { 3} 4 5/* Task context: Data processing */ 6void ipc_task(void *param) { 7 while (1) { 8 /* Wait for data arrival */ 9 rtos_sema_take(ipc_sema, RTOS_WAIT_FOREVER); 10 /* Process data (already copied to local_buffer via _memcpy) */ 11 data_pipeline_process(local_buffer); 12 } 13}
Communication Protocol Stack Registration
1.1 Channel Selection
File Path: ameba_ipccfg.h
Select and Define Channels:
Choose and customize channels by uncommenting and modifying their definitions. Example:
1/* Uncomment Channel n and add a functional description */ 2#define IPC_R2M_Channeln n /*!< User-defined channel: Sensor data transfer from KR4 to KM4 */ 3... 4//#define IPC_M2R_Channeln n /*!< User-defined channel: Sensor data transfer from KM4 to KR4 */
Confirm Transmission Direction:
1#define IPC_KR4_TO_KM4 0 // KR4 as sender, KM4 as receiver 2#define IPC_KM4_TO_KR4 2 // Reverse direction
Guidelines:
Each direction supports 8 physical channels; channel numbers must be within 0–7.
Avoid using system-reserved channels (e.g., those used by Wi-Fi/BT system components).
1.2 Define Receiver Callback Function
The receiver callback function reads messages from the message pool. For pointer-type messages, use
_memcpy()
to copy shared data to local memory. The SDK automatically clears interrupt flags after the handler exits.1uint32_t local_buffer[32] = {0}; 2 3/* Receive handler (interrupt context) */ 4void Rx_Channeln_Handler(void) { 5 PIPC_MSG_STRUCT msg = ipc_get_message(IPC_KR4_TO_KM4, IPC_R2M_Channeln); /* Retrieve message metadata */ 6 uint32_t *sensor_data = (uint32_t*)msg->msg; /* Extract data pointer */ 7 _memcpy(local_buffer, (void*)sensor_data, data_size); /* Copy data to local (data length pre-agreed) */ 8 rtos_sema_give(ipc_sema); /* Trigger task-level processing */ 9}
1.3 Register IPC Configuration Table
Register an IPC configuration table for each channel on the receiver side. The SDK automatically enables receive interrupts.
1 /* Channel configuration table */ 2IPC_TABLE_DATA_SECTION 3const IPC_INIT_TABLE ipc_chn_config = { 4 .msg_type = IPC_USER_POINT, // Message type: pointer (data > 32 bits) 5 .rx_func = Rx_Channeln_Handler, 6 .rx_irq_data = NULL, // No additional parameters 7 .tx_func = NULL, // Unidirectional transfer (no TX callback) 8 .tx_irq_data = NULL, 9 .direction = IPC_KR4_TO_KM4, // Direction identifier 10 .channel_num = IPC_R2M_Channeln // Channel macro 11};
Data Transmission and Reception
2.1. Sender Implementation
Call
ipc_send_message()
with direction, channel number, and message.Example: IPC sends a request from KR4 to KM4 via Channel n.
1 void send_sensor_data(void) { 2 IPC_MSG_STRUCT ipc_msg_temp; 3 sensor_buffer = read_sensor(); // Update shared memory 4 5 ipc_msg_temp.msg_type = IPC_USER_POINT; // Pointer-based transfer 6 ipc_msg_temp.msg = (u32)&sensor_buffer; 7 ipc_msg_temp.msg_len = 1; // Data length (bytes) 8 ipc_msg_temp.rsvd = 0; 9 10 /* Send shared data (SDK API) with timeout handling */ 11 ipc_send_message(IPC_KR4_TO_KM4, IPC_R2M_Channeln, &ipc_msg_temp); 12 }
2.2. Receiver Handling
The receiver thread monitors
ipc_sema
, processes data upon arrival, and proceeds to next steps.1 /* Interrupt context: Fast data capture */ 2void Rx_Channeln_Handler(void *data) { 3} 4 5/* Task context: Data processing */ 6void ipc_task(void *param) { 7 while (1) { 8 /* Wait for data arrival */ 9 rtos_sema_take(ipc_sema, RTOS_WAIT_FOREVER); 10 /* Process data (already copied to local_buffer via _memcpy) */ 11 data_pipeline_process(local_buffer); 12 } 13}
Communication Protocol Stack Registration
1.1 Channel Selection
File Path: ameba_ipccfg.h
Select and Define Channels:
Choose and customize channels by uncommenting and modifying their definitions. Example:
1/* Uncomment Channel n and add a functional description */ 2#define IPC_R2M_Channeln n /*!< User-defined channel: Sensor data transfer from KR4 to KM4 */ 3... 4//#define IPC_M2R_Channeln n /*!< User-defined channel: Sensor data transfer from KM4 to KR4 */ 5//#define IPC_D2R_Channeln n /*!< User-defined channel: Sensor data transfer from DSP to KR4 */ 6//#define IPC_R2D_Channeln n /*!< User-defined channel: Sensor data transfer from KR4 to DSP */ 7//#define IPC_D2M_Channeln n /*!< User-defined channel: Sensor data transfer from DSP to KM4 */ 8//#define IPC_M2D_Channeln n /*!< User-defined channel: Sensor data transfer from KM4 to DSP */
Confirm Transmission Direction:
1#define IPC_KR4_TO_KM4 0 // KR4 as sender, KM4 as receiver 2#define IPC_KR4_TO_DSP 1 // KR4 as sender, DSP as receiver 3#define IPC_KM4_TO_KR4 2 // KM4 as sender, KR4 as receiver 4#define IPC_KM4_TO_DSP 3 // KM4 as sender, DSP as receiver 5#define IPC_DSP_TO_KR4 4 // DSP as sender, KR4 as receiver 6#define IPC_DSP_TO_KM4 5 // DSP as sender, KM4 as receiver
Guidelines:
Each direction supports 8 physical channels; channel numbers must be within 0–7.
Avoid using system-reserved channels (e.g., those used by Wi-Fi/BT system components).
1.2 Define Receiver Callback Function
The receiver callback function reads messages from the message pool. For pointer-type messages, use
_memcpy()
to copy shared data to local memory. The SDK automatically clears interrupt flags after the handler exits.1uint32_t local_buffer[32] = {0}; 2 3/* Receive handler (interrupt context) */ 4void Rx_Channeln_Handler(void) { 5 PIPC_MSG_STRUCT msg = ipc_get_message(IPC_KR4_TO_KM4, IPC_R2M_Channeln); /* Retrieve message metadata */ 6 uint32_t *sensor_data = (uint32_t*)msg->msg; /* Extract data pointer */ 7 _memcpy(local_buffer, (void*)sensor_data, data_size); /* Copy data to local (data length pre-agreed) */ 8 rtos_sema_give(ipc_sema); /* Trigger task-level processing */ 9}
1.3 Register IPC Configuration Table
Register an IPC configuration table for each channel on the receiver side. The SDK automatically enables receive interrupts.
1 /* Channel configuration table */ 2IPC_TABLE_DATA_SECTION 3const IPC_INIT_TABLE ipc_chn_config = { 4 .msg_type = IPC_USER_POINT, // Message type: pointer (data > 32 bits) 5 .rx_func = Rx_Channeln_Handler, 6 .rx_irq_data = NULL, // No additional parameters 7 .tx_func = NULL, // Unidirectional transfer (no TX callback) 8 .tx_irq_data = NULL, 9 .direction = IPC_KR4_TO_KM4, // Direction identifier 10 .channel_num = IPC_R2M_Channeln // Channel macro 11};
Data Transmission and Reception
2.1. Sender Implementation
Call
ipc_send_message()
with direction, channel number, and message.Example: IPC sends a request from KR4 to KM4 via Channel n.
1 void send_sensor_data(void) { 2 IPC_MSG_STRUCT ipc_msg_temp; 3 sensor_buffer = read_sensor(); // Update shared memory 4 5 ipc_msg_temp.msg_type = IPC_USER_POINT; // Pointer-based transfer 6 ipc_msg_temp.msg = (u32)&sensor_buffer; 7 ipc_msg_temp.msg_len = 1; // Data length (bytes) 8 ipc_msg_temp.rsvd = 0; 9 10 /* Send shared data (SDK API) with timeout handling */ 11 ipc_send_message(IPC_KR4_TO_KM4, IPC_R2M_Channeln, &ipc_msg_temp); 12}
2.2. Receiver Handling
The receiver thread monitors
ipc_sema
, processes data upon arrival, and proceeds to next steps.1 /* Interrupt context: Fast data capture */ 2void Rx_Channeln_Handler(void *data) { 3} 4 5/* Task context: Data processing */ 6void ipc_task(void *param) { 7 while (1) { 8 /* Wait for data arrival */ 9 rtos_sema_take(ipc_sema, RTOS_WAIT_FOREVER); 10 /* Process data (already copied to local_buffer via _memcpy) */ 11 data_pipeline_process(local_buffer); 12 } 13}
Communication Protocol Stack Registration
1.1 Channel Selection
File Path: ameba_ipccfg.h
Select and Define Channels:
Choose and customize channels by uncommenting and modifying their definitions. Example:
1/* Uncomment Channel n and add a functional description */ 2#define IPC_L2N_Channeln n /*!< User-defined channel: Sensor data transfer from LP to NP */ 3... 4//#define IPC_N2L_Channeln n /*!< User-defined channel: Sensor data transfer from NP to LP */ 5//#define IPC_L2A_Channeln n /*!< User-defined channel: Sensor data transfer from LP to AP */ 6//#define IPC_A2L_Channeln n /*!< User-defined channel: Sensor data transfer from AP to LP */ 7//#define IPC_N2A_Channeln n /*!< User-defined channel: Sensor data transfer from NP to AP */ 8//#define IPC_A2N_Channeln n /*!< User-defined channel: Sensor data transfer from AP to NP */
Confirm Transmission Direction:
1#define IPC_LP_TO_NP 0 // LP as sender, NP as receiver 2#define IPC_LP_TO_AP 1 // LP as sender, AP as receiver 3#define IPC_NP_TO_LP 2 // NP as sender, LP as receiver 4#define IPC_NP_TO_AP 3 // NP as sender, AP as receiver 5#define IPC_AP_TO_LP 4 // AP as sender, LP as receiver 6#define IPC_AP_TO_NP 5 // AP as sender, NP as receiver
Guidelines:
Each direction supports 8 physical channels; channel numbers must be within 0–7.
Avoid using system-reserved channels (e.g., those used by Wi-Fi/BT system components).
1.2 Define Receiver Callback Function
The receiver callback function reads messages from the message pool. For pointer-type messages, use
_memcpy()
to copy shared data to local memory. The SDK automatically clears interrupt flags after the handler exits.1uint32_t local_buffer[32] = {0}; 2 3/* Receive handler (interrupt context) */ 4void Rx_Channeln_Handler(void) { 5 PIPC_MSG_STRUCT msg = ipc_get_message(IPC_LP_TO_NP, IPC_L2N_Channeln); /* Retrieve message metadata */ 6 uint32_t *sensor_data = (uint32_t*)msg->msg; /* Extract data pointer */ 7 _memcpy(local_buffer, (void*)sensor_data, data_size); /* Copy data to local (data length pre-agreed) */ 8 rtos_sema_give(ipc_sema); /* Trigger task-level processing */ 9}
1.3 Register IPC Configuration Table
Register an IPC configuration table for each channel on the receiver side. The SDK automatically enables receive interrupts.
1 /* Channel configuration table */ 2IPC_TABLE_DATA_SECTION 3const IPC_INIT_TABLE ipc_chn_config = { 4 .msg_type = IPC_USER_POINT, // Message type: pointer (data > 32 bits) 5 .rx_func = Rx_Channeln_Handler, 6 .rx_irq_data = NULL, // No additional parameters 7 .tx_func = NULL, // Unidirectional transfer (no TX callback) 8 .tx_irq_data = NULL, 9 .direction = IPC_LP_TO_NP, // Direction identifier 10 .channel_num = IPC_L2N_Channeln // Channel macro 11};
Data Transmission and Reception
2.1. Sender Implementation
Call
ipc_send_message()
with direction, channel number, and message.Example: IPC sends a request from LP to NP via Channel n.
1 void send_sensor_data(void) { 2 IPC_MSG_STRUCT ipc_msg_temp; 3 sensor_buffer = read_sensor(); // Update shared memory 4 5 ipc_msg_temp.msg_type = IPC_USER_POINT; // Pointer-based transfer 6 ipc_msg_temp.msg = (u32)&sensor_buffer; 7 ipc_msg_temp.msg_len = 1; // Data length (bytes) 8 ipc_msg_temp.rsvd = 0; 9 10 /* Send shared data (SDK API) with timeout handling */ 11 ipc_send_message(IPC_LP_TO_NP, IPC_L2N_Channeln, &ipc_msg_temp); 12}
2.2. Receiver Handling
The receiver thread monitors
ipc_sema
, processes data upon arrival, and proceeds to next steps.1 /* Interrupt context: Fast data capture */ 2void Rx_Channeln_Handler(void *data) { 3} 4 5/* Task context: Data processing */ 6void ipc_task(void *param) { 7 while (1) { 8 /* Wait for data arrival */ 9 rtos_sema_take(ipc_sema, RTOS_WAIT_FOREVER); 10 /* Process data (already copied to local_buffer via _memcpy) */ 11 data_pipeline_process(local_buffer); 12 } 13}
IPC Advanced Usage
Application Scenarios
When the channel is busy, the sending thread should yield scheduling promptly to avoid CPU resource occupation by polling, ensuring smooth system operation.
Transmission Acknowledgment Configuration
Define Transmitter Callback Function
If the
IPC_INIT_TABLE
initializes a transmitter callback function, the SDK will automatically:Enable the transmission completion interrupt for channel n.
Initialize the transmission semaphore (
ipc_Semaphore
) when the channel is busy and wait for its release to yield scheduling, preventing prolonged CPU occupation.
We recommend registering this handler as
IPC_TXHandler()
from ameba_ipc_api.c. This function releases the semaphore for channel n (ipc_Semaphore
).The SDK automatically clears interrupt flags upon IRQ exit.
void IPC_TXHandler(void *Data, u32 IrqStatus, u32 ChanNum) { UNUSED(Data); UNUSED(IrqStatus); u32 CPUID = SYS_CPUID(); IPC_TypeDef *IPCx = IPC_GetDevById(CPUID); /* Disable tx channeln interrupt */ IPC_INTConfig(IPCx, ChanNum, DISABLE); if (ipc_Semaphore[ChanNum - IPC_TX_CHANNEL_SHIFT] != NULL) { /* Release semaphore */ rtos_sema_give(ipc_Semaphore[ChanNum - IPC_TX_CHANNEL_SHIFT]); } }
Register IPC Configuration Table
- A dedicated IPC configuration table must be registered for each channel on the transmitter side.
(Note: Transmit/receive configurations can coexist in the same table but require compilation on both cores.)
/* Channel configuration table */ IPC_TABLE_DATA_SECTION const IPC_INIT_TABLE ipc_chn_config = { .msg_type = IPC_USER_POINT, // Transfer mode: pointer (data >32 bits) .rx_func = NULL, // No receive handler .rx_irq_data = NULL, // No receive parameters .tx_func = IPC_TXHandlerL, // Transmit completion callback .tx_irq_data = NULL, .direction = IPC_Direction, // Direction identifier .channel_num = IPC_Channel // Channel macro };
Troubleshooting
Channel Conflict
Phenomenon |
Error log printed: Channel Conflict for CPU A Channel n ! Ignore If CPU Has Reset |
Cause |
Two interrupt callback functions are registered for the same channel |
Solution |
Check the IPC configuration table ( |
Send Timeout
Phenomenon |
Error log printed: IPC Request Timeout |
Cause |
The sender attempts to send data through the same channel again before the previous data has been received |
Solution |
|
Semaphore Acquisition Timeout
Phenomenon |
Error log printed: IPC Get Semaphore Timeout |
Cause |
The sender has registered a send completion callback function. The sender attempts to send data through the same channel again before the previous data has been received, resulting in a timeout while waiting for a semaphore |
Solution |
|
No Response from Receiver
Phenomenon 1 |
A specific IPC channel on the receiver is not responding |
Cause |
The interrupt for that channel on the receiver has been mistakenly disabled |
Solution |
Check if the interrupt for the channel has been mistakenly disabled on the receiver and enable it by calling |
Phenomenon 2 |
All IPC channels on the receiver are not responding |
Cause |
|
Solution |
|