The whole truth about the RTOS. Article # 14. Memory sections: introduction and basic services
The memory sections were mentioned earlier in one of the previous articles (# 6), where a comparison was made with the standard C function of the malloc () language . A partition is a region of memory derived from the partition pool (memory pool). Memory sharing provides a flexible way to reliably and deterministically allocate and free memory.
Previous articles in the series:
Article # 13. Task data structures and unsupported API calls
Article # 12. Task Services
Article # 11. Tasks: configuration and introduction to the API
Article # 10. Scheduler: Additional Features and Saving Context
Article # 9. Scheduler: implementation
Article # 8. Nucleus SE: Internal Design and Deployment
Article # 7. Nucleus SE: Introduction
Article # 6. Other RTOS services
Article # 5. Interaction between tasks and synchronization
Article # 4. Tasks, Context Switching and Interrupts
Article # 3. Tasks and planning
Article # 2. RTOS: Structure and Real Time
Article # 1. RTOS: introduction.
In the Nucleus SE, partition pools are configured during creation. Up to 16 partition pools can be in one application. If they are not configured, data structures and service calls related to these pools will not be included in the application.
A partition pool is a memory area divided into a fixed number of fixed-size blocks. The developer fully controls the size and number of partitions in each pool. Tasks can request allocated sections of memory and receive a pointer to the storage area and should not record data outside the allocated section. A section can be freed by any task when passing an API function pointer. A request to allocate a partition when there are no free partitions may result in an error or suspension of the request, depending on the selected API call parameters and the configuration of the Nucleus SE.
Configuring memory sections
Number of partition pools
As with most Nucleus SE objects, partition pool configuration is mainly done using the #define directive in nuse_config.h . The main parameter is NUSE_PARTITION_POOL_NUMBER , which determines how many partition pools are defined in the application. The default value is 0 (i.e. partition pools are not used), the developer can set any value from 0 to 16. A different value will result in a compilation error that was detected during the check in nuse_config_check.h (it is included in nuse_config.c , and therefore compiles with this module), which results in the compilation of the #error directive .
The choice of a non-zero value is a priority way to activate partition pools. This leads to the definition of data structures and the assignment of the appropriate size. The data structures in the ROM must be initialized with appropriate values describing each partition pool. More details about the data structures will be in the next article. This selection also activates the API settings.
Activate API calls
Each API function (service call) in the Nucleus SE is activated by the #define directive in nuse_config.h . For partition pools, these include:
By default, they are all set to FALSE , thus disabling every service call and preventing the implementation code from being included. To configure partition pools in the application, you need to select the necessary API calls and set the corresponding directives to TRUE .
The following is an excerpt from the default nuse_config.h file :
If the partition pool API function is activated, but the pools are not configured, a compilation error occurs (except for NUSE_Partition_Pool_Count () , which is always allowed). If your code uses an API call that has not been activated, a build error will occur because the implementation code was not included in the application.
Partition Pool Utility Calls
Nucleus RTOS supports seven service calls related to partition pools, which provide the following functionality:
|Description of the functional||Nucleus RTOS||Nucleus SE|
|Section selection||NU_Allocate_Partition ()||NUSE_Partition_Allocate ()|
|Release section||NU_Deallocate_Partition ()||NUSE_Partition_Deallocate ()|
|Providing information |
about a specific partition pool
|NU_Partition_Pool_Information ()||NUSE_Partition_Pool_Information ()|
|Returning the value of the number (for the moment) of configured |
partition pools in the application
|NU_Established_Partition_Pools ()||NUSE_Partition_Pool_Count ()|
|Add (create) new partition pool to application||NU_Create_Partition_Pool ()||Not implemented.|
|Change (delete) partition pool from application||NU_Delete_Partition_Pool ()||Not implemented.|
|Return pointers to all partition pools that currently exist in the application.||NU_Partition_Pool_Pointers ()||Not implemented.|
The implementation of each call will be discussed in detail.
It is worth noting that neither the Nucleus RTOS nor the Nucleus SE has a reload function. This is intentional. Very often, one task allocates a section and passes a pointer to another task (which may later release it). If you reboot the partition pool, all partitions will be marked as unused; however, there is no mechanism for tracking and notifying all tasks that the partitions can use.
Partition allocation and release services
Fundamental partition pooling operations — allocating partitions in a pool (that is, marking a partition as used and returning its address) and freeing a partition (that is, marking a partition as unused). Nucleus RTOS and Nucleus SE provide two basic API calls for these operations, described below.
Calling the Nucleus RTOS API to allocate a partition is very flexible, which allows developers to suspend tasks for an indefinite period of time or without a timeout if the operation cannot be completed immediately, for example, when you try to allocate a partition from a pool in which all partitions are already distributed. Nucleus SE provides the same service, only the suspension of tasks in it is optional, and the timeout is not implemented.
Calling the Nucleus RTOS API to highlight a partition
STATUS NU_Allocate_Partition (NU_PARTITION_POOL * pool, VOID ** return_pointer, UNSIGNED suspend);
NU_SUCCESS - the call was successfully completed;
NU_NO_PARTITION - no sections available;
NU_INVALID_POOL - invalid partition pool pointer;
NU_INVALID_POINTER - passed null pointer to the returned data ( NULL );
NU_INVALID_SUSPEND — An attempt to suspend a task was made from a thread not associated with a task;
NU_TIMEOUT - no partitions are available, even after pausing for a specified waiting period;
NU_POOL_DELETED - the partition pool was deleted when the task was suspended.
Calling the Nucleus SE API to allocate a section
This API call supports the core Nucleus RTOS API.
STATUS NUSE_Partition_Allocate (NUSE_PARTITION_POOL pool, ADDR * return_pointer, U8 suspend);
pool - the index (ID) of the partition pool used;
return_pointer is a pointer to a variable of type ADDR that accepts the address of the section to be allocated;
suspend - the parameter to pause the task; it can be NUSE_NO_SUSPEND or NUSE_SUSPEND .
NUSE_SUCCESS - the call was successfully completed;
NUSE_NO_PARTITION - no partitions available;
NUSE_INVALID_POOL- incorrect partition pool index;
NUSE_INVALID_POINTER - passed null pointer to the returned data ( NULL );
NUSE_INVALID_SUSPEND —The attempt to suspend a task was made from a thread not associated with the task or when the blocking API functions are disabled.
Implementing partitioning in the Nucleus SE
The code of the API function NUSE_Partition_Allocate is selected using conditional compilation after checking the parameters, depending on whether the blocking API call is activated (task paused) or not. Below we will separately consider these two options.
If blocking calls are deactivated, the API call is quite simple:
First, the availability of free partitions is checked. If there are no such partitions, an error is returned ( NUSE_NO_PARTITION). Then there is a search of partitions, during which the first bytes are checked for the presence of zero values (which means that the section is not used). When such a partition is found, it is assigned a “used” flag, including the index of the partition pool (see “Freeing the partition” below), and a pointer to the next byte (the beginning of the real data area) is returned. An explanation of the data structures of the section pools will be presented in the next article in the section “Data Structures”.
If a lock is activated, the code for this API call becomes a bit more complicated:
The code is enclosed in a do ... while loop , which continues to work while the suspend parameter is set to NUSE_SUSPEND .
If no partitions are available and the pause parameter isNUSE_NO_SUSPEND , the API call terminates and returns the value NUSE_NO_PARTITION . If the suspend parameter was set to NUSE_SUSPEND , the task is suspended. When returned (for example, when the task is resumed), the return value of NUSE_SUCCESS indicates that the task was resumed because the section of memory was released and the code returns to the beginning of the cycle. Since there are no API functions for reloading partition pools, tasks cannot be resumed for other reasons, but for the stability of blocking other types of objects, the NUSE_Task_Blocking_Return  checking process is preserved.
Releasing a partition in the Nucleus RTOS and Nucleus SE makes it available again. Before release, no check is made whether this section is used by any task or not; the application programmer is responsible for this. To free a partition, only a pointer to the data area is required.
Call the Nucleus RTOS API to release the partition
STATUS NU_Deallocate_Partition (VOID * partition);
partition - a pointer to the data area (returned by the NU_Allocate_Partition () function ) of the partition being freed;
NU_SUCCESS - the call was successfully completed;
NU_INVALID_POINTER is a null section pointer ( NULL ), or does not indicate a valid used section.
Calling the Nucleus SE API to free a partition
This API call supports the core Nucleus RTOS API.
STATUS NUSE_Partition_Deallocate (ADDR partition);
partition - a pointer to the data area (as returned by the NUSE_Partition_Allocate () function ) of the partition being freed.
NUSE_SUCCESS - the call was completed successfully;
NUSE_INVALID_POINTER is a null section pointer ( NULL ), or does not indicate a valid used section
Instead of implementing using the blocking and non-blocking functions of the API, the NUSE_Partition_Deallocate () function simply contains a conditionally compiled section that is responsible for unlocking tasks. This code implements the partition itself:
First, the partition index is retrieved from its state byte. Then, the partition status changes to “unused”, the counter of the used partitions decreases, and the function reports the successful completion of the operation.
If locking is activated, the following code is used to resume tasks that are waiting for the available partition pool:
If the tasks were blocked while allocating partitions in this pool, the first table is resumed.
The next article will discuss additional API calls related to memory sections, as well as data structures associated with them.
About the author: Colin Walls has been working in the electronics industry for more than thirty years, spending a significant amount of time on embedded software. He is now an embedded software engineer in Mentor Embedded (a division of Mentor Graphics). Colin Walls often speaks at conferences and seminars, author of numerous technical articles and two books on embedded software. Lives in the UK.