1 //#include <autoconf.h>
2 #include "tcm_heap.h"
3
4 #include <string.h> // memset()
5
6 #include <osdep_service.h>
7
8 //#define _DEBUG
9
10 #if CONFIG_USE_TCM_HEAP
11 #define FREE_FILL_CODE 0xDEAD
12 #define ALLOC_FILL_CODE 0xBEEF
13
14 #define ROUND_UP2(x, pad) (((x) + ((pad) - 1)) & ~((pad) - 1))
15
16 #define TCM_HEAP_SIZE (40*1024)
17
18 static struct Heap g_tcm_heap;
19
20 #if defined (__ICCARM__)
21 #pragma location=".tcm.heap"
22 #else
23 __attribute__((section(".tcm.heap")))
24 #endif
25 HEAP_DEFINE_BUF(tcm_heap, TCM_HEAP_SIZE);
26 //unsigned char tcm_heap[TCM_HEAP_SIZE];
27
28 static int g_heap_inited=0;
29 static _lock tcm_lock;
30 #if defined(PLATFORM_FREERTOS)
31 extern void vPortSetExtFree( void (*free)( void *p ), uint32_t upper, uint32_t lower );
32 #elif defined(PLATFORM_CMSIS_RTOS)
33 extern void rtw_set_mfree_ext( void (*free)( void *p ), uint32_t upper, uint32_t lower );
34 #endif
tcm_heap_init(void)35 void tcm_heap_init(void)
36 {
37 //#ifdef _DEBUG
38 //memset(memory, FREE_FILL_CODE, size);
39 //#endif
40
41 //ASSERT2(((int)memory % alignof(heap_buf_t)) == 0,
42 //"memory buffer is unaligned, please use the HEAP_DEFINE_BUF() macro to declare heap buffers!\n");
43
44 /* Initialize heap with a single big chunk */
45 g_tcm_heap.FreeList = (MemChunk *)&tcm_heap;
46 g_tcm_heap.FreeList->next = NULL;
47 g_tcm_heap.FreeList->size = sizeof(tcm_heap);
48
49 g_heap_inited = 1;
50 rtw_spinlock_init(&tcm_lock);
51
52 #if defined(PLATFORM_FREERTOS)
53 // let RTOS know how to free memory if using as task stack
54 vPortSetExtFree(tcm_heap_free, 0x20000000, 0x1fff0000);
55 #elif defined(PLATFORM_CMSIS_RTOS)
56 rtw_set_mfree_ext(tcm_heap_free, 0x20000000, 0x1fff0000);
57 #endif
58 }
59
tcm_heap_dump(void)60 void tcm_heap_dump(void)
61 {
62 MemChunk *chunk, *prev;
63 struct Heap* h = &g_tcm_heap;
64
65 printf("---Free List--\n\r");
66 for (prev = (MemChunk *)&h->FreeList, chunk = h->FreeList;
67 chunk;
68 prev = chunk, chunk = chunk->next)
69 {
70 printf(" prev %x, chunk %x, size %d \n\r", prev, chunk, chunk->size);
71 }
72 printf("--------------\n\r");
73 }
74
tcm_heap_allocmem(int size)75 void *tcm_heap_allocmem(int size)
76 {
77 MemChunk *chunk, *prev;
78 struct Heap* h = &g_tcm_heap;
79 _irqL irqL;
80
81 rtw_enter_critical(&tcm_lock, &irqL);
82
83 if(!g_heap_inited) tcm_heap_init();
84
85 /* Round size up to the allocation granularity */
86 size = ROUND_UP2(size, sizeof(MemChunk));
87
88 /* Handle allocations of 0 bytes */
89 if (!size)
90 size = sizeof(MemChunk);
91
92 /* Walk on the free list looking for any chunk big enough to
93 * fit the requested block size.
94 */
95 for (prev = (MemChunk *)&h->FreeList, chunk = h->FreeList;
96 chunk;
97 prev = chunk, chunk = chunk->next)
98 {
99 if (chunk->size >= size)
100 {
101 if (chunk->size == size)
102 {
103 /* Just remove this chunk from the free list */
104 prev->next = chunk->next;
105 #ifdef _DEBUG
106 memset(chunk, ALLOC_FILL_CODE, size);
107 #endif
108
109 rtw_exit_critical(&tcm_lock, &irqL);
110 //printf("----ALLOC1-----\n\r");
111 //tcm_heap_dump();
112 //printf("--------------\n\r");
113 return (void *)chunk;
114 }
115 else
116 {
117 /* Allocate from the END of an existing chunk */
118 chunk->size -= size;
119 #ifdef _DEBUG
120 memset((uint8_t *)chunk + chunk->size, ALLOC_FILL_CODE, size);
121 #endif
122 rtw_exit_critical(&tcm_lock, &irqL);
123 //printf("----ALLOC2-----\n\r");
124 //tcm_heap_dump();
125 //printf("--------------\n\r");
126
127 return (void *)((uint8_t *)chunk + chunk->size);
128 }
129 }
130 }
131
132 rtw_exit_critical(&tcm_lock, &irqL);
133 //printf("----ALLOC3-----\n\r");
134 //tcm_heap_dump();
135 //printf("--------------\n\r");
136 return NULL; /* fail */
137 }
138
139
tcm_heap_freemem(void * mem,int size)140 void tcm_heap_freemem(void *mem, int size)
141 {
142 MemChunk *prev;
143 //ASSERT(mem);
144 struct Heap* h = &g_tcm_heap;
145 _irqL irqL;
146
147 rtw_enter_critical(&tcm_lock, &irqL);
148
149 if(!g_heap_inited) tcm_heap_init();
150
151 #ifdef _DEBUG
152 memset(mem, FREE_FILL_CODE, size);
153 #endif
154
155 /* Round size up to the allocation granularity */
156 size = ROUND_UP2(size, sizeof(MemChunk));
157
158 /* Handle allocations of 0 bytes */
159 if (!size)
160 size = sizeof(MemChunk);
161
162 /* Special cases: first chunk in the free list or memory completely full */
163 //ASSERT((uint8_t*)mem != (uint8_t*)h->FreeList);
164 if (((uint8_t *)mem) < ((uint8_t *)h->FreeList) || !h->FreeList)
165 {
166 /* Insert memory block before the current free list head */
167 prev = (MemChunk *)mem;
168 prev->next = h->FreeList;
169 prev->size = size;
170 h->FreeList = prev;
171 }
172 else /* Normal case: not the first chunk in the free list */
173 {
174 /*
175 * Walk on the free list. Stop at the insertion point (when mem
176 * is between prev and prev->next)
177 */
178 prev = h->FreeList;
179 while (prev->next < (MemChunk *)mem && prev->next)
180 prev = prev->next;
181
182 /* Make sure mem is not *within* prev */
183 //ASSERT((uint8_t*)mem >= (uint8_t*)prev + prev->size);
184
185 /* Should it be merged with previous block? */
186 if (((uint8_t *)prev) + prev->size == ((uint8_t *)mem))
187 {
188 /* Yes */
189 prev->size += size;
190 }
191 else /* not merged with previous chunk */
192 {
193 MemChunk *curr = (MemChunk*)mem;
194
195 /* insert it after the previous node
196 * and move the 'prev' pointer forward
197 * for the following operations
198 */
199 curr->next = prev->next;
200 curr->size = size;
201 prev->next = curr;
202
203 /* Adjust for the following test */
204 prev = curr;
205 }
206 }
207
208 /* Also merge with next chunk? */
209 if (((uint8_t *)prev) + prev->size == ((uint8_t *)prev->next))
210 {
211 prev->size += prev->next->size;
212 prev->next = prev->next->next;
213
214 /* There should be only one merge opportunity, becuase we always merge on free */
215 //ASSERT((uint8_t*)prev + prev->size != (uint8_t*)prev->next);
216 }
217
218 rtw_exit_critical(&tcm_lock, &irqL);
219 //printf("---FREE %x--\n\r", mem);
220 //tcm_heap_dump();
221 //printf("--------------\n\r");
222
223 }
224
tcm_heap_freeSpace(void)225 int tcm_heap_freeSpace(void)
226 {
227 int free_mem = 0;
228 struct Heap* h = &g_tcm_heap;
229 _irqL irqL;
230 MemChunk *chunk;
231
232 rtw_enter_critical(&tcm_lock, &irqL);
233
234 if(!g_heap_inited) tcm_heap_init();
235
236 for (chunk = h->FreeList; chunk; chunk = chunk->next)
237 free_mem += chunk->size;
238
239 rtw_exit_critical(&tcm_lock, &irqL);
240 return free_mem;
241 }
242
243
244 /**
245 * Standard malloc interface
246 */
tcm_heap_malloc(int size)247 void *tcm_heap_malloc(int size)
248 {
249 #if defined(PLATFORM_CMSIS_RTOS)
250 int64_t *mem;
251 // Make sure that block is 8-byte aligned
252 size = (size + 7U) & ~((uint32_t)7U);
253 size += sizeof(int64_t);
254 mem = (int64_t *)tcm_heap_allocmem(size);
255 #else
256 int *mem;
257 size += sizeof(int);
258 mem = (int*)tcm_heap_allocmem(size);
259 #endif
260
261
262 if (mem){
263 *mem++ = size;
264 }
265
266 return mem;
267 }
268
269 /**
270 * Standard calloc interface
271 */
tcm_heap_calloc(int size)272 void *tcm_heap_calloc(int size)
273 {
274 void *mem;
275 mem = tcm_heap_malloc(size);
276 if (mem)
277 memset(mem, 0, size);
278
279 return mem;
280 }
281
282 /**
283 * Free a block of memory, determining its size automatically.
284 *
285 * \param h Heap from which the block was allocated.
286 * \param mem Pointer to a block of memory previously allocated with
287 * either heap_malloc() or heap_calloc().
288 *
289 * \note If \a mem is a NULL pointer, no operation is performed.
290 *
291 * \note Freeing the same memory block twice has undefined behavior.
292 *
293 * \note This function works like the ANSI C free().
294 */
tcm_heap_free(void * mem)295 void tcm_heap_free(void *mem)
296 {
297 #if defined(PLATFORM_CMSIS_RTOS)
298 int64_t *_mem = (int64_t *)mem;
299 #else
300 int *_mem = (int *)mem;
301 #endif
302
303 if (_mem)
304 {
305 --_mem;
306 tcm_heap_freemem(_mem, *_mem);
307 }
308 }
309
310
alloc_test(int size,int test_len)311 static void alloc_test(int size, int test_len)
312 {
313 //Simple test
314 uint8_t *a[100];
315 int i, j;
316
317 for (i = 0; i < test_len; i++)
318 {
319 a[i] = tcm_heap_allocmem(size);
320 //ASSERT(a[i]);
321 for (j = 0; j < size; j++)
322 a[i][j] = i;
323 }
324
325 //ASSERT(heap_freeSpace(&h) == HEAP_SIZE - test_len * ROUND_UP2(size, sizeof(MemChunk)));
326
327 for (i = 0; i < test_len; i++)
328 {
329 for (j = 0; j < size; j++)
330 {
331 printf("a[%d][%d] = %d\n", i, j, a[i][j]);
332 //ASSERT(a[i][j] == i);
333 }
334 tcm_heap_freemem(a[i], size);
335 }
336 //ASSERT(heap_freeSpace(&h) == HEAP_SIZE);
337 }
338
339 #define ALLOC_SIZE 256
340 #define ALLOC_SIZE2 1024
341 #define TEST_LEN 20
342 #define TEST_LEN2 10
343 #define HEAP_SIZE 59*1024
tcm_heap_testRun(void)344 int tcm_heap_testRun(void)
345 {
346 alloc_test(ALLOC_SIZE, TEST_LEN);
347 alloc_test(ALLOC_SIZE2, TEST_LEN2);
348 /* Try to allocate the whole heap */
349 uint8_t *b = tcm_heap_allocmem(HEAP_SIZE);
350 int j;
351 //ASSERT(b);
352 //ASSERT(heap_freeSpace(&h) == 0);
353
354 //ASSERT(!heap_allocmem(&h, HEAP_SIZE));
355
356 for (j = 0; j < HEAP_SIZE; j++)
357 b[j] = j;
358
359 for (j = 0; j < HEAP_SIZE; j++)
360 {
361 printf("b[%d] = %d\n", j, j);
362 //ASSERT(b[j] == (j & 0xff));
363 }
364 tcm_heap_freemem(b, HEAP_SIZE);
365 //ASSERT(heap_freeSpace(&h) == HEAP_SIZE);
366
367 return 0;
368 }
369
370 #endif
371