• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
7 //
8 // Licensed under the Apache License, Version 2.0 (the "License");
9 // you may not use this file except in compliance with the License.
10 // You may obtain a copy of the License at
11 //
12 //     http://www.apache.org/licenses/LICENSE-2.0
13 //
14 // Unless required by applicable law or agreed to in writing, software
15 // distributed under the License is distributed on an "AS IS" BASIS,
16 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 // See the License for the specific language governing permissions and
18 // limitations under the License.
19 
20 #include <stdint.h>
21 #include <string.h>
22 #include <sys/param.h>
23 
24 #include "sdkconfig.h"
25 #include "esp_attr.h"
26 #include "esp_err.h"
27 #include "esp32/spiram.h"
28 #include "spiram_psram.h"
29 #include "esp_log.h"
30 #include "esp_osal/esp_osal.h"
31 #include "esp_osal/xtensa_api.h"
32 #include "soc/soc.h"
33 #include "esp_heap_caps_init.h"
34 #include "soc/soc_memory_layout.h"
35 #include "soc/dport_reg.h"
36 #include "esp32/himem.h"
37 #include "esp32/rom/cache.h"
38 
39 #if CONFIG_FREERTOS_UNICORE
40 #define PSRAM_MODE PSRAM_VADDR_MODE_NORMAL
41 #else
42 #if CONFIG_MEMMAP_SPIRAM_CACHE_EVENODD
43 #define PSRAM_MODE PSRAM_VADDR_MODE_EVENODD
44 #else
45 #define PSRAM_MODE PSRAM_VADDR_MODE_LOWHIGH
46 #endif
47 #endif
48 
49 #if CONFIG_SPIRAM
50 
51 static const char* TAG = "spiram";
52 
53 #if CONFIG_SPIRAM_SPEED_40M && CONFIG_ESPTOOLPY_FLASHFREQ_40M
54 #define PSRAM_SPEED PSRAM_CACHE_F40M_S40M
55 #elif CONFIG_SPIRAM_SPEED_40M && CONFIG_ESPTOOLPY_FLASHFREQ_80M
56 #define PSRAM_SPEED PSRAM_CACHE_F80M_S40M
57 #elif CONFIG_SPIRAM_SPEED_80M && CONFIG_ESPTOOLPY_FLASHFREQ_80M
58 #define PSRAM_SPEED PSRAM_CACHE_F80M_S80M
59 #else
60 #error "FLASH speed can only be equal to or higher than SRAM speed while SRAM is enabled!"
61 #endif
62 
63 #if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
64 extern uint8_t _ext_ram_bss_start, _ext_ram_bss_end;
65 #endif
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     volatile int *spiram=(volatile int*)SOC_EXTRAM_DATA_LOW;
93     size_t p;
94     size_t s=spiram_size_usable_for_malloc();
95     int errct=0;
96     int initial_err=-1;
97     for (p=0; p<(s/sizeof(int)); p+=8) {
98         spiram[p]=p^0xAAAAAAAA;
99     }
100     for (p=0; p<(s/sizeof(int)); p+=8) {
101         if (spiram[p]!=(p^0xAAAAAAAA)) {
102             errct++;
103             if (errct==1) initial_err=p*4;
104         }
105     }
106     if (errct) {
107         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);
108         return false;
109     } else {
110         ESP_EARLY_LOGI(TAG, "SPI SRAM memory test OK");
111         return true;
112     }
113 }
114 
esp_spiram_init_cache(void)115 void IRAM_ATTR esp_spiram_init_cache(void)
116 {
117     int size = esp_spiram_get_size();
118     if (size > 4 * 1024 * 1024) size = 4 * 1024 * 1024; // we can map at most 4MByte
119     //Enable external RAM in MMU
120     cache_sram_mmu_set(0, 0, SOC_EXTRAM_DATA_LOW, 0, 32, (size / 1024 / 32));
121     //Flush and enable icache for APP CPU
122 #if !CONFIG_FREERTOS_UNICORE
123     DPORT_CLEAR_PERI_REG_MASK(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MASK_DRAM1);
124     cache_sram_mmu_set(1, 0, SOC_EXTRAM_DATA_LOW, 0, 32, (size / 1024 / 32));
125 #endif
126 }
127 
esp_spiram_get_chip_size(void)128 esp_spiram_size_t esp_spiram_get_chip_size(void)
129 {
130     if (!spiram_inited) {
131         ESP_EARLY_LOGE(TAG, "SPI RAM not initialized");
132         abort();
133     }
134     psram_size_t psram_size = psram_get_size();
135     switch (psram_size) {
136         case PSRAM_SIZE_16MBITS:
137             return ESP_SPIRAM_SIZE_16MBITS;
138         case PSRAM_SIZE_32MBITS:
139             return ESP_SPIRAM_SIZE_32MBITS;
140         case PSRAM_SIZE_64MBITS:
141             return ESP_SPIRAM_SIZE_64MBITS;
142         default:
143             return ESP_SPIRAM_SIZE_INVALID;
144     }
145 }
146 
esp_spiram_init(void)147 esp_err_t esp_spiram_init(void)
148 {
149     esp_err_t r;
150     r = psram_enable(PSRAM_SPEED, PSRAM_MODE);
151     if (r != ESP_OK) {
152 #if CONFIG_SPIRAM_IGNORE_NOTFOUND
153         ESP_EARLY_LOGE(TAG, "SPI RAM enabled but initialization failed. Bailing out.");
154 #endif
155         return r;
156     }
157 
158     spiram_inited=true; //note: this needs to be set before esp_spiram_get_chip_*/esp_spiram_get_size calls
159 #if (CONFIG_SPIRAM_SIZE != -1)
160     if (esp_spiram_get_size()!=CONFIG_SPIRAM_SIZE) {
161         ESP_EARLY_LOGE(TAG, "Expected %dKiB chip but found %dKiB chip. Bailing out..", CONFIG_SPIRAM_SIZE/1024, esp_spiram_get_size()/1024);
162         return ESP_ERR_INVALID_SIZE;
163     }
164 #endif
165 
166     ESP_EARLY_LOGI(TAG, "Found %dMBit SPI RAM device",
167                                           (esp_spiram_get_size()*8)/(1024*1024));
168     ESP_EARLY_LOGI(TAG, "SPI RAM mode: %s", PSRAM_SPEED == PSRAM_CACHE_F40M_S40M ? "flash 40m sram 40m" : \
169                                           PSRAM_SPEED == PSRAM_CACHE_F80M_S40M ? "flash 80m sram 40m" : \
170                                           PSRAM_SPEED == PSRAM_CACHE_F80M_S80M ? "flash 80m sram 80m" : "ERROR");
171     ESP_EARLY_LOGI(TAG, "PSRAM initialized, cache is in %s mode.", \
172                                           (PSRAM_MODE==PSRAM_VADDR_MODE_EVENODD)?"even/odd (2-core)": \
173                                           (PSRAM_MODE==PSRAM_VADDR_MODE_LOWHIGH)?"low/high (2-core)": \
174                                           (PSRAM_MODE==PSRAM_VADDR_MODE_NORMAL)?"normal (1-core)":"ERROR");
175     return ESP_OK;
176 }
177 
178 
esp_spiram_add_to_heapalloc(void)179 esp_err_t esp_spiram_add_to_heapalloc(void)
180 {
181     //Add entire external RAM region to heap allocator. Heap allocator knows the capabilities of this type of memory, so there's
182     //no need to explicitly specify them.
183 #if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
184     ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", (spiram_size_usable_for_malloc() - (&_ext_ram_bss_end - &_ext_ram_bss_start))/1024);
185     return heap_caps_add_region((intptr_t)&_ext_ram_bss_end, (intptr_t)SOC_EXTRAM_DATA_LOW + spiram_size_usable_for_malloc()-1);
186 #else
187     ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", spiram_size_usable_for_malloc()/1024);
188     return heap_caps_add_region((intptr_t)SOC_EXTRAM_DATA_LOW, (intptr_t)SOC_EXTRAM_DATA_LOW + spiram_size_usable_for_malloc()-1);
189 #endif
190 }
191 
192 
193 static uint8_t *dma_heap;
194 
esp_spiram_reserve_dma_pool(size_t size)195 esp_err_t esp_spiram_reserve_dma_pool(size_t size) {
196     ESP_EARLY_LOGI(TAG, "Reserving pool of %dK of internal memory for DMA/internal allocations", size/1024);
197     /* Pool may be allocated in multiple non-contiguous chunks, depending on available RAM */
198     while (size > 0) {
199         size_t next_size = heap_caps_get_largest_free_block(MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL);
200         next_size = MIN(next_size, size);
201 
202         ESP_EARLY_LOGD(TAG, "Allocating block of size %d bytes", next_size);
203         dma_heap = heap_caps_malloc(next_size, MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL);
204         if (!dma_heap || next_size == 0) {
205             return ESP_ERR_NO_MEM;
206         }
207 
208         uint32_t caps[] = { 0, MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT|MALLOC_CAP_32BIT };
209         esp_err_t e = heap_caps_add_region_with_caps(caps, (intptr_t) dma_heap, (intptr_t) dma_heap+next_size-1);
210         if (e != ESP_OK) {
211             return e;
212         }
213         size -= next_size;
214     }
215     return ESP_OK;
216 }
217 
esp_spiram_get_size(void)218 size_t esp_spiram_get_size(void)
219 {
220     psram_size_t size=esp_spiram_get_chip_size();
221     if (size==PSRAM_SIZE_16MBITS) return 2*1024*1024;
222     if (size==PSRAM_SIZE_32MBITS) return 4*1024*1024;
223     if (size==PSRAM_SIZE_64MBITS) return 8*1024*1024;
224     return CONFIG_SPIRAM_SIZE;
225 }
226 
227 /*
228  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,
229  otherwise it will get lost. For now, we just read 64/128K of random PSRAM memory to do this.
230  Note that this routine assumes some unique mapping for the first 2 banks of the PSRAM memory range, as well as the
231  2 banks after the 2 MiB mark.
232 */
esp_spiram_writeback_cache(void)233 void IRAM_ATTR esp_spiram_writeback_cache(void)
234 {
235     int x;
236     volatile int i=0;
237     volatile uint8_t *psram=(volatile uint8_t*)SOC_EXTRAM_DATA_LOW;
238     int cache_was_disabled=0;
239 
240     if (!spiram_inited) return;
241 
242     //We need cache enabled for this to work. Re-enable it if needed; make sure we
243     //disable it again on exit as well.
244     if (DPORT_REG_GET_BIT(DPORT_PRO_CACHE_CTRL_REG, DPORT_PRO_CACHE_ENABLE)==0) {
245         cache_was_disabled|=(1<<0);
246         DPORT_SET_PERI_REG_BITS(DPORT_PRO_CACHE_CTRL_REG, 1, 1, DPORT_PRO_CACHE_ENABLE_S);
247     }
248 #ifndef CONFIG_FREERTOS_UNICORE
249     if (DPORT_REG_GET_BIT(DPORT_APP_CACHE_CTRL_REG, DPORT_APP_CACHE_ENABLE)==0) {
250         cache_was_disabled|=(1<<1);
251         DPORT_SET_PERI_REG_BITS(DPORT_APP_CACHE_CTRL_REG, 1, 1, DPORT_APP_CACHE_ENABLE_S);
252     }
253 #endif
254 
255 #if (PSRAM_MODE != PSRAM_VADDR_MODE_LOWHIGH)
256     /*
257     Single-core and even/odd mode only have 32K of cache evenly distributed over the address lines. We can clear
258     the cache by just reading 64K worth of cache lines.
259     */.
260     for (x=0; x<1024*64; x+=32) {
261         i+=psram[x];
262     }
263 #else
264     /*
265     Low/high psram cache mode uses one 32K cache for the lowest 2MiB of SPI flash and another 32K for the highest
266     2MiB. Clear this by reading from both regions.
267     Note: this assumes the amount of external RAM is >2M. If it is 2M or less, what this code does is undefined. If
268     we ever support external RAM chips of 2M or smaller, this may need adjusting.
269     */
270     for (x=0; x<1024*64; x+=32) {
271         i+=psram[x];
272         i+=psram[x+(1024*1024*2)];
273     }
274 #endif
275 
276     if (cache_was_disabled&(1<<0)) {
277         while (DPORT_GET_PERI_REG_BITS2(DPORT_PRO_DCACHE_DBUG0_REG, DPORT_PRO_CACHE_STATE, DPORT_PRO_CACHE_STATE_S) != 1) ;
278         DPORT_SET_PERI_REG_BITS(DPORT_PRO_CACHE_CTRL_REG, 1, 0, DPORT_PRO_CACHE_ENABLE_S);
279     }
280 #ifndef CONFIG_FREERTOS_UNICORE
281     if (cache_was_disabled&(1<<1)) {
282         while (DPORT_GET_PERI_REG_BITS2(DPORT_APP_DCACHE_DBUG0_REG, DPORT_APP_CACHE_STATE, DPORT_APP_CACHE_STATE_S) != 1);
283         DPORT_SET_PERI_REG_BITS(DPORT_APP_CACHE_CTRL_REG, 1, 0, DPORT_APP_CACHE_ENABLE_S);
284     }
285 #endif
286 }
287 
288 /**
289  * @brief If SPI RAM(PSRAM) has been initialized
290  *
291  * @return true SPI RAM has been initialized successfully
292  * @return false SPI RAM hasn't been initialized or initialized failed
293  */
esp_spiram_is_initialized(void)294 bool esp_spiram_is_initialized(void)
295 {
296     return spiram_inited;
297 }
298 
299 #endif
300