From 3fe0c484dc2e1b2e5a01e5450987fe44fa785579 Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Fri, 12 Oct 2018 09:23:57 +0200 Subject: [PATCH 1/5] Add Newlib heap implementation using FreeRTOS memory API Original file from Dave Nadler: http://www.nadler.com/embedded/newlibAndFreeRTOS.html Signed-off-by: Frederic.Pillon --- portable/MemMang/heap_useNewlib.c | 155 ++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 portable/MemMang/heap_useNewlib.c diff --git a/portable/MemMang/heap_useNewlib.c b/portable/MemMang/heap_useNewlib.c new file mode 100644 index 0000000..be12476 --- /dev/null +++ b/portable/MemMang/heap_useNewlib.c @@ -0,0 +1,155 @@ +/** + * \file heap_useNewlib.c + * \brief Wrappers required to use newlib malloc-family within FreeRTOS. + * + * \par Overview + * Route FreeRTOS memory management functions to newlib's malloc family. + * Thus newlib and FreeRTOS share memory-management routines and memory pool, + * and all newlib's internal memory-management requirements are supported. + * + * \author Dave Nadler + * \date 2-July-2017 + * + * \see http://www.nadler.com/embedded/newlibAndFreeRTOS.html + * \see https://sourceware.org/newlib/libc.html#Reentrancy + * \see https://sourceware.org/newlib/libc.html#malloc + * \see https://sourceware.org/newlib/libc.html#index-_005f_005fenv_005flock + * \see https://sourceware.org/newlib/libc.html#index-_005f_005fmalloc_005flock + * \see https://sourceforge.net/p/freertos/feature-requests/72/ + * \see http://www.billgatliff.com/newlib.html + * \see http://wiki.osdev.org/Porting_Newlib + * \see http://www.embecosm.com/appnotes/ean9/ean9-howto-newlib-1.0.html + * + * + * \copyright + * (c) Dave Nadler 2017, All Rights Reserved. + * Web: http://www.nadler.com + * email: drn@nadler.com + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Use or redistributions of source code must retain the above copyright notice, + * this list of conditions, ALL ORIGINAL COMMENTS, and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include // maps to newlib... +#include // mallinfo... +#include // ENOMEM + +#include "newlib.h" +#if (__NEWLIB__ != 2) || (__NEWLIB_MINOR__ != 5) + #warning "This wrapper was verified for newlib version 2.5.0; please ensure newlib's external requirements for malloc-family are unchanged!" +#endif + +#include "freeRTOS.h" // defines public interface we're implementing here +#if !defined(configUSE_NEWLIB_REENTRANT) || (configUSE_NEWLIB_REENTRANT!=1) + #warning "#define configUSE_NEWLIB_REENTRANT 1 // Required for thread-safety of newlib sprintf, strtok, etc..." + // If you're *really* sure you don't need FreeRTOS's newlib reentrancy support, remove this warning... +#endif +#include "task.h" + +// ================================================================================================ +// External routines required by newlib's malloc (sbrk/_sbrk, __malloc_lock/unlock) +// ================================================================================================ + +#ifndef NDEBUG + static int totalBytesProvidedBySBRK = 0; +#endif +extern char __HeapBase, __HeapLimit, HEAP_SIZE; // make sure to define these symbols in linker command file +static int heapBytesRemaining = (int)&HEAP_SIZE; // that's (&__HeapLimit)-(&__HeapBase) + +//! sbrk/_sbrk version supporting reentrant newlib (depends upon above symbols defined by linker control file). +char * sbrk(int incr) { + static char *currentHeapEnd = &__HeapBase; + vTaskSuspendAll(); // Note: safe to use before FreeRTOS scheduler started + char *previousHeapEnd = currentHeapEnd; + if (currentHeapEnd + incr > &__HeapLimit) { + #if( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + #elif 0 + // If you want to alert debugger or halt... + while(1) { __asm("bkpt #0"); }; // Stop in GUI as if at a breakpoint (if debugging, otherwise loop forever) + #else + // If you prefer to believe your application will gracefully trap out-of-memory... + _impure_ptr->_errno = ENOMEM; // newlib's thread-specific errno + xTaskResumeAll(); + #endif + return (char *)-1; // the malloc-family routine that called sbrk will return 0 + } + currentHeapEnd += incr; + heapBytesRemaining -= incr; + #ifndef NDEBUG + totalBytesProvidedBySBRK += incr; + #endif + xTaskResumeAll(); + return (char *) previousHeapEnd; +} +//! Synonym for sbrk. +char * _sbrk(int incr) { return sbrk(incr); }; + +void __malloc_lock() { vTaskSuspendAll(); }; +void __malloc_unlock() { (void)xTaskResumeAll(); }; + +// newlib also requires implementing locks for the application's environment memory space, +// accessed by newlib's setenv() and getenv() functions. +// As these are trivial functions, momentarily suspend task switching (rather than semaphore). +// ToDo: Move __env_lock/unlock to a separate newlib helper file. +void __env_lock() { vTaskSuspendAll(); }; +void __env_unlock() { (void)xTaskResumeAll(); }; + +/// /brief Wrap malloc/malloc_r to help debug who requests memory and why. +/// Add to the linker command line: -Xlinker --wrap=malloc -Xlinker --wrap=_malloc_r +// Note: These functions are normally unused and stripped by linker. +void *__wrap_malloc(size_t nbytes) { + extern void * __real_malloc(size_t nbytes); + void *p = __real_malloc(nbytes); // Solely for debug breakpoint... + return p; +}; +void *__wrap__malloc_r(void *reent, size_t nbytes) { + extern void * __real__malloc_r(size_t nbytes); + void *p = __real__malloc_r(nbytes); // Solely for debug breakpoint... + return p; +}; + + +// ================================================================================================ +// Implement FreeRTOS's memory API using newlib-provided malloc family. +// ================================================================================================ + +void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION { + void *p = malloc(xSize); + return p; +} +void vPortFree( void *pv ) PRIVILEGED_FUNCTION { + free(pv); +}; + +size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION { + struct mallinfo mi = mallinfo(); + return mi.fordblks + heapBytesRemaining; +} + +// GetMinimumEverFree is not available in newlib's malloc implementation. +// So, no implementation provided: size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION; + +//! No implementation needed, but stub provided in case application already calls vPortInitialiseBlocks +void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION {}; From 915c7e0354027235b9ec173d3865a4980686777c Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Fri, 12 Oct 2018 10:01:15 +0200 Subject: [PATCH 2/5] Update default config to use linker definition Signed-off-by: Frederic.Pillon --- src/FreeRTOSConfig_Default.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/FreeRTOSConfig_Default.h b/src/FreeRTOSConfig_Default.h index 8e68ccf..5a7ae20 100644 --- a/src/FreeRTOSConfig_Default.h +++ b/src/FreeRTOSConfig_Default.h @@ -93,6 +93,9 @@ #include extern uint32_t SystemCoreClock; #endif +extern char _end; /* Defined in the linker script */ +extern char _estack; /* Defined in the linker script */ +extern char _Min_Stack_Size; /* Defined in the linker script */ #define configUSE_PREEMPTION 1 #define configUSE_IDLE_HOOK 1 @@ -100,8 +103,13 @@ #define configCPU_CLOCK_HZ (SystemCoreClock) #define configTICK_RATE_HZ ((TickType_t)1000) #define configMAX_PRIORITIES (7) -#define configMINIMAL_STACK_SIZE ((uint16_t)128) -#define configTOTAL_HEAP_SIZE ((size_t)(15 * 1024)) +/* + * _Min_Stack_Size is often set to 0x400 in the linker script + * Use it divided by 8 to set minmimal stack size of a task to 128 by default. + * End user will have to properly configure those value depending to their needs. + */ +#define configMINIMAL_STACK_SIZE ((uint16_t)((uint32_t)&_Min_Stack_Size/8)) +#define configTOTAL_HEAP_SIZE ((size_t)(&_estack - _Min_Stack_Size - &_end)) #define configMAX_TASK_NAME_LEN (16) #define configUSE_TRACE_FACILITY 1 #define configUSE_16_BIT_TICKS 0 From 37cc3a00e2ef51698af7a86ef6833a7c4e15d09f Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Fri, 12 Oct 2018 10:22:45 +0200 Subject: [PATCH 3/5] Update Newlib heap implementation Use STM32 linker and FreeRTOS config definitions. Signed-off-by: Frederic.Pillon --- portable/MemMang/heap_useNewlib.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/portable/MemMang/heap_useNewlib.c b/portable/MemMang/heap_useNewlib.c index be12476..7224908 100644 --- a/portable/MemMang/heap_useNewlib.c +++ b/portable/MemMang/heap_useNewlib.c @@ -57,7 +57,7 @@ #warning "This wrapper was verified for newlib version 2.5.0; please ensure newlib's external requirements for malloc-family are unchanged!" #endif -#include "freeRTOS.h" // defines public interface we're implementing here +#include "FreeRTOS.h" // defines public interface we're implementing here #if !defined(configUSE_NEWLIB_REENTRANT) || (configUSE_NEWLIB_REENTRANT!=1) #warning "#define configUSE_NEWLIB_REENTRANT 1 // Required for thread-safety of newlib sprintf, strtok, etc..." // If you're *really* sure you don't need FreeRTOS's newlib reentrancy support, remove this warning... @@ -71,15 +71,15 @@ #ifndef NDEBUG static int totalBytesProvidedBySBRK = 0; #endif -extern char __HeapBase, __HeapLimit, HEAP_SIZE; // make sure to define these symbols in linker command file -static int heapBytesRemaining = (int)&HEAP_SIZE; // that's (&__HeapLimit)-(&__HeapBase) +extern char _end; // Defined in the linker script +static int heapBytesRemaining = configTOTAL_HEAP_SIZE; // that's (&__HeapLimit)-(&__HeapBase) //! sbrk/_sbrk version supporting reentrant newlib (depends upon above symbols defined by linker control file). char * sbrk(int incr) { - static char *currentHeapEnd = &__HeapBase; + static char *currentHeapEnd = &_end; vTaskSuspendAll(); // Note: safe to use before FreeRTOS scheduler started char *previousHeapEnd = currentHeapEnd; - if (currentHeapEnd + incr > &__HeapLimit) { + if (currentHeapEnd + incr > &_end + configTOTAL_HEAP_SIZE) { #if( configUSE_MALLOC_FAILED_HOOK == 1 ) { extern void vApplicationMallocFailedHook( void ); From 1715641c1fccea1568a9ebbdf967372391af9872 Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Fri, 12 Oct 2018 11:06:21 +0200 Subject: [PATCH 4/5] Use thread-safe memory manager using C runtime (Newlib) By default, the heap_useNewlib.c will be used. As suggested by @ppescher, use -1 as the number of heap implementations might grow in future versions. Signed-off-by: Frederic.Pillon --- src/FreeRTOSConfig_Default.h | 6 +++++- src/heap.c | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/FreeRTOSConfig_Default.h b/src/FreeRTOSConfig_Default.h index 5a7ae20..52dda35 100644 --- a/src/FreeRTOSConfig_Default.h +++ b/src/FreeRTOSConfig_Default.h @@ -84,7 +84,11 @@ *----------------------------------------------------------*/ /* Begin custom definitions for STM32 */ -/* Default (3) Memory allocation implementations (heap_[1-5].c) */ +/* Define memory allocation implementations to use: + * 1 to 5 for heap_[1-5].c + * -1 for heap_useNewlib.c + * Default -1 see heap.c + */ /*#define configMEMMANG_HEAP_NB 3*/ /* End custom definitions for STM32 */ diff --git a/src/heap.c b/src/heap.c index d2dd874..6a25e7a 100644 --- a/src/heap.c +++ b/src/heap.c @@ -2,6 +2,7 @@ * @file heap.c * @author Frederic Pillon for STMicroelectronics. * @brief Provide Memory allocation implementations included in the FreeRTOS source + * heap_useNewlib - thread-safe memory manager using C runtime (Newlib) * heap_1 - the very simplest, does not permit memory to be freed * heap_2 - permits memory to be freed, but not does coalescence adjacent free blocks. * heap_3 - simply wraps the standard malloc() and free() for thread safety @@ -11,10 +12,12 @@ #include "FreeRTOS.h" #ifndef configMEMMANG_HEAP_NB -#define configMEMMANG_HEAP_NB 3 +#define configMEMMANG_HEAP_NB -1 #endif -#if (configMEMMANG_HEAP_NB == 1) +#if (configMEMMANG_HEAP_NB == -1) +#include "../portable/MemMang/heap_useNewlib.c" +#elif (configMEMMANG_HEAP_NB == 1) #include "../portable/MemMang/heap_1.c" #elif (configMEMMANG_HEAP_NB == 2) #include "../portable/MemMang/heap_2.c" From f7c63adeb15f531c00de1393e99e78de4275bda1 Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Fri, 12 Oct 2018 11:38:24 +0200 Subject: [PATCH 5/5] Fix heapBytesRemaining default value configTOTAL_HEAP_SIZE could be not constant Signed-off-by: Frederic.Pillon --- portable/MemMang/heap_useNewlib.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/portable/MemMang/heap_useNewlib.c b/portable/MemMang/heap_useNewlib.c index 7224908..6a00d7e 100644 --- a/portable/MemMang/heap_useNewlib.c +++ b/portable/MemMang/heap_useNewlib.c @@ -72,12 +72,15 @@ static int totalBytesProvidedBySBRK = 0; #endif extern char _end; // Defined in the linker script -static int heapBytesRemaining = configTOTAL_HEAP_SIZE; // that's (&__HeapLimit)-(&__HeapBase) +static int heapBytesRemaining = -1; // configTOTAL_HEAP_SIZE is not constant will be init later //! sbrk/_sbrk version supporting reentrant newlib (depends upon above symbols defined by linker control file). char * sbrk(int incr) { static char *currentHeapEnd = &_end; vTaskSuspendAll(); // Note: safe to use before FreeRTOS scheduler started + if (heapBytesRemaining == -1) { + heapBytesRemaining = configTOTAL_HEAP_SIZE; + } char *previousHeapEnd = currentHeapEnd; if (currentHeapEnd + incr > &_end + configTOTAL_HEAP_SIZE) { #if( configUSE_MALLOC_FAILED_HOOK == 1 ) @@ -145,6 +148,9 @@ void vPortFree( void *pv ) PRIVILEGED_FUNCTION { size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION { struct mallinfo mi = mallinfo(); + if (heapBytesRemaining == -1) { + heapBytesRemaining = configTOTAL_HEAP_SIZE; + } return mi.fordblks + heapBytesRemaining; }