Mirror with some correction
Dependencies: mbed FastIO FastPWM USBDevice
NewMalloc/NewMalloc.cpp@116:7a67265d7c19, 2021-10-01 (annotated)
- Committer:
- arnoz
- Date:
- Fri Oct 01 08:19:46 2021 +0000
- Revision:
- 116:7a67265d7c19
- Parent:
- 79:682ae3171a08
- Correct information regarding your last merge
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
mjr | 79:682ae3171a08 | 1 | #include "mbed.h" |
mjr | 79:682ae3171a08 | 2 | #include "NewMalloc.h" |
mjr | 79:682ae3171a08 | 3 | |
mjr | 79:682ae3171a08 | 4 | extern void diagLED(int, int, int); |
mjr | 79:682ae3171a08 | 5 | |
mjr | 79:682ae3171a08 | 6 | // Custom memory allocator. We use our own version of malloc() for more |
mjr | 79:682ae3171a08 | 7 | // efficient memory usage, and to provide diagnostics if we run out of heap. |
mjr | 79:682ae3171a08 | 8 | // |
mjr | 79:682ae3171a08 | 9 | // We can implement a more efficient malloc than the library can because we |
mjr | 79:682ae3171a08 | 10 | // can make an assumption that the library can't: allocations are permanent. |
mjr | 79:682ae3171a08 | 11 | // The normal malloc has to assume that allocations can be freed, so it has |
mjr | 79:682ae3171a08 | 12 | // to track blocks individually. For the purposes of this program, though, |
mjr | 79:682ae3171a08 | 13 | // we don't have to do this because virtually all of our allocations are |
mjr | 79:682ae3171a08 | 14 | // de facto permanent. We only allocate dyanmic memory during setup, and |
mjr | 79:682ae3171a08 | 15 | // once we set things up, we never delete anything. This means that we can |
mjr | 79:682ae3171a08 | 16 | // allocate memory in bare blocks without any bookkeeping overhead. |
mjr | 79:682ae3171a08 | 17 | // |
mjr | 79:682ae3171a08 | 18 | // In addition, we can make a larger overall pool of memory available in |
mjr | 79:682ae3171a08 | 19 | // a custom allocator. The RTL malloc() seems to have a pool of about 3K |
mjr | 79:682ae3171a08 | 20 | // to work with, even though there really seems to be at least 8K left after |
mjr | 79:682ae3171a08 | 21 | // reserving a reasonable amount of space for the stack. |
mjr | 79:682ae3171a08 | 22 | |
mjr | 79:682ae3171a08 | 23 | // halt with a diagnostic display if we run out of memory |
mjr | 79:682ae3171a08 | 24 | void HaltOutOfMem() |
mjr | 79:682ae3171a08 | 25 | { |
mjr | 79:682ae3171a08 | 26 | printf("\r\nOut Of Memory\r\n"); |
mjr | 79:682ae3171a08 | 27 | // halt with the diagnostic display (by looping forever) |
mjr | 79:682ae3171a08 | 28 | for (;;) |
mjr | 79:682ae3171a08 | 29 | { |
mjr | 79:682ae3171a08 | 30 | diagLED(1, 0, 0); |
mjr | 79:682ae3171a08 | 31 | wait_us(200000); |
mjr | 79:682ae3171a08 | 32 | diagLED(1, 0, 1); |
mjr | 79:682ae3171a08 | 33 | wait_us(200000); |
mjr | 79:682ae3171a08 | 34 | } |
mjr | 79:682ae3171a08 | 35 | } |
mjr | 79:682ae3171a08 | 36 | |
mjr | 79:682ae3171a08 | 37 | // For our custom malloc, we take advantage of the known layout of the |
mjr | 79:682ae3171a08 | 38 | // mbed library memory management. The mbed library puts all of the |
mjr | 79:682ae3171a08 | 39 | // static read/write data at the low end of RAM; this includes the |
mjr | 79:682ae3171a08 | 40 | // initialized statics and the "ZI" (zero-initialized) statics. The |
mjr | 79:682ae3171a08 | 41 | // malloc heap starts just after the last static, growing upwards as |
mjr | 79:682ae3171a08 | 42 | // memory is allocated. The stack starts at the top of RAM and grows |
mjr | 79:682ae3171a08 | 43 | // downwards. |
mjr | 79:682ae3171a08 | 44 | // |
mjr | 79:682ae3171a08 | 45 | // To figure out where the free memory starts, we simply call the system |
mjr | 79:682ae3171a08 | 46 | // malloc() to make a dummy allocation the first time we're called, and |
mjr | 79:682ae3171a08 | 47 | // use the address it returns as the start of our free memory pool. The |
mjr | 79:682ae3171a08 | 48 | // first malloc() call presumably returns the lowest byte of the pool in |
mjr | 79:682ae3171a08 | 49 | // the compiler RTL's way of thinking, and from what we know about the |
mjr | 79:682ae3171a08 | 50 | // mbed heap layout, we know everything above this point should be free, |
mjr | 79:682ae3171a08 | 51 | // at least until we reach the lowest address used by the stack. |
mjr | 79:682ae3171a08 | 52 | // |
mjr | 79:682ae3171a08 | 53 | // The ultimate size of the stack is of course dynamic and unpredictable. |
mjr | 79:682ae3171a08 | 54 | // In testing, it appears that we currently need a little over 1K. To be |
mjr | 79:682ae3171a08 | 55 | // conservative, we'll reserve 2K for the stack, by taking it out of the |
mjr | 79:682ae3171a08 | 56 | // space at top of memory we consider fair game for malloc. |
mjr | 79:682ae3171a08 | 57 | // |
mjr | 79:682ae3171a08 | 58 | // Note that we could do this a little more low-level-ly if we wanted. |
mjr | 79:682ae3171a08 | 59 | // The ARM linker provides a pre-defined extern char[] variable named |
mjr | 79:682ae3171a08 | 60 | // Image$$RW_IRAM1$$ZI$$Limit, which is always placed just after the |
mjr | 79:682ae3171a08 | 61 | // last static data variable. In principle, this tells us the start |
mjr | 79:682ae3171a08 | 62 | // of the available malloc pool. However, in testing, it doesn't seem |
mjr | 79:682ae3171a08 | 63 | // safe to use this as the start of our malloc pool. I'm not sure why, |
mjr | 79:682ae3171a08 | 64 | // but probably something in the startup code (either in the C RTL or |
mjr | 79:682ae3171a08 | 65 | // the mbed library) is allocating from the pool before we get control. |
mjr | 79:682ae3171a08 | 66 | // So we won't use that approach. Besides, that would tie us even more |
mjr | 79:682ae3171a08 | 67 | // closely to the ARM compiler. With our malloc() probe approach, we're |
mjr | 79:682ae3171a08 | 68 | // at least portable to any compiler that uses the same basic memory |
mjr | 79:682ae3171a08 | 69 | // layout, with the heap above the statics and the stack at top of |
mjr | 79:682ae3171a08 | 70 | // memory; this isn't universal, but it's very typical. |
mjr | 79:682ae3171a08 | 71 | |
mjr | 79:682ae3171a08 | 72 | extern "C" { |
mjr | 79:682ae3171a08 | 73 | void *$Sub$$malloc(size_t); |
mjr | 79:682ae3171a08 | 74 | void *$Super$$malloc(size_t); |
mjr | 79:682ae3171a08 | 75 | void $Sub$$free(void *); |
mjr | 79:682ae3171a08 | 76 | }; |
mjr | 79:682ae3171a08 | 77 | |
mjr | 79:682ae3171a08 | 78 | // override the system malloc |
mjr | 79:682ae3171a08 | 79 | void *$Sub$$malloc(size_t siz) |
mjr | 79:682ae3171a08 | 80 | { |
mjr | 79:682ae3171a08 | 81 | return xmalloc(siz); |
mjr | 79:682ae3171a08 | 82 | } |
mjr | 79:682ae3171a08 | 83 | |
mjr | 79:682ae3171a08 | 84 | // custom allocator pool |
mjr | 79:682ae3171a08 | 85 | static char *xmalloc_nxt = 0; |
mjr | 79:682ae3171a08 | 86 | size_t xmalloc_rem = 0; |
mjr | 79:682ae3171a08 | 87 | |
mjr | 79:682ae3171a08 | 88 | // custom allocator |
mjr | 79:682ae3171a08 | 89 | void *xmalloc(size_t siz) |
mjr | 79:682ae3171a08 | 90 | { |
mjr | 79:682ae3171a08 | 91 | // initialize the pool if we haven't already |
mjr | 79:682ae3171a08 | 92 | if (xmalloc_nxt == 0) |
mjr | 79:682ae3171a08 | 93 | { |
mjr | 79:682ae3171a08 | 94 | // do a dummy allocation with the system malloc() to find where |
mjr | 79:682ae3171a08 | 95 | // the free pool starts |
mjr | 79:682ae3171a08 | 96 | xmalloc_nxt = (char *)$Super$$malloc(4); |
mjr | 79:682ae3171a08 | 97 | |
mjr | 79:682ae3171a08 | 98 | // figure the amount of space we can use - we have from the base |
mjr | 79:682ae3171a08 | 99 | // of the pool to the top of RAM, minus an allowance for the stack |
mjr | 79:682ae3171a08 | 100 | const uint32_t TopOfRAM = 0x20003000UL; |
mjr | 79:682ae3171a08 | 101 | const uint32_t StackSize = 2*1024; |
mjr | 79:682ae3171a08 | 102 | xmalloc_rem = TopOfRAM - StackSize - uint32_t(xmalloc_nxt); |
mjr | 79:682ae3171a08 | 103 | } |
mjr | 79:682ae3171a08 | 104 | |
mjr | 79:682ae3171a08 | 105 | // align to a dword boundary |
mjr | 79:682ae3171a08 | 106 | siz = (siz + 3) & ~3; |
mjr | 79:682ae3171a08 | 107 | |
mjr | 79:682ae3171a08 | 108 | // make sure we have enough space left for this chunk |
mjr | 79:682ae3171a08 | 109 | if (siz > xmalloc_rem) |
mjr | 79:682ae3171a08 | 110 | HaltOutOfMem(); |
mjr | 79:682ae3171a08 | 111 | |
mjr | 79:682ae3171a08 | 112 | // carve the chunk out of the remaining free pool |
mjr | 79:682ae3171a08 | 113 | char *ret = xmalloc_nxt; |
mjr | 79:682ae3171a08 | 114 | xmalloc_nxt += siz; |
mjr | 79:682ae3171a08 | 115 | xmalloc_rem -= siz; |
mjr | 79:682ae3171a08 | 116 | |
mjr | 79:682ae3171a08 | 117 | // return the allocated space |
mjr | 79:682ae3171a08 | 118 | return ret; |
mjr | 79:682ae3171a08 | 119 | } |
mjr | 79:682ae3171a08 | 120 | |
mjr | 79:682ae3171a08 | 121 | // Remaining free memory |
mjr | 79:682ae3171a08 | 122 | size_t mallocBytesFree() |
mjr | 79:682ae3171a08 | 123 | { |
mjr | 79:682ae3171a08 | 124 | return xmalloc_rem; |
mjr | 79:682ae3171a08 | 125 | } |
mjr | 79:682ae3171a08 | 126 |