/* ********************************************************************************************************* * uC/LIB * CUSTOM LIBRARY MODULES * * (c) Copyright 2004-2014; Micrium, Inc.; Weston, FL * * All rights reserved. Protected by international copyright laws. * * uC/LIB is provided in source form to registered licensees ONLY. It is * illegal to distribute this source code to any third party unless you receive * written permission by an authorized Micrium representative. Knowledge of * the source code may NOT be used to develop a similar product. * * Please help us continue to provide the Embedded community with the finest * software available. Your honesty is greatly appreciated. * * You can find our product's user manual, API reference, release notes and * more information at: https://doc.micrium.com * * You can contact us at: http://www.micrium.com ********************************************************************************************************* */ /* ********************************************************************************************************* * * STANDARD MEMORY OPERATIONS * * Filename : lib_mem.c * Version : V1.38.01 * Programmer(s) : ITJ * FGK * JFD * FBJ * EJ ********************************************************************************************************* * Note(s) : (1) NO compiler-supplied standard library functions are used in library or product software. * * (a) ALL standard library functions are implemented in the custom library modules : * * (1) \\lib_*.* * * (2) \\Ports\\\lib*_a.* * * where * directory path for custom library software * directory name for specific processor (CPU) * directory name for specific compiler * * (b) Product-specific library functions are implemented in individual products. ********************************************************************************************************* */ /* ********************************************************************************************************* * INCLUDE FILES ********************************************************************************************************* */ #define MICRIUM_SOURCE #define LIB_MEM_MODULE #include "lib_mem.h" #include "lib_math.h" #include "lib_str.h" /* ********************************************************************************************************* * LOCAL DEFINES ********************************************************************************************************* */ /* ********************************************************************************************************* * LOCAL CONSTANTS ********************************************************************************************************* */ /* ********************************************************************************************************* * LOCAL DATA TYPES ********************************************************************************************************* */ /* ********************************************************************************************************* * LOCAL TABLES ********************************************************************************************************* */ /* ********************************************************************************************************* * LOCAL GLOBAL VARIABLES ********************************************************************************************************* */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) #ifndef LIB_MEM_CFG_HEAP_BASE_ADDR CPU_INT08U Mem_Heap[LIB_MEM_CFG_HEAP_SIZE]; /* Mem heap. */ #endif MEM_SEG Mem_SegHeap; /* Heap mem seg. */ #endif MEM_SEG *Mem_SegHeadPtr; /* Ptr to head of seg list. */ /* ********************************************************************************************************* * LOCAL FUNCTION PROTOTYPES ********************************************************************************************************* */ static void Mem_SegCreateCritical (const CPU_CHAR *p_name, MEM_SEG *p_seg, CPU_ADDR seg_base_addr, CPU_SIZE_T padding_align, CPU_SIZE_T size); static MEM_SEG *Mem_SegOverlapChkCritical( CPU_ADDR seg_base_addr, CPU_SIZE_T size, LIB_ERR *p_err); static void *Mem_SegAllocInternal (const CPU_CHAR *p_name, MEM_SEG *p_seg, CPU_SIZE_T size, CPU_SIZE_T align, CPU_SIZE_T padding_align, CPU_SIZE_T *p_bytes_reqd, LIB_ERR *p_err); static void *Mem_SegAllocExtCritical ( MEM_SEG *p_seg, CPU_SIZE_T size, CPU_SIZE_T align, CPU_SIZE_T padding_align, CPU_SIZE_T *p_bytes_reqd, LIB_ERR *p_err); static void Mem_DynPoolCreateInternal(const CPU_CHAR *p_name, MEM_DYN_POOL *p_pool, MEM_SEG *p_seg, CPU_SIZE_T blk_size, CPU_SIZE_T blk_align, CPU_SIZE_T blk_padding_align, CPU_SIZE_T blk_qty_init, CPU_SIZE_T blk_qty_max, LIB_ERR *p_err); #if (LIB_MEM_CFG_DBG_INFO_EN == DEF_ENABLED) static void Mem_SegAllocTrackCritical(const CPU_CHAR *p_name, MEM_SEG *p_seg, CPU_SIZE_T size, LIB_ERR *p_err); #endif #if ((LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) && \ (LIB_MEM_CFG_HEAP_SIZE > 0u)) static CPU_BOOLEAN Mem_PoolBlkIsValidAddr ( MEM_POOL *p_pool, void *p_mem); #endif /* ********************************************************************************************************* * LOCAL CONFIGURATION ERRORS ********************************************************************************************************* */ /* ********************************************************************************************************* ********************************************************************************************************* * GLOBAL FUNCTIONS ********************************************************************************************************* ********************************************************************************************************* */ /* ********************************************************************************************************* * Mem_Init() * * Description : (1) Initializes Memory Management Module : * * (a) Initialize heap memory pool * (b) Initialize memory pool table * * * Argument(s) : none. * * Return(s) : none. * * Caller(s) : Application. * * Note(s) : (2) Mem_Init() MUST be called ... : * * (a) ONLY ONCE from a product's application; ... * (b) BEFORE product's application calls any memory library module function(s) ********************************************************************************************************* */ void Mem_Init (void) { /* ------------------ INIT SEG LIST ------------------- */ Mem_SegHeadPtr = DEF_NULL; #if (LIB_MEM_CFG_HEAP_SIZE > 0u) { LIB_ERR err; CPU_ADDR heap_base_addr; /* ------------------ INIT HEAP SEG ------------------- */ #ifdef LIB_MEM_CFG_HEAP_BASE_ADDR heap_base_addr = LIB_MEM_CFG_HEAP_BASE_ADDR; #else heap_base_addr = (CPU_ADDR)&Mem_Heap[0u]; #endif Mem_SegCreate("Heap", &Mem_SegHeap, /* Create heap seg. */ heap_base_addr, LIB_MEM_CFG_HEAP_SIZE, LIB_MEM_PADDING_ALIGN_NONE, &err); if (err != LIB_MEM_ERR_NONE) { CPU_SW_EXCEPTION(;); } } #endif } /* ********************************************************************************************************* * Mem_Clr() * * Description : Clears data buffer (see Note #2). * * Argument(s) : pmem Pointer to memory buffer to clear. * * size Number of data buffer octets to clear (see Note #1). * * Return(s) : none. * * Caller(s) : Application. * * Note(s) : (1) Null clears allowed (i.e. zero-length clears). * * See also 'Mem_Set() Note #1'. * * (2) Clear data by setting each data octet to 0. ********************************************************************************************************* */ void Mem_Clr (void *pmem, CPU_SIZE_T size) { Mem_Set(pmem, 0u, /* See Note #2. */ size); } /* ********************************************************************************************************* * Mem_Set() * * Description : Fills data buffer with specified data octet. * * Argument(s) : pmem Pointer to memory buffer to fill with specified data octet. * * data_val Data fill octet value. * * size Number of data buffer octets to fill (see Note #1). * * Return(s) : none. * * Caller(s) : Application. * * Note(s) : (1) Null sets allowed (i.e. zero-length sets). * * (2) For best CPU performance, optimized to fill data buffer using 'CPU_ALIGN'-sized data * words. Since many word-aligned processors REQUIRE that multi-octet words be accessed on * word-aligned addresses, 'CPU_ALIGN'-sized words MUST be accessed on 'CPU_ALIGN'd * addresses. * * (3) Modulo arithmetic is used to determine whether a memory buffer starts on a 'CPU_ALIGN' * address boundary. * * Modulo arithmetic in ANSI-C REQUIREs operations performed on integer values. Thus * address values MUST be cast to an appropriately-sized integer value PRIOR to any * 'mem_align_mod' arithmetic operation. ********************************************************************************************************* */ void Mem_Set (void *pmem, CPU_INT08U data_val, CPU_SIZE_T size) { CPU_SIZE_T size_rem; CPU_ALIGN data_align; CPU_ALIGN *pmem_align; CPU_INT08U *pmem_08; CPU_DATA mem_align_mod; CPU_DATA i; #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (size < 1) { /* See Note #1. */ return; } if (pmem == (void *)0) { return; } #endif data_align = 0u; for (i = 0u; i < sizeof(CPU_ALIGN); i++) { /* Fill each data_align octet with data val. */ data_align <<= DEF_OCTET_NBR_BITS; data_align |= (CPU_ALIGN)data_val; } size_rem = size; mem_align_mod = (CPU_INT08U)((CPU_ADDR)pmem % sizeof(CPU_ALIGN)); /* See Note #3. */ pmem_08 = (CPU_INT08U *)pmem; if (mem_align_mod != 0u) { /* If leading octets avail, ... */ i = mem_align_mod; while ((size_rem > 0) && /* ... start mem buf fill with leading octets ... */ (i < sizeof(CPU_ALIGN ))) { /* ... until next CPU_ALIGN word boundary. */ *pmem_08++ = data_val; size_rem -= sizeof(CPU_INT08U); i++; } } pmem_align = (CPU_ALIGN *)pmem_08; /* See Note #2. */ while (size_rem >= sizeof(CPU_ALIGN)) { /* While mem buf aligned on CPU_ALIGN word boundaries, */ *pmem_align++ = data_align; /* ... fill mem buf with CPU_ALIGN-sized data. */ size_rem -= sizeof(CPU_ALIGN); } pmem_08 = (CPU_INT08U *)pmem_align; while (size_rem > 0) { /* Finish mem buf fill with trailing octets. */ *pmem_08++ = data_val; size_rem -= sizeof(CPU_INT08U); } } /* ********************************************************************************************************* * Mem_Copy() * * Description : Copies data octets from one memory buffer to another memory buffer. * * Argument(s) : pdest Pointer to destination memory buffer. * * psrc Pointer to source memory buffer. * * size Number of octets to copy (see Note #1). * * Return(s) : none. * * Caller(s) : Application. * * Note(s) : (1) Null copies allowed (i.e. zero-length copies). * * (2) Memory buffers NOT checked for overlapping. * * (a) IEEE Std 1003.1, 2004 Edition, Section 'memcpy() : DESCRIPTION' states that "if * copying takes place between objects that overlap, the behavior is undefined". * * (b) However, data octets from a source memory buffer at a higher address value SHOULD * successfully copy to a destination memory buffer at a lower address value even * if any octets of the memory buffers overlap as long as no individual, atomic CPU * word copy overlaps. * * Since Mem_Copy() performs the data octet copy via 'CPU_ALIGN'-sized words &/or * octets; & since 'CPU_ALIGN'-sized words MUST be accessed on word-aligned addresses * (see Note #3b), neither 'CPU_ALIGN'-sized words nor octets at unique addresses can * ever overlap. * * Therefore, Mem_Copy() SHOULD be able to successfully copy overlapping memory * buffers as long as the source memory buffer is at a higher address value than the * destination memory buffer. * * (3) For best CPU performance, optimized to copy data buffer using 'CPU_ALIGN'-sized data * words. Since many word-aligned processors REQUIRE that multi-octet words be accessed on * word-aligned addresses, 'CPU_ALIGN'-sized words MUST be accessed on 'CPU_ALIGN'd * addresses. * * (4) Modulo arithmetic is used to determine whether a memory buffer starts on a 'CPU_ALIGN' * address boundary. * * Modulo arithmetic in ANSI-C REQUIREs operations performed on integer values. Thus * address values MUST be cast to an appropriately-sized integer value PRIOR to any * 'mem_align_mod' arithmetic operation. ********************************************************************************************************* */ #if (LIB_MEM_CFG_OPTIMIZE_ASM_EN != DEF_ENABLED) void Mem_Copy ( void *pdest, const void *psrc, CPU_SIZE_T size) { CPU_SIZE_T size_rem; CPU_SIZE_T mem_gap_octets; CPU_ALIGN *pmem_align_dest; const CPU_ALIGN *pmem_align_src; CPU_INT08U *pmem_08_dest; const CPU_INT08U *pmem_08_src; CPU_DATA i; CPU_DATA mem_align_mod_dest; CPU_DATA mem_align_mod_src; CPU_BOOLEAN mem_aligned; #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (size < 1) { /* See Note #1. */ return; } if (pdest == (void *)0) { return; } if (psrc == (void *)0) { return; } #endif size_rem = size; pmem_08_dest = ( CPU_INT08U *)pdest; pmem_08_src = (const CPU_INT08U *)psrc; mem_gap_octets = pmem_08_src - pmem_08_dest; if (mem_gap_octets >= sizeof(CPU_ALIGN)) { /* Avoid bufs overlap. */ /* See Note #4. */ mem_align_mod_dest = (CPU_INT08U)((CPU_ADDR)pmem_08_dest % sizeof(CPU_ALIGN)); mem_align_mod_src = (CPU_INT08U)((CPU_ADDR)pmem_08_src % sizeof(CPU_ALIGN)); mem_aligned = (mem_align_mod_dest == mem_align_mod_src) ? DEF_YES : DEF_NO; if (mem_aligned == DEF_YES) { /* If mem bufs' alignment offset equal, ... */ /* ... optimize copy for mem buf alignment. */ if (mem_align_mod_dest != 0u) { /* If leading octets avail, ... */ i = mem_align_mod_dest; while ((size_rem > 0) && /* ... start mem buf copy with leading octets ... */ (i < sizeof(CPU_ALIGN ))) { /* ... until next CPU_ALIGN word boundary. */ *pmem_08_dest++ = *pmem_08_src++; size_rem -= sizeof(CPU_INT08U); i++; } } pmem_align_dest = ( CPU_ALIGN *)pmem_08_dest; /* See Note #3. */ pmem_align_src = (const CPU_ALIGN *)pmem_08_src; while (size_rem >= sizeof(CPU_ALIGN)) { /* While mem bufs aligned on CPU_ALIGN word boundaries, */ *pmem_align_dest++ = *pmem_align_src++; /* ... copy psrc to pdest with CPU_ALIGN-sized words. */ size_rem -= sizeof(CPU_ALIGN); } pmem_08_dest = ( CPU_INT08U *)pmem_align_dest; pmem_08_src = (const CPU_INT08U *)pmem_align_src; } } while (size_rem > 0) { /* For unaligned mem bufs or trailing octets, ... */ *pmem_08_dest++ = *pmem_08_src++; /* ... copy psrc to pdest by octets. */ size_rem -= sizeof(CPU_INT08U); } } #endif /* ********************************************************************************************************* * Mem_Move() * * Description : Moves data octets from one memory buffer to another memory buffer, or within the same * memory buffer. Overlapping is correctly handled for all move operations. * * Argument(s) : pdest Pointer to destination memory buffer. * * psrc Pointer to source memory buffer. * * size Number of octets to move (see Note #1). * * Return(s) : none. * * Caller(s) : Application. * * Note(s) : (1) Null move operations allowed (i.e. zero-length). * * (2) Memory buffers checked for overlapping. * * (3) For best CPU performance, optimized to copy data buffer using 'CPU_ALIGN'-sized data * words. Since many word-aligned processors REQUIRE that multi-octet words be accessed on * word-aligned addresses, 'CPU_ALIGN'-sized words MUST be accessed on 'CPU_ALIGN'd * addresses. * * (4) Modulo arithmetic is used to determine whether a memory buffer starts on a 'CPU_ALIGN' * address boundary. * * Modulo arithmetic in ANSI-C REQUIREs operations performed on integer values. Thus * address values MUST be cast to an appropriately-sized integer value PRIOR to any * 'mem_align_mod' arithmetic operation. ********************************************************************************************************* */ void Mem_Move ( void *pdest, const void *psrc, CPU_SIZE_T size) { CPU_SIZE_T size_rem; CPU_SIZE_T mem_gap_octets; CPU_ALIGN *pmem_align_dest; const CPU_ALIGN *pmem_align_src; CPU_INT08U *pmem_08_dest; const CPU_INT08U *pmem_08_src; CPU_INT08S i; CPU_DATA mem_align_mod_dest; CPU_DATA mem_align_mod_src; CPU_BOOLEAN mem_aligned; #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (size < 1) { return; } if (pdest == (void *)0) { return; } if (psrc == (void *)0) { return; } #endif pmem_08_src = (const CPU_INT08U *)psrc; pmem_08_dest = ( CPU_INT08U *)pdest; if (pmem_08_src > pmem_08_dest) { Mem_Copy(pdest, psrc, size); return; } size_rem = size; pmem_08_dest = ( CPU_INT08U *)pdest + size - 1; pmem_08_src = (const CPU_INT08U *)psrc + size - 1; mem_gap_octets = pmem_08_dest - pmem_08_src; if (mem_gap_octets >= sizeof(CPU_ALIGN)) { /* Avoid bufs overlap. */ /* See Note #4. */ mem_align_mod_dest = (CPU_INT08U)((CPU_ADDR)pmem_08_dest % sizeof(CPU_ALIGN)); mem_align_mod_src = (CPU_INT08U)((CPU_ADDR)pmem_08_src % sizeof(CPU_ALIGN)); mem_aligned = (mem_align_mod_dest == mem_align_mod_src) ? DEF_YES : DEF_NO; if (mem_aligned == DEF_YES) { /* If mem bufs' alignment offset equal, ... */ /* ... optimize copy for mem buf alignment. */ if (mem_align_mod_dest != (sizeof(CPU_ALIGN) - 1)) {/* If leading octets avail, ... */ i = mem_align_mod_dest; while ((size_rem > 0) && /* ... start mem buf copy with leading octets ... */ (i >= 0)) { /* ... until next CPU_ALIGN word boundary. */ *pmem_08_dest-- = *pmem_08_src--; size_rem -= sizeof(CPU_INT08U); i--; } } /* See Note #3. */ pmem_align_dest = ( CPU_ALIGN *)((CPU_INT08U *)pmem_08_dest - sizeof(CPU_ALIGN) + 1); pmem_align_src = (const CPU_ALIGN *)((CPU_INT08U *)pmem_08_src - sizeof(CPU_ALIGN) + 1); while (size_rem >= sizeof(CPU_ALIGN)) { /* While mem bufs aligned on CPU_ALIGN word boundaries, */ *pmem_align_dest-- = *pmem_align_src--; /* ... copy psrc to pdest with CPU_ALIGN-sized words. */ size_rem -= sizeof(CPU_ALIGN); } pmem_08_dest = ( CPU_INT08U *)pmem_align_dest + sizeof(CPU_ALIGN) - 1; pmem_08_src = (const CPU_INT08U *)pmem_align_src + sizeof(CPU_ALIGN) - 1; } } while (size_rem > 0) { /* For unaligned mem bufs or trailing octets, ... */ *pmem_08_dest-- = *pmem_08_src--; /* ... copy psrc to pdest by octets. */ size_rem -= sizeof(CPU_INT08U); } } /* ********************************************************************************************************* * Mem_Cmp() * * Description : Verifies that ALL data octets in two memory buffers are identical in sequence. * * Argument(s) : p1_mem Pointer to first memory buffer. * * p2_mem Pointer to second memory buffer. * * size Number of data buffer octets to compare (see Note #1). * * Return(s) : DEF_YES, if 'size' number of data octets are identical in both memory buffers. * * DEF_NO, otherwise. * * Caller(s) : Application. * * Note(s) : (1) Null compares allowed (i.e. zero-length compares); 'DEF_YES' returned to indicate * identical null compare. * * (2) Many memory buffer comparisons vary ONLY in the least significant octets -- e.g. * network address buffers. Consequently, memory buffer comparison is more efficient * if the comparison starts from the end of the memory buffers which will abort sooner * on dissimilar memory buffers that vary only in the least significant octets. * * (3) For best CPU performance, optimized to compare data buffers using 'CPU_ALIGN'-sized * data words. Since many word-aligned processors REQUIRE that multi-octet words be accessed on * word-aligned addresses, 'CPU_ALIGN'-sized words MUST be accessed on 'CPU_ALIGN'd * addresses. * * (4) Modulo arithmetic is used to determine whether a memory buffer starts on a 'CPU_ALIGN' * address boundary. * * Modulo arithmetic in ANSI-C REQUIREs operations performed on integer values. Thus * address values MUST be cast to an appropriately-sized integer value PRIOR to any * 'mem_align_mod' arithmetic operation. ********************************************************************************************************* */ CPU_BOOLEAN Mem_Cmp (const void *p1_mem, const void *p2_mem, CPU_SIZE_T size) { CPU_SIZE_T size_rem; CPU_ALIGN *p1_mem_align; CPU_ALIGN *p2_mem_align; const CPU_INT08U *p1_mem_08; const CPU_INT08U *p2_mem_08; CPU_DATA i; CPU_DATA mem_align_mod_1; CPU_DATA mem_align_mod_2; CPU_BOOLEAN mem_aligned; CPU_BOOLEAN mem_cmp; if (size < 1) { /* See Note #1. */ return (DEF_YES); } if (p1_mem == (void *)0) { return (DEF_NO); } if (p2_mem == (void *)0) { return (DEF_NO); } mem_cmp = DEF_YES; /* Assume mem bufs are identical until cmp fails. */ size_rem = size; /* Start @ end of mem bufs (see Note #2). */ p1_mem_08 = (const CPU_INT08U *)p1_mem + size; p2_mem_08 = (const CPU_INT08U *)p2_mem + size; /* See Note #4. */ mem_align_mod_1 = (CPU_INT08U)((CPU_ADDR)p1_mem_08 % sizeof(CPU_ALIGN)); mem_align_mod_2 = (CPU_INT08U)((CPU_ADDR)p2_mem_08 % sizeof(CPU_ALIGN)); mem_aligned = (mem_align_mod_1 == mem_align_mod_2) ? DEF_YES : DEF_NO; if (mem_aligned == DEF_YES) { /* If mem bufs' alignment offset equal, ... */ /* ... optimize cmp for mem buf alignment. */ if (mem_align_mod_1 != 0u) { /* If trailing octets avail, ... */ i = mem_align_mod_1; while ((mem_cmp == DEF_YES) && /* ... cmp mem bufs while identical & ... */ (size_rem > 0) && /* ... start mem buf cmp with trailing octets ... */ (i > 0)) { /* ... until next CPU_ALIGN word boundary. */ p1_mem_08--; p2_mem_08--; if (*p1_mem_08 != *p2_mem_08) { /* If ANY data octet(s) NOT identical, cmp fails. */ mem_cmp = DEF_NO; } size_rem -= sizeof(CPU_INT08U); i--; } } if (mem_cmp == DEF_YES) { /* If cmp still identical, cmp aligned mem bufs. */ p1_mem_align = (CPU_ALIGN *)p1_mem_08; /* See Note #3. */ p2_mem_align = (CPU_ALIGN *)p2_mem_08; while ((mem_cmp == DEF_YES) && /* Cmp mem bufs while identical & ... */ (size_rem >= sizeof(CPU_ALIGN))) { /* ... mem bufs aligned on CPU_ALIGN word boundaries. */ p1_mem_align--; p2_mem_align--; if (*p1_mem_align != *p2_mem_align) { /* If ANY data octet(s) NOT identical, cmp fails. */ mem_cmp = DEF_NO; } size_rem -= sizeof(CPU_ALIGN); } p1_mem_08 = (CPU_INT08U *)p1_mem_align; p2_mem_08 = (CPU_INT08U *)p2_mem_align; } } while ((mem_cmp == DEF_YES) && /* Cmp mem bufs while identical ... */ (size_rem > 0)) { /* ... for unaligned mem bufs or trailing octets. */ p1_mem_08--; p2_mem_08--; if (*p1_mem_08 != *p2_mem_08) { /* If ANY data octet(s) NOT identical, cmp fails. */ mem_cmp = DEF_NO; } size_rem -= sizeof(CPU_INT08U); } return (mem_cmp); } /* ********************************************************************************************************* * Mem_HeapAlloc() * * Description : Allocates a memory block from the heap memory segment. * * Argument(s) : size Size of memory block to allocate (in bytes). * * align Alignment of memory block to specific word boundary (in bytes). * * p_bytes_reqd Optional pointer to a variable to ... : * * (a) Return the number of bytes required to successfully * allocate the memory block, if any error(s); * (b) Return 0, otherwise. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_HEAP_EMPTY No more memory available on heap. * * ---------------------RETURNED BY Mem_SegAllocInternal()--------------------- * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory block alignment requested. * LIB_MEM_ERR_INVALID_MEM_SIZE Invalid memory block size specified. * LIB_MEM_ERR_NULL_PTR Error or segment data pointer NULL. * * Return(s) : Pointer to memory block, if NO error(s). * * Pointer to NULL, otherwise. * * Caller(s) : Application. * * Note(s) : (1) Pointers to variables that return values MUST be initialized PRIOR to all other * validation or function handling in case of any error(s). * * (2) This function is DEPRECATED and will be removed in a future version of this product. * Mem_SegAlloc(), Mem_SegAllocExt() or Mem_SegAllocHW() should be used instead. ********************************************************************************************************* */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) void *Mem_HeapAlloc (CPU_SIZE_T size, CPU_SIZE_T align, CPU_SIZE_T *p_bytes_reqd, LIB_ERR *p_err) { void *p_mem; p_mem = Mem_SegAllocInternal(DEF_NULL, &Mem_SegHeap, size, align, LIB_MEM_CFG_HEAP_PADDING_ALIGN, p_bytes_reqd, p_err); if (*p_err == LIB_MEM_ERR_SEG_OVF) { *p_err = LIB_MEM_ERR_HEAP_OVF; } return (p_mem); } #endif /* ********************************************************************************************************* * Mem_HeapGetSizeRem() * * Description : Gets remaining heap memory size available to allocate. * * Argument(s) : align Desired word boundary alignment (in bytes) to return remaining memory size from. * * p_err Pointer to variable that will receive the return error code from this function * * LIB_MEM_ERR_NONE Operation was successful. * * --------------------RETURNED BY Mem_SegRemSizeGet()-------------------- * LIB_MEM_ERR_NULL_PTR Segment data pointer NULL. * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory alignment. * * Return(s) : Remaining heap memory size (in bytes), if NO error(s). * * 0, otherwise. * * Caller(s) : Application. * * Note(s) : (1) This function is DEPRECATED and will be removed in a future version of this product. * Mem_SegRemSizeGet() should be used instead. ********************************************************************************************************* */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) CPU_SIZE_T Mem_HeapGetSizeRem (CPU_SIZE_T align, LIB_ERR *p_err) { CPU_SIZE_T rem_size; rem_size = Mem_SegRemSizeGet(&Mem_SegHeap, align, DEF_NULL, p_err); if (*p_err != LIB_MEM_ERR_NONE) { return (0u); } return (rem_size); } #endif /* ********************************************************************************************************* * Mem_SegCreate() * * Description : Creates a new memory segment to be used for runtime memory allocation. * * Argument(s) : p_name Pointer to segment name. * * p_seg Pointer to segment data. Must be allocated by caller. * * seg_base_addr Address of segment's first byte. * * size Total size of segment, in bytes. * * padding_align Padding alignment, in bytes, that will be added to any allocated buffer from * this memory segment. MUST be a power of 2. LIB_MEM_PADDING_ALIGN_NONE * means no padding. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_INVALID_SEG_SIZE Invalid segment size specified. * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid padding alignment. * LIB_MEM_ERR_NULL_PTR Error or segment data pointer NULL. * * -------------------RETURNED BY Mem_SegOverlapChkCritical()------------------- * LIB_MEM_ERR_INVALID_SEG_OVERLAP Segment overlaps another existing segment. * LIB_MEM_ERR_INVALID_SEG_EXISTS Segment already exists. * * Return(s) : None. * * Caller(s) : Application. * * Note(s) : (1) New segments are checked for overlap with existing segments. A critical section needs * to be maintained during the whole list search and add procedure to prevent a reentrant * call from creating another segment overlapping with the one being added. ********************************************************************************************************* */ void Mem_SegCreate (const CPU_CHAR *p_name, MEM_SEG *p_seg, CPU_ADDR seg_base_addr, CPU_SIZE_T size, CPU_SIZE_T padding_align, LIB_ERR *p_err) { CPU_SR_ALLOC(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (p_err == DEF_NULL) { /* Chk for null err ptr. */ CPU_SW_EXCEPTION(;); } if (p_seg == DEF_NULL) { /* Chk for null seg ptr. */ *p_err = LIB_MEM_ERR_NULL_PTR; return; } if (size < 1u) { /* Chk for invalid sized seg. */ *p_err = LIB_MEM_ERR_INVALID_SEG_SIZE; return; } /* Chk for addr space ovf. */ if (seg_base_addr + (size - 1u) < seg_base_addr) { *p_err = LIB_MEM_ERR_INVALID_SEG_SIZE; return; } if ((padding_align != LIB_MEM_PADDING_ALIGN_NONE) && (MATH_IS_PWR2(padding_align) != DEF_YES)) { *p_err = LIB_MEM_ERR_INVALID_MEM_ALIGN; return; } #endif CPU_CRITICAL_ENTER(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) (void)Mem_SegOverlapChkCritical(seg_base_addr, /* Chk for overlap. */ size, p_err); if (*p_err != LIB_MEM_ERR_NONE) { CPU_CRITICAL_EXIT(); return; } #endif Mem_SegCreateCritical(p_name, /* Create seg. */ p_seg, seg_base_addr, padding_align, size); CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_NONE; } /* ********************************************************************************************************* * Mem_SegClr() * * Description : Clears a memory segment. * * Argument(s) : p_seg Pointer to segment data. Must be allocated by caller. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_NULL_PTR Segment data pointer NULL. * * Return(s) : None. * * Caller(s) : Application. * * Note(s) : (1) This function must be used with extreme caution. It must only be called on memory * segments that are no longer used. * * (2) This function is disabled when debug mode is enabled to avoid heap memory leaks. ********************************************************************************************************* */ #if (LIB_MEM_CFG_DBG_INFO_EN == DEF_DISABLED) void Mem_SegClr (MEM_SEG *p_seg, LIB_ERR *p_err) { CPU_SR_ALLOC(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (p_err == DEF_NULL) { /* Chk for null err ptr. */ CPU_SW_EXCEPTION(;); } if (p_seg == DEF_NULL) { /* Chk for null seg ptr. */ *p_err = LIB_MEM_ERR_NULL_PTR; return; } #endif CPU_CRITICAL_ENTER(); p_seg->AddrNext = p_seg->AddrBase; CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_NONE; } #endif /* ********************************************************************************************************* * Mem_SegRemSizeGet() * * Description : Gets free space of memory segment. * * Argument(s) : p_seg Pointer to segment data. * * align Alignment in bytes to assume for calculation of free space. * * p_seg_info Pointer to structure that will receive further segment info data (used size, * total size, base address and next allocation address). * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_NULL_PTR Segment data pointer NULL. * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory alignment. * * Return(s) : Memory segment remaining size in bytes, if successful. * 0, otherwise or if memory segment empty. * * Caller(s) : Application, * Mem_HeapGetSizeRem(), * Mem_OutputUsage(). * * Note(s) : None. ********************************************************************************************************* */ CPU_SIZE_T Mem_SegRemSizeGet (MEM_SEG *p_seg, CPU_SIZE_T align, MEM_SEG_INFO *p_seg_info, LIB_ERR *p_err) { CPU_SIZE_T rem_size; CPU_SIZE_T total_size; CPU_SIZE_T used_size; CPU_ADDR next_addr_align; CPU_SR_ALLOC(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (p_err == DEF_NULL) { /* Chk for null err ptr. */ CPU_SW_EXCEPTION(seg_info); } if (MATH_IS_PWR2(align) != DEF_YES) { /* Chk for invalid align val. */ *p_err = LIB_MEM_ERR_INVALID_MEM_ALIGN; return (0u); } #endif if (p_seg == DEF_NULL) { /* Dflt to heap in case p_seg is null. */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) p_seg = &Mem_SegHeap; #else *p_err = LIB_MEM_ERR_NULL_PTR; return (0u); #endif } CPU_CRITICAL_ENTER(); /* Calc seg stats. */ next_addr_align = MATH_ROUND_INC_UP_PWR2(p_seg->AddrNext, align); CPU_CRITICAL_EXIT(); total_size = p_seg->AddrEnd - p_seg->AddrBase + 1u; used_size = next_addr_align - p_seg->AddrBase; rem_size = total_size - used_size; if (p_seg_info != DEF_NULL) { p_seg_info->TotalSize = total_size; p_seg_info->UsedSize = used_size; p_seg_info->AddrBase = p_seg->AddrBase; p_seg_info->AddrNextAlloc = next_addr_align; } *p_err = LIB_MEM_ERR_NONE; return (rem_size); } /* ********************************************************************************************************* * Mem_SegAlloc() * * Description : Allocates memory from specified segment. Returned memory block will be aligned on a CPU * word boundary. * * Argument(s) : p_name Pointer to allocated object name. Used for allocations tracking. May be DEF_NULL. * * p_seg Pointer to segment from which to allocate memory. Will be allocated from * general-purpose heap if null. * * size Size of memory block to allocate, in bytes. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * * ------------------RETURNED BY Mem_SegAllocInternal()------------------- * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory block alignment requested. * LIB_MEM_ERR_INVALID_MEM_SIZE Invalid memory block size specified. * LIB_MEM_ERR_NULL_PTR Error or segment data pointer NULL. * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * Return(s) : Pointer to allocated memory block, if successful. * * DEF_NULL, otherwise. * * Caller(s) : Application. * * Note(s) : (1) The memory block returned by this function will be aligned on a word boundary. In * order to specify a specific alignment value, use either Mem_SegAllocExt() or * Mem_SegAllocHW(). ********************************************************************************************************* */ void *Mem_SegAlloc (const CPU_CHAR *p_name, MEM_SEG *p_seg, CPU_SIZE_T size, LIB_ERR *p_err) { void *p_blk; if (p_seg == DEF_NULL) { /* Alloc from heap if p_seg is null. */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) p_seg = &Mem_SegHeap; #else *p_err = LIB_MEM_ERR_NULL_PTR; return (DEF_NULL); #endif } p_blk = Mem_SegAllocInternal(p_name, p_seg, size, sizeof(CPU_ALIGN), LIB_MEM_PADDING_ALIGN_NONE, DEF_NULL, p_err); return (p_blk); } /* ********************************************************************************************************* * Mem_SegAllocExt() * * Description : Allocates memory from specified memory segment. * * Argument(s) : p_name Pointer to allocated object name. Used for allocations tracking. May be DEF_NULL. * * p_seg Pointer to segment from which to allocate memory. Will be allocated from * general-purpose heap if null. * * size Size of memory block to allocate, in bytes. * * align Required alignment of memory block, in bytes. MUST be a power of 2. * * p_bytes_reqd Pointer to variable that will receive the number of free bytes missing for * the allocation to succeed. Set to DEF_NULL to skip calculation. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * * ------------------RETURNED BY Mem_SegAllocInternal()------------------- * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory block alignment requested. * LIB_MEM_ERR_INVALID_MEM_SIZE Invalid memory block size specified. * LIB_MEM_ERR_NULL_PTR Error or segment data pointer NULL. * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * Return(s) : Pointer to allocated memory block, if successful. * * DEF_NULL, otherwise. * * Caller(s) : Application. * * Note(s) : none. ********************************************************************************************************* */ void *Mem_SegAllocExt (const CPU_CHAR *p_name, MEM_SEG *p_seg, CPU_SIZE_T size, CPU_SIZE_T align, CPU_SIZE_T *p_bytes_reqd, LIB_ERR *p_err) { void *p_blk; if (p_seg == DEF_NULL) { /* Alloc from heap if p_seg is null. */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) p_seg = &Mem_SegHeap; #else *p_err = LIB_MEM_ERR_NULL_PTR; return (DEF_NULL); #endif } p_blk = Mem_SegAllocInternal(p_name, p_seg, size, align, LIB_MEM_PADDING_ALIGN_NONE, p_bytes_reqd, p_err); return (p_blk); } /* ********************************************************************************************************* * Mem_SegAllocHW() * * Description : Allocates memory from specified segment. The returned buffer will be padded in function * of memory segment's properties. * * Argument(s) : p_name Pointer to allocated object name. Used for allocations tracking. May be DEF_NULL. * * p_seg Pointer to segment from which to allocate memory. Will be allocated from * general-purpose heap if null. * * size Size of memory block to allocate, in bytes. * * align Required alignment of memory block, in bytes. MUST be a power of 2. * * p_bytes_reqd Pointer to variable that will receive the number of free bytes missing for * the allocation to succeed. Set to DEF_NULL to skip calculation. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * * ------------------RETURNED BY Mem_SegAllocInternal()------------------- * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory block alignment requested. * LIB_MEM_ERR_INVALID_MEM_SIZE Invalid memory block size specified. * LIB_MEM_ERR_NULL_PTR Error or segment data pointer NULL. * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * Return(s) : Pointer to allocated memory block, if successful. * * DEF_NULL, otherwise. * * Caller(s) : Application. * * Note(s) : none. ********************************************************************************************************* */ void *Mem_SegAllocHW (const CPU_CHAR *p_name, MEM_SEG *p_seg, CPU_SIZE_T size, CPU_SIZE_T align, CPU_SIZE_T *p_bytes_reqd, LIB_ERR *p_err) { void *p_blk; if (p_seg == DEF_NULL) { /* Alloc from heap if p_seg is null. */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) p_seg = &Mem_SegHeap; #else *p_err = LIB_MEM_ERR_NULL_PTR; return (DEF_NULL); #endif } p_blk = Mem_SegAllocInternal(p_name, p_seg, size, align, p_seg->PaddingAlign, p_bytes_reqd, p_err); return (p_blk); } /* ********************************************************************************************************* * Mem_PoolCreate() * * Description : (1) Creates a memory pool : * * (a) Create memory pool from heap or dedicated memory * (b) Allocate memory pool memory blocks * (c) Configure memory pool * * * Argument(s) : p_pool Pointer to a memory pool structure to create (see Note #1). * * p_mem_base Memory pool segment base address : * * (a) Null address Memory pool allocated from general-purpose heap. * (b) Non-null address Memory pool allocated from dedicated memory * specified by its base address. * * mem_size Size of memory pool segment (in bytes). * * blk_nbr Number of memory pool blocks to create. * * blk_size Size of memory pool blocks to create (in bytes). * * blk_align Alignment of memory pool blocks to specific word boundary (in bytes). * * p_bytes_reqd Optional pointer to a variable to ... : * * (a) Return the number of bytes required to successfully * allocate the memory pool, if any error(s); * (b) Return 0, otherwise. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_NULL_PTR Pointer to memory pool is null. * LIB_MEM_ERR_INVALID_BLK_ALIGN Invalid block alignment requested. * LIB_MEM_ERR_INVALID_BLK_NBR Invalid number of blocks specified. * LIB_MEM_ERR_INVALID_BLK_SIZE Invalid block size specified. * LIB_MEM_ERR_INVALID_SEG_SIZE Invalid segment size. * LIB_MEM_ERR_HEAP_EMPTY No more memory available on heap. * * ---------------RETURNED BY Mem_SegOverlapChkCritical()---------------- * LIB_MEM_ERR_INVALID_SEG_EXISTS Segment already exists. * LIB_MEM_ERR_INVALID_SEG_OVERLAP Segment overlaps another existing segment. * * -----------------RETURNED BY Mem_SegAllocExtCritical()----------------- * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * ------------------RETURNED BY Mem_SegAllocInternal()------------------- * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory block alignment requested. * LIB_MEM_ERR_INVALID_MEM_SIZE Invalid memory block size specified. * LIB_MEM_ERR_NULL_PTR Error or segment data pointer NULL. * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * -----------------------RETURNED BY Mem_PoolClr()----------------------- * LIB_MEM_ERR_NULL_PTR Argument 'p_pool' passed a NULL pointer. * * Return(s) : none. * * Caller(s) : Application. * * Note(s) : (1) This function is DEPRECATED and will be removed in a future version of this product. * Mem_DynPoolCreate() or Mem_DynPoolCreateHW() should be used instead. ********************************************************************************************************* */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) void Mem_PoolCreate (MEM_POOL *p_pool, void *p_mem_base, CPU_SIZE_T mem_size, MEM_POOL_BLK_QTY blk_nbr, CPU_SIZE_T blk_size, CPU_SIZE_T blk_align, CPU_SIZE_T *p_bytes_reqd, LIB_ERR *p_err) { MEM_SEG *p_seg; void *p_pool_mem; CPU_SIZE_T pool_size; CPU_SIZE_T blk_size_align; CPU_ADDR pool_addr_end; MEM_POOL_BLK_QTY blk_ix; CPU_INT08U *p_blk; CPU_SR_ALLOC(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) /* --------------- VALIDATE RTN ERR PTR --------------- */ if (p_err == DEF_NULL) { CPU_SW_EXCEPTION(;); } /* ------------- VALIDATE MEM POOL CREATE ------------- */ if (p_pool == DEF_NULL) { *p_err = LIB_MEM_ERR_NULL_PTR; return; } if (p_mem_base != DEF_NULL) { if (mem_size < 1u) { *p_err = LIB_MEM_ERR_INVALID_SEG_SIZE; return; } } if (blk_nbr < 1u) { *p_err = LIB_MEM_ERR_INVALID_BLK_NBR; return; } if (blk_size < 1u) { *p_err = LIB_MEM_ERR_INVALID_BLK_SIZE; return; } if (MATH_IS_PWR2(blk_align) != DEF_YES) { /* Chk that req alignment is a pwr of 2. */ *p_err = LIB_MEM_ERR_INVALID_BLK_ALIGN; return; } #endif Mem_PoolClr(p_pool, p_err); /* Init mem pool. */ if (*p_err != LIB_MEM_ERR_NONE) { return; } /* -------- DETERMINE AND/OR ALLOC SEG TO USE --------- */ if (p_mem_base == DEF_NULL) { /* Use heap seg. */ p_seg = &Mem_SegHeap; } else { /* Use other seg. */ CPU_CRITICAL_ENTER(); p_seg = Mem_SegOverlapChkCritical((CPU_ADDR)p_mem_base, mem_size, p_err); switch (*p_err) { case LIB_MEM_ERR_INVALID_SEG_EXISTS: /* Seg already exists. */ break; case LIB_MEM_ERR_NONE: /* Seg must be created. */ p_seg = (MEM_SEG *)Mem_SegAllocExtCritical(&Mem_SegHeap, sizeof(MEM_SEG), sizeof(CPU_ALIGN), LIB_MEM_PADDING_ALIGN_NONE, p_bytes_reqd, p_err); if (*p_err != LIB_MEM_ERR_NONE) { CPU_CRITICAL_EXIT(); return; } #if (LIB_MEM_CFG_DBG_INFO_EN == DEF_ENABLED) /* Track alloc if req'd. */ Mem_SegAllocTrackCritical("Unknown segment data", &Mem_SegHeap, sizeof(MEM_SEG), p_err); if (*p_err != LIB_MEM_ERR_NONE) { CPU_CRITICAL_EXIT(); return; } #endif Mem_SegCreateCritical( DEF_NULL, p_seg, (CPU_ADDR)p_mem_base, LIB_MEM_PADDING_ALIGN_NONE, mem_size); break; case LIB_MEM_ERR_INVALID_SEG_OVERLAP: default: CPU_CRITICAL_EXIT(); return; /* Prevent 'break NOT reachable' compiler warning. */ } CPU_CRITICAL_EXIT(); } /* ---------------- ALLOC MEM FOR POOL ---------------- */ /* Calc blk size with align. */ blk_size_align = MATH_ROUND_INC_UP_PWR2(blk_size, blk_align); pool_size = blk_size_align * blk_nbr; /* Calc required size for pool. */ /* Alloc mem for pool. */ p_pool_mem = (void *)Mem_SegAllocInternal("Unnamed static pool", p_seg, pool_size, blk_align, LIB_MEM_PADDING_ALIGN_NONE, p_bytes_reqd, p_err); if (*p_err != LIB_MEM_ERR_NONE) { return; } /* ------------ ALLOC MEM FOR FREE BLK TBL ------------ */ p_pool->BlkFreeTbl = (void *)Mem_SegAllocInternal("Unnamed static pool free blk tbl", &Mem_SegHeap, blk_nbr * sizeof(void *), sizeof(CPU_ALIGN), LIB_MEM_PADDING_ALIGN_NONE, p_bytes_reqd, p_err); if (*p_err != LIB_MEM_ERR_NONE) { return; } /* ------------------ INIT BLK LIST ------------------- */ p_blk = (CPU_INT08U *)p_pool_mem; for (blk_ix = 0; blk_ix < blk_nbr; blk_ix++) { p_pool->BlkFreeTbl[blk_ix] = p_blk; p_blk += blk_size_align; } /* ------------------ INIT POOL DATA ------------------ */ pool_addr_end = (CPU_ADDR)p_pool_mem + (pool_size - 1u); p_pool->PoolAddrStart = p_pool_mem; p_pool->PoolAddrEnd = (void *)pool_addr_end; p_pool->BlkNbr = blk_nbr; p_pool->BlkSize = blk_size_align; p_pool->BlkFreeTblIx = blk_nbr; } #endif /* ********************************************************************************************************* * Mem_PoolClr() * * Description : Clears a memory pool (see Note #1). * * Argument(s) : p_pool Pointer to a memory pool structure to clear (see Note #2). * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_NULL_PTR Argument 'p_pool' passed a NULL pointer. * * Return(s) : none. * * Caller(s) : Application, * Mem_PoolCreate(). * * Note(s) : (1) (a) Mem_PoolClr() ONLY clears a memory pool structure's variables & should ONLY be * called to initialize a memory pool structure prior to calling Mem_PoolCreate(). * * (b) Mem_PoolClr() does NOT deallocate memory from the memory pool or deallocate the * memory pool itself & MUST NOT be called after calling Mem_PoolCreate() since * this will likely corrupt the memory pool management. * * (2) Assumes 'p_pool' points to a valid memory pool (if non-NULL). * * (3) This function is DEPRECATED and will be removed in a future version of this product. ********************************************************************************************************* */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) void Mem_PoolClr (MEM_POOL *p_pool, LIB_ERR *p_err) { #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) /* -------------- VALIDATE RTN ERR PTR --------------- */ if (p_err == DEF_NULL) { CPU_SW_EXCEPTION(;); } /* -------------- VALIDATE MEM POOL PTR --------------- */ if (p_pool == DEF_NULL) { *p_err = LIB_MEM_ERR_NULL_PTR; return; } #endif p_pool->PoolAddrStart = DEF_NULL; p_pool->PoolAddrEnd = DEF_NULL; p_pool->BlkSize = 0u; p_pool->BlkNbr = 0u; p_pool->BlkFreeTbl = DEF_NULL; p_pool->BlkFreeTblIx = 0u; *p_err = LIB_MEM_ERR_NONE; } #endif /* ********************************************************************************************************* * Mem_PoolBlkGet() * * Description : Gets a memory block from memory pool. * * Argument(s) : p_pool Pointer to memory pool to get memory block from. * * size Size of requested memory (in bytes). * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_INVALID_BLK_SIZE Invalid memory pool block size requested. * LIB_MEM_ERR_NULL_PTR Argument 'p_pool' passed a NULL pointer. * LIB_MEM_ERR_POOL_EMPTY NO memory blocks available in memory pool. * * Return(s) : Pointer to memory block, if NO error(s). * * Pointer to NULL, otherwise. * * Caller(s) : Application. * * Note(s) : (1) This function is DEPRECATED and will be removed in a future version of this product. * Mem_DynPoolBlkGet() should be used instead. ********************************************************************************************************* */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) void *Mem_PoolBlkGet (MEM_POOL *p_pool, CPU_SIZE_T size, LIB_ERR *p_err) { CPU_INT08U *p_blk; CPU_SR_ALLOC(); (void)&size; /* Prevent possible 'variable unused' warning. */ #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) /* -------------- VALIDATE MEM POOL GET --------------- */ if (p_err == DEF_NULL) { /* Validate err ptr. */ CPU_SW_EXCEPTION(DEF_NULL); } if (p_pool == DEF_NULL) { /* Validate pool ptr. */ *p_err = LIB_MEM_ERR_NULL_PTR; return (DEF_NULL); } if (size < 1u) { /* Validate req'd size as non-NULL. */ *p_err = LIB_MEM_ERR_INVALID_BLK_SIZE; return (DEF_NULL); } if (size > p_pool->BlkSize) { /* Validate req'd size <= mem pool blk size. */ *p_err = LIB_MEM_ERR_INVALID_BLK_SIZE; return (DEF_NULL); } #endif /* -------------- GET MEM BLK FROM POOL --------------- */ p_blk = DEF_NULL; CPU_CRITICAL_ENTER(); if (p_pool->BlkFreeTblIx > 0u) { p_pool->BlkFreeTblIx -= 1u; p_blk = p_pool->BlkFreeTbl[p_pool->BlkFreeTblIx]; p_pool->BlkFreeTbl[p_pool->BlkFreeTblIx] = DEF_NULL; } CPU_CRITICAL_EXIT(); if (p_blk == DEF_NULL) { *p_err = LIB_MEM_ERR_POOL_EMPTY; } else { *p_err = LIB_MEM_ERR_NONE; } return (p_blk); } #endif /* ********************************************************************************************************* * Mem_PoolBlkFree() * * Description : Free a memory block to memory pool. * * Argument(s) : p_pool Pointer to memory pool to free memory block. * * p_blk Pointer to memory block address to free. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_NULL_PTR Argument 'p_pool'/'p_blk' passed * a NULL pointer. * LIB_MEM_ERR_INVALID_BLK_ADDR Invalid memory block address. * LIB_MEM_ERR_INVALID_BLK_ADDR_IN_POOL Memory block address already * in memory pool. * LIB_MEM_ERR_POOL_FULL Pool is full. * * Return(s) : none. * * Caller(s) : Application. * * Note(s) : (1) This function is DEPRECATED and will be removed in a future version of this product. * Mem_DynPoolBlkFree() should be used instead. ********************************************************************************************************* */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) void Mem_PoolBlkFree (MEM_POOL *p_pool, void *p_blk, LIB_ERR *p_err) { #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) CPU_SIZE_T tbl_ix; CPU_BOOLEAN addr_valid; #endif CPU_SR_ALLOC(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) /* -------------- VALIDATE MEM POOL FREE -------------- */ if (p_err == DEF_NULL) { CPU_SW_EXCEPTION(;); } if (p_pool == DEF_NULL) { /* Validate mem ptrs. */ *p_err = LIB_MEM_ERR_NULL_PTR; return; } if (p_blk == DEF_NULL) { *p_err = LIB_MEM_ERR_NULL_PTR; return; } addr_valid = Mem_PoolBlkIsValidAddr(p_pool, p_blk); /* Validate mem blk as valid pool blk addr. */ if (addr_valid != DEF_OK) { *p_err = LIB_MEM_ERR_INVALID_BLK_ADDR; return; } CPU_CRITICAL_ENTER(); /* Make sure blk isn't already in free list. */ for (tbl_ix = 0u; tbl_ix < p_pool->BlkNbr; tbl_ix++) { if (p_pool->BlkFreeTbl[tbl_ix] == p_blk) { CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_INVALID_BLK_ADDR_IN_POOL; return; } } #else /* Double-free possibility if not in critical section. */ CPU_CRITICAL_ENTER(); #endif /* --------------- FREE MEM BLK TO POOL --------------- */ if (p_pool->BlkFreeTblIx >= p_pool->BlkNbr) { CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_POOL_FULL; return; } p_pool->BlkFreeTbl[p_pool->BlkFreeTblIx] = p_blk; p_pool->BlkFreeTblIx += 1u; CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_NONE; } #endif /* ********************************************************************************************************* * Mem_PoolBlkGetNbrAvail() * * Description : Get memory pool's remaining number of blocks available to allocate. * * Argument(s) : p_pool Pointer to a memory pool structure. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_NULL_PTR Argument 'p_pool' passed a NULL pointer. * * Return(s) : Remaining memory pool blocks, if NO error(s). * * 0, otherwise. * * Caller(s) : Application. * * Note(s) : (1) This function is DEPRECATED and will be removed in a future version of this product. * Mem_DynPoolBlkNbrAvailGet() should be used instead. ********************************************************************************************************* */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) MEM_POOL_BLK_QTY Mem_PoolBlkGetNbrAvail (MEM_POOL *p_pool, LIB_ERR *p_err) { CPU_SIZE_T nbr_avail; CPU_SR_ALLOC(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) /* --------------- VALIDATE RTN ERR PTR --------------- */ if (p_err == DEF_NULL) { CPU_SW_EXCEPTION(0u); } /* ---------------- VALIDATE MEM POOL ----------------- */ if (p_pool == DEF_NULL) { /* Validate mem ptr. */ *p_err = LIB_MEM_ERR_NULL_PTR; return (0u); } #endif CPU_CRITICAL_ENTER(); nbr_avail = p_pool->BlkFreeTblIx; CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_NONE; return (nbr_avail); } #endif /* ********************************************************************************************************* * Mem_DynPoolCreate() * * Description : Creates a dynamic memory pool. * * Argument(s) : p_name Pointer to pool name. * * p_pool Pointer to pool data. * * p_seg Pointer to segment from which to allocate memory. Will be allocated from * general-purpose heap if null. * * blk_size Size of memory block to allocate from pool, in bytes. See Note #1. * * blk_align Required alignment of memory block, in bytes. MUST be a power of 2. * * blk_qty_init Initial number of elements to be allocated in pool. * * blk_qty_max Maximum number of elements that can be allocated from this pool. Set to * LIB_MEM_BLK_QTY_UNLIMITED if no limit. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * * --------------------RETURNED BY Mem_DynPoolCreateInternal()------------------- * LIB_MEM_ERR_INVALID_BLK_ALIGN Invalid requested block alignment. * LIB_MEM_ERR_INVALID_BLK_SIZE Invalid requested block size. * LIB_MEM_ERR_INVALID_BLK_NBR Invalid requested block quantity max. * LIB_MEM_ERR_NULL_PTR Pool data pointer NULL. * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory block alignment requested. * LIB_MEM_ERR_INVALID_MEM_SIZE Invalid memory block size specified. * LIB_MEM_ERR_NULL_PTR Error or segment data pointer NULL. * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * Return(s) : None. * * Caller(s) : Application. * * Note(s) : (1) 'blk_size' must be big enough to fit a pointer since the pointer to the next free * block is stored in the block itself (only when free/unused). ********************************************************************************************************* */ void Mem_DynPoolCreate (const CPU_CHAR *p_name, MEM_DYN_POOL *p_pool, MEM_SEG *p_seg, CPU_SIZE_T blk_size, CPU_SIZE_T blk_align, CPU_SIZE_T blk_qty_init, CPU_SIZE_T blk_qty_max, LIB_ERR *p_err) { if (p_seg == DEF_NULL) { /* Alloc from heap if p_seg is null. */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) p_seg = &Mem_SegHeap; #else *p_err = LIB_MEM_ERR_NULL_PTR; return (DEF_NULL); #endif } Mem_DynPoolCreateInternal(p_name, p_pool, p_seg, blk_size, blk_align, LIB_MEM_PADDING_ALIGN_NONE, blk_qty_init, blk_qty_max, p_err); } /* ********************************************************************************************************* * Mem_DynPoolCreateHW() * * Description : Creates a dynamic memory pool. Memory blocks will be padded according to memory segment's * properties. * * Argument(s) : p_name Pointer to pool name. * * p_pool Pointer to pool data. * * p_seg Pointer to segment from which to allocate memory. Will allocate from * general-purpose heap if null. * * blk_size Size of memory block to allocate from pool, in bytes. See Note #1. * * blk_align Required alignment of memory block, in bytes. MUST be a power of 2. * * blk_qty_init Initial number of elements to be allocated in pool. * * blk_qty_max Maximum number of elements that can be allocated from this pool. Set to * LIB_MEM_BLK_QTY_UNLIMITED if no limit. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * * -------------------RETURNED BY Mem_DynPoolCreateInternal()------------------- * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory block alignment requested. * LIB_MEM_ERR_INVALID_MEM_SIZE Invalid memory block size specified. * LIB_MEM_ERR_NULL_PTR Error or segment data pointer NULL. * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * Return(s) : None. * * Caller(s) : Application. * * Note(s) : (1) 'blk_size' must be big enough to fit a pointer since the pointer to the next free * block is stored in the block itself (only when free/unused). ********************************************************************************************************* */ void Mem_DynPoolCreateHW (const CPU_CHAR *p_name, MEM_DYN_POOL *p_pool, MEM_SEG *p_seg, CPU_SIZE_T blk_size, CPU_SIZE_T blk_align, CPU_SIZE_T blk_qty_init, CPU_SIZE_T blk_qty_max, LIB_ERR *p_err) { if (p_seg == DEF_NULL) { /* Alloc from heap if p_seg is null. */ #if (LIB_MEM_CFG_HEAP_SIZE > 0u) p_seg = &Mem_SegHeap; #else *p_err = LIB_MEM_ERR_NULL_PTR; return (DEF_NULL); #endif } Mem_DynPoolCreateInternal(p_name, p_pool, p_seg, blk_size, blk_align, p_seg->PaddingAlign, blk_qty_init, blk_qty_max, p_err); } /* ********************************************************************************************************* * Mem_DynPoolBlkGet() * * Description : Gets a memory block from specified pool, growing it if needed. * * Argument(s) : p_pool Pointer to pool data. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_NULL_PTR Pool data pointer NULL. * LIB_MEM_ERR_POOL_EMPTY Pools is empty. * * ----------------------RETURNED BY Mem_SegAllocInternal()----------------------- * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory block alignment requested. * LIB_MEM_ERR_INVALID_MEM_SIZE Invalid memory block size specified. * LIB_MEM_ERR_NULL_PTR Error or segment data pointer NULL. * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * Return(s) : Pointer to memory block, if successful. * * DEF_NULL, otherwise. * * Caller(s) : Application. * * Note(s) : none. ********************************************************************************************************* */ void *Mem_DynPoolBlkGet (MEM_DYN_POOL *p_pool, LIB_ERR *p_err) { void *p_blk; const CPU_CHAR *p_pool_name; CPU_SR_ALLOC(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (p_err == DEF_NULL) { /* Chk for NULL err ptr. */ CPU_SW_EXCEPTION(DEF_NULL); } if (p_pool == DEF_NULL) { /* Chk for NULL pool data ptr. */ *p_err = LIB_MEM_ERR_NULL_PTR; return (DEF_NULL); } #endif /* Ensure pool is not empty if qty is limited. */ if (p_pool->BlkQtyMax != LIB_MEM_BLK_QTY_UNLIMITED) { CPU_CRITICAL_ENTER(); if (p_pool->BlkAllocCnt >= p_pool->BlkQtyMax) { CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_POOL_EMPTY; return (DEF_NULL); } p_pool->BlkAllocCnt++; CPU_CRITICAL_EXIT(); } /* --------------- ALLOC FROM FREE LIST --------------- */ CPU_CRITICAL_ENTER(); if (p_pool->BlkFreePtr != DEF_NULL) { p_blk = p_pool->BlkFreePtr; p_pool->BlkFreePtr = *((void **)p_blk); CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_NONE; return (p_blk); } CPU_CRITICAL_EXIT(); /* ------------------ ALLOC NEW BLK ------------------- */ #if (LIB_MEM_CFG_DBG_INFO_EN == DEF_ENABLED) p_pool_name = p_pool->NamePtr; #else p_pool_name = DEF_NULL; #endif p_blk = Mem_SegAllocInternal(p_pool_name, p_pool->PoolSegPtr, p_pool->BlkSize, p_pool->BlkAlign, p_pool->BlkPaddingAlign, DEF_NULL, p_err); if (*p_err != LIB_MEM_ERR_NONE) { return (DEF_NULL); } return (p_blk); } /* ********************************************************************************************************* * Mem_DynPoolBlkFree() * * Description : Frees memory block, making it available for future use. * * Argument(s) : p_pool Pointer to pool data. * * p_blk Pointer to first byte of memory block. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_NULL_PTR 'p_pool' or 'p_blk' pointer passed is NULL. * LIB_MEM_ERR_POOL_FULL Pool is full. * * Return(s) : none. * * Caller(s) : Application. * * Note(s) : none. ********************************************************************************************************* */ void Mem_DynPoolBlkFree (MEM_DYN_POOL *p_pool, void *p_blk, LIB_ERR *p_err) { CPU_SR_ALLOC(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (p_err == DEF_NULL) { /* Chk for NULL err ptr. */ CPU_SW_EXCEPTION(;); } if (p_pool == DEF_NULL) { /* Chk for NULL pool data ptr. */ *p_err = LIB_MEM_ERR_NULL_PTR; return; } if (p_blk == DEF_NULL) { *p_err = LIB_MEM_ERR_NULL_PTR; return; } #endif if (p_pool->BlkQtyMax != LIB_MEM_BLK_QTY_UNLIMITED) { /* Ensure pool is not full. */ CPU_CRITICAL_ENTER(); if (p_pool->BlkAllocCnt == 0u) { CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_POOL_FULL; return; } p_pool->BlkAllocCnt--; CPU_CRITICAL_EXIT(); } CPU_CRITICAL_ENTER(); *((void **)p_blk) = p_pool->BlkFreePtr; p_pool->BlkFreePtr = p_blk; CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_NONE; } /* ********************************************************************************************************* * Mem_DynPoolBlkNbrAvailGet() * * Description : Gets number of available blocks in dynamic memory pool. This call will fail with a * dynamic memory pool for which no limit was set at creation. * * Argument(s) : p_pool Pointer to pool data. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_NULL_PTR 'p_pool' pointer passed is NULL. * LIB_MEM_ERR_POOL_UNLIMITED Pool has no specified limit. * * Return(s) : Number of blocks available in dynamic memory pool, if successful. * * 0, if pool is empty or if an error occurred. * * Caller(s) : Application. * * Note(s) : None. ********************************************************************************************************* */ CPU_SIZE_T Mem_DynPoolBlkNbrAvailGet (MEM_DYN_POOL *p_pool, LIB_ERR *p_err) { CPU_SIZE_T blk_nbr_avail; CPU_SR_ALLOC(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (p_err == DEF_NULL) { /* Chk for NULL err ptr. */ CPU_SW_EXCEPTION(;); } if (p_pool == DEF_NULL) { /* Chk for NULL pool data ptr. */ *p_err = LIB_MEM_ERR_NULL_PTR; return (0u); } #endif if (p_pool->BlkQtyMax != LIB_MEM_BLK_QTY_UNLIMITED) { CPU_CRITICAL_ENTER(); blk_nbr_avail = p_pool->BlkQtyMax - p_pool->BlkAllocCnt; CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_NONE; } else { blk_nbr_avail = 0u; *p_err = LIB_MEM_ERR_POOL_UNLIMITED; } return (blk_nbr_avail); } /* ********************************************************************************************************* * Mem_OutputUsage() * * Description : Outputs memory usage report through 'out_fnct'. * * Argument(s) : out_fnct Pointer to output function. * * print_details DEF_YES, if the size of each allocation should be printed. * DEF_NO, otherwise. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_NONE Operation was successful. * LIB_MEM_ERR_NULL_PTR 'out_fnct' pointer passed is NULL. * * ---------------------RETURNED BY Mem_SegRemSizeGet()-------------------- * LIB_MEM_ERR_NULL_PTR Segment data pointer NULL. * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory alignment. * * Return(s) : None. * * Caller(s) : Application. * * Note(s) : none. ********************************************************************************************************* */ #if (LIB_MEM_CFG_DBG_INFO_EN == DEF_ENABLED) void Mem_OutputUsage(void (*out_fnct) (CPU_CHAR *), LIB_ERR *p_err) { CPU_CHAR str[DEF_INT_32U_NBR_DIG_MAX]; MEM_SEG *p_seg; CPU_SR_ALLOC(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (p_err == DEF_NULL) { /* Chk for NULL err ptr. */ CPU_SW_EXCEPTION(;); } if (out_fnct == DEF_NULL) { /* Chk for NULL out fnct ptr. */ *p_err = LIB_MEM_ERR_NULL_PTR; return; } #endif out_fnct("---------------- Memory allocation info ----------------\r\n"); out_fnct("| Type | Size | Free size | Name\r\n"); out_fnct("|---------|------------|------------|-------------------\r\n"); CPU_CRITICAL_ENTER(); p_seg = Mem_SegHeadPtr; while (p_seg != DEF_NULL) { CPU_SIZE_T rem_size; MEM_SEG_INFO seg_info; MEM_ALLOC_INFO *p_alloc; rem_size = Mem_SegRemSizeGet(p_seg, 1u, &seg_info, p_err); if (*p_err != LIB_MEM_ERR_NONE) { return; } out_fnct("| Section | "); (void)Str_FmtNbr_Int32U(seg_info.TotalSize, 10u, DEF_NBR_BASE_DEC, ' ', DEF_NO, DEF_YES, &str[0u]); out_fnct(str); out_fnct(" | "); (void)Str_FmtNbr_Int32U(rem_size, 10u, DEF_NBR_BASE_DEC, ' ', DEF_NO, DEF_YES, &str[0u]); out_fnct(str); out_fnct(" | "); out_fnct((p_seg->NamePtr != DEF_NULL) ? (CPU_CHAR *)p_seg->NamePtr : (CPU_CHAR *)"Unknown"); out_fnct("\r\n"); p_alloc = p_seg->AllocInfoHeadPtr; while (p_alloc != DEF_NULL) { out_fnct("| -> Obj | "); (void)Str_FmtNbr_Int32U(p_alloc->Size, 10u, DEF_NBR_BASE_DEC, ' ', DEF_NO, DEF_YES, &str[0u]); out_fnct(str); out_fnct(" | | "); out_fnct((p_alloc->NamePtr != DEF_NULL) ? (CPU_CHAR *)p_alloc->NamePtr : (CPU_CHAR *)"Unknown"); out_fnct("\r\n"); p_alloc = p_alloc->NextPtr; } p_seg = p_seg->NextPtr; } CPU_CRITICAL_EXIT(); *p_err = LIB_MEM_ERR_NONE; } #endif /* ********************************************************************************************************* ********************************************************************************************************* * LOCAL FUNCTIONS ********************************************************************************************************* ********************************************************************************************************* */ /* ********************************************************************************************************* * Mem_SegCreateCritical() * * Description : Creates a new memory segment to be used for runtime memory allocation or dynamic pools. * * Argument(s) : p_name Pointer to segment name. * * p_seg Pointer to segment data. Must be allocated by caller. * ----- Argument validated by caller. * * seg_base_addr Segment's first byte address. * * padding_align Padding alignment, in bytes, that will be added to any allocated buffer * from this memory segment. MUST be a power of 2. * LIB_MEM_PADDING_ALIGN_NONE means no padding. * ------------- Argument validated by caller. * * size Total size of segment, in bytes. * ---- Argument validated by caller. * * Return(s) : Pointer to segment data, if successful. * * DEF_NULL, otherwise. * * Caller(s) : Mem_PoolCreate(), * Mem_SegCreate(). * * Note(s) : (1) This function MUST be called within a CRITICAL_SECTION. ********************************************************************************************************* */ static void Mem_SegCreateCritical(const CPU_CHAR *p_name, MEM_SEG *p_seg, CPU_ADDR seg_base_addr, CPU_SIZE_T padding_align, CPU_SIZE_T size) { p_seg->AddrBase = seg_base_addr; p_seg->AddrEnd = (seg_base_addr + (size - 1u)); p_seg->AddrNext = seg_base_addr; p_seg->NextPtr = Mem_SegHeadPtr; p_seg->PaddingAlign = padding_align; #if (LIB_MEM_CFG_DBG_INFO_EN == DEF_ENABLED) p_seg->NamePtr = p_name; p_seg->AllocInfoHeadPtr = DEF_NULL; #else (void)&p_name; #endif Mem_SegHeadPtr = p_seg; } /* ********************************************************************************************************* * Mem_SegOverlapChkCritical() * * Description : Checks if existing memory segment exists or overlaps with specified memory area. * * Argument(s) : seg_base_addr Address of first byte of memory area. * * size Size of memory area, in bytes. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_INVALID_SEG_OVERLAP Segment overlaps another existing segment. * LIB_MEM_ERR_INVALID_SEG_EXISTS Segment already exists. * * Return(s) : Pointer to memory segment that overlaps. * * DEF_NULL, otherwise. * * Caller(s) : Mem_PoolCreate(), * Mem_SegCreate(). * * Note(s) : (1) This function MUST be called within a CRITICAL_SECTION. ********************************************************************************************************* */ static MEM_SEG *Mem_SegOverlapChkCritical (CPU_ADDR seg_base_addr, CPU_SIZE_T size, LIB_ERR *p_err) { MEM_SEG *p_seg_chk; CPU_ADDR seg_new_end; CPU_ADDR seg_chk_start; CPU_ADDR seg_chk_end; seg_new_end = seg_base_addr + (size - 1u); p_seg_chk = Mem_SegHeadPtr; while (p_seg_chk != DEF_NULL) { seg_chk_start = (CPU_ADDR)p_seg_chk->AddrBase; seg_chk_end = (CPU_ADDR)p_seg_chk->AddrEnd; if ((seg_base_addr == seg_chk_start) && (seg_new_end == seg_chk_end)) { *p_err = LIB_MEM_ERR_INVALID_SEG_EXISTS; return (p_seg_chk); } else if (((seg_base_addr >= seg_chk_start) && (seg_base_addr <= seg_chk_end)) || ((seg_base_addr <= seg_chk_start) && (seg_new_end >= seg_chk_start))) { *p_err = LIB_MEM_ERR_INVALID_SEG_OVERLAP; return (p_seg_chk); } p_seg_chk = p_seg_chk->NextPtr; } *p_err = LIB_MEM_ERR_NONE; return (DEF_NULL); } /* ********************************************************************************************************* * Mem_SegAllocInternal() * * Description : Allocates memory from specified segment. * * Argument(s) : p_name Pointer to allocated object name. Used for allocations tracking. May be DEF_NULL. * * p_seg Pointer to segment from which to allocate memory. * ----- Argument validated by caller. * * size Size of memory block to allocate, in bytes. * * align Required alignment of memory block, in bytes. MUST be a power of 2. * * padding_align Padding alignment, in bytes, that will be added to any allocated buffer from * this memory segment. MUST be a power of 2. LIB_MEM_PADDING_ALIGN_NONE * means no padding. * * p_bytes_reqd Pointer to variable that will receive the number of free bytes missing for * the allocation to succeed. Set to DEF_NULL to skip calculation. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory block alignment requested. * LIB_MEM_ERR_INVALID_MEM_SIZE Invalid memory block size specified. * LIB_MEM_ERR_NULL_PTR Error or segment data pointer NULL. * * ------------------RETURNED BY Mem_SegAllocExtCritical()------------------ * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * Return(s) : Pointer to allocated memory block, if successful. * * DEF_NULL, otherwise. * * Caller(s) : Mem_DynPoolBlkGet(), * Mem_DynPoolCreateInternal(), * Mem_HeapAlloc(), * Mem_PoolCreate(), * Mem_SegAlloc(), * Mem_SegAllocExt(), * Mem_SegAllocHW(). * * Note(s) : none. ********************************************************************************************************* */ static void *Mem_SegAllocInternal (const CPU_CHAR *p_name, MEM_SEG *p_seg, CPU_SIZE_T size, CPU_SIZE_T align, CPU_SIZE_T padding_align, CPU_SIZE_T *p_bytes_reqd, LIB_ERR *p_err) { void *p_blk; CPU_SR_ALLOC(); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (p_err == DEF_NULL) { /* Chk for null err ptr. */ CPU_SW_EXCEPTION(DEF_NULL); } if (size < 1u) { /* Chk for invalid sized mem req. */ *p_err = LIB_MEM_ERR_INVALID_MEM_SIZE; return (DEF_NULL); } if (MATH_IS_PWR2(align) != DEF_YES) { /* Chk that align is a pwr of 2. */ *p_err = LIB_MEM_ERR_INVALID_MEM_ALIGN; return (DEF_NULL); } #endif CPU_CRITICAL_ENTER(); p_blk = Mem_SegAllocExtCritical(p_seg, size, align, padding_align, p_bytes_reqd, p_err); if (*p_err != LIB_MEM_ERR_NONE) { CPU_CRITICAL_EXIT(); return (DEF_NULL); } #if (LIB_MEM_CFG_DBG_INFO_EN == DEF_ENABLED) /* Track alloc if req'd. */ Mem_SegAllocTrackCritical(p_name, p_seg, size, p_err); if (*p_err != LIB_MEM_ERR_NONE) { CPU_CRITICAL_EXIT(); return (DEF_NULL); } #else (void)&p_name; #endif CPU_CRITICAL_EXIT(); return (p_blk); } /* ********************************************************************************************************* * Mem_SegAllocExtCritical() * * Description : Allocates memory from specified segment. * * Argument(s) : p_seg Pointer to segment from which to allocate memory. * * size Size of memory block to allocate, in bytes. * * align Required alignment of memory block, in bytes. MUST be a power of 2. * * padding_align Padding alignment, in bytes, that will be added to any allocated buffer from * this memory segment. MUST be a power of 2. LIB_MEM_PADDING_ALIGN_NONE * means no padding. * * p_bytes_reqd Pointer to variable that will receive the number of free bytes missing for * the allocation to succeed. Set to DEF_NULL to skip calculation. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * Return(s) : Pointer to allocated memory block, if successful. * * DEF_NULL, otherwise. * * Caller(s) : Mem_PoolCreate(), * Mem_SegAllocInternal(), * Mem_SegAllocTrackCritical(). * * Note(s) : (1) This function MUST be called within a CRITICAL_SECTION. ********************************************************************************************************* */ static void *Mem_SegAllocExtCritical (MEM_SEG *p_seg, CPU_SIZE_T size, CPU_SIZE_T align, CPU_SIZE_T padding_align, CPU_SIZE_T *p_bytes_reqd, LIB_ERR *p_err) { CPU_ADDR blk_addr; CPU_ADDR addr_next; CPU_SIZE_T size_rem_seg; CPU_SIZE_T size_tot_blk; CPU_SIZE_T blk_align = DEF_MAX(align, padding_align); blk_addr = MATH_ROUND_INC_UP_PWR2(p_seg->AddrNext, /* Compute align'ed blk addr. */ blk_align); addr_next = MATH_ROUND_INC_UP_PWR2(blk_addr + size, /* Compute addr of next alloc. */ padding_align); size_rem_seg = p_seg->AddrEnd - p_seg->AddrNext + 1u; size_tot_blk = addr_next - p_seg->AddrNext; /* Compute tot blk size including align and padding. */ if (size_rem_seg < size_tot_blk) { /* If seg doesn't have enough space ... */ if (p_bytes_reqd != DEF_NULL) { /* ... calc nbr of req'd bytes. */ *p_bytes_reqd = size_tot_blk - size_rem_seg; } *p_err = LIB_MEM_ERR_SEG_OVF; return (DEF_NULL); } p_seg->AddrNext = addr_next; *p_err = LIB_MEM_ERR_NONE; return ((void *)blk_addr); } /* ********************************************************************************************************* * Mem_SegAllocTrackCritical() * * Description : Tracks segment allocation, adding the 'size' of the allocation under the 'p_name' entry. * * Argument(s) : p_name Pointer to the name of the object. This string is not copied and its memory should * remain accessible at all times. * * p_seg Pointer to segment data. * * size Allocation size, in bytes. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_HEAP_EMPTY No more memory available on heap * * --------------RETURNED BY Mem_SegAllocExtCritical()--------------- * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * Return(s) : none. * * Caller(s) : Mem_PoolCreate(), * Mem_SegAllocInternal(). * * Note(s) : none. ********************************************************************************************************* */ #if (LIB_MEM_CFG_DBG_INFO_EN == DEF_ENABLED) static void Mem_SegAllocTrackCritical (const CPU_CHAR *p_name, MEM_SEG *p_seg, CPU_SIZE_T size, LIB_ERR *p_err) { MEM_ALLOC_INFO *p_alloc; /* ------- UPDATE ALLOC INFO LIST, IF POSSIBLE -------- */ p_alloc = p_seg->AllocInfoHeadPtr; while (p_alloc != DEF_NULL) { if (p_alloc->NamePtr == p_name) { p_alloc->Size += size; *p_err = LIB_MEM_ERR_NONE; return; } p_alloc = p_alloc->NextPtr; } /* --------- ADD NEW ALLOC INFO ENTRY IN LIST --------- */ p_alloc = Mem_SegAllocExtCritical(&Mem_SegHeap, /* Alloc new alloc info struct on heap. */ sizeof(MEM_ALLOC_INFO), sizeof(CPU_ALIGN), LIB_MEM_PADDING_ALIGN_NONE, DEF_NULL, p_err); if (*p_err != LIB_MEM_ERR_NONE) { return; } p_alloc->NamePtr = p_name; /* Populate alloc info. */ p_alloc->Size = size; p_alloc->NextPtr = p_seg->AllocInfoHeadPtr; /* Prepend new item in list. */ p_seg->AllocInfoHeadPtr = p_alloc; } #endif /* ********************************************************************************************************* * Mem_DynPoolCreateInternal() * * Description : Creates a dynamic memory pool. * * Argument(s) : p_name Pointer to pool name. * * p_pool Pointer to pool data. * * p_seg Pointer to segment from which to allocate memory. * * blk_size Size of memory block to allocate from pool, in bytes. See Note #1. * * blk_align Required alignment of memory block, in bytes. MUST be a power of 2. * * blk_padding_align Block's padding alignment, in bytes, that will be added at the end * of block's buffer. MUST be a power of 2. LIB_MEM_PADDING_ALIGN_NONE * means no padding. * * blk_qty_init Initial number of elements to be allocated in pool. * * blk_qty_max Maximum number of elements that can be allocated from this pool. Set to * LIB_MEM_BLK_QTY_UNLIMITED if no limit. * * p_err Pointer to variable that will receive the return error code from this function : * * LIB_MEM_ERR_INVALID_BLK_ALIGN Invalid requested block alignment. * LIB_MEM_ERR_INVALID_BLK_SIZE Invalid requested block size. * LIB_MEM_ERR_INVALID_BLK_NBR Invalid requested block quantity max. * LIB_MEM_ERR_NULL_PTR Pool data pointer NULL. * * ------------------RETURNED BY Mem_SegAllocInternal()------------------- * LIB_MEM_ERR_INVALID_MEM_ALIGN Invalid memory block alignment requested. * LIB_MEM_ERR_INVALID_MEM_SIZE Invalid memory block size specified. * LIB_MEM_ERR_NULL_PTR Error or segment data pointer NULL. * LIB_MEM_ERR_SEG_OVF Allocation would overflow memory segment. * * Return(s) : None. * * Caller(s) : Mem_DynPoolCreate(), * Mem_DynPoolCreateHW(). * * Note(s) : (1) 'blk_size' must be big enough to fit a pointer since the pointer to the next free * block is stored in the block itself (only when free/unused). ********************************************************************************************************* */ static void Mem_DynPoolCreateInternal (const CPU_CHAR *p_name, MEM_DYN_POOL *p_pool, MEM_SEG *p_seg, CPU_SIZE_T blk_size, CPU_SIZE_T blk_align, CPU_SIZE_T blk_padding_align, CPU_SIZE_T blk_qty_init, CPU_SIZE_T blk_qty_max, LIB_ERR *p_err) { CPU_INT08U *p_blks; CPU_SIZE_T blk_size_align; CPU_SIZE_T blk_align_worst = DEF_MAX(blk_align, blk_padding_align); #if (LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (p_err == DEF_NULL) { /* Chk for NULL err ptr. */ CPU_SW_EXCEPTION(DEF_NULL); } if (p_pool == DEF_NULL) { /* Chk for NULL pool data ptr. */ *p_err = LIB_MEM_ERR_NULL_PTR; return; } if (blk_size < 1u) { /* Chk for inv blk size. */ *p_err = LIB_MEM_ERR_INVALID_BLK_SIZE; return; } if ((blk_qty_max != LIB_MEM_BLK_QTY_UNLIMITED) && /* Chk for invalid blk qty. */ (blk_qty_init > blk_qty_max)) { *p_err = LIB_MEM_ERR_INVALID_BLK_NBR; return; } if (MATH_IS_PWR2(blk_align) != DEF_YES) { /* Chk for illegal align spec. */ *p_err = LIB_MEM_ERR_INVALID_BLK_ALIGN; return; } #endif /* Calc blk size with align. */ if (blk_size < sizeof(void *)) { /* If size if smaller than ptr ... */ /* ... inc size to ptr size. */ blk_size_align = MATH_ROUND_INC_UP_PWR2(sizeof(void *), blk_align_worst); } else { blk_size_align = MATH_ROUND_INC_UP_PWR2(blk_size, blk_align_worst); } if (blk_qty_init != 0u) { /* Alloc init blks. */ p_blks = (CPU_INT08U *)Mem_SegAllocInternal(p_name, p_seg, blk_size_align * blk_qty_init, blk_align_worst, LIB_MEM_PADDING_ALIGN_NONE, DEF_NULL, p_err); if (*p_err != LIB_MEM_ERR_NONE) { return; } } /* ----------------- CREATE POOL DATA ----------------- */ p_pool->PoolSegPtr = p_seg; p_pool->BlkSize = blk_size; p_pool->BlkAlign = blk_align_worst; p_pool->BlkPaddingAlign = blk_padding_align; p_pool->BlkQtyMax = blk_qty_max; p_pool->BlkAllocCnt = 0u; if (blk_qty_init != 0u) { /* Init free list. */ CPU_SIZE_T i; p_pool->BlkFreePtr = (void *)p_blks; for (i = 0u; i < blk_qty_init - 1u; i++) { *((void **)p_blks) = p_blks + blk_size_align; p_blks += blk_size_align; } *((void **)p_blks) = DEF_NULL; } else { p_pool->BlkFreePtr = DEF_NULL; } #if (LIB_MEM_CFG_DBG_INFO_EN == DEF_ENABLED) p_pool->NamePtr = p_name; #endif *p_err = LIB_MEM_ERR_NONE; } /* ********************************************************************************************************* * Mem_PoolBlkIsValidAddr() * * Description : Calculates if a given memory block address is valid for the memory pool. * * Argument(s) : p_pool Pointer to memory pool structure to validate memory block address. * ------ Argument validated by caller. * * p_mem Pointer to memory block address to validate. * ----- Argument validated by caller. * * Return(s) : DEF_YES, if valid memory pool block address. * * DEF_NO, otherwise. * * Caller(s) : Mem_PoolBlkFree(). * * Note(s) : none. ********************************************************************************************************* */ #if ((LIB_MEM_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) && \ (LIB_MEM_CFG_HEAP_SIZE > 0u)) static CPU_BOOLEAN Mem_PoolBlkIsValidAddr (MEM_POOL *p_pool, void *p_mem) { CPU_ADDR pool_offset; if ((p_mem < p_pool->PoolAddrStart) || (p_mem > p_pool->PoolAddrEnd)) { return (DEF_FALSE); } pool_offset = (CPU_ADDR)p_mem - (CPU_ADDR)p_pool->PoolAddrStart; if (pool_offset % p_pool->BlkSize != 0u) { return (DEF_FALSE); } else { return (DEF_TRUE); } } #endif