heap_alloc_caps.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. #include <rom/ets_sys.h>
  14. #include <freertos/heap_regions.h>
  15. #include "esp_heap_alloc_caps.h"
  16. #include "spiram.h"
  17. #include "esp_log.h"
  18. static const char* TAG = "heap_alloc_caps";
  19. /*
  20. This file, combined with a region allocator that supports tags, solves the problem that the ESP32 has RAM that's
  21. slightly heterogeneous. Some RAM can be byte-accessed, some allows only 32-bit accesses, some can execute memory,
  22. some can be remapped by the MMU to only be accessed by a certain PID etc. In order to allow the most flexible
  23. memory allocation possible, this code makes it possible to request memory that has certain capabilities. The
  24. code will then use its knowledge of how the memory is configured along with a priority scheme to allocate that
  25. memory in the most sane way possible. This should optimize the amount of RAM accessible to the code without
  26. hardwiring addresses.
  27. */
  28. //Amount of priority slots for the tag descriptors.
  29. #define NO_PRIOS 3
  30. /*
  31. Tag descriptors. These describe the capabilities of a bit of memory that's tagged with the index into this table.
  32. Each tag contains NO_PRIOS entries; later entries are only taken if earlier ones can't fulfill the memory request.
  33. */
  34. static const uint32_t tagDesc[][NO_PRIOS]={
  35. { MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT, 0 }, //Tag 0: Plain ole D-port RAM
  36. { 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_EXEC }, //Tag 1: Plain ole D-port RAM which has an alias on the I-port
  37. { MALLOC_CAP_EXEC|MALLOC_CAP_32BIT, 0, 0 }, //Tag 2: IRAM
  38. { MALLOC_CAP_PID2, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, //Tag 3-8: PID 2-7 IRAM
  39. { MALLOC_CAP_PID3, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, //
  40. { MALLOC_CAP_PID4, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, //
  41. { MALLOC_CAP_PID5, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, //
  42. { MALLOC_CAP_PID6, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, //
  43. { MALLOC_CAP_PID7, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, //
  44. { MALLOC_CAP_PID2, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, //Tag 9-14: PID 2-7 DRAM
  45. { MALLOC_CAP_PID3, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, //
  46. { MALLOC_CAP_PID4, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, //
  47. { MALLOC_CAP_PID5, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, //
  48. { MALLOC_CAP_PID6, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, //
  49. { MALLOC_CAP_PID7, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, //
  50. { MALLOC_CAP_SPISRAM, 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}, //Tag 15: SPI SRAM data
  51. { MALLOC_CAP_INVALID, MALLOC_CAP_INVALID, MALLOC_CAP_INVALID } //End
  52. };
  53. /*
  54. Region descriptors. These describe all regions of memory available, and tag them according to the
  55. capabilities the hardware has. This array is not marked constant; the initialization code may want to
  56. change the tags of some regions because eg BT is detected, applications are loaded etc.
  57. The priorities here roughly work like this:
  58. - For a normal malloc (MALLOC_CAP_8BIT), give away the DRAM-only memory first, then pass off any dual-use IRAM regions,
  59. finally eat into the application memory.
  60. - For a malloc where 32-bit-aligned-only access is okay, first allocate IRAM, then DRAM, finally application IRAM.
  61. - Application mallocs (PIDx) will allocate IRAM first, if possible, then DRAM.
  62. - Most other malloc caps only fit in one region anyway.
  63. These region descriptors are very ESP32 specific, because they describe the memory pools available there.
  64. Because of requirements in the coalescing code as well as the heap allocator itself, this list should always
  65. be sorted from low to high start address.
  66. This array is *NOT* const because it gets modified depending on what pools are/aren't available.
  67. */
  68. static HeapRegionTagged_t regions[]={
  69. { (uint8_t *)0x3F800000, 0x20000, 15, 0}, //SPI SRAM, if available
  70. { (uint8_t *)0x3FFAE000, 0x2000, 0, 0}, //pool 16 <- used for rom code
  71. { (uint8_t *)0x3FFB0000, 0x8000, 0, 0}, //pool 15 <- can be used for BT
  72. { (uint8_t *)0x3FFB8000, 0x8000, 0, 0}, //pool 14 <- can be used for BT
  73. { (uint8_t *)0x3FFC0000, 0x2000, 0, 0}, //pool 10-13, mmu page 0
  74. { (uint8_t *)0x3FFC2000, 0x2000, 0, 0}, //pool 10-13, mmu page 1
  75. { (uint8_t *)0x3FFC4000, 0x2000, 0, 0}, //pool 10-13, mmu page 2
  76. { (uint8_t *)0x3FFC6000, 0x2000, 0, 0}, //pool 10-13, mmu page 3
  77. { (uint8_t *)0x3FFC8000, 0x2000, 0, 0}, //pool 10-13, mmu page 4
  78. { (uint8_t *)0x3FFCA000, 0x2000, 0, 0}, //pool 10-13, mmu page 5
  79. { (uint8_t *)0x3FFCC000, 0x2000, 0, 0}, //pool 10-13, mmu page 6
  80. { (uint8_t *)0x3FFCE000, 0x2000, 0, 0}, //pool 10-13, mmu page 7
  81. { (uint8_t *)0x3FFD0000, 0x2000, 0, 0}, //pool 10-13, mmu page 8
  82. { (uint8_t *)0x3FFD2000, 0x2000, 0, 0}, //pool 10-13, mmu page 9
  83. { (uint8_t *)0x3FFD4000, 0x2000, 0, 0}, //pool 10-13, mmu page 10
  84. { (uint8_t *)0x3FFD6000, 0x2000, 0, 0}, //pool 10-13, mmu page 11
  85. { (uint8_t *)0x3FFD8000, 0x2000, 0, 0}, //pool 10-13, mmu page 12
  86. { (uint8_t *)0x3FFDA000, 0x2000, 0, 0}, //pool 10-13, mmu page 13
  87. { (uint8_t *)0x3FFDC000, 0x2000, 0, 0}, //pool 10-13, mmu page 14
  88. { (uint8_t *)0x3FFDE000, 0x2000, 0, 0}, //pool 10-13, mmu page 15
  89. { (uint8_t *)0x3FFE0000, 0x4000, 1, 0x400BC000}, //pool 9 blk 1
  90. { (uint8_t *)0x3FFE4000, 0x4000, 1, 0x400B8000}, //pool 9 blk 0
  91. { (uint8_t *)0x3FFE8000, 0x8000, 1, 0x400B0000}, //pool 8 <- can be remapped to ROM, used for MAC dump
  92. { (uint8_t *)0x3FFF0000, 0x8000, 1, 0x400A8000}, //pool 7 <- can be used for MAC dump
  93. { (uint8_t *)0x3FFF8000, 0x4000, 1, 0x400A4000}, //pool 6 blk 1 <- can be used as trace memory
  94. { (uint8_t *)0x3FFFC000, 0x4000, 1, 0x400A0000}, //pool 6 blk 0 <- can be used as trace memory
  95. { (uint8_t *)0x40070000, 0x8000, 2, 0}, //pool 0
  96. { (uint8_t *)0x40078000, 0x8000, 2, 0}, //pool 1
  97. { (uint8_t *)0x40080000, 0x2000, 2, 0}, //pool 2-5, mmu page 0
  98. { (uint8_t *)0x40082000, 0x2000, 2, 0}, //pool 2-5, mmu page 1
  99. { (uint8_t *)0x40084000, 0x2000, 2, 0}, //pool 2-5, mmu page 2
  100. { (uint8_t *)0x40086000, 0x2000, 2, 0}, //pool 2-5, mmu page 3
  101. { (uint8_t *)0x40088000, 0x2000, 2, 0}, //pool 2-5, mmu page 4
  102. { (uint8_t *)0x4008A000, 0x2000, 2, 0}, //pool 2-5, mmu page 5
  103. { (uint8_t *)0x4008C000, 0x2000, 2, 0}, //pool 2-5, mmu page 6
  104. { (uint8_t *)0x4008E000, 0x2000, 2, 0}, //pool 2-5, mmu page 7
  105. { (uint8_t *)0x40090000, 0x2000, 2, 0}, //pool 2-5, mmu page 8
  106. { (uint8_t *)0x40092000, 0x2000, 2, 0}, //pool 2-5, mmu page 9
  107. { (uint8_t *)0x40094000, 0x2000, 2, 0}, //pool 2-5, mmu page 10
  108. { (uint8_t *)0x40096000, 0x2000, 2, 0}, //pool 2-5, mmu page 11
  109. { (uint8_t *)0x40098000, 0x2000, 2, 0}, //pool 2-5, mmu page 12
  110. { (uint8_t *)0x4009A000, 0x2000, 2, 0}, //pool 2-5, mmu page 13
  111. { (uint8_t *)0x4009C000, 0x2000, 2, 0}, //pool 2-5, mmu page 14
  112. { (uint8_t *)0x4009E000, 0x2000, 2, 0}, //pool 2-5, mmu page 15
  113. { NULL, 0, 0, 0} //end
  114. };
  115. //Modify regions array to disable the given range of memory.
  116. static void disable_mem_region(void *from, void *to) {
  117. int i;
  118. //Align from and to on word boundaries
  119. from=(void*)((uint32_t)from&~3);
  120. to=(void*)(((uint32_t)to+3)&~3);
  121. for (i=0; regions[i].xSizeInBytes!=0; i++) {
  122. void *regStart=regions[i].pucStartAddress;
  123. void *regEnd=regions[i].pucStartAddress+regions[i].xSizeInBytes;
  124. if (regStart>=from && regEnd<=to) {
  125. //Entire region falls in the range. Disable entirely.
  126. regions[i].xTag=-1;
  127. } else if (regStart>=from && regEnd>to && regStart<to) {
  128. //Start of the region falls in the range. Modify address/len.
  129. int overlap=(uint8_t *)to-(uint8_t *)regStart;
  130. regions[i].pucStartAddress+=overlap;
  131. regions[i].xSizeInBytes-=overlap;
  132. if (regions[i].xExecAddr) regions[i].xExecAddr+=overlap;
  133. } else if (regStart<from && regEnd>from && regEnd<=to) {
  134. //End of the region falls in the range. Modify length.
  135. regions[i].xSizeInBytes-=(uint8_t *)regEnd-(uint8_t *)from;
  136. } else if (regStart<from && regEnd>to) {
  137. //Range punches a hole in the region! We do not support this.
  138. ESP_EARLY_LOGE(TAG, "region %d: hole punching is not supported!", i);
  139. regions[i].xTag=-1; //Just disable memory region. That'll teach them!
  140. }
  141. }
  142. }
  143. /*
  144. ToDo: These are very dependent on the linker script, and the logic involving this works only
  145. because we're not using the SPI flash yet! If we enable that, this will break. ToDo: Rewrite by then.
  146. */
  147. extern int _bss_start, _heap_start;
  148. /*
  149. Initialize the heap allocator. We pass it a bunch of region descriptors, but we need to modify those first to accommodate for
  150. the data as loaded by the bootloader.
  151. ToDo: The regions are different when stuff like trace memory, BT, ... is used. Modify the regions struct on the fly for this.
  152. Same with loading of apps. Same with using SPI RAM.
  153. */
  154. void heap_alloc_caps_init() {
  155. int i;
  156. //Disable the bits of memory where this code is loaded.
  157. disable_mem_region(&_bss_start, &_heap_start);
  158. disable_mem_region((void*)0x3ffae000, (void*)0x3ffb0000); //knock out ROM data region
  159. disable_mem_region((void*)0x40070000, (void*)0x40078000); //CPU0 cache region
  160. disable_mem_region((void*)0x40078000, (void*)0x40080000); //CPU1 cache region
  161. disable_mem_region((void*)0x40080000, (void*)0x400a0000); //pool 2-5
  162. // TODO: this region should be checked, since we don't need to knock out all region finally
  163. disable_mem_region((void*)0x3ffe0000, (void*)0x3ffe8000); //knock out ROM data region
  164. #if CONFIG_MEMMAP_BT
  165. disable_mem_region((void*)0x3ffb0000, (void*)0x3ffc0000); //knock out BT data region
  166. #endif
  167. #if CONFIG_MEMMAP_TRACEMEM
  168. #if CONFIG_MEMMAP_TRACEMEM_TWOBANKS
  169. disable_mem_region((void*)0x3fff8000, (void*)0x40000000); //knock out trace mem region
  170. #else
  171. disable_mem_region((void*)0x3fff8000, (void*)0x3fffc000); //knock out trace mem region
  172. #endif
  173. #endif
  174. #if 0
  175. enable_spi_sram();
  176. #else
  177. disable_mem_region((void*)0x3f800000, (void*)0x3f820000); //SPI SRAM not installed
  178. #endif
  179. //The heap allocator will treat every region given to it as separate. In order to get bigger ranges of contiguous memory,
  180. //it's useful to coalesce adjacent regions that have the same tag.
  181. for (i=1; regions[i].xSizeInBytes!=0; i++) {
  182. if (regions[i].pucStartAddress == (regions[i-1].pucStartAddress + regions[i-1].xSizeInBytes) &&
  183. regions[i].xTag == regions[i-1].xTag ) {
  184. regions[i-1].xTag=-1;
  185. regions[i].pucStartAddress=regions[i-1].pucStartAddress;
  186. regions[i].xSizeInBytes+=regions[i-1].xSizeInBytes;
  187. }
  188. }
  189. ESP_EARLY_LOGI(TAG, "Initializing heap allocator:");
  190. for (i=0; regions[i].xSizeInBytes!=0; i++) {
  191. if (regions[i].xTag != -1) {
  192. ESP_EARLY_LOGI(TAG, "Region %02d: %08X len %08X tag %d", i,
  193. (int)regions[i].pucStartAddress, regions[i].xSizeInBytes, regions[i].xTag);
  194. }
  195. }
  196. //Initialize the malloc implementation.
  197. vPortDefineHeapRegionsTagged( regions );
  198. }
  199. /*
  200. Standard malloc() implementation. Will return ho-hum byte-accessible data memory.
  201. */
  202. void *pvPortMalloc( size_t xWantedSize )
  203. {
  204. return pvPortMallocCaps( xWantedSize, MALLOC_CAP_8BIT );
  205. }
  206. /*
  207. Routine to allocate a bit of memory with certain capabilities. caps is a bitfield of MALLOC_CAP_* bits.
  208. */
  209. void *pvPortMallocCaps( size_t xWantedSize, uint32_t caps )
  210. {
  211. int prio;
  212. int tag, j;
  213. void *ret=NULL;
  214. uint32_t remCaps;
  215. for (prio=0; prio<NO_PRIOS; prio++) {
  216. //Iterate over tag descriptors for this priority
  217. for (tag=0; tagDesc[tag][prio]!=MALLOC_CAP_INVALID; tag++) {
  218. if ((tagDesc[tag][prio]&caps)!=0) {
  219. //Tag has at least one of the caps requested. If caps has other bits set that this prio
  220. //doesn't cover, see if they're available in other prios.
  221. remCaps=caps&(~tagDesc[tag][prio]); //Remaining caps to be fulfilled
  222. j=prio+1;
  223. while (remCaps!=0 && j<NO_PRIOS) {
  224. remCaps=remCaps&(~tagDesc[tag][j]);
  225. j++;
  226. }
  227. if (remCaps==0) {
  228. //This tag can satisfy all the requested capabilities. See if we can grab some memory using it.
  229. ret=pvPortMallocTagged(xWantedSize, tag);
  230. if (ret!=NULL) return ret;
  231. }
  232. }
  233. }
  234. }
  235. //Nothing usable found.
  236. return NULL;
  237. }