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