IPC Notify
This module define’s APIs for low latency IPC between different core’s on a CPU. These low latency IPC APIs are constrained in features but offer extermely fast transfer of message values between two cores.
Underlying implementation will use HW mechanisms to interrupt the receiving cores, it will also use HW FIFOs (when available) or shared memory based SW FIFOs in fast internal RAM’s to transport the message values.
AM62DX uses HW mailbox based HW FIFOs to transport the message and interrupt the receiving core.
Features Supported
Low latency message send and receive between any to any CPUs running no-RTOS or RTOS
Low latency is achieved by
Accessing the HW in very few steps (due to this most error checking is left to the user)
Combining the message and client ID into a single 32b value that is send via HW/SW FIFO’s (due to this there are constraints on max client ID and max message value)
Handling the message received within ISR itself and calling user callback within few steps of receiving the interrupt.
Client ID field allows to send messages to different SW clients on the receving side
Typically each SW client will be indepedant SW entity, so client ID allows to do basic demultiplexing of messages and thus keep the SW entities indepedant of each other even when IPC is involved.
Ability to register different user handlers for different client ID’s
Callback based mechnism to recieve messages
Ability to block on message send for ever OR return with error, if the underlying IPC HW/SW FIFO is full.
SysConfig Features
Note
It is strongly recommend to use SysConfig where it is available instead of using direct SW API calls. This will help simplify the SW application and also catch common mistakes early in the development cycle.
Enable/Disable IPC Notify between different CPUs
Features NOT Supported
IPC Notify cannot be used to talk to clients running Linux OS
Ability to generate interrupt when the underlying IPC HW/SW FIFO is free to send messages (i.e. only FIFO status polling mode is supported)
Important Usage Guidelines
To balance low latency performance vs flexiblility to end user, below contraints are introduced in the API
The maximum number of clients supported is constrained to
IPC_NOTIFY_CLIENT_ID_MAX.The maximum message value that is exchanged is contrained to
IPC_NOTIFY_MSG_VALUE_MAX
IPC_NOTIFY_MSG_VALUE_MAXis < 32b, and hence one cannot pass pointers as messages.However, passing pointers as messages is not a recommended SW design and instead one should pass offset from some known base address as values instead.
Offsets can easily fit within
IPC_NOTIFY_MSG_VALUE_MAXlimit.
Internally the implementation will combine client ID and message value as one 32b integer.
Example Usage
Include the below files to access the APIs
#include <stdio.h>
#include <drivers/ipc_notify.h>
#include <kernel/dpl/DebugP.h>
Initialize IPC Notify
int32_t status;
IpcNotify_Params notifyParams;
/* initialize parameters to default */
IpcNotify_Params_init(¬ifyParams);
/* specify the core on which this API is called */
notifyParams.selfCoreId = CSL_CORE_ID_R5FSS0_0;
/* list the cores that will do IPC Notify with this core
* Make sure to NOT list `self` core in the list below
*/
notifyParams.numCores = 1;
notifyParams.coreIdList[0] = CSL_CORE_ID_R5FSS0_1;
status = IpcNotify_init(¬ifyParams);
DebugP_assert(status==SystemP_SUCCESS);
Register handler to receive messages
int32_t status;
/* client ID to register against, make sure messages are sent to this client ID */
uint16_t clientId = 4;
/* create a local queue to hold the emssage */
MyQueue_create(&gMyLocalQ);
/* register a handler to receive messages */
status = IpcNotify_registerClient(clientId, MyMsg_handler, &gMyLocalQ);
DebugP_assert(status==SystemP_SUCCESS);
Send message
/* send `msgValue` to `clientId` of core CSL_CORE_ID_R5FSS0_1 */
int32_t status;
/* client ID for which this message is intended,
* make sure a handler is registered for this client ID
*/
uint16_t clientId = 4;
/* message value to send, amke sure the
* registered handler handles this message
*/
uint32_t msgValue = 0x08765432;
/* no error checks done inside IpcNotify_sendMsg(), so doing here just to show the constraints */
DebugP_assert(msgValue < IPC_NOTIFY_MSG_VALUE_MAX);
DebugP_assert(clientId < IPC_NOTIFY_CLIENT_ID_MAX);
/* wait until msg is put into internal HW/SW FIFO */
status = IpcNotify_sendMsg(CSL_CORE_ID_R5FSS0_0, clientId, msgValue, 1);
DebugP_assert(status==SystemP_SUCCESS);
Receive message and handle it in a task
/* NOTE: local queue implementation not shown, this is a standard FIFO like SW queue,
* which is thread and interrupt safe and can block until there is a element to dequeue
*/
/* local Q to hold received messages */
MyQueue_Obj gMyLocalQ;
void MyMsg_handler(uint32_t remoteCoreId, uint16_t localClientId, uint32_t msgValue, void *args)
{
MyQueue_Obj *myLocalQ = (MyQueue_Obj*)args;
/* message received from remote core `remoteCoreId`, for client ID `localClientId` on this core */
/* instead of handling the messages in callback which is called within ISR, queue this into a larger SW queue.
* Handle to the SW queue is passed via args in this example.
* SW queue could be one per remote core, one per client ID or a common Q for all remote cores and so on.
* Passing queue handle as argument allows the handler to remain
* common across multiple remote cores and client ID's
*/
MyQueue_put(myLocalQ, msgValue);
/* NOTE: THis is a sample handler, actually application can have different design based on its
* specific requirements
*/
}
/* Message handler task */
void MyTask_main(void *args)
{
while(1)
{
uint32_t msgValue;
/* block until there is a element to dequeue from this Q */
MyQueue_wait(&gMyLocalQ, &msgValue);
if(msgValue == 0x08765432)
{
/* handle message value.
* typically message value will be a command to execute
* OR
* it will point (offset or index within a known shared memory base address or array)
* to command and parameters to execute
*/
}
}
}
API Reference
Defines
-
IPC_NOTIFY_CLIENT_ID_MAX
Maximum number of clients possible for receiving messages.
-
IPC_NOTIFY_CLIENT_ID_RPMSG
Client ID used by rpmessage, this client ID should not be used by other users.
-
IPC_NOTIFY_CLIENT_ID_SYNC
Client ID used for sync messages, this client ID should not be used by other users.
-
IPC_NOTIFY_CLIENT_ID_RP_MBOX
Client ID used for remoteproc (RP_MBOX) related messages, this client ID should not be used by other users.
-
IPC_NOTIFY_MSG_VALUE_MAX
Maximum value of message that can be sent and received.
Typedefs
-
typedef void (*IpcNotify_FxnCallback)(uint16_t remoteCoreId, uint16_t localClientId, uint32_t msgValue, void *args)
User callback that is invoked when a message is received from a reote core for a given client ID.
Before invoking the API, the IPC module would have ‘popped` the message from the HW or SW FIFO already. This callback is called frm ISR context, so all constraints of ISR should be applied by the callback, e.g. no blocking wait, quickly handle message and exit ISR.
For most applications, it is recommended to put the message value into a local SW queue and defer the message handling itself to a application task.
- Param remoteCoreId:
[in] Remote core that has sent the message
- Param localClientId:
[in] Local client ID to which the message is sent
- Param msgValue:
[in] Message value that is sent
- Param args:
[in] Argument pointer passed by user when IpcNotify_registerClient is called
-
typedef void (*IpcNotify_NonNotifyCallback)(uint32_t remoteCoreId)
This is a driver library internal API and is used in certain SOCs by the separate mailbox driver.
- Attention
This API should not be used by end users.
- Param remoteCoreId:
[in] remote core ID that generated the interrupt
Enums
-
enum IpcNotify_RP_Mbox_Message
Enum to list the various messages sent by remote proc kernel driver.
Client ID for this always will be 0xFF IPC_NOTIFY_CLIENT_ID_RP_MBOX
Note
This structure and call to IpcNotify_init would be generated by SysConfig.
Values:
-
enumerator IPC_NOTIFY_RP_MBOX_READY
-
enumerator IPC_NOTIFY_RP_MBOX_PENDING_MSG
-
enumerator IPC_NOTIFY_RP_MBOX_CRASH
-
enumerator IPC_NOTIFY_RP_MBOX_ECHO_REQUEST
-
enumerator IPC_NOTIFY_RP_MBOX_ECHO_REPLY
-
enumerator IPC_NOTIFY_RP_MBOX_ABORT_REQUEST
-
enumerator IPC_NOTIFY_RP_MBOX_SUSPEND_AUTO
-
enumerator IPC_NOTIFY_RP_MBOX_SUSPEND_SYSTEM
-
enumerator IPC_NOTIFY_RP_MBOX_SUSPEND_ACK
-
enumerator IPC_NOTIFY_RP_MBOX_SUSPEND_CANCEL
-
enumerator IPC_NOTIFY_RP_MBOX_SHUTDOWN
-
enumerator IPC_NOTIFY_RP_MBOX_SHUTDOWN_ACK
-
enumerator IPC_NOTIFY_RP_MBOX_END_MSG
-
enumerator IPC_NOTIFY_RP_MBOX_READY
Functions
-
void IpcNotify_Params_init(IpcNotify_Params *params)
Set default value to IpcNotify_Params.
- Parameters:
params – [out] Default initialized structure
-
int32_t IpcNotify_init(const IpcNotify_Params *params)
Initialize IPC Notify module.
This API will initialize the HW used for IPC including registering interrupts for receiving messages.
- Parameters:
params – [in] Initializaion parameters
-
void IpcNotify_deInit(void)
De-initialize IPC Notify module.
This API will de-initialize the HW used for IPC including un-registering interrupts for receiving messages.
-
int32_t IpcNotify_sendMsg(uint32_t remoteCoreId, uint16_t remoteClientId, uint32_t msgValue, uint32_t waitForFifoNotFull)
Send message to a specific remote core and specific client ID on that remote core.
Note
To reduce latency, error checks are avoided in this API. Users need to make sure the client ID value is < IPC_NOTIFY_CLIENT_ID_MAX and message value is < IPC_NOTIFY_MSG_VALUE_MAX
Note
This API can be called from within ISRs and is also thread-safe. Internally this API disables interrupts for a short while to make the API ISR and thread safe.
Note
One cannot send messages to self, i.e remoteCoreId, cannot be same as core ID of the CPU that called this API.
- Parameters:
remoteCoreId – [in] Remote core to sent message to, see CSL_CoreID for valid values.
remoteClientId – [in] Remote core client ID to send message to, MUST be < IPC_NOTIFY_CLIENT_ID_MAX
msgValue – [in] Message value to send, MUST be < IPC_NOTIFY_MSG_VALUE_MAX
waitForFifoNotFull – [in] 1: wait for message to be inserted into HW or SW FIFO, 0: if FIFO is full, dont send message and return with error.
- Returns:
SystemP_SUCCESS, message sent successfully
- Returns:
SystemP_FAILURE, message could not be sent since HW or SW FIFO for holding the message is full.
-
int32_t IpcNotify_registerClient(uint16_t localClientId, IpcNotify_FxnCallback msgCallback, void *args)
Register a callback to handle messages received from a specific remote core and for a specific local client ID.
- Parameters:
localClientId – [in] Client ID to which the message has been sent
msgCallback – [in] Callback to invoke, if callback is already registered, error will be returned.
args – [in] User arguments, that are passed back to user when the callback is invoked
- Returns:
SystemP_SUCCESS, callback registered sucessfully
- Returns:
SystemP_FAILURE, callback registration failed, either remoteCoreId or localClientId is invalid or callback already registered.
-
int32_t IpcNotify_unregisterClient(uint16_t localClientId)
Un-register a previously registered callback.
- Parameters:
localClientId – [in] Client ID to which the message has been sent
- Returns:
SystemP_SUCCESS, callback un-registered sucessfully
- Returns:
SystemP_FAILURE, callback un-registration failed, either remoteCoreId or localClientId is invalid
-
uint16_t IpcNotify_getSelfCoreId(void)
Return current core ID.
- Returns:
Core ID, see CSL_CoreID for valid values.
-
uint32_t IpcNotify_isCoreEnabled(uint32_t coreId)
Check if a core is enabled for IPC.
- Parameters:
coreId – [in] Core ID, see CSL_CoreID for valid values.
- Returns:
1: core is enabled for IPC, 0: core is not enabled for IPC
-
int32_t IpcNotify_sendSync(uint32_t remoteCoreId)
Send a sync message to specific core.
This API can be used to send sync message’s to very specific core’s For most users recommend to use the more simpler IpcNotify_syncAll() API.
- Parameters:
remoteCoreId – [in] Core ID, see CSL_CoreID for valid values.
- Returns:
SystemP_SUCCESS, sync message was sent successfully, else failure
-
int32_t IpcNotify_waitSync(uint32_t remoteCoreId, uint32_t timeout)
Wait for a sync message to be received from the specified core.
This API can be used to recevice sync message from very specific core’s For most users recommend to use the more simpler IpcNotify_syncAll() API.
- Parameters:
remoteCoreId – [in] Core ID, see CSL_CoreID for valid values.
timeout – [in] Amount of time in units of ticks to wait
- Returns:
SystemP_SUCCESS, sync message was recevied successfully
- Returns:
SystemP_TIMEOUT, sync message was NOT recevied after
timeoutticks- Returns:
SystemP_FAILURE, invalid arguments
-
int32_t IpcNotify_syncAll(uint32_t timeout)
Send a message to all enabled cores and wait for sync message from all enabled cores.
This API when called on all CPUs, make sure all CPUs execute upto a certain and then proceed only when all other CPUs have also executed to the same point.
This is useful esp during system init to make sure message exchange can be started only after all CPUs have finished their system initialization.
- Parameters:
timeout – [in] Amount of time in units of ticks to wait for the sync
- Returns:
SystemP_SUCCESS, all sync messages recevied successfully
- Returns:
SystemP_TIMEOUT, some sync messages was NOT recevied after
timeoutticks- Returns:
SystemP_FAILURE, invalid arguments
-
void IpcNotify_registerNonNotifyCallback(IpcNotify_NonNotifyCallback callback)
This is a driver library internal API and is used in certain SOCs by the separate mailbox driver.
- Attention
This API should not be used by end users.
- Parameters:
callback – [in] Callback to call when a interrupt is received from a CPU which is not part of IPC Notify core list
-
struct IpcNotify_Params
- #include <ipc_notify.h>
Parameters used by IpcNotify_init.
Recommend to call IpcNotify_Params_init before setting values to this structure
Note
This structure and call to IpcNotify_init would be generated by SysConfig.
Public Members
-
uint32_t numCores
Number of remote cores participating in IPC, excluding the core on which this API is called.
-
uint32_t coreIdList[CSL_CORE_ID_MAX]
List of Core ID’s participating in IPC, excluding the core on which this API is called.
See CSL_CoreID for valid values for this field.
-
uint16_t selfCoreId
Core ID of the core calling this API
See CSL_CoreID for valid values for this field.
-
uint32_t linuxCoreId
When linux IPC is enabled, this is the core ID of linux
-
uint32_t timeout
-
uint32_t numCores