An I/O controller for virtual pinball machines: accelerometer nudge sensing, analog plunger input, button input encoding, LedWiz compatible output controls, and more.

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers NewMalloc.cpp Source File

NewMalloc.cpp

00001 #include "mbed.h"
00002 #include "NewMalloc.h"
00003 
00004 extern void diagLED(int, int, int);
00005 
00006 // Custom memory allocator.  We use our own version of malloc() for more
00007 // efficient memory usage, and to provide diagnostics if we run out of heap.
00008 //
00009 // We can implement a more efficient malloc than the library can because we
00010 // can make an assumption that the library can't: allocations are permanent.
00011 // The normal malloc has to assume that allocations can be freed, so it has
00012 // to track blocks individually.  For the purposes of this program, though,
00013 // we don't have to do this because virtually all of our allocations are 
00014 // de facto permanent.  We only allocate dyanmic memory during setup, and 
00015 // once we set things up, we never delete anything.  This means that we can 
00016 // allocate memory in bare blocks without any bookkeeping overhead.
00017 //
00018 // In addition, we can make a larger overall pool of memory available in
00019 // a custom allocator.  The RTL malloc() seems to have a pool of about 3K 
00020 // to work with, even though there really seems to be at least 8K left after 
00021 // reserving a reasonable amount of space for the stack.
00022 
00023 // halt with a diagnostic display if we run out of memory
00024 void HaltOutOfMem()
00025 {
00026     printf("\r\nOut Of Memory\r\n");
00027     // halt with the diagnostic display (by looping forever)
00028     for (;;)
00029     {
00030         diagLED(1, 0, 0);
00031         wait_us(200000);
00032         diagLED(1, 0, 1);
00033         wait_us(200000);
00034     }
00035 }
00036 
00037 // For our custom malloc, we take advantage of the known layout of the
00038 // mbed library memory management.  The mbed library puts all of the
00039 // static read/write data at the low end of RAM; this includes the
00040 // initialized statics and the "ZI" (zero-initialized) statics.  The
00041 // malloc heap starts just after the last static, growing upwards as
00042 // memory is allocated.  The stack starts at the top of RAM and grows
00043 // downwards.  
00044 //
00045 // To figure out where the free memory starts, we simply call the system
00046 // malloc() to make a dummy allocation the first time we're called, and 
00047 // use the address it returns as the start of our free memory pool.  The
00048 // first malloc() call presumably returns the lowest byte of the pool in
00049 // the compiler RTL's way of thinking, and from what we know about the
00050 // mbed heap layout, we know everything above this point should be free,
00051 // at least until we reach the lowest address used by the stack.
00052 //
00053 // The ultimate size of the stack is of course dynamic and unpredictable.
00054 // In testing, it appears that we currently need a little over 1K.  To be
00055 // conservative, we'll reserve 2K for the stack, by taking it out of the
00056 // space at top of memory we consider fair game for malloc.
00057 //
00058 // Note that we could do this a little more low-level-ly if we wanted.
00059 // The ARM linker provides a pre-defined extern char[] variable named 
00060 // Image$$RW_IRAM1$$ZI$$Limit, which is always placed just after the
00061 // last static data variable.  In principle, this tells us the start
00062 // of the available malloc pool.  However, in testing, it doesn't seem
00063 // safe to use this as the start of our malloc pool.  I'm not sure why,
00064 // but probably something in the startup code (either in the C RTL or 
00065 // the mbed library) is allocating from the pool before we get control. 
00066 // So we won't use that approach.  Besides, that would tie us even more
00067 // closely to the ARM compiler.  With our malloc() probe approach, we're
00068 // at least portable to any compiler that uses the same basic memory
00069 // layout, with the heap above the statics and the stack at top of 
00070 // memory; this isn't universal, but it's very typical.
00071 
00072 extern "C" {
00073     void *$Sub$$malloc(size_t);
00074     void *$Super$$malloc(size_t);
00075     void $Sub$$free(void *);
00076 };
00077 
00078 // override the system malloc
00079 void *$Sub$$malloc(size_t siz)
00080 {
00081     return xmalloc(siz);
00082 }
00083 
00084 // custom allocator pool
00085 static char *xmalloc_nxt = 0;
00086 size_t xmalloc_rem = 0;
00087 
00088 // custom allocator
00089 void *xmalloc(size_t siz)
00090 {
00091     // initialize the pool if we haven't already
00092     if (xmalloc_nxt == 0)
00093     {
00094         // do a dummy allocation with the system malloc() to find where
00095         // the free pool starts
00096         xmalloc_nxt = (char *)$Super$$malloc(4);
00097         
00098         // figure the amount of space we can use - we have from the base
00099         // of the pool to the top of RAM, minus an allowance for the stack
00100         const uint32_t TopOfRAM = 0x20003000UL;
00101         const uint32_t StackSize = 2*1024;
00102         xmalloc_rem = TopOfRAM - StackSize - uint32_t(xmalloc_nxt);
00103     }
00104     
00105     // align to a dword boundary
00106     siz = (siz + 3) & ~3;
00107     
00108     // make sure we have enough space left for this chunk
00109     if (siz > xmalloc_rem)
00110         HaltOutOfMem();
00111         
00112     // carve the chunk out of the remaining free pool
00113     char *ret = xmalloc_nxt;
00114     xmalloc_nxt += siz;
00115     xmalloc_rem -= siz;
00116     
00117     // return the allocated space
00118     return ret;
00119 }
00120 
00121 // Remaining free memory
00122 size_t mallocBytesFree() 
00123 {
00124     return xmalloc_rem;
00125 }
00126