Co-Processor Communication
Overview
The purpose of the Co-Processor Protocol (CPC) is to act as a serial link multiplexer that allows data sent from multiple applications to be transported over a secure shared physical link.
In CPC, data transfers between processors are segmented in sequential packets over endpoints. Transfers are guaranteed to be error-free and sent in order.
The CPC secondary interacts with the host's CPC daemon over an encrypted UART or SPI link.
Read and write operations with the CPC API are thread-safe and zero-copy. Care must be taken to only alter the passed buffer when alteration is allowed and to free any buffer provided by the CPC core to the user. It is worth mentioning that, in order to minimize memory allocation, when the write buffer is encrypted the original content is lost and replaced by its encrypted counterpart.
The
CPC daemon
(CPCd) supports the upgrade of the secondary's firmware in standalone bootloader mode via UART XMODEM and SPI EZSP. The upgrade image must be in Gecko Bootloader (.gbl) format. For this function to be available, the secondary must have the Gecko Bootloader installed, and the secondary application image must be generated with the component
bootloader_interface
.
CPCD Building block
CPCD Diagram
Secondary Application - User Endpoints
User Endpoints
A secondary application must first open a user endpoint before it can be opened by a host application.
On the secondary device, CPC operations are zero-copy. For write operations, this means that the application must wait for confirmation that a buffer has been transmitted before it re-uses the buffer. An on-write-completed callback can be registered on an endpoint for this purpose. For read operations, this means that the application is responsible for freeing the reception buffer by calling the function
sl_cpc_free_rx_buffer()
.
A callback can be registered on an endpoint to notify the application that the host application has closed the endpoint. The secondary application must close and re-open the endpoint to re-establish the connection.
The endpoint IDs available for user endpoints are enumerated by
sl_cpc_user_endpoint_id_t
in
sl_cpc.h
.
Sample Code
#include <stdlib.h>
// CPC API
#include "sl_cpc.h"
static bool endpoint_connected = false;
static bool write_completed = false;
// Callback triggered on endpoint error
static void on_endpoint_error(uint8_t endpoint_id,
void *arg)
{
(void) arg;
(void) endpoint_id;
endpoint_connected = false;
}
// Callback triggered on write complete
static void on_write_completed(sl_cpc_user_endpoint_id_t endpoint_id,
void *buffer,
void *arg,
sl_status_t status)
{
(void) arg;
EFM_ASSERT(status == SL_STATUS_OK);
// allocated buffer can be safely freed
free(buffer);
}
void app(void)
{
sl_status_t status;
sl_cpc_endpoint_handle_t endpoint_handle;
void *read_array;
uint8_t *write_array;
uint16_t size;
while (1) {
if (endpoint_connected == false) {
// open user endpoint 0
status = sl_cpc_open_user_endpoint(&endpoint_handle, SL_CPC_ENDPOINT_USER_ID_0, SL_CPC_OPEN_ENDPOINT_FLAG_NONE, 1);
EFM_ASSERT(status == SL_STATUS_OK);
// register callback for write complete
status = sl_cpc_set_endpoint_option(endpoint_handle, SL_CPC_ENDPOINT_ON_IFRAME_WRITE_COMPLETED,
(void *)on_write_completed);
EFM_ASSERT(status == SL_STATUS_OK);
// register callback to re-open endpoint in case of error
status = sl_cpc_set_endpoint_option(ep, SL_CPC_ENDPOINT_ON_ERROR,
(void *)on_endpoint_error);
EFM_ASSERT(status == SL_STATUS_OK);
}
// blocking read - wait for primary to connect to the endpoint and send data
status = sl_cpc_read(&endpoint_handle, &read_array, &size, 0, 0u);
EFM_ASSERT(status == SL_STATUS_OK);
// copy data to write buffer
write_array = (uint8_t *)malloc(size);
EFM_ASSERT(write_array != NULL);
memcpy(write_array, read_array, size);
// return the buffer to CPC
sl_cpc_free_rx_buffer(read_array);
// echo the data back to the primary. Buffer will be freed in write complete callback
status = sl_cpc_write(&endpoint_handle, write_array, size, 0u, NULL);
EFM_ASSERT(status == SL_STATUS_OK);
}
}
A more complete sample application can be found in the
Example Projects & Demos
tab of Simplicity Studio.
CPC Debugging Tools
SystemView
SEGGER SystemView is a real-time recording and visualization tool for embedded systems. SystemView events are available when the
segger_systemview
component is added to the project.
Two options are available:
-
SL_CPC_DEBUG_SYSTEM_VIEW_LOG_CORE_EVENT
Enable events that happen at the core level -
SL_CPC_DEBUG_SYSTEM_VIEW_LOG_ENDPOINT_EVENT
Enable events that happen on an endpoint
Counters
Debug counters can be used to track the count of various CPC events. These counters are accessible via the debugger through the
sl_cpc_core_debug
structure.
Three options are available:
-
SL_CPC_DEBUG_CORE_EVENT_COUNTERS
Enable counters for events that happen at the core level -
SL_CPC_DEBUG_ENDPOINT_EVENT_COUNTERS
Enable counters for events that happen on an endpoint -
SL_CPC_DEBUG_MEMORY_ALLOCATOR_COUNTERS
Enable counters for memory allocation
Assertions
When debugging,
DEBUG_EFM
should be defined from the compiler to enable the default internal assert handler.