Initial commit of firmware

This commit is contained in:
2025-04-12 13:30:57 +01:00
commit 264a3462e0
374 changed files with 332649 additions and 0 deletions

View File

@@ -0,0 +1,971 @@
/***************************************************************************//**
* @file
* @brief Memory Manager Driver API definition.
*******************************************************************************
* # License
* <b>Copyright 2024 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#ifndef SL_MEMORY_MANAGER_H_
#define SL_MEMORY_MANAGER_H_
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "sl_status.h"
#include "sl_core.h"
#include "sl_memory_manager_region.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************//**
* @addtogroup memory_manager Memory Manager
*
* @details
* ## Overview
*
* The Memory Manager is a platform-level software module that provides different
* ways to perform runtime allocations, either one shot or dynamic.
* The Memory Manager complements the toolchain linker by managing the RAM memory
* not allocated/partitioned by the linker. It offers different constructs that
* will help the different Silicon Labs SDK software modules and your application
* to build, as much as possible, an efficient and optimized RAM layout. The main
* Memory Manager constructs will be:
* - A dynamic allocation API
* - A memory pool API
* - A dynamic reservation API
*
* The Memory Manager can be used in an RTOS context as it is thread-safe by
* protecting adequately its internal shared resources.
*
* ## Initialization
*
* The initialization part includes the following configuration files:
* - \b sl_memory_manager_region_config.h
* - \b sl_memory_manager_config.h
*
* These header files offer a few configurations for the Memory Manager.
* They use the CMSIS Configuration Wizard Annotations that can be rendered
* by Simplicity Studio to set graphically the configuration settings value.
*
* The API function sl_memory_init() is used to initialize the Memory Manager
* module. This function must be called early during your initialization sequence.
* If the SL System component (@ref system) is used by your application, the
* sl_memory_init() call will be added automatically to your initialization
* sequence.
*
* \a sl_memory_manager_region_config.h allows to configure the stack size for
* the application. The default value of 4096 bytes for SL_STACK_SIZE will
* be used by the linker to allocate a stack zone in the RAM. In a baremetal
* application, the stack size is bound to the value set by SL_STACK_SIZE.
* So you should carefully size the stack in that case.
* In an RTOS application, the stack size SL_STACK_SIZE will serve mainly for the
* code running in the main() context until the kernel is launched. Once the kernel is
* started, the different tasks' stacks, created upon tasks' creation, will allow
* to save the different contexts (that is task, function, ISR contexts). The main
* stack will be less active while the application's tasks are running.
*
* @note It is not possible to specify a minimum heap size via a configuration value
* in \a sl_memory_manager_region_config.h. The GCC and IAR linker files define
* a heap section in RAM that will be the last zone of the RAM partitioned by
* the toolchain linker. The size of this heap zone will be the remaining
* space of the RAM. If you need to perform some checks on the heap size, you
* should do it at runtime using the Memory Manager \ref subsubsection-statistics
* "statistics API". You cannot do it during the toolchain preprocessor time.
*
* ## Functionalities
*
* The Memory Manager offers different functionalities such as:
* - Dynamically allocating and freeing blocks.
* - Creating and deleting memory pools. Allocating and freeing fixed-size
* blocks from a given pool.
* - Reserving and releasing blocks.
* - Getting statistics about the heap usage and the stack.
* - Retargeting the standard C library memory functions malloc()/free()/
* calloc()/realloc() to the Memory Manager ones.
* - Overloading the C++ standard new/delete operators to the Memory Manager
* malloc()/free()
*
* \subsubsection subsubsection-dynamic-allocation Dynamic Allocation
* The dynamic allocation API allows to dynamically allocate and free memory blocks
* of various sizes. The API supports the classic signatures of memory functions
* malloc()/free()/calloc()/realloc() while also offering variants of the same
* functions.
*
* <table>
* <tr><th>Operation<th>Standard-Like Function<th>Variant Function
* <tr><td>Allocating a block<td>sl_malloc()<td>
* <ul>
* <li>sl_memory_alloc()
* <li>sl_memory_alloc_advanced()
* </ul>
* <tr><td>Freeing a block<td>sl_free()<td>sl_memory_free()
* <tr><td>Allocating a block whose content is zero'ed<td>sl_calloc()<td>sl_memory_calloc()
* <tr><td>Re-allocating a block<td>sl_realloc()<td>sl_memory_realloc()
* </table>
*
* The variants functions \a sl_memory_xxxx() differs from the standard-like functions
* with the following:
* - They return an error code of type \a sl_status_t. You may want
* to process any returned error code different from \a SL_STATUS_OK.
* - They allow to specify a block alignment requirement in bytes. The alignment
* can be any power-of-two values between 1 and 512 bytes inclusively. The default
* block alignment the Memory Manager will use is 8 bytes to maximize CPU accesses
* to allocated memory blocks.
* - They allow to specify a block type as long-term or short-term (further explained
* below). The Memory Manager allows to allocate a block from different ends of the heap
* to limit the fragmentation.
*
* Allocating a block can be done by specifying your requested size with the simple
* sl_malloc(). If you have a special alignment requirement, the function
* sl_memory_alloc_advanced() is the one to use.
* The Memory Manager will use a first fit algorithm to find the block fitting
* the requested size. If the found block is too large, the allocator tries to split it
* to create a new free block from the unwanted portion of the found block. The block
* internal split operation helps to limit the internal fragmentation.
*
* The dynamic allocation API allows to specify the block type as long-term
* (BLOCK_TYPE_LONG_TERM) or short-term (BLOCK_TYPE_SHORT_TERM) with the
* functions sl_memory_alloc() or sl_memory_alloc_advanced().
* The long-term (LT) allocations are allocated from the heap start,
* while short-term (ST) ones are allocated from the heap end. LT/ST allocations
* relate to the expected lifetime of the block allocation. LT blocks are used for
* the full duration of the application or for something that is expected
* to last a long time. For instance, a control data structure enabling the proper
* functioning of a stack's layer, a driver, a part of the application layer.
* ST blocks are used for something that is expected to be freed quite quickly.
* For example, a received buffer that needs to be processed and once processed
* will be freed.
* Grouping your allocations as LT blocks and/or ST blocks can help to limit the
* heap fragmentation.
* Certain functions does not allow to indicate the block type. In that case,
* a default type is selected by the allocator.
*
* <table>
* <tr><th>Function<th>Block type
* <tr><td>sl_malloc()<td>Long-term by default<td>
* <tr><td>sl_memory_alloc()<td>Long-term or short-term<td>
* <tr><td>sl_memory_alloc_advanced()<td>Long-term or short-term<td>
* <tr><td>sl_calloc()<td>Long-term by default<td>
* <tr><td>sl_memory_calloc()<td>Long-term or short-term<td>
* <tr><td>sl_realloc()<td>Long-term by default<td>
* <tr><td>sl_memory_realloc()<td>Long-term by default<td>
* </table>
*
* Freeing a block is done by calling sl_free() or sl_memory_free(). sl_memory_free()
* returns an error code of type sl_status_t that you may want to test. Passing a NULL
* pointer to sl_free() or sl_memory_free() results in a neutral situation where the
* free() function will do nothing. If the same block is freed twice, the function
* sl_memory_free() will return an error. During the free operation, the function will
* try to merge adjacent blocks to the block that is being freed in order to limit
* the internal fragmentation. The adjacent blocks must, of course, not be in use
* to be merged.
*
* If you want to get a block from the heap whose content has been initialized to zero
* to avoid any garbage values, the function sl_calloc() or sl_memory_calloc() can be
* called.
*
* If you need to reallocate a block, the function sl_realloc() or sl_memory_realloc()
* should be called. Both versions allow to:
* - Extend the block with the requested size greater than the original size.
* - Reduce the block with the requested size smaller than the original size.
* - Extend a different block with the requested size greater than the original size.
*
* The block can be moved elsewhere in the heap if it is impossible to extend it in its
* current memory space. A reduced block will always stay in the original block space as the
* allocator does not need to provide a different block.
* The content of the reallocated memory block is preserved up to the lesser of the
* new and old sizes, even if the block is moved to a new location. If the new size
* is larger, the value of the newly allocated portion is indeterminate.
* Some combinations of input parameters when calling sl_realloc() or sl_memory_realloc()
* will lead to the same behavior as sl_malloc(), sl_memory_alloc() or sl_free(),
* sl_memory_free() (cf. the sl_realloc() or sl_memory_realloc() function description
* for more details about those combinations).
*
* The following code snippet shows a basic block allocation and
* deallocation using the standard-like functions:
* @code{.c}
* uint8_t *ptr8;
*
* ptr8 = (uint8_t *)sl_malloc(200);
* memset(ptr8, 0xAA, 100);
* sl_free(ptr8);
* @endcode
*
* This other code snippet shows the same basic block allocation and
* deallocation using the variant functions:
* @code{.c}
* uint8_t *ptr8;
* sl_status_t status;
*
* status = sl_memory_alloc(100, BLOCK_TYPE_LONG_TERM, (void **)&ptr8);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
*
* memset(ptr8, 0xBB, 100);
*
* status = sl_memory_free(ptr8);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
* @endcode
*
* ### Memory Pool
*
* The memory pool API allows to:
* - Create a pool composed of N number of fixed-size blocks: sl_memory_create_pool().
* - Delete a pool: sl_memory_delete_pool().
* - Get a block from the pool: sl_memory_pool_alloc().
* - Free a pool's block: sl_memory_pool_free().
*
* Memory pools are convenient if you want to ensure a sort of guaranteed quotas
* for some memory allocations situations. It is also more robust to unexpected
* allocations errors as opposed to the dynamic allocation API in which a block
* allocation can fail randomly if there is no free block to satisfy the requested
* size.
*
* The memory pool API uses a pool handle. This handle is initialized when the pool
* is created with sl_memory_create_pool(). Then this handle is passed as an input
* parameter of the other functions. The handle can be allocated statically or
* dynamically. A static pool handle means the handle of type
* @ref sl_memory_pool_t "sl_memory_pool_t{}" is a global variable for example.
* A dynamic pool handle means the handle is obtained from the heap itself by
* calling the function sl_memory_pool_handle_alloc().The dynamic pool handle will
* be freed with a call to sl_memory_pool_handle_free().
*
* The following code snippet shows a typical memory pool API sequence using
* a static pool handle:
* @code{.c}
* uint8_t *ptr8;
* sl_status_t status;
* sl_memory_pool_t pool1_handle = { 0 };
*
* // Create a pool of 15 blocks whose size is 100 bytes for each block.
* status = sl_memory_create_pool(100, 15, &pool1_handle);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
*
* status = sl_memory_pool_alloc(&pool1_handle, (void **)&ptr8);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
*
* memset(ptr8, 0xCC, 100);
*
* status = sl_memory_pool_free(&pool1_handle, ptr8);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
*
* status = sl_memory_delete_pool(&pool1_handle);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
* @endcode
*
* This other code snippet presents the previous typical memory pool API sequence
* using a dynamic pool handle:
* @code{.c}
* sl_status_t status;
* sl_memory_pool_t *pool1_handle = NULL;
*
* status = sl_memory_pool_handle_alloc(&pool1_handle);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
*
* // Create a pool of 15 blocks of 100 bytes in size.
* status = sl_memory_create_pool(100, 15, &pool1_handle);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
*
* // Get blocks from the pool, use them and free them once done.
* ...
*
* status = sl_memory_delete_pool(&pool1_handle);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
*
* status = sl_memory_pool_handle_free(pool1_handle);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
* @endcode
*
* ### Dynamic Reservation
*
* The dynamic reservation is a special construct allowing to reserve a block
* of a given size with sl_memory_reserve_block() and to release it with
* sl_memory_release_block(). The reserved block can then be used to any
* application purposes. The reserved block will be taken from the
* short-term section at the end of the heap.
* Please note that the dynamic reservation API is not meant
* to be used in the same way as the \ref subsubsection-dynamic-allocation
* "dynamic allocation API".
*
* The dynamic reservation API uses a reservation handle. This handle is initialized
* when the block is reserved with sl_memory_reserve_block(). Then this handle
* is passed as an input parameter to the other functions. The handle can be
* allocated statically or dynamically. A static reservation handle means the handle
* of type @ref sl_memory_reservation_t "sl_memory_reservation_t{}" is a global
* variable for example. A dynamic reservation handle means the handle is
* obtained from the heap itself by calling the function
* sl_memory_reservation_handle_alloc(). The dynamic reservaiton handle will be
* freed with a call to sl_memory_reservation_handle_free().
*
* The following code snippet shows a typical dynamic reservation API sequence
* using a static reservation handle:
* @code{.c}
* uint8_t *ptr8;
* sl_status_t status;
* sl_memory_reservation_t reservation_handle1 = { 0 };
*
* status = sl_memory_reserve_block(1024,
* SL_MEMORY_BLOCK_ALIGN_8_BYTES,
* reservation_handle1,
* (void **)&ptr8);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
*
* memset(ptr8, 0xDD, 1024);
*
* status = sl_memory_release_block(&reservation_handle1);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
* @endcode
*
* This other code snippet demonstrates the previous typical dynamic reservation API
* sequence using a dynamic reservation handle:
* @code{.c}
* uint8_t *ptr8;
* sl_status_t status;
* sl_memory_reservation_t *reservation_handle1;
*
* status = sl_memory_reservation_handle_alloc(&reservation_handle1);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
*
* status = sl_memory_reserve_block(1024,
* SL_MEMORY_BLOCK_ALIGN_8_BYTES,
* reservation_handle1,
* (void **)&ptr8);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
*
* memset(ptr8, 0xEE, 1024);
*
* status = sl_memory_release_block(&reservation_handle1);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
*
* status = sl_memory_reservation_handle_free(reservation_handle1);
* if (status != SL_STATUS_OK) {
* // Process the error condition.
* }
* @endcode
*
* \subsubsection subsubsection-statistics Statistics
*
* As your code is allocating and freeing blocks, you may want to know at a certain
* instant what the current state of the heap is. Some heap statistics queries at
* runtime can help to understand the current usage of the heap. By using the
* following statistics functions, you may be able to perform some asynchronous
* runtime heap checks:
* - Total heap size: sl_memory_get_total_heap_size().
* - Current free heap size: sl_memory_get_free_heap_size().
* - Current used heap size: sl_memory_get_used_heap_size().
* - Highest accumulated heap size usage: sl_memory_get_heap_high_watermark().
* - You can reset the high heap usage watermark with
* sl_memory_reset_heap_high_watermark().
*
* Besides a few functions each dedicated to a specific statistic, the function
* sl_memory_get_heap_info() allows to get a general heap information structure
* of type @ref sl_memory_heap_info_t "sl_memory_heap_info_t{}" with several heap
* statistics. Some of them overlap the statistics returned by the dedicated
* functions while the others complements statistics returned by the dedicated
* functions. Refer to the description of @ref sl_memory_heap_info_t
* "sl_memory_heap_info_t{}" for more information of each field.
*
* If you want to know the start address and the total size of the program's
* stack and/or heap, simply call respectively the function sl_memory_get_stack_region()
* and/or sl_memory_get_heap_region().
*
* ### C/C++ Toolchains Standard Memory Functions Retarget/Overload
*
* A program can perform dynamic memory allocations and deallocations using the
* standard memory functions whose implementation is provided by the C or C++
* toolchain libraries.
* - C toolchain for the classic malloc()/free()/calloc()/realloc()
* - C++ toolchain for the new/delete operators
*
* The Memory Manager supports the C standard memory functions retarget and the C++
* new/delete overload.
*
* When the \b memory_manager component is installed, the C standard memory
* functions are automatically retargeted to the Memory Manager ones:
* - GCC: coupled to the linker option "--wrap", the functions retargeted are
* - standard _malloc_r() -> sl_malloc()
* - standard _free_r() -> sl_free()
* - standard _calloc_r() -> sl_calloc()
* - standard _realloc_r() -> sl_realloc()
* - IAR: it has three separate heap memory handlers (the basic, the advanced,
* and the no-free heap handlers). IAR generally auto-selects one of the handlers.
* - Basic heap
* - standard __basic_malloc() -> sl_malloc()
* - standard __basic_free() -> sl_free()
* - standard __basic_calloc() -> sl_calloc()
* - standard __basic_realloc() -> sl_realloc()
* - Advanced heap
* - standard __iar_dl_malloc() -> sl_malloc()
* - standard __iar_dl_free() -> sl_free()
* - standard __iar_dl_calloc() -> sl_calloc()
* - standard __iar_dl_realloc() -> sl_realloc()
* - No Free heap
* - standard __no_free_malloc() -> sl_malloc()
* - standard __no_free_calloc() -> sl_calloc()
*
* If you need the C++ new/delete global overload calling sl_memory_alloc() and
* sl_memory_free(), please install the additional component
* \b memory_manager_cpp.
* This global overload of new/delete operators will also apply to any C++
* standard containers (for example vector, string, list).
*
* @note The Silicon Labs SDK generates a GCC or IAR linker script with Simplicity
* Studio. A typical toolchain linker script will define a section called "heap"
* or "HEAP". Usually, the C memory standard functions will assume a linker-defined
* "heap" section exists. If the memory_manager component is present, the
* toolchain linker script will define a new heap section named "memory_manager_heap"
* or "MEMORY_MANAGER_HEAP". Since the Memory Manager retargets the standard
* function malloc()/free()/calloc()/realloc() to the Memory Manager ones, there
* should not be any issues in your program. If an unlikely situation occurs where
* the toolchain standard memory functions retarget does not work, your application
* might end up calling a standard malloc() implementation from the
* toolchain instead of the Memory Manager one. In that case, a runtime
* error can occur and it is expected. You should then review the project settings
* to detect why the Memory Manager retarget did not work properly.
*
* ## Hints
* ### Memory Allocations from ISR
*
* In general, ISR must be kept short. Allocating and freeing blocks from an ISR is
* possible but you should be careful. Nothing really prevents you from calling the
* dynamic allocation API functions such as sl_malloc() and sl_free(). But keep in
* mind a few things with the dynamic allocation API:
* - The dynamic allocation API functions protect their internal resources
* such as global lists managing the heap metadata by using critical sections.
* So when in your ISR, you will disable interrupts for a certain period of time,
* preventing other interrupts to be processed in time if your application has hard
* real-time constraints. This increases the overall interrupt latency of your
* system if this ISR executes very often to perform a dynamic memory operation
* - They can introduce non-deterministic behavior which is undesirable if your
* application requires crucial precise timing
* - A function such as sl_malloc() can fail if there is no block to satisfy
* your requested size allocation. Implementing the proper error handling in the
* ISR may increase the time spent in the ISR.
*
* In the end, it really depends of your ISR processing context doing memory
* allocations/deallocations. If you really need to perform dynamic allocation from
* ISR, it may be better at least to use a memory pool. Getting and releasing a block
* from a pool is an operation more deterministic. And if you have properly
* sized your pool with a number of available blocks, you are less likely to
* encounter an allocation error.
*
* @{
*****************************************************************************/
// ----------------------------------------------------------------------------
// DEFINES
/// Special value to indicate the default block alignment to the Memory Manager
/// allocator. 8 bytes is the minimum alignment to account for largest CPU data
/// type that can be used in some block allocation scenarios.
#define SL_MEMORY_BLOCK_ALIGN_DEFAULT 0xFFFFFFFFU
/// Pre-defined values for block alignment managed by the Memory Manager allocator.
#define SL_MEMORY_BLOCK_ALIGN_8_BYTES 8U ///< 8 bytes alignment.
#define SL_MEMORY_BLOCK_ALIGN_16_BYTES 16U ///< 16 bytes alignment.
#define SL_MEMORY_BLOCK_ALIGN_32_BYTES 32U ///< 32 bytes alignment.
#define SL_MEMORY_BLOCK_ALIGN_64_BYTES 64U ///< 64 bytes alignment.
#define SL_MEMORY_BLOCK_ALIGN_128_BYTES 128U ///< 128 bytes alignment.
#define SL_MEMORY_BLOCK_ALIGN_256_BYTES 256U ///< 256 bytes alignment.
#define SL_MEMORY_BLOCK_ALIGN_512_BYTES 512U ///< 512 bytes alignment.
// ----------------------------------------------------------------------------
// DATA TYPES
/// @brief Block type.
typedef enum {
BLOCK_TYPE_LONG_TERM = 0, ///< Long-term block type.
BLOCK_TYPE_SHORT_TERM = 1 ///< Short-term block type.
} sl_memory_block_type_t;
/// @brief General purpose heap information.
typedef struct {
uint32_t base_addr; ///< Heap base address.
size_t used_size; ///< Used size (in bytes), independently of alignment.
size_t free_size; ///< Free size (in bytes), independently of alignment.
size_t total_size; ///< Total heap size (in bytes).
size_t free_block_count; ///< Number of free blocks.
size_t free_block_largest_size; ///< Largest free block size (in bytes).
size_t free_block_smallest_size; ///< Smallest free block size (in bytes).
size_t used_block_count; ///< Number of used blocks.
size_t used_block_largest_size; ///< Largest used block size (in bytes).
size_t used_block_smallest_size; ///< Smallest used block size (in bytes).
} sl_memory_heap_info_t;
/// @brief Memory block reservation handle.
typedef struct {
void *block_address; ///< Reserved block base address.
size_t block_size; ///< Reserved block size (in bytes).
} sl_memory_reservation_t;
/// @brief Memory pool handle.
typedef struct {
#if defined(SL_MEMORY_POOL_POWER_AWARE)
sl_memory_reservation_t *reservation; ///< Pointer to reservation handle.
#else
void *block_address; ///< Reserved block base address.
#endif
uint32_t *block_free; ///< Pointer to pool's free blocks list.
size_t block_count; ///< Max quantity of blocks in the pool.
size_t block_size; ///< Size of each block.
} sl_memory_pool_t;
// ----------------------------------------------------------------------------
// PROTOTYPES
/***************************************************************************//**
* Initializes the memory manager.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
*
* @note This function should only be called once.
******************************************************************************/
sl_status_t sl_memory_init(void);
/***************************************************************************//**
* Reserves a memory block that will never need retention in EM2.
*
* @param[in] size Size of the block, in bytes.
* @param[in] align Required alignment for the block, in bytes.
* @param[out] block Pointer to variable that will receive the start address
* of the allocated block. NULL in case of error condition.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
*
* @note Required alignment of memory block (in bytes) MUST be a power of 2
* and can range from 1 to 512 bytes.
* The define SL_MEMORY_BLOCK_ALIGN_DEFAULT can be specified to select
* the default alignment.
******************************************************************************/
sl_status_t sl_memory_reserve_no_retention(size_t size,
size_t align,
void **block);
/***************************************************************************//**
* Allocates a memory block of at least requested size from the heap. Simple
* version.
*
* @param[in] size Size of the block, in bytes.
*
* @return Pointer to allocated block if successful. Null pointer if
* allocation failed.
*
* @note Requesting a block of 0 byte will return a null pointer.
*
* @note All allocated blocks using this function will be considered long-term
* allocations.
******************************************************************************/
void *sl_malloc(size_t size);
/***************************************************************************//**
* Dynamically allocates a block of memory.
*
* @param[in] size Size of the block, in bytes.
* @param[in] type Type of block (long-term or short-term).
* BLOCK_TYPE_LONG_TERM
* BLOCK_TYPE_SHORT_TERM
* @param[out] block Pointer to variable that will receive the start address
* of the allocated block. NULL in case of error condition.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_alloc(size_t size,
sl_memory_block_type_t type,
void **block);
/***************************************************************************//**
* Dynamically allocates a block of memory. Advanced version that allows to
* specify alignment.
*
* @param[in] size Size of the block, in bytes.
* @param[in] align Required alignment for the block, in bytes.
* @param[in] type Type of block (long-term or short term).
* BLOCK_TYPE_LONG_TERM
* BLOCK_TYPE_SHORT_TERM
* @param[out] block Pointer to variable that will receive the start address
* of the allocated block. NULL in case of error condition.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
*
* @note Required alignment of memory block (in bytes) MUST be a power of 2
* and can range from 1 to 512 bytes.
* The define SL_MEMORY_BLOCK_ALIGN_DEFAULT can be specified to select
* the default alignment.
******************************************************************************/
sl_status_t sl_memory_alloc_advanced(size_t size,
size_t align,
sl_memory_block_type_t type,
void **block);
/***************************************************************************//**
* Frees a previously allocated block back into the heap. Simple version.
*
* @param[in] ptr Pointer to memory block to be freed.
*
* @note Passing a null pointer does nothing.
******************************************************************************/
void sl_free(void *ptr);
/***************************************************************************//**
* Frees a dynamically allocated block of memory.
*
* @param[in] block Pointer to the block that must be freed.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_free(void *block);
/***************************************************************************//**
* Dynamically allocates a block of memory cleared to 0. Simple version.
*
* @param[in] item_count Number of elements to be allocated.
* @param[in] size Size of each elements, in bytes.
*
* @return Pointer to allocated block if successful. Null pointer if
* allocation failed.
*
* @note All allocated blocks using this function will be considered long-term
* allocations.
******************************************************************************/
void *sl_calloc(size_t item_count,
size_t size);
/***************************************************************************//**
* Dynamically allocates a block of memory cleared to 0.
*
* @param[in] item_count Number of elements to be allocated.
* @param[in] size Size of each elements, in bytes.
* @param[in] type Type of block (long-term or short-term).
* BLOCK_TYPE_LONG_TERM
* BLOCK_TYPE_SHORT_TERM
* @param[out] block Pointer to variable that will receive the start
* address of the allocated block. NULL in case of
* error condition.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_calloc(size_t item_count,
size_t size,
sl_memory_block_type_t type,
void **block);
/***************************************************************************//**
* Resizes a previously allocated memory block. Simple version.
*
* @param[in] ptr Pointer to the allocation to resize. If NULL, behavior
* is same as sl_malloc(), sl_memory_alloc().
* @param[in] size New size of the block, in bytes. If 0, behavior is same as
* sl_free(), sl_memory_free().
*
* @return Pointer to newly allocated block, if successful. Null pointer if
* re-allocation failed.
*
* @note All re-allocated blocks using this function will be considered
* long-term allocations.
*
* @note 'ptr' NULL and 'size' of 0 bytes is an incorrect parameters
* combination. No reallocation will be done by the function as it is
* an error condition.
*
* @note If the new 'size' is the same as the old, the function changes nothing
* and returns the same provided address 'ptr'.
******************************************************************************/
void *sl_realloc(void *ptr,
size_t size);
/***************************************************************************//**
* Resizes a previously allocated memory block.
*
* @param[in] ptr Pointer to the allocation to resize. If NULL, behavior
* is same as sl_malloc(), sl_memory_alloc().
* @param[in] size New size of the block, in bytes. If 0, behavior is same as
* sl_free(), sl_memory_free().
* @param[out] block Pointer to variable that will receive the start address of
* the new allocated memory. NULL in case of error condition.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
*
* @note All re-allocated blocks using this function will be considered
* long-term allocations.
*
* @note 'ptr' NULL and 'size' of 0 bytes is an incorrect parameters
* combination. No reallocation will be done by the function as it is
* an error condition.
*
* @note If the new 'size' is the same as the old, the function changes nothing
* and returns the same provided address 'ptr'.
******************************************************************************/
sl_status_t sl_memory_realloc(void *ptr,
size_t size,
void **block);
/***************************************************************************//**
* Dynamically reserves a block of memory.
*
* @param[in] size Size of the block, in bytes.
* @param[in] align Required alignment for the block, in bytes.
* @param[in] handle Handle to the reserved block.
* @param[out] block Pointer to variable that will receive the start address
* of the allocated block. NULL in case of error condition.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
*
* @note Required alignment of memory block (in bytes) MUST be a power of 2
* and can range from 1 to 512 bytes.
* The define SL_MEMORY_BLOCK_ALIGN_DEFAULT can be specified to select
* the default alignment.
******************************************************************************/
sl_status_t sl_memory_reserve_block(size_t size,
size_t align,
sl_memory_reservation_t *handle,
void **block);
/***************************************************************************//**
* Frees a dynamically reserved block of memory.
*
* @param[in] handle Handle to the reserved block.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_release_block(sl_memory_reservation_t *handle);
/***************************************************************************//**
* Dynamically allocates a block reservation handle.
*
* @param[out] handle Handle to the reserved block.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_reservation_handle_alloc(sl_memory_reservation_t **handle);
/***************************************************************************//**
* Frees a dynamically allocated block reservation handle.
*
* @param[in] handle Handle to the reserved block.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_reservation_handle_free(sl_memory_reservation_t *handle);
/***************************************************************************//**
* Gets the size of the memory reservation handle structure.
*
* @return Memory reservation handle structure's size in bytes.
******************************************************************************/
uint32_t sl_memory_reservation_handle_get_size(void);
/***************************************************************************//**
* Creates a memory pool.
*
* @param[in] block_size Size of each block, in bytes.
* @param[in] block_count Number of blocks in the pool.
* @param[in] pool_handle Handle to the memory pool.
*
* @note This function assumes the 'pool_handle' is provided by the caller:
* - either statically (e.g. as a global variable)
* - or dynamically by calling sl_memory_pool_handle_alloc().
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_create_pool(size_t block_size,
uint32_t block_count,
sl_memory_pool_t *pool_handle);
/***************************************************************************//**
* Deletes a memory pool.
*
* @param[in] pool_handle Handle to the memory pool.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
*
* @note All pool allocations need to be freed by calling sl_memory_pool_free()
* on each block before calling sl_memory_delete_pool().
*
* @note The pool_handle provided is neither freed or invalidated. It can be
* reused in a new call to sl_memory_create_pool() to create another pool.
******************************************************************************/
sl_status_t sl_memory_delete_pool(sl_memory_pool_t *pool_handle);
/***************************************************************************//**
* Allocates a block from a memory pool.
*
* @param[in] pool_handle Handle to the memory pool.
* @param[out] block Pointer to a variable that will receive the address
* of the allocated block. NULL in case of error
* condition.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_pool_alloc(sl_memory_pool_t *pool_handle,
void **block);
/***************************************************************************//**
* Frees a block from a memory pool.
*
* @param[in] pool_handle Handle to the memory pool.
* @param[in] block Pointer to the block to free.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_pool_free(sl_memory_pool_t *pool_handle,
void *block);
/***************************************************************************//**
* Dynamically allocates a memory pool handle.
*
* @param[out] pool_handle Handle to the memory pool.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_pool_handle_alloc(sl_memory_pool_t **pool_handle);
/***************************************************************************//**
* Frees a dynamically allocated memory pool handle.
*
* @param[in] pool_handle Handle to the memory pool.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_pool_handle_free(sl_memory_pool_t *pool_handle);
/***************************************************************************//**
* Gets the size of the memory pool handle structure.
*
* @return Memory pool handle structure's size.
******************************************************************************/
uint32_t sl_memory_pool_handle_get_size(void);
/***************************************************************************//**
* Gets the total count of blocks in a memory pool.
*
* @param[in] pool_handle Handle to the memory pool.
*
* @return Total number of blocks.
******************************************************************************/
uint32_t sl_memory_pool_get_total_block_count(const sl_memory_pool_t *pool_handle);
/***************************************************************************//**
* Gets the count of free blocks in a memory pool.
*
* @param[in] pool_handle Handle to the memory pool.
*
* @return Number of free blocks.
******************************************************************************/
uint32_t sl_memory_pool_get_free_block_count(const sl_memory_pool_t *pool_handle);
/***************************************************************************//**
* Gets the count of used blocks in a memory pool.
*
* @param[in] pool_handle Handle to the memory pool.
*
* @return Number of used blocks.
******************************************************************************/
uint32_t sl_memory_pool_get_used_block_count(const sl_memory_pool_t *pool_handle);
/***************************************************************************//**
* Populates an sl_memory_heap_info_t{} structure with the current status of
* the heap.
*
* @param[in] heap_info Pointer to structure that will receive further heap
* information data.
*
* @return SL_STATUS_OK if successful. Error code otherwise.
******************************************************************************/
sl_status_t sl_memory_get_heap_info(sl_memory_heap_info_t *heap_info);
/***************************************************************************//**
* Gets the total size of the heap.
*
* @return Heap's size in bytes.
******************************************************************************/
size_t sl_memory_get_total_heap_size(void);
/***************************************************************************//**
* Gets the current free heap size.
*
* @return Free heap size in bytes.
******************************************************************************/
size_t sl_memory_get_free_heap_size(void);
/***************************************************************************//**
* Gets the current used heap size.
*
* @return Used heap size in bytes.
******************************************************************************/
size_t sl_memory_get_used_heap_size(void);
/***************************************************************************//**
* Gets heap high watermark.
*
* @return Highest heap usage in bytes recorded.
******************************************************************************/
size_t sl_memory_get_heap_high_watermark(void);
/***************************************************************************//**
* Reset heap high watermark to the current heap used.
******************************************************************************/
void sl_memory_reset_heap_high_watermark(void);
/** @} (end addtogroup memory_manager) */
#ifdef __cplusplus
}
#endif
#endif /* SL_MEMORY_MANAGER_H_ */

View File

@@ -0,0 +1,77 @@
/***************************************************************************//**
* @file
* @brief Getters for Heap and stack.
*******************************************************************************
* # License
* <b>Copyright 2024 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#ifndef SL_MEMORY_MANAGER_REGION_H_
#define SL_MEMORY_MANAGER_REGION_H_
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup memory_manager Memory Manager
* @{
******************************************************************************/
// ----------------------------------------------------------------------------
// DATA TYPES
/// @brief Memory region structure.
typedef struct sl_memory_region_t {
void * addr; ///< Pointer to the beginning of the memory region. Can be NULL.
size_t size; ///< Size of this memory region.
} sl_memory_region_t;
// ----------------------------------------------------------------------------
// PROTOTYPES
/***************************************************************************//**
* Gets size and location of the stack.
*
* @return description of the region reserved for the C stack.
******************************************************************************/
sl_memory_region_t sl_memory_get_stack_region(void);
/***************************************************************************//**
* Gets size and location of the heap.
*
* @return description of the region reserved for the C heap.
******************************************************************************/
sl_memory_region_t sl_memory_get_heap_region(void);
/** @} end addtogroup memory_manager) */
#ifdef __cplusplus
}
#endif
#endif /* SL_MEMORY_MANAGER_REGION_H_ */

View File

@@ -0,0 +1,631 @@
/***************************************************************************//**
* @file
* @brief Macros and functions for memory profiling
*******************************************************************************
* # License
* <b>Copyright 2023 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#ifndef SLI_MEMORY_PROFILER_H
#define SLI_MEMORY_PROFILER_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "sl_status.h"
#if defined(SL_COMPONENT_CATALOG_PRESENT)
// The component catalog is present, so we're in an application build and can
// check if the Memory Profiler is present
#include "sl_component_catalog.h"
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
// The Memory Profiler is present. Use its configuration and enable profiling.
#include "sli_memory_profiler_config.h"
#define SLI_MEMORY_PROFILER_ENABLE_PROFILING 1
#else
// The Memory Profiler is not present. Disable profiling.
#define SLI_MEMORY_PROFILER_ENABLE_PROFILING 0
#endif // defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
#else // defined(SL_COMPONENT_CATALOG_PRESENT)
// The component catalog is not present, so we're in a library build. The build
// environment of the library must specify the configuration defines that affect
// the macros in this header if it wants a non-default configuration. We default
// to enabling basic profiling but disabling ownership tracking.
#if !defined(SLI_MEMORY_PROFILER_ENABLE_PROFILING)
#define SLI_MEMORY_PROFILER_ENABLE_PROFILING 1
#endif
#if !defined(SLI_MEMORY_PROFILER_ENABLE_OWNERSHIP_TRACKING)
#define SLI_MEMORY_PROFILER_ENABLE_OWNERSHIP_TRACKING 0
#endif
#endif // defined(SL_COMPONENT_CATALOG_PRESENT)
#ifdef __cplusplus
extern "C" {
#endif
/// @cond DO_NOT_INCLUDE_WITH_DOXYGEN
/***************************************************************************//**
* @addtogroup memory_profiler Memory Profiler
* @{
*
* @brief Memory profiler for tracking and reporting system RAM usage
*
* This memory profiler component provides a tool for tracking and reporting of
* system RAM usage. It is implemented as a combination of a simple component
* running in the Device Under Test (DUT) and a Python script running on a
* computer. The on-DUT component is a relatively thin layer that uses J-Link
* RTT (Real Time Transfer) to output events about memory allocation on the
* device. The Python script on the computer receives these events and maintains
* the memory usage bookkeeping. The script is available in
* `platform/service/memory_manager/profiler/scripts/memory_profiler_platform.py`.
*
* The memory profiler represents memory in a hierarchical structure with the
* physical RAM as the root of the tree. The physical RAM is split into
* allocations such as stack and heap, and the heap is further split into
* allocations for different uses of memory.
*
* Each node in the tree is represented by one instance of a memory tracker.
* Each tracker has a short description and records the current and peak number
* of bytes allocated. Leaves in the tree represent concrete uses of RAM, for
* example allocations of a particular object types in a wireless stack.
* Intermediate nodes typically correspond to memory allocation abstractions,
* such as memory pools or helper functions that are used by the concrete users
* of memory.
*
* The trackers can represent two different types of allocation behavior:
*
* 1. Pool trackers represent memory abstractions that make one contiguous
* allocation from their parent memory to allocate a pool. Portions of that
* pool are then given to callers that allocate memory from the pool
* abstraction. The Memory Manager (MM) heap is one example of such a
* pool. The MM heap reserves some portion of the physical RAM at
* initialization time and gives portions of that pool to its callers.
* Another example of a pool tracker are the individual pools that can be
* created and deleted at runtime using the MM Pool API. Each pool is
* associated with a pool tracker to track the allocation and freeing of the
* pool items during the lifetime of the pool.
*
* 2. Trackers that are not pools are used to collect the allocation statistics
* of a certain type of allocation or use case so that the memory use of that
* particular type can be easily distinguished from all the other users of
* memory. For example, the Memory Manager creates a separate tracker
* for long-term heap allocations, short-term heap allocations, and heap
* reservations, so that these can be counted individually. Components such
* as wireless stacks may create their own trackers for tracking things like
* the total sum of allocations made by a particular component, or more
* granularly the allocations made for a particular type of a resource.
*
* The Memory Manager is integrated to the Memory Profiler and all
* allocations from the heap are automatically tracked by the top-level trackers
* for the heap. This provides the usage tracking for the heap as a whole, i.e.
* how many bytes of heap are used in total. If a component or an application
* wants to track how many bytes it has allocated from the heap, it can create a
* new memory profiler tracker. Pool memory trackers are created with a call to
* `sli_memory_profiler_create_pool_tracker()` and other trackers are created
* with `sli_memory_profiler_create_tracker()`.
*
* Both types of trackers are identified by a handle of type
* `sli_memory_tracker_handle_t`, which is just a void pointer that is provided
* by the code that creates the tracker. Code that creates a tracker can use any
* void pointer that is guaranteed to be unique among all trackers for the
* duration of the tracker lifetime, i.e. until the tracker is deleted with a
* call to `sli_memory_profiler_delete_tracker()`. A good approach is to use a
* pointer that is tightly associated with the tracked object or use case. For
* example, a tracker for a memory pool can use the pool handle as the tracker
* handle. A function that wants to track the allocations it makes can use its
* own function pointer as the tracker handle.
*
* Top-level trackers for the heap are created at platform initialization time
* and are never deleted. Other trackers have the same lifetime as the entity or
* component that is being tracked. For example, when a memory pool is created,
* a tracker is created for the pool. When the pool is deleted, the tracker is
* also deleted. Similarly if a component creates a tracker for its memory use
* when the component is initialized or started, the component would typically
* delete the tracker for its memory when the component is de-initialized or
* stopped. Trackers are deleted with a call to
* `sli_memory_profiler_delete_tracker()`.
*
* The memory profiler implementation aims to minimize the impact of the memory
* profiler calls on both RAM and CPU usage when the profiling is not enabled by
* including the component in the SW configuration. To keep the calling code
* clean of conditional compilation but still allow completely removing the
* tracking calls when the profiler is not used, the calling code is encouraged
* to do the tracking via the macros such as @ref
* SLI_MEMORY_PROFILER_TRACK_ALLOC() and @ref SLI_MEMORY_PROFILER_TRACK_FREE().
* These macros will automatically expand to empty when the profiling is
* disabled at build time.
*
* If a component is compiled into a library (as opposed to being compiled at
* application build time), the library may contain calls to the Memory Profiler
* API even if tracking will be disabled in the application. In this case the SW
* is linked with a stub Memory Profiler API implementation that provides dummy
* functions. The stub implementation does not use any RAM or RTT, so the
* overhead is minimized.
*
* Trackers use @ref SLI_MEMORY_PROFILER_TRACK_ALLOC() to track the allocation of
* memory blocks and include the bytes of RAM to the counts in the trackers. To
* make sure that any arbitrary pointer can be correctly mapped to the
* allocation it belongs to, every tracker must accurately track the pointer and
* size of the memory block that they are operating with. As an example, when an
* allocation from the MM heap is made, the MM heap implementation tracks the
* full allocation RAM block including any extra metadata or size padding that
* is used around the block returned to the caller. The caller will track the
* allocation using the size it requested from the heap (which is smaller than
* the full block needed to satisfy the allocation), and the pointer it received
* from the heap (which is within the full block allocated from the RAM).
*
* Trackers use @ref SLI_MEMORY_PROFILER_TRACK_FREE() to mark the freeing of a
* memory block. Here too the callers must accurately identify the freed block
* by providing the same pointer they provided in the corresponding call to
* track the allocation. The tracking of the free does not need to be fully
* symmetric, though. As an example, assume that an app has created the tracker
* `app_mallocs` and is using @ref SLI_MEMORY_PROFILER_TRACK_ALLOC() to track on
* that tracker every block it received from @ref sl_malloc(). When the
* application calls @ef sl_free() to free a memory block, the application does
* not need to call @ref SLI_MEMORY_PROFILER_TRACK_FREE() to mark the freeing.
* The MM heap will track the freeing in `sl_free()` and the freeing of the
* memory is automatically counted in the `app_mallocs` tracker as well.
*
******************************************************************************/
/**
* @brief Memory tracker handle type
*/
typedef const void* sli_memory_tracker_handle_t;
/**
* @brief Value used to indicate an invalid memory tracker handle
*/
#define SLI_INVALID_MEMORY_TRACKER_HANDLE ((sli_memory_tracker_handle_t) NULL)
/**
* @brief Initialize the memory profiler
*
* Memory Profiler initialization is handled internally by the Platform
* initialization. Applications do not need to and should not call this function
* directly.
*/
void sli_memory_profiler_init();
/**
* @brief Get the current program counter
*
* This helper can be used to obtain the current program counter when invoking
* @ref SLI_MEMORY_PROFILER_TRACK_OWNERSHIP() with the intention of assigning
* the ownership to the location that is invoking the tracking macro.
*
* @return Current program counter
*/
#if defined(__GNUC__)
__attribute__( (always_inline) ) static inline void * sli_memory_profiler_get_pc(void)
{
void *pc;
__asm volatile ("MOV %0, PC" : "=r" (pc));
return pc;
}
#elif defined(__IAR_SYSTEMS_ICC__)
_Pragma("inline=forced") static inline void * sli_memory_profiler_get_pc(void)
{
void *pc;
__asm volatile ("MOV %0, PC" : "=r" (pc));
return pc;
}
#else
static inline void * sli_memory_profiler_get_pc(void)
{
// Memory Profiler supports ownership tracking only with the GCC or IAR compiler
return NULL;
}
#endif
/**
* @brief Get the return address of the current function
*
* This helper can be used to obtain the return address of the current function
* when invoking @ref SLI_MEMORY_PROFILER_TRACK_OWNERSHIP() with the intention
* of assigning the ownership to the location that made the call to the function
* that is invoking the tracking macro.
*
* Note that the IAR compiler does not provide a mechanism to reliably obtain
* the return address, so we must emulate that by taking the content of the link
* register. This is never completely reliable, but is guaranteed to fail if
* `sli_memory_profiler_get_return_address()` is used in a function after calls
* to other functions have already been made. Therefore this function should
* always be called right at the beginning of the function that wants to know
* the return address.
*
* @return The return address of the current function
*/
#if defined(__GNUC__)
__attribute__( (always_inline) ) static inline void * sli_memory_profiler_get_return_address(void)
{
return __builtin_extract_return_addr(__builtin_return_address(0));
}
#elif defined(__IAR_SYSTEMS_ICC__)
_Pragma("inline=forced") static inline void * sli_memory_profiler_get_return_address(void)
{
uint32_t lr;
lr = __get_LR();
return (void *)lr;
}
#else
static inline void * sli_memory_profiler_get_return_address(void)
{
// Memory Profiler supports ownership tracking only with the GCC or IAR compiler
return NULL;
}
#endif
/**
* @brief Create a pool memory tracker
*
* This function creates a memory tracker for a memory use case that allocates
* one fixed-size pool from some allocator and then allocates portions of the
* pool to its own clients.
*
* The memory block specified by @p pointer and @p size must match a memory
* allocation that the pool implementation has successfully obtained from some
* memory allocator that has tracked the allocation. If the pool implementation
* later frees the pool memory block (for example when the component that uses
* the pool is de-initialized), the pool implementation must delete the pool
* tracker with a call to @ref sli_memory_profiler_delete_tracker before it
* frees the memory block that the pool was using.
*
* @param[in] tracker_handle The handle to identify the tracker. The handle must
* remain unique among all trackers until the tracker is deleted with @ref
* sli_memory_profiler_delete_tracker.
* @param[in] description Short description of the usage of the tracker memory,
* or NULL to omit the description. The description can be set or updated
* later with a call to @ref sli_memory_profiler_describe_tracker.
* @param[in] ptr Pointer to the pool block allocated from the parent memory
* @param[in] size The size of the pool block allocated from the parent memory
*
* @return SL_STATUS_OK if a tracker was created, SL_STATUS_NOT_AVAILABLE if the
* Memory Profiler is not included in the application.
*/
sl_status_t sli_memory_profiler_create_pool_tracker(sli_memory_tracker_handle_t tracker_handle,
const char *description,
void* ptr,
size_t size);
/**
* @brief Create a memory tracker
*
* This function creates a memory tracker for a use case that allocates blocks
* from tracked parent allocator.
*
* @param[in] tracker_handle The handle to identify the tracker. The handle must
* remain unique among all trackers until the tracker is deleted with @ref
* sli_memory_profiler_delete_tracker.
* @param[in] description Short description of the usage of the tracker memory,
* or NULL to omit the description. The description can be set or updated
* later with a call to @ref sli_memory_profiler_describe_tracker.
*
* @return SL_STATUS_OK if a tracker was created, SL_STATUS_NOT_AVAILABLE if the
* Memory Profiler is not included in the application.
*/
sl_status_t sli_memory_profiler_create_tracker(sli_memory_tracker_handle_t tracker_handle,
const char *description);
/**
* @brief Add or update a description to a previously created memory tracker
*
* This function is typically used to assign a description to a tracker that has
* been created by a lower layer that does not know the use case of the memory.
* For example, when the Memory Manager creates a memory pool object, it
* creates a Memory Profiler tracker with the same handle as the pool handle but
* cannot assign a descriptive name, as it cannot know what the pool is used
* for. The caller that created the pool can then use this function with the
* pool handle to assign a description for the pool memory.
*
* @param[in] tracker_handle The handle of the memory tracker
* @param[in] description Short description of the usage of the tracker memory,
* or NULL to clear the description.
*/
void sli_memory_profiler_describe_tracker(sli_memory_tracker_handle_t tracker_handle,
const char *description);
/**
* @brief Delete a memory tracker
*
* This function deletes a previously created memory tracker.
*
* @param[in] tracker_handle The handle of the memory tracker
*/
void sli_memory_profiler_delete_tracker(sli_memory_tracker_handle_t tracker_handle);
/**
* @brief Track the allocation of a memory block
*
* NOTE: This function is intended to be called via the @ref
* SLI_MEMORY_PROFILER_TRACK_ALLOC() macro.
*
* @param[in] tracker_handle The handle of the memory tracker
* @param[in] ptr Pointer to the allocated memory or NULL if allocation failed
* @param[in] size The number of bytes allocated, or attempted to allocate
*/
void sli_memory_profiler_track_alloc(sli_memory_tracker_handle_t tracker_handle,
void * ptr,
size_t size);
/**
* @brief Track the reallocation of a previously allocated memory block
*
* NOTE: This function is intended to be called via the @ref
* SLI_MEMORY_PROFILER_TRACK_REALLOC() macro.
*
* NOTE: Reallocation is a special operation that is intended to be tracked only
* by the underlying heap allocator in the Memory Manager. Any
* higher-level allocations that are tracked within the reallocated block are
* automatically moved and resized without any tracking calls from the
* higher-level trackers.
*
* If the realloc operation involves allocating a new block and freeing the
* previous block, the Memory Manager heap must track the realloc when
* the allocation of the new memory has been tracked but the old memory block
* has not been freed yet. This is needed to guarantee that both @p ptr and @p
* realloced_ptr are valid and owned by the calling thread when the realloc is
* tracked.
*
* @param[in] tracker_handle The handle of the lowest-layer heap memory tracker
* @param[in] ptr Pointer to the original memory block
* @param[in] realloced_ptr Pointer to the resized or allocated memory
* @param[in] size The size that the block was reallocated to
*/
void sli_memory_profiler_track_realloc(sli_memory_tracker_handle_t tracker_handle,
void * ptr,
void * realloced_ptr,
size_t size);
/**
* @brief Track the allocation of a memory block and record ownership
*
* NOTE: This function is intended to be called via the @ref
* SLI_MEMORY_PROFILER_TRACK_ALLOC_WITH_OWNERSHIP() macro.
*
* @param[in] tracker_handle The handle of the memory tracker
* @param[in] ptr Pointer to the allocated memory or NULL if allocation failed
* @param[in] size The number of bytes allocated, or attempted to allocate
* @param[in] pc The program counter at the location of the allocation
*/
void sli_memory_profiler_track_alloc_with_ownership(sli_memory_tracker_handle_t tracker_handle,
void * ptr,
size_t size,
void * pc);
/**
* @brief Track the freeing of a memory block
*
* NOTE: This function is intended to be called via the
* @ref SLI_MEMORY_PROFILER_TRACK_FREE() macro.
*
* @param[in] tracker_handle The handle of the memory tracker
* @param[in] ptr Pointer to the allocated memory
*/
void sli_memory_profiler_track_free(sli_memory_tracker_handle_t tracker_handle,
void * ptr);
/**
* @brief Track the transfer of memory allocation ownership
*
* NOTE: This function is intended to be called via @ref
* SLI_MEMORY_PROFILER_TRACK_OWNERSHIP() or @ref
* SLI_MEMORY_PROFILER_TRACK_OWNERSHIP_ON_TRACKER() macros.
*
* @param[in] tracker_handle Handle of the tracker level at which the ownership
* is taken. This is used to disambiguate in cases where nested allocations
* start at the same memory location, and the caller is specifically taking
* ownership of one of the outer blocks that may contain smaller allocations
* that have their own (more detailed) owners. If set to
* `SLI_INVALID_MEMORY_TRACKER_HANDLE`, the ownership of the innermost
* allocation is taken.
* @param[in] ptr Pointer to the allocated memory for which ownership is taken.
* The caller may pass a NULL pointer to indicate that the location pointer to
* be @p pc has failed to obtain a valid pointer, for example because a memory
* allocation that was meant to provide the pointer has failed.
* @param[in] pc The program counter at the location that took ownership
*/
void sli_memory_profiler_track_ownership(sli_memory_tracker_handle_t tracker_handle,
void * ptr,
void * pc);
/**
* @brief Trigger the creation of a snapshot of the current state
*
* This function can be used by the device to trigger the analysis software on
* the PC or Mac to take a snapshot of the current state of the allocation
* bookkeeping. This would typically be used by test cases that communicate with
* the device under test and want to synchronously record the state of the
* allocations at a known point in the test sequence.
*
* @param[in] name Short name for the snapshot that is being created. The name
* is immediately sent in the RTT event to the analysis software and does not
* need to be retained in the device.
*/
void sli_memory_profiler_take_snapshot(const char *name);
/**
* @brief Output a generic log event in the RTT event stream
*
* This function is meant to be used for temporary debugging purposes only. When
* debugging a memory leak requires visibility to software actions other than
* memory allocation or free, calls to this generic logging mechanism can be
* added so that the sequence of software events can be seen with respect to the
* allocation and free events that are visible in the normal Memory Profiler
* events.
*
* @param[in] log_id Numeric identifier of this log event. This is passed in the
* RTT event to the analysis tool and the ID appears in the log produced by
* the analyzer but the value is not otherwise used by the Memory Profiler. It
* is the responsibility of the caller to use values that are sufficiently
* unique that the developer can identify the logs.
*
* @param[in] arg1 Arbitrary 32-bit argument that's relevant for the developer
*
* @param[in] arg2 Arbitrary 32-bit argument that's relevant for the developer
*
* @param[in] arg3 Arbitrary 32-bit argument that's relevant for the developer
*
* @param[in] pc The program counter at the location of the log call
*/
void sli_memory_profiler_log(uint32_t log_id,
uint32_t arg1,
uint32_t arg2,
uint32_t arg3,
void * pc);
// The macros expand to their full content only when profiling is included
#if SLI_MEMORY_PROFILER_ENABLE_PROFILING
/**
* @brief Macro to wrap calls to @ref sli_memory_profiler_track_alloc
*/
#define SLI_MEMORY_PROFILER_TRACK_ALLOC(tracker_handle, ptr, size) \
do { \
sli_memory_profiler_track_alloc((tracker_handle), (ptr), (size)); \
} while (0)
/**
* @brief Macro to wrap calls to @ref sli_memory_profiler_track_alloc_with_ownership
*
* If ownership tracking is disabled at build time, the macro reduces to normal
* tracking without ownership.
*/
#if SLI_MEMORY_PROFILER_ENABLE_OWNERSHIP_TRACKING
#define SLI_MEMORY_PROFILER_TRACK_ALLOC_WITH_OWNERSHIP(tracker_handle, ptr, size, pc) \
do { \
void * volatile _pc = (pc); \
sli_memory_profiler_track_alloc_with_ownership((tracker_handle), (ptr), (size), _pc); \
} while (0)
#else // SLI_MEMORY_PROFILER_ENABLE_OWNERSHIP_TRACKING
#define SLI_MEMORY_PROFILER_TRACK_ALLOC_WITH_OWNERSHIP(tracker_handle, ptr, size, pc) \
do { \
(void) (pc); \
sli_memory_profiler_track_alloc((tracker_handle), (ptr), (size)); \
} while (0)
#endif // SLI_MEMORY_PROFILER_ENABLE_OWNERSHIP_TRACKING
/**
* @brief Macro to wrap calls to @ref sli_memory_profiler_track_realloc
*/
#define SLI_MEMORY_PROFILER_TRACK_REALLOC(tracker_handle, ptr, realloced_ptr, size) \
do { \
sli_memory_profiler_track_realloc((tracker_handle), (ptr), (realloced_ptr), (size)); \
} while (0)
/**
* @brief Macro to wrap calls to @ref sli_memory_profiler_track_free
*/
#define SLI_MEMORY_PROFILER_TRACK_FREE(tracker_handle, ptr) \
do { \
sli_memory_profiler_track_free((tracker_handle), (ptr)); \
} while (0)
#else // SLI_MEMORY_PROFILER_ENABLE_PROFILING
// Empty implementation of tracking macros when memory profiling calls are
// excluded at build time
#define SLI_MEMORY_PROFILER_TRACK_ALLOC(tracker_handle, ptr, size) \
do { \
(void) (tracker_handle); \
(void) (ptr); \
(void) (size); \
} while (0)
#define SLI_MEMORY_PROFILER_TRACK_ALLOC_WITH_OWNERSHIP(tracker_handle, ptr, size, pc) \
do { \
(void) (tracker_handle); \
(void) (ptr); \
(void) (size); \
(void) (pc); \
} while (0)
#define SLI_MEMORY_PROFILER_TRACK_REALLOC(tracker_handle, ptr, realloced_ptr, size) \
do { \
(void) (tracker_handle); \
(void) (ptr); \
(void) (realloced_ptr); \
(void) (size); \
} while (0)
#define SLI_MEMORY_PROFILER_TRACK_FREE(tracker_handle, ptr) \
do { \
(void) (tracker_handle); \
(void) (ptr); \
} while (0)
#endif // SLI_MEMORY_PROFILER_ENABLE_PROFILING
// Ownership tracking calls are included based on dedicated configuration
#if SLI_MEMORY_PROFILER_ENABLE_PROFILING && SLI_MEMORY_PROFILER_ENABLE_OWNERSHIP_TRACKING
/**
* @brief Macro to wrap calls to @ref sli_memory_profiler_track_ownership
*/
#define SLI_MEMORY_PROFILER_TRACK_OWNERSHIP(ptr, pc) \
do { \
void * volatile _pc = (pc); \
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE, (ptr), _pc); \
} while (0)
/**
* @brief Macro to wrap calls to @ref sli_memory_profiler_track_ownership
*/
#define SLI_MEMORY_PROFILER_TRACK_OWNERSHIP_ON_TRACKER(tracker_handle, ptr, pc) \
do { \
void * volatile _pc = (pc); \
sli_memory_profiler_track_ownership((tracker_handle), (ptr), _pc); \
} while (0)
#else // SLI_MEMORY_PROFILER_ENABLE_PROFILING && SLI_MEMORY_PROFILER_ENABLE_OWNERSHIP_TRACKING
#define SLI_MEMORY_PROFILER_TRACK_OWNERSHIP(ptr, pc) \
do { \
(void) (ptr); \
(void) (pc); \
} while (0)
#define SLI_MEMORY_PROFILER_TRACK_OWNERSHIP_ON_TRACKER(tracker_handle, ptr, pc) \
do { \
(void) (tracker_handle); \
(void) (ptr); \
(void) (pc); \
} while (0)
#endif // SLI_MEMORY_PROFILER_ENABLE_PROFILING && SLI_MEMORY_PROFILER_ENABLE_OWNERSHIP_TRACKING
/** @} end memory_profiler */
/// @endcond
#ifdef __cplusplus
}
#endif
#endif // SLI_MEMORY_PROFILER_H

View File

@@ -0,0 +1,126 @@
/***************************************************************************//**
* @file
* @brief Stub implementation of memory profiler
*******************************************************************************
* # License
* <b>Copyright 2023 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include "sli_memory_profiler.h"
#include "sl_status.h"
/* Create a memory tracker */
sl_status_t sli_memory_profiler_create_tracker(sli_memory_tracker_handle_t tracker_handle,
const char *description)
{
(void) tracker_handle;
(void) description;
return SL_STATUS_NOT_AVAILABLE;
}
/* Create a pool memory tracker */
sl_status_t sli_memory_profiler_create_pool_tracker(sli_memory_tracker_handle_t tracker_handle,
const char *description,
void* ptr,
size_t size)
{
(void) tracker_handle;
(void) description;
(void) ptr;
(void) size;
return SL_STATUS_NOT_AVAILABLE;
}
/* Add or update a description to a previously created memory tracker */
void sli_memory_profiler_describe_tracker(sli_memory_tracker_handle_t tracker_handle,
const char *description)
{
(void) tracker_handle;
(void) description;
}
/* Delete a memory tracker */
void sli_memory_profiler_delete_tracker(sli_memory_tracker_handle_t tracker_handle)
{
(void) tracker_handle;
}
/* Track the allocation of a memory block */
void sli_memory_profiler_track_alloc(sli_memory_tracker_handle_t tracker_handle, void * ptr, size_t size)
{
(void) tracker_handle;
(void) ptr;
(void) size;
}
/* Track the allocation of a memory block and record ownership */
void sli_memory_profiler_track_alloc_with_ownership(sli_memory_tracker_handle_t tracker_handle,
void * ptr,
size_t size,
void * pc)
{
(void) tracker_handle;
(void) ptr;
(void) size;
(void) pc;
}
/* Track the freeing of a memory block */
void sli_memory_profiler_track_free(sli_memory_tracker_handle_t tracker_handle, void * ptr)
{
(void) tracker_handle;
(void) ptr;
}
/* Track the transfer of memory allocation ownership */
void sli_memory_profiler_track_ownership(sli_memory_tracker_handle_t tracker_handle,
void * ptr,
void * pc)
{
(void) tracker_handle;
(void) ptr;
(void) pc;
}
/* Trigger the creation of a snapshot of the current state */
void sli_memory_profiler_take_snapshot(const char *name)
{
(void) name;
}
/* Send a generic log */
void sli_memory_profiler_log(uint32_t log_id,
uint32_t arg1,
uint32_t arg2,
uint32_t arg3,
void * pc)
{
(void) log_id;
(void) arg1;
(void) arg2;
(void) arg3;
(void) pc;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,212 @@
/***************************************************************************//**
* @file
* @brief Memory Manager Driver's C++ Implementation.
*******************************************************************************
* # License
* <b>Copyright 2023 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include <cstddef>
#include "sl_memory_manager.h"
#include "sl_assert.h"
//--------------------------------------------------------------------------
// MEMORY MANAGER PRE-INITIALIZATION
#if defined (__GNUC__)
/***************************************************************************//**
* Initializes the memory manager.
*
* @note With a C++ application, the memory manager requires to be initialized
* prior to any malloc() done in any static global objects' constructors.
*
* This function is called after copying in RAM the .data section
* (initialized global variables) and zeroing the .bss section (non-initialized
* global variables) and before the main() entry point. The function is placed
* in a special section called .preinit_array. preinit_array hold pointers
* to functions that should be executed before any other initialization
* functions, including C++ static constructors. It allows very early
* initialization tasks that need to be completed before any dynamic linking
* or library initialization occurs.
*
* When sl_memory_init() is called early during the GCC/G++ startup code,
* sl_memory_init() is not called by the component sl_system.
******************************************************************************/
static void sl_memory_preinit(void) {
sl_memory_init();
}
__attribute__((used, section(".preinit_array")))
static void (*preinit_array)(void) = sl_memory_preinit;
#endif
#if defined(__IAR_SYSTEMS_ICC__)
/***************************************************************************//**
* Initializes the memory manager.
*
* @note This special C++ class and its associated global object allows the
* memory manager sl_memory_init() to be initialized prior to any malloc() done
* in any static global objects' constructors.
* It serves the same purpose as the C function sl_memory_preinit() used for
* GCC/G++ early initialization above. It will use the special section
* .preinit_array.
*
* The IAR "#pragma early_dynamic_initialization" marks certain global objects
* for earlier initialization by registering their constructor to the
* .preinit_array section. When sl_memory_init() is called early during the
* IAR startup code (i.e. after copying in RAM the .data section
* (initialized global variables) and zeroing the .bss section (non-initialized
* global variables) and before the main() entry point), sl_memory_init()
* is not called by the component sl_system.
******************************************************************************/
#pragma early_dynamic_initialization
class sl_memory_preinit
{
public:
sl_memory_preinit()
{
sl_memory_init();
}
~sl_memory_preinit()
{
}
};
sl_memory_preinit sl_memory_preinit_obj;
#endif
//--------------------------------------------------------------------------
// GLOBAL C++ NEW/DELETE OVERLOAD
/***************************************************************************//**
* Overloaded new operator.
* Allocates a memory block of at least requested size from the heap.
*
* @param[in] size Size of the block, in bytes.
*
* @return Pointer to allocated block if successful. Null pointer if
* allocation failed.
******************************************************************************/
void *operator new(size_t size)
{
void *block = NULL;
sl_status_t status;
status = sl_memory_alloc(size, BLOCK_TYPE_LONG_TERM, (void **)&block);
if (status != SL_STATUS_OK) {
// Convert C NULL pointer to C++ dedicated type.
block = nullptr;
}
return block;
}
/***************************************************************************//**
* Overloaded delete operator used for single object allocations.
* Frees a previously allocated block back into the heap.
*
* @param[in] ptr Pointer to memory block to be freed.
******************************************************************************/
void operator delete(void *ptr)
{
sl_status_t status;
status = sl_memory_free(ptr);
if (status != SL_STATUS_OK) {
EFM_ASSERT(false);
}
}
/***************************************************************************//**
* Overloaded delete operator used for array of objects allocations.
* Frees a previously allocated block back into the heap.
*
* @param[in] ptr Pointer to memory block to be freed.
******************************************************************************/
void operator delete[](void *ptr)
{
sl_status_t status;
status = sl_memory_free(ptr);
if (status != SL_STATUS_OK) {
EFM_ASSERT(false);
}
}
/***************************************************************************//**
* Overloaded delete operator for single object allocations.
* Frees a previously allocated block back into the heap.
*
* @param[in] ptr Pointer to memory block to be freed.
* @param[in] size Size of block to be freed, in bytes.
*
* @note The -Wsized-deallocation option in G++ is a warning option related
* to C++17 and later versions. This other overloaded version of delete
* is recommended by -Wsized-deallocation option for C++17. Indeed,
* certain standard containers (e.g. vector, list) with C++17 use a delete
* that needs to provide a 'size' parameter. The Memory Manager
* does not provide a free() function taking a 'size' parameter. Hence,
* the 'size' parameter is simply ignored. When compiling with C++11,
* the same containers such as vector, list will use an overloaded delete
* without a 'size' parameter.
* The -Wsized-deallocation option is used to catch situations where
* you might be using the delete operator incorrectly with pointers
* that were not allocated with new or that were allocated with a type
* that doesn't have a suitable operator delete.
******************************************************************************/
void operator delete(void *ptr,
size_t size)
{
sl_status_t status;
(void)size;
status = sl_memory_free(ptr);
if (status != SL_STATUS_OK) {
EFM_ASSERT(false);
}
}
/***************************************************************************//**
* Overloaded delete operator used for array of objects allocations.
* Frees a previously allocated block back into the heap.
*
* @param[in] ptr Pointer to memory block to be freed.
* @param[in] size Size of block to be freed, in bytes.
******************************************************************************/
void operator delete[](void *ptr,
size_t size)
{
sl_status_t status;
(void)size;
status = sl_memory_free(ptr);
if (status != SL_STATUS_OK) {
EFM_ASSERT(false);
}
}

View File

@@ -0,0 +1,377 @@
/***************************************************************************//**
* @file
* @brief Memory Manager Driver's Block Reservation Feature Implementation.
*******************************************************************************
* # License
* <b>Copyright 2023 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdalign.h>
#include "sl_memory_manager_config.h"
#include "sl_memory_manager.h"
#include "sli_memory_manager.h"
#include "sl_assert.h"
#include "sl_bit.h"
#include "sl_common.h"
#if defined(SL_COMPONENT_CATALOG_PRESENT)
#include "sl_component_catalog.h"
#endif
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
#include "sli_memory_profiler.h"
#endif
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* Dynamically reserves a block of memory.
*
* @note (1) This function assumes the 'handle' is provided by the caller:
* - either statically (e.g. as a global variable)
* - or dynamically by calling sl_memory_reservation_handle_alloc().
******************************************************************************/
sl_status_t sl_memory_reserve_block(size_t size,
size_t align,
sl_memory_reservation_t *handle,
void **block)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
// Check proper alignment characteristics.
EFM_ASSERT((align == SL_MEMORY_BLOCK_ALIGN_DEFAULT)
|| (SL_MATH_IS_PWR2(align)
&& (align <= SL_MEMORY_BLOCK_ALIGN_512_BYTES)));
(void) align;
sli_block_metadata_t *free_block_metadata = NULL;
void *reserved_blk = NULL;
size_t current_block_len;
size_t size_real;
size_t size_adjusted;
size_t block_size_remaining;
sl_memory_region_t heap_region = sl_memory_get_heap_region();
#if defined(DEBUG_EFM) || defined(DEBUG_EFM_USER)
reserve_no_retention_first = false;
#endif
// Verify that the handle pointer isn't NULL. See Note #1.
if ((handle == NULL) || (block == NULL)) {
return SL_STATUS_NULL_POINTER;
}
// Check that the block does not exist yet.
if ((handle->block_size != 0) || (handle->block_address != NULL)) {
return SL_STATUS_FAIL;
}
*block = NULL; // No block reserved yet.
if ((size == 0) || (size >= heap_region.size)) {
return SL_STATUS_INVALID_PARAMETER;
}
size_real = SLI_ALIGN_ROUND_UP(size, SLI_BLOCK_ALLOC_MIN_ALIGN);
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
// For block reservations, the size_adjusted contains the metadata.
size_adjusted = sli_memory_find_free_block(size_real, align, BLOCK_TYPE_SHORT_TERM, true, &free_block_metadata);
if ((free_block_metadata == NULL) || (size_adjusted == 0)) {
CORE_EXIT_ATOMIC();
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_alloc_with_ownership(sli_mm_heap_name, NULL, size, return_address);
#endif
return SL_STATUS_ALLOCATION_FAILED;
}
current_block_len = SLI_BLOCK_LEN_DWORD_TO_BYTE(free_block_metadata->length);
// SLI_BLOCK_METADATA_SIZE_BYTE is added to the free block length to get the real remaining size as size_adjusted contains the metadata size.
block_size_remaining = (current_block_len + SLI_BLOCK_METADATA_SIZE_BYTE) - size_adjusted;
// Create a new block = reserved block returned to requester. This new block is the nearest to the heap end.
reserved_blk = (sli_block_metadata_t *)((uint8_t *)free_block_metadata + block_size_remaining);
sli_free_blocks_number--;
// Split free and reserved blocks if possible.
if (block_size_remaining >= SLI_BLOCK_RESERVATION_MIN_SIZE_BYTE) {
// Changes size of free block.
free_block_metadata->length -= SLI_BLOCK_LEN_BYTE_TO_DWORD(size_real);
// Account for the split block that is free.
sli_free_blocks_number++;
} else {
sli_block_metadata_t *neighbour_block = NULL;
// Update next neighbour.
if (free_block_metadata->offset_neighbour_next != 0) {
neighbour_block = (sli_block_metadata_t *)((uint64_t *)free_block_metadata + free_block_metadata->offset_neighbour_next);
if (free_block_metadata->offset_neighbour_prev != 0) {
neighbour_block->offset_neighbour_prev += free_block_metadata->offset_neighbour_prev;
} else {
// Heap start.
neighbour_block->offset_neighbour_next = 0;
}
}
// Update previous neighbour.
if (free_block_metadata->offset_neighbour_prev != 0) {
neighbour_block = (sli_block_metadata_t *)((uint64_t *)free_block_metadata - free_block_metadata->offset_neighbour_prev);
if (free_block_metadata->offset_neighbour_next != 0) {
neighbour_block->offset_neighbour_next += free_block_metadata->offset_neighbour_next;
} else {
// Heap end.
neighbour_block->offset_neighbour_next = 0;
}
}
// Update head pointers accordingly.
sli_update_free_list_heads(neighbour_block, free_block_metadata, true);
}
CORE_EXIT_ATOMIC();
handle->block_size = size;
handle->block_address = reserved_blk;
*block = reserved_blk;
#ifdef SLI_MEMORY_MANAGER_ENABLE_TEST_UTILITIES
// Save the reservation for heap integrity check purposes.
sli_memory_save_reservation_handle(handle, align);
#endif
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_alloc(sli_mm_heap_name, handle->block_address, size_real);
sli_memory_profiler_track_alloc_with_ownership(sli_mm_heap_reservation_name,
handle->block_address,
handle->block_size,
return_address);
#endif
return SL_STATUS_OK;
}
/***************************************************************************//**
* Frees a dynamically reserved block of memory.
*
* @note (1) Block reservation are not part of the double linked-list as they
* don't have metadata next to them compared to a LT/ST block that
* has a metadata with offset. When releasing a reserved block,
* the previous and next blocks that are part of the double linked-list
* need to be found to insert this new free block. The neighbours are
* found by browsing through the linked-list from the heap start until
* a block metadata's address is higher than the block being
* released's address. The previous and next blocks are then saved to
* properly update the double linked-list.
******************************************************************************/
sl_status_t sl_memory_release_block(sl_memory_reservation_t *handle)
{
sl_memory_region_t heap_region = sl_memory_get_heap_region();
uint16_t new_free_block_length;
sli_block_metadata_t *new_free_block = NULL;
sli_block_metadata_t *prev_block = NULL;
sli_block_metadata_t *next_block = NULL;
uint16_t reserved_block_offset;
sli_block_metadata_t *current_metadata = (sli_block_metadata_t *)heap_region.addr;
// Verify that the handle isn't NULL.
if (handle == NULL) {
return SL_STATUS_NULL_POINTER;
}
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_free(sli_mm_heap_name, handle->block_address);
#endif
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
// Find neighbours by searching from the heap start. See Note #1.
while ((uintptr_t)current_metadata < (uintptr_t)handle->block_address) {
prev_block = current_metadata;
if (current_metadata->offset_neighbour_next == 0) {
break;
}
current_metadata = (sli_block_metadata_t *)((uint64_t *)current_metadata + current_metadata->offset_neighbour_next);
}
next_block = ((uintptr_t)current_metadata >= (uintptr_t)handle->block_address) ? current_metadata : NULL;
new_free_block = (sli_block_metadata_t *)handle->block_address;
new_free_block_length = (uint16_t)SLI_BLOCK_LEN_BYTE_TO_DWORD(handle->block_size) - SLI_BLOCK_METADATA_SIZE_DWORD;
// Create a new free block while trying to merge it with the previous and next free blocks if possible.
if (prev_block != NULL) {
// Calculate offset between the reserved block and the previous block's payload address.
reserved_block_offset = (uint16_t)((uint64_t *)handle->block_address - (uint64_t *)prev_block - SLI_BLOCK_METADATA_SIZE_DWORD);
// Then calculate the difference between the above offset and the length of the previous block.
reserved_block_offset -= prev_block->length;
// Make sure there's no reserved block between the freed block and the previous block.
// Layout around the reserved block to free (aka R1) will be:
// |...|Metadata Free block|Data Free block|R1||
if ((prev_block->block_in_use == 0) && (reserved_block_offset < SLI_BLOCK_RESERVATION_MIN_SIZE_DWORD)) {
// New freed block's previous block is free, so merge both free blocks.
new_free_block = prev_block;
prev_block = (sli_block_metadata_t *)((uint64_t *)prev_block - prev_block->offset_neighbour_prev);
new_free_block_length += new_free_block->length + SLI_BLOCK_METADATA_SIZE_DWORD;
} else {
// Create a new free block, because previous block is a dynamic allocation, a reserved block or the start of the heap.
// Layout around the reserved block to free (aka R1) will be:
// |...|Metadata Free block|Data Free block|R2|R1|| or |...|Metadata ST1|Data ST1|R1|| or |...|Metadata LT|Data LT|R1||
sli_free_blocks_number++;
}
}
if (next_block != NULL) {
// Calculate offset between the reserved block and the next block.
reserved_block_offset = (uint16_t)((uint64_t *)next_block - (uint64_t *)handle->block_address);
// Then calculate the difference between the above offset and the size of the block being released.
reserved_block_offset -= SLI_BLOCK_LEN_BYTE_TO_DWORD(handle->block_size);
// Make sure there's no reserved block between the freed block and the next block.
if ((next_block->block_in_use == 0) && (reserved_block_offset < SLI_BLOCK_RESERVATION_MIN_SIZE_DWORD)) {
// New freed block's following block is free, so merge both free blocks.
new_free_block_length += next_block->length + reserved_block_offset + SLI_BLOCK_METADATA_SIZE_DWORD;
// Invalidate the next block metadata.
next_block->length = 0;
// 2 free blocks have been merged, account for 1 free block only.
sli_free_blocks_number--;
if (next_block->offset_neighbour_next != 0) {
// Get next block following current next block.
next_block = (sli_block_metadata_t *)((uint64_t *)next_block + next_block->offset_neighbour_next);
} else {
next_block = NULL;
}
}
}
// Update the new free metadata block accordingly.
sli_memory_metadata_init(new_free_block);
new_free_block->length = new_free_block_length;
if (next_block != NULL) {
new_free_block->offset_neighbour_next = (uint16_t)((uint64_t *)next_block - (uint64_t *)new_free_block);
next_block->offset_neighbour_prev = new_free_block->offset_neighbour_next;
} else {
// Heap end.
new_free_block->offset_neighbour_next = 0;
}
if (prev_block != NULL) {
new_free_block->offset_neighbour_prev = (uint16_t)((uint64_t *)new_free_block - (uint64_t *)prev_block);
prev_block->offset_neighbour_next = new_free_block->offset_neighbour_prev;
} else {
// Heap start.
new_free_block->offset_neighbour_prev = 0;
}
if (sli_free_lt_list_head == NULL // LT list is empty. Freed block becomes the new 1st element.
|| sli_free_lt_list_head > new_free_block // LT list not empty. Verify if freed block becomes the head.
|| sli_free_lt_list_head->length == 0) {
sli_free_lt_list_head = new_free_block;
}
if (sli_free_st_list_head == NULL // ST list is empty. Freed block becomes the new 1st element.
|| sli_free_st_list_head < new_free_block // ST list not empty. Verify if freed block becomes the head.
|| sli_free_st_list_head->length == 0) {
sli_free_st_list_head = new_free_block;
}
// Invalidate handle.
handle->block_address = NULL;
handle->block_size = 0;
CORE_EXIT_ATOMIC();
#ifdef SLI_MEMORY_MANAGER_ENABLE_TEST_UTILITIES
sli_memory_remove_reservation_handle(handle);
#endif
return SL_STATUS_OK;
}
/***************************************************************************//**
* Dynamically allocates a block reservation handle.
******************************************************************************/
sl_status_t sl_memory_reservation_handle_alloc(sl_memory_reservation_t **handle)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
sl_status_t status;
status = sl_memory_alloc(sizeof(sl_memory_reservation_t), BLOCK_TYPE_LONG_TERM, (void**)handle);
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE, *handle, return_address);
#endif
if (status != SL_STATUS_OK) {
return status;
}
// Initialize handle data.
(*handle)->block_address = NULL;
(*handle)->block_size = 0;
return status;
}
/***************************************************************************//**
* Frees a dynamically allocated block reservation handle.
******************************************************************************/
sl_status_t sl_memory_reservation_handle_free(sl_memory_reservation_t *handle)
{
// Check that block has been released before freeing handle.
if ((handle->block_size != 0) || (handle->block_address != NULL)) {
return SL_STATUS_FAIL;
}
return sl_memory_free((void *)handle);
}
/***************************************************************************//**
* Gets the size of the memory reservation handle structure.
******************************************************************************/
uint32_t sl_memory_reservation_handle_get_size(void)
{
return sizeof(sl_memory_reservation_t);
}

View File

@@ -0,0 +1,242 @@
/***************************************************************************//**
* @file
* @brief Memory Manager Driver's Memory Pool Lightweight Feature Implementation.
*******************************************************************************
* # License
* <b>Copyright 2023 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include <string.h>
#include "sl_memory_manager.h"
#include "sli_memory_manager.h"
#include "sl_assert.h"
#include "sl_core.h"
#if defined(SL_COMPONENT_CATALOG_PRESENT)
#include "sl_component_catalog.h"
#endif
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
#include "sli_memory_profiler.h"
#endif
#define SLI_MEM_POOL_OUT_OF_MEMORY 0xFFFFFFFF
#define SLI_MEM_POOL_REQUIRED_PADDING(obj_size) (((sizeof(size_t) - ((obj_size) % sizeof(size_t))) % sizeof(size_t)))
/***************************************************************************//**
* Creates a memory pool.
******************************************************************************/
sl_status_t sl_memory_create_pool(size_t block_size,
uint32_t block_count,
sl_memory_pool_t *pool_handle)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
sl_status_t status = SL_STATUS_OK;
uint8_t *block = NULL;
size_t block_addr;
size_t pool_size;
EFM_ASSERT(block_count != 0);
EFM_ASSERT(block_size != 0);
if (pool_handle == NULL) {
return SL_STATUS_NULL_POINTER;
}
// SLI_MEM_POOL_REQUIRED_PADDING Rounds up to the nearest platform-dependant size. On a 32-bit processor,
// it will be rounded-up to 4 bytes. E.g. 101 bytes will be rounded up to 104 bytes.
pool_handle->block_size = block_size + (uint16_t)SLI_MEM_POOL_REQUIRED_PADDING(block_size);
pool_handle->block_count = block_count;
// Reserve a block in which the entire pool will reside. Uses a long term allocation to keep
// behavior similar to dynamic reservation.
pool_size = pool_handle->block_size * pool_handle->block_count;
status = sl_memory_alloc(pool_size, BLOCK_TYPE_LONG_TERM, (void **)&block);
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE, block, return_address);
#endif
if (status != SL_STATUS_OK) {
return status;
}
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
// Create the tracker for the pool with no description. The code that created
// the pool can add the tracker description if relevant.
sli_memory_profiler_create_pool_tracker(pool_handle, NULL, block, pool_size);
#endif
pool_handle->block_address = (void *)block;
// Returned block pointer not used because its reference is already stored in block_address.
(void)&block;
pool_handle->block_free = (uint32_t *)pool_handle->block_address;
block_addr = (size_t)pool_handle->block_address;
// Populate the list of free blocks except the last block.
for (uint16_t i = 0; i < (block_count - 1); i++) {
*(size_t *)block_addr = block_addr + pool_handle->block_size;
block_addr += pool_handle->block_size;
}
// Last element will indicate out of memory.
*(size_t *)block_addr = SLI_MEM_POOL_OUT_OF_MEMORY;
return status;
}
/***************************************************************************//**
* Deletes a memory pool.
*
* @note The pool_handle provided is neither freed or invalidated. It can be
* reused in a new call to sl_memory_create_pool() to create another pool.
******************************************************************************/
sl_status_t sl_memory_delete_pool(sl_memory_pool_t *pool_handle)
{
sl_status_t status;
// Verify that the handle pointer isn't NULL.
if (pool_handle == NULL) {
return SL_STATUS_NULL_POINTER;
}
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
// Delete the memory tracker
sli_memory_profiler_delete_tracker(pool_handle);
#endif
// Free block.
status = sl_memory_free(pool_handle->block_address);
return status;
}
/***************************************************************************//**
* Allocates a block from a memory pool.
******************************************************************************/
sl_status_t sl_memory_pool_alloc(sl_memory_pool_t *pool_handle,
void **block)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
CORE_DECLARE_IRQ_STATE;
if ((pool_handle == NULL) || (block == NULL)) {
return SL_STATUS_NULL_POINTER;
}
// No block allocated yet.
*block = NULL;
CORE_ENTER_ATOMIC();
if ((size_t)pool_handle->block_free == SLI_MEM_POOL_OUT_OF_MEMORY) {
CORE_EXIT_ATOMIC();
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_alloc_with_ownership(pool_handle, NULL, pool_handle->block_size, return_address);
#endif
return SL_STATUS_EMPTY;
}
// Get the next free block.
void *block_addr = pool_handle->block_free;
// Update the next free block using the address saved in that block.
pool_handle->block_free = (void *)*(size_t *)block_addr;
CORE_EXIT_ATOMIC();
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_alloc_with_ownership(pool_handle, block_addr, pool_handle->block_size, return_address);
#endif
*block = block_addr;
return SL_STATUS_OK;
}
/***************************************************************************//**
* Frees a block from a memory pool.
******************************************************************************/
sl_status_t sl_memory_pool_free(sl_memory_pool_t *pool_handle,
void *block)
{
CORE_DECLARE_IRQ_STATE;
if ((pool_handle == NULL) || (block == NULL)) {
return SL_STATUS_NULL_POINTER;
}
// Validate that the provided address is in the pool payload range.
EFM_ASSERT((block >= pool_handle->block_address) \
&& ((size_t)block <= ((size_t)pool_handle->block_address + (pool_handle->block_size * pool_handle->block_count))));
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_free(pool_handle, block);
#endif
CORE_ENTER_ATOMIC();
// Save the current free block address in this block.
*(size_t *)block = (size_t)pool_handle->block_free;
pool_handle->block_free = block;
CORE_EXIT_ATOMIC();
return SL_STATUS_OK;
}
/***************************************************************************//**
* Gets the count of free blocks in a memory pool.
******************************************************************************/
uint32_t sl_memory_pool_get_free_block_count(const sl_memory_pool_t *pool_handle)
{
uint32_t free_block_count = 0;
uint32_t *free_block;
if (pool_handle == NULL) {
return 0;
}
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
free_block = pool_handle->block_free;
// Go through the free block list and count the number of free blocks remaining.
while ((size_t)free_block != SLI_MEM_POOL_OUT_OF_MEMORY) {
free_block = *(uint32_t **)free_block;
free_block_count++;
}
CORE_EXIT_ATOMIC();
return free_block_count;
}

View File

@@ -0,0 +1,112 @@
/***************************************************************************//**
* @file
* @brief Memory Manager Driver's Memory Pool Common Implementation.
*******************************************************************************
* # License
* <b>Copyright 2024 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include "sl_memory_manager.h"
#if defined(SL_COMPONENT_CATALOG_PRESENT)
#include "sl_component_catalog.h"
#endif
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
#include "sli_memory_profiler.h"
#endif
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* Dynamically allocates a memory pool handle.
******************************************************************************/
sl_status_t sl_memory_pool_handle_alloc(sl_memory_pool_t **pool_handle)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
sl_status_t status;
// Allocate pool_handle as a long-term block.
status = sl_memory_alloc(sizeof(sl_memory_pool_t), BLOCK_TYPE_LONG_TERM, (void **)pool_handle);
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE, *pool_handle, return_address);
#endif
return status;
}
/***************************************************************************//**
* Frees a dynamically allocated memory pool handle.
******************************************************************************/
sl_status_t sl_memory_pool_handle_free(sl_memory_pool_t *pool_handle)
{
sl_status_t status;
// Free memory pool_handle.
status = sl_memory_free((void *)pool_handle);
return status;
}
/***************************************************************************//**
* Gets the size of the memory pool handle structure.
******************************************************************************/
uint32_t sl_memory_pool_handle_get_size(void)
{
return sizeof(sl_memory_pool_t);
}
/***************************************************************************//**
* Gets the total count of blocks in a memory pool.
******************************************************************************/
uint32_t sl_memory_pool_get_total_block_count(const sl_memory_pool_t *pool_handle)
{
if (pool_handle == NULL) {
return 0;
}
return pool_handle->block_count;
}
/***************************************************************************//**
* Gets the count of used blocks in a memory pool.
******************************************************************************/
uint32_t sl_memory_pool_get_used_block_count(const sl_memory_pool_t *pool_handle)
{
uint32_t used_block_count = 0;
if (pool_handle == NULL) {
return 0;
}
used_block_count = pool_handle->block_count - sl_memory_pool_get_free_block_count(pool_handle);
return used_block_count;
}

View File

@@ -0,0 +1,144 @@
/***************************************************************************//**
* @file
* @brief Getters for Heap and stack
*******************************************************************************
* # License
* <b>Copyright 2024 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include <stdint.h>
#include "em_device.h"
#include "sl_memory_manager_region.h"
#include "sl_memory_manager_region_config.h"
#include "sl_component_catalog.h"
#define IAR_HEAP_BLOCK_NAME "MEMORY_MANAGER_HEAP"
// Prevent's compilation errors when building in simulation.
#ifndef __USED
#define __USED
#endif
#if defined(__GNUC__)
// Declare stack object used with GCC.
static char sl_stack[SL_STACK_SIZE] __attribute__ ((aligned(8), used, section(".stack")));
/*
* Declare the base and limit of the full heap region used with GCC to make
* use of otherwise unused memory.
*/
extern char __HeapBase[];
extern char __HeapLimit[];
#elif defined(__ICCARM__)
// Declare stack object used with IAR.
__root char sl_stack[SL_STACK_SIZE] @ ".stack";
#pragma section=IAR_HEAP_BLOCK_NAME
#endif
/***************************************************************************//**
* Gets size and location of the stack.
******************************************************************************/
sl_memory_region_t sl_memory_get_stack_region(void)
{
sl_memory_region_t region;
region.addr = &sl_stack;
region.size = SL_STACK_SIZE;
return region;
}
/***************************************************************************//**
* Gets size and location of the heap.
******************************************************************************/
sl_memory_region_t sl_memory_get_heap_region(void)
{
sl_memory_region_t region;
// Report the actual heap region.
#if defined(__GNUC__)
region.addr = __HeapBase;
region.size = (uintptr_t) __HeapLimit - (uintptr_t) __HeapBase;
#elif defined(__ICCARM__)
region.addr = __section_begin(IAR_HEAP_BLOCK_NAME);
region.size = __section_size(IAR_HEAP_BLOCK_NAME);
#endif
return region;
}
#if defined(__GNUC__)
/***************************************************************************//**
* Extends the process data space.
*
* @param[in] incr Number of bytes to increment/decrement
*
* @return Start of the new space allocated if successful. -1 if error.
*
* @note (1) This is a helper function called by the standard C library
* function malloc(). _sbrk() is used to dynamically change the
* amount of space allocated for the calling process data segment.
* The change is made by allocating the appropriate amount of space
* from the end of heap.
* _sbrk() adds 'incr' bytes to the end of heap and changes
* the allocated space accordingly. 'incr' can be negative, in which
* case the amount of allocated space is decreased.
*
* @note (2) When the Memory Manager (MM) is used, there is no need for
* _sbrk() as there is no possible extension with the MM controlling
* the entire heap size.
* If _sbrk() is called by the standard C library, then the project
* may have used the standard C malloc() function implementation.
* In that case, the MM retarget, wrapping the GCC malloc() to the
* MM sl_malloc(), may have not worked. You may want to double-check
* your project settings.
******************************************************************************/
__USED void * _sbrk(int incr)
{
#if defined(SL_CATALOG_MEMORY_MANAGER_PRESENT)
(void)incr;
// This means there is an issue with the setup of C standard library. See Note #2.
while (1) {
// infinite loop
}
#else
static char *heap_end = __HeapBase;
char *prev_heap_end;
if ((heap_end + incr) > __HeapLimit) {
// Not enough heap
return (void *) -1;
}
prev_heap_end = heap_end;
heap_end += incr;
return prev_heap_end;
#endif
}
#endif

View File

@@ -0,0 +1,378 @@
/***************************************************************************//**
* @file
* @brief Memory Manager Driver's Retarget Implementation.
*******************************************************************************
* # License
* <b>Copyright 2024 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include "sl_memory_manager.h"
#if defined(SL_COMPONENT_CATALOG_PRESENT)
#include "sl_component_catalog.h"
#endif
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
#include "sli_memory_profiler.h"
#endif
#if defined(__GNUC__)
// Wrapping a system function with GCC works by using the linker option '--wrap=symbol'.
// Any undefined reference to "symbol" will be resolved to "__wrap_symbol".
// Any undefined reference to "__real_symbol" will be resolved to "symbol".
// In our case, "__real_symbol" is not really required as the retargeted standard
// C memory functions will call the corresponding Memory Manager (MM)
// native function and not the standard functions again. So it should be seen:
// standard _malloc_r() -> MM sl_malloc()
// standard _free_r() -> MM sl_free()
// standard _calloc_r() -> MM sl_calloc()
// standard _realloc_r() -> MM sl_realloc()
#define STD_LIB_WRAPPER_MALLOC __wrap__malloc_r
#define STD_LIB_WRAPPER_FREE __wrap__free_r
#define STD_LIB_WRAPPER_CALLOC __wrap__calloc_r
#define STD_LIB_WRAPPER_REALLOC __wrap__realloc_r
// The GNU gold linker has an issue with LTO and wrapping, where the symbol is
// stripped even if it is used in the source code, which leads to link-time errors.
// https://sourceware.org/bugzilla/show_bug.cgi?id=24415
// By marking the wrapper as externally_visible, the symbol will not be stripped
// from the final binary, regardless if it is referenced or not in the source code.
#define ATTR_EXT_VIS __attribute__((externally_visible))
// Reentrant parameter.
#define RARG const struct _reent *reent,
#define VOID_RARG (void) reent
#elif defined(__IAR_SYSTEMS_ICC__)
// Wrapping a system function works with IAR by patching symbol definitions using $Super$$ and $Sub$$
// The $Super$$ special pattern identifies the original unpatched function used for calling
// the original function directly.
// The $Sub$$ special pattern identifies the new function that is called instead of the
// original function.
// In our case, $Super$$ is not really required as the retargeted standard
// C memory functions will call the corresponding Memory Manager (MM)
// native function and not the standard functions again.
//
// NOTE: IAR supports three separate heap memory handlers: the basic, the advanced, and the no-free
// heap handlers.
// - If there are calls to heap memory allocation routines in your application, but no calls
// to heap deallocation routines, the linker automatically chooses the no-free heap.
// - If there are calls to heap memory allocation routines in your application, the linker
// automatically chooses the advanced heap.
// - If there are calls to heap memory allocation routines in a library for example, the linker
// automatically chooses the basic heap.
//
// Depending on the heap handler type, IAR will select a different malloc/free/calloc/realloc
// implementation provided by the IAR system library. That's why, there are different sets of
// macros below below to wrap the right IAR standard memory functions with $Sub$$.
// - Basic heap: IAR memory functions are prefixed with "basic_"
// - Advanced heap: IAR memory functions are prefixed with "dl"
// - No Free heap: IAR memory functions are prefixed with "no_free"
//
// For No Free heap, IAR does not provide a free and realloc implementation.
#if (__VER__ == 8050009)
#define STD_LIB_WRAPPER_MALLOC $Sub$$__iar_dlmalloc
#define STD_LIB_WRAPPER_FREE $Sub$$__iar_dlfree
#define STD_LIB_WRAPPER_CALLOC $Sub$$__iar_dlcalloc
#define STD_LIB_WRAPPER_REALLOC $Sub$$__iar_dlrealloc
#elif (__VER__ == 9040001)
#define STD_LIB_WRAPPER_MALLOC $Sub$$__basic_malloc
#define STD_LIB_WRAPPER_FREE $Sub$$__basic_free
#define STD_LIB_WRAPPER_CALLOC $Sub$$__basic_calloc
#define STD_LIB_WRAPPER_REALLOC $Sub$$__basic_realloc
#define STD_LIB_WRAPPER_MALLOC_ADVANCED $Sub$$__iar_dlmalloc
#define STD_LIB_WRAPPER_FREE_ADVANCED $Sub$$__iar_dlfree
#define STD_LIB_WRAPPER_CALLOC_ADVANCED $Sub$$__iar_dlcalloc
#define STD_LIB_WRAPPER_REALLOC_ADVANCED $Sub$$__iar_dlrealloc
#define STD_LIB_WRAPPER_MALLOC_NO_FREE $Sub$$__no_free_malloc
#define STD_LIB_WRAPPER_CALLOC_NO_FREE $Sub$$__no_free_calloc
#else
#error Unsupported IAR compiler version for standard C memory functions retarget
#endif
// Since IAR does not use LTO, resolve the attribute as nothing.
#define ATTR_EXT_VIS
// Since IAR does not use reentrant functions, resolve reentrant parameter to nothing.
#define RARG
#define VOID_RARG
#else
#error Unsupported compiler for standard C memory functions retarget
#endif
#if defined(TEST_MEMORY_MANAGER_RETARGET_PRESENT)
volatile uint32_t retarget_malloc_counter = 0;
volatile uint32_t retarget_free_counter = 0;
volatile uint32_t retarget_calloc_counter = 0;
volatile uint32_t retarget_realloc_counter = 0;
#endif
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* malloc() wrapper. Allocates a memory block of at least requested size from
* the heap.
*
* @param[in] size Size of the block, in bytes.
*
* @return Pointer to allocated block if successful. Null pointer if
* allocation failed.
*
* @note Requesting a block of 0 byte will return a null pointer.
*
* @note All allocated blocks using this function will be considered long-term
* allocations.
******************************************************************************/
ATTR_EXT_VIS void *STD_LIB_WRAPPER_MALLOC(RARG
size_t size)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
VOID_RARG;
void *ptr;
ptr = sl_malloc(size);
#if defined(TEST_MEMORY_MANAGER_RETARGET_PRESENT)
retarget_malloc_counter++;
#endif
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE,
ptr,
return_address);
#endif
return ptr;
}
#if defined(__IAR_SYSTEMS_ICC__) && (__VER__ == 9040001)
void *STD_LIB_WRAPPER_MALLOC_ADVANCED(size_t size)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
void *ptr;
ptr = sl_malloc(size);
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE,
ptr,
return_address);
#endif
return ptr;
}
void *STD_LIB_WRAPPER_MALLOC_NO_FREE(size_t size)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
void *ptr;
ptr = sl_malloc(size);
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE,
ptr,
return_address);
#endif
return ptr;
}
#endif
/***************************************************************************//**
* free() wrapper. Frees a previously allocated block back into the heap.
*
* @param[in] ptr Pointer to memory block to be freed.
*
* @note Passing a null pointer does nothing.
******************************************************************************/
ATTR_EXT_VIS void STD_LIB_WRAPPER_FREE(RARG
void *ptr)
{
VOID_RARG;
sl_free(ptr);
#if defined(TEST_MEMORY_MANAGER_RETARGET_PRESENT)
retarget_free_counter++;
#endif
}
#if defined(__IAR_SYSTEMS_ICC__) && (__VER__ == 9040001)
void STD_LIB_WRAPPER_FREE_ADVANCED(void *ptr)
{
sl_free(ptr);
}
#endif
/***************************************************************************//**
* calloc() wrapper. Dynamically allocates a block of memory cleared to 0.
*
* @param[in] item_count Number of elements to be allocated.
* @param[in] size Size of each elements, in bytes.
*
* @return Pointer to allocated block if successful. Null pointer if
* allocation failed.
*
* @note All allocated blocks using this function will be considered long-term
* allocations.
******************************************************************************/
ATTR_EXT_VIS void *STD_LIB_WRAPPER_CALLOC(RARG
size_t item_count,
size_t size)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
VOID_RARG;
void *ptr;
ptr = sl_calloc(item_count, size);
#if defined(TEST_MEMORY_MANAGER_RETARGET_PRESENT)
retarget_calloc_counter++;
#endif
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE,
ptr,
return_address);
#endif
return ptr;
}
#if defined(__IAR_SYSTEMS_ICC__) && (__VER__ == 9040001)
void *STD_LIB_WRAPPER_CALLOC_ADVANCED(size_t item_count,
size_t size)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
void *ptr;
ptr = sl_calloc(item_count, size);
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE,
ptr,
return_address);
#endif
return ptr;
}
void *STD_LIB_WRAPPER_CALLOC_NO_FREE(size_t item_count,
size_t size)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
void *ptr;
ptr = sl_calloc(item_count, size);
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE,
ptr,
return_address);
#endif
return ptr;
}
#endif
/***************************************************************************//**
* realloc() wrapper. Resizes a previously allocated memory block.
*
* @param[in] ptr Pointer to the allocation to resize. If NULL, behavior
* is same as sl_malloc(), sl_memory_alloc().
* @param[in] size New size of the block, in bytes. If 0, behavior is same as
* sl_free(), sl_memory_free().
*
* @return Pointer to newly allocated block, if successful. Null pointer if
* re-allocation failed.
*
* @note All re-allocated blocks using this function will be considered
* long-term allocations.
*
* @note 'ptr' NULL and 'size' of 0 bytes is an incorrect parameters
* combination. No reallocation will be done by the function as it is
* an error condition.
*
* @note If the new 'size' is the same as the old, the function changes nothing
* and returns the same provided address 'ptr'.
******************************************************************************/
ATTR_EXT_VIS void *STD_LIB_WRAPPER_REALLOC(RARG
void *ptr,
size_t size)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
VOID_RARG;
void *r_ptr;
r_ptr = sl_realloc(ptr, size);
#if defined(TEST_MEMORY_MANAGER_RETARGET_PRESENT)
retarget_realloc_counter++;
#endif
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE,
r_ptr,
return_address);
#endif
return r_ptr;
}
#if defined(__IAR_SYSTEMS_ICC__) && (__VER__ == 9040001)
void *STD_LIB_WRAPPER_REALLOC_ADVANCED(void *ptr,
size_t size)
{
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
void * volatile return_address = sli_memory_profiler_get_return_address();
#endif
void *r_ptr;
r_ptr = sl_realloc(ptr, size);
#if defined(SL_CATALOG_MEMORY_PROFILER_PRESENT)
sli_memory_profiler_track_ownership(SLI_INVALID_MEMORY_TRACKER_HANDLE,
r_ptr,
return_address);
#endif
return r_ptr;
}
#endif

View File

@@ -0,0 +1,340 @@
/***************************************************************************//**
* @file
* @brief Memory Manager Driver API definition.
*******************************************************************************
* # License
* <b>Copyright 2023 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#ifndef SLI_MEMORY_MANAGER_H_
#define SLI_MEMORY_MANAGER_H_
#include "sl_memory_manager.h"
#if defined(SL_COMPONENT_CATALOG_PRESENT)
#include "sl_component_catalog.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*******************************************************************************
********************************* DEFINES *********************************
******************************************************************************/
// Memory Manager integration to SystemView is enabled on GCC builds of
// applications that include the SystemView component
#if defined(SL_CATALOG_SYSTEMVIEW_TRACE_PRESENT) && defined(__GNUC__)
#define SLI_MEMORY_MANAGER_ENABLE_SYSTEMVIEW
#endif
// Minimum block alignment in bytes. 8 bytes is the minimum alignment to account for largest CPU data type
// that can be used in some block allocation scenarios. 64-bit data type may be used to manipulate the
// allocated block. The ARM processor ABI defines data types and byte alignment, and 8-byte alignment
// can be seen for the largest data object type.
#define SLI_BLOCK_ALLOC_MIN_ALIGN SL_MEMORY_BLOCK_ALIGN_8_BYTES
// Minimum block allocation size to avoid creating a block too small while splitting up an allocated block.
// Minimum size is formed from (metadata + payload) size. Size expressed in bytes.
#define SLI_BLOCK_ALLOCATION_MIN_SIZE (SLI_BLOCK_METADATA_SIZE_BYTE + SL_MEMORY_MANAGER_BLOCK_ALLOCATION_MIN_SIZE)
// Minimum block reservation size to avoid creating a block too small while splitting up a reserved block.
// Contrary to block allocations, reservations don't have metadata.
#define SLI_BLOCK_RESERVATION_MIN_SIZE_BYTE SL_MEMORY_MANAGER_BLOCK_ALLOCATION_MIN_SIZE
#define SLI_BLOCK_RESERVATION_MIN_SIZE_DWORD SLI_BLOCK_LEN_BYTE_TO_DWORD(SL_MEMORY_MANAGER_BLOCK_ALLOCATION_MIN_SIZE)
// 64-bit word size (in octets).
#define SLI_WORD_SIZE_64 8u
// 32-bit word size (in octets).
#define SLI_WORD_SIZE_32 4u
#define SLI_DEF_INT_32_NBR_BITS 32u
// 1-byte size (in bits).
#define SLI_DEF_INT_08_NBR_BITS 8u
// Size of block metadata area in different units.
#define SLI_BLOCK_METADATA_SIZE_BYTE sizeof(sli_block_metadata_t)
#define SLI_BLOCK_METADATA_SIZE_DWORD SLI_BLOCK_LEN_BYTE_TO_DWORD(SLI_BLOCK_METADATA_SIZE_BYTE)
// Size of reservation handle area in different units.
#define SLI_RESERVATION_HANDLE_SIZE_BYTE sizeof(sl_memory_reservation_t)
#define SLI_RESERVATION_HANDLE_SIZE_DWORD SLI_BLOCK_LEN_BYTE_TO_DWORD(SLI_RESERVATION_HANDLE_SIZE_BYTE)
// Size of pool handle area in different units.
#define SLI_POOL_HANDLE_SIZE_BYTE sizeof(sl_memory_pool_t)
#define SLI_POOL_HANDLE_SIZE_DWORD SLI_BLOCK_LEN_BYTE_TO_DWORD(SLI_POOL_HANDLE_SIZE_BYTE)
#ifdef SLI_MEMORY_MANAGER_ENABLE_TEST_UTILITIES
#define SLI_MAX_RESERVATION_COUNT 32
#endif
/*******************************************************************************
********************************** MACROS *********************************
******************************************************************************/
// Macros to align a value to the nearest value multiple of the specified alignment
// (rounded up or down). These macros are used for memory addresses requiring an alignment.
#define SLI_ALIGN_ROUND_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
#define SLI_ALIGN_ROUND_DOWN(num, align) ((num) & ~((align) - 1))
// Macros to convert block length in different units (bytes, double words).
// Byte to word will round up to account for extra bytes.
#define SLI_BLOCK_LEN_DWORD_TO_BYTE(len) ((len) * SLI_WORD_SIZE_64)
#define SLI_BLOCK_LEN_BYTE_TO_DWORD(len) ((len + SLI_WORD_SIZE_64 - 1) / SLI_WORD_SIZE_64)
// Macro to test address given a specified data alignment.
#define SLI_ADDR_IS_ALIGNED(ptr, align_byte) (((uintptr_t)(const void *)(ptr)) % (align_byte) == 0)
// Macro to convert from bits to byte.
#define SLI_POOL_BITS_TO_BYTE(bits) (((bits) + 7u) / SLI_DEF_INT_08_NBR_BITS)
/*******************************************************************************
********************************* TYPEDEF *********************************
******************************************************************************/
// Block metadata containing information about allocated block.
// This metadata allows to implement explicit free blocks list.
// NOTE: The metadata size should ideally be a multiple of 8 bytes (see description of
// SLI_BLOCK_ALLOC_MIN_ALIGN for other details) or at least multiple of CPU data size
// (e.g. 4 bytes for 32-bit CPU).
// 'length' is expressed in double words unit. It can described a block up to 512 KB (65535 * 8 bytes).
typedef struct {
uint16_t block_in_use : 1; // Flag indicating if block allocated or not.
uint16_t heap_start_align : 1; // Flag indicating if first block at heap start undergone a data payload adjustment.
#if defined(SLI_MEMORY_MANAGER_ENABLE_SYSTEMVIEW)
uint16_t block_type : 1; // Block type (LT or ST).
uint16_t reserved : 13; // Unallocated for future usage.
#else
uint16_t reserved : 14; // Unallocated for future usage.
#endif
uint16_t length; // Block size (metadata not included just data payload), in double words (64 bit).
uint16_t offset_neighbour_prev; // Offset to previous neighbor, in double words. It includes metadata/payload sizes.
uint16_t offset_neighbour_next; // Offset to next neighbor, in double words.
} sli_block_metadata_t;
/*******************************************************************************
**************************** GLOBAL VARIABLES *****************************
******************************************************************************/
extern sli_block_metadata_t *sli_free_st_list_head;
extern sli_block_metadata_t *sli_free_lt_list_head;
extern uint32_t sli_free_blocks_number;
#if defined(DEBUG_EFM) || defined(DEBUG_EFM_USER)
extern bool reserve_no_retention_first;
#endif
#ifdef SLI_MEMORY_MANAGER_ENABLE_TEST_UTILITIES
extern sl_memory_reservation_t* sli_reservation_handle_ptr_table[];
extern uint32_t sli_reservation_alignment_table[];
extern sl_memory_reservation_t sli_reservation_no_retention_table[];
extern uint32_t sli_reservation_no_retention_alignment_table[];
#endif
// The heap name is also used as the Memory Profiler tracker handle for the heap
// pool managed by the Memory Manager
extern const char sli_mm_heap_name[];
extern const char sli_mm_heap_reservation_name[];
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
/***************************************************************************//**
* Initializes a memory block metadata to some reset values.
*
* @param[in] block_metadata Pointer to block metadata.
******************************************************************************/
void sli_memory_metadata_init(sli_block_metadata_t *block_metadata);
/***************************************************************************//**
* Gets pointer to the first free block of adequate size.
*
* @param[in] size Size of the block, in bytes.
* @param[in] align Required alignment for the block, in bytes.
* @param[in] type Type of block (long-term or short term).
* BLOCK_TYPE_LONG_TERM
* BLOCK_TYPE_SHORT_TERM
* @param[in] block_reservation Indicates if the free block is for a dynamic
* reservation.
* @param[out] block Pointer to variable that will receive the
* start address of the free block.
*
* @return Size of the block adjusted with the alignment.
******************************************************************************/
size_t sli_memory_find_free_block(size_t size,
size_t align,
sl_memory_block_type_t type,
bool block_reservation,
sli_block_metadata_t **block);
/***************************************************************************//**
* Finds the next free block that will become the long-term or short-term head
* pointer.
*
* @param[in] type Type of block (long-term or short term).
* BLOCK_TYPE_LONG_TERM
* BLOCK_TYPE_SHORT_TERM
*
* @param[in] block_start_from Pointer to block where to start searching.
* NULL pointer means start from one of heap
* ends according to the block type.
*
* @return Pointer to the new free block.
******************************************************************************/
sli_block_metadata_t *sli_memory_find_head_free_block(sl_memory_block_type_t type,
sli_block_metadata_t *block_start_from);
/***************************************************************************//**
* Gets long-term head pointer to the first free block.
*
* @return Pointer to first free long-term block.
******************************************************************************/
void *sli_memory_get_longterm_head_ptr(void);
/***************************************************************************//**
* Gets short-term head pointer to the first free block.
*
* @return Pointer to first free short-term block.
******************************************************************************/
void *sli_memory_get_shortterm_head_ptr(void);
/***************************************************************************//**
* Update free lists heads (short and long terms)
*
* @param[in] free_head Block from where to start searching or next free block.
*
* @param[in] condition_block Block condition to check if update is necessary
* or not.
*
* @param[in] search Boolean condition to check if searching the heap for a free
* block is necessary.
******************************************************************************/
void sli_update_free_list_heads(sli_block_metadata_t *free_head,
const sli_block_metadata_t *condition_block,
bool search);
#ifdef SLI_MEMORY_MANAGER_ENABLE_TEST_UTILITIES
/***************************************************************************//**
* Gets the pointer to sl_memory_reservation_t{} by block address.
*
* @param[in] addr Pointer to the block reservation.
*
* @return Pointer to reservation handle.
******************************************************************************/
sl_memory_reservation_t *sli_memory_get_reservation_handle_by_addr(void *addr);
/***************************************************************************//**
* Gets the size of a reservation by block address.
*
* @param[in] addr Pointer to the block reservation.
*
* @return Size of the reservation in bytes.
******************************************************************************/
uint32_t sli_memory_get_reservation_size_by_addr(void *addr);
/***************************************************************************//**
* Get the alignment of a reservation by block address.
*
* @param[in] addr Pointer to the block reservation.
*
* @return Alignment of the reservation in bytes.
******************************************************************************/
uint32_t sli_memory_get_reservation_align_by_addr(void *addr);
/***************************************************************************//**
* Bookkeeps a reservation for profiling purposes.
*
* @param[in] reservation_handle_ptr Pointer to the reservation handle.
* @param[in] align Alignment of the reservation.
*
* @return SL_STATUS_FULL if record is full.
******************************************************************************/
sl_status_t sli_memory_save_reservation_handle(sl_memory_reservation_t *reservation_handle_ptr,
uint32_t align);
/***************************************************************************//**
* Removes a reservation from records.
*
* @param[in] reservation_handle_ptr Pointer to the reservation handle.
*
* @return SL_STATUS_NOT_FOUND if reservation is does not exist in records.
******************************************************************************/
sl_status_t sli_memory_remove_reservation_handle(sl_memory_reservation_t *reservation_handle_ptr);
/***************************************************************************//**
* Bookkeeps a reservation (no retention) for profiling purposes.
*
* @param[in] block_address Pointer to the block reservation.
* @param[in] block_size Size of the reservation.
* @param[in] align Alignment of the reservation.
*
* @return SL_STATUS_NOT_FOUND if reservation is does not exist in records.
******************************************************************************/
sl_status_t sli_memory_save_reservation_no_retention(void * block_address, uint32_t block_size, uint32_t align);
/***************************************************************************//**
* Gets the size of a reservation (no retention) by block address.
*
* @param[in] addr Pointer to the block reservation.
*
* @return Size of the reservation (no retention) in bytes.
******************************************************************************/
uint32_t sli_memory_get_reservation_no_retention_size(void * addr);
/***************************************************************************//**
* Gets the alignment of a reservation (no retention) by block address.
*
* @param[in] addr Pointer to the block reservation.
*
* @return Alignment of the reservation in bytes.
******************************************************************************/
uint32_t sli_memory_get_reservation_no_retention_align(void * addr);
/***************************************************************************//**
* Does a heap integrity check forwards from sli_free_lt_list_head and return
* the pointer to the corrupted sli_block_metadata_t{} (if applicable).
* This could go past reservations so there are checks.
*
* @return Pointer to the corrupted sli_block_metadata_t{}.
******************************************************************************/
sli_block_metadata_t * sli_memory_check_heap_integrity_forwards(void);
/***************************************************************************//**
* Does a heap integrity check backwards from sli_free_st_list_head and return
* the pointer to the corrupted sli_block_metadata_t{} (if applicable).
* This should not go past any reservations, hence there are no checks.
*
* @return Pointer to the corrupted sli_block_metadata_t{}.
******************************************************************************/
sli_block_metadata_t *sli_memory_check_heap_integrity_backwards(void);
#endif /* SLI_MEMORY_MANAGER_ENABLE_TEST_UTILITIES */
#ifdef __cplusplus
}
#endif
#endif /* SLI_MEMORY_MANAGER_H_ */

View File

@@ -0,0 +1,712 @@
/***************************************************************************//**
* @file
* @brief Memory Manager Driver Implementation.
*******************************************************************************
* # License
* <b>Copyright 2023 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdalign.h>
#include "sl_memory_manager_config.h"
#include "sl_memory_manager.h"
#include "sli_memory_manager.h"
#include "sl_assert.h"
#include "sl_bit.h"
#include "sl_common.h"
#if defined(SL_COMPONENT_CATALOG_PRESENT)
#include "sl_component_catalog.h"
#endif
/*******************************************************************************
********************************* DEFINES *********************************
******************************************************************************/
// Minimum block alignment in bytes. 8 bytes is the minimum alignment to account for largest CPU data type
// that can be used in some block allocation scenarios. 64-bit data type may be used to manipulate the
// allocated block. The ARM processor ABI defines data types and byte alignment, and 8-byte alignment
// can be seen for the largest data object type.
#define SLI_BLOCK_ALLOC_MIN_ALIGN SL_MEMORY_BLOCK_ALIGN_8_BYTES
// Minimum block allocation size to avoid creating a block too small while splitting up an allocated block.
// Minimum size is formed from (metadata + payload) size. Size expressed in bytes.
#define SLI_BLOCK_ALLOCATION_MIN_SIZE (SLI_BLOCK_METADATA_SIZE_BYTE + SL_MEMORY_MANAGER_BLOCK_ALLOCATION_MIN_SIZE)
// 64-bit word size (in octets).
#define SLI_WORD_SIZE_64 8u
// Size of metadata area in different units.
#define SLI_BLOCK_METADATA_SIZE_BYTE sizeof(sli_block_metadata_t)
#define SLI_BLOCK_METADATA_SIZE_DWORD SLI_BLOCK_LEN_BYTE_TO_DWORD(SLI_BLOCK_METADATA_SIZE_BYTE)
/*******************************************************************************
*************************** LOCAL VARIABLES ********************************
******************************************************************************/
sli_block_metadata_t *sli_free_lt_list_head;
sli_block_metadata_t *sli_free_st_list_head;
uint32_t sli_free_blocks_number;
#ifdef SLI_MEMORY_MANAGER_ENABLE_TEST_UTILITIES
// Dynamic reservation bookkeeping.
sl_memory_reservation_t *sli_reservation_handle_ptr_table[SLI_MAX_RESERVATION_COUNT] = { NULL };
uint32_t sli_reservation_alignment_table[SLI_MAX_RESERVATION_COUNT] = { 0 };
// Reservation no retention bookkeeping.
// Array of structs instead of pointers to avoid dynamic allocation for handle.
sl_memory_reservation_t sli_reservation_no_retention_table[SLI_MAX_RESERVATION_COUNT] = { 0 };
uint32_t sli_reservation_no_retention_alignment_table[SLI_MAX_RESERVATION_COUNT] = { 0 };
#endif
/*******************************************************************************
*************************** LOCAL FUNCTIONS *******************************
******************************************************************************/
#ifdef SLI_MEMORY_MANAGER_ENABLE_TEST_UTILITIES
/***************************************************************************//**
* Gets the index in sli_reservation_handle_ptr_table[] by block address.
*
* @param[in] addr Pointer to block reservation.
*
* @return Corresponding index in sli_reservation_handle_ptr_table.
******************************************************************************/
static uint32_t get_reservation_ix_by_addr(void *addr)
{
for (uint32_t ix = 0; ix < SLI_MAX_RESERVATION_COUNT; ix++) {
if (sli_reservation_handle_ptr_table[ix] == NULL) {
continue;
}
if (sli_reservation_handle_ptr_table[ix]->block_address == addr) {
return ix;
}
}
return -1;
}
/***************************************************************************//**
* Gets the index in sli_reservation_handle_ptr_table[]
* by reservation handle pointer.
*
* @param[in] reservation_handle_ptr Pointer to reservation handle.
*
* @return Corresponding index in sli_reservation_handle_ptr_table.
******************************************************************************/
static uint32_t get_reservation_ix_by_handle(sl_memory_reservation_t *reservation_handle_ptr)
{
for (uint32_t ix = 0; ix < SLI_MAX_RESERVATION_COUNT; ix++) {
if (sli_reservation_handle_ptr_table[ix] == NULL) {
continue;
}
if (sli_reservation_handle_ptr_table[ix] == reservation_handle_ptr) {
return ix;
}
}
return -1;
}
/***************************************************************************//**
* Get an index of sli_reservation_handle_ptr_table that is free.
*
* @return Index of an empty entry in sli_reservation_handle_ptr_table.
******************************************************************************/
static uint32_t get_available_reservation_handle_ix(void)
{
for (uint32_t ix = 0; ix < SLI_MAX_RESERVATION_COUNT; ix++) {
if (sli_reservation_handle_ptr_table[ix] == NULL) {
return ix;
}
}
return -1;
}
/***************************************************************************//**
* Gets the index in sli_reservation_no_retention_table[] by block address.
*
* @param[in] addr Pointer to block reservation.
*
* @return Corresponding index in sli_reservation_no_retention_table[].
******************************************************************************/
static uint32_t get_reservation_no_retention_ix(void *addr)
{
for (uint32_t ix = 0; ix < SLI_MAX_RESERVATION_COUNT; ix++) {
if (sli_reservation_no_retention_table[ix].block_address == NULL) {
continue;
}
if (sli_reservation_no_retention_table[ix].block_address == addr) {
return ix;
}
}
return -1;
}
/***************************************************************************//**
* Gets an index of sli_reservation_no_retention_table[] that is free.
*
* @return Index of an empty entry in sli_reservation_no_retention_table[].
******************************************************************************/
static uint32_t get_available_reservation_no_retention_ix(void)
{
for (uint32_t ix = 0; ix < SLI_MAX_RESERVATION_COUNT; ix++) {
if (sli_reservation_no_retention_table[ix].block_address == NULL) {
return ix;
}
}
return -1;
}
#endif
/***************************************************************************//**
* Initializes a memory block metadata to some reset values.
******************************************************************************/
void sli_memory_metadata_init(sli_block_metadata_t *block_metadata)
{
block_metadata->block_in_use = 0;
block_metadata->heap_start_align = 0;
block_metadata->reserved = 0;
block_metadata->length = 0;
block_metadata->offset_neighbour_prev = 0;
block_metadata->offset_neighbour_next = 0;
}
/***************************************************************************//**
* Gets pointer pointing to the first free block of adequate size.
*
* @note (1) For a block reservation, there's no metadata next to the
* reserved block. For this reason, when looking for a free block
* large enough to fit a new reserved block, the size of the metadata
* is counted in the available size of the free blocks.
*
* @note (2) For a short-term block, if the required data alignment is greater
* than 8 bytes, the found block size must account for the correct
* alignment of the block data payload. A series of computations is
* done starting from the end of the found block to determine the
* best data offset needed to align the data payload. The worst
* alignment (size_real + block_align) cannot be taken by default
* as it may imply loosing too many bytes in internal fragmentation
* due to the alignment requirement.
******************************************************************************/
size_t sli_memory_find_free_block(size_t size,
size_t align,
sl_memory_block_type_t type,
bool block_reservation,
sli_block_metadata_t **block)
{
sli_block_metadata_t *current_block_metadata = NULL;
void *data_payload = NULL;
size_t size_adjusted = 0;
size_t current_block_len;
size_t block_align = (align == SL_MEMORY_BLOCK_ALIGN_DEFAULT) ? SLI_BLOCK_ALLOC_MIN_ALIGN : align;
size_t data_payload_offset;
bool is_aligned = false;
*block = NULL;
current_block_metadata = (type == BLOCK_TYPE_LONG_TERM) ? sli_free_lt_list_head : sli_free_st_list_head;
if (current_block_metadata == NULL) {
return 0;
}
current_block_len = SLI_BLOCK_LEN_DWORD_TO_BYTE(current_block_metadata->length);
// For a block reservation, add the metadata's size to the free blocks' available memory space. See Note #2.
current_block_len += block_reservation ? SLI_BLOCK_METADATA_SIZE_BYTE : 0;
// Try to find a block to allocate (first-fit).
while (current_block_metadata != NULL) {
if ((!current_block_metadata->block_in_use) && (current_block_len >= size)) {
if (type == BLOCK_TYPE_LONG_TERM) {
// Check alignment requested and ensure size of found block can accommodate worst case alignment.
// For LT, alignment requirement can be verified here whether the block is split or not.
data_payload = (void *)((uint8_t *)current_block_metadata + SLI_BLOCK_METADATA_SIZE_BYTE);
is_aligned = SLI_ADDR_IS_ALIGNED(data_payload, block_align);
data_payload_offset = (uintptr_t)data_payload % block_align;
if (is_aligned || (current_block_len >= (size + data_payload_offset))) {
// Compute remaining block size given an alignment handling or not.
size_adjusted = is_aligned ? size : (size + data_payload_offset);
break;
}
} else {
if (block_align == SLI_BLOCK_ALLOC_MIN_ALIGN) {
// If alignment is 8 bytes (default min alignment), take the requested adjusted size.
size_adjusted = size;
} else {
// If non 8-byte alignment, search the more optimized size accounting for the required alignment. See Note #3.
uint8_t *block_end = (uint8_t *)((uint64_t *)current_block_metadata + SLI_BLOCK_METADATA_SIZE_DWORD + current_block_metadata->length);
data_payload = (void *)(block_end - size);
data_payload = (void *)SLI_ALIGN_ROUND_DOWN(((uintptr_t)data_payload), block_align);
size_adjusted = (size_t)(block_end - (uint8_t *)data_payload);
}
if (current_block_len >= size_adjusted) {
break;
}
}
}
// Get next block.
if (type == BLOCK_TYPE_LONG_TERM) {
if (current_block_metadata->offset_neighbour_next == 0) {
return 0; // End of heap. No block found.
}
// Long-term browsing direction goes from start to end of heap.
current_block_metadata = (sli_block_metadata_t *)((uint64_t *)current_block_metadata + (current_block_metadata->offset_neighbour_next));
} else {
if (current_block_metadata->offset_neighbour_prev == 0) {
return 0; // Start of heap. No block found.
}
// Short-term browsing direction goes from end to start of heap.
current_block_metadata = (sli_block_metadata_t *)((uint64_t *)current_block_metadata - (current_block_metadata->offset_neighbour_prev));
}
current_block_len = SLI_BLOCK_LEN_DWORD_TO_BYTE(current_block_metadata->length);
current_block_len += block_reservation ? SLI_BLOCK_METADATA_SIZE_BYTE : 0;
}
*block = current_block_metadata;
return size_adjusted;
}
/***************************************************************************//**
* Finds the next free block that will become the long-term or short-term head
* pointer.
******************************************************************************/
sli_block_metadata_t *sli_memory_find_head_free_block(sl_memory_block_type_t type,
sli_block_metadata_t *block_start_from)
{
sli_block_metadata_t *current_block_metadata = NULL;
sli_block_metadata_t *free_block_metadata = NULL;
bool search = true;
if (sli_free_blocks_number == 0) {
// No more free blocks.
return NULL;
}
if (block_start_from != NULL) {
// Start searching from the given block.
current_block_metadata = block_start_from;
} else {
// Start searching from heap start (long-term [LT]) or near heap end (short-term [ST]).
// For ST, searching cannot start at the absolute heap end. So the ST head pointer is used as it points
// to the last free block closest to the heap end.
sl_memory_region_t heap_region = sl_memory_get_heap_region();
current_block_metadata = (type == BLOCK_TYPE_LONG_TERM) ? (sli_block_metadata_t *)heap_region.addr : sli_free_st_list_head;
}
// Long-term block: find the first free block closest to the heap start.
// Short-term block: find the first free block closest to the heap end.
do {
if (current_block_metadata->block_in_use == 0) {
free_block_metadata = current_block_metadata;
search = false;
} else if ((type == BLOCK_TYPE_LONG_TERM) && (current_block_metadata->offset_neighbour_next != 0)) {
current_block_metadata = (sli_block_metadata_t *)((uint64_t *)current_block_metadata + (current_block_metadata->offset_neighbour_next));
} else if ((type == BLOCK_TYPE_SHORT_TERM) && (current_block_metadata->offset_neighbour_prev != 0)) {
current_block_metadata = (sli_block_metadata_t *)((uint64_t *)current_block_metadata - (current_block_metadata->offset_neighbour_prev));
} else {
free_block_metadata = NULL;
break;
}
} while (search);
return free_block_metadata;
}
/***************************************************************************//**
* Gets long-term head pointer pointing to the first free block.
******************************************************************************/
void *sli_memory_get_longterm_head_ptr(void)
{
return (void *)sli_free_lt_list_head;
}
/***************************************************************************//**
* Gets short-term head pointer pointing to the first free block.
******************************************************************************/
void *sli_memory_get_shortterm_head_ptr(void)
{
return (void *)sli_free_st_list_head;
}
/***************************************************************************//**
* Update free lists heads (short and long terms).
******************************************************************************/
void sli_update_free_list_heads(sli_block_metadata_t *free_head,
const sli_block_metadata_t *condition_block,
bool search)
{
if (search) {
if ((sli_free_lt_list_head == condition_block) || (condition_block == NULL)) {
sli_free_lt_list_head = sli_memory_find_head_free_block(BLOCK_TYPE_LONG_TERM, free_head);
}
if ((sli_free_st_list_head == condition_block) || (condition_block == NULL)) {
sli_free_st_list_head = sli_memory_find_head_free_block(BLOCK_TYPE_SHORT_TERM, free_head);
}
} else {
if (sli_free_lt_list_head == condition_block) {
sli_free_lt_list_head = free_head;
} else if (free_head < sli_free_lt_list_head) {
sli_free_lt_list_head = free_head;
}
if (sli_free_st_list_head == condition_block) {
sli_free_st_list_head = free_head;
} else if (free_head > sli_free_st_list_head) {
sli_free_st_list_head = free_head;
}
}
}
#ifdef SLI_MEMORY_MANAGER_ENABLE_TEST_UTILITIES
/***************************************************************************//**
* Gets the pointer to sl_memory_reservation_t{} by block address.
******************************************************************************/
sl_memory_reservation_t *sli_memory_get_reservation_handle_by_addr(void *addr)
{
uint32_t reservation_ix;
reservation_ix = get_reservation_ix_by_addr(addr);
if (reservation_ix != (uint32_t)-1) {
return sli_reservation_handle_ptr_table[reservation_ix];
}
return NULL;
}
/***************************************************************************//**
* Gets the size of a reservation by block address.
******************************************************************************/
uint32_t sli_memory_get_reservation_size_by_addr(void *addr)
{
sl_memory_reservation_t * reservation_handle_ptr;
reservation_handle_ptr = sli_memory_get_reservation_handle_by_addr(addr);
if (reservation_handle_ptr != NULL) {
return reservation_handle_ptr->block_size;
}
// Not a reservation, return 0 size.
return 0;
}
/***************************************************************************//**
* Gets the alignment of a reservation by block address.
******************************************************************************/
uint32_t sli_memory_get_reservation_align_by_addr(void *addr)
{
uint32_t reservation_ix = -1;
reservation_ix = get_reservation_ix_by_addr(addr);
if (reservation_ix != (uint32_t)-1) {
return sli_reservation_alignment_table[reservation_ix];
}
return 0;
}
/***************************************************************************//**
* Bookkeeps a reservation for profiling purposes.
******************************************************************************/
sl_status_t sli_memory_save_reservation_handle(sl_memory_reservation_t *reservation_handle_ptr,
uint32_t align)
{
uint32_t reservation_ix = -1;
reservation_ix = get_available_reservation_handle_ix();
if (reservation_ix != (uint32_t)-1) {
sli_reservation_handle_ptr_table[reservation_ix] = reservation_handle_ptr;
sli_reservation_alignment_table[reservation_ix] = align;
return SL_STATUS_OK;
} else {
return SL_STATUS_FULL;
}
}
/***************************************************************************//**
* Removes a reservation from records.
******************************************************************************/
sl_status_t sli_memory_remove_reservation_handle(sl_memory_reservation_t *reservation_handle_ptr)
{
uint32_t reservation_ix = -1;
reservation_ix = get_reservation_ix_by_handle(reservation_handle_ptr);
if (reservation_ix != (uint32_t)-1) {
sli_reservation_handle_ptr_table[reservation_ix] = NULL;
sli_reservation_alignment_table[reservation_ix] = 0;
return SL_STATUS_OK;
} else {
return SL_STATUS_NOT_FOUND;
}
}
/***************************************************************************//**
* Bookkeeps a reservation (no retention) for profiling purposes.
******************************************************************************/
sl_status_t sli_memory_save_reservation_no_retention(void * block_address, uint32_t block_size, uint32_t align)
{
uint32_t reservation_ix = -1;
reservation_ix = get_available_reservation_no_retention_ix();
if (reservation_ix != (uint32_t)-1) {
sli_reservation_no_retention_table[reservation_ix].block_address = block_address;
sli_reservation_no_retention_table[reservation_ix].block_size = block_size;
sli_reservation_no_retention_alignment_table[reservation_ix] = align;
return SL_STATUS_OK;
} else {
return SL_STATUS_FULL;
}
}
/***************************************************************************//**
* Gets the size of a reservation (no retention) by block address.
******************************************************************************/
uint32_t sli_memory_get_reservation_no_retention_size(void * addr)
{
uint32_t reservation_ix = -1;
reservation_ix = get_reservation_no_retention_ix(addr);
if (reservation_ix != (uint32_t)-1) {
return sli_reservation_no_retention_table[reservation_ix].block_size;
}
// Not a reservation (no retention), return 0 size.
return 0;
}
/***************************************************************************//**
* Gets the alignment of a reservation (no retention) by block address.
******************************************************************************/
uint32_t sli_memory_get_reservation_no_retention_align(void * addr)
{
uint32_t reservation_ix = -1;
reservation_ix = get_reservation_no_retention_ix(addr);
if (reservation_ix != (uint32_t)-1) {
return sli_reservation_no_retention_alignment_table[reservation_ix];
}
return 0;
}
/***************************************************************************//**
* Does a heap integrity check forwards from sli_free_lt_list_head and return
* the pointer to the corrupted sli_block_metadata_t{} (if applicable).
* This could go past reservations so there are checks.
******************************************************************************/
sli_block_metadata_t *sli_memory_check_heap_integrity_forwards(void)
{
uint64_t * heap_end_by_metadata = 0;
uint32_t is_corrupted = 0;
uint32_t reservation_size;
uint32_t reservation_size_real;
uint32_t alignment;
sli_block_metadata_t* current = sli_free_lt_list_head;
sl_memory_region_t heap_region;
heap_region = sl_memory_get_heap_region();
while (current != NULL) {
// Reached last block in heap.
if (current->offset_neighbour_next == 0) {
heap_end_by_metadata = ((uint64_t *)current + (current->length + SLI_BLOCK_METADATA_SIZE_DWORD));
// Check if reservation (one or more).
alignment = sli_memory_get_reservation_align_by_addr((void *)heap_end_by_metadata);
reservation_size = sli_memory_get_reservation_size_by_addr((void *)heap_end_by_metadata);
if (alignment == SL_MEMORY_BLOCK_ALIGN_DEFAULT) {
reservation_size_real = reservation_size;
} else {
reservation_size_real = SLI_ALIGN_ROUND_UP(reservation_size, alignment);
}
while (reservation_size != 0) {
heap_end_by_metadata = (uint64_t *)((uint8_t*)heap_end_by_metadata + reservation_size_real);
alignment = sli_memory_get_reservation_align_by_addr((void *)heap_end_by_metadata);
reservation_size = sli_memory_get_reservation_size_by_addr((void *)heap_end_by_metadata);
if (alignment == SL_MEMORY_BLOCK_ALIGN_DEFAULT) {
reservation_size_real = reservation_size;
} else {
reservation_size_real = SLI_ALIGN_ROUND_UP(reservation_size, alignment);
}
}
// Check if reservation no retention (one or more).
// Only needed for forwards.
alignment = sli_memory_get_reservation_no_retention_align((void *)heap_end_by_metadata);
reservation_size = sli_memory_get_reservation_no_retention_size((void *)heap_end_by_metadata);
if (alignment == SL_MEMORY_BLOCK_ALIGN_DEFAULT) {
reservation_size_real = reservation_size;
} else {
reservation_size_real = SLI_ALIGN_ROUND_UP(reservation_size, alignment);
}
while (reservation_size != 0) {
heap_end_by_metadata = (uint64_t *)((uint8_t*)heap_end_by_metadata + reservation_size_real);
alignment = sli_memory_get_reservation_no_retention_align((void *)heap_end_by_metadata);
reservation_size = sli_memory_get_reservation_no_retention_size((void *)heap_end_by_metadata);
if (alignment == SL_MEMORY_BLOCK_ALIGN_DEFAULT) {
reservation_size_real = reservation_size;
} else {
reservation_size_real = SLI_ALIGN_ROUND_UP(reservation_size, alignment);
}
}
if (heap_end_by_metadata != (void *)((uintptr_t)heap_region.addr + heap_region.size)) {
is_corrupted = 1;
}
break;
}
// Calculate the address of the next block using offset and length.
sli_block_metadata_t *next_blk_by_offset = (sli_block_metadata_t *)((uint64_t *)current + (current->offset_neighbour_next));
sli_block_metadata_t *next_blk_by_len = (sli_block_metadata_t *)((uint64_t *)current + (current->length + SLI_BLOCK_METADATA_SIZE_DWORD));
// Check if reservation (one or more).
alignment = sli_memory_get_reservation_align_by_addr((void *)next_blk_by_len);
reservation_size = sli_memory_get_reservation_size_by_addr((void *)next_blk_by_len);
if (alignment == SL_MEMORY_BLOCK_ALIGN_DEFAULT) {
reservation_size_real = reservation_size;
} else {
reservation_size_real = SLI_ALIGN_ROUND_UP(reservation_size, alignment);
}
while (reservation_size != 0) {
next_blk_by_len = (sli_block_metadata_t *)((uint8_t*)next_blk_by_len + reservation_size_real);
alignment = sli_memory_get_reservation_align_by_addr((void *)next_blk_by_len);
reservation_size = sli_memory_get_reservation_size_by_addr((void *)next_blk_by_len);
if (alignment == SL_MEMORY_BLOCK_ALIGN_DEFAULT) {
reservation_size_real = reservation_size;
} else {
reservation_size_real = SLI_ALIGN_ROUND_UP(reservation_size, alignment);
}
}
// Check if reservation no retention (one or more).
// Only needed for forwards.
alignment = sli_memory_get_reservation_no_retention_align((void *)next_blk_by_len);
reservation_size = sli_memory_get_reservation_no_retention_size((void *)next_blk_by_len);
if (alignment == SL_MEMORY_BLOCK_ALIGN_DEFAULT) {
reservation_size_real = reservation_size;
} else {
reservation_size_real = SLI_ALIGN_ROUND_UP(reservation_size, alignment);
}
while (reservation_size != 0) {
next_blk_by_len = (sli_block_metadata_t *)((uint8_t*)next_blk_by_len + reservation_size_real);
alignment = sli_memory_get_reservation_no_retention_align((void *)next_blk_by_len);
reservation_size = sli_memory_get_reservation_no_retention_size((void *)next_blk_by_len);
if (alignment == SL_MEMORY_BLOCK_ALIGN_DEFAULT) {
reservation_size_real = reservation_size;
} else {
reservation_size_real = SLI_ALIGN_ROUND_UP(reservation_size, alignment);
}
}
if (next_blk_by_offset != next_blk_by_len) {
is_corrupted = 1;
break;
} else {
current = next_blk_by_offset;
}
}
if (is_corrupted) {
return (sli_block_metadata_t *)current;
}
return NULL;
}
/***************************************************************************//**
* Does a heap integrity check backwards from sli_free_st_list_head and return
* the pointer to the corrupted sli_block_metadata_t{} (if applicable).
* This should not go past any reservations, hence there are no checks.
******************************************************************************/
sli_block_metadata_t *sli_memory_check_heap_integrity_backwards(void)
{
uint64_t * heap_base_by_metadata = 0;
uint32_t is_corrupted = 0;
uint32_t reservation_size;
uint32_t reservation_size_real;
uint32_t alignment;
sli_block_metadata_t* current = sli_free_st_list_head;
sl_memory_region_t heap_region;
heap_region = sl_memory_get_heap_region();
while (current != NULL) {
// Reached first block in heap.
if (current->offset_neighbour_prev == 0) {
heap_base_by_metadata = ((uint64_t *)current);
if (heap_base_by_metadata != (void *)heap_region.addr) {
is_corrupted = 1;
}
break;
}
// Calculate the address of the current block using offset and length of the previous block.
sli_block_metadata_t *prev_blk_by_offset = (sli_block_metadata_t *)((uint64_t *)current - (current->offset_neighbour_prev));
sli_block_metadata_t *current_by_prev_offset = (sli_block_metadata_t *)((uint64_t *)prev_blk_by_offset + (prev_blk_by_offset->offset_neighbour_next));
sli_block_metadata_t *current_by_prev_len = (sli_block_metadata_t *)((uint64_t *)prev_blk_by_offset + (prev_blk_by_offset->length + SLI_BLOCK_METADATA_SIZE_DWORD));
// Check if reservation (one or more).
// This is required when sli_free_st_list_head has reservations before it.
alignment = sli_memory_get_reservation_align_by_addr((void *)current_by_prev_len);
reservation_size = sli_memory_get_reservation_size_by_addr((void *)current_by_prev_len);
if (alignment == SL_MEMORY_BLOCK_ALIGN_DEFAULT) {
reservation_size_real = reservation_size;
} else {
reservation_size_real = SLI_ALIGN_ROUND_UP(reservation_size, alignment);
}
while (reservation_size != 0) {
current_by_prev_len = (sli_block_metadata_t *)((uint8_t*)current_by_prev_len + reservation_size_real);
alignment = sli_memory_get_reservation_align_by_addr((void *)current_by_prev_len);
reservation_size = sli_memory_get_reservation_size_by_addr((void *)current_by_prev_len);
if (alignment == SL_MEMORY_BLOCK_ALIGN_DEFAULT) {
reservation_size_real = reservation_size;
} else {
reservation_size_real = SLI_ALIGN_ROUND_UP(reservation_size, alignment);
}
}
if (current_by_prev_len != current_by_prev_offset) {
is_corrupted = 1;
break;
} else {
current = prev_blk_by_offset;
}
}
if (is_corrupted) {
return (sli_block_metadata_t *)current;
}
return NULL;
}
#endif /* SLI_MEMORY_MANAGER_ENABLE_TEST_UTILITIES */