Initial commit of firmware
This commit is contained in:
971
Libs/platform/service/memory_manager/inc/sl_memory_manager.h
Normal file
971
Libs/platform/service/memory_manager/inc/sl_memory_manager.h
Normal 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_ */
|
||||
@@ -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_ */
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
1200
Libs/platform/service/memory_manager/src/sl_memory_manager.c
Normal file
1200
Libs/platform/service/memory_manager/src/sl_memory_manager.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)█
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
340
Libs/platform/service/memory_manager/src/sli_memory_manager.h
Normal file
340
Libs/platform/service/memory_manager/src/sli_memory_manager.h
Normal 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_ */
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user