No Changes
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
source/core/MicroBitHeapAllocator.cpp@75:ae9a17da71af, 2017-01-13 (annotated)
- Committer:
- Asimov
- Date:
- Fri Jan 13 21:14:06 2017 +0000
- Revision:
- 75:ae9a17da71af
- Parent:
- 1:8aa5cdb4ab67
No Changes
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Jonathan Austin |
1:8aa5cdb4ab67 | 1 | /* |
Jonathan Austin |
1:8aa5cdb4ab67 | 2 | The MIT License (MIT) |
Jonathan Austin |
1:8aa5cdb4ab67 | 3 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 4 | Copyright (c) 2016 British Broadcasting Corporation. |
Jonathan Austin |
1:8aa5cdb4ab67 | 5 | This software is provided by Lancaster University by arrangement with the BBC. |
Jonathan Austin |
1:8aa5cdb4ab67 | 6 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 7 | Permission is hereby granted, free of charge, to any person obtaining a |
Jonathan Austin |
1:8aa5cdb4ab67 | 8 | copy of this software and associated documentation files (the "Software"), |
Jonathan Austin |
1:8aa5cdb4ab67 | 9 | to deal in the Software without restriction, including without limitation |
Jonathan Austin |
1:8aa5cdb4ab67 | 10 | the rights to use, copy, modify, merge, publish, distribute, sublicense, |
Jonathan Austin |
1:8aa5cdb4ab67 | 11 | and/or sell copies of the Software, and to permit persons to whom the |
Jonathan Austin |
1:8aa5cdb4ab67 | 12 | Software is furnished to do so, subject to the following conditions: |
Jonathan Austin |
1:8aa5cdb4ab67 | 13 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 14 | The above copyright notice and this permission notice shall be included in |
Jonathan Austin |
1:8aa5cdb4ab67 | 15 | all copies or substantial portions of the Software. |
Jonathan Austin |
1:8aa5cdb4ab67 | 16 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
Jonathan Austin |
1:8aa5cdb4ab67 | 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
Jonathan Austin |
1:8aa5cdb4ab67 | 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
Jonathan Austin |
1:8aa5cdb4ab67 | 20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
Jonathan Austin |
1:8aa5cdb4ab67 | 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
Jonathan Austin |
1:8aa5cdb4ab67 | 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
Jonathan Austin |
1:8aa5cdb4ab67 | 23 | DEALINGS IN THE SOFTWARE. |
Jonathan Austin |
1:8aa5cdb4ab67 | 24 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 25 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 26 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 27 | * A simple 32 bit block based memory allocator. This allows one or more memory segments to |
Jonathan Austin |
1:8aa5cdb4ab67 | 28 | * be designated as heap storage, and is designed to run in a static memory area or inside the standard C |
Jonathan Austin |
1:8aa5cdb4ab67 | 29 | * heap for use by the micro:bit runtime. This is required for several reasons: |
Jonathan Austin |
1:8aa5cdb4ab67 | 30 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 31 | * 1) It reduces memory fragmentation due to the high churn sometime placed on the heap |
Jonathan Austin |
1:8aa5cdb4ab67 | 32 | * by ManagedTypes, fibers and user code. Underlying heap implentations are often have very simplistic |
Jonathan Austin |
1:8aa5cdb4ab67 | 33 | * allocation pilicies and suffer from fragmentation in prolonged use - which can cause programs to |
Jonathan Austin |
1:8aa5cdb4ab67 | 34 | * stop working after a period of time. The algorithm implemented here is simple, but highly tolerant to |
Jonathan Austin |
1:8aa5cdb4ab67 | 35 | * large amounts of churn. |
Jonathan Austin |
1:8aa5cdb4ab67 | 36 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 37 | * 2) It allows us to reuse the 8K of SRAM set aside for SoftDevice as additional heap storage |
Jonathan Austin |
1:8aa5cdb4ab67 | 38 | * when BLE is not in use. |
Jonathan Austin |
1:8aa5cdb4ab67 | 39 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 40 | * 3) It gives a simple example of how memory allocation works! :-) |
Jonathan Austin |
1:8aa5cdb4ab67 | 41 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 42 | * P.S. This is a very simple allocator, therefore not without its weaknesses. Why don't you consider |
Jonathan Austin |
1:8aa5cdb4ab67 | 43 | * what these are, and consider the tradeoffs against simplicity... |
Jonathan Austin |
1:8aa5cdb4ab67 | 44 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 45 | * @note The need for this should be reviewed in the future, if a different memory allocator is |
Jonathan Austin |
1:8aa5cdb4ab67 | 46 | * made availiable in the mbed platform. |
Jonathan Austin |
1:8aa5cdb4ab67 | 47 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 48 | * TODO: Consider caching recently freed blocks to improve allocation time. |
Jonathan Austin |
1:8aa5cdb4ab67 | 49 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 50 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 51 | #include "MicroBitConfig.h" |
Jonathan Austin |
1:8aa5cdb4ab67 | 52 | #include "MicroBitHeapAllocator.h" |
Jonathan Austin |
1:8aa5cdb4ab67 | 53 | #include "MicroBitDevice.h" |
Jonathan Austin |
1:8aa5cdb4ab67 | 54 | #include "ErrorNo.h" |
Jonathan Austin |
1:8aa5cdb4ab67 | 55 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 56 | struct HeapDefinition |
Jonathan Austin |
1:8aa5cdb4ab67 | 57 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 58 | uint32_t *heap_start; // Physical address of the start of this heap. |
Jonathan Austin |
1:8aa5cdb4ab67 | 59 | uint32_t *heap_end; // Physical address of the end of this heap. |
Jonathan Austin |
1:8aa5cdb4ab67 | 60 | }; |
Jonathan Austin |
1:8aa5cdb4ab67 | 61 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 62 | // A list of all active heap regions, and their dimensions in memory. |
Jonathan Austin |
1:8aa5cdb4ab67 | 63 | HeapDefinition heap[MICROBIT_MAXIMUM_HEAPS] = { }; |
Jonathan Austin |
1:8aa5cdb4ab67 | 64 | uint8_t heap_count = 0; |
Jonathan Austin |
1:8aa5cdb4ab67 | 65 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 66 | #if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG) |
Jonathan Austin |
1:8aa5cdb4ab67 | 67 | // Diplays a usage summary about a given heap... |
Jonathan Austin |
1:8aa5cdb4ab67 | 68 | void microbit_heap_print(HeapDefinition &heap) |
Jonathan Austin |
1:8aa5cdb4ab67 | 69 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 70 | uint32_t blockSize; |
Jonathan Austin |
1:8aa5cdb4ab67 | 71 | uint32_t *block; |
Jonathan Austin |
1:8aa5cdb4ab67 | 72 | int totalFreeBlock = 0; |
Jonathan Austin |
1:8aa5cdb4ab67 | 73 | int totalUsedBlock = 0; |
Jonathan Austin |
1:8aa5cdb4ab67 | 74 | int cols = 0; |
Jonathan Austin |
1:8aa5cdb4ab67 | 75 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 76 | if (heap.heap_start == NULL) |
Jonathan Austin |
1:8aa5cdb4ab67 | 77 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 78 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("--- HEAP NOT INITIALISED ---\n"); |
Jonathan Austin |
1:8aa5cdb4ab67 | 79 | return; |
Jonathan Austin |
1:8aa5cdb4ab67 | 80 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 81 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 82 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("heap_start : %p\n", heap.heap_start); |
Jonathan Austin |
1:8aa5cdb4ab67 | 83 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("heap_end : %p\n", heap.heap_end); |
Jonathan Austin |
1:8aa5cdb4ab67 | 84 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("heap_size : %d\n", (int)heap.heap_end - (int)heap.heap_start); |
Jonathan Austin |
1:8aa5cdb4ab67 | 85 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 86 | // Disable IRQ temporarily to ensure no race conditions! |
Jonathan Austin |
1:8aa5cdb4ab67 | 87 | __disable_irq(); |
Jonathan Austin |
1:8aa5cdb4ab67 | 88 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 89 | block = heap.heap_start; |
Jonathan Austin |
1:8aa5cdb4ab67 | 90 | while (block < heap.heap_end) |
Jonathan Austin |
1:8aa5cdb4ab67 | 91 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 92 | blockSize = *block & ~MICROBIT_HEAP_BLOCK_FREE; |
Jonathan Austin |
1:8aa5cdb4ab67 | 93 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("[%c:%d] ", *block & MICROBIT_HEAP_BLOCK_FREE ? 'F' : 'U', blockSize*4); |
Jonathan Austin |
1:8aa5cdb4ab67 | 94 | if (cols++ == 20) |
Jonathan Austin |
1:8aa5cdb4ab67 | 95 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 96 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("\n"); |
Jonathan Austin |
1:8aa5cdb4ab67 | 97 | cols = 0; |
Jonathan Austin |
1:8aa5cdb4ab67 | 98 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 99 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 100 | if (*block & MICROBIT_HEAP_BLOCK_FREE) |
Jonathan Austin |
1:8aa5cdb4ab67 | 101 | totalFreeBlock += blockSize; |
Jonathan Austin |
1:8aa5cdb4ab67 | 102 | else |
Jonathan Austin |
1:8aa5cdb4ab67 | 103 | totalUsedBlock += blockSize; |
Jonathan Austin |
1:8aa5cdb4ab67 | 104 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 105 | block += blockSize; |
Jonathan Austin |
1:8aa5cdb4ab67 | 106 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 107 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 108 | // Enable Interrupts |
Jonathan Austin |
1:8aa5cdb4ab67 | 109 | __enable_irq(); |
Jonathan Austin |
1:8aa5cdb4ab67 | 110 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 111 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("\n"); |
Jonathan Austin |
1:8aa5cdb4ab67 | 112 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 113 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("mb_total_free : %d\n", totalFreeBlock*4); |
Jonathan Austin |
1:8aa5cdb4ab67 | 114 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("mb_total_used : %d\n", totalUsedBlock*4); |
Jonathan Austin |
1:8aa5cdb4ab67 | 115 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 116 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 117 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 118 | // Diagnostics function. Displays a usage summary about all initialised heaps. |
Jonathan Austin |
1:8aa5cdb4ab67 | 119 | void microbit_heap_print() |
Jonathan Austin |
1:8aa5cdb4ab67 | 120 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 121 | for (int i=0; i < heap_count; i++) |
Jonathan Austin |
1:8aa5cdb4ab67 | 122 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 123 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("\nHEAP %d: \n", i); |
Jonathan Austin |
1:8aa5cdb4ab67 | 124 | microbit_heap_print(heap[i]); |
Jonathan Austin |
1:8aa5cdb4ab67 | 125 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 126 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 127 | #endif |
Jonathan Austin |
1:8aa5cdb4ab67 | 128 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 129 | void microbit_initialise_heap(HeapDefinition &heap) |
Jonathan Austin |
1:8aa5cdb4ab67 | 130 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 131 | // Simply mark the entire heap as free. |
Jonathan Austin |
1:8aa5cdb4ab67 | 132 | *heap.heap_start = ((uint32_t) heap.heap_end - (uint32_t) heap.heap_start) / MICROBIT_HEAP_BLOCK_SIZE; |
Jonathan Austin |
1:8aa5cdb4ab67 | 133 | *heap.heap_start |= MICROBIT_HEAP_BLOCK_FREE; |
Jonathan Austin |
1:8aa5cdb4ab67 | 134 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 135 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 136 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 137 | * Create and initialise a given memory region as for heap storage. |
Jonathan Austin |
1:8aa5cdb4ab67 | 138 | * After this is called, any future calls to malloc, new, free or delete may use the new heap. |
Jonathan Austin |
1:8aa5cdb4ab67 | 139 | * The heap allocator will attempt to allocate memory from heaps in the order that they are created. |
Jonathan Austin |
1:8aa5cdb4ab67 | 140 | * i.e. memory will be allocated from first heap created until it is full, then the second heap, and so on. |
Jonathan Austin |
1:8aa5cdb4ab67 | 141 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 142 | * @param start The start address of memory to use as a heap region. |
Jonathan Austin |
1:8aa5cdb4ab67 | 143 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 144 | * @param end The end address of memory to use as a heap region. |
Jonathan Austin |
1:8aa5cdb4ab67 | 145 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 146 | * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the heap could not be allocated. |
Jonathan Austin |
1:8aa5cdb4ab67 | 147 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 148 | * @note Only code that #includes MicroBitHeapAllocator.h will use this heap. This includes all micro:bit runtime |
Jonathan Austin |
1:8aa5cdb4ab67 | 149 | * code, and user code targetting the runtime. External code can choose to include this file, or |
Jonathan Austin |
1:8aa5cdb4ab67 | 150 | * simply use the standard heap. |
Jonathan Austin |
1:8aa5cdb4ab67 | 151 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 152 | int microbit_create_heap(uint32_t start, uint32_t end) |
Jonathan Austin |
1:8aa5cdb4ab67 | 153 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 154 | // Ensure we don't exceed the maximum number of heap segments. |
Jonathan Austin |
1:8aa5cdb4ab67 | 155 | if (heap_count == MICROBIT_MAXIMUM_HEAPS) |
Jonathan Austin |
1:8aa5cdb4ab67 | 156 | return MICROBIT_NO_RESOURCES; |
Jonathan Austin |
1:8aa5cdb4ab67 | 157 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 158 | // Sanity check. Ensure range is valid, large enough and word aligned. |
Jonathan Austin |
1:8aa5cdb4ab67 | 159 | if (end <= start || end - start < MICROBIT_HEAP_BLOCK_SIZE*2 || end % 4 != 0 || start % 4 != 0) |
Jonathan Austin |
1:8aa5cdb4ab67 | 160 | return MICROBIT_INVALID_PARAMETER; |
Jonathan Austin |
1:8aa5cdb4ab67 | 161 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 162 | // Disable IRQ temporarily to ensure no race conditions! |
Jonathan Austin |
1:8aa5cdb4ab67 | 163 | __disable_irq(); |
Jonathan Austin |
1:8aa5cdb4ab67 | 164 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 165 | // Record the dimensions of this new heap |
Jonathan Austin |
1:8aa5cdb4ab67 | 166 | heap[heap_count].heap_start = (uint32_t *)start; |
Jonathan Austin |
1:8aa5cdb4ab67 | 167 | heap[heap_count].heap_end = (uint32_t *)end; |
Jonathan Austin |
1:8aa5cdb4ab67 | 168 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 169 | // Initialise the heap as being completely empty and available for use. |
Jonathan Austin |
1:8aa5cdb4ab67 | 170 | microbit_initialise_heap(heap[heap_count]); |
Jonathan Austin |
1:8aa5cdb4ab67 | 171 | heap_count++; |
Jonathan Austin |
1:8aa5cdb4ab67 | 172 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 173 | // Enable Interrupts |
Jonathan Austin |
1:8aa5cdb4ab67 | 174 | __enable_irq(); |
Jonathan Austin |
1:8aa5cdb4ab67 | 175 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 176 | #if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG) |
Jonathan Austin |
1:8aa5cdb4ab67 | 177 | microbit_heap_print(); |
Jonathan Austin |
1:8aa5cdb4ab67 | 178 | #endif |
Jonathan Austin |
1:8aa5cdb4ab67 | 179 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 180 | return MICROBIT_OK; |
Jonathan Austin |
1:8aa5cdb4ab67 | 181 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 182 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 183 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 184 | * Create and initialise a heap region within the current the heap region specified |
Jonathan Austin |
1:8aa5cdb4ab67 | 185 | * by the linker script. |
Jonathan Austin |
1:8aa5cdb4ab67 | 186 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 187 | * If the requested amount is not available, then the amount requested will be reduced |
Jonathan Austin |
1:8aa5cdb4ab67 | 188 | * automatically to fit the space available. |
Jonathan Austin |
1:8aa5cdb4ab67 | 189 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 190 | * @param ratio The proportion of the underlying heap to allocate. |
Jonathan Austin |
1:8aa5cdb4ab67 | 191 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 192 | * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the heap could not be allocated. |
Jonathan Austin |
1:8aa5cdb4ab67 | 193 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 194 | int microbit_create_nested_heap(float ratio) |
Jonathan Austin |
1:8aa5cdb4ab67 | 195 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 196 | uint32_t length; |
Jonathan Austin |
1:8aa5cdb4ab67 | 197 | void *p; |
Jonathan Austin |
1:8aa5cdb4ab67 | 198 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 199 | if (ratio <= 0.0 || ratio > 1.0) |
Jonathan Austin |
1:8aa5cdb4ab67 | 200 | return MICROBIT_INVALID_PARAMETER; |
Jonathan Austin |
1:8aa5cdb4ab67 | 201 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 202 | // Snapshot something at the top of the main heap. |
Jonathan Austin |
1:8aa5cdb4ab67 | 203 | p = native_malloc(sizeof(uint32_t)); |
Jonathan Austin |
1:8aa5cdb4ab67 | 204 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 205 | // Estimate the size left in our heap, taking care to ensure it lands on a word boundary. |
Jonathan Austin |
1:8aa5cdb4ab67 | 206 | length = (uint32_t) (((float)(MICROBIT_HEAP_END - (uint32_t)p)) * ratio); |
Jonathan Austin |
1:8aa5cdb4ab67 | 207 | length &= 0xFFFFFFFC; |
Jonathan Austin |
1:8aa5cdb4ab67 | 208 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 209 | // Release our reference pointer. |
Jonathan Austin |
1:8aa5cdb4ab67 | 210 | native_free(p); |
Jonathan Austin |
1:8aa5cdb4ab67 | 211 | p = NULL; |
Jonathan Austin |
1:8aa5cdb4ab67 | 212 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 213 | // Allocate memory for our heap. |
Jonathan Austin |
1:8aa5cdb4ab67 | 214 | // We iteratively reduce the size of memory are allocate until it fits within available space. |
Jonathan Austin |
1:8aa5cdb4ab67 | 215 | while (p == NULL) |
Jonathan Austin |
1:8aa5cdb4ab67 | 216 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 217 | p = native_malloc(length); |
Jonathan Austin |
1:8aa5cdb4ab67 | 218 | if (p == NULL) |
Jonathan Austin |
1:8aa5cdb4ab67 | 219 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 220 | length -= 32; |
Jonathan Austin |
1:8aa5cdb4ab67 | 221 | if (length <= 0) |
Jonathan Austin |
1:8aa5cdb4ab67 | 222 | return MICROBIT_NO_RESOURCES; |
Jonathan Austin |
1:8aa5cdb4ab67 | 223 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 224 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 225 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 226 | uint32_t start = (uint32_t) p; |
Jonathan Austin |
1:8aa5cdb4ab67 | 227 | microbit_create_heap(start, start + length); |
Jonathan Austin |
1:8aa5cdb4ab67 | 228 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 229 | return MICROBIT_OK; |
Jonathan Austin |
1:8aa5cdb4ab67 | 230 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 231 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 232 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 233 | * Attempt to allocate a given amount of memory from any of our configured heap areas. |
Jonathan Austin |
1:8aa5cdb4ab67 | 234 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 235 | * @param size The amount of memory, in bytes, to allocate. |
Jonathan Austin |
1:8aa5cdb4ab67 | 236 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 237 | * @return A pointer to the allocated memory, or NULL if insufficient memory is available. |
Jonathan Austin |
1:8aa5cdb4ab67 | 238 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 239 | void *microbit_malloc(size_t size, HeapDefinition &heap) |
Jonathan Austin |
1:8aa5cdb4ab67 | 240 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 241 | uint32_t blockSize = 0; |
Jonathan Austin |
1:8aa5cdb4ab67 | 242 | uint32_t blocksNeeded = size % MICROBIT_HEAP_BLOCK_SIZE == 0 ? size / MICROBIT_HEAP_BLOCK_SIZE : size / MICROBIT_HEAP_BLOCK_SIZE + 1; |
Jonathan Austin |
1:8aa5cdb4ab67 | 243 | uint32_t *block; |
Jonathan Austin |
1:8aa5cdb4ab67 | 244 | uint32_t *next; |
Jonathan Austin |
1:8aa5cdb4ab67 | 245 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 246 | if (size <= 0) |
Jonathan Austin |
1:8aa5cdb4ab67 | 247 | return NULL; |
Jonathan Austin |
1:8aa5cdb4ab67 | 248 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 249 | // Account for the index block; |
Jonathan Austin |
1:8aa5cdb4ab67 | 250 | blocksNeeded++; |
Jonathan Austin |
1:8aa5cdb4ab67 | 251 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 252 | // Disable IRQ temporarily to ensure no race conditions! |
Jonathan Austin |
1:8aa5cdb4ab67 | 253 | __disable_irq(); |
Jonathan Austin |
1:8aa5cdb4ab67 | 254 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 255 | // We implement a first fit algorithm with cache to handle rapid churn... |
Jonathan Austin |
1:8aa5cdb4ab67 | 256 | // We also defragment free blocks as we search, to optimise this and future searches. |
Jonathan Austin |
1:8aa5cdb4ab67 | 257 | block = heap.heap_start; |
Jonathan Austin |
1:8aa5cdb4ab67 | 258 | while (block < heap.heap_end) |
Jonathan Austin |
1:8aa5cdb4ab67 | 259 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 260 | // If the block is used, then keep looking. |
Jonathan Austin |
1:8aa5cdb4ab67 | 261 | if(!(*block & MICROBIT_HEAP_BLOCK_FREE)) |
Jonathan Austin |
1:8aa5cdb4ab67 | 262 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 263 | block += *block; |
Jonathan Austin |
1:8aa5cdb4ab67 | 264 | continue; |
Jonathan Austin |
1:8aa5cdb4ab67 | 265 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 266 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 267 | blockSize = *block & ~MICROBIT_HEAP_BLOCK_FREE; |
Jonathan Austin |
1:8aa5cdb4ab67 | 268 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 269 | // We have a free block. Let's see if the subsequent ones are too. If so, we can merge... |
Jonathan Austin |
1:8aa5cdb4ab67 | 270 | next = block + blockSize; |
Jonathan Austin |
1:8aa5cdb4ab67 | 271 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 272 | while (*next & MICROBIT_HEAP_BLOCK_FREE) |
Jonathan Austin |
1:8aa5cdb4ab67 | 273 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 274 | if (next >= heap.heap_end) |
Jonathan Austin |
1:8aa5cdb4ab67 | 275 | break; |
Jonathan Austin |
1:8aa5cdb4ab67 | 276 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 277 | // We can merge! |
Jonathan Austin |
1:8aa5cdb4ab67 | 278 | blockSize += (*next & ~MICROBIT_HEAP_BLOCK_FREE); |
Jonathan Austin |
1:8aa5cdb4ab67 | 279 | *block = blockSize | MICROBIT_HEAP_BLOCK_FREE; |
Jonathan Austin |
1:8aa5cdb4ab67 | 280 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 281 | next = block + blockSize; |
Jonathan Austin |
1:8aa5cdb4ab67 | 282 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 283 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 284 | // We have a free block. Let's see if it's big enough. |
Jonathan Austin |
1:8aa5cdb4ab67 | 285 | // If so, we have a winner. |
Jonathan Austin |
1:8aa5cdb4ab67 | 286 | if (blockSize >= blocksNeeded) |
Jonathan Austin |
1:8aa5cdb4ab67 | 287 | break; |
Jonathan Austin |
1:8aa5cdb4ab67 | 288 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 289 | // Otherwise, keep looking... |
Jonathan Austin |
1:8aa5cdb4ab67 | 290 | block += blockSize; |
Jonathan Austin |
1:8aa5cdb4ab67 | 291 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 292 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 293 | // We're full! |
Jonathan Austin |
1:8aa5cdb4ab67 | 294 | if (block >= heap.heap_end) |
Jonathan Austin |
1:8aa5cdb4ab67 | 295 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 296 | __enable_irq(); |
Jonathan Austin |
1:8aa5cdb4ab67 | 297 | return NULL; |
Jonathan Austin |
1:8aa5cdb4ab67 | 298 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 299 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 300 | // If we're at the end of memory or have very near match then mark the whole segment as in use. |
Jonathan Austin |
1:8aa5cdb4ab67 | 301 | if (blockSize <= blocksNeeded+1 || block+blocksNeeded+1 >= heap.heap_end) |
Jonathan Austin |
1:8aa5cdb4ab67 | 302 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 303 | // Just mark the whole block as used. |
Jonathan Austin |
1:8aa5cdb4ab67 | 304 | *block &= ~MICROBIT_HEAP_BLOCK_FREE; |
Jonathan Austin |
1:8aa5cdb4ab67 | 305 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 306 | else |
Jonathan Austin |
1:8aa5cdb4ab67 | 307 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 308 | // We need to split the block. |
Jonathan Austin |
1:8aa5cdb4ab67 | 309 | uint32_t *splitBlock = block + blocksNeeded; |
Jonathan Austin |
1:8aa5cdb4ab67 | 310 | *splitBlock = blockSize - blocksNeeded; |
Jonathan Austin |
1:8aa5cdb4ab67 | 311 | *splitBlock |= MICROBIT_HEAP_BLOCK_FREE; |
Jonathan Austin |
1:8aa5cdb4ab67 | 312 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 313 | *block = blocksNeeded; |
Jonathan Austin |
1:8aa5cdb4ab67 | 314 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 315 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 316 | // Enable Interrupts |
Jonathan Austin |
1:8aa5cdb4ab67 | 317 | __enable_irq(); |
Jonathan Austin |
1:8aa5cdb4ab67 | 318 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 319 | return block+1; |
Jonathan Austin |
1:8aa5cdb4ab67 | 320 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 321 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 322 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 323 | * Release a given area of memory from the heap. |
Jonathan Austin |
1:8aa5cdb4ab67 | 324 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 325 | * @param mem The memory area to release. |
Jonathan Austin |
1:8aa5cdb4ab67 | 326 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 327 | void *microbit_malloc(size_t size) |
Jonathan Austin |
1:8aa5cdb4ab67 | 328 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 329 | void *p; |
Jonathan Austin |
1:8aa5cdb4ab67 | 330 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 331 | // Assign the memory from the first heap created that has space. |
Jonathan Austin |
1:8aa5cdb4ab67 | 332 | for (int i=0; i < heap_count; i++) |
Jonathan Austin |
1:8aa5cdb4ab67 | 333 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 334 | p = microbit_malloc(size, heap[i]); |
Jonathan Austin |
1:8aa5cdb4ab67 | 335 | if (p != NULL) |
Jonathan Austin |
1:8aa5cdb4ab67 | 336 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 337 | #if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG) |
Jonathan Austin |
1:8aa5cdb4ab67 | 338 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("microbit_malloc: ALLOCATED: %d [%p]\n", size, p); |
Jonathan Austin |
1:8aa5cdb4ab67 | 339 | #endif |
Jonathan Austin |
1:8aa5cdb4ab67 | 340 | return p; |
Jonathan Austin |
1:8aa5cdb4ab67 | 341 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 342 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 343 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 344 | // If we reach here, then either we have no memory available, or our heap spaces |
Jonathan Austin |
1:8aa5cdb4ab67 | 345 | // haven't been initialised. Either way, we try the native allocator. |
Jonathan Austin |
1:8aa5cdb4ab67 | 346 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 347 | p = native_malloc(size); |
Jonathan Austin |
1:8aa5cdb4ab67 | 348 | if (p != NULL) |
Jonathan Austin |
1:8aa5cdb4ab67 | 349 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 350 | #if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG) |
Jonathan Austin |
1:8aa5cdb4ab67 | 351 | // Keep everything trasparent if we've not been initialised yet |
Jonathan Austin |
1:8aa5cdb4ab67 | 352 | if (heap_count > 0) |
Jonathan Austin |
1:8aa5cdb4ab67 | 353 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("microbit_malloc: NATIVE ALLOCATED: %d [%p]\n", size, p); |
Jonathan Austin |
1:8aa5cdb4ab67 | 354 | #endif |
Jonathan Austin |
1:8aa5cdb4ab67 | 355 | return p; |
Jonathan Austin |
1:8aa5cdb4ab67 | 356 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 357 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 358 | // We're totally out of options (and memory!). |
Jonathan Austin |
1:8aa5cdb4ab67 | 359 | #if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG) |
Jonathan Austin |
1:8aa5cdb4ab67 | 360 | // Keep everything transparent if we've not been initialised yet |
Jonathan Austin |
1:8aa5cdb4ab67 | 361 | if (heap_count > 0) |
Jonathan Austin |
1:8aa5cdb4ab67 | 362 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("microbit_malloc: OUT OF MEMORY [%d]\n", size); |
Jonathan Austin |
1:8aa5cdb4ab67 | 363 | #endif |
Jonathan Austin |
1:8aa5cdb4ab67 | 364 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 365 | #if CONFIG_ENABLED(MICROBIT_PANIC_HEAP_FULL) |
Jonathan Austin |
1:8aa5cdb4ab67 | 366 | microbit_panic(MICROBIT_OOM); |
Jonathan Austin |
1:8aa5cdb4ab67 | 367 | #endif |
Jonathan Austin |
1:8aa5cdb4ab67 | 368 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 369 | return NULL; |
Jonathan Austin |
1:8aa5cdb4ab67 | 370 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 371 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 372 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 373 | * Release a given area of memory from the heap. |
Jonathan Austin |
1:8aa5cdb4ab67 | 374 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 375 | * @param mem The memory area to release. |
Jonathan Austin |
1:8aa5cdb4ab67 | 376 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 377 | void microbit_free(void *mem) |
Jonathan Austin |
1:8aa5cdb4ab67 | 378 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 379 | uint32_t *memory = (uint32_t *)mem; |
Jonathan Austin |
1:8aa5cdb4ab67 | 380 | uint32_t *cb = memory-1; |
Jonathan Austin |
1:8aa5cdb4ab67 | 381 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 382 | #if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG) |
Jonathan Austin |
1:8aa5cdb4ab67 | 383 | if (heap_count > 0) |
Jonathan Austin |
1:8aa5cdb4ab67 | 384 | if(SERIAL_DEBUG) SERIAL_DEBUG->printf("microbit_free: %p\n", mem); |
Jonathan Austin |
1:8aa5cdb4ab67 | 385 | #endif |
Jonathan Austin |
1:8aa5cdb4ab67 | 386 | // Sanity check. |
Jonathan Austin |
1:8aa5cdb4ab67 | 387 | if (memory == NULL) |
Jonathan Austin |
1:8aa5cdb4ab67 | 388 | return; |
Jonathan Austin |
1:8aa5cdb4ab67 | 389 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 390 | // If this memory was created from a heap registered with us, free it. |
Jonathan Austin |
1:8aa5cdb4ab67 | 391 | for (int i=0; i < heap_count; i++) |
Jonathan Austin |
1:8aa5cdb4ab67 | 392 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 393 | if(memory > heap[i].heap_start && memory < heap[i].heap_end) |
Jonathan Austin |
1:8aa5cdb4ab67 | 394 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 395 | // The memory block given is part of this heap, so we can simply |
Jonathan Austin |
1:8aa5cdb4ab67 | 396 | // flag that this memory area is now free, and we're done. |
Jonathan Austin |
1:8aa5cdb4ab67 | 397 | *cb |= MICROBIT_HEAP_BLOCK_FREE; |
Jonathan Austin |
1:8aa5cdb4ab67 | 398 | return; |
Jonathan Austin |
1:8aa5cdb4ab67 | 399 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 400 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 401 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 402 | // If we reach here, then the memory is not part of any registered heap. |
Jonathan Austin |
1:8aa5cdb4ab67 | 403 | // Forward it to the native heap allocator, and let nature take its course... |
Jonathan Austin |
1:8aa5cdb4ab67 | 404 | native_free(mem); |
Jonathan Austin |
1:8aa5cdb4ab67 | 405 | } |