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