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