Common Token Manager (CTM)#
Note: Refer to this information if you are developing with SiSDK V.2025.6 or above.
The earlier version of the Token Manager software module included multiple sets of APIs, including HAL APIs, to handle read and write operations for manufacturing and dynamic tokens. However, managing so many interfaces made token access complex. To address this, the CTM software module was introduced with a simplified design, offering simple APIs to manage all token types. This streamlined approach not only reduces complexity but also ensures consistent functionality across supported platforms, including Series 2 and Series 3 devices.
For a general overview of tokens and their purpose, see About Tokens.
Token Format#
CTM supports both static and dynamic tokens. Static tokens are further categorized into static device tokens and static secure tokens. Static device tokens are stored in secure memory regions with limited capacity and are protected against mass erase. Static secure tokens, on the other hand, are stored in a dedicated flash region, with an 8 KB area reserved for storing larger static secure tokens. This size is configurable on Series 3 devices only; refer to Configuring Static Secure Token Storage Size for details on adjusting the default 8 KB allocation.
The dynamic tokens support only reading and writing of basic tokens (data or counter objects).
The CTM uses a 32-bit identifier for each type of token and includes token specific information. To distinguish between different token types, the upper 5 bits of the key identifier are used, as illustrated below.
Bit (31:27) | Maps to |
---|---|
0b00000 | NVM3 (Default instance) |
0b00001 | NVM3 (Secondary instance - Zwave usage) |
0b110xx | Static device tokens |
0b111xx | Static secure tokens |
Below is the representation of a 32-bit token value for static and dynamic tokens.
The token can be created with the help of the MACRO’s as shown below. Note that the example below assumes a user defined dynamic token. For a different stack, the NVM3 domain would be different. Refer to NVM3 Default Instance Key Space for details on domain and its corresponding NVM3 key space region.
// To create a token, the CTM provides helper macros such as
SL_TOKEN_GET_STATIC_DEVICE_TOKEN(token) //To create a 32-bit static device token.
SL_TOKEN_GET_STATIC_SECURE_TOKEN(token) //To create a 32-bit static secure token.
SL_TOKEN_GET_DYNAMIC_TOKEN(token, IsCounterObj) //To create dynamic token, in case of counter object 'IsCounterObj' should be set to '1'.
/**
* Example usage
*/
/* Creating dynamic tokens */
// Basic token
#define TOKEN_DYNAMIC_USER_DATA SL_TOKEN_GET_DYNAMIC_TOKEN((NVM3KEY_DOMAIN_USER | 0x000A), 0)
// Counter token
#define TOKEN_DYNAMIC_COUNTER SL_TOKEN_GET_DYNAMIC_TOKEN((NVM3KEY_DOMAIN_USER | 0x000C), 1)
/* Creating static token */
// Static device token
#define STATIC_DEVICE_TOKEN_EXAMPLE (SL_TOKEN_STATIC_DEVICE_TOKENS | 0x600)
#define STATIC_DEVICE_TOKEN_EXAMPLE_SIZE 4 //4 bytes
#define TOKEN_STATIC_DEVICE_DATA SL_TOKEN_GET_STATIC_DEVICE_TOKEN(STATIC_DEVICE_TOKEN_EXAMPLE)
// Static secure token
#define STATIC_SECURE_TOKEN_EXAMPLE (SL_TOKEN_STATIC_SECURE_DATA_TOKENS | 0x610)
#define STATIC_SECURE_TOKEN_EXAMPLE_SIZE 2 //2 bytes
#define TOKEN_STATIC_SECURE_DATA SL_TOKEN_GET_STATIC_SECURE_TOKEN(STATIC_SECURE_TOKEN_EXAMPLE)
Note: When using the CTM APIs, the token must be a 32-bit identifier created with the macros mentioned above.
Token Attributes#
Token Type | Mass Erase Protected | Security Feature (Internal Flash) | Security Feature (External Flash) | Rewritable (Series 2) | Rewritable (Series 3) |
---|---|---|---|---|---|
Static device token | Yes | NA | NA | No | Yes* |
Static secure token | No | NA | Yes | No | No |
Dynamic token | No | NA | Yes | Yes | Yes |
Note: Static device tokens have limited endurance, with a maximum of 100 write/erase cycles. Once this limit is reached, the tokens in the static device space can no longer be updated or written.
Token APIs#
The diagram shows, at high level, the difference in the legacy APIs and the CTM APIs.
Refer to Common Token Manager Documentation for information on API’s supported by CTM.
Token Security#
On devices that use external flash, tokens are handled with varying levels of security depending on their type. By default, dynamic tokens are stored using NVM3, which ensures secure storage. Static device tokens, on the other hand, are placed in a protected memory region. However, they do not employ additional encryption. In contrast to static device tokens, static secure tokens follow the same security model as NVM3 tokens and are protected using AES-GCM 128-bit authenticated encryption.
Token Storage#
On Series 3 devices, static tokens are stored using a structured format that organizes token data in a consistent and manageable way. This format supports sequential storage of multiple tokens, enabling efficient access.
Static device tokens are compact in size and cannot accommodate large data objects. In contrast, static secure tokens come with a significantly larger memory allocation—typically 8 KB by default—making them suitable for storing larger data objects. While static device tokens are protected against mass erase operations, static secure tokens are erased when a device-wide mass erase is performed.
It's important to understand that the entire reserved space is not fully available for storing raw token data. Structural overheads—such as headers and formatting information—applies to both types of static tokens: static device tokens and static secure tokens. However, only static secure tokens include additional security overhead, such as encryption and authentication metadata. As a result, the effective space available for actual token content is reduced, particularly for static secure tokens.
Note:
Static secure tokens are immutable; updates to token data must be made by creating a new token.
For Series 2 devices, token storage continues to follow the existing supported method with no changes. The explanations provided above regarding token storage behavior and overhead apply specifically to Series 3 devices.
Configuring Static Secure Token Storage Size#
It is possible to adjust the default size allocated for static secure token storage on Series 3 devices by specifying the static_secure_tokens_storage_size
template contribution in your .slcp
file. By default, the size is 8 KB. In the example below, it has been reconfigured to 4 KB.
template_contribution:
- name: static_secure_tokens_storage_size
value: 4096
Note: Change in the default static secure configuration size would result in change in NVM3 start and end address.
Token Override#
With CTM, static tokens can be overridden and stored in custom storage space like NVM3. The NVM3 subregions 0x8E000 and 0x8F000 are reserved for static override tokens. Refer to NVM3 Default Instance Key Space for details. The NVM3 override mechanism has been introduced for Series 2 and Series 3 devices to address the following needs:
Allowing the use of NVM3-stored tokens as an alternative to actual static data during development, particularly in cases where static storage is not defined.
Enabling override of factory-programmed values with custom tokens stored in NVM3.
It provides a flexible solution by allowing applications to read and write to the NVM3 copy of the tokens, rather than relying solely on the manufacturing data. This ensures the integrity of factory-set data while enabling customization when needed.
To enable NVM3 override functionality, follow one of these options:
Option 1: In Simplicity Studio, navigate to the Software Components tab, then go to Services > Token Manager > Common Token Manager Core. Click "Configure" and select the radio button labeled
Enable Override Tokens
to activate this feature.Option 2: If you are using "slc generate", set the
SL_TOKEN_MANAGER_ENABLE_OVERRIDE_TOKENS
flag to1
in your project'ssl_token_manager_config.h
configuration file.
By enabling this feature, the default token retrieval mechanisms can be overridden by custom implementations.
The search order for tokens read request (when the override flag is enabled) is as follows:
By default search a static token in NVM3, when NVM3 override config flag is active, if found return the token data read from NVM3 region.
If the token is not found in NVM3, search in static memory region.
Once a token is overridden, it will always be read from NVM3. The original factory value will only be used if the override token is explicitly deleted from NVM3 or if NVM3 is erased. In these cases, token access will fall back to the factory-programmed value.
The override token key is derived from the existing token, as shown below.
The static override token can be created with the help of the following macros.
// Create an override token using below MACRO
SL_TOKEN_GET_DYNAMIC_OVER_RIDE_TOKEN(token)
/** Illustration using existing static device token **/
// Writing an override token
status = sl_token_manager_set_data(SL_TOKEN_GET_DYNAMIC_OVER_RIDE_TOKEN(TOKEN_MFG_CTUNE), &write_ctune_data, TOKEN_MFG_CTUNE_SIZE);
// Reading an override token
status = sl_token_manager_get_data(SL_TOKEN_GET_STATIC_DEVICE_TOKEN(TOKEN_MFG_CTUNE), &read_ctune_data, TOKEN_MFG_CTUNE_SIZE);
/** Illustration using existing static secure token **/
// Writing an override token
status = sl_token_manager_set_data(SL_TOKEN_GET_DYNAMIC_OVER_RIDE_TOKEN(TOKEN_MFG_INSTALLATION_CODE), &write_installation_code, TOKEN_MFG_INSTALLATION_CODE_SIZE);
// Reading an override token
status = sl_token_manager_get_data(SL_TOKEN_GET_STATIC_SECURE_TOKEN(TOKEN_MFG_INSTALLATION_CODE), &read_installation_code, TOKEN_MFG_INSTALLATION_CODE_SIZE);
Note:
Since the NVM3 override tokens are stored in NVM3 region, these tokens are lost when the device mass erase is triggered.
Enabling the override flag may introduce additional latency compared to direct reads from the static region, particularly in time-critical scenarios such as device boot-up where rapid token access is essential. In addition, since override token data is stored in NVM3, it consumes NVM3 memory. Therefore, we recommend increasing the NVM3 memory allocation when the override feature is enabled.
If a fallback to manufacturing data is required, the user must delete the corresponding override token to regain access to the original secure or device region values. For details on how to delete a dynamic token, refer to the API usage section delete dynamic token.
The Common Token Manager (CTM) serves as the foundation for unified token management across Silicon Labs devices. Before exploring component setup and usage, it's important to understand how CTM streamlines token handling, improves compatibility, and supports migration from legacy APIs. The following sections will guide you through integrating CTM into your project, ensuring a smooth transition and optimal performance on Series 2 and Series 3 platforms.
Common Token Manager Component#
To use common token manager on series 2 or series 3 devices, the common_token_manager component (common_token_manager.slcc
) should be added to the project. By default, both dynamic and static token storage are supported. If an app only needs static token support—such as for manufacturing tokens—it can include the common_token_manager_static
component without adding dynamic token support.
Coexistence#
Coexistence refers to the scenario where both CTM (new APIs) and legacy TM (old APIs) are present within the same binary. Until the legacy TM is fully deprecated, there may be cases where some stacks have migrated to CTM, while S2 customers continue to use the legacy HAL APIs for reading and writing tokens. In such cases, it is essential that both CTM and legacy TM produce consistent results when their respective read/write APIs are invoked, ensuring compatibility and functional parity across both software modules.
Note: Coexistence is supported exclusively on Series 2 devices. For Series 3 devices, CTM must be used.
Migration Guide#
This section outlines the steps for migrating from the token_manager component to the common_token_manager component. This migration was driven by several key factors:
The legacy Token Manager and HAL APIs are being deprecated.
CTM offers a unified, simplified, and platform-consistent API for managing all token types.
CTM supports override tokens. Refer Override tokens for details.
Series 3 devices support only CTM—legacy APIs will not work.
Key Differences Between token_manager and common_token_manager#
Feature | token_manager | common_token_manager |
---|---|---|
API Style | Multiple APIs (HAL, Token Manager) | Unified CTM API |
Token Format | 16/20-bit keys | 32-bit token identifiers |
Indexed Token Support | Yes | No (must be managed manually) |
Platform Support | Series 1 & 2 | Series 2 & 3 |
Override Tokens | NA | Supported via NVM3 |
Migration Process#
Step 1: Update Project Configuration
If you use Simplicity Studio, update your .slcp file to replace the token_manager component with new a common_token_manager
component. If you use the slc cli, update your .slcp file to replace the token_manager component with a new common_token_manager
component and re-generate your project.
Step 2: Replace Legacy API Calls
Operation | Legacy API(s) | CTM API(s) |
---|---|---|
Init | halStackInitTokens(), sl_token_init() | No manual init needed; CTM auto-initializes |
Read Token | halCommonGetToken(&data, TOKEN_X), sl_token_get_data() | sl_token_manager_get_data(TOKEN_X, &data, sizeof(data)) |
Write Token | halCommonSetToken(TOKEN_X, &data), sl_token_set_data() | sl_token_manager_set_data(TOKEN_X, &data, sizeof(data)) |
Increment Counter | halCommonIncrementCounterToken(TOKEN_X), sl_token_increment_counter() | sl_token_manager_increment_counter(TOKEN_X) |
Partial Read | N/A | sl_token_manager_get_partial_data() |
Delete Token | N/A | sl_token_manager_delete_dynamic_token() |
Get Size | N/A | sl_token_manager_get_size() |
Refer to Common Token Manager Documentation for details on supported CTM APIs.
Step 3: Update Token Definitions
The legacy macros like DEFINE_BASIC_TOKEN
and TOKEN_DEF
, used for dynamic tokens, and TOKEN_MFG
and TOKEN_NEXT_ADDRESS
, used for static tokens, are no longer supported. Instead of using these, CTM provides macro to define a 32-bit token for both static and dynamic token. Refer to Token Format for details on CTM provided macros. Below is an example use of CTM macro to create a token.
/****Illustration of dynamic token creation ****/
// Create basic token
#define TOKEN_USER_DATA1 SL_TOKEN_GET_DYNAMIC_TOKEN((SL_TOKEN_NVM3_REGION_USER | 0x000A), 0)
// Create counter token
#define TOKEN_USER_COUNTER_DATA1 SL_TOKEN_GET_DYNAMIC_TOKEN((SL_TOKEN_NVM3_REGION_USER | 0x0010), 1)
// Create indexed token (assuming the token has 5 indices)
// Here ‘TOKEN_USER_INDEX_ARRAY_DATA1’ is base token while writing/reading.
#define TOKEN_USER_INDEX_ARRAY_DATA1 SL_TOKEN_GET_DYNAMIC_TOKEN((SL_TOKEN_NVM3_REGION_USER | 0x0100), 0)
/****Illustration of static token creation ****/
// Create static device token
#define STATIC_DEVICE_USER_DATA 0x1204 //This value should be always 16-bit
#define STATIC_DEVICE_USER_DATA_SIZE 4 // 4bytes long
#define TOKEN_STATIC_DEVICE_USER_DATA SL_TOKEN_GET_STATIC_DEVICE_TOKEN(TOKEN_STATIC_DEVICE_USER_DATA)
// Create static secure token
#define STATIC_SECURE_USER_DATA 0x2400 //This value should be always 16-bit
#define STATIC_SECURE_USER_DATA_SIZE 4 // 4bytes long
#define TOKEN_STATIC_SECURE_USER_DATA SL_TOKEN_GET_STATIC_SECURE_TOKEN(TOKEN_STATIC_SECURE_USER_DATA)
Step 4: Storing and Accessing a Token
The example below illustrates how to write and read a token using CTM APIs, utilizing the tokens created in Step 3.
// The tokens created in the step 3 are used for read/write operation in below example
/****Example of Writing and Reading a Dynamic Token****/
/** Basic token operation **/
// Write a basic token
uint8_t data = 0x1F;
sl_status_t status;
status = sl_token_manager_set_data(TOKEN_USER_DATA1 , &data, sizeof(data));
// Read a basic token
status = sl_token_manager_get_data(TOKEN_USER_DATA1 , &data[0], sizeof(data));
/** Counter token operation **/
// Write a counter token
uint32_t data = 1;
sl_status_t status;
status = sl_token_manager_set_data(TOKEN_USER_COUNTER_DATA1, &data, sizeof(data));
// Increment counter value
Status = sl_token_manager_increment_counter(TOKEN_USER_COUNTER_DATA1);
// Read a counter token
status = sl_token_manager_get_data(TOKEN_USER_COUNTER_DATA1, &data, sizeof(data));
/** Index token operation **/
// Here ‘TOKEN_USER_INDEX_ARRAY_DATA1’ is base token while reading. Make sure that the range of tokens that are reserved for index tokens are unique
uint16_t data[5] = {1, 2, 3, 4, 5};
sl_status_t status;
// Write indexed tokens
for(i = 0; i < 5; i++) {
status = sl_token_manager_set_data(TOKEN_USER_INDEX_ARRAY_DATA1 + index, &data[index], sizeof(data));
}
// Read an index token
// Here ‘index’ can be 0 to 4
status = sl_token_manager_get_data(TOKEN_USER_INDEX_ARRAY_DATA1 + index, &data, sizeof(data));
/****Example of Writing and Reading a Static token****/
/** Static device token operation **/
uint32_t data = 0x12345678;
sl_status_t status;
// Write static device token
status = sl_token_manager_set_data(TOKEN_STATIC_DEVICE_USER_DATA, &data, STATIC_DEVICE_USER_DATA_SIZE);
// Read static device token
status = sl_token_manager_get_data(TOKEN_STATIC_DEVICE_USER_DATA, &data, STATIC_DEVICE_USER_DATA_SIZE);
/** Static secure token operation **/
uint32_t data = 0x1234;
sl_status_t status;
// Write static secure token
status = sl_token_manager_set_data(TOKEN_STATIC_SECURE_USER_DATA, &data, STATIC_SECURE_USER_DATA_SIZE);
// Read static secure token
status = sl_token_manager_get_data(TOKEN_STATIC_SECURE_USER_DATA, &data, STATIC_SECURE_USER_DATA_SIZE);