Inter-Processor Communication
Overview
The nRF5340 DK application uses dual-core architecture where the APP core and NET core communicate via RPMsg/OpenAMP protocol. This enables efficient separation of concerns:
APP Core: High-level application logic (Matter, Thread, UI)
NET Core: Low-level radio protocols (BLE, 802.15.4)
Communication Mechanisms
The cores communicate using:
RPMsg/OpenAMP: Zephyr’s inter-processor communication framework
Shared Memory: Zero-copy message passing
Interrupts: Fast notification between cores
Message Queues: Buffered asynchronous communication
IPC Architecture
Inter-Processor Communication Flow
Inter-Processor Communication Flow:
APP Core NET Core
──────────────────────────── ────────────────────────────
│ │
│ smarthome::ipc::IPCCore │ smarthome::ipc::IPCCore
│ ┌───────────────────┐ │ ┌───────────────────┐
│ │ send(message) │ │ │ receive_callback()│
│ └────────┬──────────┘ │ └────────▲──────────┘
│ │ │ │
│ ▼ │ │
│ ┌──────────────┐ │ ┌──────────────┐
│ │ serialize │ │ │ deserialize │
│ └──────┬───────┘ │ └──────▲───────┘
│ │ │ │
│ ▼ │ │
│ ┌────────────────────────────────────────────┴─────┐
│ │ Shared Memory (RPMsg Buffer) │
│ │ ┌──────────────┐ ┌──────────────┐ │
│ │ │ TX Queue (→) │ │ RX Queue (←) │ │
│ │ └──────────────┘ └──────────────┘ │
│ └────────┬────────────────────────────▲───────────┘
│ │ │
│ ▼ │
│ [Interrupt NET] [Interrupt APP]
│ │
└────────────────────────────────────────┘
Note
PlantUML sequence diagram available in CI/CD builds at doc/diagrams/ipc_communication.puml
The smarthome::ipc::IPCCore module provides a C++ wrapper around Zephyr’s OpenAMP API:
namespace smarthome {
namespace ipc {
class IPCCore {
public:
static IPCCore& getInstance();
void init();
void send(const Message& msg);
void registerCallback(MessageCallback callback);
private:
// OpenAMP endpoint
struct rpmsg_endpoint endpoint_;
// Message queue for RX
K_KERNEL_STACK_MEMBER(rx_stack_, 1024);
struct k_thread rx_thread_;
};
} // namespace ipc
} // namespace smarthome
Note
See doc/diagrams/ipc_communication.puml for detailed PlantUML sequence diagram.
Message Types
IPC Message Structure
namespace smarthome {
namespace ipc {
enum class MessageType : uint8_t {
RADIO_CONTROL, // APP → NET: Radio commands
BLE_STATUS, // NET → APP: BLE connection state
NETWORK_STATS, // NET → APP: Link quality data
POWER_MANAGEMENT, // Bidirectional: Sleep/wake
};
struct Message {
MessageType type;
uint32_t timestamp;
uint16_t length;
uint8_t data[256];
};
} // namespace ipc
} // namespace smarthome
Communication Patterns
APP → NET Core
Use Case: APP core requests BLE advertising
// APP Core
smarthome::ipc::Message msg;
msg.type = MessageType::RADIO_CONTROL;
// ... populate data ...
smarthome::ipc::IPCCore::getInstance().send(msg);
// NET Core receives and processes
void handle_radio_control(const Message& msg) {
smarthome::protocol::ble::BLEManager::getInstance()
.startAdvertising();
}
NET → APP Core
Use Case: NET core notifies BLE connection
// NET Core
smarthome::ipc::Message msg;
msg.type = MessageType::BLE_STATUS;
// ... populate connection info ...
smarthome::ipc::IPCCore::getInstance().send(msg);
// APP Core receives and updates UI
void handle_ble_status(const Message& msg) {
// Update LED status or notify Matter stack
}
Implementation Details
Initialization
Both cores must initialize IPC before communication:
APP Core (app_core.cpp):
extern "C" int main() {
// Initialize IPC first
smarthome::ipc::IPCCore::getInstance().init();
// Register callback
smarthome::ipc::IPCCore::getInstance()
.registerCallback(app_ipc_callback);
// ... rest of initialization ...
}
NET Core (net_core.cpp):
namespace net {
void NetCoreManager::init() {
// Initialize IPC
smarthome::ipc::IPCCore::getInstance().init();
// Register callback
smarthome::ipc::IPCCore::getInstance()
.registerCallback([this](const auto& msg) {
this->handleMessage(msg);
});
// Initialize radio subsystems
ble_manager_.init();
radio_manager_.init();
}
} // namespace net
Message Threading
The IPC module uses Zephyr threading for message processing:
class IPCCore {
private:
// RX thread for processing incoming messages
K_KERNEL_STACK_MEMBER(rx_stack_, 1024);
struct k_thread rx_thread_;
// Thread entry point
static void rx_thread_entry(void* p1, void* p2, void* p3);
};
The RX thread:
Blocks on OpenAMP receive
Deserializes incoming message
Invokes registered callback
Returns to blocking receive
Performance Characteristics
- Latency:
< 100μs for message passing between cores
- Throughput:
Up to 10,000 messages/second
- Message Size:
Up to 256 bytes per message
- Queue Depth:
Configurable in OpenAMP (default 16 messages)
The low latency is achieved through:
Shared Memory: Zero-copy message passing
Hardware Interrupts: Direct core-to-core notification
No Context Switching: Dedicated IPC thread on each core
Configuration
OpenAMP Configuration
Zephyr configuration for inter-processor communication:
# Enable OpenAMP/RPMsg
CONFIG_OPENAMP=y
CONFIG_OPENAMP_RSC_TABLE=y
# IPC configuration
CONFIG_IPC_SERVICE=y
CONFIG_IPC_SERVICE_BACKEND_RPMSG=y
# Shared memory region
CONFIG_OPENAMP_SHM_BASE_ADDRESS=0x20070000
CONFIG_OPENAMP_SHM_SIZE=0x10000
Thread Stack Sizes
// IPC RX thread stack
K_KERNEL_STACK_MEMBER(rx_stack_, 1024); // 1 KB sufficient
// NET core main stack
K_KERNEL_STACK_DEFINE(net_core_stack, 2048); // 2 KB
Usage Examples
Example 1: Request Network Statistics
APP core requests network quality data from NET core:
// APP Core - request stats
smarthome::ipc::Message req;
req.type = MessageType::NETWORK_STATS;
smarthome::ipc::IPCCore::getInstance().send(req);
// NET Core - respond with stats
void handle_stats_request(const Message& req) {
smarthome::ipc::Message resp;
resp.type = MessageType::NETWORK_STATS;
// Populate with actual stats
int8_t rssi = get_current_rssi();
memcpy(resp.data, &rssi, sizeof(rssi));
smarthome::ipc::IPCCore::getInstance().send(resp);
}
Example 2: BLE Connection Notification
NET core notifies APP core of BLE connection changes:
// NET Core - BLE callback
void ble_connected_callback(bool connected) {
smarthome::ipc::Message msg;
msg.type = MessageType::BLE_STATUS;
msg.data[0] = connected ? 1 : 0;
msg.length = 1;
smarthome::ipc::IPCCore::getInstance().send(msg);
}
// APP Core - handle notification
void app_ipc_callback(const smarthome::ipc::Message& msg) {
if (msg.type == MessageType::BLE_STATUS) {
bool connected = msg.data[0] != 0;
LOG_INF("BLE connection status: %s",
connected ? "connected" : "disconnected");
}
}
Testing
Build and Flash =========both cores:
cd software ./make.sh build
Flash to nRF5340 DK:
./make.sh flashMonitor serial output:
screen /dev/ttyACM0 115200
Test IPC by pressing buttons - observe messages between cores
Debugging IPC
Enable IPC debug logging in prj.conf:
CONFIG_OPENAMP_LOG_LEVEL_DBG=y
CONFIG_IPC_SERVICE_LOG_LEVEL_DBG=y
Key Zephyr APIs Used
OpenAMP/RPMsg
API |
Description |
|---|---|
|
Register IPC endpoint |
|
Send message to remote core |
|
Receive message (callback) |
IPC Service
API |
Description |
|---|---|
|
Open IPC instance |
|
Register endpoint |
|
Send data via IPC |
Threading
API |
Description |
|---|---|
|
Define thread stack in class |
|
Create new thread |
|
Yield thread execution |
Source Code Reference
IPC Module Files
app/src/sdk/ipc/ipc_core.hpp- IPC interfaceapp/src/sdk/ipc/ipc_core.cpp- IPC implementationapp/src/app_core/app_core.cpp- APP core usageapp/src/net_core/net_core.cpp- NET core usageapp/src/modules/uart/uartmodule.hpp- UART module interfaceapp/src/modules/uart/uartmodule.cpp- UART module implementation
Task Files
app/src/thread/uart_task.h- UART task interfaceapp/src/thread/uart_task.cpp- UART task implementation