]> granicus.if.org Git - esp-idf/commitdiff
Add initial SPI RAM support. This adds support for an ESP-PSRAM32 chip connected...
authorJeroen Domburg <jeroen@espressif.com>
Thu, 20 Jul 2017 08:26:35 +0000 (16:26 +0800)
committerJeroen Domburg <jeroen@espressif.com>
Mon, 4 Sep 2017 04:05:49 +0000 (12:05 +0800)
22 files changed:
components/bootloader/subproject/main/component.mk
components/esp32/Kconfig
components/esp32/Makefile.projbuild
components/esp32/component.mk
components/esp32/cpu_start.c
components/esp32/include/esp_spiram.h [new file with mode: 0644]
components/esp32/ld/esp32.rom.ld
components/esp32/ld/esp32.rom.spiram_incompatible_fns.ld [new file with mode: 0644]
components/esp32/libstdcc++-cache-workaround.a [new file with mode: 0644]
components/esp32/spiram.c [new file with mode: 0644]
components/esp32/spiram.h [deleted file]
components/esp32/spiram_psram.c [new file with mode: 0644]
components/esp32/spiram_psram.h [new file with mode: 0644]
components/esp32/test/test_spiram_cache_flush.c [new file with mode: 0644]
components/newlib/component.mk
components/newlib/lib/libc-psram-workaround.a [new file with mode: 0644]
components/newlib/lib/libg-psram-workaround.a [new file with mode: 0644]
components/newlib/lib/libm-psram-workaround.a [new file with mode: 0644]
components/soc/component.mk [changed mode: 0755->0644]
components/soc/esp32/include/soc/soc.h
components/spi_flash/flash_mmap.c
tools/unit-test-app/sdkconfig

index df07108224f9ae9aac528592ce8870137c2af44e..a54fe30a9046d9249fc9d44fcb8ee723e37bf0ec 100644 (file)
@@ -8,6 +8,7 @@
 LINKER_SCRIPTS := \
        esp32.bootloader.ld \
        $(IDF_PATH)/components/esp32/ld/esp32.rom.ld \
+       $(IDF_PATH)/components/esp32/ld/esp32.rom.spiram_incompatible_fns.ld \
        $(IDF_PATH)/components/esp32/ld/esp32.peripherals.ld \
        esp32.bootloader.rom.ld
 
index 9c554f9ed9ee7b7966089f84603b08e365d5fc82..86932c3bf83a04ccda97cc654e2ec636227dad5c 100644 (file)
@@ -27,9 +27,103 @@ config MEMMAP_SMP
         The ESP32 contains two cores. If you plan to only use one, you can disable this item
         to save some memory. (ToDo: Make this automatically depend on unicore support)
 
+config SPIRAM_SUPPORT
+    bool "Support for external, SPI-connected RAM"
+    default "n"
+    help
+        This enables support for an external SPI RAM chip, connected in parallel with the 
+        main SPI flash chip.
+
+menu "SPI RAM config"
+    depends on SPIRAM_SUPPORT
+
+config SPIRAM_BOOT_INIT
+    bool "Initialize SPI RAM when booting the ESP32"
+    default "y"
+    help
+        If this is enabled, the SPI RAM will be enabled during initial boot. Unless you
+        have specific requirements, you'll want to leave this enabled so memory allocated
+        during boot-up can also be placed in SPI RAM.
+
+choice SPIRAM_USE
+    prompt "SPI RAM access method"
+    default SPIRAM_USE_MEMMAP
+    help
+        The SPI RAM can be accessed in multiple methods: by just having it available as an unmanaged
+        memory region in the ESP32 memory map, by integrating it in the ESP32s heap as 'special' memory
+        needing heap_caps_malloc to allocate, or by fully integrating it making malloc() also able to
+        return SPI RAM pointers.
+
+config SPIRAM_USE_MEMMAP
+    bool "Integrate RAM into ESP32 memory map"
+config SPIRAM_USE_CAPS_ALLOC
+    bool "Make RAM allocatable using heap_caps_malloc(..., MALLOC_CAP_SPISRAM)"
+    depends on TO_BE_DONE
+config SPIRAM_USE_MALLOC
+    bool "Make RAM allocatable using malloc as well"
+    depends on TO_BE_DONE
+endchoice
+
+choice SPIRAM_TYPE
+    prompt "Type of SPI RAM chip in use"
+    default SPIRAM_TYPE_ESPPSRAM32
+
+config SPIRAM_TYPE_ESPPSRAM32
+    bool "ESP-PSRAM32 or IS25WP032"
+endchoice
+
+config SPIRAM_SIZE
+    int
+    default 4194304 if SPIRAM_TYPE_ESPPSRAM32
+    default 0
+
+choice SPIRAM_SPEED
+    prompt "Set RAM clock speed"
+    default SPIRAM_CACHE_SPEED_40M
+    help
+        Select the speed for the SPI RAM chip.
+        If SPI RAM is enabled, we only support three combinations of SPI speed mode we supported now:
+
+        1. Flash SPI running at 40Mhz and RAM SPI running at 40Mhz
+        2. Flash SPI running at 80Mhz and RAM SPI running at 40Mhz
+        3. Flash SPI running at 80Mhz and RAM SPI running at 80Mhz
+
+           Note: If the third mode(80Mhz+80Mhz) is enabled, the VSPI port will be occupied by the system.
+                 Application code should never touch VSPI hardware in this case. The option to select
+                 80MHz will only be visible if the flash SPI speed is also 80MHz. (ESPTOOLPY_FLASHFREQ_80M is true)
+
+config SPIRAM_SPEED_40M
+    bool "40MHz clock speed"
+config SPIRAM_SPEED_80M
+    depends on ESPTOOLPY_FLASHFREQ_80M
+    bool "80MHz clock speed"
+endchoice
+
+config SPIRAM_MEMTEST
+    bool "Run memory test on SPI RAM initialization"
+    default "y"
+    help
+        Runs a rudimentary memory test on initialization. Aborts when memory test fails. Disable this for
+        slightly faster startop.
+
+config SPIRAM_CACHE_WORKAROUND
+    bool "Enable workaround for bug in SPI RAM cache for Rev1 ESP32s"
+    depends on SPIRAM_USE_MEMMAP || SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC
+    default "y"
+    help
+        Revision 1 of the ESP32 has a bug that can cause a write to PSRAM not to take place in some situations
+        when the cache line needs to be fetched from external RAM and an interrupt occurs. This enables a
+        fix in the compiler that makes sure the specific code that is vulnerable to this will not be emitted.
+        
+        This will also not use any bits of newlib that are located in ROM, opting for a version that is compiled
+        with the workaround and located in flash instead.
+
+
+endmenu
+
 config MEMMAP_TRACEMEM
-       bool
-       default "n"
+    bool
+    default "n"
 
 config MEMMAP_TRACEMEM_TWOBANKS
     bool
@@ -104,16 +198,6 @@ config ESP32_CORE_DUMP_LOG_LEVEL
     help
         Config core dump module logging level (0-5).
 
-# Not implemented and/or needs new silicon rev to work
-config MEMMAP_SPISRAM
-    bool "Use external SPI SRAM chip as main memory"
-    depends on ESP32_NEEDS_NEW_SILICON_REV
-    default "n"
-    help
-        The ESP32 can control an external SPI SRAM chip, adding the memory it contains to the
-        main memory map. Enable this if you have this hardware and want to use it in the same
-        way as on-chip RAM.
-        
 choice NUMBER_OF_UNIVERSAL_MAC_ADDRESS
     bool "Number of universally administered (by IEEE) MAC address"
     default FOUR_UNIVERSAL_MAC_ADDRESS
index 01f8d03c5af8f1f12c88233952059039be716f56..e832f2984d5ab5a8bea6fb86ebba2ce7cb51a8c3 100644 (file)
@@ -30,3 +30,14 @@ all: phy_init_data
 flash: phy_init_data
 
 endif # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION
+
+
+# Enable psram cache bug workaround in compiler if selected
+ifdef CONFIG_SPIRAM_CACHE_WORKAROUND
+CFLAGS+=-mfix-esp32-psram-cache-issue
+CXXFLAGS+=-mfix-esp32-psram-cache-issue
+#Filter out the standard libstdc++ linkage. The component.mk will add a specific 
+#cache-workaround-enabled version.
+LDFLAGS:=$(filter-out -lstdc++,$(LDFLAGS))
+endif
+
index bb58ad88f46b2533b5e7e687c50cb442aa5bd6d7..a8cd8606c723425a1b4bafc163e1e4fd7641464e 100644 (file)
@@ -7,8 +7,17 @@ ifndef CONFIG_NO_BLOBS
 LIBS := core rtc net80211 pp wpa smartconfig coexist wps wpa2 phy
 endif
 
+#Linker scripts used to link the final application.
+#Warning: These linker scripts are only used when the normal app is compiled; the bootloader
+#specifies its own scripts.
 LINKER_SCRIPTS += esp32.common.ld esp32.rom.ld esp32.peripherals.ld
 
+#SPI-RAM incompatible functions can be used in when the SPI RAM 
+#workaround is not enabled.
+ifndef CONFIG_SPIRAM_CACHE_WORKAROUND
+LINKER_SCRIPTS += esp32.rom.spiram_incompatible_fns.ld
+endif
+
 ifeq ("$(CONFIG_NEWLIB_NANO_FORMAT)","y")
 LINKER_SCRIPTS += esp32.rom.nanofmt.ld
 endif
@@ -28,6 +37,11 @@ COMPONENT_ADD_LDFLAGS += $(COMPONENT_PATH)/libhal.a \
                          -u ld_include_panic_highint_hdl \
                          $(addprefix -T ,$(LINKER_SCRIPTS))
 
+#The cache workaround also needs a c++ standard library recompiled with the workaround.
+ifdef CONFIG_SPIRAM_CACHE_WORKAROUND
+COMPONENT_ADD_LDFLAGS += $(COMPONENT_PATH)/libstdcc++-cache-workaround.a
+endif
+
 ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS))
 
 COMPONENT_SUBMODULES += lib
index 9f3a3bb22a81e8f82b94835d11106fb7ac7f5c5a..8650290cf06d7f6563109ace816075189a14cf06 100644 (file)
@@ -63,6 +63,7 @@
 #include "esp_core_dump.h"
 #include "esp_app_trace.h"
 #include "esp_efuse.h"
+#include "esp_spiram.h"
 #include "esp_clk.h"
 #include "esp_timer.h"
 #include "trax.h"
@@ -147,6 +148,13 @@ void IRAM_ATTR call_start_cpu0()
         memset(&_rtc_bss_start, 0, (&_rtc_bss_end - &_rtc_bss_start) * sizeof(_rtc_bss_start));
     }
 
+#if CONFIG_SPIRAM_BOOT_INIT
+    if (esp_spiram_init() != ESP_OK) {
+        ESP_EARLY_LOGE(TAG, "Failed to init external RAM!");
+        abort();
+    }
+#endif
+
     ESP_EARLY_LOGI(TAG, "Pro cpu up.");
 
 #if !CONFIG_FREERTOS_UNICORE
@@ -175,11 +183,23 @@ void IRAM_ATTR call_start_cpu0()
     DPORT_CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN);
 #endif
 
+
+#if CONFIG_SPIRAM_MEMTEST
+    bool ext_ram_ok=esp_spiram_test();
+    if (!ext_ram_ok) {
+        ESP_EARLY_LOGE(TAG, "External RAM failed memory test!");
+        abort();
+    }
+#endif
+
     /* Initialize heap allocator. WARNING: This *needs* to happen *after* the app cpu has booted.
        If the heap allocator is initialized first, it will put free memory linked list items into
        memory also used by the ROM. Starting the app cpu will let its ROM initialize that memory,
        corrupting those linked lists. Initializing the allocator *after* the app cpu has booted
-       works around this problem. */
+       works around this problem.
+       With SPI RAM enabled, there's a second reason: half of the SPI RAM will be managed by the
+       app CPU, and when that is not up yet, the memory will be inaccessible and heap_caps_init may
+       fail initializing it properly. */
     heap_caps_init();
 
     ESP_EARLY_LOGI(TAG, "Pro cpu start user code");
diff --git a/components/esp32/include/esp_spiram.h b/components/esp32/include/esp_spiram.h
new file mode 100644 (file)
index 0000000..2eb9c08
--- /dev/null
@@ -0,0 +1,61 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#ifndef __ESP_SPIRAM_H
+#define __ESP_SPIRAM_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "esp_err.h"
+
+/**
+ * @brief Initialize spiram interface/hardware. Normally called from cpu_start.c.
+ *
+ * @return ESP_OK on success
+ */
+esp_err_t esp_spiram_init();
+
+
+/**
+ * @brief Memory test for SPI RAM. Should be called after SPI RAM is initialized and
+ * (in case of a dual-core system) the app CPU is online. This test overwrites the 
+ * memory with crap, so do not call after e.g. the heap allocator has stored important
+ * stuff in SPI RAM.
+ *
+ * @return true on success, false on failed memory test
+ */
+bool esp_spiram_test();
+
+
+/**
+ * @brief Get the size of the attached SPI RAM chip selected in menuconfig
+ *
+ * @return Size in bytes, or 0 if no external RAM chip support compiled in.
+ */
+size_t esp_spiram_get_size();
+
+
+/**
+ * @brief Force a writeback of the data in the SPI RAM cache. This is to be called whenever
+ * cache is disabled, because disabling cache on the ESP32 discards the data in the SPI
+ * RAM cache.
+ *
+ * This is meant for use from within the SPI flash code.
+ */
+void esp_spiram_writeback_cache();
+
+
+
+#endif
\ No newline at end of file
index 6961854b4030676488ff2ff837f9286fdafb7ea1..d60eeb90c3a5faac2e552b035dd89af06e811495 100644 (file)
@@ -4,7 +4,6 @@ Generated for ROM with MD5sum:
 ab8282ae908fe9e7a63fb2a4ac2df013  ../../rom_image/prorom.elf
 */
 PROVIDE ( abort = 0x4000bba4 );
-PROVIDE ( abs = 0x40056340 );
 PROVIDE ( __absvdi2 = 0x4006387c );
 PROVIDE ( __absvsi2 = 0x40063868 );
 PROVIDE ( Add2SelfBigHex256 = 0x40015b7c );
@@ -21,15 +20,8 @@ PROVIDE ( aes_128_cbc_encrypt = 0x4005cc18 );
 PROVIDE ( aes_unwrap = 0x4005ccf0 );
 PROVIDE ( app_gpio_arg = 0x3ffe003c );
 PROVIDE ( app_gpio_handler = 0x3ffe0040 );
-PROVIDE ( __ascii_wctomb = 0x40058ef0 );
-PROVIDE ( asctime = 0x40059588 );
-PROVIDE ( asctime_r = 0x40000ec8 );
 PROVIDE ( __ashldi3 = 0x4000c818 );
 PROVIDE ( __ashrdi3 = 0x4000c830 );
-PROVIDE ( atoi = 0x400566c4 );
-PROVIDE ( _atoi_r = 0x400566d4 );
-PROVIDE ( atol = 0x400566ec );
-PROVIDE ( _atol_r = 0x400566fc );
 PROVIDE ( base64_decode = 0x4005ced8 );
 PROVIDE ( base64_encode = 0x4005cdbc );
 PROVIDE ( BasePoint_x_256 = 0x3ff97488 );
@@ -49,7 +41,6 @@ PROVIDE ( btdm_r_modules_func_p_get = 0x4005427c );
 PROVIDE ( btdm_r_modules_func_p_set = 0x40054270 );
 PROVIDE ( btdm_r_plf_func_p_set = 0x40054288 );
 PROVIDE ( bt_util_buf_env = 0x3ffb8bd4 );
-PROVIDE ( bzero = 0x4000c1f4 );
 PROVIDE ( cache_flash_mmu_set_rom = 0x400095e0 );
 PROVIDE ( Cache_Flush_rom = 0x40009a14 );
 PROVIDE ( Cache_Read_Disable_rom = 0x40009ab8 );
@@ -60,10 +51,7 @@ PROVIDE ( cache_sram_mmu_set_rom = 0x400097f4 );
 PROVIDE ( calc_rtc_memory_crc = 0x40008170 );
 PROVIDE ( calloc = 0x4000bee4 );
 PROVIDE ( _calloc_r = 0x4000bbf8 );
-PROVIDE ( _cleanup = 0x40001df8 );
-PROVIDE ( _cleanup_r = 0x40001d48 );
 PROVIDE ( __clear_cache = 0x40063860 );
-PROVIDE ( close = 0x40001778 );
 PROVIDE ( _close_r = 0x4000bd3c );
 PROVIDE ( __clrsbdi2 = 0x40064a38 );
 PROVIDE ( __clrsbsi2 = 0x40064a20 );
@@ -79,9 +67,6 @@ PROVIDE ( crc32_be = 0x4005d024 );
 PROVIDE ( crc32_le = 0x4005cfec );
 PROVIDE ( crc8_be = 0x4005d114 );
 PROVIDE ( crc8_le = 0x4005d0e0 );
-PROVIDE ( creat = 0x40000e8c );
-PROVIDE ( ctime = 0x400595b0 );
-PROVIDE ( ctime_r = 0x400595c4 );
 PROVIDE ( _ctype_ = 0x3ff96354 );
 PROVIDE ( __ctype_ptr__ = 0x3ff96350 );
 PROVIDE ( __ctzdi2 = 0x4000ca64 );
@@ -118,7 +103,6 @@ PROVIDE ( dh_group2_generator = 0x3ff9ada2 );
 PROVIDE ( dh_group2_prime = 0x3ff9ad22 );
 PROVIDE ( dh_group5_generator = 0x3ff9ad21 );
 PROVIDE ( dh_group5_prime = 0x3ff9ac61 );
-PROVIDE ( div = 0x40056348 );
 PROVIDE ( __divdc3 = 0x40064460 );
 PROVIDE ( __divdf3 = 0x40002954 );
 PROVIDE ( __divdi3 = 0x4000ca84 );
@@ -126,14 +110,10 @@ PROVIDE ( __divsc3 = 0x40064200 );
 PROVIDE ( __divsf3 = 0x4000234c );
 PROVIDE ( __divsi3 = 0x4000c7b8 );
 PROVIDE ( g_rom_spiflash_dummy_len_plus = 0x3ffae290 );
-PROVIDE ( __dummy_lock = 0x4000c728 );
-PROVIDE ( __dummy_lock_try = 0x4000c730 );
 PROVIDE ( ecc_env = 0x3ffb8d60 );
 PROVIDE ( ecc_Jacobian_InfinityPoint256 = 0x3ff972e8 );
 PROVIDE ( em_buf_env = 0x3ffb8d74 );
 PROVIDE ( environ = 0x3ffae0b4 );
-PROVIDE ( __env_lock = 0x40001fd4 );
-PROVIDE ( __env_unlock = 0x40001fe0 );
 PROVIDE ( __eqdf2 = 0x400636a8 );
 PROVIDE ( __eqsf2 = 0x40063374 );
 PROVIDE ( esp_crc8 = 0x4005d144 );
@@ -143,13 +123,8 @@ PROVIDE ( ets_startup_callback = 0x3ffe0404 );
 PROVIDE ( exc_cause_table = 0x3ff991d0 );
 PROVIDE ( _exit_r = 0x4000bd28 );
 PROVIDE ( __extendsfdf2 = 0x40002c34 );
-PROVIDE ( fclose = 0x400020ac );
-PROVIDE ( _fclose_r = 0x40001fec );
-PROVIDE ( fflush = 0x40059394 );
-PROVIDE ( _fflush_r = 0x40059320 );
 PROVIDE ( __ffsdi2 = 0x4000ca2c );
 PROVIDE ( __ffssi2 = 0x4000c804 );
-PROVIDE ( _findenv_r = 0x40001f44 );
 PROVIDE ( __fixdfdi = 0x40002ac4 );
 PROVIDE ( __fixdfsi = 0x40002a78 );
 PROVIDE ( __fixsfdi = 0x4000244c );
@@ -165,31 +140,19 @@ PROVIDE ( __floatundidf = 0x4000c978 );
 PROVIDE ( __floatundisf = 0x4000c8b0 );
 PROVIDE ( __floatunsidf = 0x4000c938 );
 PROVIDE ( __floatunsisf = 0x4000c864 );
-PROVIDE ( __fp_lock_all = 0x40001f1c );
-PROVIDE ( __fp_unlock_all = 0x40001f30 );
-PROVIDE ( fputwc = 0x40058ea8 );
-PROVIDE ( __fputwc = 0x40058da0 );
-PROVIDE ( _fputwc_r = 0x40058e4c );
 PROVIDE ( free = 0x4000beb8 );
 PROVIDE ( _free_r = 0x4000bbcc );
 PROVIDE ( _fstat_r = 0x4000bccc );
-PROVIDE ( _fwalk = 0x4000c738 );
-PROVIDE ( _fwalk_reent = 0x4000c770 );
 PROVIDE ( __gcc_bcmp = 0x40064a70 );
 PROVIDE ( __gedf2 = 0x40063768 );
 PROVIDE ( __gesf2 = 0x4006340c );
-PROVIDE ( __get_current_time_locale = 0x40001834 );
-PROVIDE ( _getenv_r = 0x40001fbc );
 PROVIDE ( _getpid_r = 0x4000bcfc );
 PROVIDE ( __getreent = 0x4000be8c );
 PROVIDE ( _gettimeofday_r = 0x4000bc58 );
-PROVIDE ( __gettzinfo = 0x40001fcc );
 PROVIDE ( GF_Jacobian_Point_Addition256 = 0x400163a4 );
 PROVIDE ( GF_Jacobian_Point_Double256 = 0x40016260 );
 PROVIDE ( GF_Point_Jacobian_To_Affine256 = 0x40016b0c );
 PROVIDE ( _global_impure_ptr = 0x3ffae0b0 );
-PROVIDE ( gmtime = 0x40059848 );
-PROVIDE ( gmtime_r = 0x40059868 );
 PROVIDE ( g_phyFuns_instance = 0x3ffae0c4 );
 PROVIDE ( g_rom_flashchip = 0x3ffae270 );
 PROVIDE ( __gtdf2 = 0x400636dc );
@@ -216,26 +179,10 @@ PROVIDE ( hmac_sha1 = 0x40060acc );
 PROVIDE ( hmac_sha1_vector = 0x400609e4 );
 PROVIDE ( hmac_sha256 = 0x40060d58 );
 PROVIDE ( hmac_sha256_vector = 0x40060c84 );
-PROVIDE ( isalnum = 0x40000f04 );
-PROVIDE ( isalpha = 0x40000f18 );
-PROVIDE ( isascii = 0x4000c20c );
-PROVIDE ( _isatty_r = 0x40000ea0 );
-PROVIDE ( isblank = 0x40000f2c );
-PROVIDE ( iscntrl = 0x40000f50 );
-PROVIDE ( isdigit = 0x40000f64 );
-PROVIDE ( isgraph = 0x40000f94 );
-PROVIDE ( islower = 0x40000f78 );
-PROVIDE ( isprint = 0x40000fa8 );
-PROVIDE ( ispunct = 0x40000fc0 );
-PROVIDE ( isspace = 0x40000fd4 );
-PROVIDE ( isupper = 0x40000fe8 );
-PROVIDE ( itoa = 0x400566b4 );
-PROVIDE ( __itoa = 0x40056678 );
 PROVIDE ( jd_decomp = 0x400613e8 );
 PROVIDE ( jd_prepare = 0x40060fa8 );
 PROVIDE ( ke_env = 0x3ffb93cc );
 PROVIDE ( _kill_r = 0x4000bd10 );
-PROVIDE ( labs = 0x40056370 );
 PROVIDE ( lb_default_handler = 0x3ff982b8 );
 PROVIDE ( lb_default_state_tab_p_get = 0x4001c198 );
 PROVIDE ( lb_env = 0x3ffb9424 );
@@ -251,7 +198,6 @@ PROVIDE ( ld_acl_br_types = 0x3ff98a36 );
 PROVIDE ( ld_acl_edr_sizes = 0x3ff98a14 );
 PROVIDE ( ld_acl_edr_types = 0x3ff98a22 );
 PROVIDE ( ld_env = 0x3ffb9510 );
-PROVIDE ( ldiv = 0x40056378 );
 PROVIDE ( ld_pcm_settings_dft = 0x3ff98a0c );
 PROVIDE ( ld_sched_params = 0x3ffb96c0 );
 PROVIDE ( ld_sync_train_channels = 0x3ff98a3c );
@@ -292,25 +238,14 @@ PROVIDE ( lm_n_page_tab = 0x3ff990e8 );
 PROVIDE ( lmp_desc_tab = 0x3ff96e6c );
 PROVIDE ( lmp_ext_desc_tab = 0x3ff96d9c );
 PROVIDE ( lm_state = 0x3ffb9a1c );
-PROVIDE ( __locale_charset = 0x40059540 );
-PROVIDE ( __locale_cjk_lang = 0x40059558 );
-PROVIDE ( localeconv = 0x4005957c );
-PROVIDE ( _localeconv_r = 0x40059560 );
-PROVIDE ( __locale_mb_cur_max = 0x40059548 );
-PROVIDE ( __locale_msgcharset = 0x40059550 );
-PROVIDE ( localtime = 0x400595dc );
-PROVIDE ( localtime_r = 0x400595fc );
-PROVIDE ( _lock_acquire = 0x4000be14 );
 PROVIDE ( _lock_acquire_recursive = 0x4000be28 );
 PROVIDE ( _lock_close = 0x4000bdec );
 PROVIDE ( _lock_close_recursive = 0x4000be00 );
 PROVIDE ( _lock_init = 0x4000bdc4 );
 PROVIDE ( _lock_init_recursive = 0x4000bdd8 );
-PROVIDE ( _lock_release = 0x4000be64 );
 PROVIDE ( _lock_release_recursive = 0x4000be78 );
 PROVIDE ( _lock_try_acquire = 0x4000be3c );
 PROVIDE ( _lock_try_acquire_recursive = 0x4000be50 );
-PROVIDE ( longjmp = 0x400562cc );
 PROVIDE ( _lseek_r = 0x4000bd8c );
 PROVIDE ( __lshrdi3 = 0x4000c84c );
 PROVIDE ( __ltdf2 = 0x40063790 );
@@ -323,14 +258,6 @@ PROVIDE ( MD5Final = 0x4005db1c );
 PROVIDE ( MD5Init = 0x4005da7c );
 PROVIDE ( MD5Update = 0x4005da9c );
 PROVIDE ( md5_vector = 0x4005db80 );
-PROVIDE ( memccpy = 0x4000c220 );
-PROVIDE ( memchr = 0x4000c244 );
-PROVIDE ( memcmp = 0x4000c260 );
-PROVIDE ( memcpy = 0x4000c2c8 );
-PROVIDE ( memmove = 0x4000c3c0 );
-PROVIDE ( memrchr = 0x4000c400 );
-PROVIDE ( memset = 0x4000c44c );
-PROVIDE ( mktime = 0x4005a5e8 );
 PROVIDE ( mmu_init = 0x400095a4 );
 PROVIDE ( __moddi3 = 0x4000cd4c );
 PROVIDE ( __modsi3 = 0x4000c7c0 );
@@ -360,7 +287,6 @@ PROVIDE ( __nesf2 = 0x40063374 );
 PROVIDE ( notEqual256 = 0x40015b04 );
 PROVIDE ( __nsau_data = 0x3ff96544 );
 PROVIDE ( one_bits = 0x3ff971f8 );
-PROVIDE ( open = 0x4000178c );
 PROVIDE ( _open_r = 0x4000bd54 );
 PROVIDE ( __paritysi2 = 0x40002f3c );
 PROVIDE ( pbkdf2_sha1 = 0x40060ba4 );
@@ -372,10 +298,6 @@ PROVIDE ( __powidf2 = 0x400638d4 );
 PROVIDE ( __powisf2 = 0x4006389c );
 PROVIDE ( _Pri_4_HandlerAddress = 0x3ffe0648 );
 PROVIDE ( _Pri_5_HandlerAddress = 0x3ffe064c );
-PROVIDE ( qsort = 0x40056424 );
-PROVIDE ( _raise_r = 0x4000bc70 );
-PROVIDE ( rand = 0x40001058 );
-PROVIDE ( rand_r = 0x400010d4 );
 PROVIDE ( r_btdm_option_data = 0x3ffae6e0 );
 PROVIDE ( r_bt_util_buf_acl_rx_alloc = 0x40010218 );
 PROVIDE ( r_bt_util_buf_acl_rx_free = 0x40010234 );
@@ -419,7 +341,6 @@ PROVIDE ( r_E22 = 0x400109b4 );
 PROVIDE ( r_E3 = 0x40010a58 );
 PROVIDE ( r_ea_alarm_clear = 0x40015ab4 );
 PROVIDE ( r_ea_alarm_set = 0x40015a10 );
-PROVIDE ( read = 0x400017dc );
 PROVIDE ( _read_r = 0x4000bda8 );
 PROVIDE ( r_ea_elt_cancel = 0x400150d0 );
 PROVIDE ( r_ea_elt_create = 0x40015264 );
@@ -1440,24 +1361,10 @@ PROVIDE ( rwip_priority = 0x3ff99159 );
 PROVIDE ( rwip_rf = 0x3ffbdb28 );
 PROVIDE ( rwip_rf_p_get = 0x400558f4 );
 PROVIDE ( r_XorKey = 0x400112c0 );
-PROVIDE ( sbrk = 0x400017f4 );
 PROVIDE ( _sbrk_r = 0x4000bce4 );
-PROVIDE ( __sccl = 0x4000c498 );
-PROVIDE ( __sclose = 0x400011b8 );
-PROVIDE ( __seofread = 0x40001148 );
-PROVIDE ( setjmp = 0x40056268 );
-PROVIDE ( setlocale = 0x40059568 );
-PROVIDE ( _setlocale_r = 0x4005950c );
 PROVIDE ( __sf_fake_stderr = 0x3ff96458 );
 PROVIDE ( __sf_fake_stdin = 0x3ff96498 );
 PROVIDE ( __sf_fake_stdout = 0x3ff96478 );
-PROVIDE ( __sflush_r = 0x400591e0 );
-PROVIDE ( __sfmoreglue = 0x40001dc8 );
-PROVIDE ( __sfp = 0x40001e90 );
-PROVIDE ( __sfp_lock_acquire = 0x40001e08 );
-PROVIDE ( __sfp_lock_release = 0x40001e14 );
-PROVIDE ( __sfputs_r = 0x40057790 );
-PROVIDE ( __sfvwrite_r = 0x4005893c );
 PROVIDE ( sha1_prf = 0x40060ae8 );
 PROVIDE ( sha1_vector = 0x40060b64 );
 PROVIDE ( sha256_prf = 0x40060d70 );
@@ -1466,9 +1373,6 @@ PROVIDE ( sha_blk_bits = 0x3ff99290 );
 PROVIDE ( sha_blk_bits_bytes = 0x3ff99288 );
 PROVIDE ( sha_blk_hash_bytes = 0x3ff9928c );
 PROVIDE ( sig_matrix = 0x3ffae293 );
-PROVIDE ( __sinit = 0x40001e38 );
-PROVIDE ( __sinit_lock_acquire = 0x40001e20 );
-PROVIDE ( __sinit_lock_release = 0x40001e2c );
 PROVIDE ( sip_after_tx_complete = 0x4000b358 );
 PROVIDE ( sip_alloc_to_host_evt = 0x4000ab9c );
 PROVIDE ( sip_get_ptr = 0x4000b34c );
@@ -1494,15 +1398,7 @@ PROVIDE ( slc_reattach = 0x4000b62c );
 PROVIDE ( slc_send_to_host_chain = 0x4000b6a0 );
 PROVIDE ( slc_set_host_io_max_window = 0x4000b89c );
 PROVIDE ( slc_to_host_chain_recycle = 0x4000b758 );
-PROVIDE ( __smakebuf_r = 0x40059108 );
 PROVIDE ( specialModP256 = 0x4001600c );
-PROVIDE ( srand = 0x40001004 );
-PROVIDE ( __sread = 0x40001118 );
-PROVIDE ( __srefill_r = 0x400593d4 );
-PROVIDE ( __sseek = 0x40001184 );
-PROVIDE ( __ssprint_r = 0x40056ff8 );
-PROVIDE ( __ssputs_r = 0x40056f2c );
-PROVIDE ( __ssrefill_r = 0x40057fec );
 PROVIDE ( __stack = 0x3ffe3f20 );
 PROVIDE ( __stack_app = 0x3ffe7e30 );
 PROVIDE ( _stack_sentry = 0x3ffe1320 );
@@ -1511,41 +1407,7 @@ PROVIDE ( _start = 0x40000704 );
 PROVIDE ( start_tb_console = 0x4005a980 );
 PROVIDE ( _stat_r = 0x4000bcb4 );
 PROVIDE ( _stext = 0x40000560 );
-PROVIDE ( strcasecmp = 0x400011cc );
-PROVIDE ( strcasestr = 0x40001210 );
-PROVIDE ( strcat = 0x4000c518 );
-PROVIDE ( strchr = 0x4000c53c );
-PROVIDE ( strcmp = 0x40001274 );
-PROVIDE ( strcoll = 0x40001398 );
-PROVIDE ( strcpy = 0x400013ac );
-PROVIDE ( strcspn = 0x4000c558 );
-PROVIDE ( strdup = 0x4000143c );
-PROVIDE ( _strdup_r = 0x40001450 );
-PROVIDE ( strftime = 0x40059ab4 );
-PROVIDE ( strlcat = 0x40001470 );
-PROVIDE ( strlcpy = 0x4000c584 );
-PROVIDE ( strlen = 0x400014c0 );
-PROVIDE ( strlwr = 0x40001524 );
-PROVIDE ( strncasecmp = 0x40001550 );
-PROVIDE ( strncat = 0x4000c5c4 );
-PROVIDE ( strncmp = 0x4000c5f4 );
-PROVIDE ( strncpy = 0x400015d4 );
-PROVIDE ( strndup = 0x400016b0 );
-PROVIDE ( _strndup_r = 0x400016c4 );
-PROVIDE ( strnlen = 0x4000c628 );
-PROVIDE ( strrchr = 0x40001708 );
-PROVIDE ( strsep = 0x40001734 );
-PROVIDE ( strspn = 0x4000c648 );
-PROVIDE ( strstr = 0x4000c674 );
-PROVIDE ( __strtok_r = 0x4000c6a8 );
-PROVIDE ( strtok_r = 0x4000c70c );
-PROVIDE ( strtol = 0x4005681c );
-PROVIDE ( _strtol_r = 0x40056714 );
-PROVIDE ( strtoul = 0x4005692c );
-PROVIDE ( _strtoul_r = 0x40056834 );
-PROVIDE ( strupr = 0x4000174c );
 PROVIDE ( __subdf3 = 0x400026e4 );
-PROVIDE ( __submore = 0x40058f3c );
 PROVIDE ( __subsf3 = 0x400021d0 );
 PROVIDE ( SubtractBigHex256 = 0x40015bcc );
 PROVIDE ( SubtractBigHexMod256 = 0x40015e8c );
@@ -1554,15 +1416,9 @@ PROVIDE ( SubtractFromSelfBigHex256 = 0x40015c20 );
 PROVIDE ( SubtractFromSelfBigHexSign256 = 0x40015dc8 );
 PROVIDE ( __subvdi3 = 0x40002d20 );
 PROVIDE ( __subvsi3 = 0x40002cf8 );
-PROVIDE ( _sungetc_r = 0x40057f6c );
-PROVIDE ( __swbuf = 0x40058cb4 );
-PROVIDE ( __swbuf_r = 0x40058bec );
-PROVIDE ( __swrite = 0x40001150 );
-PROVIDE ( __swsetup_r = 0x40058cc8 );
 PROVIDE ( sw_to_hw = 0x3ffb8d40 );
 PROVIDE ( syscall_table_ptr_app = 0x3ffae020 );
 PROVIDE ( syscall_table_ptr_pro = 0x3ffae024 );
-PROVIDE ( _system_r = 0x4000bc10 );
 PROVIDE ( tdefl_compress = 0x400600bc );
 PROVIDE ( tdefl_compress_buffer = 0x400607f4 );
 PROVIDE ( tdefl_compress_mem_to_mem = 0x40060900 );
@@ -1572,24 +1428,13 @@ PROVIDE ( tdefl_get_prev_return_status = 0x400608d0 );
 PROVIDE ( tdefl_init = 0x40060810 );
 PROVIDE ( tdefl_write_image_to_png_file_in_memory = 0x4006091c );
 PROVIDE ( tdefl_write_image_to_png_file_in_memory_ex = 0x40060910 );
-PROVIDE ( time = 0x40001844 );
-PROVIDE ( __time_load_locale = 0x4000183c );
-PROVIDE ( times = 0x40001808 );
 PROVIDE ( _times_r = 0x4000bc40 );
 PROVIDE ( _timezone = 0x3ffae0a0 );
 PROVIDE ( tinfl_decompress = 0x4005ef30 );
 PROVIDE ( tinfl_decompress_mem_to_callback = 0x40060090 );
 PROVIDE ( tinfl_decompress_mem_to_mem = 0x40060050 );
-PROVIDE ( toascii = 0x4000c720 );
-PROVIDE ( tolower = 0x40001868 );
-PROVIDE ( toupper = 0x40001884 );
 PROVIDE ( __truncdfsf2 = 0x40002b90 );
-PROVIDE ( __tzcalc_limits = 0x400018a0 );
-PROVIDE ( __tz_lock = 0x40001a04 );
 PROVIDE ( _tzname = 0x3ffae030 );
-PROVIDE ( tzset = 0x40001a1c );
-PROVIDE ( _tzset_r = 0x40001a28 );
-PROVIDE ( __tz_unlock = 0x40001a10 );
 PROVIDE ( UartDev = 0x3ffe019c );
 PROVIDE ( __ucmpdi2 = 0x40063840 );
 PROVIDE ( __udivdi3 = 0x4000cff8 );
@@ -1599,20 +1444,12 @@ PROVIDE ( __udiv_w_sdiv = 0x40064aa8 );
 PROVIDE ( __umoddi3 = 0x4000d280 );
 PROVIDE ( __umodsi3 = 0x4000c7d0 );
 PROVIDE ( __umulsidi3 = 0x4000c7d8 );
-PROVIDE ( ungetc = 0x400590f4 );
-PROVIDE ( _ungetc_r = 0x40058fa0 );
 PROVIDE ( _unlink_r = 0x4000bc84 );
 PROVIDE ( __unorddf2 = 0x400637f4 );
 PROVIDE ( __unordsf2 = 0x40063478 );
 PROVIDE ( user_code_start = 0x3ffe0400 );
-PROVIDE ( utoa = 0x40056258 );
-PROVIDE ( __utoa = 0x400561f0 );
 PROVIDE ( veryBigHexP256 = 0x3ff9736c );
-PROVIDE ( wcrtomb = 0x40058920 );
-PROVIDE ( _wcrtomb_r = 0x400588d8 );
 PROVIDE ( __wctomb = 0x3ff96540 );
-PROVIDE ( _wctomb_r = 0x40058f14 );
-PROVIDE ( write = 0x4000181c );
 PROVIDE ( _write_r = 0x4000bd70 );
 PROVIDE ( xthal_bcopy = 0x4000c098 );
 PROVIDE ( xthal_copy123 = 0x4000c124 );
diff --git a/components/esp32/ld/esp32.rom.spiram_incompatible_fns.ld b/components/esp32/ld/esp32.rom.spiram_incompatible_fns.ld
new file mode 100644 (file)
index 0000000..e4899b6
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ If the spiram compiler workaround is active, these functions from ROM cannot be used. If the workaround is not
+ active (e.g. because the system does not use SPI RAM) then these functions are okay to use.
+*/
+PROVIDE ( abs = 0x40056340 );
+PROVIDE ( __ascii_wctomb = 0x40058ef0 );
+PROVIDE ( asctime = 0x40059588 );
+PROVIDE ( asctime_r = 0x40000ec8 );
+PROVIDE ( atoi = 0x400566c4 );
+PROVIDE ( _atoi_r = 0x400566d4 );
+PROVIDE ( atol = 0x400566ec );
+PROVIDE ( _atol_r = 0x400566fc );
+PROVIDE ( bzero = 0x4000c1f4 );
+PROVIDE ( _cleanup = 0x40001df8 );
+PROVIDE ( _cleanup_r = 0x40001d48 );
+PROVIDE ( close = 0x40001778 );
+PROVIDE ( creat = 0x40000e8c );
+PROVIDE ( ctime = 0x400595b0 );
+PROVIDE ( ctime_r = 0x400595c4 );
+PROVIDE ( div = 0x40056348 );
+PROVIDE ( __dummy_lock = 0x4000c728 );
+PROVIDE ( __dummy_lock_try = 0x4000c730 );
+PROVIDE ( __env_lock = 0x40001fd4 );
+PROVIDE ( __env_unlock = 0x40001fe0 );
+PROVIDE ( fclose = 0x400020ac );
+PROVIDE ( _fclose_r = 0x40001fec );
+PROVIDE ( fflush = 0x40059394 );
+PROVIDE ( _fflush_r = 0x40059320 );
+PROVIDE ( _findenv_r = 0x40001f44 );
+PROVIDE ( __fp_lock_all = 0x40001f1c );
+PROVIDE ( __fp_unlock_all = 0x40001f30 );
+PROVIDE ( fputwc = 0x40058ea8 );
+PROVIDE ( __fputwc = 0x40058da0 );
+PROVIDE ( _fputwc_r = 0x40058e4c );
+PROVIDE ( _fwalk = 0x4000c738 );
+PROVIDE ( _fwalk_reent = 0x4000c770 );
+PROVIDE ( __get_current_time_locale = 0x40001834 );
+PROVIDE ( _getenv_r = 0x40001fbc );
+PROVIDE ( __gettzinfo = 0x40001fcc );
+PROVIDE ( gmtime = 0x40059848 );
+PROVIDE ( gmtime_r = 0x40059868 );
+PROVIDE ( isalnum = 0x40000f04 );
+PROVIDE ( isalpha = 0x40000f18 );
+PROVIDE ( isascii = 0x4000c20c );
+PROVIDE ( _isatty_r = 0x40000ea0 );
+PROVIDE ( isblank = 0x40000f2c );
+PROVIDE ( iscntrl = 0x40000f50 );
+PROVIDE ( isdigit = 0x40000f64 );
+PROVIDE ( isgraph = 0x40000f94 );
+PROVIDE ( islower = 0x40000f78 );
+PROVIDE ( isprint = 0x40000fa8 );
+PROVIDE ( ispunct = 0x40000fc0 );
+PROVIDE ( isspace = 0x40000fd4 );
+PROVIDE ( isupper = 0x40000fe8 );
+PROVIDE ( itoa = 0x400566b4 );
+PROVIDE ( __itoa = 0x40056678 );
+PROVIDE ( labs = 0x40056370 );
+PROVIDE ( ldiv = 0x40056378 );
+PROVIDE ( __locale_charset = 0x40059540 );
+PROVIDE ( __locale_cjk_lang = 0x40059558 );
+PROVIDE ( localeconv = 0x4005957c );
+PROVIDE ( _localeconv_r = 0x40059560 );
+PROVIDE ( __locale_mb_cur_max = 0x40059548 );
+PROVIDE ( __locale_msgcharset = 0x40059550 );
+PROVIDE ( localtime = 0x400595dc );
+PROVIDE ( localtime_r = 0x400595fc );
+PROVIDE ( _lock_acquire = 0x4000be14 );
+PROVIDE ( _lock_release = 0x4000be64 );
+PROVIDE ( longjmp = 0x400562cc );
+PROVIDE ( memccpy = 0x4000c220 );
+PROVIDE ( memchr = 0x4000c244 );
+PROVIDE ( memcmp = 0x4000c260 );
+PROVIDE ( memcpy = 0x4000c2c8 );
+PROVIDE ( memmove = 0x4000c3c0 );
+PROVIDE ( memrchr = 0x4000c400 );
+PROVIDE ( memset = 0x4000c44c );
+PROVIDE ( mktime = 0x4005a5e8 );
+PROVIDE ( open = 0x4000178c );
+PROVIDE ( qsort = 0x40056424 );
+PROVIDE ( _raise_r = 0x4000bc70 );
+PROVIDE ( rand = 0x40001058 );
+PROVIDE ( rand_r = 0x400010d4 );
+PROVIDE ( read = 0x400017dc );
+PROVIDE ( sbrk = 0x400017f4 );
+PROVIDE ( __sccl = 0x4000c498 );
+PROVIDE ( __sclose = 0x400011b8 );
+PROVIDE ( __seofread = 0x40001148 );
+PROVIDE ( setjmp = 0x40056268 );
+PROVIDE ( setlocale = 0x40059568 );
+PROVIDE ( _setlocale_r = 0x4005950c );
+PROVIDE ( __sflush_r = 0x400591e0 );
+PROVIDE ( __sfmoreglue = 0x40001dc8 );
+PROVIDE ( __sfp = 0x40001e90 );
+PROVIDE ( __sfp_lock_acquire = 0x40001e08 );
+PROVIDE ( __sfp_lock_release = 0x40001e14 );
+PROVIDE ( __sfputs_r = 0x40057790 );
+PROVIDE ( __sfvwrite_r = 0x4005893c );
+PROVIDE ( __sinit = 0x40001e38 );
+PROVIDE ( __sinit_lock_acquire = 0x40001e20 );
+PROVIDE ( __sinit_lock_release = 0x40001e2c );
+PROVIDE ( __smakebuf_r = 0x40059108 );
+PROVIDE ( srand = 0x40001004 );
+PROVIDE ( __sread = 0x40001118 );
+PROVIDE ( __srefill_r = 0x400593d4 );
+PROVIDE ( __sseek = 0x40001184 );
+PROVIDE ( __ssprint_r = 0x40056ff8 );
+PROVIDE ( __ssputs_r = 0x40056f2c );
+PROVIDE ( __ssrefill_r = 0x40057fec );
+PROVIDE ( strcasecmp = 0x400011cc );
+PROVIDE ( strcasestr = 0x40001210 );
+PROVIDE ( strcat = 0x4000c518 );
+PROVIDE ( strchr = 0x4000c53c );
+PROVIDE ( strcmp = 0x40001274 );
+PROVIDE ( strcoll = 0x40001398 );
+PROVIDE ( strcpy = 0x400013ac );
+PROVIDE ( strcspn = 0x4000c558 );
+PROVIDE ( strdup = 0x4000143c );
+PROVIDE ( _strdup_r = 0x40001450 );
+PROVIDE ( strftime = 0x40059ab4 );
+PROVIDE ( strlcat = 0x40001470 );
+PROVIDE ( strlcpy = 0x4000c584 );
+PROVIDE ( strlen = 0x400014c0 );
+PROVIDE ( strlwr = 0x40001524 );
+PROVIDE ( strncasecmp = 0x40001550 );
+PROVIDE ( strncat = 0x4000c5c4 );
+PROVIDE ( strncmp = 0x4000c5f4 );
+PROVIDE ( strncpy = 0x400015d4 );
+PROVIDE ( strndup = 0x400016b0 );
+PROVIDE ( _strndup_r = 0x400016c4 );
+PROVIDE ( strnlen = 0x4000c628 );
+PROVIDE ( strrchr = 0x40001708 );
+PROVIDE ( strsep = 0x40001734 );
+PROVIDE ( strspn = 0x4000c648 );
+PROVIDE ( strstr = 0x4000c674 );
+PROVIDE ( __strtok_r = 0x4000c6a8 );
+PROVIDE ( strtok_r = 0x4000c70c );
+PROVIDE ( strtol = 0x4005681c );
+PROVIDE ( _strtol_r = 0x40056714 );
+PROVIDE ( strtoul = 0x4005692c );
+PROVIDE ( _strtoul_r = 0x40056834 );
+PROVIDE ( strupr = 0x4000174c );
+PROVIDE ( __submore = 0x40058f3c );
+PROVIDE ( _sungetc_r = 0x40057f6c );
+PROVIDE ( __swbuf = 0x40058cb4 );
+PROVIDE ( __swbuf_r = 0x40058bec );
+PROVIDE ( __swrite = 0x40001150 );
+PROVIDE ( __swsetup_r = 0x40058cc8 );
+PROVIDE ( _system_r = 0x4000bc10 );
+PROVIDE ( time = 0x40001844 );
+PROVIDE ( __time_load_locale = 0x4000183c );
+PROVIDE ( times = 0x40001808 );
+PROVIDE ( toascii = 0x4000c720 );
+PROVIDE ( tolower = 0x40001868 );
+PROVIDE ( toupper = 0x40001884 );
+PROVIDE ( __tzcalc_limits = 0x400018a0 );
+PROVIDE ( __tz_lock = 0x40001a04 );
+PROVIDE ( tzset = 0x40001a1c );
+PROVIDE ( _tzset_r = 0x40001a28 );
+PROVIDE ( __tz_unlock = 0x40001a10 );
+PROVIDE ( ungetc = 0x400590f4 );
+PROVIDE ( _ungetc_r = 0x40058fa0 );
+PROVIDE ( utoa = 0x40056258 );
+PROVIDE ( __utoa = 0x400561f0 );
+PROVIDE ( wcrtomb = 0x40058920 );
+PROVIDE ( _wcrtomb_r = 0x400588d8 );
+PROVIDE ( _wctomb_r = 0x40058f14 );
+PROVIDE ( write = 0x4000181c );
diff --git a/components/esp32/libstdcc++-cache-workaround.a b/components/esp32/libstdcc++-cache-workaround.a
new file mode 100644 (file)
index 0000000..2e57b43
Binary files /dev/null and b/components/esp32/libstdcc++-cache-workaround.a differ
diff --git a/components/esp32/spiram.c b/components/esp32/spiram.c
new file mode 100644 (file)
index 0000000..2cd5d52
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+Abstraction layer for spi-ram. For now, it's no more than a stub for the spiram_psram functions, but if 
+we add more types of external RAM memory, this can be made into a more intelligent dispatcher.
+*/
+
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdint.h>
+#include <string.h>
+
+#include "sdkconfig.h"
+#include "esp_attr.h"
+#include "esp_err.h"
+#include "spiram_psram.h"
+#include "esp_log.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/xtensa_api.h"
+#include "soc/soc.h"
+#include "soc/dport_reg.h"
+#include "rom/cache.h"
+
+#if CONFIG_FREERTOS_UNICORE
+#define PSRAM_MODE PSRAM_VADDR_MODE_NORMAL
+#else
+#if CONFIG_MEMMAP_SPIRAM_CACHE_EVENODD
+#define PSRAM_MODE PSRAM_VADDR_MODE_EVENODD
+#else
+#define PSRAM_MODE PSRAM_VADDR_MODE_LOWHIGH
+#endif
+#endif
+
+#if CONFIG_SPIRAM_SUPPORT
+
+static const char* TAG = "spiram";
+
+#if CONFIG_SPIRAM_SPEED_40M && CONFIG_ESPTOOLPY_FLASHFREQ_40M
+#define PSRAM_SPEED PSRAM_CACHE_F40M_S40M
+#elif CONFIG_SPIRAM_SPEED_40M && CONFIG_ESPTOOLPY_FLASHFREQ_80M
+#define PSRAM_SPEED PSRAM_CACHE_F80M_S40M
+#elif CONFIG_SPIRAM_SPEED_80M && CONFIG_ESPTOOLPY_FLASHFREQ_80M
+#define PSRAM_SPEED PSRAM_CACHE_F80M_S80M
+#else
+#error "FLASH speed can only be equal to or higher than SRAM speed while SRAM is enabled!"
+#endif
+
+
+static bool spiram_inited=false;
+
+
+/*
+ Simple RAM test. Writes a word every 32 bytes. Takes about a second to complete for 4MiB. Returns
+ true when RAM seems OK, false when test fails. WARNING: Do not run this before the 2nd cpu has been
+ initialized (in a two-core system) or after the heap allocator has taken ownership of the memory.
+*/
+bool esp_spiram_test()
+{
+    volatile int *spiram=(volatile int*)SOC_EXTRAM_DATA_LOW;
+    size_t p;
+    size_t s=CONFIG_SPIRAM_SIZE;
+    int errct=0;
+    int initial_err=-1;
+    for (p=0; p<(s/sizeof(int)); p+=8) {
+        spiram[p]=p^0xAAAAAAAA;
+    }
+    for (p=0; p<(s/sizeof(int)); p+=8) {
+        if (spiram[p]!=(p^0xAAAAAAAA)) {
+            errct++;
+            if (errct==1) initial_err=p*4;
+        }
+    }
+    if (errct) {
+        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);
+        return false;
+    } else {
+        ESP_EARLY_LOGI(TAG, "SPI SRAM memory test OK");
+        return true;
+    }
+}
+
+
+
+esp_err_t esp_spiram_init()
+{
+    //Enable external RAM in MMU
+    cache_sram_mmu_set( 0, 0, SOC_EXTRAM_DATA_LOW, 0, 32, 128 );
+    //Flush and enable icache for APP CPU
+#if !CONFIG_FREERTOS_UNICORE
+    DPORT_CLEAR_PERI_REG_MASK(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MASK_DRAM1);
+    cache_sram_mmu_set( 1, 0, SOC_EXTRAM_DATA_LOW, 0, 32, 128 );
+#endif
+
+    esp_err_t r;
+    r = psram_enable(PSRAM_SPEED, PSRAM_MODE);
+    if (r != ESP_OK) {
+        ESP_EARLY_LOGE(TAG, "SPI RAM enabled but initialization failed. Bailing out.");
+        return r;
+    }
+
+    ESP_EARLY_LOGI(TAG, "SPI RAM mode: %s", PSRAM_SPEED == PSRAM_CACHE_F40M_S40M ? "flash 40m sram 40m" : \
+                                          PSRAM_SPEED == PSRAM_CACHE_F80M_S40M ? "flash 80m sram 40m" : \
+                                          PSRAM_SPEED == PSRAM_CACHE_F80M_S80M ? "flash 80m sram 80m" : "ERROR");
+    ESP_EARLY_LOGI(TAG, "PSRAM initialized, cache is in %s mode.", \
+                                          (PSRAM_MODE==PSRAM_VADDR_MODE_EVENODD)?"even/odd (2-core)": \
+                                          (PSRAM_MODE==PSRAM_VADDR_MODE_LOWHIGH)?"low/high (2-core)": \
+                                          (PSRAM_MODE==PSRAM_VADDR_MODE_NORMAL)?"normal (1-core)":"ERROR");
+    spiram_inited=true;
+    return ESP_OK;
+}
+
+
+
+size_t esp_spiram_get_size()
+{
+    return CONFIG_SPIRAM_SIZE;
+}
+
+/*
+ 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,
+ otherwise it will get lost. For now, we just read 64/128K of random PSRAM memory to do this.
+*/
+void IRAM_ATTR esp_spiram_writeback_cache() 
+{
+    int x;
+    volatile int i=0;
+    volatile uint8_t *psram=(volatile uint8_t*)SOC_EXTRAM_DATA_LOW;
+    int cache_was_disabled=0;
+
+    if (!spiram_inited) return;
+
+    //We need cache enabled for this to work. Re-enable it if needed; make sure we 
+    //disable it again on exit as well.
+    if (DPORT_REG_GET_BIT(DPORT_PRO_CACHE_CTRL_REG, DPORT_PRO_CACHE_ENABLE)==0) {
+        cache_was_disabled|=(1<<0);
+        DPORT_SET_PERI_REG_BITS(DPORT_PRO_CACHE_CTRL_REG, 1, 1, DPORT_PRO_CACHE_ENABLE_S);
+    }
+#ifndef CONFIG_FREERTOS_UNICORE
+    if (DPORT_REG_GET_BIT(DPORT_APP_CACHE_CTRL_REG, DPORT_APP_CACHE_ENABLE)==0) {
+        cache_was_disabled|=(1<<1);
+        DPORT_SET_PERI_REG_BITS(DPORT_APP_CACHE_CTRL_REG, 1, 1, DPORT_APP_CACHE_ENABLE_S);
+    }
+#endif
+
+#if CONFIG_FREERTOS_UNICORE
+    for (x=0; x<1024*64; x+=32) {
+        i+=psram[x];
+    }
+#else
+    /*
+    Note: this assumes the amount of external RAM is >2M. If it is 2M or less, what this code does is undefined. If 
+    we ever support external RAM chips of 2M or smaller, this may need adjusting.
+    */
+    for (x=0; x<1024*64; x+=32) {
+        i+=psram[x];
+        i+=psram[x+(1024*1024*2)+(1024*64)]; //address picked to also clear cache of app cpu in low/high mode
+    }
+#endif
+
+    if (cache_was_disabled&(1<<0)) {
+        while (DPORT_GET_PERI_REG_BITS2(DPORT_PRO_DCACHE_DBUG0_REG, DPORT_PRO_CACHE_STATE, DPORT_PRO_CACHE_STATE_S) != 1) ;
+        DPORT_SET_PERI_REG_BITS(DPORT_PRO_CACHE_CTRL_REG, 1, 0, DPORT_PRO_CACHE_ENABLE_S);
+    }
+#ifndef CONFIG_FREERTOS_UNICORE
+    if (cache_was_disabled&(1<<1)) {
+        while (DPORT_GET_PERI_REG_BITS2(DPORT_APP_DCACHE_DBUG0_REG, DPORT_APP_CACHE_STATE, DPORT_APP_CACHE_STATE_S) != 1);
+        DPORT_SET_PERI_REG_BITS(DPORT_APP_CACHE_CTRL_REG, 1, 0, DPORT_APP_CACHE_ENABLE_S);
+    }
+#endif
+}
+
+
+
+#endif
\ No newline at end of file
diff --git a/components/esp32/spiram.h b/components/esp32/spiram.h
deleted file mode 100644 (file)
index ebdbcaa..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#ifndef SPI_RAM_H
-#define SPI_RAM_H
-
-void enable_spi_sram();
-
-#endif
\ No newline at end of file
diff --git a/components/esp32/spiram_psram.c b/components/esp32/spiram_psram.c
new file mode 100644 (file)
index 0000000..d6653d1
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+ Driver bits for PSRAM chips (at the moment only the ESP-PSRAM32 chip).
+*/
+
+// Copyright 2013-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#include "sdkconfig.h"
+#include "string.h"
+#include "esp_attr.h"
+#include "esp_err.h"
+#include "esp_types.h"
+#include "spiram_psram.h"
+#include "rom/ets_sys.h"
+#include "rom/spi_flash.h"
+#include "rom/gpio.h"
+#include "rom/cache.h"
+#include "soc/io_mux_reg.h"
+#include "soc/dport_reg.h"
+#include "soc/gpio_sig_map.h"
+#include "driver/gpio.h"
+#include "driver/spi_common.h"
+
+#if CONFIG_SPIRAM_SUPPORT
+
+//Commands for PSRAM chip
+#define PSRAM_READ              0x03
+#define PSRAM_FAST_READ         0x0B
+#define PSRAM_FAST_READ_DUMMY   0x3
+#define PSRAM_FAST_READ_QUAD    0xEB
+#define PSRAM_WRITE             0x02
+#define PSRAM_QUAD_WRITE        0x38
+#define PSRAM_ENTER_QMODE       0x35
+#define PSRAM_EXIT_QMODE        0xF5
+#define PSRAM_RESET_EN          0x66
+#define PSRAM_RESET             0x99
+#define PSRAM_SET_BURST_LEN     0xC0
+#define PSRAM_DEVICE_ID         0x9F
+
+#if CONFIG_SPIRAM_TYPE_ESPPSRAM32
+
+#define PSRAM_MFG_ID_M          0xff
+#define PSRAM_MFG_ID_S             8
+#define PSRAM_MFG_ID_V          0x5d
+
+#endif
+
+// IO-pins for PSRAM. These need to be in the VDD_SIO power domain because all chips we
+// currently support are 1.8V parts.
+// WARNING: PSRAM shares all but the CS and CLK pins with the flash, so these defines
+// hardcode the flash pins as well, making this code incompatible with either a setup
+// that has the flash on non-standard pins or ESP32s with built-in flash.
+#define FLASH_CLK_IO      6  //Psram clock is a delayed version of this in 40MHz mode
+#define FLASH_CS_IO       11
+#define PSRAM_CLK_IO      17
+#define PSRAM_CS_IO       16
+#define PSRAM_SPIQ_IO     7
+#define PSRAM_SPID_IO     8
+#define PSRAM_SPIWP_IO    10
+#define PSRAM_SPIHD_IO    9
+
+#define PSRAM_IO_MATRIX_DUMMY_40M   1
+#define PSRAM_IO_MATRIX_DUMMY_80M   2
+
+#if CONFIG_FLASHMODE_QIO
+#define SPI_CACHE_DUMMY    SPI0_R_QIO_DUMMY_CYCLELEN   //qio 3
+#elif CONFIG_FLASHMODE_QOUT
+#define SPI_CACHE_DUMMY    SPI0_R_FAST_DUMMY_CYCLELEN  //qout 7
+#elif CONFIG_FLASHMODE_DIO
+#define SPI_CACHE_DUMMY    SPI0_R_DIO_DUMMY_CYCLELEN   //dio 3
+#elif CONFIG_FLASHMODE_DOUT
+#define SPI_CACHE_DUMMY    SPI0_R_FAST_DUMMY_CYCLELEN  //dout 7
+#endif
+
+
+typedef enum {
+    PSRAM_SPI_1  = 0x1,
+    PSRAM_SPI_2,
+    PSRAM_SPI_3,
+    PSRAM_SPI_MAX ,
+} psram_spi_num_t;
+
+static psram_cache_mode_t s_psram_mode = PSRAM_CACHE_MAX;
+
+//For now, we only use F40M + S40M, and we don't have to go through gpio matrix
+#define ENABLE_GPIO_MATRIX_SPI   1
+
+/* dummy_len_plus values defined in ROM for SPI flash configuration */
+extern uint8_t g_rom_spiflash_dummy_len_plus[];
+
+static int extra_dummy = 0;
+
+typedef enum {
+    PSRAM_CMD_QPI,
+    PSRAM_CMD_SPI,
+} psram_cmd_mode_t;
+
+typedef struct {
+    uint16_t cmd;                /*!< Command value */
+    uint16_t cmdBitLen;          /*!< Command byte length*/
+    uint32_t *addr;              /*!< Point to address value*/
+    uint16_t addrBitLen;         /*!< Address byte length*/
+    uint32_t *txData;            /*!< Point to send data buffer*/
+    uint16_t txDataBitLen;       /*!< Send data byte length.*/
+    uint32_t *rxData;            /*!< Point to recevie data buffer*/
+    uint16_t rxDataBitLen;       /*!< Recevie Data byte length.*/
+    uint32_t dummyBitLen;
+} psram_cmd_t;
+
+static void IRAM_ATTR psram_cache_init(psram_cache_mode_t psram_cache_mode, psram_vaddr_mode_t vaddrmode);
+
+static void psram_clear_spi_fifo(psram_spi_num_t spi_num)
+{
+    int i;
+    for (i = 0; i < 16; i++) {
+        WRITE_PERI_REG(SPI_W0_REG(spi_num)+i*4, 0);
+    }
+}
+
+//set basic SPI write mode
+static void psram_set_basic_write_mode(psram_spi_num_t spi_num)
+{
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_QIO);
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_DIO);
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_QUAD);
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_DUAL);
+}
+//set QPI write mode
+static void psram_set_qio_write_mode(psram_spi_num_t spi_num)
+{
+    SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_QIO);
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_DIO);
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_QUAD);
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_DUAL);
+}
+//set QPI read mode
+static void psram_set_qio_read_mode(psram_spi_num_t spi_num)
+{
+    SET_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_QIO);
+    CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_QUAD);
+    CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_DUAL);
+    CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_DIO);
+}
+//set SPI read mode
+static void psram_set_basic_read_mode(psram_spi_num_t spi_num)
+{
+    CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_QIO);
+    CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_QUAD);
+    CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_DUAL);
+    CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_DIO);
+}
+
+
+//start sending cmd/addr and optionally, receiving data
+static void IRAM_ATTR psram_cmd_recv_start(psram_spi_num_t spi_num, uint32_t* pRxData, uint16_t rxByteLen,
+        psram_cmd_mode_t cmd_mode)
+{
+    //get cs1
+    CLEAR_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS1_DIS_M);
+    SET_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS0_DIS_M);
+
+    uint32_t mode_backup = (READ_PERI_REG(SPI_USER_REG(spi_num)) >> SPI_FWRITE_DUAL_S) & 0xf;
+    uint32_t rd_mode_backup = READ_PERI_REG(SPI_CTRL_REG(spi_num)) & (SPI_FREAD_DIO_M | SPI_FREAD_DUAL_M | SPI_FREAD_QUAD_M | SPI_FREAD_QIO_M);
+    if (cmd_mode == PSRAM_CMD_SPI) {
+        psram_set_basic_write_mode(spi_num);
+        psram_set_basic_read_mode(spi_num);
+    } else if (cmd_mode == PSRAM_CMD_QPI) {
+        psram_set_qio_write_mode(spi_num);
+        psram_set_qio_read_mode(spi_num);
+    }
+
+    //Wait for SPI0 to idle
+    while ( READ_PERI_REG(SPI_EXT2_REG(0)) != 0);
+    DPORT_SET_PERI_REG_MASK(DPORT_HOST_INF_SEL_REG, 1 << 14);
+
+    // Start send data
+    SET_PERI_REG_MASK(SPI_CMD_REG(spi_num), SPI_USR);
+    while ((READ_PERI_REG(SPI_CMD_REG(spi_num)) & SPI_USR));
+    DPORT_CLEAR_PERI_REG_MASK(DPORT_HOST_INF_SEL_REG, 1 << 14);
+
+    //recover spi mode
+    SET_PERI_REG_BITS(SPI_USER_REG(spi_num), (pRxData?SPI_FWRITE_DUAL_M:0xf), mode_backup, SPI_FWRITE_DUAL_S);
+    CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), (SPI_FREAD_DIO_M|SPI_FREAD_DUAL_M|SPI_FREAD_QUAD_M|SPI_FREAD_QIO_M));
+    SET_PERI_REG_MASK(SPI_CTRL_REG(spi_num), rd_mode_backup);
+
+    //return cs to cs0
+    SET_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS1_DIS_M);
+    CLEAR_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS0_DIS_M);
+
+    if (pRxData) {
+        int idx = 0;
+        // Read data out
+        do {
+            *pRxData++ = READ_PERI_REG(SPI_W0_REG(spi_num) + (idx << 2));
+        } while (++idx < ((rxByteLen / 4) + ((rxByteLen % 4) ? 1 : 0)));
+    }
+}
+
+static uint32_t backup_usr[3];
+static uint32_t backup_usr1[3];
+static uint32_t backup_usr2[3];
+
+
+
+//setup spi command/addr/data/dummy in user mode
+static int psram_cmd_config(psram_spi_num_t spi_num, psram_cmd_t* pInData)
+{
+    while (READ_PERI_REG(SPI_CMD_REG(spi_num)) & SPI_USR);
+    backup_usr[spi_num]=READ_PERI_REG(SPI_USER_REG(spi_num));
+    backup_usr1[spi_num]=READ_PERI_REG(SPI_USER1_REG(spi_num));
+    backup_usr2[spi_num]=READ_PERI_REG(SPI_USER2_REG(spi_num));
+    // Set command by user.
+    if (pInData->cmdBitLen != 0) {
+        // Max command length 16 bits.
+        SET_PERI_REG_BITS(SPI_USER2_REG(spi_num), SPI_USR_COMMAND_BITLEN, pInData->cmdBitLen - 1,
+                SPI_USR_COMMAND_BITLEN_S);
+        // Enable command
+        SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_COMMAND);
+        // Load command,bit15-0 is cmd value.
+        SET_PERI_REG_BITS(SPI_USER2_REG(spi_num), SPI_USR_COMMAND_VALUE, pInData->cmd, SPI_USR_COMMAND_VALUE_S);
+    } else {
+        CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_COMMAND);
+        SET_PERI_REG_BITS(SPI_USER2_REG(spi_num), SPI_USR_COMMAND_BITLEN, 0, SPI_USR_COMMAND_BITLEN_S);
+    }
+    // Set Address by user.
+    if (pInData->addrBitLen != 0) {
+        SET_PERI_REG_BITS(SPI_USER1_REG(spi_num), SPI_USR_ADDR_BITLEN, (pInData->addrBitLen - 1), SPI_USR_ADDR_BITLEN_S);
+        // Enable address
+        SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_ADDR);
+        // Set address
+        WRITE_PERI_REG(SPI_ADDR_REG(spi_num), *pInData->addr);
+    } else {
+        CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_ADDR);
+        SET_PERI_REG_BITS(SPI_USER1_REG(spi_num), SPI_USR_ADDR_BITLEN, 0, SPI_USR_ADDR_BITLEN_S);
+    }
+    // Set data by user.
+    uint32_t* p_tx_val = pInData->txData;
+    if (pInData->txDataBitLen != 0) {
+        // Enable MOSI
+        SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_MOSI);
+        // Load send buffer
+        int len = (pInData->txDataBitLen + 31) / 32;
+        if (p_tx_val != NULL) {
+            memcpy((void*)SPI_W0_REG(spi_num), p_tx_val, len * 4);
+        }
+        // Set data send buffer length.Max data length 64 bytes.
+        SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(spi_num), SPI_USR_MOSI_DBITLEN, (pInData->txDataBitLen - 1),
+                SPI_USR_MOSI_DBITLEN_S);
+    } else {
+        CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_MOSI);
+        SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(spi_num), SPI_USR_MOSI_DBITLEN, 0, SPI_USR_MOSI_DBITLEN_S);
+    }
+    // Set rx data by user.
+    if (pInData->rxDataBitLen != 0) {
+        // Enable MOSI
+        SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_MISO);
+        // Set data send buffer length.Max data length 64 bytes.
+        SET_PERI_REG_BITS(SPI_MISO_DLEN_REG(spi_num), SPI_USR_MISO_DBITLEN, (pInData->rxDataBitLen - 1),
+                SPI_USR_MISO_DBITLEN_S);
+    } else {
+        CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_MISO);
+        SET_PERI_REG_BITS(SPI_MISO_DLEN_REG(spi_num), SPI_USR_MISO_DBITLEN, 0, SPI_USR_MISO_DBITLEN_S);
+    }
+    if (pInData->dummyBitLen != 0) {
+        SET_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_USR_DUMMY); // dummy en
+        SET_PERI_REG_BITS(SPI_USER1_REG(PSRAM_SPI_1), SPI_USR_DUMMY_CYCLELEN_V, pInData->dummyBitLen - 1,
+                SPI_USR_DUMMY_CYCLELEN_S);  //DUMMY
+    } else {
+        CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_USR_DUMMY); // dummy en
+        SET_PERI_REG_BITS(SPI_USER1_REG(PSRAM_SPI_1), SPI_USR_DUMMY_CYCLELEN_V, 0, SPI_USR_DUMMY_CYCLELEN_S);  //DUMMY
+    }
+    return 0;
+}
+
+void psram_cmd_end(int spi_num) {
+    while (READ_PERI_REG(SPI_CMD_REG(spi_num)) & SPI_USR);
+    WRITE_PERI_REG(SPI_USER_REG(spi_num), backup_usr[spi_num]);
+    WRITE_PERI_REG(SPI_USER1_REG(spi_num), backup_usr1[spi_num]);
+    WRITE_PERI_REG(SPI_USER2_REG(spi_num), backup_usr2[spi_num]);
+}
+
+//exit QPI mode(set back to SPI mode)
+static void psram_disable_qio_mode(psram_spi_num_t spi_num)
+{
+    psram_cmd_t ps_cmd;
+    uint32_t cmd_exit_qpi;
+    switch (s_psram_mode) {
+        case PSRAM_CACHE_F80M_S80M:
+            cmd_exit_qpi = PSRAM_EXIT_QMODE;
+            ps_cmd.txDataBitLen = 8;
+            break;
+        case PSRAM_CACHE_F80M_S40M:
+        case PSRAM_CACHE_F40M_S40M:
+        default:
+            cmd_exit_qpi = PSRAM_EXIT_QMODE << 8;
+            ps_cmd.txDataBitLen = 16;
+            break;
+    }
+    ps_cmd.txData = &cmd_exit_qpi;
+    ps_cmd.cmd = 0;
+    ps_cmd.cmdBitLen = 0;
+    ps_cmd.addr = 0;
+    ps_cmd.addrBitLen = 0;
+    ps_cmd.rxData = NULL;
+    ps_cmd.rxDataBitLen = 0;
+    ps_cmd.dummyBitLen = 0;
+    psram_cmd_config(spi_num, &ps_cmd);
+    psram_cmd_recv_start(spi_num, NULL, 0, PSRAM_CMD_QPI);
+    psram_cmd_end(spi_num);
+}
+
+//read psram id
+static void psram_read_id(uint32_t* dev_id)
+{
+    psram_spi_num_t spi_num = PSRAM_SPI_1;
+    psram_disable_qio_mode(spi_num);
+    uint32_t addr = (PSRAM_DEVICE_ID << 24) | 0;
+    uint32_t dummy_bits = 0;
+    psram_cmd_t ps_cmd;
+    switch (s_psram_mode) {
+        case PSRAM_CACHE_F80M_S80M:
+            dummy_bits = 0 + extra_dummy;
+            ps_cmd.cmdBitLen = 0;
+            break;
+        case PSRAM_CACHE_F80M_S40M:
+        case PSRAM_CACHE_F40M_S40M:
+        default:
+            dummy_bits = 0 + extra_dummy;
+            ps_cmd.cmdBitLen = 2;   //this two bits is used to delay 2 clock cycle
+            break;
+    }
+    ps_cmd.cmd = 0;
+    ps_cmd.addr = &addr;
+    ps_cmd.addrBitLen = 4 * 8;
+    ps_cmd.txDataBitLen = 0;
+    ps_cmd.txData = NULL;
+    ps_cmd.rxDataBitLen = 4 * 8;
+    ps_cmd.rxData = dev_id;
+    ps_cmd.dummyBitLen = dummy_bits;
+    psram_cmd_config(spi_num, &ps_cmd);
+    psram_clear_spi_fifo(spi_num);
+    psram_cmd_recv_start(spi_num, ps_cmd.rxData, ps_cmd.rxDataBitLen / 8, PSRAM_CMD_SPI);
+    psram_cmd_end(spi_num);
+}
+
+//enter QPI mode
+static esp_err_t IRAM_ATTR psram_enable_qio_mode(psram_spi_num_t spi_num)
+{
+    psram_cmd_t ps_cmd;
+    uint32_t addr = (PSRAM_ENTER_QMODE << 24) | 0;
+    switch (s_psram_mode) {
+        case PSRAM_CACHE_F80M_S80M:
+            ps_cmd.cmdBitLen = 0;
+            break;
+        case PSRAM_CACHE_F80M_S40M:
+        case PSRAM_CACHE_F40M_S40M:
+        default:
+            ps_cmd.cmdBitLen = 2;
+            break;
+    }
+    ps_cmd.cmd = 0;
+    ps_cmd.addr = &addr;
+    ps_cmd.addrBitLen = 8;
+    ps_cmd.txData = NULL;
+    ps_cmd.txDataBitLen = 0;
+    ps_cmd.rxData = NULL;
+    ps_cmd.rxDataBitLen = 0;
+    ps_cmd.dummyBitLen = 0;
+    psram_cmd_config(spi_num, &ps_cmd);
+    psram_cmd_recv_start(spi_num, NULL, 0, PSRAM_CMD_SPI);
+    psram_cmd_end(spi_num);
+    return ESP_OK;
+}
+
+//spi param init for psram
+void IRAM_ATTR psram_spi_init(psram_spi_num_t spi_num, psram_cache_mode_t mode)
+{
+    uint8_t i, k;
+    CLEAR_PERI_REG_MASK(SPI_SLAVE_REG(spi_num), SPI_TRANS_DONE << 5);
+    SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_CS_SETUP);
+    // SPI_CPOL & SPI_CPHA
+    CLEAR_PERI_REG_MASK(SPI_PIN_REG(spi_num), SPI_CK_IDLE_EDGE);
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_CK_OUT_EDGE);
+    // SPI bit order
+    CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_WR_BIT_ORDER);
+    CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_RD_BIT_ORDER);
+    // SPI bit order
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_DOUTDIN);
+    // May be not must to do.
+    WRITE_PERI_REG(SPI_USER1_REG(spi_num), 0);
+    // SPI mode type
+    CLEAR_PERI_REG_MASK(SPI_SLAVE_REG(spi_num), SPI_SLAVE_MODE);
+    // Set SPI speed for non-80M mode. (80M mode uses APB clock directly.)
+    if (mode!=PSRAM_CACHE_F80M_S80M) {
+        i = 1;      //Pre-divider
+        k = 2;      //Main divider. Divide by 2 so we get 40MHz
+         //clear bit 31, set SPI clock div
+        CLEAR_PERI_REG_MASK(SPI_CLOCK_REG(spi_num), SPI_CLK_EQU_SYSCLK);
+        WRITE_PERI_REG(SPI_CLOCK_REG(spi_num),
+                (((i - 1) & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) |
+                (((k - 1) & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) |
+                ((((k + 1) / 2 - 1) & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) | //50% duty cycle
+                (((k - 1) & SPI_CLKCNT_L) << SPI_CLKCNT_L_S));
+    }
+    // Enable MOSI
+    SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_MOSI);
+    memset((void*)SPI_W0_REG(spi_num), 0, 16 * 4);
+}
+
+static void IRAM_ATTR psram_gpio_config(psram_cache_mode_t mode)
+{
+    gpio_matrix_out(FLASH_CLK_IO, SPICLK_OUT_IDX, 0, 0);
+    gpio_matrix_out(FLASH_CS_IO, SPICS0_OUT_IDX, 0, 0);
+    gpio_matrix_out(PSRAM_SPIQ_IO, SPIQ_OUT_IDX, 0, 0);
+    gpio_matrix_in(PSRAM_SPIQ_IO, SPIQ_IN_IDX, 0);
+    gpio_matrix_out(PSRAM_SPID_IO, SPID_OUT_IDX, 0, 0);
+    gpio_matrix_in(PSRAM_SPID_IO, SPID_IN_IDX, 0);
+    gpio_matrix_out(PSRAM_SPIWP_IO, SPIWP_OUT_IDX, 0, 0);
+    gpio_matrix_in(PSRAM_SPIWP_IO, SPIWP_IN_IDX, 0);
+    gpio_matrix_out(PSRAM_SPIHD_IO, SPIHD_OUT_IDX, 0, 0);
+    gpio_matrix_in(PSRAM_SPIHD_IO, SPIHD_IN_IDX, 0);
+
+    switch (mode) {
+        case PSRAM_CACHE_F80M_S40M:
+            extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M;
+            g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_40M;
+            SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_80M, SPI_USR_DUMMY_CYCLELEN_S);  //DUMMY
+            break;
+        case PSRAM_CACHE_F80M_S80M:
+            extra_dummy = PSRAM_IO_MATRIX_DUMMY_80M;
+            g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_80M;
+            SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_80M, SPI_USR_DUMMY_CYCLELEN_S);  //DUMMY
+            break;
+        case PSRAM_CACHE_F40M_S40M:
+            extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M;
+            g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_40M;
+            SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_40M, SPI_USR_DUMMY_CYCLELEN_S);  //DUMMY
+            break;
+        default:
+            break;
+    }
+    SET_PERI_REG_MASK(SPI_USER_REG(0), SPI_USR_DUMMY); // dummy en
+
+    //select pin function gpio
+    PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 2);
+    PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 2);
+    PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U, 2);
+    PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, 2);
+    PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 2);
+    PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 2);
+}
+
+//psram gpio init , different working frequency we have different solutions
+esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vaddrmode)   //psram init
+{
+    /*   note: If the third mode(80Mhz+80Mhz) is enabled, VSPI port will be occupied by the system,
+         Application code should never touch VSPI hardware in this case.  We try to stop applications
+         from doing this using the drivers by claiming the port for ourselves*/
+    if (mode == PSRAM_CACHE_F80M_S80M) {
+        bool r=spicommon_periph_claim(VSPI_HOST);
+        if (!r) {
+            return ESP_ERR_INVALID_STATE;
+        }
+    }
+
+    WRITE_PERI_REG(GPIO_ENABLE_W1TC_REG, BIT(PSRAM_CLK_IO) | BIT(PSRAM_CS_IO));   //DISABLE OUPUT FOR IO16/17
+    assert(mode < PSRAM_CACHE_MAX && "we don't support any other mode for now.");
+    s_psram_mode = mode;
+
+    DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN);
+    DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST);
+    DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_1);
+    DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_1);
+    DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_2);
+    DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_2);
+
+    WRITE_PERI_REG(SPI_EXT3_REG(0), 0x1);
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_USR_PREP_HOLD_M);
+
+    switch (mode) {
+        case PSRAM_CACHE_F80M_S80M:
+            psram_spi_init(PSRAM_SPI_1, mode);
+            CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_CS_HOLD);
+            gpio_matrix_out(PSRAM_CS_IO, SPICS1_OUT_IDX, 0, 0);
+            gpio_matrix_out(PSRAM_CLK_IO, VSPICLK_OUT_IDX, 0, 0);
+            //use spi3 clock,but use spi1 data/cs wires
+            WRITE_PERI_REG(SPI_ADDR_REG(PSRAM_SPI_3), 32 << 24);
+            WRITE_PERI_REG(SPI_CLOCK_REG(PSRAM_SPI_3), SPI_CLK_EQU_SYSCLK_M);   //SET 80M AND CLEAR OTHERS
+            SET_PERI_REG_MASK(SPI_CMD_REG(PSRAM_SPI_3), SPI_FLASH_READ_M);
+            uint32_t spi_status;
+            while (1) {
+                spi_status = READ_PERI_REG(SPI_EXT2_REG(PSRAM_SPI_3));
+                if (spi_status != 0 && spi_status != 1) {
+                    DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, BIT(PSRAM_CS_IO));   //DPORT_SPI_CLK_EN
+                    break;
+                }
+            }
+            break;
+        case PSRAM_CACHE_F80M_S40M:
+        case PSRAM_CACHE_F40M_S40M:
+        default:
+            psram_spi_init(PSRAM_SPI_1, mode);
+            CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_CS_HOLD);
+            gpio_matrix_out(PSRAM_CS_IO, SPICS1_OUT_IDX, 0, 0);
+            /* We need to delay CLK to the PSRAM with respect to the clock signal as output by the SPI peripheral.
+            We do this by routing it signal to signal 224/225, which are used as a loopback; the extra run through 
+            the GPIO matrix causes the delay. We use GPIO20 (which is not in any package but has pad logic in 
+            silicon) as a temporary pad for this. So the signal path is: 
+            GPIO6(SPI CLK) --> signal224(in then out) --> internal GPIO20 --> signal225(in then out) --> GPIO17(PSRAM CLK)
+            */
+            gpio_matrix_in(FLASH_CLK_IO, SIG_IN_FUNC224_IDX, 0);
+            gpio_matrix_out(20, SIG_IN_FUNC224_IDX, 0, 0);
+            gpio_matrix_in(20, SIG_IN_FUNC225_IDX, 0);
+            gpio_matrix_out(PSRAM_CLK_IO, SIG_IN_FUNC225_IDX, 0, 0);
+            break;
+    }
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_CS_SETUP_M);
+
+#if ENABLE_GPIO_MATRIX_SPI
+    psram_gpio_config(mode);
+#endif
+    WRITE_PERI_REG(GPIO_ENABLE_W1TS_REG, BIT(PSRAM_CS_IO)| BIT(PSRAM_CLK_IO));
+    PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CS_IO], 2);
+    PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], 2);
+    uint32_t id;
+    psram_read_id(&id);
+    if (((id >> PSRAM_MFG_ID_S) & PSRAM_MFG_ID_M) != PSRAM_MFG_ID_V) {
+        return ESP_FAIL;
+    }
+    psram_enable_qio_mode(PSRAM_SPI_1);
+
+    psram_cache_init(mode, vaddrmode);
+    return ESP_OK;
+}
+
+//register initialization for sram cache params and r/w commands
+static void IRAM_ATTR psram_cache_init(psram_cache_mode_t psram_cache_mode, psram_vaddr_mode_t vaddrmode)
+{
+    CLEAR_PERI_REG_MASK(SPI_CLOCK_REG(0), SPI_CLK_EQU_SYSCLK_M);
+    SET_PERI_REG_BITS(SPI_CLOCK_REG(0), SPI_CLKDIV_PRE_V, 0, SPI_CLKDIV_PRE_S);
+    SET_PERI_REG_BITS(SPI_CLOCK_REG(0), SPI_CLKCNT_N, 1, SPI_CLKCNT_N_S);
+    SET_PERI_REG_BITS(SPI_CLOCK_REG(0), SPI_CLKCNT_H, 0, SPI_CLKCNT_H_S);
+    SET_PERI_REG_BITS(SPI_CLOCK_REG(0), SPI_CLKCNT_L, 1, SPI_CLKCNT_L_S);
+
+    switch (psram_cache_mode) {
+        case PSRAM_CACHE_F80M_S80M:
+            CLEAR_PERI_REG_MASK(SPI_DATE_REG(0), BIT(31));   //flash 1 div clk,80+40;
+            CLEAR_PERI_REG_MASK(SPI_DATE_REG(0), BIT(30)); //pre clk div , ONLY IF SPI/SRAM@ DIFFERENT SPEED,JUST FOR SPI0. FLASH DIV 2+SRAM DIV4
+            WRITE_PERI_REG(SPI_CLOCK_REG(0), SPI_CLK_EQU_SYSCLK_M);   //SET 1DIV CLOCK AND RESET OTHER PARAMS
+            SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_USR_RD_SRAM_DUMMY_M);   //enable cache read dummy
+            SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0), SPI_SRAM_DUMMY_CYCLELEN_V, PSRAM_FAST_READ_DUMMY + extra_dummy,
+                    SPI_SRAM_DUMMY_CYCLELEN_S); //dummy, psram cache :  40m--+1dummy,80m--+2dummy
+            SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_CACHE_SRAM_USR_RCMD_M); //enable user mode for cache read command
+            break;
+        case PSRAM_CACHE_F80M_S40M:
+            SET_PERI_REG_MASK(SPI_DATE_REG(0), BIT(31)); //flash 1 div clk
+            CLEAR_PERI_REG_MASK(SPI_DATE_REG(0), BIT(30)); //pre clk div , ONLY IF SPI/SRAM@ DIFFERENT SPEED,JUST FOR SPI0.
+            SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_USR_RD_SRAM_DUMMY_M); //enable cache read dummy
+            SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0), SPI_SRAM_DUMMY_CYCLELEN_V, PSRAM_FAST_READ_DUMMY + extra_dummy,
+                    SPI_SRAM_DUMMY_CYCLELEN_S); //dummy, psram cache :  40m--+1dummy,80m--+2dummy
+            SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_CACHE_SRAM_USR_RCMD_M); //enable user mode for cache read command
+            break;
+        case PSRAM_CACHE_F40M_S40M:
+        default:
+            CLEAR_PERI_REG_MASK(SPI_DATE_REG(0), BIT(31)); //flash 1 div clk
+            CLEAR_PERI_REG_MASK(SPI_DATE_REG(0), BIT(30)); //pre clk div
+            SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_USR_RD_SRAM_DUMMY_M); //enable cache read dummy
+            SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0), SPI_SRAM_DUMMY_CYCLELEN_V, PSRAM_FAST_READ_DUMMY + extra_dummy,
+                    SPI_SRAM_DUMMY_CYCLELEN_S); //dummy, psram cache :  40m--+1dummy,80m--+2dummy
+            SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_CACHE_SRAM_USR_RCMD_M); //enable user mode for cache read command
+            break;
+    }
+    SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_CACHE_SRAM_USR_WCMD_M);     // cache write command enable
+    SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0), SPI_SRAM_ADDR_BITLEN_V, 23, SPI_SRAM_ADDR_BITLEN_S); //write address for cache command.
+    SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_USR_SRAM_QIO_M);     //enable qio mode for cache command
+    CLEAR_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_USR_SRAM_DIO_M);     //disable dio mode for cache command
+
+
+    //config sram cache r/w command
+    switch (psram_cache_mode) {
+        case PSRAM_CACHE_F80M_S80M: //in this mode , no delay is needed
+            SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_BITLEN, 7,
+                    SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S);
+            SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_VALUE, PSRAM_QUAD_WRITE,
+                    SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S); //0x38
+            SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V, 7,
+                    SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S);
+            SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V, PSRAM_FAST_READ,
+                    SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S); //0x0b
+            break;
+        case PSRAM_CACHE_F80M_S40M: //is sram is @40M, need 2 cycles of delay
+        case PSRAM_CACHE_F40M_S40M:
+        default:
+            SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V, 15,
+                    SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S); //read command length, 2 bytes(1byte for delay),sending in qio mode in cache
+            SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V, ((PSRAM_FAST_READ) << 8),
+                    SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S); //0x0b, read command value,(0x00 for delay,0x0b for cmd)
+            SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_BITLEN, 15,
+                    SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S); //write command length,2 bytes(1byte for delay,send in qio mode in cache)
+            SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_VALUE, ((PSRAM_QUAD_WRITE) << 8),
+                    SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S); //0x38, write command value,(0x00 for delay)
+            break;
+    }
+
+    DPORT_CLEAR_PERI_REG_MASK(DPORT_PRO_CACHE_CTRL_REG, DPORT_PRO_DRAM_HL|DPORT_PRO_DRAM_SPLIT);
+    DPORT_CLEAR_PERI_REG_MASK(DPORT_APP_CACHE_CTRL_REG, DPORT_APP_DRAM_HL|DPORT_APP_DRAM_SPLIT);
+    if (vaddrmode == PSRAM_VADDR_MODE_LOWHIGH) {
+        DPORT_SET_PERI_REG_MASK(DPORT_PRO_CACHE_CTRL_REG, DPORT_PRO_DRAM_HL);
+        DPORT_SET_PERI_REG_MASK(DPORT_APP_CACHE_CTRL_REG, DPORT_APP_DRAM_HL);
+    } else if (vaddrmode == PSRAM_VADDR_MODE_EVENODD) {
+        DPORT_SET_PERI_REG_MASK(DPORT_PRO_CACHE_CTRL_REG, DPORT_PRO_DRAM_SPLIT);
+        DPORT_SET_PERI_REG_MASK(DPORT_APP_CACHE_CTRL_REG, DPORT_APP_DRAM_SPLIT);
+    }
+
+    DPORT_CLEAR_PERI_REG_MASK(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MASK_DRAM1|DPORT_PRO_CACHE_MASK_OPSDRAM); //use Dram1 to visit ext sram.
+    //cache page mode : 1 -->16k  4 -->2k  0-->32k,(accord with the settings in cache_sram_mmu_set)
+    DPORT_SET_PERI_REG_BITS(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CMMU_SRAM_PAGE_MODE, 0, DPORT_PRO_CMMU_SRAM_PAGE_MODE_S);
+    DPORT_CLEAR_PERI_REG_MASK(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MASK_DRAM1|DPORT_APP_CACHE_MASK_OPSDRAM); //use Dram1 to visit ext sram.
+    //cache page mode : 1 -->16k  4 -->2k  0-->32k,(accord with the settings in cache_sram_mmu_set)
+    DPORT_SET_PERI_REG_BITS(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CMMU_SRAM_PAGE_MODE, 0, DPORT_APP_CMMU_SRAM_PAGE_MODE_S);
+
+    CLEAR_PERI_REG_MASK(SPI_PIN_REG(0), SPI_CS1_DIS_M); //ENABLE SPI0 CS1 TO PSRAM(CS0--FLASH; CS1--SRAM)
+
+}
+
+#endif // CONFIG_SPIRAM_SUPPORT
diff --git a/components/esp32/spiram_psram.h b/components/esp32/spiram_psram.h
new file mode 100644 (file)
index 0000000..10de4e1
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#ifndef _PSRAM_H
+#define _PSRAM_H
+#include "soc/spi_reg.h"
+#include "esp_err.h"
+#include "sdkconfig.h"
+
+typedef enum {
+    PSRAM_CACHE_F80M_S40M = 0,
+    PSRAM_CACHE_F40M_S40M,
+    PSRAM_CACHE_F80M_S80M,
+    PSRAM_CACHE_MAX,
+} psram_cache_mode_t;
+
+
+/*
+See the TRM, chapter PID/MPU/MMU, header 'External RAM' for the definitions of these modes.
+
+Important is that NORMAL works with the app CPU cache disabled, but gives huge cache coherency
+issues when both app and pro CPU are enabled. LOWHIGH and EVENODD do not have these coherency
+issues but cannot be used when the app CPU cache is disabled.
+*/
+typedef enum {
+    PSRAM_VADDR_MODE_NORMAL=0, ///< App and pro CPU use their own flash cache for external RAM access
+    PSRAM_VADDR_MODE_LOWHIGH,  ///< App and pro CPU share external RAM caches: pro CPU has low 2M, app CPU has high 2M
+    PSRAM_VADDR_MODE_EVENODD,  ///< App and pro CPU share external RAM caches: pro CPU does even 32yte ranges, app does odd ones.
+} psram_vaddr_mode_t;
+
+/**
+ * @brief psram cache enable function
+ *
+ * Esp-idf uses this to initialize cache for psram, mapping it into the main memory
+ * address space.
+ *
+ * @param mode       SPI mode to access psram in
+ * @param vaddrmode  Mode the psram cache works in.
+ * @return ESP_OK on success, ESP_ERR_INVALID_STATE when VSPI peripheral is needed but cannot be claimed.
+ */
+esp_err_t psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vaddrmode);
+
+#endif
diff --git a/components/esp32/test/test_spiram_cache_flush.c b/components/esp32/test/test_spiram_cache_flush.c
new file mode 100644 (file)
index 0000000..8fdf8f5
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+This code tests the interaction between PSRAM and SPI flash routines.
+*/
+
+#include <esp_types.h>
+#include <stdio.h>
+#include "rom/ets_sys.h"
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/semphr.h"
+#include "freertos/queue.h"
+#include "freertos/xtensa_api.h"
+#include "unity.h"
+#include "soc/dport_reg.h"
+#include "soc/io_mux_reg.h"
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "rom/ets_sys.h"
+#include "esp_heap_caps.h"
+#include "esp_spi_flash.h"
+#include "esp_partition.h"
+#include "test_utils.h"
+
+
+#if CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MEMMAP
+
+#define TSTSZ (16*1024)
+
+
+volatile static int res[2], err[2];
+
+void tstMem(void *arg) {
+    volatile unsigned char *mem=(volatile unsigned char*)arg;
+    int p=0;
+    while(1) {
+        for (int i=0; i<TSTSZ; i++) {
+            mem[i]=(i^p);
+        }
+        for (int i=0; i<TSTSZ; i++) {
+            if (mem[i]!=((i^p)&0xff)) {
+                printf("Core %d mem err! Got %x espected %x at addr %p\n", xPortGetCoreID(), mem[i], (i^p)&0xff, &mem[i]);
+                err[xPortGetCoreID()]++;
+            }
+        }
+        p++;
+        res[xPortGetCoreID()]++;
+    }
+}
+
+
+TEST_CASE("Spiram cache flush on mmap", "[spiram][ignore]")
+{
+    void *mem[2];
+    res[0]=0; res[1]=0;
+#if CONFIG_SPIRAM_USE_CAPS_ALLOC
+    mem[0]=pvPortMallocCaps(TSTSZ, MALLOC_CAP_SPIRAM);
+    mem[1]=pvPortMallocCaps(TSTSZ, MALLOC_CAP_SPIRAM);
+#else
+    mem[0]=(void*)0x3f800000;
+    mem[1]=(void*)0x3f800000+TSTSZ;
+#endif
+    TaskHandle_t th[2];
+    err[0]=0; err[1]=0;
+    printf("Creating tasks\n");
+    xTaskCreatePinnedToCore(tstMem  , "tskone"  , 2048, mem[0], 3, &th[0], 0);
+    xTaskCreatePinnedToCore(tstMem  , "tsktwo"  , 2048, mem[1], 3, &th[1], 1);
+
+    const esp_partition_t* part = get_test_data_partition();
+    for (int l=0; l<10; l++) {
+        for (int p=0; p<4096*1024; p+=65536) {
+            const void *out;
+            spi_flash_mmap_handle_t h;
+            spi_flash_mmap(p, 65536, SPI_FLASH_MMAP_DATA, &out, &h);
+            spi_flash_munmap(h);
+        }
+        printf("%d/10\n", l);
+    }
+
+    printf("Checked memory %d and %d times. Errors: %d and %d\n", res[0], res[1], err[0], err[1]);
+
+    vTaskDelete(th[0]);
+    vTaskDelete(th[1]);
+#if CONFIG_SPIRAM_USE_CAPS_ALLOC
+    free(mem[0]);
+    free(mem[1]);
+#endif
+    TEST_ASSERT(err[0]==0);
+    TEST_ASSERT(err[1]==0);
+}
+
+
+#define CYCLES 1024
+
+TEST_CASE("Spiram cache flush on write/read", "[spiram][ignore]")
+{
+    void *mem[2];
+    res[0]=0; res[1]=0;
+#if CONFIG_SPIRAM_USE_CAPS_ALLOC
+    mem[0]=pvPortMallocCaps(TSTSZ, MALLOC_CAP_SPIRAM);
+    mem[1]=pvPortMallocCaps(TSTSZ, MALLOC_CAP_SPIRAM);
+#else
+    mem[0]=(void*)0x3f800000;
+    mem[1]=(void*)0x3f800000+TSTSZ;
+#endif
+    TaskHandle_t th[2];
+    const esp_partition_t* part = get_test_data_partition();
+    assert(part!=NULL);
+
+    printf("Erasing sector...\n");
+    esp_partition_erase_range(part, 0, 64*1024);
+    printf("Erased.\n");
+    printf("Creating tasks\n");
+    xTaskCreatePinnedToCore(tstMem  , "tskone"  , 2048, mem[0], 3, &th[0], 0);
+    xTaskCreatePinnedToCore(tstMem  , "tsktwo"  , 2048, mem[1], 3, &th[1], 1);
+    char buf[512];
+
+    const void *out;
+    spi_flash_mmap_handle_t handle;
+    esp_partition_mmap(part, 0, 512, SPI_FLASH_MMAP_DATA, &out, &handle);
+    for (int i=0; i<CYCLES; i++) {
+        printf("%d/%d\n", i, CYCLES);
+        esp_partition_write(part, 0, buf, 512);
+        esp_partition_read(part, 0, buf, 512);
+        vTaskDelay(1);
+    }
+    spi_flash_munmap(handle);
+
+    printf("Checked memory %d and %d times.\n", res[0], res[1]);
+
+    vTaskDelete(th[0]);
+    vTaskDelete(th[1]);
+#if CONFIG_SPIRAM_USE_CAPS_ALLOC
+    free(mem[0]);
+    free(mem[1]);
+#endif
+}
+
+#endif  //CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MEMMAP
index 2f46f1dd387a0917d304936934f93197fca21223..0bf6275bf2ff0d77093c1c98ddb8f599a3b443bf 100644 (file)
@@ -1,5 +1,12 @@
 
-ifdef CONFIG_NEWLIB_NANO_FORMAT
+
+ifeq ("$(CONFIG_SPIRAM_CACHE_WORKAROUND)","y")
+LIBC_PATH := $(COMPONENT_PATH)/lib/libc-psram-workaround.a
+LIBM_PATH := $(COMPONENT_PATH)/lib/libm-psram-workaround.a
+
+else
+
+ifeq ("$(CONFIG_NEWLIB_NANO_FORMAT)","y")
 LIBC_PATH := $(COMPONENT_PATH)/lib/libc_nano.a
 else
 LIBC_PATH := $(COMPONENT_PATH)/lib/libc.a
@@ -7,7 +14,9 @@ endif
 
 LIBM_PATH := $(COMPONENT_PATH)/lib/libm.a
 
-COMPONENT_ADD_LDFLAGS += $(LIBC_PATH) $(LIBM_PATH)
+endif
+
+COMPONENT_ADD_LDFLAGS := $(LIBC_PATH) $(LIBM_PATH) -lnewlib
 
 COMPONENT_ADD_LINKER_DEPS := $(LIBC_PATH) $(LIBM_PATH)
 
diff --git a/components/newlib/lib/libc-psram-workaround.a b/components/newlib/lib/libc-psram-workaround.a
new file mode 100644 (file)
index 0000000..cddebee
Binary files /dev/null and b/components/newlib/lib/libc-psram-workaround.a differ
diff --git a/components/newlib/lib/libg-psram-workaround.a b/components/newlib/lib/libg-psram-workaround.a
new file mode 100644 (file)
index 0000000..cddebee
Binary files /dev/null and b/components/newlib/lib/libg-psram-workaround.a differ
diff --git a/components/newlib/lib/libm-psram-workaround.a b/components/newlib/lib/libm-psram-workaround.a
new file mode 100644 (file)
index 0000000..74a8765
Binary files /dev/null and b/components/newlib/lib/libm-psram-workaround.a differ
old mode 100755 (executable)
new mode 100644 (file)
index 784f8f1089d8ba3d76d41bb815f889d7debc8412..5d5cf3b7db4645e52b7a23fee006ecedbc20ae79 100644 (file)
@@ -67,6 +67,9 @@
 #define SOC_RTC_IRAM_HIGH 0x400C2000
 #define SOC_RTC_DATA_LOW  0x50000000
 #define SOC_RTC_DATA_HIGH 0x50002000
+#define SOC_EXTRAM_DATA_LOW 0x3F800000
+#define SOC_EXTRAM_DATA_HIGH 0x3FC00000
+
 
 #define DR_REG_DPORT_BASE                       0x3ff00000
 #define DR_REG_AES_BASE                         0x3ff01000
 #define DR_REG_UART2_BASE                       0x3ff6E000
 #define DR_REG_PWM2_BASE                        0x3ff6F000
 #define DR_REG_PWM3_BASE                        0x3ff70000
-#define PERIPHS_SPI_ENCRYPT_BASEADDR           DR_REG_SPI_ENCRYPT_BASE
+#define PERIPHS_SPI_ENCRYPT_BASEADDR            DR_REG_SPI_ENCRYPT_BASE
 
 //Registers Operation {{
 #define ETS_UNCACHED_ADDR(addr) (addr)
index 2db0c2437f9de1434ad183352e1f35d734bd1274..8b98e2cb108f6463f18461167b0d433466eabf80 100644 (file)
@@ -31,6 +31,7 @@
 #include "esp_flash_encrypt.h"
 #include "esp_log.h"
 #include "cache_utils.h"
+#include "esp_spiram.h"
 
 #ifndef NDEBUG
 // Enable built-in checks in queue.h in debug builds
@@ -225,6 +226,9 @@ esp_err_t IRAM_ATTR spi_flash_mmap_pages(int *pages, size_t page_count, spi_flas
        entire cache.
     */
     if (!did_flush && need_flush) {
+#if CONFIG_SPIRAM_SUPPORT
+        esp_spiram_writeback_cache();
+#endif
         Cache_Flush(0);
         Cache_Flush(1);
     }
@@ -330,6 +334,9 @@ static inline IRAM_ATTR bool update_written_pages(size_t start_addr, size_t leng
                tricky because mmaped memory can be used on un-pinned
                cores, or the pointer passed between CPUs.
             */
+#if CONFIG_SPIRAM_SUPPORT
+            esp_spiram_writeback_cache();
+#endif
             Cache_Flush(0);
 #ifndef CONFIG_FREERTOS_UNICORE
             Cache_Flush(1);
index 17202ba96094d9c04cf49b53926441f268730076..25697d0bcf9b06a46781bc36c79b06a7bceef2db 100644 (file)
@@ -118,6 +118,7 @@ CONFIG_BT_RESERVE_DRAM=0
 CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
 CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
 CONFIG_MEMMAP_SMP=y
+# CONFIG_SPIRAM_SUPPORT is not set
 # CONFIG_MEMMAP_TRACEMEM is not set
 # CONFIG_MEMMAP_TRACEMEM_TWOBANKS is not set
 # CONFIG_ESP32_TRAX is not set