Skip to content

Use thread-safe memory manager using C runtime (Newlib) #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions portable/MemMang/heap_useNewlib.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/**
* \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 <stdlib.h> // maps to newlib...
#include <malloc.h> // mallinfo...
#include <errno.h> // 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 _end; // Defined in the linker script
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 )
{
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();
if (heapBytesRemaining == -1) {
heapBytesRemaining = configTOTAL_HEAP_SIZE;
}
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 {};
18 changes: 15 additions & 3 deletions src/FreeRTOSConfig_Default.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand All @@ -93,15 +97,23 @@
#include <stdint.h>
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
#define configUSE_TICK_HOOK 1
#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
Expand Down
7 changes: 5 additions & 2 deletions src/heap.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* @file heap.c
* @author Frederic Pillon <frederic.pillon@st.com> 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
Expand All @@ -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"
Expand Down