1 /*
2 Abstraction layer for spi-ram. For now, it's no more than a stub for the spiram_psram functions, but if
3 we add more types of external RAM memory, this can be made into a more intelligent dispatcher.
4 */
5
6 /*
7 * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
8 *
9 * SPDX-License-Identifier: Apache-2.0
10 */
11 #if defined(__ZEPHYR__)
12 #include <zephyr/kernel.h>
13 #include <zephyr/logging/log.h>
14 #include "esp_rom_uart.h"
15 #endif
16 #include <stdint.h>
17 #include <string.h>
18 #include <sys/param.h>
19
20 #include "sdkconfig.h"
21 #include "esp_attr.h"
22 #include "esp_err.h"
23 #include "esp32/spiram.h"
24 #include "spiram_psram.h"
25 #include "esp_log.h"
26 #include "soc/soc.h"
27 #if !defined(__ZEPHYR__)
28 #include "esp_heap_caps_init.h"
29 #endif
30 #include "soc/soc_memory_layout.h"
31 #include "soc/dport_reg.h"
32 #include "esp32/himem.h"
33 #include "esp32/rom/cache.h"
34
35 #if CONFIG_FREERTOS_UNICORE
36 #define PSRAM_MODE PSRAM_VADDR_MODE_NORMAL
37 #else
38 #if CONFIG_MEMMAP_SPIRAM_CACHE_EVENODD
39 #define PSRAM_MODE PSRAM_VADDR_MODE_EVENODD
40 #else
41 #define PSRAM_MODE PSRAM_VADDR_MODE_LOWHIGH
42 #endif
43 #endif
44
45 #if CONFIG_SPIRAM
46
47 static const char* TAG = "spiram";
48
49 #if CONFIG_SPIRAM_SPEED_40M && CONFIG_ESPTOOLPY_FLASHFREQ_40M
50 #define PSRAM_SPEED PSRAM_CACHE_F40M_S40M
51 #elif CONFIG_SPIRAM_SPEED_40M && CONFIG_ESPTOOLPY_FLASHFREQ_80M
52 #define PSRAM_SPEED PSRAM_CACHE_F80M_S40M
53 #elif CONFIG_SPIRAM_SPEED_80M && CONFIG_ESPTOOLPY_FLASHFREQ_80M
54 #define PSRAM_SPEED PSRAM_CACHE_F80M_S80M
55 #else
56 #error "FLASH speed can only be equal to or higher than SRAM speed while SRAM is enabled!"
57 #endif
58
59 #if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
60 extern uint8_t _ext_ram_bss_start, _ext_ram_bss_end;
61 #endif
62 #if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
63 extern uint8_t _ext_ram_noinit_start, _ext_ram_noinit_end;
64 #endif
65
66 static bool spiram_inited=false;
67
68
69 //If no function in esp_himem.c is used, this function will be linked into the
70 //binary instead of the one in esp_himem.c, automatically making sure no memory
71 //is reserved if no himem function is used.
esp_himem_reserved_area_size(void)72 size_t __attribute__((weak)) esp_himem_reserved_area_size(void) {
73 return 0;
74 }
75
76
spiram_size_usable_for_malloc(void)77 static size_t spiram_size_usable_for_malloc(void)
78 {
79 /* SPIRAM chip may be larger than the size we can map into address space */
80 size_t s = MIN(esp_spiram_get_size(), SOC_EXTRAM_DATA_SIZE);
81 return s - esp_himem_reserved_area_size();
82 }
83
84
85 /*
86 Simple RAM test. Writes a word every 32 bytes. Takes about a second to complete for 4MiB. Returns
87 true when RAM seems OK, false when test fails. WARNING: Do not run this before the 2nd cpu has been
88 initialized (in a two-core system) or after the heap allocator has taken ownership of the memory.
89 */
esp_spiram_test(void)90 bool esp_spiram_test(void)
91 {
92
93 #if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
94 const void *keepout_addr_low = (const void*)&_ext_ram_noinit_start;
95 const void *keepout_addr_high = (const void*)&_ext_ram_noinit_end;
96 #else
97 const void *keepout_addr_low = 0;
98 const void *keepout_addr_high = 0;
99 #endif
100
101 volatile int *spiram=(volatile int*)SOC_EXTRAM_DATA_LOW;
102 size_t p;
103 size_t s=spiram_size_usable_for_malloc();
104 int errct=0;
105 int initial_err=-1;
106 for (p=0; p<(s/sizeof(int)); p+=8) {
107 const void *addr = (const void *)&spiram[p];
108 if ((keepout_addr_low <= addr) && (addr < keepout_addr_high)) {
109 continue;
110 }
111 spiram[p]=p^0xAAAAAAAA;
112 }
113 for (p=0; p<(s/sizeof(int)); p+=8) {
114 const void *addr = (const void *)&spiram[p];
115 if ((keepout_addr_low <= addr) && (addr < keepout_addr_high)) {
116 continue;
117 }
118 if (spiram[p]!=(p^0xAAAAAAAA)) {
119 errct++;
120 if (errct==1) initial_err=p*4;
121 }
122 }
123 if (errct) {
124 ESP_EARLY_LOGE(TAG, "SPI SRAM memory test fail. %d/%d writes failed, first @ %X\n", errct, s/32, initial_err+SOC_EXTRAM_DATA_LOW);
125 return false;
126 } else {
127 ESP_EARLY_LOGI(TAG, "SPI SRAM memory test OK");
128 return true;
129 }
130 }
131
esp_spiram_init_cache(void)132 void IRAM_ATTR esp_spiram_init_cache(void)
133 {
134 int size = esp_spiram_get_size();
135 if (size > 4 * 1024 * 1024) size = 4 * 1024 * 1024; // we can map at most 4MByte
136 //Enable external RAM in MMU
137 cache_sram_mmu_set(0, 0, SOC_EXTRAM_DATA_LOW, 0, 32, (size / 1024 / 32));
138 //Flush and enable icache for APP CPU
139 #if !CONFIG_FREERTOS_UNICORE
140 DPORT_CLEAR_PERI_REG_MASK(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MASK_DRAM1);
141 cache_sram_mmu_set(1, 0, SOC_EXTRAM_DATA_LOW, 0, 32, (size / 1024 / 32));
142 #endif
143 }
144
esp_spiram_get_chip_size(void)145 esp_spiram_size_t esp_spiram_get_chip_size(void)
146 {
147 if (!spiram_inited) {
148 ESP_EARLY_LOGE(TAG, "SPI RAM not initialized");
149 abort();
150 }
151 psram_size_t psram_size = psram_get_size();
152 switch (psram_size) {
153 case PSRAM_SIZE_16MBITS:
154 return ESP_SPIRAM_SIZE_16MBITS;
155 case PSRAM_SIZE_32MBITS:
156 return ESP_SPIRAM_SIZE_32MBITS;
157 case PSRAM_SIZE_64MBITS:
158 return ESP_SPIRAM_SIZE_64MBITS;
159 default:
160 return ESP_SPIRAM_SIZE_INVALID;
161 }
162 }
163
esp_spiram_init(void)164 esp_err_t esp_spiram_init(void)
165 {
166 esp_err_t r;
167 r = psram_enable(PSRAM_SPEED, PSRAM_MODE);
168 if (r != ESP_OK) {
169 #if CONFIG_SPIRAM_IGNORE_NOTFOUND
170 ESP_EARLY_LOGE(TAG, "SPI RAM enabled but initialization failed. Bailing out.");
171 #endif
172 return r;
173 }
174
175 spiram_inited=true; //note: this needs to be set before esp_spiram_get_chip_*/esp_spiram_get_size calls
176 #if (CONFIG_SPIRAM_SIZE != -1)
177 if (esp_spiram_get_size()!=CONFIG_SPIRAM_SIZE) {
178 ESP_EARLY_LOGE(TAG, "Expected %dKiB chip but found %dKiB chip. Bailing out..", CONFIG_SPIRAM_SIZE/1024, esp_spiram_get_size()/1024);
179 return ESP_ERR_INVALID_SIZE;
180 }
181 #endif
182
183 ESP_EARLY_LOGI(TAG, "Found %dMBit SPI RAM device",
184 (esp_spiram_get_size()*8)/(1024*1024));
185 ESP_EARLY_LOGI(TAG, "SPI RAM mode: %s", PSRAM_SPEED == PSRAM_CACHE_F40M_S40M ? "flash 40m sram 40m" : \
186 PSRAM_SPEED == PSRAM_CACHE_F80M_S40M ? "flash 80m sram 40m" : \
187 PSRAM_SPEED == PSRAM_CACHE_F80M_S80M ? "flash 80m sram 80m" : "ERROR");
188 ESP_EARLY_LOGI(TAG, "PSRAM initialized, cache is in %s mode.", \
189 (PSRAM_MODE==PSRAM_VADDR_MODE_EVENODD)?"even/odd (2-core)": \
190 (PSRAM_MODE==PSRAM_VADDR_MODE_LOWHIGH)?"low/high (2-core)": \
191 (PSRAM_MODE==PSRAM_VADDR_MODE_NORMAL)?"normal (1-core)":"ERROR");
192 // workaround to fix garbage in the output
193 #ifdef __ZEPHYR__
194 esp_rom_uart_tx_wait_idle(0);
195 #endif
196 return ESP_OK;
197 }
198
199 #if !defined(__ZEPHYR__)
esp_spiram_add_to_heapalloc(void)200 esp_err_t esp_spiram_add_to_heapalloc(void)
201 {
202 //Add entire external RAM region to heap allocator. Heap allocator knows the capabilities of this type of memory, so there's
203 //no need to explicitly specify them.
204 intptr_t mallocable_ram_start = (intptr_t)SOC_EXTRAM_DATA_LOW;
205 #if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
206 if (mallocable_ram_start < (intptr_t)&_ext_ram_bss_end) {
207 mallocable_ram_start = (intptr_t)&_ext_ram_bss_end;
208 }
209 #endif
210 #if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
211 if (mallocable_ram_start < (intptr_t)&_ext_ram_noinit_end) {
212 mallocable_ram_start = (intptr_t)&_ext_ram_noinit_end;
213 }
214 #endif
215 intptr_t mallocable_ram_end = (intptr_t)SOC_EXTRAM_DATA_LOW + spiram_size_usable_for_malloc() - 1;
216 ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", (mallocable_ram_end - mallocable_ram_start)/1024);
217 return heap_caps_add_region(mallocable_ram_start, mallocable_ram_end);
218 }
219
220
221 static uint8_t *dma_heap;
222
esp_spiram_reserve_dma_pool(size_t size)223 esp_err_t esp_spiram_reserve_dma_pool(size_t size) {
224 ESP_EARLY_LOGI(TAG, "Reserving pool of %dK of internal memory for DMA/internal allocations", size/1024);
225 /* Pool may be allocated in multiple non-contiguous chunks, depending on available RAM */
226 while (size > 0) {
227 size_t next_size = heap_caps_get_largest_free_block(MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL);
228 next_size = MIN(next_size, size);
229
230 ESP_EARLY_LOGD(TAG, "Allocating block of size %d bytes", next_size);
231 dma_heap = heap_caps_malloc(next_size, MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL);
232 if (!dma_heap || next_size == 0) {
233 return ESP_ERR_NO_MEM;
234 }
235
236 uint32_t caps[] = { 0, MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT|MALLOC_CAP_32BIT };
237 esp_err_t e = heap_caps_add_region_with_caps(caps, (intptr_t) dma_heap, (intptr_t) dma_heap+next_size-1);
238 if (e != ESP_OK) {
239 return e;
240 }
241 size -= next_size;
242 }
243 return ESP_OK;
244 }
245 #endif
esp_spiram_get_size(void)246 size_t esp_spiram_get_size(void)
247 {
248 psram_size_t size=esp_spiram_get_chip_size();
249 if (size==PSRAM_SIZE_16MBITS) return 2*1024*1024;
250 if (size==PSRAM_SIZE_32MBITS) return 4*1024*1024;
251 if (size==PSRAM_SIZE_64MBITS) return 8*1024*1024;
252 return CONFIG_SPIRAM_SIZE;
253 }
254
255 /*
256 Before flushing the cache, if psram is enabled as a memory-mapped thing, we need to write back the data in the cache to the psram first,
257 otherwise it will get lost. For now, we just read 64/128K of random PSRAM memory to do this.
258 Note that this routine assumes some unique mapping for the first 2 banks of the PSRAM memory range, as well as the
259 2 banks after the 2 MiB mark.
260 */
esp_spiram_writeback_cache(void)261 void IRAM_ATTR esp_spiram_writeback_cache(void)
262 {
263 int x;
264 volatile int i=0;
265 volatile uint8_t *psram=(volatile uint8_t*)SOC_EXTRAM_DATA_LOW;
266 int cache_was_disabled=0;
267
268 if (!spiram_inited) return;
269
270 //We need cache enabled for this to work. Re-enable it if needed; make sure we
271 //disable it again on exit as well.
272 if (DPORT_REG_GET_BIT(DPORT_PRO_CACHE_CTRL_REG, DPORT_PRO_CACHE_ENABLE)==0) {
273 cache_was_disabled|=(1<<0);
274 DPORT_SET_PERI_REG_BITS(DPORT_PRO_CACHE_CTRL_REG, 1, 1, DPORT_PRO_CACHE_ENABLE_S);
275 }
276 #ifndef CONFIG_FREERTOS_UNICORE
277 if (DPORT_REG_GET_BIT(DPORT_APP_CACHE_CTRL_REG, DPORT_APP_CACHE_ENABLE)==0) {
278 cache_was_disabled|=(1<<1);
279 DPORT_SET_PERI_REG_BITS(DPORT_APP_CACHE_CTRL_REG, 1, 1, DPORT_APP_CACHE_ENABLE_S);
280 }
281 #endif
282
283 #if (PSRAM_MODE != PSRAM_VADDR_MODE_LOWHIGH)
284 /*
285 Single-core and even/odd mode only have 32K of cache evenly distributed over the address lines. We can clear
286 the cache by just reading 64K worth of cache lines.
287 */.
288 for (x=0; x<1024*64; x+=32) {
289 i+=psram[x];
290 }
291 #else
292 /*
293 Low/high psram cache mode uses one 32K cache for the lowest 2MiB of SPI flash and another 32K for the highest
294 2MiB. Clear this by reading from both regions.
295 Note: this assumes the amount of external RAM is >2M. If it is 2M or less, what this code does is undefined. If
296 we ever support external RAM chips of 2M or smaller, this may need adjusting.
297 */
298 for (x=0; x<1024*64; x+=32) {
299 i+=psram[x];
300 i+=psram[x+(1024*1024*2)];
301 }
302 #endif
303
304 if (cache_was_disabled&(1<<0)) {
305 while (DPORT_GET_PERI_REG_BITS2(DPORT_PRO_DCACHE_DBUG0_REG, DPORT_PRO_CACHE_STATE, DPORT_PRO_CACHE_STATE_S) != 1) ;
306 DPORT_SET_PERI_REG_BITS(DPORT_PRO_CACHE_CTRL_REG, 1, 0, DPORT_PRO_CACHE_ENABLE_S);
307 }
308 #ifndef CONFIG_FREERTOS_UNICORE
309 if (cache_was_disabled&(1<<1)) {
310 while (DPORT_GET_PERI_REG_BITS2(DPORT_APP_DCACHE_DBUG0_REG, DPORT_APP_CACHE_STATE, DPORT_APP_CACHE_STATE_S) != 1);
311 DPORT_SET_PERI_REG_BITS(DPORT_APP_CACHE_CTRL_REG, 1, 0, DPORT_APP_CACHE_ENABLE_S);
312 }
313 #endif
314 }
315
316 /**
317 * @brief If SPI RAM(PSRAM) has been initialized
318 *
319 * @return true SPI RAM has been initialized successfully
320 * @return false SPI RAM hasn't been initialized or initialized failed
321 */
esp_spiram_is_initialized(void)322 bool esp_spiram_is_initialized(void)
323 {
324 return spiram_inited;
325 }
326
esp_spiram_get_cs_io(void)327 uint8_t esp_spiram_get_cs_io(void)
328 {
329 return psram_get_cs_io();
330 }
331 #endif
332