Skip to content

Commit 4eca9f9

Browse files
committed
Add heap_useNewlib_ST.c from Dave Nadler to support newlib 3.1
https://nadler.com/embedded/newlibAndFreeRTOS.html https://github.com/DRNadler/FreeRTOS_helpers Signed-off-by: Frederic Pillon <[email protected]>
1 parent 53ea273 commit 4eca9f9

File tree

1 file changed

+284
-0
lines changed

1 file changed

+284
-0
lines changed

portable/MemMang/heap_useNewlib_ST.c

+284
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
/**
2+
* \file heap_useNewlib_ST.c
3+
* \brief Wrappers required to use newlib malloc-family within FreeRTOS.
4+
*
5+
* \par Overview
6+
* Route FreeRTOS memory management functions to newlib's malloc family.
7+
* Thus newlib and FreeRTOS share memory-management routines and memory pool,
8+
* and all newlib's internal memory-management requirements are supported.
9+
*
10+
* \author Dave Nadler
11+
* \date 20-August-2019
12+
* \version 27-Jun-2020 Correct "FreeRTOS.h" capitalization, commentary
13+
* \version 24-Jun-2020 commentary only
14+
* \version 11-Sep-2019 malloc accounting, comments, newlib version check
15+
*
16+
* \see http://www.nadler.com/embedded/newlibAndFreeRTOS.html
17+
* \see https://sourceware.org/newlib/libc.html#Reentrancy
18+
* \see https://sourceware.org/newlib/libc.html#malloc
19+
* \see https://sourceware.org/newlib/libc.html#index-_005f_005fenv_005flock
20+
* \see https://sourceware.org/newlib/libc.html#index-_005f_005fmalloc_005flock
21+
* \see https://sourceforge.net/p/freertos/feature-requests/72/
22+
* \see http://www.billgatliff.com/newlib.html
23+
* \see http://wiki.osdev.org/Porting_Newlib
24+
* \see http://www.embecosm.com/appnotes/ean9/ean9-howto-newlib-1.0.html
25+
*
26+
*
27+
* \copyright
28+
* (c) Dave Nadler 2017-2020, All Rights Reserved.
29+
* Web: http://www.nadler.com
30+
31+
*
32+
* Redistribution and use in source and binary forms, with or without modification,
33+
* are permitted provided that the following conditions are met:
34+
*
35+
* - Use or redistributions of source code must retain the above copyright notice,
36+
* this list of conditions, and the following disclaimer.
37+
*
38+
* - Use or redistributions of source code must retain ALL ORIGINAL COMMENTS, AND
39+
* ANY CHANGES MUST BE DOCUMENTED, INCLUDING:
40+
* - Reason for change (purpose)
41+
* - Functional change
42+
* - Date and author contact
43+
*
44+
* - Redistributions in binary form must reproduce the above copyright notice, this
45+
* list of conditions and the following disclaimer in the documentation and/or
46+
* other materials provided with the distribution.
47+
*
48+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
49+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
50+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
51+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
52+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
53+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
54+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
55+
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
56+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
57+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58+
*/
59+
60+
// ================================================================================================
61+
// ======================================= Configuration ========================================
62+
// These configuration symbols could be provided by from build...
63+
#define STM_VERSION // Replace sane LD symbols with STM CubeMX's poor standard exported LD symbols
64+
#define ISR_STACK_LENGTH_BYTES (configISR_STACK_SIZE_WORDS*4) // bytes to reserve for ISR (MSP) stack
65+
// ======================================= Configuration ========================================
66+
// ================================================================================================
67+
68+
69+
#include <stdlib.h> // maps to newlib...
70+
#include <malloc.h> // mallinfo...
71+
#include <errno.h> // ENOMEM
72+
#include <stdbool.h>
73+
#include <stddef.h>
74+
75+
#include "newlib.h"
76+
#if ((__NEWLIB__ == 2) && (__NEWLIB_MINOR__ < 5)) ||((__NEWLIB__ == 3) && (__NEWLIB_MINOR__ > 1))
77+
#warning "This wrapper was verified for newlib versions 2.5 - 3.1; please ensure newlib's external requirements for malloc-family are unchanged!"
78+
#endif
79+
80+
#include "FreeRTOS.h" // defines public interface we're implementing here
81+
#if !defined(configUSE_NEWLIB_REENTRANT) || (configUSE_NEWLIB_REENTRANT!=1)
82+
#warning "#define configUSE_NEWLIB_REENTRANT 1 // Required for thread-safety of newlib sprintf, dtoa, strtok, etc..."
83+
// If you're *REALLY* sure you don't need FreeRTOS's newlib reentrancy support, comment out the above warning...
84+
#endif
85+
#include "task.h"
86+
87+
// ================================================================================================
88+
// External routines required by newlib's malloc (sbrk/_sbrk, __malloc_lock/unlock)
89+
// ================================================================================================
90+
91+
// Simplistic sbrk implementations assume stack grows downwards from top of memory,
92+
// and heap grows upwards starting just after BSS.
93+
// FreeRTOS normally allocates task stacks from a pool placed within BSS or DATA.
94+
// Thus within a FreeRTOS task, stack pointer is always below end of BSS.
95+
// When using this module, stacks are allocated from malloc pool, still always prior
96+
// current unused heap area...
97+
98+
// Doesn't work with FreeRTOS: STM CubeMX 2018-2019 Incorrect Implementation
99+
#if 0
100+
caddr_t _sbrk(int incr)
101+
{
102+
extern char end asm("end"); // From linker: lowest unused RAM address, just beyond end of BSS.
103+
static char *heap_end;
104+
char *prev_heap_end;
105+
if (heap_end == 0) heap_end = &end;
106+
prev_heap_end = heap_end;
107+
if (heap_end + incr > stack_ptr) // Fails here: always true for FreeRTOS task stacks
108+
{
109+
errno = ENOMEM; // ...so first call inside a FreeRTOS task lands here
110+
return (caddr_t) -1;
111+
}
112+
heap_end += incr;
113+
return (caddr_t) prev_heap_end;
114+
}
115+
#endif
116+
117+
register char * stack_ptr asm("sp");
118+
119+
#ifdef STM_VERSION // Use STM CubeMX LD symbols for heap+stack area
120+
// To avoid modifying STM LD file (and then having CubeMX trash it), use available STM symbols
121+
// Unfortunately STM does not provide standardized markers for RAM suitable for heap!
122+
// STM CubeMX-generated LD files provide the following symbols:
123+
// end /* aligned first word beyond BSS */
124+
// _estack /* one word beyond end of "RAM" Ram type memory, for STM32F429 0x20030000 */
125+
// Kludge below uses CubeMX-generated symbols instead of sane LD definitions
126+
#define __HeapBase end
127+
#define __HeapLimit _estack // In K64F LD this is already adjusted for ISR stack space...
128+
static int heapBytesRemaining;
129+
// no DRN HEAP_SIZE symbol from LD... // that's (&__HeapLimit)-(&__HeapBase)
130+
uint32_t TotalHeapSize; // publish for diagnostic routines; filled in first _sbrk call.
131+
#else
132+
// Note: DRN's K64F LD provided: __StackTop (byte beyond end of memory), __StackLimit, HEAP_SIZE, STACK_SIZE
133+
// __HeapLimit was already adjusted to be below reserved stack area.
134+
extern char HEAP_SIZE; // make sure to define this symbol in linker LD command file
135+
static int heapBytesRemaining = (int)&HEAP_SIZE; // that's (&__HeapLimit)-(&__HeapBase)
136+
#endif
137+
138+
139+
#ifdef MALLOCS_INSIDE_ISRs // STM code to avoid malloc within ISR (USB CDC stack)
140+
// We can't use vTaskSuspendAll() within an ISR.
141+
// STM's stunningly bad coding malpractice calls malloc within ISRs (for example, on USB connect function USBD_CDC_Init)
142+
// So, we must just suspend/resume interrupts, lengthening max interrupt response time, aarrggg...
143+
#define DRN_ENTER_CRITICAL_SECTION(_usis) { _usis = taskENTER_CRITICAL_FROM_ISR(); } // Disables interrupts (after saving prior state)
144+
#define DRN_EXIT_CRITICAL_SECTION(_usis) { taskEXIT_CRITICAL_FROM_ISR(_usis); } // Re-enables interrupts (unless already disabled prior taskENTER_CRITICAL)
145+
#else
146+
#define DRN_ENTER_CRITICAL_SECTION(_usis) vTaskSuspendAll(); // Note: safe to use before FreeRTOS scheduler started, but not in ISR
147+
#define DRN_EXIT_CRITICAL_SECTION(_usis) xTaskResumeAll(); // Note: safe to use before FreeRTOS scheduler started, but not in ISR
148+
#endif
149+
150+
#ifndef NDEBUG
151+
static int totalBytesProvidedBySBRK = 0;
152+
#endif
153+
extern char __HeapBase, __HeapLimit; // symbols from linker LD command file
154+
155+
// Use of vTaskSuspendAll() in _sbrk_r() is normally redundant, as newlib malloc family routines call
156+
// __malloc_lock before calling _sbrk_r(). Note vTaskSuspendAll/xTaskResumeAll support nesting.
157+
158+
//! _sbrk_r version supporting reentrant newlib (depends upon above symbols defined by linker control file).
159+
void * _sbrk_r(struct _reent *pReent, int incr) {
160+
#ifdef MALLOCS_INSIDE_ISRs // block interrupts during free-storage use
161+
UBaseType_t usis; // saved interrupt status
162+
#endif
163+
static char *currentHeapEnd = &__HeapBase;
164+
#ifdef STM_VERSION // Use STM CubeMX LD symbols for heap
165+
if(TotalHeapSize==0) {
166+
TotalHeapSize = heapBytesRemaining = (int)((&__HeapLimit)-(&__HeapBase))-ISR_STACK_LENGTH_BYTES;
167+
};
168+
#endif
169+
char* limit = (xTaskGetSchedulerState()==taskSCHEDULER_NOT_STARTED) ?
170+
stack_ptr : // Before scheduler is started, limit is stack pointer (risky!)
171+
&__HeapLimit-ISR_STACK_LENGTH_BYTES; // Once running, OK to reuse all remaining RAM except ISR stack (MSP) stack
172+
DRN_ENTER_CRITICAL_SECTION(usis);
173+
if (currentHeapEnd + incr > limit) {
174+
// Ooops, no more memory available...
175+
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
176+
{
177+
extern void vApplicationMallocFailedHook( void );
178+
DRN_EXIT_CRITICAL_SECTION(usis);
179+
vApplicationMallocFailedHook();
180+
}
181+
#elif defined(configHARD_STOP_ON_MALLOC_FAILURE)
182+
// If you want to alert debugger or halt...
183+
// WARNING: brkpt instruction may prevent watchdog operation...
184+
while(1) { __asm("bkpt #0"); }; // Stop in GUI as if at a breakpoint (if debugging, otherwise loop forever)
185+
#else
186+
// Default, if you prefer to believe your application will gracefully trap out-of-memory...
187+
pReent->_errno = ENOMEM; // newlib's thread-specific errno
188+
DRN_EXIT_CRITICAL_SECTION(usis);
189+
#endif
190+
return (char *)-1; // the malloc-family routine that called sbrk will return 0
191+
}
192+
// 'incr' of memory is available: update accounting and return it.
193+
char *previousHeapEnd = currentHeapEnd;
194+
currentHeapEnd += incr;
195+
heapBytesRemaining -= incr;
196+
#ifndef NDEBUG
197+
totalBytesProvidedBySBRK += incr;
198+
#endif
199+
DRN_EXIT_CRITICAL_SECTION(usis);
200+
return (char *) previousHeapEnd;
201+
}
202+
//! non-reentrant sbrk uses is actually reentrant by using current context
203+
// ... because the current _reent structure is pointed to by global _impure_ptr
204+
char * sbrk(int incr) { return _sbrk_r(_impure_ptr, incr); }
205+
//! _sbrk is a synonym for sbrk.
206+
char * _sbrk(int incr) { return sbrk(incr); };
207+
208+
#ifdef MALLOCS_INSIDE_ISRs // block interrupts during free-storage use
209+
static UBaseType_t malLock_uxSavedInterruptStatus;
210+
#endif
211+
void __malloc_lock(struct _reent *r) {
212+
#if defined(MALLOCS_INSIDE_ISRs)
213+
DRN_ENTER_CRITICAL_SECTION(malLock_uxSavedInterruptStatus);
214+
#else
215+
bool insideAnISR = xPortIsInsideInterrupt();
216+
configASSERT( !insideAnISR ); // Make damn sure no more mallocs inside ISRs!!
217+
vTaskSuspendAll();
218+
#endif
219+
};
220+
void __malloc_unlock(struct _reent *r) {
221+
#if defined(MALLOCS_INSIDE_ISRs)
222+
DRN_EXIT_CRITICAL_SECTION(malLock_uxSavedInterruptStatus);
223+
#else
224+
(void)xTaskResumeAll();
225+
#endif
226+
};
227+
228+
// newlib also requires implementing locks for the application's environment memory space,
229+
// accessed by newlib's setenv() and getenv() functions.
230+
// As these are trivial functions, momentarily suspend task switching (rather than semaphore).
231+
// Not required (and trimmed by linker) in applications not using environment variables.
232+
// ToDo: Move __env_lock/unlock to a separate newlib helper file.
233+
void __env_lock() { vTaskSuspendAll(); };
234+
void __env_unlock() { (void)xTaskResumeAll(); };
235+
236+
#if 1 // Provide malloc debug and accounting wrappers
237+
/// /brief Wrap malloc/malloc_r to help debug who requests memory and why.
238+
/// To use these, add linker options: -Xlinker --wrap=malloc -Xlinker --wrap=_malloc_r
239+
// Note: These functions are normally unused and stripped by linker.
240+
size_t TotalMallocdBytes;
241+
int MallocCallCnt;
242+
static bool inside_malloc;
243+
void *__wrap_malloc(size_t nbytes) {
244+
extern void * __real_malloc(size_t nbytes);
245+
MallocCallCnt++;
246+
TotalMallocdBytes += nbytes;
247+
inside_malloc = true;
248+
void *p = __real_malloc(nbytes); // will call malloc_r...
249+
inside_malloc = false;
250+
return p;
251+
};
252+
void *__wrap__malloc_r(void *reent, size_t nbytes) {
253+
extern void * __real__malloc_r(size_t nbytes);
254+
if(!inside_malloc) {
255+
MallocCallCnt++;
256+
TotalMallocdBytes += nbytes;
257+
};
258+
void *p = __real__malloc_r(nbytes);
259+
return p;
260+
};
261+
#endif
262+
263+
// ================================================================================================
264+
// Implement FreeRTOS's memory API using newlib-provided malloc family.
265+
// ================================================================================================
266+
267+
void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION {
268+
void *p = malloc(xSize);
269+
return p;
270+
}
271+
void vPortFree( void *pv ) PRIVILEGED_FUNCTION {
272+
free(pv);
273+
};
274+
275+
size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION {
276+
struct mallinfo mi = mallinfo(); // available space now managed by newlib
277+
return mi.fordblks + heapBytesRemaining; // plus space not yet handed to newlib by sbrk
278+
}
279+
280+
// GetMinimumEverFree is not available in newlib's malloc implementation.
281+
// So, no implementation is provided: size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION;
282+
283+
//! No implementation needed, but stub provided in case application already calls vPortInitialiseBlocks
284+
void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION {};

0 commit comments

Comments
 (0)