The Supervisor call interface is a thread-safe method to call into the current application or into an external application using a Supervisor instruction. The Supervisor interface is split between the
caller
that uses a Supervisor instruction and the
handler
that implements a Supervisor exception handler to process the request. Both the caller and the handler must agree on the shared data passed between them through the Supervisor call.
The shorthand for a Supervisor call is SVC.
Supervisor calls
Supervisor calls are calls using the Thumb Supervisor instruction. Calling this instruction from code raises and exception on the device and branches into the SVC vector. During the exception, the following registers are stacked in the exception frame:
R0-R3, R12, R14, LR, PC and xPSP
.
The Supervisor exception passes control to the Supervisor handler inside the
Master Boot Record
, which will forward the Supervisor call to the SVC vector that is configured in the Master Boot Record. These exceptions are forwarded to the main application by default, with the exception of SVC call numbers that are reserved for use in the SoftDevice, which will always be handled inside the SoftDevice SVC vector (see
Reserved SVC numbers
).
When an SVC instruction is called, an exception is raised and the SVC exception handler is executed immediately, unless an exception with the same or higher priority is currently being handled.
SVC instructions will give access to an external functionality without knowing absolute addresses for the functions that are being reached. Supervisor calls are normally limited to functions with 0 to 4 arguments, as R0 - R3 are the only parameters that are stacked in the Supervisor exception frame.
There is no standardized way of calling the SVC instruction in C code, so the implementation depends on the compiler in use.
Indirect Supervisor calls
Indirect Supervisor calls is an extension to the Supervisor call interface, where the stacked R12 register is reserved to provide extra information about the Supervisor call. When R12 is used, it is possible to extend the number of call types beyond the limited range of the SVC number (being 8-bit in size). When indirect Supervisor calls are used, the SVC number is zero by default.
The shorthand for indirect Supervisor calls is SVCI.
Limitations
It is not possible to execute nested SVC instructions, i.e. executing an SVC instruction from inside of an SVC handler function. This is due to the fact that a new SVC instruction raises an exception which will be blocked because it will have the same priority as the ongoing operation. This means that it is impossible for an SVC handler to directly call any SoftDevice APIs.
This limitation can be circumvented by implementing a scheme of using Supervisor calls that return function pointers for the functions that require using the Supervisor call functionality. The main application can then call these functions directly to prevent using a nested Supervisor call.
- Warning
- When the SVC handler is implemented in an external application, the handler function and any functions called by it cannot use static memory unless the external application is compiled to use dedicated and reserved memory that is not in use by the main application.
- When the SVC handler is implemented in an external application, Logger module cannot be used by the handler function or any functions called by it.
- SVC instructions cannot be used in a Reset, NMI, or Hard Fault handler as it can cause a lockup due to the fact that the SVC priority is always lower than these handlers' priority and therefore it is automatically blocked.
-
SVC instructions can only be used in a lower interrupt priority level (higher priority value for the priority level) than the SVC priority. Using the SVC instruction from a context that has the same or higher interrupt priority causes a Hard Fault or a locked state. The interupt priority level for the SVC handler is 4 if there is a
Master Boot Recordpresent on the device.
See
Interrupt model and processor availability
for more information about the exception and interrupt model in case the device uses a
SoftDevice
and/or
Master Boot Record
.
Redirecting Supervisor calls to an external application
The Master Boot Record will forward the SVC exception to the main application by default. It is possible to control where the SVC exceptions are forwarded by setting the vector table base address through the SoftDevice APIs. This can be done by calling sd_softdevice_vector_table_base_set . The same functionality is also available in an alternative API in sd_mbr_command .
- Warning
- When vector table base address is changed, all SoftDevice events will be forwarded to the new address. Due care must be taken to prevent loss of critical events from the SoftDevice when changing the vector table base address.
Calling an external application by changing the vector table base address
// Set the Vector table base address to EXTERNAL_APP_START_ADDR to forward SVC exceptions
// to EXTERNAL_APP_START_ADDR instead of main application.
err_code = sd_softdevice_vector_table_base_set(EXTERNAL_APP_START_ADDR);
VERIFY_SUCCESS(err_code);
// Your Supervisor call
err_code = some_svc_call(param_0, param_1);
VERIFY_SUCCESS(err_code);
// Set the vector table base address back to main application.
err_code = sd_softdevice_vector_table_base_set(APP_START_ADDR);
VERIFY_SUCCESS(err_code);
- Note
- Setting a new vector table base address is only possible from the main application as the API call to do so is a Supervisor call (See Limitations ).
Reserved SVC numbers
The following is a table of reserved SVC numbers:
| Number | Owner | Description |
|---|---|---|
| 0x00 | No owner | Reserved for indirect Supervisor calls. |
| 0x01 - 0x0F | No owner | Available range of Supervisor calls. |
| 0x10 - 0xFF | SoftDevice | Reserved numbers for Supervisor calls to the Nordic Semiconductor BLE stack. |
- Note
- All Supervisor calls with SVC numbers that are not reserved will be forwarded to the main application SVC vector or to a redirected address (see Redirecting Supervisor calls to an external application ).
- Warning
- The reserved ranges for the SoftDevice will always be forwarded to the SoftDevice SVC vector, regardless of user configuration. These should never be used outside of the context of the SoftDevice APIs.
Reserved SVC numbers in the Secure DFU Bootloader
The following is a table of reserved SVC numbers when calling into the Secure DFU bootloader:
| Number | Owner | Description |
|---|---|---|
| 0x00 | Bootloader | Reserved for indirect Supervisor calls. |
| 0x01 - 0x0F | No owner | Not in use. |
| 0x10 -0xFF | SoftDevice | Reserved number for SoftDevice Supervisor calls (Will never reach the bootloader). |
- Note
- Vector table base address must be set to the bootloader start address for Supervisor calls to be redirected to the bootloader (see Redirecting Supervisor calls to an external application ).
- Warning
- The reserved ranges for the SoftDevice will always be forwarded to the SoftDevice SVC vector, regardless of user configuration. These should never be used outside of the context of the SoftDevice APIs.
Supervisor call interface
The Supervisor call interface can be implemented in the main or external app by using the macros
SVCALL
or
SVCI
to declare a function that will use an SVC instruction instead of a direct function call.
Declaring an SVC function
// Reserve an SVC number for the API.
#define SVC_NUM (3)
// This declares an SVC function with the following signature:
// uint32_t svc_func(int a, char b);
SVCALL(SVC_NUM, uint32_t, svc_func, int a, char b);
// Calling this function from code
uint32_t ret_val;
ret_val = svc_func(32, 2);
Declaring an indirect SVC function
// Reserve an indirect SVC number for the API.
#define SVCI_NUM (0x3000)
// This declares an SVC function with the following signature:
// uint32_t svc_func(int a, char b);
SVCI(SVCI_NUM, uint32_t, svc_func, int a, char b);
// Calling this function from code
uint32_t ret_val;
ret_val = svc_func(32, 2);
Supervisor call handler interface
The Supervisor call handler interface can be implemented in the main or external application by adding the file
nrf_svc_handler.c
to the project and compiling it. This will allow for handling of the SVC exception in your application.
To handle SVC instructions, you must perform registration using Experimental: Section variables . This can be achieved by using the convenience macros NRF_SVC_FUNCTION_REGISTER or NRF_SVCI_FUNCTION_REGISTER . The macro NRF_SVCI_FUNCTION_REGISTER will use SVC number 0 and and an indirect number stored in R12.
Registering an SVC function in the SVC handler
Example registation of an SVC function in the SVC handler:
// In header file:
// Example function for Supervisor call.
typedef uint32_t (*svc_func_t)(int a, char b);
#define SVC_NUMBER (3)
// In source file:
//
// svc_func_inst is the name of the nrf_section variable in code. It must be unique
// in the code unit, but it will not be accessed directly.
NRF_SVC_FUNCTION_REGISTER(SVC_NUMBER, svc_func_inst, svc_func_t);
Example registration
// In header file:
//
// Example function for an indirect Supervisor call.
typedef uint32_t (*svc_func_t)(int a, char b);
#define SVCI_NUMBER (0x4000)
// In source file:
//
// svc_func_inst is the name of the nrf_section variable in code. It must be unique
// in the code unit, but it will not be accessed directly.
NRF_SVCI_FUNCTION_REGISTER(SVCI_NUMBER, svc_func_inst, svc_func_t);
- Note
- Both the SVC caller and handler must know the signature of the function and the corresponding SVC or SVCI number. This can be done by declaring them in a shared header file.
- Warning
- The macros to register a Supervisor caller or an indirect Supervisor caller must be invoked from a source file (and not a header). This is a limitation in Experimental: Section variables . Invoking NRF_SVC_FUNCTION_REGISTER or NRF_SVCI_FUNCTION_REGISTER from a header file will lead to compiler errors.
Asynchronous Supervisor interface
The asynchronous Supervisor interface is meant to facilitate asynchronous calls into an external application that require memory management and/or rely on asynchronous operations to complete. The memory for the exchanged parameter and the ongoing state is held in the main application. System events required to complete the asynchronous operation must be forwarded through the interface.
- Note
-
By default, the asynchronous Supervisor interface uses an
indirect Supervisor callwhen initializing the interface. After successful initialization, all calls through the interface happen by directly calling function pointers. This is done to prevent using nested Supervisor calls (see Limitations ).
Limitations
The asynchronous Supervisor interface is limited to forwarding one parameter of data, which can be a structure type when multiple parameters are required for the call. The lifetime of the data and the state must be kept for the entirety of the asynchronous operation.
The asynchronous Supervisor interface is limited to forwarding system events in case of ongoing progress information. If custom event types are required for progressing the operation, add them to the parameter type for the SVCI interface, and call the request mechanism multiple times to progress the operation.
The asynchronous Supervisor interface does not come with predefined ways of handling multiple instances. If multiple instances are required for the asynchronous Supervisor interface calls, instance information must be added to the parameter exchanged and the state type.
For other limitations, see Limitations .
Shared data
The
caller
and
handler
must know the parameter type and the type to hold the state through a shared header file.
- Warning
- The Asynchronous SVC handler has no method of detecting changes in either the parameter type or state type. If the parameter or state type change, use a different indirect Supervisor call number to keep API compatibility.
Asynchronous Supervisor interface caller
Registering a caller requires a declaration of the shared data in accordance with Shared data .
To register the interface for a caller, run the macro NRF_SVCI_ASYNC_FUNC_DEFINE inside a source file.
The parameters for this macro are as follows:
| Name | Description |
|---|---|
| svci_num | Supervisor indirect number (SVC number will be zero). |
| name | Name of the interface. This is used as a base for all convenience functions. |
| param_type | User-defined parameter type to exchange through the asynchronous Supervisor interface. |
Registration of a caller
Example of asynchronous Supervisor caller registration:
#define SVCI_NUM (5)
// This macro invocation created an asynchronous Supervisor caller interface.
// The SVCI_NUM, function name, and parameter are used as input
NRF_SVCI_ASYNC_FUNC_DEFINE(SVCI_NUM, my_func, my_func_data_t);
Asynchronous Supervisor interface handler
Registering a handler requires a declaration of the shared data in accordance with Shared data .
To register the interface for a handler, run the macro NRF_SVCI_ASYNC_HANDLER_CREATE and implement the required handler functions.
The parameters for this macro are as follows:
| Name | Description |
|---|---|
| svci_num | Supervisor indirect number (SVC number will be zero). |
| name | Name of the interface. This is used as a base for all required handler functions. |
| param_type | Type of parameter to exchange through the interface. |
| state_type | Type of the ongoing progress state when calling the interface. |
Registration of a handler
Example implementation of asynchronous Supervisor interface handler functions, given that the function interface is
my_func
, the parameter is
my_func_data_t
and the state is
my_func_state_t:
#define SVCI_NUM (0x1000)
NRF_SVCI_ASYNC_HANDLER_CREATE(SVCI_NUM, my_func, my_func_data_t, my_func_state_t);
// The handler function for the Supervisor call.
// This call initializes the async interface and resets the state.
static uint32_t my_func_handler(my_func_data_svci_async_t * p_async)
{
VERIFY_PARAM_NOT_NULL(p_async);
p_async->async_func = my_func_on_call;
p_async->sys_evt_handler = my_func_on_sys_evt;
p_async->state = SOME_USER_DEFINED_INITIALIZED_STATE;
return NRF_SUCCESS;
}
// Function called when the user calls through the asynchronous Supervisor interface.
static uint32_t my_func_on_call(my_func_data_t * p_data, my_func_state_t * p_state)
{
// Return value can be customized to user-specific needs
uint32_t ret_val = NRF_ERROR_BUSY;
VERIFY_PARAM_NOT_NULL(p_state);
// State can be customized to user specific needs
(*p_state) = SOME_USER_DEFINED_ONGOING_STATE;
// Call some asynchronous functionality in the handler
ret_val = some_async_call(p_data);
return ret_val;
}
// Function called when sys events are forwarded to the asynchronous Supervisor interface.
static uint32_t my_func_on_sys_evt(uint32_t sys_evt, my_func_state_t * p_state)
{
uint32_t ret_val = NRF_ERROR_BUSY;
VERIFY_PARAM_NOT_NULL(p_state)
if (*p_state == SOME_USER_DEFINED_ONGOING_STATE)
{
// Handle the sys-event.
}
return ret_val;
}
Convenience functions
There are some static convenience functions that get created when running
NRF_SVCI_ASYNC_FUNC_DEFINE
. These can be used when calling the asynchronous Supervisor interface from the main application. Given that the defined function is named
my_func
, then the available convenience functions are as follows:
| Name | Parameter | Description |
|---|---|---|
| my_func_init | N/A | Function to initialize the asynchronous interface. Intended to be run once. |
| my_fync | User defined | Function to request asynchronous operation. A user-defined type is exchanged in the call. |
| my_func_on_sys_evt | System event | Function to forward system events required for completing the asynchronous operation. |
| my_func_is_initialized | N/A | Function to check if the asynchronous interface is initialized. |
Convenience functions example
// There must be a declaration in a header file for the
// asynchronous Supervisor interface.
//
// The example declaration contains the following:
// my_func - The function to be called
// my_func_data_t - The type of data to be exchanged
// my_func_state_t - The state to be held between calls.
// NRF_SVCI_ASYNC_FUNC_DECLARE(SVCI_NUM, my_func, my_func_data_t, my_func_state_t);
//
// There must be a definition in a source file where the convenience
// function is to be called.
//
// The example registration for these convenience functions is as follows:
// NRF_SVCI_ASYNC_FUNC_DEFINE(SVCI_NUM, my_func, my_func_state_t);
uint32_t err_code;
my_func_data_t my_data;
my_func_state_t my_state;
// Convenience function to initialize the interface
err_code = my_func_init();
VERIFY_SUCCESS(err_code);
// Convenience function to check if the interface is already initialized.
if (my_func_is_initialized())
{
//Interface was initialized.
}
// Convenience function to call the interface
err_code = my_func(my_data);
VERIFY_SUCCESS(err_code);
// Convenience function to forward system events
err_code = my_func_on_sys_evt(sys_evt);
if (err_code == NRF_ERROR_BUSY)
{
// Operation is still ongoing
}
{
// Operation finished successfully
}
else
{
// Any other error reported by the async interface
}