From b146104885e20c0e47220a6a378209fc024e5297 Mon Sep 17 00:00:00 2001 From: suda-morris <362953310@qq.com> Date: Fri, 10 May 2019 11:34:06 +0800 Subject: [PATCH] add esp32s2beta component --- components/esp32/CMakeLists.txt | 2 +- components/esp32s2beta/CMakeLists.txt | 81 + components/esp32s2beta/Kconfig | 1045 ++++++++++++ components/esp32s2beta/Makefile.projbuild | 44 + components/esp32s2beta/brownout.c | 56 + components/esp32s2beta/cache_err_int.c | 69 + components/esp32s2beta/clk.c | 307 ++++ components/esp32s2beta/component.mk | 4 + components/esp32s2beta/cpu_start.c | 523 ++++++ components/esp32s2beta/crosscore_int.c | 119 ++ components/esp32s2beta/dport_access.c | 312 ++++ .../esp32s2beta/dport_panic_highint_hdl.S | 203 +++ components/esp32s2beta/esp_clk_internal.h | 44 + .../esp32s2beta/esp_timer_esp32s2beta.c | 407 +++++ components/esp32s2beta/gdbstub.c | 356 +++++ components/esp32s2beta/hw_random.c | 70 + .../include/esp32s2beta/brownout.h | 21 + .../include/esp32s2beta/cache_err_int.h | 33 + .../esp32s2beta/include/esp32s2beta/clk.h | 75 + .../include/esp32s2beta/dport_access.h | 52 + .../esp32s2beta/include/esp32s2beta/pm.h | 42 + .../esp32s2beta/include/esp32s2beta/spiram.h | 90 ++ components/esp32s2beta/include/esp_attr.h | 58 + components/esp32s2beta/include/esp_clk.h | 75 + components/esp32s2beta/include/esp_intr.h | 89 ++ .../esp32s2beta/include/esp_intr_alloc.h | 295 ++++ components/esp32s2beta/include/esp_sleep.h | 325 ++++ components/esp32s2beta/include/esp_spiram.h | 90 ++ components/esp32s2beta/include/esp_ssc.h | 119 ++ components/esp32s2beta/int_wdt.c | 106 ++ components/esp32s2beta/intr_alloc.c | 900 +++++++++++ .../esp32s2beta/ld/esp32s2beta.common.ld | 253 +++ components/esp32s2beta/ld/esp32s2beta.ld | 74 + .../esp32s2beta/ld/esp32s2beta.peripherals.ld | 29 + components/esp32s2beta/linker.lf | 14 + components/esp32s2beta/panic.c | 672 ++++++++ components/esp32s2beta/pm_esp32s2beta.c | 573 +++++++ components/esp32s2beta/pm_trace.c | 52 + components/esp32s2beta/sleep_modes.c | 650 ++++++++ components/esp32s2beta/spiram.c | 367 +++++ components/esp32s2beta/spiram_psram.c | 903 +++++++++++ components/esp32s2beta/spiram_psram.h | 80 + components/esp32s2beta/system_api.c | 369 +++++ components/esp32s2beta/task_wdt.c | 414 +++++ components/esp_wifi/src/phy_init.c | 26 +- components/mbedtls/port/esp32s2beta/aes.c | 381 +++++ components/mbedtls/port/esp32s2beta/sha.c | 184 +++ .../mbedtls/port/include/esp32s2beta/aes.h | 269 ++++ .../mbedtls/port/include/esp32s2beta/sha.h | 207 +++ .../include/xtensa/config/core-isa.h | 712 +++++++++ .../include/xtensa/config/core-matmap.h | 322 ++++ .../esp32s2beta/include/xtensa/config/core.h | 1408 +++++++++++++++++ .../esp32s2beta/include/xtensa/config/defs.h | 37 + .../include/xtensa/config/specreg.h | 103 ++ .../include/xtensa/config/system.h | 277 ++++ .../include/xtensa/config/tie-asm.h | 130 ++ .../esp32s2beta/include/xtensa/config/tie.h | 130 ++ components/xtensa/esp32s2beta/libhal.a | Bin 0 -> 517178 bytes 58 files changed, 14636 insertions(+), 12 deletions(-) create mode 100644 components/esp32s2beta/CMakeLists.txt create mode 100644 components/esp32s2beta/Kconfig create mode 100644 components/esp32s2beta/Makefile.projbuild create mode 100644 components/esp32s2beta/brownout.c create mode 100644 components/esp32s2beta/cache_err_int.c create mode 100644 components/esp32s2beta/clk.c create mode 100644 components/esp32s2beta/component.mk create mode 100644 components/esp32s2beta/cpu_start.c create mode 100644 components/esp32s2beta/crosscore_int.c create mode 100644 components/esp32s2beta/dport_access.c create mode 100644 components/esp32s2beta/dport_panic_highint_hdl.S create mode 100644 components/esp32s2beta/esp_clk_internal.h create mode 100644 components/esp32s2beta/esp_timer_esp32s2beta.c create mode 100644 components/esp32s2beta/gdbstub.c create mode 100644 components/esp32s2beta/hw_random.c create mode 100644 components/esp32s2beta/include/esp32s2beta/brownout.h create mode 100644 components/esp32s2beta/include/esp32s2beta/cache_err_int.h create mode 100644 components/esp32s2beta/include/esp32s2beta/clk.h create mode 100644 components/esp32s2beta/include/esp32s2beta/dport_access.h create mode 100644 components/esp32s2beta/include/esp32s2beta/pm.h create mode 100644 components/esp32s2beta/include/esp32s2beta/spiram.h create mode 100644 components/esp32s2beta/include/esp_attr.h create mode 100644 components/esp32s2beta/include/esp_clk.h create mode 100644 components/esp32s2beta/include/esp_intr.h create mode 100644 components/esp32s2beta/include/esp_intr_alloc.h create mode 100644 components/esp32s2beta/include/esp_sleep.h create mode 100644 components/esp32s2beta/include/esp_spiram.h create mode 100644 components/esp32s2beta/include/esp_ssc.h create mode 100644 components/esp32s2beta/int_wdt.c create mode 100644 components/esp32s2beta/intr_alloc.c create mode 100644 components/esp32s2beta/ld/esp32s2beta.common.ld create mode 100644 components/esp32s2beta/ld/esp32s2beta.ld create mode 100644 components/esp32s2beta/ld/esp32s2beta.peripherals.ld create mode 100644 components/esp32s2beta/linker.lf create mode 100644 components/esp32s2beta/panic.c create mode 100644 components/esp32s2beta/pm_esp32s2beta.c create mode 100644 components/esp32s2beta/pm_trace.c create mode 100644 components/esp32s2beta/sleep_modes.c create mode 100644 components/esp32s2beta/spiram.c create mode 100644 components/esp32s2beta/spiram_psram.c create mode 100644 components/esp32s2beta/spiram_psram.h create mode 100644 components/esp32s2beta/system_api.c create mode 100644 components/esp32s2beta/task_wdt.c create mode 100644 components/mbedtls/port/esp32s2beta/aes.c create mode 100644 components/mbedtls/port/esp32s2beta/sha.c create mode 100644 components/mbedtls/port/include/esp32s2beta/aes.h create mode 100644 components/mbedtls/port/include/esp32s2beta/sha.h create mode 100644 components/xtensa/esp32s2beta/include/xtensa/config/core-isa.h create mode 100644 components/xtensa/esp32s2beta/include/xtensa/config/core-matmap.h create mode 100644 components/xtensa/esp32s2beta/include/xtensa/config/core.h create mode 100644 components/xtensa/esp32s2beta/include/xtensa/config/defs.h create mode 100644 components/xtensa/esp32s2beta/include/xtensa/config/specreg.h create mode 100644 components/xtensa/esp32s2beta/include/xtensa/config/system.h create mode 100644 components/xtensa/esp32s2beta/include/xtensa/config/tie-asm.h create mode 100644 components/xtensa/esp32s2beta/include/xtensa/config/tie.h create mode 100644 components/xtensa/esp32s2beta/libhal.a diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index 3710237896..3620f43798 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -76,7 +76,7 @@ else() add_custom_command( OUTPUT esp32_out.ld COMMAND "${CMAKE_C_COMPILER}" -C -P -x c -E -o esp32_out.ld -I ${config_dir} ${LD_DIR}/esp32.ld - MAIN_DEPENDENCY ${LD_DIR}/esp32.ld ${SDKCONFIG_H} + MAIN_DEPENDENCY ${LD_DIR}/esp32.ld ${sdkconfig_header} COMMENT "Generating linker script..." VERBATIM) diff --git a/components/esp32s2beta/CMakeLists.txt b/components/esp32s2beta/CMakeLists.txt new file mode 100644 index 0000000000..d4eed8a23b --- /dev/null +++ b/components/esp32s2beta/CMakeLists.txt @@ -0,0 +1,81 @@ +if(BOOTLOADER_BUILD AND CONFIG_IDF_TARGET_ESP32S2BETA) + # For bootloader, all we need from esp32s2beta is headers + set(COMPONENT_ADD_INCLUDEDIRS include) + set(COMPONENT_REQUIRES ${IDF_COMPONENTS} soc) #unfortunately rom/uart uses SOC registers directly + set(COMPONENT_SRCS ) + register_component() +elseif(CONFIG_IDF_TARGET_ESP32S2BETA) + # Regular app build + + set(COMPONENT_SRCS "brownout.c" + "cache_err_int.c" + "clk.c" + "cpu_start.c" + "crosscore_int.c" + "dport_access.c" + "dport_panic_highint_hdl.S" + "esp_timer_esp32s2beta.c" + "gdbstub.c" + "hw_random.c" + "int_wdt.c" + "intr_alloc.c" + "panic.c" + "pm_esp32s2beta.c" + "pm_trace.c" + "sleep_modes.c" + "spiram.c" + "spiram_psram.c" + "system_api.c" + "task_wdt.c") + set(COMPONENT_ADD_INCLUDEDIRS "include") + + set(COMPONENT_REQUIRES driver esp_event efuse soc) #unfortunately rom/uart uses SOC registers directly + + # driver is a public requirement because esp_sleep.h uses gpio_num_t & touch_pad_t + # app_update is added here because cpu_start.c uses esp_ota_get_app_description() function. + set(COMPONENT_PRIV_REQUIRES + app_trace app_update bootloader_support log mbedtls nvs_flash + pthread smartconfig_ack spi_flash vfs wpa_supplicant espcoredump esp_common esp_wifi) + + set(COMPONENT_ADD_LDFRAGMENTS linker.lf ld/esp32s2beta_fragments.lf) + + register_component() + + target_linker_script(${COMPONENT_LIB} "${CMAKE_CURRENT_BINARY_DIR}/esp32s2beta_out.ld") + + # Process the template file through the linker script generation mechanism, and use the output for linking the + # final binary + target_linker_script(${COMPONENT_LIB} "${CMAKE_CURRENT_LIST_DIR}/ld/esp32s2beta.project.ld.in" PROCESS) + + target_linker_script(${COMPONENT_LIB} "ld/esp32s2beta.peripherals.ld") + + target_link_libraries(${COMPONENT_LIB} gcc) + target_link_libraries(${COMPONENT_LIB} "-u call_user_start_cpu0") + + #ld_include_panic_highint_hdl is added as an undefined symbol because otherwise the + #linker will ignore panic_highint_hdl.S as it has no other files depending on any + #symbols in it. + target_link_libraries(${COMPONENT_LIB} "-u ld_include_panic_highint_hdl") + + idf_build_get_property(sdkconfig_header SDKCONFIG_HEADER) + get_filename_component(config_dir ${sdkconfig_header} DIRECTORY) + # Preprocess esp32s2beta.ld linker script to include configuration, becomes esp32s2beta_out.ld + set(LD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ld) + add_custom_command( + OUTPUT esp32s2beta_out.ld + COMMAND "${CMAKE_C_COMPILER}" -C -P -x c -E -o esp32s2beta_out.ld -I ${config_dir} ${LD_DIR}/esp32s2beta.ld + MAIN_DEPENDENCY ${LD_DIR}/esp32s2beta.ld ${sdkconfig_header} + COMMENT "Generating linker script..." + VERBATIM) + + add_custom_target(esp32s2beta_linker_script DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/esp32s2beta_out.ld) + add_dependencies(${COMPONENT_LIB} esp32s2beta_linker_script) + + # disable stack protection in files which are involved in initialization of that feature + set_source_files_properties( + cpu_start.c + PROPERTIES COMPILE_FLAGS + -fno-stack-protector) +else() + register_config_only_component() +endif() diff --git a/components/esp32s2beta/Kconfig b/components/esp32s2beta/Kconfig new file mode 100644 index 0000000000..a55e23fd5c --- /dev/null +++ b/components/esp32s2beta/Kconfig @@ -0,0 +1,1045 @@ +menu "ESP32S2-specific" + + choice ESP32_DEFAULT_CPU_FREQ_MHZ + prompt "CPU frequency" + default ESP32_DEFAULT_CPU_FREQ_160 + help + CPU frequency to be set on application startup. + + config ESP32_DEFAULT_CPU_FREQ_80 + bool "80 MHz" + config ESP32_DEFAULT_CPU_FREQ_160 + bool "160 MHz" + config ESP32_DEFAULT_CPU_FREQ_240 + bool "240 MHz" + endchoice + + config ESP32_DEFAULT_CPU_FREQ_MHZ + int + default 80 if ESP32_DEFAULT_CPU_FREQ_80 + default 160 if ESP32_DEFAULT_CPU_FREQ_160 + default 240 if ESP32_DEFAULT_CPU_FREQ_240 + + menu "Cache config" + + choice INSTRUCTION_CACHE_SIZE + prompt "Instruction cache size" + default INSTRUCTION_CACHE_8KB + help + Instruction cache size to be set on application startup. + If you use 8KB instruction cache rather than 16KB instruction cache, the other 8KB will be added to the heap. + + config INSTRUCTION_CACHE_8KB + bool "8KB" + config INSTRUCTION_CACHE_16KB + bool "16KB" + endchoice + + choice INSTRUCTION_CACHE_ASSOCIATED_WAYS + prompt "Instruction cache associated ways" + default INSTRUCTION_CACHE_8WAYS + help + Instruction cache associated ways to be set on application startup. + + config INSTRUCTION_CACHE_4WAYS + bool "4 ways" + config INSTRUCTION_CACHE_8WAYS + bool "8 ways" + endchoice + + choice INSTRUCTION_CACHE_LINE_SIZE + prompt "Instruction cache line size" + default INSTRUCTION_CACHE_LINE_32B + help + Instruction cache line size to be set on application startup. + + config INSTRUCTION_CACHE_LINE_16B + bool "16 Bytes" + config INSTRUCTION_CACHE_LINE_32B + bool "32 Bytes" + config INSTRUCTION_CACHE_LINE_64B + bool "64 Bytes" + endchoice + + choice DATA_CACHE_SIZE + prompt "Data cache size" + default DATA_CACHE_8KB + help + Data cache size to be set on application startup. + If you use 8KB data cache rather than 16KB data cache, the other 8KB will be added to the heap. + + config DATA_CACHE_0KB + depends on !SPIRAM_SUPPORT + bool "0KB" + config DATA_CACHE_8KB + bool "8KB" + config DATA_CACHE_16KB + bool "16KB" + endchoice + + choice DATA_CACHE_ASSOCIATED_WAYS + prompt "Data cache associated ways" + default DATA_CACHE_8WAYS + help + Data cache associated ways to be set on application startup. + + config DATA_CACHE_4WAYS + bool "4 ways" + config DATA_CACHE_8WAYS + bool "8 ways" + endchoice + + choice DATA_CACHE_LINE_SIZE + prompt "Data cache line size" + default DATA_CACHE_LINE_32B + help + Data cache line size to be set on application startup. + + config DATA_CACHE_LINE_16B + bool "16 Bytes" + config DATA_CACHE_LINE_32B + bool "32 Bytes" + config DATA_CACHE_LINE_64B + bool "64 Bytes" + endchoice + + config RODATA_USE_DATA_CACHE + depends on DATA_CACHE_8KB || DATA_CACHE_16KB + bool "Use data cache rather than instruction cache to access read only data" + default "n" + help + If enabled, CPU will access rodata through data cache, which will reduce the overload + of instruction cache, however will increase the overload of data cache. + + config ENABLE_INSTRUCTION_CACHE_WRAP + bool "Enable instruction cache wrap" + default "n" + help + If enabled, instruction cache will use wrap mode to read spi flash (maybe spiram). + The wrap length equals to INSTRUCTION_CACHE_LINE_SIZE. + However, it depends on complex conditions. + + config ENABLE_DATA_CACHE_WRAP + bool "Enable data cache wrap" + default "n" + help + If enabled, data cache will use wrap mode to read spiram (maybe spi flash). + The wrap length equals to DATA_CACHE_LINE_SIZE. + However, it depends on complex conditions. + + endmenu + + 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. + + config SPIRAM_IGNORE_NOTFOUND + bool "Ignore PSRAM when not found" + default "n" + depends on SPIRAM_BOOT_INIT + help + Normally, if psram initialization is enabled during compile time but not found at runtime, it + is seen as an error making the ESP32 panic. If this is enabled, the ESP32 will keep on + running but will not add the (non-existing) RAM to any allocator. + + choice SPIRAM_USE + prompt "SPI RAM access method" + default SPIRAM_USE_MALLOC + 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_SPIRAM)" + config SPIRAM_USE_MALLOC + bool "Make RAM allocatable using malloc() as well" + select SUPPORT_STATIC_ALLOCATION + endchoice + + choice SPIRAM_TYPE + prompt "Type of SPI RAM chip in use" + default SPIRAM_TYPE_AUTO + + config SPIRAM_TYPE_AUTO + bool "Auto-detect" + + config SPIRAM_TYPE_ESPPSRAM32 + bool "ESP-PSRAM32 or IS25WP032" + + config SPIRAM_TYPE_ESPPSRAM64 + bool "ESP-PSRAM64 or LY68L6400" + endchoice + + config SPIRAM_SIZE + int + default -1 if SPIRAM_TYPE_AUTO + default 4194304 if SPIRAM_TYPE_ESPPSRAM32 + default 8388608 if SPIRAM_TYPE_ESPPSRAM64 + default 0 + + config INSTRUCTION_USE_SPIRAM + bool "Cache fetch instructions from SPI RAM" + default n + help + If enabled, instruction in flash will be copied into SPIRAM. + If you also enable RODATA_USE_SPIRAM option, you can run the instruction when you are erasing or programming the flash. + + config RODATA_USE_SPIRAM + bool "Cache load read only data from SPI RAM" + default n + help + If enabled, radata in flash will be copied into SPIRAM. + If you also enable INSTRUCTION_USE_SPIRAM option, you can run the instruction when you erasing or programming the flash. + + config USE_AHB_DBUS3_ACCESS_SPIRAM + bool "Enable AHB DBUS3 to access SPIRAM" + default n + help + If Enabled, if SPI_CONFIG_SIZE is bigger then 10MB+576KB, then you can have 4MB more space to map the SPIRAM. + However, the AHB bus is slower than other data cache buses. + + 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 for SPI RAM of type 32MBit, one of the HSPI/VSPI host + will be occupied by the system. Which SPI host to use can be selected by the config item + SPIRAM_OCCUPY_SPI_HOST. Application code should never touch HSPI/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" + depends on SPIRAM_BOOT_INIT + 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 (-mfix-esp32-psram-cache-issue) 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. + + config SPIRAM_MALLOC_ALWAYSINTERNAL + int "Maximum malloc() size, in bytes, to always put in internal memory" + depends on SPIRAM_USE_MALLOC + default 16384 + range 0 131072 + help + If malloc() is capable of also allocating SPI-connected ram, its allocation strategy will prefer to + allocate chunks less than this size in internal memory, while allocations larger than this will be + done from external RAM. If allocation from the preferred region fails, an attempt is made to allocate + from the non-preferred region instead, so malloc() will not suddenly fail when either internal or + external memory is full. + + config WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST + bool "Try to allocate memories of WiFi and LWIP in SPIRAM firstly. If failed, allocate internal memory" + depends on SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC + default "n" + help + Try to allocate memories of WiFi and LWIP in SPIRAM firstly. If failed, try to allocate internal + memory then. + + config SPIRAM_MALLOC_RESERVE_INTERNAL + int "Reserve this amount of bytes for data that specifically needs to be in DMA or internal memory" + depends on SPIRAM_USE_MALLOC + default 32768 + range 0 262144 + help + Because the external/internal RAM allocation strategy is not always perfect, it sometimes may happen + that the internal memory is entirely filled up. This causes allocations that are specifically done in + internal memory, for example the stack for new tasks or memory to service DMA or have memory that's + also available when SPI cache is down, to fail. This option reserves a pool specifically for requests + like that; the memory in this pool is not given out when a normal malloc() is called. + + Set this to 0 to disable this feature. + + Note that because FreeRTOS stacks are forced to internal memory, they will also use this memory pool; + be sure to keep this in mind when adjusting this value. + + Note also that the DMA reserved pool may not be one single contiguous memory region, depending on the + configured size and the static memory usage of the app. + + config SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY + bool "Allow external memory as an argument to xTaskCreateStatic" + default n + depends on SPIRAM_USE_MALLOC + help + Because some bits of the ESP32 code environment cannot be recompiled with the cache workaround, + normally tasks cannot be safely run with their stack residing in external memory; for this reason + xTaskCreate and friends always allocate stack in internal memory and xTaskCreateStatic will check if + the memory passed to it is in internal memory. If you have a task that needs a large amount of stack + and does not call on ROM code in any way (no direct calls, but also no Bluetooth/WiFi), you can try to + disable this and use xTaskCreateStatic to create the tasks stack in external memory. + + endmenu + + config MEMMAP_TRACEMEM + bool + default "n" + + config MEMMAP_TRACEMEM_TWOBANKS + bool + default "n" + + config ESP32_TRAX + bool "Use TRAX tracing feature" + default "n" + select MEMMAP_TRACEMEM + help + The ESP32 contains a feature which allows you to trace the execution path the processor + has taken through the program. This is stored in a chunk of 32K (16K for single-processor) + of memory that can't be used for general purposes anymore. Disable this if you do not know + what this is. + + config ESP32_TRAX_TWOBANKS + bool "Reserve memory for tracing both pro as well as app cpu execution" + default "n" + depends on ESP32_TRAX && !FREERTOS_UNICORE + select MEMMAP_TRACEMEM_TWOBANKS + help + The ESP32 contains a feature which allows you to trace the execution path the processor + has taken through the program. This is stored in a chunk of 32K (16K for single-processor) + of memory that can't be used for general purposes anymore. Disable this if you do not know + what this is. + + # Memory to reverse for trace, used in linker script + config TRACEMEM_RESERVE_DRAM + hex + default 0x8000 if MEMMAP_TRACEMEM && MEMMAP_TRACEMEM_TWOBANKS + default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS + default 0x0 + + choice ESP32_COREDUMP_TO_FLASH_OR_UART + prompt "Core dump destination" + default ESP32_ENABLE_COREDUMP_TO_NONE + help + Select place to store core dump: flash, uart or none (to disable core dumps generation). + + If core dump is configured to be stored in flash and custom partition table is used add + corresponding entry to your CSV. For examples, please see predefined partition table CSV descriptions + in the components/partition_table directory. + + config ESP32_ENABLE_COREDUMP_TO_FLASH + bool "Flash" + select ESP32_ENABLE_COREDUMP + config ESP32_ENABLE_COREDUMP_TO_UART + bool "UART" + select ESP32_ENABLE_COREDUMP + config ESP32_ENABLE_COREDUMP_TO_NONE + bool "None" + endchoice + + config ESP32_ENABLE_COREDUMP + bool + default F + help + Enables/disable core dump module. + + config ESP32_CORE_DUMP_UART_DELAY + int "Core dump print to UART delay" + depends on ESP32_ENABLE_COREDUMP_TO_UART + default 0 + help + Config delay (in ms) before printing core dump to UART. + Delay can be interrupted by pressing Enter key. + + config ESP32_CORE_DUMP_LOG_LEVEL + int "Core dump module logging level" + depends on ESP32_ENABLE_COREDUMP + default 1 + help + Config core dump module logging level (0-5). + + choice NUMBER_OF_UNIVERSAL_MAC_ADDRESS + bool "Number of universally administered (by IEEE) MAC address" + default FOUR_UNIVERSAL_MAC_ADDRESS + help + Configure the number of universally administered (by IEEE) MAC addresses. + During initialisation, MAC addresses for each network interface are generated or derived from a + single base MAC address. + If the number of universal MAC addresses is four, all four interfaces (WiFi station, WiFi softap, + Bluetooth and Ethernet) receive a universally administered MAC address. These are generated + sequentially by adding 0, 1, 2 and 3 (respectively) to the final octet of the base MAC address. + If the number of universal MAC addresses is two, only two interfaces (WiFi station and Bluetooth) + receive a universally administered MAC address. These are generated sequentially by adding 0 + and 1 (respectively) to the base MAC address. The remaining two interfaces (WiFi softap and Ethernet) + receive local MAC addresses. These are derived from the universal WiFi station and Bluetooth MAC + addresses, respectively. + When using the default (Espressif-assigned) base MAC address, either setting can be used. When using + a custom universal MAC address range, the correct setting will depend on the allocation of MAC + addresses in this range (either 2 or 4 per device.) + + config TWO_UNIVERSAL_MAC_ADDRESS + bool "Two" + config FOUR_UNIVERSAL_MAC_ADDRESS + bool "Four" + endchoice + + config NUMBER_OF_UNIVERSAL_MAC_ADDRESS + int + default 2 if TWO_UNIVERSAL_MAC_ADDRESS + default 4 if FOUR_UNIVERSAL_MAC_ADDRESS + + config SYSTEM_EVENT_QUEUE_SIZE + int "System event queue size" + default 32 + help + Config system event queue size in different application. + + config SYSTEM_EVENT_TASK_STACK_SIZE + int "Event loop task stack size" + default 2304 + help + Config system event task stack size in different application. + + config MAIN_TASK_STACK_SIZE + int "Main task stack size" + default 3584 + help + Configure the "main task" stack size. This is the stack of the task + which calls app_main(). If app_main() returns then this task is deleted + and its stack memory is freed. + + config IPC_TASK_STACK_SIZE + int "Inter-Processor Call (IPC) task stack size" + default 1024 + range 512 65536 if !ESP32_APPTRACE_ENABLE + range 2048 65536 if ESP32_APPTRACE_ENABLE + help + Configure the IPC tasks stack size. One IPC task runs on each core + (in dual core mode), and allows for cross-core function calls. + + See IPC documentation for more details. + + The default stack size should be enough for most common use cases. + It can be shrunk if you are sure that you do not use any custom + IPC functionality. + + config TIMER_TASK_STACK_SIZE + int "High-resolution timer task stack size" + default 3584 + range 2048 65536 + help + Configure the stack size of esp_timer/ets_timer task. This task is used + to dispatch callbacks of timers created using ets_timer and esp_timer + APIs. If you are seing stack overflow errors in timer task, increase + this value. + + Note that this is not the same as FreeRTOS timer task. To configure + FreeRTOS timer task size, see "FreeRTOS timer task stack size" option + in "FreeRTOS" menu. + + choice NEWLIB_STDOUT_LINE_ENDING + prompt "Line ending for UART output" + default NEWLIB_STDOUT_LINE_ENDING_CRLF + help + This option allows configuring the desired line endings sent to UART + when a newline ('\n', LF) appears on stdout. + Three options are possible: + + CRLF: whenever LF is encountered, prepend it with CR + + LF: no modification is applied, stdout is sent as is + + CR: each occurence of LF is replaced with CR + + This option doesn't affect behavior of the UART driver (drivers/uart.h). + + config NEWLIB_STDOUT_LINE_ENDING_CRLF + bool "CRLF" + config NEWLIB_STDOUT_LINE_ENDING_LF + bool "LF" + config NEWLIB_STDOUT_LINE_ENDING_CR + bool "CR" + endchoice + + choice NEWLIB_STDIN_LINE_ENDING + prompt "Line ending for UART input" + default NEWLIB_STDIN_LINE_ENDING_CR + help + This option allows configuring which input sequence on UART produces + a newline ('\n', LF) on stdin. + Three options are possible: + + CRLF: CRLF is converted to LF + + LF: no modification is applied, input is sent to stdin as is + + CR: each occurence of CR is replaced with LF + + This option doesn't affect behavior of the UART driver (drivers/uart.h). + + config NEWLIB_STDIN_LINE_ENDING_CRLF + bool "CRLF" + config NEWLIB_STDIN_LINE_ENDING_LF + bool "LF" + config NEWLIB_STDIN_LINE_ENDING_CR + bool "CR" + endchoice + + config NEWLIB_NANO_FORMAT + bool "Enable 'nano' formatting options for printf/scanf family" + default n + help + ESP32 ROM contains parts of newlib C library, including printf/scanf family + of functions. These functions have been compiled with so-called "nano" + formatting option. This option doesn't support 64-bit integer formats and C99 + features, such as positional arguments. + + For more details about "nano" formatting option, please see newlib readme file, + search for '--enable-newlib-nano-formatted-io': + https://sourceware.org/newlib/README + + If this option is enabled, build system will use functions available in + ROM, reducing the application binary size. Functions available in ROM run + faster than functions which run from flash. Functions available in ROM can + also run when flash instruction cache is disabled. + + If you need 64-bit integer formatting support or C99 features, keep this + option disabled. + + choice CONSOLE_UART + prompt "UART for console output" + default CONSOLE_UART_DEFAULT + help + Select whether to use UART for console output (through stdout and stderr). + + - Default is to use UART0 on pins GPIO1(TX) and GPIO3(RX). + - If "Custom" is selected, UART0 or UART1 can be chosen, + and any pins can be selected. + - If "None" is selected, there will be no console output on any UART, except + for initial output from ROM bootloader. This output can be further suppressed by + bootstrapping GPIO13 pin to low logic level. + + config CONSOLE_UART_DEFAULT + bool "Default: UART0, TX=GPIO1, RX=GPIO3" + config CONSOLE_UART_CUSTOM + bool "Custom" + config CONSOLE_UART_NONE + bool "None" + endchoice + + choice CONSOLE_UART_NUM + prompt "UART peripheral to use for console output (0-1)" + depends on CONSOLE_UART_CUSTOM + default CONSOLE_UART_CUSTOM_NUM_0 + help + Due of a ROM bug, UART2 is not supported for console output + via ets_printf. + + config CONSOLE_UART_CUSTOM_NUM_0 + bool "UART0" + config CONSOLE_UART_CUSTOM_NUM_1 + bool "UART1" + endchoice + + config CONSOLE_UART_NUM + int + default 0 if CONSOLE_UART_DEFAULT || CONSOLE_UART_NONE + default 0 if CONSOLE_UART_CUSTOM_NUM_0 + default 1 if CONSOLE_UART_CUSTOM_NUM_1 + + config CONSOLE_UART_TX_GPIO + int "UART TX on GPIO#" + depends on CONSOLE_UART_CUSTOM + range 0 33 + default 19 + + config CONSOLE_UART_RX_GPIO + int "UART RX on GPIO#" + depends on CONSOLE_UART_CUSTOM + range 0 39 + default 21 + + config CONSOLE_UART_BAUDRATE + int "UART console baud rate" + depends on !CONSOLE_UART_NONE + default 115200 + range 1200 4000000 + + config ULP_COPROC_ENABLED + bool "Enable Ultra Low Power (ULP) Coprocessor" + default "n" + help + Set to 'y' if you plan to load a firmware for the coprocessor. + + If this option is enabled, further coprocessor configuration will appear in the Components menu. + + config ULP_COPROC_RESERVE_MEM + int + prompt "RTC slow memory reserved for coprocessor" if ULP_COPROC_ENABLED + default 512 if ULP_COPROC_ENABLED + range 32 8192 if ULP_COPROC_ENABLED + default 0 if !ULP_COPROC_ENABLED + range 0 0 if !ULP_COPROC_ENABLED + help + Bytes of memory to reserve for ULP coprocessor firmware & data. + + Data is reserved at the beginning of RTC slow memory. + + choice ESP32_PANIC + prompt "Panic handler behaviour" + default ESP32_PANIC_PRINT_REBOOT + help + If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is + invoked. Configure the panic handlers action here. + + config ESP32_PANIC_PRINT_HALT + bool "Print registers and halt" + help + Outputs the relevant registers over the serial port and halt the + processor. Needs a manual reset to restart. + + config ESP32_PANIC_PRINT_REBOOT + bool "Print registers and reboot" + help + Outputs the relevant registers over the serial port and immediately + reset the processor. + + config ESP32_PANIC_SILENT_REBOOT + bool "Silent reboot" + help + Just resets the processor without outputting anything + + config ESP32_PANIC_GDBSTUB + bool "Invoke GDBStub" + help + Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem + of the crash. + endchoice + + config ESP32_DEBUG_OCDAWARE + bool "Make exception and panic handlers JTAG/OCD aware" + default y + help + The FreeRTOS panic and unhandled exception handers can detect a JTAG OCD debugger and + instead of panicking, have the debugger stop on the offending instruction. + + config ESP32_DEBUG_STUBS_ENABLE + bool "OpenOCD debug stubs" + default OPTIMIZATION_LEVEL_DEBUG + depends on !ESP32_TRAX + help + Debug stubs are used by OpenOCD to execute pre-compiled onboard code which does some useful debugging, + e.g. GCOV data dump. + + config INT_WDT + bool "Interrupt watchdog" + default y + help + This watchdog timer can detect if the FreeRTOS tick interrupt has not been called for a certain time, + either because a task turned off interrupts and did not turn them on for a long time, or because an + interrupt handler did not return. It will try to invoke the panic handler first and failing that + reset the SoC. + + config INT_WDT_TIMEOUT_MS + int "Interrupt watchdog timeout (ms)" + depends on INT_WDT + default 300 if !SPIRAM_SUPPORT + default 800 if SPIRAM_SUPPORT + range 10 10000 + help + The timeout of the watchdog, in miliseconds. Make this higher than the FreeRTOS tick rate. + + config INT_WDT_CHECK_CPU1 + bool "Also watch CPU1 tick interrupt" + depends on INT_WDT && !FREERTOS_UNICORE + default y + help + Also detect if interrupts on CPU 1 are disabled for too long. + + config TASK_WDT + bool "Initialize Task Watchdog Timer on startup" + default y + help + The Task Watchdog Timer can be used to make sure individual tasks are still + running. Enabling this option will cause the Task Watchdog Timer to be + initialized automatically at startup. The Task Watchdog timer can be + initialized after startup as well (see Task Watchdog Timer API Reference) + + config TASK_WDT_PANIC + bool "Invoke panic handler on Task Watchdog timeout" + depends on TASK_WDT + default n + help + If this option is enabled, the Task Watchdog Timer will be configured to + trigger the panic handler when it times out. This can also be configured + at run time (see Task Watchdog Timer API Reference) + + config TASK_WDT_TIMEOUT_S + int "Task Watchdog timeout period (seconds)" + depends on TASK_WDT + range 1 60 + default 5 + help + Timeout period configuration for the Task Watchdog Timer in seconds. + This is also configurable at run time (see Task Watchdog Timer API Reference) + + config TASK_WDT_CHECK_IDLE_TASK_CPU0 + bool "Watch CPU0 Idle Task" + depends on TASK_WDT + default y + help + If this option is enabled, the Task Watchdog Timer will watch the CPU0 + Idle Task. Having the Task Watchdog watch the Idle Task allows for detection + of CPU starvation as the Idle Task not being called is usually a symptom of + CPU starvation. Starvation of the Idle Task is detrimental as FreeRTOS household + tasks depend on the Idle Task getting some runtime every now and then. + + config TASK_WDT_CHECK_IDLE_TASK_CPU1 + bool "Watch CPU1 Idle Task" + depends on TASK_WDT && !FREERTOS_UNICORE + default y + help + If this option is enabled, the Task Wtachdog Timer will wach the CPU1 + Idle Task. + + config BROWNOUT_DET + #The brownout detector code is disabled (by making it depend on a nonexisting symbol) because the current + #revision of ESP32 silicon has a bug in the brown-out detector, rendering it unusable for resetting the CPU. + bool "Hardware brownout detect & reset" + default y + help + The ESP32 has a built-in brownout detector which can detect if the voltage is lower than + a specific value. If this happens, it will reset the chip in order to prevent unintended + behaviour. + + choice BROWNOUT_DET_LVL_SEL + prompt "Brownout voltage level" + depends on BROWNOUT_DET + default BROWNOUT_DET_LVL_SEL_25 + help + The brownout detector will reset the chip when the supply voltage is approximately + below this level. Note that there may be some variation of brownout voltage level + between each ESP32 chip. + + #The voltage levels here are estimates, more work needs to be done to figure out the exact voltages + #of the brownout threshold levels. + config BROWNOUT_DET_LVL_SEL_0 + bool "2.43V +/- 0.05" + config BROWNOUT_DET_LVL_SEL_1 + bool "2.48V +/- 0.05" + config BROWNOUT_DET_LVL_SEL_2 + bool "2.58V +/- 0.05" + config BROWNOUT_DET_LVL_SEL_3 + bool "2.62V +/- 0.05" + config BROWNOUT_DET_LVL_SEL_4 + bool "2.67V +/- 0.05" + config BROWNOUT_DET_LVL_SEL_5 + bool "2.70V +/- 0.05" + config BROWNOUT_DET_LVL_SEL_6 + bool "2.77V +/- 0.05" + config BROWNOUT_DET_LVL_SEL_7 + bool "2.80V +/- 0.05" + endchoice + + config BROWNOUT_DET_LVL + int + default 0 if BROWNOUT_DET_LVL_SEL_0 + default 1 if BROWNOUT_DET_LVL_SEL_1 + default 2 if BROWNOUT_DET_LVL_SEL_2 + default 3 if BROWNOUT_DET_LVL_SEL_3 + default 4 if BROWNOUT_DET_LVL_SEL_4 + default 5 if BROWNOUT_DET_LVL_SEL_5 + default 6 if BROWNOUT_DET_LVL_SEL_6 + default 7 if BROWNOUT_DET_LVL_SEL_7 + + + # Note about the use of "FRC1" name: currently FRC1 timer is not used for + # high resolution timekeeping anymore. Instead the esp_timer API, implemented + # using FRC2 timer, is used. + # FRC1 name in the option name is kept for compatibility. + choice ESP32_TIME_SYSCALL + prompt "Timers used for gettimeofday function" + default ESP32_TIME_SYSCALL_USE_RTC_FRC1 + help + This setting defines which hardware timers are used to + implement 'gettimeofday' and 'time' functions in C library. + + - If both high-resolution and RTC timers are used, timekeeping will + continue in deep sleep. Time will be reported at 1 microsecond + resolution. This is the default, and the recommended option. + - If only high-resolution timer is used, gettimeofday will + provide time at microsecond resolution. + Time will not be preserved when going into deep sleep mode. + - If only RTC timer is used, timekeeping will continue in + deep sleep, but time will be measured at 6.(6) microsecond + resolution. Also the gettimeofday function itself may take + longer to run. + - If no timers are used, gettimeofday and time functions + return -1 and set errno to ENOSYS. + - When RTC is used for timekeeping, two RTC_STORE registers are + used to keep time in deep sleep mode. + + config ESP32_TIME_SYSCALL_USE_RTC_FRC1 + bool "RTC and high-resolution timer" + config ESP32_TIME_SYSCALL_USE_RTC + bool "RTC" + config ESP32_TIME_SYSCALL_USE_FRC1 + bool "High-resolution timer" + config ESP32_TIME_SYSCALL_USE_NONE + bool "None" + endchoice + + choice ESP32_RTC_CLOCK_SOURCE + prompt "RTC clock source" + default ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC + help + Choose which clock is used as RTC clock source. + + config ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC + bool "Internal 150kHz RC oscillator" + config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + bool "External 32kHz crystal" + endchoice + + config ESP32_RTC_CLK_CAL_CYCLES + int "Number of cycles for RTC_SLOW_CLK calibration" + default 3000 if ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + default 1024 if ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC + range 0 125000 + help + When the startup code initializes RTC_SLOW_CLK, it can perform + calibration by comparing the RTC_SLOW_CLK frequency with main XTAL + frequency. This option sets the number of RTC_SLOW_CLK cycles measured + by the calibration routine. Higher numbers increase calibration + precision, which may be important for applications which spend a lot of + time in deep sleep. Lower numbers reduce startup time. + + When this option is set to 0, clock calibration will not be performed at + startup, and approximate clock frequencies will be assumed: + + - 150000 Hz if internal RC oscillator is used as clock source. For this use value 1024. + - 32768 Hz if the 32k crystal oscillator is used. For this use value 3000 or more. + In case more value will help improve the definition of the launch of the crystal. + If the crystal could not start, it will be switched to internal RC. + + config ESP32_RTC_XTAL_BOOTSTRAP_CYCLES + int "Bootstrap cycles for external 32kHz crystal" + depends on ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + default 5 + range 0 32768 + help + To reduce the startup time of an external RTC crystal, + we bootstrap it with a 32kHz square wave for a fixed number of cycles. + Setting 0 will disable bootstrapping (if disabled, the crystal may take + longer to start up or fail to oscillate under some conditions). + + If this value is too high, a faulty crystal may initially start and then fail. + If this value is too low, an otherwise good crystal may not start. + + To accurately determine if the crystal has started, + set a larger "Number of cycles for RTC_SLOW_CLK calibration" (about 3000). + + config ESP32_DEEP_SLEEP_WAKEUP_DELAY + int "Extra delay in deep sleep wake stub (in us)" + default 2000 + range 0 5000 + help + When ESP32 exits deep sleep, the CPU and the flash chip are powered on + at the same time. CPU will run deep sleep stub first, and then + proceed to load code from flash. Some flash chips need sufficient + time to pass between power on and first read operation. By default, + without any extra delay, this time is approximately 900us, although + some flash chip types need more than that. + + By default extra delay is set to 2000us. When optimizing startup time + for applications which require it, this value may be reduced. + + If you are seeing "flash read err, 1000" message printed to the + console after deep sleep reset, try increasing this value. + + choice ESP32_XTAL_FREQ_SEL + prompt "Main XTAL frequency" + default ESP32_XTAL_FREQ_40 + help + ESP32 currently supports the following XTAL frequencies: + + - 26 MHz + - 40 MHz + + Startup code can automatically estimate XTAL frequency. This feature + uses the internal 8MHz oscillator as a reference. Because the internal + oscillator frequency is temperature dependent, it is not recommended + to use automatic XTAL frequency detection in applications which need + to work at high ambient temperatures and use high-temperature + qualified chips and modules. + config ESP32_XTAL_FREQ_40 + bool "40 MHz" + config ESP32_XTAL_FREQ_26 + bool "26 MHz" + config ESP32_XTAL_FREQ_AUTO + bool "Autodetect" + endchoice + + # Keep these values in sync with rtc_xtal_freq_t enum in soc/rtc.h + config ESP32_XTAL_FREQ + int + default 0 if ESP32_XTAL_FREQ_AUTO + default 40 if ESP32_XTAL_FREQ_40 + default 26 if ESP32_XTAL_FREQ_26 + + config DISABLE_BASIC_ROM_CONSOLE + bool "Permanently disable BASIC ROM Console" + default n + help + If set, the first time the app boots it will disable the BASIC ROM Console + permanently (by burning an eFuse). + + Otherwise, the BASIC ROM Console starts on reset if no valid bootloader is + read from the flash. + + (Enabling secure boot also disables the BASIC ROM Console by default.) + + config NO_BLOBS + bool "No Binary Blobs" + depends on !BT_ENABLED + default n + help + If enabled, this disables the linking of binary libraries in the application build. Note + that after enabling this Wi-Fi/Bluetooth will not work. + + config ESP_TIMER_PROFILING + bool "Enable esp_timer profiling features" + default n + help + If enabled, esp_timer_dump will dump information such as number of times + the timer was started, number of times the timer has triggered, and the + total time it took for the callback to run. + This option has some effect on timer performance and the amount of memory + used for timer storage, and should only be used for debugging/testing + purposes. + + config COMPATIBLE_PRE_V2_1_BOOTLOADERS + bool "App compatible with bootloaders before IDF v2.1" + default n + help + Bootloaders before IDF v2.1 did less initialisation of the + system clock. This setting needs to be enabled to build an app + which can be booted by these older bootloaders. + + If this setting is enabled, the app can be booted by any bootloader + from IDF v1.0 up to the current version. + + If this setting is disabled, the app can only be booted by bootloaders + from IDF v2.1 or newer. + + Enabling this setting adds approximately 1KB to the app's IRAM usage. + + config ESP_ERR_TO_NAME_LOOKUP + bool "Enable lookup of error code strings" + default "y" + help + Functions esp_err_to_name() and esp_err_to_name_r() return string + representations of error codes from a pre-generated lookup table. + This option can be used to turn off the use of the look-up table in + order to save memory but this comes at the price of sacrificing + distinguishable (meaningful) output string representations. + +endmenu # ESP32S2-Specific + +menu "Power Management" + + config PM_ENABLE + bool "Support for power management" + default n + help + If enabled, application is compiled with support for power management. + This option has run-time overhead (increased interrupt latency, + longer time to enter idle state), and it also reduces accuracy of + RTOS ticks and timers used for timekeeping. + Enable this option if application uses power management APIs. + + config PM_DFS_INIT_AUTO + bool "Enable dynamic frequency scaling (DFS) at startup" + depends on PM_ENABLE + default n + help + If enabled, startup code configures dynamic frequency scaling. + Max CPU frequency is set to CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ setting, + min frequency is set to XTAL frequency. + If disabled, DFS will not be active until the application + configures it using esp_pm_configure function. + + config PM_USE_RTC_TIMER_REF + bool "Use RTC timer to prevent time drift (EXPERIMENTAL)" + depends on PM_ENABLE && (ESP32_TIME_SYSCALL_USE_RTC || ESP32_TIME_SYSCALL_USE_RTC_FRC1) + default n + help + When APB clock frequency changes, high-resolution timer (esp_timer) + scale and base value need to be adjusted. Each adjustment may cause + small error, and over time such small errors may cause time drift. + If this option is enabled, RTC timer will be used as a reference to + compensate for the drift. + It is recommended that this option is only used if 32k XTAL is selected + as RTC clock source. + + config PM_PROFILING + bool "Enable profiling counters for PM locks" + depends on PM_ENABLE + default n + help + If enabled, esp_pm_* functions will keep track of the amount of time + each of the power management locks has been held, and esp_pm_dump_locks + function will print this information. + This feature can be used to analyze which locks are preventing the chip + from going into a lower power state, and see what time the chip spends + in each power saving mode. This feature does incur some run-time + overhead, so should typically be disabled in production builds. + + config PM_TRACE + bool "Enable debug tracing of PM using GPIOs" + depends on PM_ENABLE + default n + help + If enabled, some GPIOs will be used to signal events such as RTOS ticks, + frequency switching, entry/exit from idle state. Refer to pm_trace.c + file for the list of GPIOs. + This feature is intended to be used when analyzing/debugging behavior + of power management implementation, and should be kept disabled in + applications. + + +endmenu # "Power Management" diff --git a/components/esp32s2beta/Makefile.projbuild b/components/esp32s2beta/Makefile.projbuild new file mode 100644 index 0000000000..9f92c377d5 --- /dev/null +++ b/components/esp32s2beta/Makefile.projbuild @@ -0,0 +1,44 @@ +ifdef CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION + +PHY_INIT_DATA_OBJ = $(BUILD_DIR_BASE)/phy_init_data.o +PHY_INIT_DATA_BIN = $(BUILD_DIR_BASE)/phy_init_data.bin + +# Command to flash PHY init data partition +PHY_INIT_DATA_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN) +ESPTOOL_ALL_FLASH_ARGS += $(PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN) + +ESP32_COMPONENT_PATH := $(COMPONENT_PATH) + +$(PHY_INIT_DATA_OBJ): $(ESP32_COMPONENT_PATH)/phy_init_data.h $(BUILD_DIR_BASE)/include/sdkconfig.h + $(summary) CC $(notdir $@) + printf "#include \"phy_init_data.h\"\n" | $(CC) -I $(BUILD_DIR_BASE)/include -I $(ESP32_COMPONENT_PATH) -I $(ESP32_COMPONENT_PATH)/include -c -o $@ -xc - + +$(PHY_INIT_DATA_BIN): $(PHY_INIT_DATA_OBJ) + $(summary) BIN $(notdir $@) + $(OBJCOPY) -O binary $< $@ + +phy_init_data: $(PHY_INIT_DATA_BIN) + +phy_init_data-flash: $(BUILD_DIR_BASE)/phy_init_data.bin + @echo "Flashing PHY init data..." + $(PHY_INIT_DATA_FLASH_CMD) + +phy_init_data-clean: + rm -f $(PHY_INIT_DATA_BIN) $(PHY_INIT_DATA_OBJ) + +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 +endif + +# Enable dynamic esp_timer overflow value if building unit tests +ifneq ("$(TEST_COMPONENTS_LIST)","") +CPPFLAGS += -DESP_TIMER_DYNAMIC_OVERFLOW_VAL +endif diff --git a/components/esp32s2beta/brownout.c b/components/esp32s2beta/brownout.c new file mode 100644 index 0000000000..a48dd4b8ee --- /dev/null +++ b/components/esp32s2beta/brownout.c @@ -0,0 +1,56 @@ +// 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 +#include +#include +#include +#include "sdkconfig.h" +#include "soc/soc.h" +#include "soc/cpu.h" +#include "soc/rtc_cntl_reg.h" +#include "esp32s2beta/rom/ets_sys.h" +#include "esp_private/system_internal.h" +#include "driver/rtc_cntl.h" +#include "freertos/FreeRTOS.h" + +#ifdef CONFIG_BROWNOUT_DET_LVL +#define BROWNOUT_DET_LVL CONFIG_BROWNOUT_DET_LVL +#else +#define BROWNOUT_DET_LVL 0 +#endif //CONFIG_BROWNOUT_DET_LVL + +static void rtc_brownout_isr_handler() +{ + /* Normally RTC ISR clears the interrupt flag after the application-supplied + * handler returns. Since restart is called here, the flag needs to be + * cleared manually. + */ + REG_WRITE(RTC_CNTL_INT_CLR_REG, RTC_CNTL_BROWN_OUT_INT_CLR); + /* Stall the other CPU to make sure the code running there doesn't use UART + * at the same time as the following ets_printf. + */ + esp_cpu_stall(!xPortGetCoreID()); + ets_printf("\r\nBrownout detector was triggered\r\n\r\n"); + esp_restart_noos(); +} + +void esp_brownout_init() +{ + //TODO, chip7.2.2 will use i2c inteface to configure brown out threshold + + ESP_ERROR_CHECK( rtc_isr_register(rtc_brownout_isr_handler, NULL, RTC_CNTL_BROWN_OUT_INT_ENA_M) ); + + REG_SET_BIT(RTC_CNTL_INT_ENA_REG, RTC_CNTL_BROWN_OUT_INT_ENA_M); +} diff --git a/components/esp32s2beta/cache_err_int.c b/components/esp32s2beta/cache_err_int.c new file mode 100644 index 0000000000..b57eb157b6 --- /dev/null +++ b/components/esp32s2beta/cache_err_int.c @@ -0,0 +1,69 @@ +// 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. + +/* + The cache has an interrupt that can be raised as soon as an access to a cached + region (flash, psram) is done without the cache being enabled. We use that here + to panic the CPU, which from a debugging perspective is better than grabbing bad + data from the bus. +*/ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "esp_attr.h" +#include "soc/dport_reg.h" +#include "soc/periph_defs.h" +#include "sdkconfig.h" +#include "esp32s2beta/dport_access.h" + +void esp_cache_err_int_init() +{ + uint32_t core_id = xPortGetCoreID(); + ESP_INTR_DISABLE(ETS_CACHEERR_INUM); + + // We do not register a handler for the interrupt because it is interrupt + // level 4 which is not serviceable from C. Instead, xtensa_vectors.S has + // a call to the panic handler for + // this interrupt. + intr_matrix_set(core_id, ETS_CACHE_IA_INTR_SOURCE, ETS_CACHEERR_INUM); + + // Enable invalid cache access interrupt when the cache is disabled. + // When the interrupt happens, we can not determine the CPU where the + // invalid cache access has occurred. We enable the interrupt to catch + // invalid access on both CPUs, but the interrupt is connected to the + // CPU which happens to call this function. + // For this reason, panic handler backtrace will not be correct if the + // interrupt is connected to PRO CPU and invalid access happens on the APP + // CPU. +#if 0 + DPORT_SET_PERI_REG_MASK(DPORT_PRO_CACHE_IA_INT_EN_REG, + DPORT_CACHE_IA_INT_PRO_DRAM1 | + DPORT_CACHE_IA_INT_PRO_DROM0 | + DPORT_CACHE_IA_INT_PRO_IROM0 | + DPORT_CACHE_IA_INT_PRO_IRAM0 | + DPORT_CACHE_IA_INT_PRO_IRAM1); +#endif + ESP_INTR_ENABLE(ETS_CACHEERR_INUM); +} + +int IRAM_ATTR esp_cache_err_get_cpuid() +{ + esp_dport_access_int_pause(); + return PRO_CPU_NUM; +} diff --git a/components/esp32s2beta/clk.c b/components/esp32s2beta/clk.c new file mode 100644 index 0000000000..9fefa402a0 --- /dev/null +++ b/components/esp32s2beta/clk.c @@ -0,0 +1,307 @@ +// 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 +#include +#include +#include +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "esp32s2beta/clk.h" +#include "esp_clk_internal.h" +#include "esp32s2beta/rom/ets_sys.h" +#include "esp32s2beta/rom/uart.h" +#include "esp32s2beta/rom/rtc.h" +#include "soc/soc.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/i2s_reg.h" +#include "driver/periph_ctrl.h" +#include "xtensa/core-macros.h" +#include "bootloader_clock.h" +#include "soc/syscon_reg.h" + +/* Number of cycles to wait from the 32k XTAL oscillator to consider it running. + * Larger values increase startup delay. Smaller values may cause false positive + * detection (i.e. oscillator runs for a few cycles and then stops). + */ +#define SLOW_CLK_CAL_CYCLES CONFIG_ESP32_RTC_CLK_CAL_CYCLES + +#define MHZ (1000000) + +static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk); + +// g_ticks_us defined in ROMs for PRO and APP CPU +extern uint32_t g_ticks_per_us_pro; +#if !CONFIG_FREERTOS_UNICORE +extern uint32_t g_ticks_per_us_app; +#endif //!CONFIG_FREERTOS_UNICORE + +static const char* TAG = "clk"; + + +void esp_clk_init(void) +{ + rtc_config_t cfg = RTC_CONFIG_DEFAULT(); + rtc_init(cfg); + +#ifdef CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS + /* Check the bootloader set the XTAL frequency. + + Bootloaders pre-v2.1 don't do this. + */ + rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); + if (xtal_freq == RTC_XTAL_FREQ_AUTO) { + ESP_EARLY_LOGW(TAG, "RTC domain not initialised by bootloader"); + bootloader_clock_configure(); + } +#else + /* If this assertion fails, either upgrade the bootloader or enable CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS */ + assert(rtc_clk_xtal_freq_get() != RTC_XTAL_FREQ_AUTO); +#endif + + rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M); + +#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL); +#else + select_rtc_slow_clk(RTC_SLOW_FREQ_RTC); +#endif + + uint32_t freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; + rtc_cpu_freq_t freq = RTC_CPU_FREQ_80M; + switch(freq_mhz) { + case 240: + freq = RTC_CPU_FREQ_240M; + break; + case 160: + freq = RTC_CPU_FREQ_160M; + break; + default: + freq_mhz = 80; + /* no break */ + case 80: + freq = RTC_CPU_FREQ_80M; + break; + } + + // Wait for UART TX to finish, otherwise some UART output will be lost + // when switching APB frequency + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + + uint32_t freq_before = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ ; + + rtc_clk_cpu_freq_set(freq); + + // Re calculate the ccount to make time calculation correct. + uint32_t freq_after = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; + XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before ); +} + +int IRAM_ATTR esp_clk_cpu_freq(void) +{ + return g_ticks_per_us_pro * 1000000; +} + +int IRAM_ATTR esp_clk_apb_freq(void) +{ + return MIN(g_ticks_per_us_pro, 80) * 1000000; +} + +void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) +{ + /* Update scale factors used by ets_delay_us */ + g_ticks_per_us_pro = ticks_per_us; +#if !CONFIG_FREERTOS_UNICORE + g_ticks_per_us_app = ticks_per_us; +#endif //!CONFIG_FREERTOS_UNICORE +} + +static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) +{ + uint32_t cal_val = 0; + uint32_t wait = 0; + const uint32_t warning_timeout = 3 /* sec */ * 32768 /* Hz */ / (2 * SLOW_CLK_CAL_CYCLES); + bool changing_clock_to_150k = false; + do { + if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) { + /* 32k XTAL oscillator needs to be enabled and running before it can + * be used. Hardware doesn't have a direct way of checking if the + * oscillator is running. Here we use rtc_clk_cal function to count + * the number of main XTAL cycles in the given number of 32k XTAL + * oscillator cycles. If the 32k XTAL has not started up, calibration + * will time out, returning 0. + */ + ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up"); + rtc_clk_32k_enable(true); + cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, SLOW_CLK_CAL_CYCLES); + if(cal_val == 0 || cal_val < 15000000L){ + ESP_EARLY_LOGE(TAG, "RTC: Not found External 32 kHz XTAL. Switching to Internal 150 kHz RC chain"); + slow_clk = RTC_SLOW_FREQ_RTC; + changing_clock_to_150k = true; + } + } + rtc_clk_slow_freq_set(slow_clk); + if (changing_clock_to_150k == true && wait > 1){ + // This helps when there are errors when switching the clock from External 32 kHz XTAL to Internal 150 kHz RC chain. + rtc_clk_32k_enable(false); + uint32_t min_bootstrap = 5; // Min bootstrapping for continue switching the clock. + rtc_clk_32k_bootstrap(min_bootstrap); + rtc_clk_32k_enable(true); + } + + if (SLOW_CLK_CAL_CYCLES > 0) { + /* TODO: 32k XTAL oscillator has some frequency drift at startup. + * Improve calibration routine to wait until the frequency is stable. + */ + cal_val = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES); + } else { + const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL; + cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz()); + } + if (++wait % warning_timeout == 0) { + ESP_EARLY_LOGW(TAG, "still waiting for source selection RTC"); + } + } while (cal_val == 0); + ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", cal_val); + esp_clk_slowclk_cal_set(cal_val); +} + +void rtc_clk_select_rtc_slow_clk() +{ + select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL); +} + +/* This function is not exposed as an API at this point. + * All peripheral clocks are default enabled after chip is powered on. + * This function disables some peripheral clocks when cpu starts. + * These peripheral clocks are enabled when the peripherals are initialized + * and disabled when they are de-initialized. + */ +void esp_perip_clk_init(void) +{ + uint32_t common_perip_clk, hwcrypto_perip_clk, wifi_bt_sdio_clk = 0; + uint32_t common_perip_clk1 = 0; + +#if CONFIG_FREERTOS_UNICORE + RESET_REASON rst_reas[1]; +#else + RESET_REASON rst_reas[2]; +#endif + + rst_reas[0] = rtc_get_reset_reason(0); + +#if !CONFIG_FREERTOS_UNICORE + rst_reas[1] = rtc_get_reset_reason(1); +#endif + + /* For reason that only reset CPU, do not disable the clocks + * that have been enabled before reset. + */ + if ((rst_reas[0] >= TG0WDT_CPU_RESET && rst_reas[0] <= TG0WDT_CPU_RESET && rst_reas[0] != RTCWDT_BROWN_OUT_RESET) +#if !CONFIG_FREERTOS_UNICORE + || (rst_reas[1] >= TGWDT_CPU_RESET && rst_reas[1] <= RTCWDT_CPU_RESET) +#endif + ) { + common_perip_clk = ~DPORT_READ_PERI_REG(DPORT_PERIP_CLK_EN_REG); + hwcrypto_perip_clk = ~DPORT_READ_PERI_REG(DPORT_PERI_CLK_EN_REG); + wifi_bt_sdio_clk = ~DPORT_READ_PERI_REG(DPORT_WIFI_CLK_EN_REG); + } + else { + common_perip_clk = DPORT_WDG_CLK_EN | + DPORT_I2S0_CLK_EN | +#if CONFIG_CONSOLE_UART_NUM != 0 + DPORT_UART_CLK_EN | +#endif +#if CONFIG_CONSOLE_UART_NUM != 1 + DPORT_UART1_CLK_EN | +#endif + DPORT_USB_CLK_EN | + DPORT_SPI2_CLK_EN | + DPORT_I2C_EXT0_CLK_EN | + DPORT_UHCI0_CLK_EN | + DPORT_RMT_CLK_EN | + DPORT_PCNT_CLK_EN | + DPORT_LEDC_CLK_EN | + DPORT_TIMERGROUP1_CLK_EN | + DPORT_SPI3_CLK_EN | + DPORT_SPI4_CLK_EN | + DPORT_PWM0_CLK_EN | + DPORT_CAN_CLK_EN | + DPORT_PWM1_CLK_EN | + DPORT_I2S1_CLK_EN | + DPORT_SPI2_DMA_CLK_EN | + DPORT_SPI3_DMA_CLK_EN | + DPORT_PWM2_CLK_EN | + DPORT_PWM3_CLK_EN; + common_perip_clk1 = DPORT_SPI_SHARED_DMA_CLK_EN; + hwcrypto_perip_clk = DPORT_PERI_EN_AES | + DPORT_PERI_EN_SHA | + DPORT_PERI_EN_RSA | + DPORT_PERI_EN_SECUREBOOT; + wifi_bt_sdio_clk = DPORT_WIFI_CLK_WIFI_EN | + DPORT_WIFI_CLK_BT_EN_M | + DPORT_WIFI_CLK_UNUSED_BIT5 | + DPORT_WIFI_CLK_UNUSED_BIT12 | + DPORT_WIFI_CLK_SDIOSLAVE_EN | + DPORT_WIFI_CLK_SDIO_HOST_EN | + DPORT_WIFI_CLK_EMAC_EN; + } + + //Reset the communication peripherals like I2C, SPI, UART, I2S and bring them to known state. + common_perip_clk |= DPORT_I2S0_CLK_EN | +#if CONFIG_CONSOLE_UART_NUM != 0 + DPORT_UART_CLK_EN | +#endif +#if CONFIG_CONSOLE_UART_NUM != 1 + DPORT_UART1_CLK_EN | +#endif + DPORT_USB_CLK_EN | + DPORT_SPI2_CLK_EN | + DPORT_I2C_EXT0_CLK_EN | + DPORT_UHCI0_CLK_EN | + DPORT_RMT_CLK_EN | + DPORT_UHCI1_CLK_EN | + DPORT_SPI3_CLK_EN | + DPORT_SPI4_CLK_EN | + DPORT_I2C_EXT1_CLK_EN | + DPORT_I2S1_CLK_EN | + DPORT_SPI2_DMA_CLK_EN | + DPORT_SPI3_DMA_CLK_EN; + common_perip_clk1 = DPORT_SPI_SHARED_DMA_CLK_EN; + + /* Change I2S clock to audio PLL first. Because if I2S uses 160MHz clock, + * the current is not reduced when disable I2S clock. + */ + REG_SET_FIELD(I2S_CLKM_CONF_REG(0), I2S_CLK_SEL, I2S_CLK_AUDIO_PLL); + REG_SET_FIELD(I2S_CLKM_CONF_REG(1), I2S_CLK_SEL, I2S_CLK_AUDIO_PLL); + + /* Disable some peripheral clocks. */ + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, common_perip_clk); + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, common_perip_clk); + + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN1_REG, common_perip_clk1); + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN1_REG, common_perip_clk1); + + /* Disable hardware crypto clocks. */ + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERI_CLK_EN_REG, hwcrypto_perip_clk); + DPORT_SET_PERI_REG_MASK(DPORT_PERI_RST_EN_REG, hwcrypto_perip_clk); + + /* Disable WiFi/BT/SDIO clocks. */ + DPORT_CLEAR_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, wifi_bt_sdio_clk); + + /* Enable RNG clock. */ + periph_module_enable(PERIPH_RNG_MODULE); +} diff --git a/components/esp32s2beta/component.mk b/components/esp32s2beta/component.mk new file mode 100644 index 0000000000..a2f7dc1797 --- /dev/null +++ b/components/esp32s2beta/component.mk @@ -0,0 +1,4 @@ +# +# Component Makefile +# +COMPONENT_CONFIG_ONLY := 1 diff --git a/components/esp32s2beta/cpu_start.c b/components/esp32s2beta/cpu_start.c new file mode 100644 index 0000000000..99f92843c9 --- /dev/null +++ b/components/esp32s2beta/cpu_start.c @@ -0,0 +1,523 @@ +// Copyright 2015-2018 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 +#include + +#include "esp_attr.h" +#include "esp_err.h" + +#include "esp32s2beta/rom/ets_sys.h" +#include "esp32s2beta/rom/uart.h" +#include "esp32s2beta/rom/rtc.h" +#include "esp32s2beta/rom/cache.h" + +#include "soc/cpu.h" +#include "soc/rtc.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_reg.h" +#include "soc/periph_defs.h" + +#include "driver/rtc_io.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/portmacro.h" + +#include "esp_heap_caps_init.h" +#include "sdkconfig.h" +#include "esp_system.h" +#include "esp_spi_flash.h" +#include "nvs_flash.h" +#include "esp_event.h" +#include "esp_spi_flash.h" +#include "esp_ipc.h" +#include "esp32s2beta/dport_access.h" +#include "esp_private/crosscore_int.h" +#include "esp_log.h" +#include "esp_vfs_dev.h" +#include "esp_newlib.h" +#include "esp32s2beta/brownout.h" +#include "esp_int_wdt.h" +#include "esp_task.h" +#include "esp_task_wdt.h" +#include "esp_phy_init.h" +#include "esp32s2beta/cache_err_int.h" +#include "esp_coexist_internal.h" +#include "esp_debug_helpers.h" +#include "esp_core_dump.h" +#include "esp_app_trace.h" +#include "esp_private/dbg_stubs.h" +#include "esp_efuse.h" +#include "esp32s2beta/spiram.h" +#include "esp_clk_internal.h" +#include "esp_timer.h" +#include "esp_pm.h" +#include "esp_private/pm_impl.h" +#include "trax.h" + +#define STRINGIFY(s) STRINGIFY2(s) +#define STRINGIFY2(s) #s + +void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))) __attribute__((noreturn)); +void start_cpu0_default(void) IRAM_ATTR __attribute__((noreturn)); +#if !CONFIG_FREERTOS_UNICORE +static void IRAM_ATTR call_start_cpu1() __attribute__((noreturn)); +void start_cpu1(void) __attribute__((weak, alias("start_cpu1_default"))) __attribute__((noreturn)); +void start_cpu1_default(void) IRAM_ATTR __attribute__((noreturn)); +static bool app_cpu_started = false; +#endif //!CONFIG_FREERTOS_UNICORE + +static void do_global_ctors(void); +static void main_task(void* args); +extern void app_main(void); +extern esp_err_t esp_pthread_init(void); + +extern int _bss_start; +extern int _bss_end; +extern int _rtc_bss_start; +extern int _rtc_bss_end; +extern int _init_start; +extern void (*__init_array_start)(void); +extern void (*__init_array_end)(void); +extern volatile int port_xSchedulerRunning[2]; + +static const char* TAG = "cpu_start"; + +struct object { long placeholder[ 10 ]; }; +void __register_frame_info (const void *begin, struct object *ob); +extern char __eh_frame[]; + +//If CONFIG_SPIRAM_IGNORE_NOTFOUND is set and external RAM is not found or errors out on testing, this is set to false. +static bool s_spiram_okay=true; + +/* + * We arrive here after the bootloader finished loading the program from flash. The hardware is mostly uninitialized, + * and the app CPU is in reset. We do have a stack, so we can do the initialization in C. + */ + +void IRAM_ATTR call_start_cpu0() +{ +#if CONFIG_FREERTOS_UNICORE + RESET_REASON rst_reas[1]; +#else + RESET_REASON rst_reas[2]; +#endif + cpu_configure_region_protection(); + + //Move exception vectors to IRAM + asm volatile (\ + "wsr %0, vecbase\n" \ + ::"r"(&_init_start)); + + rst_reas[0] = rtc_get_reset_reason(0); + +#if !CONFIG_FREERTOS_UNICORE + rst_reas[1] = rtc_get_reset_reason(1); +#endif + + // from panic handler we can be reset by RWDT or TG0WDT + if (rst_reas[0] == RTCWDT_SYS_RESET || rst_reas[0] == TG0WDT_SYS_RESET +#if !CONFIG_FREERTOS_UNICORE + || rst_reas[1] == RTCWDT_SYS_RESET || rst_reas[1] == TG0WDT_SYS_RESET +#endif + ) { + esp_panic_wdt_stop(); + } + + //Clear BSS. Please do not attempt to do any complex stuff (like early logging) before this. + memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start)); + + /* Unless waking from deep sleep (implying RTC memory is intact), clear RTC bss */ + if (rst_reas[0] != DEEPSLEEP_RESET) { + memset(&_rtc_bss_start, 0, (&_rtc_bss_end - &_rtc_bss_start) * sizeof(_rtc_bss_start)); + } + + /* Configure the mode of instruction cache : cache size, cache associated ways, cache line size. */ +extern void esp_config_instruction_cache_mode(void); + esp_config_instruction_cache_mode(); + + /* copy MMU table from ICache to DCache, so we can use DCache to access rodata later. */ +#if CONFIG_RODATA_USE_DATA_CACHE + MMU_Drom0_I2D_Copy(); +#endif + + /* If we need use SPIRAM, we should use data cache, or if we want to access rodata, we also should use data cache. + Configure the mode of data : cache size, cache associated ways, cache line size. + Enable data cache, so if we don't use SPIRAM, it just works. */ +#if CONFIG_SPIRAM_BOOT_INIT || CONFIG_RODATA_USE_DATA_CACHE +extern void esp_config_data_cache_mode(void); + esp_config_data_cache_mode(); + Cache_Enable_DCache(0); +#endif + + /* In SPIRAM code, we will reconfigure data cache, as well as instruction cache, so that we can: + 1. make data buses works with SPIRAM + 2. make instruction and rodata work with SPIRAM, still through instruction cache */ +#if CONFIG_SPIRAM_BOOT_INIT + esp_spiram_init_cache(); + if (esp_spiram_init() != ESP_OK) { +#if CONFIG_SPIRAM_IGNORE_NOTFOUND + ESP_EARLY_LOGI(TAG, "Failed to init external RAM; continuing without it."); + s_spiram_okay = false; +#else + ESP_EARLY_LOGE(TAG, "Failed to init external RAM!"); + abort(); +#endif + } +#endif + + /* Start to use data cache to access rodata. */ +#if CONFIG_RODATA_USE_DATA_CACHE +extern void esp_switch_rodata_to_dcache(void); + esp_switch_rodata_to_dcache(); +#endif + + ESP_EARLY_LOGI(TAG, "Pro cpu up."); + +#if !CONFIG_FREERTOS_UNICORE + ESP_EARLY_LOGI(TAG, "Starting app cpu, entry point is %p", call_start_cpu1); + //Flush and enable icache for APP CPU + Cache_Flush(1); + Cache_Read_Enable(1); + esp_cpu_unstall(1); + // Enable clock and reset APP CPU. Note that OpenOCD may have already + // enabled clock and taken APP CPU out of reset. In this case don't reset + // APP CPU again, as that will clear the breakpoints which may have already + // been set. + if (!DPORT_GET_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN)) { + DPORT_SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_C_REG, DPORT_APPCPU_RUNSTALL); + DPORT_SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_A_REG, DPORT_APPCPU_RESETTING); + DPORT_CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_A_REG, DPORT_APPCPU_RESETTING); + } + ets_set_appcpu_boot_addr((uint32_t)call_start_cpu1); + + while (!app_cpu_started) { + ets_delay_us(100); + } +#else + ESP_EARLY_LOGI(TAG, "Single core mode"); +#endif + + +#if CONFIG_SPIRAM_MEMTEST + if (s_spiram_okay) { + bool ext_ram_ok=esp_spiram_test(); + if (!ext_ram_ok) { + ESP_EARLY_LOGE(TAG, "External RAM failed memory test!"); + abort(); + } + } +#endif + +#if CONFIG_INSTRUCTION_USE_SPIRAM +extern void esp_spiram_enable_instruction_access(void); + esp_spiram_enable_instruction_access(); +#endif +#if CONFIG_RODATA_USE_SPIRAM +extern void esp_spiram_enable_rodata_access(void); + esp_spiram_enable_rodata_access(); +#endif + +#if CONFIG_ENABLE_INSTRUCTION_CACHE_WRAP || CONFIG_ENABLE_DATA_CACHE_WRAP +uint32_t icache_wrap_enable = 0,dcache_wrap_enable = 0; +#if CONFIG_ENABLE_INSTRUCTION_CACHE_WRAP + icache_wrap_enable = 1; +#endif +#if CONFIG_ENABLE_DATA_CACHE_WRAP + dcache_wrap_enable = 1; +#endif +extern void esp_enable_cache_wrap(uint32_t icache_wrap_enable, uint32_t dcache_wrap_enable); + esp_enable_cache_wrap(icache_wrap_enable, dcache_wrap_enable); +#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. + 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"); + start_cpu0(); +} + +#if !CONFIG_FREERTOS_UNICORE + +static void wdt_reset_cpu1_info_enable(void) +{ + DPORT_REG_SET_BIT(DPORT_APP_CPU_RECORD_CTRL_REG, DPORT_APP_CPU_PDEBUG_ENABLE | DPORT_APP_CPU_RECORD_ENABLE); + DPORT_REG_CLR_BIT(DPORT_APP_CPU_RECORD_CTRL_REG, DPORT_APP_CPU_RECORD_ENABLE); +} + +void IRAM_ATTR call_start_cpu1() +{ + asm volatile (\ + "wsr %0, vecbase\n" \ + ::"r"(&_init_start)); + + ets_set_appcpu_boot_addr(0); + cpu_configure_region_protection(); + +#if CONFIG_CONSOLE_UART_NONE + ets_install_putc1(NULL); + ets_install_putc2(NULL); +#else // CONFIG_CONSOLE_UART_NONE + uartAttach(); + ets_install_uart_printf(); + uart_tx_switch(CONFIG_CONSOLE_UART_NUM); +#endif + + wdt_reset_cpu1_info_enable(); + ESP_EARLY_LOGI(TAG, "App cpu up."); + app_cpu_started = 1; + start_cpu1(); +} +#endif //!CONFIG_FREERTOS_UNICORE + +static void intr_matrix_clear(void) +{ + //Clear all the interrupt matrix register + for (int i = ETS_WIFI_MAC_INTR_SOURCE; i < ETS_MAX_INTR_SOURCE; i++) { + intr_matrix_set(0, i, ETS_INVALID_INUM); +#if !CONFIG_FREERTOS_UNICORE + intr_matrix_set(1, i, ETS_INVALID_INUM); +#endif + } +} + +void start_cpu0_default(void) +{ + esp_err_t err; + esp_setup_syscall_table(); + + if (s_spiram_okay) { +#if CONFIG_SPIRAM_BOOT_INIT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC) + esp_err_t r=esp_spiram_add_to_heapalloc(); + if (r != ESP_OK) { + ESP_EARLY_LOGE(TAG, "External RAM could not be added to heap!"); + abort(); + } +#if CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL + r=esp_spiram_reserve_dma_pool(CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL); + if (r != ESP_OK) { + ESP_EARLY_LOGE(TAG, "Could not reserve internal/DMA pool!"); + abort(); + } +#endif +#if CONFIG_SPIRAM_USE_MALLOC + heap_caps_malloc_extmem_enable(CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL); +#endif +#endif + } + +//Enable trace memory and immediately start trace. +#if CONFIG_ESP32_TRAX +#if CONFIG_ESP32_TRAX_TWOBANKS + trax_enable(TRAX_ENA_PRO_APP); +#else + trax_enable(TRAX_ENA_PRO); +#endif + trax_start_trace(TRAX_DOWNCOUNT_WORDS); +#endif + esp_clk_init(); + esp_perip_clk_init(); + intr_matrix_clear(); + +#ifndef CONFIG_CONSOLE_UART_NONE +#ifdef CONFIG_PM_ENABLE + const int uart_clk_freq = REF_CLK_FREQ; + /* When DFS is enabled, use REFTICK as UART clock source */ + CLEAR_PERI_REG_MASK(UART_CONF0_REG(CONFIG_CONSOLE_UART_NUM), UART_TICK_REF_ALWAYS_ON); +#else + const int uart_clk_freq = APB_CLK_FREQ; +#endif // CONFIG_PM_DFS_ENABLE + uart_div_modify(CONFIG_CONSOLE_UART_NUM, (uart_clk_freq << 4) / CONFIG_CONSOLE_UART_BAUDRATE); +#endif // CONFIG_CONSOLE_UART_NONE + +#if CONFIG_BROWNOUT_DET + esp_brownout_init(); +#endif +#if CONFIG_DISABLE_BASIC_ROM_CONSOLE + esp_efuse_disable_basic_rom_console(); +#endif + rtc_gpio_force_hold_dis_all(); + esp_vfs_dev_uart_register(); + esp_reent_init(_GLOBAL_REENT); +#ifndef CONFIG_CONSOLE_UART_NONE + const char* default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_CONSOLE_UART_NUM); + _GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r"); + _GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w"); + _GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w"); +#else + _GLOBAL_REENT->_stdin = (FILE*) &__sf_fake_stdin; + _GLOBAL_REENT->_stdout = (FILE*) &__sf_fake_stdout; + _GLOBAL_REENT->_stderr = (FILE*) &__sf_fake_stderr; +#endif + esp_timer_init(); + esp_set_time_from_rtc(); +#if CONFIG_ESP32_APPTRACE_ENABLE + err = esp_apptrace_init(); + assert(err == ESP_OK && "Failed to init apptrace module on PRO CPU!"); +#endif +#if CONFIG_SYSVIEW_ENABLE + SEGGER_SYSVIEW_Conf(); +#endif +#if CONFIG_ESP32_DEBUG_STUBS_ENABLE + esp_dbg_stubs_init(); +#endif + err = esp_pthread_init(); + assert(err == ESP_OK && "Failed to init pthread module!"); + + do_global_ctors(); +#if CONFIG_INT_WDT + //esp_int_wdt_init(); + //Initialize the interrupt watch dog for CPU0. + //esp_int_wdt_cpu_init(); +#endif + //esp_cache_err_int_init(); + esp_crosscore_int_init(); + esp_ipc_init(); +#ifndef CONFIG_FREERTOS_UNICORE + esp_dport_access_int_init(); +#endif + spi_flash_init(); + /* init default OS-aware flash access critical section */ + spi_flash_guard_set(&g_flash_guard_default_ops); +#ifdef CONFIG_PM_ENABLE + esp_pm_impl_init(); +#ifdef CONFIG_PM_DFS_INIT_AUTO + rtc_cpu_freq_t max_freq; + rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ, &max_freq); + esp_pm_config_esp32_t cfg = { + .max_cpu_freq = max_freq, + .min_cpu_freq = RTC_CPU_FREQ_XTAL + }; + esp_pm_configure(&cfg); +#endif //CONFIG_PM_DFS_INIT_AUTO +#endif //CONFIG_PM_ENABLE + +#if CONFIG_ESP32_ENABLE_COREDUMP + esp_core_dump_init(); +#endif + + portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, "main", + ESP_TASK_MAIN_STACK, NULL, + ESP_TASK_MAIN_PRIO, NULL, 0); + assert(res == pdTRUE); + ESP_LOGI(TAG, "Starting scheduler on PRO CPU."); + vTaskStartScheduler(); + abort(); /* Only get to here if not enough free heap to start scheduler */ +} + +#if !CONFIG_FREERTOS_UNICORE +void start_cpu1_default(void) +{ + // Wait for FreeRTOS initialization to finish on PRO CPU + while (port_xSchedulerRunning[0] == 0) { + ; + } +#if CONFIG_ESP32_TRAX_TWOBANKS + trax_start_trace(TRAX_DOWNCOUNT_WORDS); +#endif +#if CONFIG_ESP32_APPTRACE_ENABLE + esp_err_t err = esp_apptrace_init(); + assert(err == ESP_OK && "Failed to init apptrace module on APP CPU!"); +#endif +#if CONFIG_INT_WDT + //Initialize the interrupt watch dog for CPU1. + //esp_int_wdt_cpu_init(); +#endif + //Take care putting stuff here: if asked, FreeRTOS will happily tell you the scheduler + //has started, but it isn't active *on this CPU* yet. + esp_cache_err_int_init(); + esp_crosscore_int_init(); + esp_dport_access_int_init(); + + ESP_EARLY_LOGI(TAG, "Starting scheduler on APP CPU."); + xPortStartScheduler(); + abort(); /* Only get to here if FreeRTOS somehow very broken */ +} +#endif //!CONFIG_FREERTOS_UNICORE + +#ifdef CONFIG_CXX_EXCEPTIONS +size_t __cxx_eh_arena_size_get() +{ + return CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE; +} +#endif + +static void do_global_ctors(void) +{ +#ifdef CONFIG_CXX_EXCEPTIONS + static struct object ob; + __register_frame_info( __eh_frame, &ob ); +#endif + + void (**p)(void); + for (p = &__init_array_end - 1; p >= &__init_array_start; --p) { + (*p)(); + } +} + +static void main_task(void* args) +{ + // Now that the application is about to start, disable boot watchdogs + REG_CLR_BIT(TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN_S); + REG_CLR_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN); +#if !CONFIG_FREERTOS_UNICORE + // Wait for FreeRTOS initialization to finish on APP CPU, before replacing its startup stack + while (port_xSchedulerRunning[1] == 0) { + ; + } +#endif + //Enable allocation in region where the startup stacks were located. + heap_caps_enable_nonos_stack_heaps(); + + //Initialize task wdt if configured to do so +#ifdef CONFIG_TASK_WDT_PANIC + //ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_TASK_WDT_TIMEOUT_S, true)) +#elif CONFIG_TASK_WDT + //ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_TASK_WDT_TIMEOUT_S, false)) +#endif + + //Add IDLE 0 to task wdt +#if 0 +#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 + TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); + if(idle_0 != NULL){ + ESP_ERROR_CHECK(esp_task_wdt_add(idle_0)) + } +#endif + //Add IDLE 1 to task wdt +#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 + TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1); + if(idle_1 != NULL){ + ESP_ERROR_CHECK(esp_task_wdt_add(idle_1)) + } +#endif +#endif + + app_main(); + vTaskDelete(NULL); +} + diff --git a/components/esp32s2beta/crosscore_int.c b/components/esp32s2beta/crosscore_int.c new file mode 100644 index 0000000000..3bfcc60bf9 --- /dev/null +++ b/components/esp32s2beta/crosscore_int.c @@ -0,0 +1,119 @@ +// 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. +#include +#include + +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_intr_alloc.h" + +#include "esp32s2beta/rom/ets_sys.h" +#include "esp32s2beta/rom/uart.h" + +#include "soc/cpu.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/periph_defs.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/portmacro.h" + + +#define REASON_YIELD BIT(0) +#define REASON_FREQ_SWITCH BIT(1) + +static portMUX_TYPE reason_spinlock = portMUX_INITIALIZER_UNLOCKED; +static volatile uint32_t reason[ portNUM_PROCESSORS ]; + +/* +ToDo: There is a small chance the CPU already has yielded when this ISR is serviced. In that case, it's running the intended task but +the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that. +*/ +static inline void IRAM_ATTR esp_crosscore_isr_handle_yield() +{ + portYIELD_FROM_ISR(); +} + +static void IRAM_ATTR esp_crosscore_isr(void *arg) { + uint32_t my_reason_val; + //A pointer to the correct reason array item is passed to this ISR. + volatile uint32_t *my_reason=arg; + + //Clear the interrupt first. + if (xPortGetCoreID()==0) { + DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); + } else { + DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0); + } + //Grab the reason and clear it. + portENTER_CRITICAL_ISR(&reason_spinlock); + my_reason_val=*my_reason; + *my_reason=0; + portEXIT_CRITICAL_ISR(&reason_spinlock); + + //Check what we need to do. + if (my_reason_val & REASON_YIELD) { + esp_crosscore_isr_handle_yield(); + } + if (my_reason_val & REASON_FREQ_SWITCH) { + /* Nothing to do here; the frequency switch event was already + * handled by a hook in xtensa_vectors.S. Could be used in the future + * to allow DFS features without the extra latency of the ISR hook. + */ + } +} + +//Initialize the crosscore interrupt on this core. Call this once +//on each active core. +void esp_crosscore_int_init() { + portENTER_CRITICAL(&reason_spinlock); + reason[xPortGetCoreID()]=0; + portEXIT_CRITICAL(&reason_spinlock); + esp_err_t err; + if (xPortGetCoreID()==0) { + err = esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[0], NULL); + } else { + err = esp_intr_alloc(ETS_FROM_CPU_INTR1_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[1], NULL); + } + assert(err == ESP_OK); +} + +static void IRAM_ATTR esp_crosscore_int_send(int core_id, uint32_t reason_mask) { + assert(core_id +#include + +#include +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "esp32s2beta/rom/ets_sys.h" +#include "esp32s2beta/rom/uart.h" + +#include "soc/cpu.h" +#include "soc/dport_reg.h" +#include "soc/spi_mem_reg.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/portmacro.h" + +#include "xtensa/core-macros.h" + +#ifndef CONFIG_FREERTOS_UNICORE +static portMUX_TYPE g_dport_mux = portMUX_INITIALIZER_UNLOCKED; + +#define DPORT_CORE_STATE_IDLE 0 +#define DPORT_CORE_STATE_RUNNING 1 +static uint32_t volatile dport_core_state[portNUM_PROCESSORS]; //cpu is already run + +/* these global variables are accessed from interrupt vector, hence not declared as static */ +uint32_t volatile dport_access_start[portNUM_PROCESSORS]; //dport register could be accessed +uint32_t volatile dport_access_end[portNUM_PROCESSORS]; //dport register is accessed over + +static uint32_t volatile dport_access_ref[portNUM_PROCESSORS]; //dport access reference + +#ifdef DPORT_ACCESS_BENCHMARK +#define DPORT_ACCESS_BENCHMARK_STORE_NUM +static uint32_t ccount_start[portNUM_PROCESSORS]; +static uint32_t ccount_end[portNUM_PROCESSORS]; +static uint32_t ccount_margin[portNUM_PROCESSORS][DPORT_ACCESS_BENCHMARK_STORE_NUM]; +static uint32_t ccount_margin_cnt; +#endif + + +static BaseType_t oldInterruptLevel[2]; +#endif // CONFIG_FREERTOS_UNICORE + +/* stall other cpu that this cpu is pending to access dport register start */ +void IRAM_ATTR esp_dport_access_stall_other_cpu_start(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + if (dport_core_state[0] == DPORT_CORE_STATE_IDLE + || dport_core_state[1] == DPORT_CORE_STATE_IDLE) { + return; + } + + BaseType_t intLvl = portENTER_CRITICAL_NESTED(); + + int cpu_id = xPortGetCoreID(); + +#ifdef DPORT_ACCESS_BENCHMARK + ccount_start[cpu_id] = XTHAL_GET_CCOUNT(); +#endif + + if (dport_access_ref[cpu_id] == 0) { + portENTER_CRITICAL_ISR(&g_dport_mux); + + oldInterruptLevel[cpu_id]=intLvl; + + dport_access_start[cpu_id] = 0; + dport_access_end[cpu_id] = 0; + + if (cpu_id == 0) { + _DPORT_REG_WRITE(DPORT_CPU_INTR_FROM_CPU_3_REG, DPORT_CPU_INTR_FROM_CPU_3); //interrupt on cpu1 + } else { + _DPORT_REG_WRITE(DPORT_CPU_INTR_FROM_CPU_2_REG, DPORT_CPU_INTR_FROM_CPU_2); //interrupt on cpu0 + } + + while (!dport_access_start[cpu_id]) {}; + + REG_READ(SPI_DATE_REG(3)); //just read a APB register sure that the APB-bus is idle + } + + dport_access_ref[cpu_id]++; + + if (dport_access_ref[cpu_id] > 1) { + /* Interrupts are already disabled by the parent, we're nested here. */ + portEXIT_CRITICAL_NESTED(intLvl); + } +#endif /* CONFIG_FREERTOS_UNICORE */ +} + +/* stall other cpu that this cpu is pending to access dport register end */ +void IRAM_ATTR esp_dport_access_stall_other_cpu_end(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + int cpu_id = xPortGetCoreID(); + + if (dport_core_state[0] == DPORT_CORE_STATE_IDLE + || dport_core_state[1] == DPORT_CORE_STATE_IDLE) { + return; + } + + if (dport_access_ref[cpu_id] == 0) { + assert(0); + } + + dport_access_ref[cpu_id]--; + + if (dport_access_ref[cpu_id] == 0) { + dport_access_end[cpu_id] = 1; + + portEXIT_CRITICAL_ISR(&g_dport_mux); + + portEXIT_CRITICAL_NESTED(oldInterruptLevel[cpu_id]); + } + +#ifdef DPORT_ACCESS_BENCHMARK + ccount_end[cpu_id] = XTHAL_GET_CCOUNT(); + ccount_margin[cpu_id][ccount_margin_cnt] = ccount_end[cpu_id] - ccount_start[cpu_id]; + ccount_margin_cnt = (ccount_margin_cnt + 1)&(DPORT_ACCESS_BENCHMARK_STORE_NUM - 1); +#endif +#endif /* CONFIG_FREERTOS_UNICORE */ +} + +void IRAM_ATTR esp_dport_access_stall_other_cpu_start_wrap(void) +{ + DPORT_STALL_OTHER_CPU_START(); +} + +void IRAM_ATTR esp_dport_access_stall_other_cpu_end_wrap(void) +{ + DPORT_STALL_OTHER_CPU_END(); +} + +#ifndef CONFIG_FREERTOS_UNICORE +static void dport_access_init_core(void *arg) +{ + int core_id = 0; + uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE; + + + core_id = xPortGetCoreID(); + if (core_id == 1) { + intr_source = ETS_FROM_CPU_INTR3_SOURCE; + } + + ESP_INTR_DISABLE(ETS_DPORT_INUM); + intr_matrix_set(core_id, intr_source, ETS_DPORT_INUM); + ESP_INTR_ENABLE(ETS_DPORT_INUM); + + dport_access_ref[core_id] = 0; + dport_access_start[core_id] = 0; + dport_access_end[core_id] = 0; + dport_core_state[core_id] = DPORT_CORE_STATE_RUNNING; + + vTaskDelete(NULL); +} +#endif + +/* Defer initialisation until after scheduler is running */ +void esp_dport_access_int_init(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + portBASE_TYPE res = xTaskCreatePinnedToCore(&dport_access_init_core, "dport", configMINIMAL_STACK_SIZE, NULL, 5, NULL, xPortGetCoreID()); + assert(res == pdTRUE); +#endif +} + +void IRAM_ATTR esp_dport_access_int_pause(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + portENTER_CRITICAL_ISR(&g_dport_mux); + dport_core_state[0] = DPORT_CORE_STATE_IDLE; + dport_core_state[1] = DPORT_CORE_STATE_IDLE; + portEXIT_CRITICAL_ISR(&g_dport_mux); +#endif +} + +//Used in panic code: the enter_critical stuff may be messed up so we just stop everything without checking the mux. +void IRAM_ATTR esp_dport_access_int_abort(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + dport_core_state[0] = DPORT_CORE_STATE_IDLE; + dport_core_state[1] = DPORT_CORE_STATE_IDLE; +#endif +} + +void IRAM_ATTR esp_dport_access_int_resume(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + portENTER_CRITICAL_ISR(&g_dport_mux); + dport_core_state[0] = DPORT_CORE_STATE_RUNNING; + dport_core_state[1] = DPORT_CORE_STATE_RUNNING; + portEXIT_CRITICAL_ISR(&g_dport_mux); +#endif +} + +/** + * @brief Read a sequence of DPORT registers to the buffer, SMP-safe version. + * + * This implementation uses a method of the pre-reading of the APB register + * before reading the register of the DPORT, without stall other CPU. + * There is disable/enable interrupt. + * + * @param[out] buff_out Contains the read data. + * @param[in] address Initial address for reading registers. + * @param[in] num_words The number of words. + */ +void IRAM_ATTR esp_dport_access_read_buffer(uint32_t *buff_out, uint32_t address, uint32_t num_words) +{ + DPORT_INTERRUPT_DISABLE(); + for (uint32_t i = 0; i < num_words; ++i) { + buff_out[i] = DPORT_SEQUENCE_REG_READ(address + i * 4); + } + DPORT_INTERRUPT_RESTORE(); +} + +/** + * @brief Read value from register, SMP-safe version. + * + * This method uses the pre-reading of the APB register before reading the register of the DPORT. + * This implementation is useful for reading DORT registers for single reading without stall other CPU. + * There is disable/enable interrupt. + * + * @param reg Register address + * @return Value + */ +uint32_t IRAM_ATTR esp_dport_access_reg_read(uint32_t reg) +{ +#if defined(BOOTLOADER_BUILD) || defined(CONFIG_FREERTOS_UNICORE) || !defined(ESP_PLATFORM) + return _DPORT_REG_READ(reg); +#else + uint32_t apb; + unsigned int intLvl; + __asm__ __volatile__ (\ + "movi %[APB], "XTSTR(0x3f400078)"\n"\ + "rsil %[LVL], "XTSTR(3)"\n"\ + "l32i %[APB], %[APB], 0\n"\ + "l32i %[REG], %[REG], 0\n"\ + "wsr %[LVL], "XTSTR(PS)"\n"\ + "rsync\n"\ + : [APB]"=a"(apb), [REG]"+a"(reg), [LVL]"=a"(intLvl)\ + : \ + : "memory" \ + ); + return reg; +#endif +} + +/** + * @brief Read value from register, NOT SMP-safe version. + * + * This method uses the pre-reading of the APB register before reading the register of the DPORT. + * There is not disable/enable interrupt. + * The difference from DPORT_REG_READ() is that the user himself must disable interrupts while DPORT reading. + * This implementation is useful for reading DORT registers in loop without stall other CPU. Note the usage example. + * The recommended way to read registers sequentially without stall other CPU + * is to use the method esp_dport_read_buffer(buff_out, address, num_words). It allows you to read registers in the buffer. + * + * \code{c} + * // This example shows how to use it. + * { // Use curly brackets to limit the visibility of variables in macros DPORT_INTERRUPT_DISABLE/RESTORE. + * DPORT_INTERRUPT_DISABLE(); // Disable interrupt only on current CPU. + * for (i = 0; i < max; ++i) { + * array[i] = esp_dport_access_sequence_reg_read(Address + i * 4); // reading DPORT registers + * } + * DPORT_INTERRUPT_RESTORE(); // restore the previous interrupt level + * } + * \endcode + * + * @param reg Register address + * @return Value + */ +uint32_t IRAM_ATTR esp_dport_access_sequence_reg_read(uint32_t reg) +{ +#if defined(BOOTLOADER_BUILD) || defined(CONFIG_FREERTOS_UNICORE) || !defined(ESP_PLATFORM) + return _DPORT_REG_READ(reg); +#else + uint32_t apb; + __asm__ __volatile__ (\ + "movi %[APB], "XTSTR(0x3f400078)"\n"\ + "l32i %[APB], %[APB], 0\n"\ + "l32i %[REG], %[REG], 0\n"\ + : [APB]"=a"(apb), [REG]"+a"(reg)\ + : \ + : "memory" \ + ); + return reg; +#endif +} diff --git a/components/esp32s2beta/dport_panic_highint_hdl.S b/components/esp32s2beta/dport_panic_highint_hdl.S new file mode 100644 index 0000000000..a329ec7d53 --- /dev/null +++ b/components/esp32s2beta/dport_panic_highint_hdl.S @@ -0,0 +1,203 @@ +// 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 +#include +#include +#include "freertos/xtensa_context.h" +#include "esp_private/panic_reason.h" +#include "sdkconfig.h" +#include "soc/soc.h" +#include "soc/dport_reg.h" + +/* + +Interrupt , a high-priority interrupt, is used for several things: +- Dport access mediation +- Cache error panic handler +- Interrupt watchdog panic handler + +*/ + +#define L4_INTR_STACK_SIZE 8 +#define L4_INTR_A2_OFFSET 0 +#define L4_INTR_A3_OFFSET 4 + .data +_l4_intr_stack: + .space L4_INTR_STACK_SIZE + + .section .iram1,"ax" + .global xt_highint4 + .type xt_highint4,@function + .align 4 +xt_highint4: + +#ifndef CONFIG_FREERTOS_UNICORE + /* See if we're here for the dport access interrupt */ + rsr a0, INTERRUPT + extui a0, a0, ETS_DPORT_INUM, 1 + bnez a0, .handle_dport_access_int +#endif // CONFIG_FREERTOS_UNICORE + + /* Allocate exception frame and save minimal context. */ + mov a0, sp + addi sp, sp, -XT_STK_FRMSZ + s32i a0, sp, XT_STK_A1 + #if XCHAL_HAVE_WINDOWED + s32e a0, sp, -12 /* for debug backtrace */ + #endif + rsr a0, PS /* save interruptee's PS */ + s32i a0, sp, XT_STK_PS + rsr a0, EPC_4 /* save interruptee's PC */ + s32i a0, sp, XT_STK_PC + #if XCHAL_HAVE_WINDOWED + s32e a0, sp, -16 /* for debug backtrace */ + #endif + s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ + s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ + call0 _xt_context_save + + /* Save vaddr into exception frame */ + rsr a0, EXCVADDR + s32i a0, sp, XT_STK_EXCVADDR + + /* Figure out reason, save into EXCCAUSE reg */ + + rsr a0, INTERRUPT + extui a0, a0, ETS_CACHEERR_INUM, 1 /* get cacheerr int bit */ + beqz a0, 1f + /* Kill this interrupt; we cannot reset it. */ + rsr a0, INTENABLE + movi a4, ~(1< timer_val && + time_base == *((volatile uint64_t*) &s_time_base_us) && + ticks_per_us == *((volatile uint32_t*) &s_timer_ticks_per_us) && + overflow == timer_overflow_happened()) { + break; + } + + /* If any value has changed (other than the counter increasing), read again */ + } while(true); + + uint64_t result = time_base + + timer_val / ticks_per_us; + return result; +} + +void IRAM_ATTR esp_timer_impl_set_alarm(uint64_t timestamp) +{ + portENTER_CRITICAL(&s_time_update_lock); + // Alarm time relative to the moment when counter was 0 + uint64_t time_after_timebase_us = timestamp - s_time_base_us; + // Adjust current time if overflow has happened + bool overflow = timer_overflow_happened(); + uint64_t cur_count = REG_READ(FRC_TIMER_COUNT_REG(1)); + + if (overflow) { + assert(time_after_timebase_us > s_timer_us_per_overflow); + time_after_timebase_us -= s_timer_us_per_overflow; + s_overflow_happened = true; + } + // Calculate desired timer compare value (may exceed 2^32-1) + uint64_t compare_val = time_after_timebase_us * s_timer_ticks_per_us; + uint32_t alarm_reg_val = ALARM_OVERFLOW_VAL; + // Use calculated alarm value if it is less than ALARM_OVERFLOW_VAL. + // Note that if by the time we update ALARM_REG, COUNT_REG value is higher, + // interrupt will not happen for another ALARM_OVERFLOW_VAL timer ticks, + // so need to check if alarm value is too close in the future (e.g. <2 us away). + const uint32_t offset = s_timer_ticks_per_us * 2; + if (compare_val < ALARM_OVERFLOW_VAL) { + if (compare_val < cur_count + offset) { + compare_val = cur_count + offset; + if (compare_val > ALARM_OVERFLOW_VAL) { + compare_val = ALARM_OVERFLOW_VAL; + } + } + alarm_reg_val = (uint32_t) compare_val; + } + REG_WRITE(FRC_TIMER_ALARM_REG(1), alarm_reg_val); + portEXIT_CRITICAL(&s_time_update_lock); +} + +static void IRAM_ATTR timer_alarm_isr(void *arg) +{ + portENTER_CRITICAL_ISR(&s_time_update_lock); + // Timekeeping: adjust s_time_base_us if counter has passed ALARM_OVERFLOW_VAL + if (timer_overflow_happened()) { + timer_count_reload(); + s_time_base_us += s_timer_us_per_overflow; + s_overflow_happened = false; + } + s_mask_overflow = false; + // Clear interrupt status + REG_WRITE(FRC_TIMER_INT_REG(1), FRC_TIMER_INT_CLR); + // Set alarm to the next overflow moment. Later, upper layer function may + // call esp_timer_impl_set_alarm to change this to an earlier value. + REG_WRITE(FRC_TIMER_ALARM_REG(1), ALARM_OVERFLOW_VAL); + portEXIT_CRITICAL_ISR(&s_time_update_lock); + // Call the upper layer handler + (*s_alarm_handler)(arg); +} + +void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us) +{ + portENTER_CRITICAL_ISR(&s_time_update_lock); + /* Bail out if the timer is not initialized yet */ + if (s_timer_interrupt_handle == NULL) { + portEXIT_CRITICAL_ISR(&s_time_update_lock); + return; + } + + uint32_t new_ticks_per_us = apb_ticks_per_us / TIMER_DIV; + uint32_t alarm = REG_READ(FRC_TIMER_ALARM_REG(1)); + uint32_t count = REG_READ(FRC_TIMER_COUNT_REG(1)); + uint64_t ticks_to_alarm = alarm - count; + uint64_t new_ticks = (ticks_to_alarm * new_ticks_per_us) / s_timer_ticks_per_us; + uint32_t new_alarm_val; + if (alarm > count && new_ticks <= ALARM_OVERFLOW_VAL) { + new_alarm_val = new_ticks; + } else { + new_alarm_val = ALARM_OVERFLOW_VAL; + if (alarm != ALARM_OVERFLOW_VAL) { + s_mask_overflow = true; + } + } + REG_WRITE(FRC_TIMER_ALARM_REG(1), new_alarm_val); + REG_WRITE(FRC_TIMER_LOAD_REG(1), 0); + + s_time_base_us += count / s_timer_ticks_per_us; + +#ifdef CONFIG_PM_DFS_USE_RTC_TIMER_REF + // Due to the extra time required to read RTC time, don't attempt this + // adjustment when switching to a higher frequency (which usually + // happens in an interrupt). + if (new_ticks_per_us < s_timer_ticks_per_us) { + uint64_t rtc_time = esp_clk_rtc_time(); + uint64_t new_rtc_time_diff = s_time_base_us - rtc_time; + if (s_rtc_time_diff != 0) { + uint64_t correction = new_rtc_time_diff - s_rtc_time_diff; + s_time_base_us -= correction; + } else { + s_rtc_time_diff = new_rtc_time_diff; + } + } +#endif // CONFIG_PM_DFS_USE_RTC_TIMER_REF + + s_timer_ticks_per_us = new_ticks_per_us; + s_timer_us_per_overflow = ALARM_OVERFLOW_VAL / new_ticks_per_us; + + portEXIT_CRITICAL_ISR(&s_time_update_lock); +} + +void esp_timer_impl_advance(int64_t time_us) +{ + assert(time_us > 0 && "negative adjustments not supported yet"); + + portENTER_CRITICAL(&s_time_update_lock); + uint64_t count = REG_READ(FRC_TIMER_COUNT_REG(1)); + /* Trigger an ISR to handle past alarms and set new one. + * ISR handler will run once we exit the critical section. + */ + REG_WRITE(FRC_TIMER_ALARM_REG(1), 0); + REG_WRITE(FRC_TIMER_LOAD_REG(1), 0); + s_time_base_us += count / s_timer_ticks_per_us + time_us; + s_overflow_happened = false; + portEXIT_CRITICAL(&s_time_update_lock); +} + +esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler) +{ + s_alarm_handler = alarm_handler; + + esp_err_t err = esp_intr_alloc(ETS_TIMER2_INTR_SOURCE, + ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM, + &timer_alarm_isr, NULL, &s_timer_interrupt_handle); + + if (err != ESP_OK) { + ESP_EARLY_LOGE(TAG, "esp_intr_alloc failed (0x%0x)", err); + return err; + } + + uint32_t apb_freq = rtc_clk_apb_freq_get(); + s_timer_ticks_per_us = apb_freq / 1000000 / TIMER_DIV; + assert(s_timer_ticks_per_us > 0 + && apb_freq % TIMER_DIV == 0 + && "APB frequency does not result in a valid ticks_per_us value"); + s_timer_us_per_overflow = ALARM_OVERFLOW_VAL / s_timer_ticks_per_us; + s_time_base_us = 0; + + REG_WRITE(FRC_TIMER_ALARM_REG(1), ALARM_OVERFLOW_VAL); + REG_WRITE(FRC_TIMER_LOAD_REG(1), 0); + REG_WRITE(FRC_TIMER_CTRL_REG(1), + TIMER_DIV_CFG | FRC_TIMER_ENABLE | FRC_TIMER_LEVEL_INT); + REG_WRITE(FRC_TIMER_INT_REG(1), FRC_TIMER_INT_CLR); + ESP_ERROR_CHECK( esp_intr_enable(s_timer_interrupt_handle) ); + + return ESP_OK; +} + +void esp_timer_impl_deinit() +{ + esp_intr_disable(s_timer_interrupt_handle); + + REG_WRITE(FRC_TIMER_CTRL_REG(1), 0); + REG_WRITE(FRC_TIMER_ALARM_REG(1), 0); + REG_WRITE(FRC_TIMER_LOAD_REG(1), 0); + + esp_intr_free(s_timer_interrupt_handle); + s_timer_interrupt_handle = NULL; +} + +// FIXME: This value is safe for 80MHz APB frequency. +// Should be modified to depend on clock frequency. + +uint64_t IRAM_ATTR esp_timer_impl_get_min_period_us() +{ + return 50; +} + +#ifdef ESP_TIMER_DYNAMIC_OVERFLOW_VAL +uint32_t esp_timer_impl_get_overflow_val() +{ + return s_alarm_overflow_val; +} + +void esp_timer_impl_set_overflow_val(uint32_t overflow_val) +{ + s_alarm_overflow_val = overflow_val; + /* update alarm value */ + esp_timer_impl_update_apb_freq(esp_clk_apb_freq() / 1000000); +} +#endif // ESP_TIMER_DYNAMIC_OVERFLOW_VAL diff --git a/components/esp32s2beta/gdbstub.c b/components/esp32s2beta/gdbstub.c new file mode 100644 index 0000000000..38d87c0d87 --- /dev/null +++ b/components/esp32s2beta/gdbstub.c @@ -0,0 +1,356 @@ +// 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. + +/****************************************************************************** + * Description: A stub to make the ESP32 debuggable by GDB over the serial + * port, at least enough to do a backtrace on panic. This gdbstub is read-only: + * it allows inspecting the ESP32 state + *******************************************************************************/ + +#include "esp32s2beta/rom/ets_sys.h" +#include "soc/uart_reg.h" +#include "soc/io_mux_reg.h" +#include "esp_private/gdbstub.h" +#include "driver/gpio.h" + +//Length of buffer used to reserve GDB commands. Has to be at least able to fit the G command, which +//implies a minimum size of about 320 bytes. +#define PBUFLEN 512 + +static unsigned char cmd[PBUFLEN]; //GDB command input buffer +static char chsum; //Running checksum of the output packet + +#define ATTR_GDBFN + +//Receive a char from the uart. Uses polling and feeds the watchdog. +static int ATTR_GDBFN gdbRecvChar() { + int i; + while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT)==0) ; + i=READ_PERI_REG(UART_FIFO_AHB_REG(0)); + return i; +} + +//Send a char to the uart. +static void ATTR_GDBFN gdbSendChar(char c) { + while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ; + WRITE_PERI_REG(UART_FIFO_AHB_REG(0), c); +} + +//Send the start of a packet; reset checksum calculation. +static void ATTR_GDBFN gdbPacketStart() { + chsum=0; + gdbSendChar('$'); +} + +//Send a char as part of a packet +static void ATTR_GDBFN gdbPacketChar(char c) { + if (c=='#' || c=='$' || c=='}' || c=='*') { + gdbSendChar('}'); + gdbSendChar(c^0x20); + chsum+=(c^0x20)+'}'; + } else { + gdbSendChar(c); + chsum+=c; + } +} + +//Send a string as part of a packet +static void ATTR_GDBFN gdbPacketStr(const char *c) { + while (*c!=0) { + gdbPacketChar(*c); + c++; + } +} + +//Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent. +static void ATTR_GDBFN gdbPacketHex(int val, int bits) { + char hexChars[]="0123456789abcdef"; + int i; + for (i=bits; i>0; i-=4) { + gdbPacketChar(hexChars[(val>>(i-4))&0xf]); + } +} + +//Finish sending a packet. +static void ATTR_GDBFN gdbPacketEnd() { + gdbSendChar('#'); + gdbPacketHex(chsum, 8); +} + +//Error states used by the routines that grab stuff from the incoming gdb packet +#define ST_ENDPACKET -1 +#define ST_ERR -2 +#define ST_OK -3 +#define ST_CONT -4 + +//Grab a hex value from the gdb packet. Ptr will get positioned on the end +//of the hex string, as far as the routine has read into it. Bits/4 indicates +//the max amount of hex chars it gobbles up. Bits can be -1 to eat up as much +//hex chars as possible. +static long ATTR_GDBFN gdbGetHexVal(unsigned char **ptr, int bits) { + int i; + int no; + unsigned int v=0; + char c; + no=bits/4; + if (bits==-1) no=64; + for (i=0; i='0' && c<='9') { + v<<=4; + v|=(c-'0'); + } else if (c>='A' && c<='F') { + v<<=4; + v|=(c-'A')+10; + } else if (c>='a' && c<='f') { + v<<=4; + v|=(c-'a')+10; + } else if (c=='#') { + if (bits==-1) { + (*ptr)--; + return v; + } + return ST_ENDPACKET; + } else { + if (bits==-1) { + (*ptr)--; + return v; + } + return ST_ERR; + } + } + return v; +} + +//Swap an int into the form gdb wants it +static int ATTR_GDBFN iswap(int i) { + int r; + r=((i>>24)&0xff); + r|=((i>>16)&0xff)<<8; + r|=((i>>8)&0xff)<<16; + r|=((i>>0)&0xff)<<24; + return r; +} + +//Read a byte from ESP32 memory. +static unsigned char ATTR_GDBFN readbyte(unsigned int p) { + int *i=(int*)(p&(~3)); + if (p<0x20000000 || p>=0x80000000) return -1; + return *i>>((p&3)*8); +} + + +//Register file in the format exp108 gdb port expects it. +//Inspired by gdb/regformats/reg-xtensa.dat +typedef struct { + uint32_t pc; + uint32_t a[64]; + uint32_t lbeg; + uint32_t lend; + uint32_t lcount; + uint32_t sar; + uint32_t windowbase; + uint32_t windowstart; + uint32_t configid0; + uint32_t configid1; + uint32_t ps; + uint32_t threadptr; + uint32_t br; + uint32_t scompare1; + uint32_t acclo; + uint32_t acchi; + uint32_t m0; + uint32_t m1; + uint32_t m2; + uint32_t m3; + uint32_t expstate; //I'm going to assume this is exccause... + uint32_t f64r_lo; + uint32_t f64r_hi; + uint32_t f64s; + uint32_t f[16]; + uint32_t fcr; + uint32_t fsr; +} GdbRegFile; + + +GdbRegFile gdbRegFile; + +/* +//Register format as the Xtensa HAL has it: +STRUCT_FIELD (long, 4, XT_STK_EXIT, exit) +STRUCT_FIELD (long, 4, XT_STK_PC, pc) +STRUCT_FIELD (long, 4, XT_STK_PS, ps) +STRUCT_FIELD (long, 4, XT_STK_A0, a0) +[..] +STRUCT_FIELD (long, 4, XT_STK_A15, a15) +STRUCT_FIELD (long, 4, XT_STK_SAR, sar) +STRUCT_FIELD (long, 4, XT_STK_EXCCAUSE, exccause) +STRUCT_FIELD (long, 4, XT_STK_EXCVADDR, excvaddr) +STRUCT_FIELD (long, 4, XT_STK_LBEG, lbeg) +STRUCT_FIELD (long, 4, XT_STK_LEND, lend) +STRUCT_FIELD (long, 4, XT_STK_LCOUNT, lcount) +// Temporary space for saving stuff during window spill +STRUCT_FIELD (long, 4, XT_STK_TMP0, tmp0) +STRUCT_FIELD (long, 4, XT_STK_TMP1, tmp1) +STRUCT_FIELD (long, 4, XT_STK_TMP2, tmp2) +STRUCT_FIELD (long, 4, XT_STK_VPRI, vpri) +STRUCT_FIELD (long, 4, XT_STK_OVLY, ovly) +#endif +STRUCT_END(XtExcFrame) +*/ + + +static void dumpHwToRegfile(XtExcFrame *frame) { + int i; + long *frameAregs=&frame->a0; + gdbRegFile.pc=frame->pc; + for (i=0; i<16; i++) gdbRegFile.a[i]=frameAregs[i]; + for (i=16; i<64; i++) gdbRegFile.a[i]=0xDEADBEEF; + gdbRegFile.sar=frame->sar; + //All windows have been spilled to the stack by the ISR routines. The following values should indicate that. + gdbRegFile.sar=frame->sar; + gdbRegFile.windowbase=0; //0 + gdbRegFile.windowstart=0x1; //1 + gdbRegFile.configid0=0xdeadbeef; //ToDo + gdbRegFile.configid1=0xdeadbeef; //ToDo + gdbRegFile.ps=frame->ps-PS_EXCM_MASK; + gdbRegFile.threadptr=0xdeadbeef; //ToDo + gdbRegFile.br=0xdeadbeef; //ToDo + gdbRegFile.scompare1=0xdeadbeef; //ToDo + gdbRegFile.acclo=0xdeadbeef; //ToDo + gdbRegFile.acchi=0xdeadbeef; //ToDo + gdbRegFile.m0=0xdeadbeef; //ToDo + gdbRegFile.m1=0xdeadbeef; //ToDo + gdbRegFile.m2=0xdeadbeef; //ToDo + gdbRegFile.m3=0xdeadbeef; //ToDo + gdbRegFile.expstate=frame->exccause; //ToDo +} + + +//Send the reason execution is stopped to GDB. +static void sendReason() { + //exception-to-signal mapping + char exceptionSignal[]={4,31,11,11,2,6,8,0,6,7,0,0,7,7,7,7}; + int i=0; + gdbPacketStart(); + gdbPacketChar('T'); + i=gdbRegFile.expstate&0x7f; + if (i=PBUFLEN) return ST_ERR; + } + //A # has been received. Get and check the received chsum. + sentchs[0]=gdbRecvChar(); + sentchs[1]=gdbRecvChar(); + ptr=&sentchs[0]; + rchsum=gdbGetHexVal(&ptr, 8); + if (rchsum!=chsum) { + gdbSendChar('-'); + return ST_ERR; + } else { + gdbSendChar('+'); + return gdbHandleCommand(cmd, p); + } +} + + + +void esp_gdbstub_panic_handler(XtExcFrame *frame) { + dumpHwToRegfile(frame); + //Make sure txd/rxd are enabled + gpio_pullup_dis(1); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD); + + sendReason(); + while(gdbReadCommand()!=ST_CONT); + while(1); +} + diff --git a/components/esp32s2beta/hw_random.c b/components/esp32s2beta/hw_random.c new file mode 100644 index 0000000000..b2c6418491 --- /dev/null +++ b/components/esp32s2beta/hw_random.c @@ -0,0 +1,70 @@ +// Copyright 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. + + +#include +#include +#include +#include +#include "esp_attr.h" +#include "esp32s2beta/clk.h" +#include "soc/wdev_reg.h" +#include "freertos/FreeRTOSConfig.h" +#include "xtensa/core-macros.h" + +uint32_t IRAM_ATTR esp_random(void) +{ + /* The PRNG which implements WDEV_RANDOM register gets 2 bits + * of extra entropy from a hardware randomness source every APB clock cycle + * (provided WiFi or BT are enabled). To make sure entropy is not drained + * faster than it is added, this function needs to wait for at least 16 APB + * clock cycles after reading previous word. This implementation may actually + * wait a bit longer due to extra time spent in arithmetic and branch statements. + * + * As a (probably unncessary) precaution to avoid returning the + * RNG state as-is, the result is XORed with additional + * WDEV_RND_REG reads while waiting. + */ + + /* This code does not run in a critical section, so CPU frequency switch may + * happens while this code runs (this will not happen in the current + * implementation, but possible in the future). However if that happens, + * the number of cycles spent on frequency switching will certainly be more + * than the number of cycles we need to wait here. + */ + uint32_t cpu_to_apb_freq_ratio = esp_clk_cpu_freq() / esp_clk_apb_freq(); + + static uint32_t last_ccount = 0; + uint32_t ccount; + uint32_t result = 0; + do { + ccount = XTHAL_GET_CCOUNT(); + result ^= REG_READ(WDEV_RND_REG); + } while (ccount - last_ccount < cpu_to_apb_freq_ratio * 16); + last_ccount = ccount; + return result ^ REG_READ(WDEV_RND_REG); +} + +void esp_fill_random(void *buf, size_t len) +{ + assert(buf != NULL); + uint8_t *buf_bytes = (uint8_t *)buf; + while (len > 0) { + uint32_t word = esp_random(); + uint32_t to_copy = MIN(sizeof(word), len); + memcpy(buf_bytes, &word, to_copy); + buf_bytes += to_copy; + len -= to_copy; + } +} diff --git a/components/esp32s2beta/include/esp32s2beta/brownout.h b/components/esp32s2beta/include/esp32s2beta/brownout.h new file mode 100644 index 0000000000..5a0b1aec00 --- /dev/null +++ b/components/esp32s2beta/include/esp32s2beta/brownout.h @@ -0,0 +1,21 @@ +// 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 __ESP_BROWNOUT_H +#define __ESP_BROWNOUT_H + +void esp_brownout_init(); + +#endif \ No newline at end of file diff --git a/components/esp32s2beta/include/esp32s2beta/cache_err_int.h b/components/esp32s2beta/include/esp32s2beta/cache_err_int.h new file mode 100644 index 0000000000..bcbd63e799 --- /dev/null +++ b/components/esp32s2beta/include/esp32s2beta/cache_err_int.h @@ -0,0 +1,33 @@ +// 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. + + +/** + * @brief initialize cache invalid access interrupt + * + * This function enables cache invalid access interrupt source and connects it + * to interrupt input number ETS_CACHEERR_INUM (see soc/soc.h). It is called + * from the startup code. + */ +void esp_cache_err_int_init(); + + +/** + * @brief get the CPU which caused cache invalid access interrupt + * @return + * - PRO_CPU_NUM, if PRO_CPU has caused cache IA interrupt + * - APP_CPU_NUM, if APP_CPU has caused cache IA interrupt + * - (-1) otherwise + */ +int esp_cache_err_get_cpuid(); diff --git a/components/esp32s2beta/include/esp32s2beta/clk.h b/components/esp32s2beta/include/esp32s2beta/clk.h new file mode 100644 index 0000000000..6526aa9272 --- /dev/null +++ b/components/esp32s2beta/include/esp32s2beta/clk.h @@ -0,0 +1,75 @@ +// 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. + +#pragma once + +/** + * @file esp_clk.h + * + * This file contains declarations of clock related functions. + */ + +/** + * @brief Get the calibration value of RTC slow clock + * + * The value is in the same format as returned by rtc_clk_cal (microseconds, + * in Q13.19 fixed-point format). + * + * @return the calibration value obtained using rtc_clk_cal, at startup time + */ +uint32_t esp_clk_slowclk_cal_get(); + +/** + * @brief Update the calibration value of RTC slow clock + * + * The value has to be in the same format as returned by rtc_clk_cal (microseconds, + * in Q13.19 fixed-point format). + * This value is used by timekeeping functions (such as gettimeofday) to + * calculate current time based on RTC counter value. + * @param value calibration value obtained using rtc_clk_cal + */ +void esp_clk_slowclk_cal_set(uint32_t value); + +/** + * @brief Return current CPU clock frequency + * When frequency switching is performed, this frequency may change. + * However it is guaranteed that the frequency never changes with a critical + * section. + * + * @return CPU clock frequency, in Hz + */ +int esp_clk_cpu_freq(void); + +/** + * @brief Return current APB clock frequency + * + * When frequency switching is performed, this frequency may change. + * However it is guaranteed that the frequency never changes with a critical + * section. + * + * @return APB clock frequency, in Hz + */ +int esp_clk_apb_freq(void); + + +/** + * @brief Read value of RTC counter, converting it to microseconds + * @attention The value returned by this function may change abruptly when + * calibration value of RTC counter is updated via esp_clk_slowclk_cal_set + * function. This should not happen unless application calls esp_clk_slowclk_cal_set. + * In ESP-IDF, esp_clk_slowclk_cal_set is only called in startup code. + * + * @return Value or RTC counter, expressed in microseconds + */ +uint64_t esp_clk_rtc_time(); diff --git a/components/esp32s2beta/include/esp32s2beta/dport_access.h b/components/esp32s2beta/include/esp32s2beta/dport_access.h new file mode 100644 index 0000000000..d5da5b8d80 --- /dev/null +++ b/components/esp32s2beta/include/esp32s2beta/dport_access.h @@ -0,0 +1,52 @@ +// Copyright 2010-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 + +#ifndef _ESP_DPORT_ACCESS_H_ +#define _ESP_DPORT_ACCESS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void esp_dport_access_stall_other_cpu_start(void); +void esp_dport_access_stall_other_cpu_end(void); +void esp_dport_access_int_init(void); +void esp_dport_access_int_pause(void); +void esp_dport_access_int_resume(void); +void esp_dport_access_read_buffer(uint32_t *buff_out, uint32_t address, uint32_t num_words); +uint32_t esp_dport_access_reg_read(uint32_t reg); +uint32_t esp_dport_access_sequence_reg_read(uint32_t reg); +//This routine does not stop the dport routines in any way that is recoverable. Please +//only call in case of panic(). +void esp_dport_access_int_abort(void); + +#if defined(BOOTLOADER_BUILD) || defined(CONFIG_FREERTOS_UNICORE) || !defined(ESP_PLATFORM) || !defined(CONFIG_CHIP_IS_ESP32) +#define DPORT_STALL_OTHER_CPU_START() +#define DPORT_STALL_OTHER_CPU_END() +#define DPORT_INTERRUPT_DISABLE() +#define DPORT_INTERRUPT_RESTORE() +#else +#define DPORT_STALL_OTHER_CPU_START() esp_dport_access_stall_other_cpu_start() +#define DPORT_STALL_OTHER_CPU_END() esp_dport_access_stall_other_cpu_end() +#define DPORT_INTERRUPT_DISABLE() unsigned int intLvl = XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL) +#define DPORT_INTERRUPT_RESTORE() XTOS_RESTORE_JUST_INTLEVEL(intLvl) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP_DPORT_ACCESS_H_ */ diff --git a/components/esp32s2beta/include/esp32s2beta/pm.h b/components/esp32s2beta/include/esp32s2beta/pm.h new file mode 100644 index 0000000000..a7cbf0eac7 --- /dev/null +++ b/components/esp32s2beta/include/esp32s2beta/pm.h @@ -0,0 +1,42 @@ +// Copyright 2016-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. + + +#pragma once +#include +#include +#include "esp_err.h" + +#include "soc/rtc.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @brief Power management config for ESP32 + * + * Pass a pointer to this structure as an argument to esp_pm_configure function. + */ +typedef struct { + rtc_cpu_freq_t max_cpu_freq; /*!< Maximum CPU frequency to use */ + rtc_cpu_freq_t min_cpu_freq; /*!< Minimum CPU frequency to use when no frequency locks are taken */ + bool light_sleep_enable; /*!< Enter light sleep when no locks are taken */ +} esp_pm_config_esp32_t; + + +#ifdef __cplusplus +} +#endif diff --git a/components/esp32s2beta/include/esp32s2beta/spiram.h b/components/esp32s2beta/include/esp32s2beta/spiram.h new file mode 100644 index 0000000000..9663dcddcb --- /dev/null +++ b/components/esp32s2beta/include/esp32s2beta/spiram.h @@ -0,0 +1,90 @@ +// 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 +#include +#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 Configure Cache/MMU for access to external SPI RAM. + * + * Normally this function is called from cpu_start, if CONFIG_SPIRAM_BOOT_INIT + * option is enabled. Applications which need to enable SPI RAM at run time + * can disable CONFIG_SPIRAM_BOOT_INIT, and call this function later. + * + * @attention this function must be called with flash cache disabled. + */ +void esp_spiram_init_cache(); + + +/** + * @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 Add the initialized SPI RAM to the heap allocator. + */ +esp_err_t esp_spiram_add_to_heapalloc(); + + +/** + * @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(); + + + +/** + * @brief Reserve a pool of internal memory for specific DMA/internal allocations + * + * @param size Size of reserved pool in bytes + * + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM when no memory available for pool + */ +esp_err_t esp_spiram_reserve_dma_pool(size_t size); + + +#endif diff --git a/components/esp32s2beta/include/esp_attr.h b/components/esp32s2beta/include/esp_attr.h new file mode 100644 index 0000000000..5bf9a22926 --- /dev/null +++ b/components/esp32s2beta/include/esp_attr.h @@ -0,0 +1,58 @@ +// 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 __ESP_ATTR_H__ +#define __ESP_ATTR_H__ + +#define ROMFN_ATTR + +//Normally, the linker script will put all code and rodata in flash, +//and all variables in shared RAM. These macros can be used to redirect +//particular functions/variables to other memory regions. + +// Forces code into IRAM instead of flash. +#define IRAM_ATTR __attribute__((section(".iram1"))) + +// Forces data into DRAM instead of flash +#define DRAM_ATTR __attribute__((section(".dram1"))) + +// Forces data to be 4 bytes aligned +#define WORD_ALIGNED_ATTR __attribute__((aligned(4))) + +// Forces data to be placed to DMA-capable places +#define DMA_ATTR WORD_ALIGNED_ATTR DRAM_ATTR + +// Forces a string into DRAM instead of flash +// Use as ets_printf(DRAM_STR("Hello world!\n")); +#define DRAM_STR(str) (__extension__({static const DRAM_ATTR char __c[] = (str); (const char *)&__c;})) + +// Forces code into RTC fast memory. See "docs/deep-sleep-stub.rst" +#define RTC_IRAM_ATTR __attribute__((section(".rtc.text"))) + +// Forces data into RTC slow memory. See "docs/deep-sleep-stub.rst" +// Any variable marked with this attribute will keep its value +// during a deep sleep / wake cycle. +#define RTC_DATA_ATTR __attribute__((section(".rtc.data"))) + +// Forces read-only data into RTC slow memory. See "docs/deep-sleep-stub.rst" +#define RTC_RODATA_ATTR __attribute__((section(".rtc.rodata"))) + +// Forces data into noinit section to avoid initialization after restart. +#define __NOINIT_ATTR __attribute__((section(".noinit"))) + +// Forces data into RTC slow memory of .noinit section. +// Any variable marked with this attribute will keep its value +// after restart or during a deep sleep / wake cycle. +#define RTC_NOINIT_ATTR __attribute__((section(".rtc_noinit"))) + +#endif /* __ESP_ATTR_H__ */ diff --git a/components/esp32s2beta/include/esp_clk.h b/components/esp32s2beta/include/esp_clk.h new file mode 100644 index 0000000000..6526aa9272 --- /dev/null +++ b/components/esp32s2beta/include/esp_clk.h @@ -0,0 +1,75 @@ +// 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. + +#pragma once + +/** + * @file esp_clk.h + * + * This file contains declarations of clock related functions. + */ + +/** + * @brief Get the calibration value of RTC slow clock + * + * The value is in the same format as returned by rtc_clk_cal (microseconds, + * in Q13.19 fixed-point format). + * + * @return the calibration value obtained using rtc_clk_cal, at startup time + */ +uint32_t esp_clk_slowclk_cal_get(); + +/** + * @brief Update the calibration value of RTC slow clock + * + * The value has to be in the same format as returned by rtc_clk_cal (microseconds, + * in Q13.19 fixed-point format). + * This value is used by timekeeping functions (such as gettimeofday) to + * calculate current time based on RTC counter value. + * @param value calibration value obtained using rtc_clk_cal + */ +void esp_clk_slowclk_cal_set(uint32_t value); + +/** + * @brief Return current CPU clock frequency + * When frequency switching is performed, this frequency may change. + * However it is guaranteed that the frequency never changes with a critical + * section. + * + * @return CPU clock frequency, in Hz + */ +int esp_clk_cpu_freq(void); + +/** + * @brief Return current APB clock frequency + * + * When frequency switching is performed, this frequency may change. + * However it is guaranteed that the frequency never changes with a critical + * section. + * + * @return APB clock frequency, in Hz + */ +int esp_clk_apb_freq(void); + + +/** + * @brief Read value of RTC counter, converting it to microseconds + * @attention The value returned by this function may change abruptly when + * calibration value of RTC counter is updated via esp_clk_slowclk_cal_set + * function. This should not happen unless application calls esp_clk_slowclk_cal_set. + * In ESP-IDF, esp_clk_slowclk_cal_set is only called in startup code. + * + * @return Value or RTC counter, expressed in microseconds + */ +uint64_t esp_clk_rtc_time(); diff --git a/components/esp32s2beta/include/esp_intr.h b/components/esp32s2beta/include/esp_intr.h new file mode 100644 index 0000000000..579eb6353d --- /dev/null +++ b/components/esp32s2beta/include/esp_intr.h @@ -0,0 +1,89 @@ +// Copyright 2010-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 __ESP_INTR_H__ +#define __ESP_INTR_H__ + +#include "rom/ets_sys.h" +#include "freertos/xtensa_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_CCOMPARE_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_CCOMPARE_INUM, (func), (void *)(arg)) + +#define ESP_EPWM_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_EPWM_INUM, (func), (void *)(arg)) + +#define ESP_MPWM_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_MPWM_INUM, (func), (void *)(arg)) + +#define ESP_SPI1_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_SPI1_INUM, (func), (void *)(arg)) + +#define ESP_SPI2_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_SPI2_INUM, (func), (void *)(arg)) + +#define ESP_SPI3_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_SPI3_INUM, (func), (void *)(arg)) + +#define ESP_I2S0_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_I2S0_INUM, (func), (void *)(arg)) + +#define ESP_PCNT_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_PCNT_INUM, (func), (void *)(arg)) + +#define ESP_LEDC_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_LEDC_INUM, (func), (void *)(arg)) + +#define ESP_WMAC_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_WMAC_INUM, (func), (void *)(arg)) + +#define ESP_FRC_TIMER1_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_FRC_TIMER1_INUM, (func), (void *)(arg)) + +#define ESP_FRC_TIMER2_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_FRC_TIMER2_INUM, (func), (void *)(arg)) + +#define ESP_GPIO_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_GPIO_INUM, (func), (void *)(arg)) + +#define ESP_UART0_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_UART0_INUM, (func), (void *)(arg)) + +#define ESP_WDT_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_WDT_INUM, (func), (void *)(arg)) + +#define ESP_RTC_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_RTC_INUM, (func), (void *)(arg)) + +#define ESP_SLC_INTR_ATTACH(func, arg) \ + xt_set_interrupt_handler(ETS_SLC_INUM, (func), (void *)(arg)) + +#define ESP_RMT_CTRL_INTRL(func,arg)\ + xt_set_interrupt_handler(ETS_RMT_CTRL_INUM, (func), (void *)(arg)) + +#define ESP_INTR_ENABLE(inum) \ + xt_ints_on((1< +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @addtogroup Intr_Alloc + * @{ + */ + + +/** @brief Interrupt allocation flags + * + * These flags can be used to specify which interrupt qualities the + * code calling esp_intr_alloc* needs. + * + */ + +//Keep the LEVELx values as they are here; they match up with (1<3 + * is requested, because these types of interrupts aren't C-callable. + * @param arg Optional argument for passed to the interrupt handler + * @param ret_handle Pointer to an intr_handle_t to store a handle that can later be + * used to request details or free the interrupt. Can be NULL if no handle + * is required. + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * ESP_OK otherwise + */ +esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle); + + +/** + * @brief Allocate an interrupt with the given parameters. + * + * + * This essentially does the same as esp_intr_alloc, but allows specifying a register and mask + * combo. For shared interrupts, the handler is only called if a read from the specified + * register, ANDed with the mask, returns non-zero. By passing an interrupt status register + * address and a fitting mask, this can be used to accelerate interrupt handling in the case + * a shared interrupt is triggered; by checking the interrupt statuses first, the code can + * decide which ISRs can be skipped + * + * @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux + * sources, as defined in soc/soc.h, or one of the internal + * ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header. + * @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the + * choice of interrupts that this routine can choose from. If this value + * is 0, it will default to allocating a non-shared interrupt of level + * 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared + * interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return + * from this function with the interrupt disabled. + * @param intrstatusreg The address of an interrupt status register + * @param intrstatusmask A mask. If a read of address intrstatusreg has any of the bits + * that are 1 in the mask set, the ISR will be called. If not, it will be + * skipped. + * @param handler The interrupt handler. Must be NULL when an interrupt of level >3 + * is requested, because these types of interrupts aren't C-callable. + * @param arg Optional argument for passed to the interrupt handler + * @param ret_handle Pointer to an intr_handle_t to store a handle that can later be + * used to request details or free the interrupt. Can be NULL if no handle + * is required. + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * ESP_OK otherwise + */ +esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, void *arg, intr_handle_t *ret_handle); + + +/** + * @brief Disable and free an interrupt. + * + * Use an interrupt handle to disable the interrupt and release the resources + * associated with it. + * + * @note + * When the handler shares its source with other handlers, the interrupt status + * bits it's responsible for should be managed properly before freeing it. see + * ``esp_intr_disable`` for more details. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if handle is invalid, or esp_intr_free runs on another core than + * where the interrupt is allocated on. + * ESP_OK otherwise + */ +esp_err_t esp_intr_free(intr_handle_t handle); + + +/** + * @brief Get CPU number an interrupt is tied to + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return The core number where the interrupt is allocated + */ +int esp_intr_get_cpu(intr_handle_t handle); + +/** + * @brief Get the allocated interrupt for a certain handle + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return The interrupt number + */ +int esp_intr_get_intno(intr_handle_t handle); + +/** + * @brief Disable the interrupt associated with the handle + * + * @note + * 1. For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the + * CPU the interrupt is allocated on. Other interrupts have no such restriction. + * 2. When several handlers sharing a same interrupt source, interrupt status bits, which are + * handled in the handler to be disabled, should be masked before the disabling, or handled + * in other enabled interrupts properly. Miss of interrupt status handling will cause infinite + * interrupt calls and finally system crash. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_OK otherwise + */ +esp_err_t esp_intr_disable(intr_handle_t handle); + +/** + * @brief Enable the interrupt associated with the handle + * + * @note For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the + * CPU the interrupt is allocated on. Other interrupts have no such restriction. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_OK otherwise + */ +esp_err_t esp_intr_enable(intr_handle_t handle); + +/** + * @brief Set the "in IRAM" status of the handler. + * + * @note Does not work on shared interrupts. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * @param is_in_iram Whether the handler associated with this handle resides in IRAM. + * Handlers residing in IRAM can be called when cache is disabled. + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_OK otherwise + */ +esp_err_t esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram); + +/** + * @brief Disable interrupts that aren't specifically marked as running from IRAM + */ +void esp_intr_noniram_disable(); + + +/** + * @brief Re-enable interrupts disabled by esp_intr_noniram_disable + */ +void esp_intr_noniram_enable(); + +/**@}*/ + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/esp32s2beta/include/esp_sleep.h b/components/esp32s2beta/include/esp_sleep.h new file mode 100644 index 0000000000..8e50c7e20a --- /dev/null +++ b/components/esp32s2beta/include/esp_sleep.h @@ -0,0 +1,325 @@ +// 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. + +#pragma once + +#include +#include "esp_err.h" +#include "driver/gpio.h" +#include "driver/touch_pad.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Logic function used for EXT1 wakeup mode. + */ +typedef enum { + ESP_EXT1_WAKEUP_ALL_LOW = 0, //!< Wake the chip when all selected GPIOs go low + ESP_EXT1_WAKEUP_ANY_HIGH = 1 //!< Wake the chip when any of the selected GPIOs go high +} esp_sleep_ext1_wakeup_mode_t; + +/** + * @brief Power domains which can be powered down in sleep mode + */ +typedef enum { + ESP_PD_DOMAIN_RTC_PERIPH, //!< RTC IO, sensors and ULP co-processor + ESP_PD_DOMAIN_RTC_SLOW_MEM, //!< RTC slow memory + ESP_PD_DOMAIN_RTC_FAST_MEM, //!< RTC fast memory + ESP_PD_DOMAIN_XTAL, //!< XTAL oscillator + ESP_PD_DOMAIN_MAX //!< Number of domains +} esp_sleep_pd_domain_t; + +/** + * @brief Power down options + */ +typedef enum { + ESP_PD_OPTION_OFF, //!< Power down the power domain in sleep mode + ESP_PD_OPTION_ON, //!< Keep power domain enabled during sleep mode + ESP_PD_OPTION_AUTO //!< Keep power domain enabled in sleep mode, if it is needed by one of the wakeup options. Otherwise power it down. +} esp_sleep_pd_option_t; + +/** + * @brief Sleep wakeup cause + */ +typedef enum { + ESP_SLEEP_WAKEUP_UNDEFINED, //!< In case of deep sleep, reset was not caused by exit from deep sleep + ESP_SLEEP_WAKEUP_EXT0, //!< Wakeup caused by external signal using RTC_IO + ESP_SLEEP_WAKEUP_EXT1, //!< Wakeup caused by external signal using RTC_CNTL + ESP_SLEEP_WAKEUP_TIMER, //!< Wakeup caused by timer + ESP_SLEEP_WAKEUP_TOUCHPAD, //!< Wakeup caused by touchpad + ESP_SLEEP_WAKEUP_ULP, //!< Wakeup caused by ULP program +} esp_sleep_source_t; + +/* Leave this type define for compatibility */ +typedef esp_sleep_source_t esp_sleep_wakeup_cause_t; + +/** + * @brief Disable wakeup source + * + * This function is used to deactivate wake up trigger for source + * defined as parameter of the function. + * + * @note This function does not modify wake up configuration in RTC. + * It will be performed in esp_sleep_start function. + * + * See docs/sleep-modes.rst for details. + * + * @param source - number of source to disable of type esp_sleep_source_t + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if trigger was not active + */ +esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source); + +/** + * @brief Enable wakeup by ULP coprocessor + * @note In revisions 0 and 1 of the ESP32, ULP wakeup source + * can not be used when RTC_PERIPH power domain is forced + * to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup + * source is used. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled or if wakeup triggers conflict + */ +esp_err_t esp_sleep_enable_ulp_wakeup(); + +/** + * @brief Enable wakeup by timer + * @param time_in_us time before wakeup, in microseconds + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if value is out of range (TBD) + */ +esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us); + +/** + * @brief Enable wakeup by touch sensor + * + * @note In revisions 0 and 1 of the ESP32, touch wakeup source + * can not be used when RTC_PERIPH power domain is forced + * to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup + * source is used. + * + * @note The FSM mode of the touch button should be configured + * as the timer trigger mode. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if wakeup triggers conflict + */ +esp_err_t esp_sleep_enable_touchpad_wakeup(); + +/** + * @brief Get the touch pad which caused wakeup + * + * If wakeup was caused by another source, this function will return TOUCH_PAD_MAX; + * + * @return touch pad which caused wakeup + */ +touch_pad_t esp_sleep_get_touchpad_wakeup_status(); + +/** + * @brief Enable wakeup using a pin + * + * This function uses external wakeup feature of RTC_IO peripheral. + * It will work only if RTC peripherals are kept on during sleep. + * + * This feature can monitor any pin which is an RTC IO. Once the pin transitions + * into the state given by level argument, the chip will be woken up. + * + * @note This function does not modify pin configuration. The pin is + * configured in esp_sleep_start, immediately before entering sleep mode. + * + * @note In revisions 0 and 1 of the ESP32, ext0 wakeup source + * can not be used together with touch or ULP wakeup sources. + * + * @param gpio_num GPIO number used as wakeup source. Only GPIOs which are have RTC + * functionality can be used: 0,2,4,12-15,25-27,32-39. + * @param level input level which will trigger wakeup (0=low, 1=high) + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if the selected GPIO is not an RTC GPIO, + * or the mode is invalid + * - ESP_ERR_INVALID_STATE if wakeup triggers conflict + */ +esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level); + +/** + * @brief Enable wakeup using multiple pins + * + * This function uses external wakeup feature of RTC controller. + * It will work even if RTC peripherals are shut down during sleep. + * + * This feature can monitor any number of pins which are in RTC IOs. + * Once any of the selected pins goes into the state given by mode argument, + * the chip will be woken up. + * + * @note This function does not modify pin configuration. The pins are + * configured in esp_sleep_start, immediately before + * entering sleep mode. + * + * @note internal pullups and pulldowns don't work when RTC peripherals are + * shut down. In this case, external resistors need to be added. + * Alternatively, RTC peripherals (and pullups/pulldowns) may be + * kept enabled using esp_sleep_pd_config function. + * + * @param mask bit mask of GPIO numbers which will cause wakeup. Only GPIOs + * which are have RTC functionality can be used in this bit map: + * 0,2,4,12-15,25-27,32-39. + * @param mode select logic function used to determine wakeup condition: + * - ESP_EXT1_WAKEUP_ALL_LOW: wake up when all selected GPIOs are low + * - ESP_EXT1_WAKEUP_ANY_HIGH: wake up when any of the selected GPIOs is high + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if any of the selected GPIOs is not an RTC GPIO, + * or mode is invalid + */ +esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode); + + +/** + * @brief Get the bit mask of GPIOs which caused wakeup (ext1) + * + * If wakeup was caused by another source, this function will return 0. + * + * @return bit mask, if GPIOn caused wakeup, BIT(n) will be set + */ +uint64_t esp_sleep_get_ext1_wakeup_status(); + +/** + * @brief Set power down mode for an RTC power domain in sleep mode + * + * If not set set using this API, all power domains default to ESP_PD_OPTION_AUTO. + * + * @param domain power domain to configure + * @param option power down option (ESP_PD_OPTION_OFF, ESP_PD_OPTION_ON, or ESP_PD_OPTION_AUTO) + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if either of the arguments is out of range + */ +esp_err_t esp_sleep_pd_config(esp_sleep_pd_domain_t domain, + esp_sleep_pd_option_t option); + +/** + * @brief Enter deep sleep with the configured wakeup options + * + * This function does not return. + */ +void esp_deep_sleep_start() __attribute__((noreturn)); + +/** + * @brief Enter light sleep with the configured wakeup options + * + * @return + * - ESP_OK on success (returned after wakeup) + * - ESP_ERR_INVALID_STATE if WiFi or BT is not stopped + */ +esp_err_t esp_light_sleep_start(); + +/** + * @brief Enter deep-sleep mode + * + * The device will automatically wake up after the deep-sleep time + * Upon waking up, the device calls deep sleep wake stub, and then proceeds + * to load application. + * + * Call to this function is equivalent to a call to esp_deep_sleep_enable_timer_wakeup + * followed by a call to esp_deep_sleep_start. + * + * esp_deep_sleep does not shut down WiFi, BT, and higher level protocol + * connections gracefully. + * Make sure relevant WiFi and BT stack functions are called to close any + * connections and deinitialize the peripherals. These include: + * - esp_bluedroid_disable + * - esp_bt_controller_disable + * - esp_wifi_stop + * + * This function does not return. + * + * @param time_in_us deep-sleep time, unit: microsecond + */ +void esp_deep_sleep(uint64_t time_in_us) __attribute__((noreturn)); + +/** + * @brief Enter deep-sleep mode + * + * Function has been renamed to esp_deep_sleep. + * This name is deprecated and will be removed in a future version. + * + * @param time_in_us deep-sleep time, unit: microsecond + */ +void system_deep_sleep(uint64_t time_in_us) __attribute__((noreturn, deprecated)); + + +/** + * @brief Get the source which caused wakeup from sleep + * + * @return wakeup cause, or ESP_DEEP_SLEEP_WAKEUP_UNDEFINED if reset happened for reason other than deep sleep wakeup + */ +esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(); + + +/** + * @brief Default stub to run on wake from deep sleep. + * + * Allows for executing code immediately on wake from sleep, before + * the software bootloader or ESP-IDF app has started up. + * + * This function is weak-linked, so you can implement your own version + * to run code immediately when the chip wakes from + * sleep. + * + * See docs/deep-sleep-stub.rst for details. + */ +void esp_wake_deep_sleep(void); + +/** + * @brief Function type for stub to run on wake from sleep. + * + */ +typedef void (*esp_deep_sleep_wake_stub_fn_t)(void); + +/** + * @brief Install a new stub at runtime to run on wake from deep sleep + * + * If implementing esp_wake_deep_sleep() then it is not necessary to + * call this function. + * + * However, it is possible to call this function to substitute a + * different deep sleep stub. Any function used as a deep sleep stub + * must be marked RTC_IRAM_ATTR, and must obey the same rules given + * for esp_wake_deep_sleep(). + */ +void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub); + +/** + * @brief Get current wake from deep sleep stub + * @return Return current wake from deep sleep stub, or NULL if + * no stub is installed. + */ +esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void); + +/** + * @brief The default esp-idf-provided esp_wake_deep_sleep() stub. + * + * See docs/deep-sleep-stub.rst for details. + */ +void esp_default_wake_deep_sleep(void); + + +#ifdef __cplusplus +} +#endif diff --git a/components/esp32s2beta/include/esp_spiram.h b/components/esp32s2beta/include/esp_spiram.h new file mode 100644 index 0000000000..9663dcddcb --- /dev/null +++ b/components/esp32s2beta/include/esp_spiram.h @@ -0,0 +1,90 @@ +// 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 +#include +#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 Configure Cache/MMU for access to external SPI RAM. + * + * Normally this function is called from cpu_start, if CONFIG_SPIRAM_BOOT_INIT + * option is enabled. Applications which need to enable SPI RAM at run time + * can disable CONFIG_SPIRAM_BOOT_INIT, and call this function later. + * + * @attention this function must be called with flash cache disabled. + */ +void esp_spiram_init_cache(); + + +/** + * @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 Add the initialized SPI RAM to the heap allocator. + */ +esp_err_t esp_spiram_add_to_heapalloc(); + + +/** + * @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(); + + + +/** + * @brief Reserve a pool of internal memory for specific DMA/internal allocations + * + * @param size Size of reserved pool in bytes + * + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM when no memory available for pool + */ +esp_err_t esp_spiram_reserve_dma_pool(size_t size); + + +#endif diff --git a/components/esp32s2beta/include/esp_ssc.h b/components/esp32s2beta/include/esp_ssc.h new file mode 100644 index 0000000000..02893ff410 --- /dev/null +++ b/components/esp32s2beta/include/esp_ssc.h @@ -0,0 +1,119 @@ +// 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 __ESP_SSC_H__ +#define __ESP_SSC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CMD_T_ASYNC 0x01 +#define CMD_T_SYNC 0x02 + +typedef struct cmd_s { + char *cmd_str; + uint8_t flag; + uint8_t id; + void (* cmd_func)(void); + void (* cmd_callback)(void *arg); +} ssc_cmd_t; + +#define MAX_LINE_N 127 + +typedef enum { + SSC_BR_9600 = 9600, + SSC_BR_19200 = 19200, + SSC_BR_38400 = 38400, + SSC_BR_57600 = 57600, + SSC_BR_74880 = 74880, + SSC_BR_115200 = 115200, + SSC_BR_230400 = 230400, + SSC_BR_460800 = 460800, + SSC_BR_921600 = 921600 +} SscBaudRate; + +/** \defgroup SSC_APIs SSC APIs + * @brief SSC APIs + * + * SSC means simple serial command. + * SSC APIs allows users to define their own command, users can refer to spiffs_test/test_main.c. + * + */ + +/** @addtogroup SSC_APIs + * @{ + */ + +/** + * @brief Initial the ssc function. + * + * @attention param is no use, just compatible with ESP8266, default bandrate is 115200 + * + * @param SscBaudRate bandrate : baud rate + * + * @return null + */ +void ssc_attach(SscBaudRate bandrate); + +/** + * @brief Get the length of the simple serial command. + * + * @param null + * + * @return length of the command. + */ +int ssc_param_len(void); + +/** + * @brief Get the simple serial command string. + * + * @param null + * + * @return the command. + */ +char *ssc_param_str(void); + +/** + * @brief Parse the simple serial command (ssc). + * + * @param char *pLine : [input] the ssc string + * @param char *argv[] : [output] parameters of the ssc + * + * @return the number of parameters. + */ +int ssc_parse_param(char *pLine, char *argv[]); + +/** + * @brief Register the user-defined simple serial command (ssc) set. + * + * @param ssc_cmd_t *cmdset : the ssc set + * @param uint8 cmdnum : number of commands + * @param void (* help)(void) : callback of user-guide + * + * @return null + */ +void ssc_register(ssc_cmd_t *cmdset, uint8_t cmdnum, void (* help)(void)); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_SSC_H__ */ diff --git a/components/esp32s2beta/int_wdt.c b/components/esp32s2beta/int_wdt.c new file mode 100644 index 0000000000..34a10d66c7 --- /dev/null +++ b/components/esp32s2beta/int_wdt.c @@ -0,0 +1,106 @@ +// Copyright 2015-2018 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 +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "esp_attr.h" +#include "esp_freertos_hooks.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/timer.h" +#include "driver/periph_ctrl.h" +#include "esp_int_wdt.h" + +#if CONFIG_INT_WDT + + +#define WDT_INT_NUM 24 + + +//Take care: the tick hook can also be called before esp_int_wdt_init() is called. +#if CONFIG_INT_WDT_CHECK_CPU1 +//Not static; the ISR assembly checks this. +bool int_wdt_app_cpu_ticked=false; + +static void IRAM_ATTR tick_hook(void) { + if (xPortGetCoreID()!=0) { + int_wdt_app_cpu_ticked=true; + } else { + //Only feed wdt if app cpu also ticked. + if (int_wdt_app_cpu_ticked) { + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; + int_wdt_app_cpu_ticked=false; + } + } +} +#else +static void IRAM_ATTR tick_hook(void) { + if (xPortGetCoreID()!=0) return; + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; +} +#endif + + +void esp_int_wdt_init() { + periph_module_enable(PERIPH_TIMG1_MODULE); + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS + TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS + TIMERG1.wdt_config0.level_int_en=1; + TIMERG1.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt + TIMERG1.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system + TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS + //The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets + //it to their actual value. + TIMERG1.wdt_config2=10000; + TIMERG1.wdt_config3=10000; + TIMERG1.wdt_config0.en=1; + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; + TIMERG1.int_clr.wdt=1; + timer_group_intr_enable(TIMER_GROUP_1, TIMG_WDT_INT_ENA_M); +} + +void esp_int_wdt_cpu_init() +{ + esp_register_freertos_tick_hook_for_cpu(tick_hook, xPortGetCoreID()); + ESP_INTR_DISABLE(WDT_INT_NUM); + intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); + //We do not register a handler for the interrupt because it is interrupt level 4 which + //is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for + //this interrupt. + ESP_INTR_ENABLE(WDT_INT_NUM); +} + + + +#endif diff --git a/components/esp32s2beta/intr_alloc.c b/components/esp32s2beta/intr_alloc.c new file mode 100644 index 0000000000..e6f4f10fe3 --- /dev/null +++ b/components/esp32s2beta/intr_alloc.c @@ -0,0 +1,900 @@ +// 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. + + + +#include "sdkconfig.h" +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "esp_err.h" +//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE +#include "esp_log.h" +#include "esp_intr_alloc.h" +#include "esp_attr.h" +#include "esp_intr_alloc.h" +#include +#include + +static const char* TAG = "intr_alloc"; + +#define ETS_INTERNAL_TIMER0_INTR_NO 6 +#define ETS_INTERNAL_TIMER1_INTR_NO 15 +#define ETS_INTERNAL_TIMER2_INTR_NO 16 +#define ETS_INTERNAL_SW0_INTR_NO 7 +#define ETS_INTERNAL_SW1_INTR_NO 29 +#define ETS_INTERNAL_PROFILING_INTR_NO 11 + + +/* +Define this to debug the choices made when allocating the interrupt. This leads to much debugging +output within a critical region, which can lead to weird effects like e.g. the interrupt watchdog +being triggered, that is why it is separate from the normal LOG* scheme. +*/ +//define DEBUG_INT_ALLOC_DECISIONS +#ifdef DEBUG_INT_ALLOC_DECISIONS +# define ALCHLOG(...) ESP_EARLY_LOGD(TAG, __VA_ARGS__) +#else +# define ALCHLOG(...) do {} while (0) +#endif + + +typedef enum { + INTDESC_NORMAL=0, + INTDESC_RESVD, + INTDESC_SPECIAL //for xtensa timers / software ints +} int_desc_flag_t; + +typedef enum { + INTTP_LEVEL=0, + INTTP_EDGE, + INTTP_NA +} int_type_t; + +typedef struct { + int level; + int_type_t type; + int_desc_flag_t cpuflags[2]; +} int_desc_t; + + +//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer +//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in +//the table below. +#if CONFIG_FREERTOS_CORETIMER_0 +#define INT6RES INTDESC_RESVD +#else +#define INT6RES INTDESC_SPECIAL +#endif + +#if CONFIG_FREERTOS_CORETIMER_1 +#define INT15RES INTDESC_RESVD +#else +#define INT15RES INTDESC_SPECIAL +#endif + +//This is basically a software-readable version of the interrupt usage table in include/soc/soc.h +const static int_desc_t int_desc[32]={ + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //0 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //1 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //2 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //3 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //4 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //5 + { 1, INTTP_NA, {INT6RES, INT6RES } }, //6 + { 1, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //7 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //8 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //9 + { 1, INTTP_EDGE , {INTDESC_NORMAL, INTDESC_NORMAL} }, //10 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //11 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //12 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //13 + { 7, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //14, NMI + { 3, INTTP_NA, {INT15RES, INT15RES } }, //15 + { 5, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL} }, //16 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //17 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //18 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //19 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //20 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //21 + { 3, INTTP_EDGE, {INTDESC_RESVD, INTDESC_NORMAL} }, //22 + { 3, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //23 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //24 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //25 + { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //26 + { 3, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //27 + { 4, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //28 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //29 + { 4, INTTP_EDGE, {INTDESC_RESVD, INTDESC_RESVD } }, //30 + { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //31 +}; + +typedef struct shared_vector_desc_t shared_vector_desc_t; +typedef struct vector_desc_t vector_desc_t; + +struct shared_vector_desc_t { + int disabled: 1; + int source: 8; + volatile uint32_t *statusreg; + uint32_t statusmask; + intr_handler_t isr; + void *arg; + shared_vector_desc_t *next; +}; + + +#define VECDESC_FL_RESERVED (1<<0) +#define VECDESC_FL_INIRAM (1<<1) +#define VECDESC_FL_SHARED (1<<2) +#define VECDESC_FL_NONSHARED (1<<3) + +//Pack using bitfields for better memory use +struct vector_desc_t { + int flags: 16; //OR of VECDESC_FLAG_* defines + unsigned int cpu: 1; + unsigned int intno: 5; + int source: 8; //Interrupt mux flags, used when not shared + shared_vector_desc_t *shared_vec_info; //used when VECDESC_FL_SHARED + vector_desc_t *next; +}; + +struct intr_handle_data_t { + vector_desc_t *vector_desc; + shared_vector_desc_t *shared_vector_desc; +}; + +typedef struct non_shared_isr_arg_t non_shared_isr_arg_t; + +struct non_shared_isr_arg_t { + intr_handler_t isr; + void *isr_arg; + int source; +}; + +//Linked list of vector descriptions, sorted by cpu.intno value +static vector_desc_t *vector_desc_head = NULL; + +//This bitmask has an 1 if the int should be disabled when the flash is disabled. +static uint32_t non_iram_int_mask[portNUM_PROCESSORS]; +//This bitmask has 1 in it if the int was disabled using esp_intr_noniram_disable. +static uint32_t non_iram_int_disabled[portNUM_PROCESSORS]; +static bool non_iram_int_disabled_flag[portNUM_PROCESSORS]; + +#if CONFIG_SYSVIEW_ENABLE +extern uint32_t port_switch_flag[]; +#endif + +static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; + +//Inserts an item into vector_desc list so that the list is sorted +//with an incrementing cpu.intno value. +static void insert_vector_desc(vector_desc_t *to_insert) +{ + vector_desc_t *vd=vector_desc_head; + vector_desc_t *prev=NULL; + while(vd!=NULL) { + if (vd->cpu > to_insert->cpu) break; + if (vd->cpu == to_insert->cpu && vd->intno >= to_insert->intno) break; + prev=vd; + vd=vd->next; + } + if ((vector_desc_head==NULL) || (prev==NULL)) { + //First item + to_insert->next = vd; + vector_desc_head=to_insert; + } else { + prev->next=to_insert; + to_insert->next=vd; + } +} + +//Returns a vector_desc entry for an intno/cpu, or NULL if none exists. +static vector_desc_t *find_desc_for_int(int intno, int cpu) +{ + vector_desc_t *vd=vector_desc_head; + while(vd!=NULL) { + if (vd->cpu==cpu && vd->intno==intno) break; + vd=vd->next; + } + return vd; +} + +//Returns a vector_desc entry for an intno/cpu. +//Either returns a preexisting one or allocates a new one and inserts +//it into the list. Returns NULL on malloc fail. +static vector_desc_t *get_desc_for_int(int intno, int cpu) +{ + vector_desc_t *vd=find_desc_for_int(intno, cpu); + if (vd==NULL) { + vector_desc_t *newvd=heap_caps_malloc(sizeof(vector_desc_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); + if (newvd==NULL) return NULL; + memset(newvd, 0, sizeof(vector_desc_t)); + newvd->intno=intno; + newvd->cpu=cpu; + insert_vector_desc(newvd); + return newvd; + } else { + return vd; + } +} + +//Returns a vector_desc entry for an source, the cpu parameter is used to tell GPIO_INT and GPIO_NMI from different CPUs +static vector_desc_t * find_desc_for_source(int source, int cpu) +{ + vector_desc_t *vd=vector_desc_head; + while(vd!=NULL) { + if ( !(vd->flags & VECDESC_FL_SHARED) ) { + if ( vd->source == source && cpu == vd->cpu ) break; + } else if ( vd->cpu == cpu ) { + // check only shared vds for the correct cpu, otherwise skip + bool found = false; + shared_vector_desc_t *svd = vd->shared_vec_info; + assert(svd != NULL ); + while(svd) { + if ( svd->source == source ) { + found = true; + break; + } + svd = svd->next; + } + if ( found ) break; + } + vd=vd->next; + } + return vd; +} + +esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram) +{ + if (intno>31) return ESP_ERR_INVALID_ARG; + if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + vector_desc_t *vd=get_desc_for_int(intno, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_NO_MEM; + } + vd->flags=VECDESC_FL_SHARED; + if (is_int_ram) vd->flags|=VECDESC_FL_INIRAM; + portEXIT_CRITICAL(&spinlock); + + return ESP_OK; +} + +esp_err_t esp_intr_reserve(int intno, int cpu) +{ + if (intno>31) return ESP_ERR_INVALID_ARG; + if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + vector_desc_t *vd=get_desc_for_int(intno, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_NO_MEM; + } + vd->flags=VECDESC_FL_RESERVED; + portEXIT_CRITICAL(&spinlock); + + return ESP_OK; +} + +//Interrupt handler table and unhandled uinterrupt routine. Duplicated +//from xtensa_intr.c... it's supposed to be private, but we need to look +//into it in order to see if someone allocated an int using +//xt_set_interrupt_handler. +typedef struct xt_handler_table_entry { + void * handler; + void * arg; +} xt_handler_table_entry; +extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS]; +extern void xt_unhandled_interrupt(void * arg); + +//Returns true if handler for interrupt is not the default unhandled interrupt handler +static bool int_has_handler(int intr, int cpu) +{ + return (_xt_interrupt_table[intr*portNUM_PROCESSORS+cpu].handler != xt_unhandled_interrupt); +} + +static bool is_vect_desc_usable(vector_desc_t *vd, int flags, int cpu, int force) +{ + //Check if interrupt is not reserved by design + int x = vd->intno; + if (int_desc[x].cpuflags[cpu]==INTDESC_RESVD) { + ALCHLOG("....Unusable: reserved"); + return false; + } + if (int_desc[x].cpuflags[cpu]==INTDESC_SPECIAL && force==-1) { + ALCHLOG("....Unusable: special-purpose int"); + return false; + } + //Check if the interrupt level is acceptable + if (!(flags&(1<flags&VECDESC_FL_RESERVED) { + ALCHLOG("....Unusable: reserved at runtime."); + return false; + } + + //Ints can't be both shared and non-shared. + assert(!((vd->flags&VECDESC_FL_SHARED)&&(vd->flags&VECDESC_FL_NONSHARED))); + //check if interrupt already is in use by a non-shared interrupt + if (vd->flags&VECDESC_FL_NONSHARED) { + ALCHLOG("....Unusable: already in (non-shared) use."); + return false; + } + // check shared interrupt flags + if (vd->flags&VECDESC_FL_SHARED ) { + if (flags&ESP_INTR_FLAG_SHARED) { + bool in_iram_flag=((flags&ESP_INTR_FLAG_IRAM)!=0); + bool desc_in_iram_flag=((vd->flags&VECDESC_FL_INIRAM)!=0); + //Bail out if int is shared, but iram property doesn't match what we want. + if ((vd->flags&VECDESC_FL_SHARED) && (desc_in_iram_flag!=in_iram_flag)) { + ALCHLOG("....Unusable: shared but iram prop doesn't match"); + return false; + } + } else { + //We need an unshared IRQ; can't use shared ones; bail out if this is shared. + ALCHLOG("...Unusable: int is shared, we need non-shared."); + return false; + } + } else if (int_has_handler(x, cpu)) { + //Check if interrupt already is allocated by xt_set_interrupt_handler + ALCHLOG("....Unusable: already allocated"); + return false; + } + + return true; +} + +//Locate a free interrupt compatible with the flags given. +//The 'force' argument can be -1, or 0-31 to force checking a certain interrupt. +//When a CPU is forced, the INTDESC_SPECIAL marked interrupts are also accepted. +static int get_available_int(int flags, int cpu, int force, int source) +{ + int x; + int best=-1; + int bestLevel=9; + int bestSharedCt=INT_MAX; + //Default vector desc, for vectors not in the linked list + vector_desc_t empty_vect_desc; + memset(&empty_vect_desc, 0, sizeof(vector_desc_t)); + + + //Level defaults to any low/med interrupt + if (!(flags&ESP_INTR_FLAG_LEVELMASK)) flags|=ESP_INTR_FLAG_LOWMED; + + ALCHLOG("get_available_int: try to find existing. Cpu: %d, Source: %d", cpu, source); + vector_desc_t *vd = find_desc_for_source(source, cpu); + if ( vd ) { + // if existing vd found, don't need to search any more. + ALCHLOG("get_avalible_int: existing vd found. intno: %d", vd->intno); + if ( force != -1 && force != vd->intno ) { + ALCHLOG("get_avalible_int: intr forced but not matach existing. existing intno: %d, force: %d", vd->intno, force); + } else if ( !is_vect_desc_usable(vd, flags, cpu, force) ) { + ALCHLOG("get_avalible_int: existing vd invalid."); + } else { + best = vd->intno; + } + return best; + } + if (force!=-1) { + ALCHLOG("get_available_int: try to find force. Cpu: %d, Source: %d, Force: %d", cpu, source, force); + //if force assigned, don't need to search any more. + vd = find_desc_for_int(force, cpu); + if (vd == NULL ) { + //if existing vd not found, just check the default state for the intr. + empty_vect_desc.intno = force; + vd = &empty_vect_desc; + } + if ( is_vect_desc_usable(vd, flags, cpu, force) ) { + best = vd->intno; + } else { + ALCHLOG("get_avalible_int: forced vd invalid."); + } + return best; + } + + ALCHLOG("get_free_int: start looking. Current cpu: %d", cpu); + //No allocated handlers as well as forced intr, iterate over the 32 possible interrupts + for (x=0; x<32; x++) { + //Grab the vector_desc for this vector. + vd=find_desc_for_int(x, cpu); + if (vd==NULL) { + empty_vect_desc.intno = x; + vd=&empty_vect_desc; + } + + ALCHLOG("Int %d reserved %d level %d %s hasIsr %d", + x, int_desc[x].cpuflags[cpu]==INTDESC_RESVD, int_desc[x].level, + int_desc[x].type==INTTP_LEVEL?"LEVEL":"EDGE", int_has_handler(x, cpu)); + + if ( !is_vect_desc_usable(vd, flags, cpu, force) ) continue; + + if (flags&ESP_INTR_FLAG_SHARED) { + //We're allocating a shared int. + + //See if int already is used as a shared interrupt. + if (vd->flags&VECDESC_FL_SHARED) { + //We can use this already-marked-as-shared interrupt. Count the already attached isrs in order to see + //how useful it is. + int no=0; + shared_vector_desc_t *svdesc=vd->shared_vec_info; + while (svdesc!=NULL) { + no++; + svdesc=svdesc->next; + } + if (noint_desc[x].level) { + //Seems like this shared vector is both okay and has the least amount of ISRs already attached to it. + best=x; + bestSharedCt=no; + bestLevel=int_desc[x].level; + ALCHLOG("...int %d more usable as a shared int: has %d existing vectors", x, no); + } else { + ALCHLOG("...worse than int %d", best); + } + } else { + if (best==-1) { + //We haven't found a feasible shared interrupt yet. This one is still free and usable, even if + //not marked as shared. + //Remember it in case we don't find any other shared interrupt that qualifies. + if (bestLevel>int_desc[x].level) { + best=x; + bestLevel=int_desc[x].level; + ALCHLOG("...int %d usable as a new shared int", x); + } + } else { + ALCHLOG("...already have a shared int"); + } + } + } else { + //Seems this interrupt is feasible. Select it and break out of the loop; no need to search further. + if (bestLevel>int_desc[x].level) { + best=x; + bestLevel=int_desc[x].level; + } else { + ALCHLOG("...worse than int %d", best); + } + } + } + ALCHLOG("get_available_int: using int %d", best); + + //Okay, by now we have looked at all potential interrupts and hopefully have selected the best one in best. + return best; +} + +//Common shared isr handler. Chain-call all ISRs. +static void IRAM_ATTR shared_intr_isr(void *arg) +{ + vector_desc_t *vd=(vector_desc_t*)arg; + shared_vector_desc_t *sh_vec=vd->shared_vec_info; + portENTER_CRITICAL(&spinlock); + while(sh_vec) { + if (!sh_vec->disabled) { + if ((sh_vec->statusreg == NULL) || (*sh_vec->statusreg & sh_vec->statusmask)) { +#if CONFIG_SYSVIEW_ENABLE + traceISR_ENTER(sh_vec->source+ETS_INTERNAL_INTR_SOURCE_OFF); +#endif + sh_vec->isr(sh_vec->arg); +#if CONFIG_SYSVIEW_ENABLE + // check if we will return to scheduler or to interrupted task after ISR + if (!port_switch_flag[xPortGetCoreID()]) { + traceISR_EXIT(); + } +#endif + } + } + sh_vec=sh_vec->next; + } + portEXIT_CRITICAL(&spinlock); +} + +#if CONFIG_SYSVIEW_ENABLE +//Common non-shared isr handler wrapper. +static void IRAM_ATTR non_shared_intr_isr(void *arg) +{ + non_shared_isr_arg_t *ns_isr_arg=(non_shared_isr_arg_t*)arg; + portENTER_CRITICAL(&spinlock); + traceISR_ENTER(ns_isr_arg->source+ETS_INTERNAL_INTR_SOURCE_OFF); + // FIXME: can we call ISR and check port_switch_flag after releasing spinlock? + // when CONFIG_SYSVIEW_ENABLE = 0 ISRs for non-shared IRQs are called without spinlock + ns_isr_arg->isr(ns_isr_arg->isr_arg); + // check if we will return to scheduler or to interrupted task after ISR + if (!port_switch_flag[xPortGetCoreID()]) { + traceISR_EXIT(); + } + portEXIT_CRITICAL(&spinlock); +} +#endif + +//We use ESP_EARLY_LOG* here because this can be called before the scheduler is running. +esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, + void *arg, intr_handle_t *ret_handle) +{ + intr_handle_data_t *ret=NULL; + int force=-1; + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): checking args", xPortGetCoreID()); + //Shared interrupts should be level-triggered. + if ((flags&ESP_INTR_FLAG_SHARED) && (flags&ESP_INTR_FLAG_EDGE)) return ESP_ERR_INVALID_ARG; + //You can't set an handler / arg for a non-C-callable interrupt. + if ((flags&ESP_INTR_FLAG_HIGH) && (handler)) return ESP_ERR_INVALID_ARG; + //Shared ints should have handler and non-processor-local source + if ((flags&ESP_INTR_FLAG_SHARED) && (!handler || source<0)) return ESP_ERR_INVALID_ARG; + //Statusreg should have a mask + if (intrstatusreg && !intrstatusmask) return ESP_ERR_INVALID_ARG; + //If the ISR is marked to be IRAM-resident, the handler must not be in the cached region + if ((flags&ESP_INTR_FLAG_IRAM) && + (ptrdiff_t) handler >= SOC_RTC_IRAM_HIGH && + (ptrdiff_t) handler < SOC_RTC_DATA_LOW ) { + return ESP_ERR_INVALID_ARG; + } + + //Default to prio 1 for shared interrupts. Default to prio 1, 2 or 3 for non-shared interrupts. + if ((flags&ESP_INTR_FLAG_LEVELMASK)==0) { + if (flags&ESP_INTR_FLAG_SHARED) { + flags|=ESP_INTR_FLAG_LEVEL1; + } else { + flags|=ESP_INTR_FLAG_LOWMED; + } + } + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): Args okay. Resulting flags 0x%X", xPortGetCoreID(), flags); + + //Check 'special' interrupt sources. These are tied to one specific interrupt, so we + //have to force get_free_int to only look at that. + if (source==ETS_INTERNAL_TIMER0_INTR_SOURCE) force=ETS_INTERNAL_TIMER0_INTR_NO; + if (source==ETS_INTERNAL_TIMER1_INTR_SOURCE) force=ETS_INTERNAL_TIMER1_INTR_NO; + if (source==ETS_INTERNAL_TIMER2_INTR_SOURCE) force=ETS_INTERNAL_TIMER2_INTR_NO; + if (source==ETS_INTERNAL_SW0_INTR_SOURCE) force=ETS_INTERNAL_SW0_INTR_NO; + if (source==ETS_INTERNAL_SW1_INTR_SOURCE) force=ETS_INTERNAL_SW1_INTR_NO; + if (source==ETS_INTERNAL_PROFILING_INTR_SOURCE) force=ETS_INTERNAL_PROFILING_INTR_NO; + + //Allocate a return handle. If we end up not needing it, we'll free it later on. + ret=heap_caps_malloc(sizeof(intr_handle_data_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); + if (ret==NULL) return ESP_ERR_NO_MEM; + + portENTER_CRITICAL(&spinlock); + int cpu=xPortGetCoreID(); + //See if we can find an interrupt that matches the flags. + int intr=get_available_int(flags, cpu, force, source); + if (intr==-1) { + //None found. Bail out. + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NOT_FOUND; + } + //Get an int vector desc for int. + vector_desc_t *vd=get_desc_for_int(intr, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NO_MEM; + } + + //Allocate that int! + if (flags&ESP_INTR_FLAG_SHARED) { + //Populate vector entry and add to linked list. + shared_vector_desc_t *sh_vec=malloc(sizeof(shared_vector_desc_t)); + if (sh_vec==NULL) { + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NO_MEM; + } + memset(sh_vec, 0, sizeof(shared_vector_desc_t)); + sh_vec->statusreg=(uint32_t*)intrstatusreg; + sh_vec->statusmask=intrstatusmask; + sh_vec->isr=handler; + sh_vec->arg=arg; + sh_vec->next=vd->shared_vec_info; + sh_vec->source=source; + sh_vec->disabled=0; + vd->shared_vec_info=sh_vec; + vd->flags|=VECDESC_FL_SHARED; + //(Re-)set shared isr handler to new value. + xt_set_interrupt_handler(intr, shared_intr_isr, vd); + } else { + //Mark as unusable for other interrupt sources. This is ours now! + vd->flags=VECDESC_FL_NONSHARED; + if (handler) { +#if CONFIG_SYSVIEW_ENABLE + non_shared_isr_arg_t *ns_isr_arg=malloc(sizeof(non_shared_isr_arg_t)); + if (!ns_isr_arg) { + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NO_MEM; + } + ns_isr_arg->isr=handler; + ns_isr_arg->isr_arg=arg; + ns_isr_arg->source=source; + xt_set_interrupt_handler(intr, non_shared_intr_isr, ns_isr_arg); +#else + xt_set_interrupt_handler(intr, handler, arg); +#endif + } + if (flags&ESP_INTR_FLAG_EDGE) xthal_set_intclear(1 << intr); + vd->source=source; + } + if (flags&ESP_INTR_FLAG_IRAM) { + vd->flags|=VECDESC_FL_INIRAM; + non_iram_int_mask[cpu]&=~(1<flags&=~VECDESC_FL_INIRAM; + non_iram_int_mask[cpu]|=(1<=0) { + intr_matrix_set(cpu, source, intr); + } + + //Fill return handle data. + ret->vector_desc=vd; + ret->shared_vector_desc=vd->shared_vec_info; + + //Enable int at CPU-level; + ESP_INTR_ENABLE(intr); + + //If interrupt has to be started disabled, do that now; ints won't be enabled for real until the end + //of the critical section. + if (flags&ESP_INTR_FLAG_INTRDISABLED) { + esp_intr_disable(ret); + } + + portEXIT_CRITICAL(&spinlock); + + //Fill return handle if needed, otherwise free handle. + if (ret_handle!=NULL) { + *ret_handle=ret; + } else { + free(ret); + } + + ESP_EARLY_LOGD(TAG, "Connected src %d to int %d (cpu %d)", source, intr, cpu); + return ESP_OK; +} + +esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle) +{ + /* + As an optimization, we can create a table with the possible interrupt status registers and masks for every single + source there is. We can then add code here to look up an applicable value and pass that to the + esp_intr_alloc_intrstatus function. + */ + return esp_intr_alloc_intrstatus(source, flags, 0, 0, handler, arg, ret_handle); +} + +esp_err_t IRAM_ATTR esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + vector_desc_t *vd = handle->vector_desc; + if (vd->flags & VECDESC_FL_SHARED) { + return ESP_ERR_INVALID_ARG; + } + portENTER_CRITICAL(&spinlock); + uint32_t mask = (1 << vd->intno); + if (is_in_iram) { + vd->flags |= VECDESC_FL_INIRAM; + non_iram_int_mask[vd->cpu] &= ~mask; + } else { + vd->flags &= ~VECDESC_FL_INIRAM; + non_iram_int_mask[vd->cpu] |= mask; + } + portEXIT_CRITICAL(&spinlock); + return ESP_OK; +} + +esp_err_t esp_intr_free(intr_handle_t handle) +{ + bool free_shared_vector=false; + if (!handle) return ESP_ERR_INVALID_ARG; + //This routine should be called from the interrupt the task is scheduled on. + if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + esp_intr_disable(handle); + if (handle->vector_desc->flags&VECDESC_FL_SHARED) { + //Find and kill the shared int + shared_vector_desc_t *svd=handle->vector_desc->shared_vec_info; + shared_vector_desc_t *prevsvd=NULL; + assert(svd); //should be something in there for a shared int + while (svd!=NULL) { + if (svd==handle->shared_vector_desc) { + //Found it. Now kill it. + if (prevsvd) { + prevsvd->next=svd->next; + } else { + handle->vector_desc->shared_vec_info=svd->next; + } + free(svd); + break; + } + prevsvd=svd; + svd=svd->next; + } + //If nothing left, disable interrupt. + if (handle->vector_desc->shared_vec_info==NULL) free_shared_vector=true; + ESP_LOGV(TAG, "esp_intr_free: Deleting shared int: %s. Shared int is %s", svd?"not found or last one":"deleted", free_shared_vector?"empty now.":"still in use"); + } + + if ((handle->vector_desc->flags&VECDESC_FL_NONSHARED) || free_shared_vector) { + ESP_LOGV(TAG, "esp_intr_free: Disabling int, killing handler"); +#if CONFIG_SYSVIEW_ENABLE + if (!free_shared_vector) { + void *isr_arg = xt_get_interrupt_handler_arg(handle->vector_desc->intno); + if (isr_arg) { + free(isr_arg); + } + } +#endif + //Reset to normal handler + xt_set_interrupt_handler(handle->vector_desc->intno, xt_unhandled_interrupt, (void*)((int)handle->vector_desc->intno)); + //Theoretically, we could free the vector_desc... not sure if that's worth the few bytes of memory + //we save.(We can also not use the same exit path for empty shared ints anymore if we delete + //the desc.) For now, just mark it as free. + handle->vector_desc->flags&=!(VECDESC_FL_NONSHARED|VECDESC_FL_RESERVED); + //Also kill non_iram mask bit. + non_iram_int_mask[handle->vector_desc->cpu]&=~(1<<(handle->vector_desc->intno)); + } + portEXIT_CRITICAL(&spinlock); + free(handle); + return ESP_OK; +} + +int esp_intr_get_intno(intr_handle_t handle) +{ + return handle->vector_desc->intno; +} + +int esp_intr_get_cpu(intr_handle_t handle) +{ + return handle->vector_desc->cpu; +} + +/* + Interrupt disabling strategy: + If the source is >=0 (meaning a muxed interrupt), we disable it by muxing the interrupt to a non-connected + interrupt. If the source is <0 (meaning an internal, per-cpu interrupt), we disable it using ESP_INTR_DISABLE. + This allows us to, for the muxed CPUs, disable an int from the other core. It also allows disabling shared + interrupts. + */ + +//Muxing an interrupt source to interrupt 6, 7, 11, 15, 16 or 29 cause the interrupt to effectively be disabled. +#define INT_MUX_DISABLED_INTNO 6 + +esp_err_t IRAM_ATTR esp_intr_enable(intr_handle_t handle) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + portENTER_CRITICAL(&spinlock); + int source; + if (handle->shared_vector_desc) { + handle->shared_vector_desc->disabled=0; + source=handle->shared_vector_desc->source; + } else { + source=handle->vector_desc->source; + } + if (source >= 0) { + //Disabled using int matrix; re-connect to enable + intr_matrix_set(handle->vector_desc->cpu, source, handle->vector_desc->intno); + } else { + //Re-enable using cpu int ena reg + if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu + ESP_INTR_ENABLE(handle->vector_desc->intno); + } + portEXIT_CRITICAL(&spinlock); + return ESP_OK; +} + +esp_err_t IRAM_ATTR esp_intr_disable(intr_handle_t handle) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + portENTER_CRITICAL(&spinlock); + int source; + bool disabled = 1; + if (handle->shared_vector_desc) { + handle->shared_vector_desc->disabled=1; + source=handle->shared_vector_desc->source; + + shared_vector_desc_t *svd=handle->vector_desc->shared_vec_info; + assert( svd != NULL ); + while( svd ) { + if ( svd->source == source && svd->disabled == 0 ) { + disabled = 0; + break; + } + svd = svd->next; + } + } else { + source=handle->vector_desc->source; + } + + if (source >= 0) { + if ( disabled ) { + //Disable using int matrix + intr_matrix_set(handle->vector_desc->cpu, source, INT_MUX_DISABLED_INTNO); + } + } else { + //Disable using per-cpu regs + if (handle->vector_desc->cpu!=xPortGetCoreID()) { + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu + } + ESP_INTR_DISABLE(handle->vector_desc->intno); + } + portEXIT_CRITICAL(&spinlock); + return ESP_OK; +} + + +void IRAM_ATTR esp_intr_noniram_disable() +{ + int oldint; + int cpu=xPortGetCoreID(); + int intmask=~non_iram_int_mask[cpu]; + if (non_iram_int_disabled_flag[cpu]) abort(); + non_iram_int_disabled_flag[cpu]=true; + asm volatile ( + "movi %0,0\n" + "xsr %0,INTENABLE\n" //disable all ints first + "rsync\n" + "and a3,%0,%1\n" //mask ints that need disabling + "wsr a3,INTENABLE\n" //write back + "rsync\n" + :"=&r"(oldint):"r"(intmask):"a3"); + //Save which ints we did disable + non_iram_int_disabled[cpu]=oldint&non_iram_int_mask[cpu]; +} + +void IRAM_ATTR esp_intr_noniram_enable() +{ + int cpu=xPortGetCoreID(); + int intmask=non_iram_int_disabled[cpu]; + if (!non_iram_int_disabled_flag[cpu]) abort(); + non_iram_int_disabled_flag[cpu]=false; + asm volatile ( + "movi a3,0\n" + "xsr a3,INTENABLE\n" + "rsync\n" + "or a3,a3,%0\n" + "wsr a3,INTENABLE\n" + "rsync\n" + ::"r"(intmask):"a3"); +} + +//These functions are provided in ROM, but the ROM-based functions use non-multicore-capable +//virtualized interrupt levels. Thus, we disable them in the ld file and provide working +//equivalents here. + + +void IRAM_ATTR ets_isr_unmask(unsigned int mask) { + xt_ints_on(mask); +} + +void IRAM_ATTR ets_isr_mask(unsigned int mask) { + xt_ints_off(mask); +} + + + + diff --git a/components/esp32s2beta/ld/esp32s2beta.common.ld b/components/esp32s2beta/ld/esp32s2beta.common.ld new file mode 100644 index 0000000000..8195d2320a --- /dev/null +++ b/components/esp32s2beta/ld/esp32s2beta.common.ld @@ -0,0 +1,253 @@ +/* Default entry point: */ +ENTRY(call_start_cpu0); + +SECTIONS +{ + /* RTC fast memory holds RTC wake stub code, + including from any source file named rtc_wake_stub*.c + */ + .rtc.text : + { + . = ALIGN(4); + *(.rtc.literal .rtc.text) + *rtc_wake_stub*.o(.literal .text .literal.* .text.*) + } > rtc_iram_seg + + /* RTC slow memory holds RTC wake stub + data/rodata, including from any source file + named rtc_wake_stub*.c + */ + .rtc.data : + { + _rtc_data_start = ABSOLUTE(.); + *(.rtc.data) + *(.rtc.rodata) + *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*) + _rtc_data_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* RTC bss, from any source file named rtc_wake_stub*.c */ + .rtc.bss (NOLOAD) : + { + _rtc_bss_start = ABSOLUTE(.); + *rtc_wake_stub*.o(.bss .bss.*) + *rtc_wake_stub*.o(COMMON) + _rtc_bss_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* This section holds data that should not be initialized at power up + and will be retained during deep sleep. The section located in + RTC SLOW Memory area. User data marked with RTC_NOINIT_ATTR will be placed + into this section. See the file "esp_attr.h" for more information. + */ + .rtc_noinit (NOLOAD): + { + . = ALIGN(4); + _rtc_noinit_start = ABSOLUTE(.); + *(.rtc_noinit .rtc_noinit.*) + . = ALIGN(4) ; + _rtc_noinit_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* Send .iram0 code to iram */ + .iram0.vectors : + { + /* Vectors go to IRAM */ + _init_start = ABSOLUTE(.); + /* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */ + . = 0x0; + KEEP(*(.WindowVectors.text)); + . = 0x180; + KEEP(*(.Level2InterruptVector.text)); + . = 0x1c0; + KEEP(*(.Level3InterruptVector.text)); + . = 0x200; + KEEP(*(.Level4InterruptVector.text)); + . = 0x240; + KEEP(*(.Level5InterruptVector.text)); + . = 0x280; + KEEP(*(.DebugExceptionVector.text)); + . = 0x2c0; + KEEP(*(.NMIExceptionVector.text)); + . = 0x300; + KEEP(*(.KernelExceptionVector.text)); + . = 0x340; + KEEP(*(.UserExceptionVector.text)); + . = 0x3C0; + KEEP(*(.DoubleExceptionVector.text)); + . = 0x400; + *(.*Vector.literal) + + *(.UserEnter.literal); + *(.UserEnter.text); + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + _init_end = ABSOLUTE(.); + + /* This goes here, not at top of linker script, so addr2line finds it last, + and uses it in preference to the first symbol in IRAM */ + _iram_start = ABSOLUTE(0); + } > iram0_0_seg + + .iram0.text : + { + /* Code marked as runnning out of IRAM */ + _iram_text_start = ABSOLUTE(.); + *(.iram1 .iram1.*) + *libfreertos.a:(.literal .text .literal.* .text.*) + *libheap.a:multi_heap.o(.literal .text .literal.* .text.*) + *libheap.a:multi_heap_poisoning.o(.literal .text .literal.* .text.*) + *libesp32c.a:panic.o(.literal .text .literal.* .text.*) + *libesp32c.a:core_dump.o(.literal .text .literal.* .text.*) + *libapp_trace.a:(.literal .text .literal.* .text.*) + *libxtensa-debug-module.a:eri.o(.literal .text .literal.* .text.*) + *librtc.a:(.literal .text .literal.* .text.*) + *libsoc.a:(.literal .text .literal.* .text.*) + *libhal.a:(.literal .text .literal.* .text.*) + *libgcc.a:lib2funcs.o(.literal .text .literal.* .text.*) + *libspi_flash.a:spi_flash_rom_patch.o(.literal .text .literal.* .text.*) + *libgcov.a:(.literal .text .literal.* .text.*) + INCLUDE esp32.spiram.rom-functions-iram.ld + _iram_text_end = ABSOLUTE(.); + } > iram0_0_seg + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + *(.dram1 .dram1.*) + *libesp32c.a:panic.o(.rodata .rodata.*) + *libphy.a:(.rodata .rodata.*) + *libsoc.a:rtc_clk.o(.rodata .rodata.*) + *libapp_trace.a:(.rodata .rodata.*) + *libgcov.a:(.rodata .rodata.*) + *libheap.a:multi_heap.o(.rodata .rodata.*) + *libheap.a:multi_heap_poisoning.o(.rodata .rodata.*) + INCLUDE esp32.spiram.rom-functions-dram.ld + _data_end = ABSOLUTE(.); + . = ALIGN(4); + } > dram0_0_seg + + /*This section holds data that should not be initialized at power up. + The section located in Internal SRAM memory region. The macro _NOINIT + can be used as attribute to place data into this section. + See the esp_attr.h file for more information. + */ + .noinit (NOLOAD): + { + . = ALIGN(4); + _noinit_start = ABSOLUTE(.); + *(.noinit .noinit.*) + . = ALIGN(4) ; + _noinit_end = ABSOLUTE(.); + } > dram0_0_seg + + /* Shared RAM */ + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.share.mem) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + /* The heap starts right after end of this section */ + _heap_start = ABSOLUTE(.); + } > dram0_0_seg + + .flash.rodata : + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE_ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table .gcc_except_table.*) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + . = (. + 3) & ~ 3; + __eh_frame = ABSOLUTE(.); + KEEP(*(.eh_frame)) + . = (. + 7) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS_ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + _rodata_end = ABSOLUTE(.); + /* Literals are also RO data. */ + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + . = ALIGN(4); + _thread_local_start = ABSOLUTE(.); + *(.tdata) + *(.tdata.*) + *(.tbss) + *(.tbss.*) + _thread_local_end = ABSOLUTE(.); + . = ALIGN(4); + } >drom0_0_seg + + .flash.text : + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */ + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + + /* Similar to _iram_start, this symbol goes here so it is + resolved by addr2line in preference to the first symbol in + the flash.text segment. + */ + _flash_cache_start = ABSOLUTE(0); + } >iram0_2_seg +} diff --git a/components/esp32s2beta/ld/esp32s2beta.ld b/components/esp32s2beta/ld/esp32s2beta.ld new file mode 100644 index 0000000000..077d8d0cd5 --- /dev/null +++ b/components/esp32s2beta/ld/esp32s2beta.ld @@ -0,0 +1,74 @@ +/* ESP32 Linker Script Memory Layout + + This file describes the memory layout (memory blocks) as virtual + memory addresses. + + esp32.common.ld contains output sections to link compiler output + into these memory blocks. + + *** + + This linker script is passed through the C preprocessor to include + configuration options. + + Please use preprocessor features sparingly! Restrict + to simple macros with numeric values, and/or #if/#endif blocks. +*/ +#include "sdkconfig.h" + +/* If BT is not built at all */ +#ifndef CONFIG_BT_RESERVE_DRAM +#define CONFIG_BT_RESERVE_DRAM 0 +#endif + +MEMORY +{ + /* All these values assume the flash cache is on, and have the blocks this uses subtracted from the length + of the various regions. The 'data access port' dram/drom regions map to the same iram/irom regions but + are connected to the data port of the CPU and eg allow bytewise access. */ + + /* IRAM for PRO cpu. Not sure if happy with this, this is MMU area... */ + iram0_0_seg (RX) : org = 0x40028000, len = 0x18000 + + /* Even though the segment name is iram, it is actually mapped to flash + */ + iram0_2_seg (RX) : org = 0x40080018, len = 0xb80000-0x18 + + /* + (0x18 offset above is a convenience for the app binary image generation. Flash cache has 64KB pages. The .bin file + which is flashed to the chip has a 0x18 byte file header. Setting this offset makes it simple to meet the flash + cache MMU's constraint that (paddr % 64KB == vaddr % 64KB).) + */ + + + /* Shared data RAM, excluding memory reserved for ROM bss/data/stack. + + Enabling Bluetooth & Trace Memory features in menuconfig will decrease + the amount of RAM available. + + Note: Length of this section *should* be 0x50000, and this extra DRAM is available + in heap at runtime. However due to static ROM memory usage at this 176KB mark, the + additional static memory temporarily cannot be used. + */ + dram0_0_seg (RW) : org = 0x3FFD0000 + CONFIG_BT_RESERVE_DRAM, + len = 0x28000 - CONFIG_BT_RESERVE_DRAM + + /* Flash mapped constant data */ + drom0_0_seg (R) : org = 0x3F000018, len = 0x3f0000-0x18 + + /* (See iram0_2_seg for meaning of 0x18 offset in the above.) */ + + /* RTC fast memory (executable). Persists over deep sleep. + */ + rtc_iram_seg(RWX) : org = 0x40070000, len = 0x2000 + + /* RTC slow memory (data accessible). Persists over deep sleep. + + Start of RTC slow memory is reserved for ULP co-processor code + data, if enabled. + */ + rtc_slow_seg(RW) : org = 0x50000000 + CONFIG_ULP_COPROC_RESERVE_MEM, + len = 0x1000 - CONFIG_ULP_COPROC_RESERVE_MEM +} + +/* Heap ends at top of dram0_0_seg */ +_heap_end = 0x40000000 - CONFIG_TRACEMEM_RESERVE_DRAM; diff --git a/components/esp32s2beta/ld/esp32s2beta.peripherals.ld b/components/esp32s2beta/ld/esp32s2beta.peripherals.ld new file mode 100644 index 0000000000..af516099c6 --- /dev/null +++ b/components/esp32s2beta/ld/esp32s2beta.peripherals.ld @@ -0,0 +1,29 @@ +PROVIDE ( UART0 = 0x3f400000 ); +PROVIDE ( SPIMEM1 = 0x3f402000 ); +PROVIDE ( SPIMEM0 = 0x3f403000 ); +PROVIDE ( GPIO = 0x3f404000 ); +PROVIDE ( SIGMADELTA = 0x3f404f00 ); +PROVIDE ( RTCCNTL = 0x3f408000 ); +PROVIDE ( RTCIO = 0x3f408400 ); +PROVIDE ( SENS = 0x3f408800 ); +PROVIDE ( HINF = 0x3f40B000 ); +PROVIDE ( I2S0 = 0x3f40F000 ); +PROVIDE ( UART1 = 0x3f410000 ); +PROVIDE ( I2C0 = 0x3f413000 ); +PROVIDE ( UHCI0 = 0x3f414000 ); +PROVIDE ( HOST = 0x3f415000 ); +PROVIDE ( RMT = 0x3f416000 ); +PROVIDE ( RMTMEM = 0x3f416800 ); +PROVIDE ( PCNT = 0x3f417000 ); +PROVIDE ( SLC = 0x3f418000 ); +PROVIDE ( LEDC = 0x3f419000 ); +PROVIDE ( TIMERG0 = 0x3f41F000 ); +PROVIDE ( TIMERG1 = 0x3f420000 ); +PROVIDE ( GPSPI2 = 0x3f424000 ); +PROVIDE ( GPSPI3 = 0x3f425000 ); +PROVIDE ( SYSCON = 0x3f426000 ); +PROVIDE ( I2C1 = 0x3f427000 ); +PROVIDE ( GPSPI4 = 0x3f437000 ); + +PROVIDE ( ToBeCleanedUpBelow = 0x00000000 ); +PROVIDE ( UART2 = 0x3f410000 ); diff --git a/components/esp32s2beta/linker.lf b/components/esp32s2beta/linker.lf new file mode 100644 index 0000000000..b0d99f2211 --- /dev/null +++ b/components/esp32s2beta/linker.lf @@ -0,0 +1,14 @@ +[mapping:esp32s2beta] +archive: libesp32s2beta.a +entries: + panic (noflash) + +[mapping:gcc] +archive: libgcc.a +entries: + lib2funcs (noflash_text) + +[mapping:gcov] +archive: libgcov.a +entries: + * (noflash) diff --git a/components/esp32s2beta/panic.c b/components/esp32s2beta/panic.c new file mode 100644 index 0000000000..3109faf118 --- /dev/null +++ b/components/esp32s2beta/panic.c @@ -0,0 +1,672 @@ +// 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. +#include + +#include + +#include "esp32s2beta/rom/rtc.h" +#include "esp32s2beta/rom/uart.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/xtensa_api.h" + +#include "soc/uart_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/dport_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "soc/cpu.h" +#include "soc/rtc.h" +#include "soc/rtc_wdt.h" + +#include "esp_private/gdbstub.h" +#include "esp_debug_helpers.h" +#include "esp_private/panic_reason.h" +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_core_dump.h" +#include "esp_spi_flash.h" +#include "esp32s2beta/cache_err_int.h" +#include "esp_app_trace.h" +#include "esp_private/system_internal.h" +#include "sdkconfig.h" +#if CONFIG_SYSVIEW_ENABLE +#include "SEGGER_RTT.h" +#endif + +#if CONFIG_ESP32_APPTRACE_ONPANIC_HOST_FLUSH_TMO == -1 +#define APPTRACE_ONPANIC_HOST_FLUSH_TMO ESP_APPTRACE_TMO_INFINITE +#else +#define APPTRACE_ONPANIC_HOST_FLUSH_TMO (1000*CONFIG_ESP32_APPTRACE_ONPANIC_HOST_FLUSH_TMO) +#endif +/* + Panic handlers; these get called when an unhandled exception occurs or the assembly-level + task switching / interrupt code runs into an unrecoverable error. The default task stack + overflow handler and abort handler are also in here. +*/ + +/* + Note: The linker script will put everything in this file in IRAM/DRAM, so it also works with flash cache disabled. +*/ + +#if !CONFIG_ESP32_PANIC_SILENT_REBOOT +//printf may be broken, so we fix our own printing fns... +static void panicPutChar(char c) +{ + while (((READ_PERI_REG(UART_STATUS_REG(CONFIG_CONSOLE_UART_NUM)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT) >= 126) ; + WRITE_PERI_REG(UART_FIFO_AHB_REG(CONFIG_CONSOLE_UART_NUM), c); +} + +static void panicPutStr(const char *c) +{ + int x = 0; + while (c[x] != 0) { + panicPutChar(c[x]); + x++; + } +} + +static void panicPutHex(int a) +{ + int x; + int c; + for (x = 0; x < 8; x++) { + c = (a >> 28) & 0xf; + if (c < 10) { + panicPutChar('0' + c); + } else { + panicPutChar('a' + c - 10); + } + a <<= 4; + } +} + +static void panicPutDec(int a) +{ + int n1, n2; + n1 = a % 10; + n2 = a / 10; + if (n2 == 0) { + panicPutChar(' '); + } else { + panicPutChar(n2 + '0'); + } + panicPutChar(n1 + '0'); +} +#else +//No printing wanted. Stub out these functions. +static void panicPutChar(char c) { } +static void panicPutStr(const char *c) { } +static void panicPutHex(int a) { } +static void panicPutDec(int a) { } +#endif + +void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) +{ + panicPutStr("***ERROR*** A stack overflow in task "); + panicPutStr((char *)pcTaskName); + panicPutStr(" has been detected.\r\n"); + abort(); +} + +static bool abort_called; + +static __attribute__((noreturn)) inline void invoke_abort() +{ + abort_called = true; +#if CONFIG_ESP32_APPTRACE_ENABLE +#if CONFIG_SYSVIEW_ENABLE + SEGGER_RTT_ESP32_FlushNoLock(CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO); +#else + esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH, + APPTRACE_ONPANIC_HOST_FLUSH_TMO); +#endif +#endif + while (1) { + if (esp_cpu_in_ocd_debug_mode()) { + __asm__ ("break 0,0"); + } + *((int *) 0) = 0; + } +} + +void abort() +{ +#if !CONFIG_ESP32_PANIC_SILENT_REBOOT + ets_printf("abort() was called at PC 0x%08x on core %d\r\n", (intptr_t)__builtin_return_address(0) - 3, xPortGetCoreID()); +#endif + invoke_abort(); +} + + +static const char *edesc[] = { + "IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError", + "Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue", + "Privileged", "LoadStoreAlignment", "res", "res", + "InstrPDAddrError", "LoadStorePIFDataError", "InstrPIFAddrError", "LoadStorePIFAddrError", + "InstTLBMiss", "InstTLBMultiHit", "InstFetchPrivilege", "res", + "InstrFetchProhibited", "res", "res", "res", + "LoadStoreTLBMiss", "LoadStoreTLBMultihit", "LoadStorePrivilege", "res", + "LoadProhibited", "StoreProhibited", "res", "res", + "Cp0Dis", "Cp1Dis", "Cp2Dis", "Cp3Dis", + "Cp4Dis", "Cp5Dis", "Cp6Dis", "Cp7Dis" +}; + +#define NUM_EDESCS (sizeof(edesc) / sizeof(char *)) + +static void commonErrorHandler(XtExcFrame *frame); +static inline void disableAllWdts(); + +//The fact that we've panic'ed probably means the other CPU is now running wild, possibly +//messing up the serial output, so we stall it here. +static void haltOtherCore() +{ + esp_cpu_stall( xPortGetCoreID() == 0 ? 1 : 0 ); +} + + +static void setFirstBreakpoint(uint32_t pc) +{ + asm( + "wsr.ibreaka0 %0\n" \ + "rsr.ibreakenable a3\n" \ + "movi a4,1\n" \ + "or a4, a4, a3\n" \ + "wsr.ibreakenable a4\n" \ + ::"r"(pc):"a3", "a4"); +} + +//When interrupt watchdog happen in one core, both cores will be interrupted. +//The core which doesn't trigger the interrupt watchdog will save the frame and return. +//The core which triggers the interrupt watchdog will use the saved frame, and dump frames for both cores. +#if !CONFIG_FREERTOS_UNICORE +static volatile XtExcFrame * other_core_frame = NULL; +#endif //!CONFIG_FREERTOS_UNICORE + +void panicHandler(XtExcFrame *frame) +{ + int core_id = xPortGetCoreID(); + //Please keep in sync with PANIC_RSN_* defines + const char *reasons[] = { + "Unknown reason", + "Unhandled debug exception", + "Double exception", + "Unhandled kernel exception", + "Coprocessor exception", + "Interrupt wdt timeout on CPU0", + "Interrupt wdt timeout on CPU1", + "Cache disabled but cached memory region accessed", + }; + const char *reason = reasons[0]; + //The panic reason is stored in the EXCCAUSE register. + if (frame->exccause <= PANIC_RSN_MAX) { + reason = reasons[frame->exccause]; + } + +#if !CONFIG_FREERTOS_UNICORE + //Save frame for other core. + if ((frame->exccause == PANIC_RSN_INTWDT_CPU0 && core_id == 1) || (frame->exccause == PANIC_RSN_INTWDT_CPU1 && core_id == 0)) { + other_core_frame = frame; + while (1); + } + + //The core which triggers the interrupt watchdog will delay 1 us, so the other core can save its frame. + if (frame->exccause == PANIC_RSN_INTWDT_CPU0 || frame->exccause == PANIC_RSN_INTWDT_CPU1) { + ets_delay_us(1); + } + + if (frame->exccause == PANIC_RSN_CACHEERR && esp_cache_err_get_cpuid() != core_id) { + // Cache error interrupt will be handled by the panic handler + // on the other CPU. + while (1); + } +#endif //!CONFIG_FREERTOS_UNICORE + + haltOtherCore(); + esp_dport_access_int_abort(); + panicPutStr("Guru Meditation Error: Core "); + panicPutDec(core_id); + panicPutStr(" panic'ed ("); + panicPutStr(reason); + panicPutStr(")\r\n"); +#ifdef PANIC_COMPLETE_IN_ESP32C + if (frame->exccause == PANIC_RSN_DEBUGEXCEPTION) { + int debugRsn; + asm("rsr.debugcause %0":"=r"(debugRsn)); + panicPutStr("Debug exception reason: "); + if (debugRsn & XCHAL_DEBUGCAUSE_ICOUNT_MASK) { + panicPutStr("SingleStep "); + } + if (debugRsn & XCHAL_DEBUGCAUSE_IBREAK_MASK) { + panicPutStr("HwBreakpoint "); + } + if (debugRsn & XCHAL_DEBUGCAUSE_DBREAK_MASK) { + //Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK + //reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the + //debugcause if the cause is watchdog 1 and clearing it if it's watchdog 0. + if (debugRsn & (1 << 8)) { +#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK + const char *name = pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(core_id)); + panicPutStr("Stack canary watchpoint triggered ("); + panicPutStr(name); + panicPutStr(") "); +#else + panicPutStr("Watchpoint 1 triggered "); +#endif + } else { + panicPutStr("Watchpoint 0 triggered "); + } + } + if (debugRsn & XCHAL_DEBUGCAUSE_BREAK_MASK) { + panicPutStr("BREAK instr "); + } + if (debugRsn & XCHAL_DEBUGCAUSE_BREAKN_MASK) { + panicPutStr("BREAKN instr "); + } + if (debugRsn & XCHAL_DEBUGCAUSE_DEBUGINT_MASK) { + panicPutStr("DebugIntr "); + } + panicPutStr("\r\n"); + } + + if (esp_cpu_in_ocd_debug_mode()) { + disableAllWdts(); + if (frame->exccause == PANIC_RSN_INTWDT_CPU0 || + frame->exccause == PANIC_RSN_INTWDT_CPU1) { + TIMERG1.int_clr.wdt = 1; + } +#if CONFIG_ESP32_APPTRACE_ENABLE +#if CONFIG_SYSVIEW_ENABLE + SEGGER_RTT_ESP32_FlushNoLock(CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO); +#else + esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH, + APPTRACE_ONPANIC_HOST_FLUSH_TMO); +#endif +#endif + setFirstBreakpoint(frame->pc); + return; + } +#endif + commonErrorHandler(frame); +} + +void xt_unhandled_exception(XtExcFrame *frame) +{ + haltOtherCore(); + esp_dport_access_int_abort(); + if (!abort_called) { + panicPutStr("Guru Meditation Error: Core "); + panicPutDec(xPortGetCoreID()); + panicPutStr(" panic'ed ("); + int exccause = frame->exccause; + if (exccause < NUM_EDESCS) { + panicPutStr(edesc[exccause]); + } else { + panicPutStr("Unknown"); + } + panicPutStr(")"); +#ifdef PANIC_COMPLETE_IN_ESP32C + if (esp_cpu_in_ocd_debug_mode()) { + panicPutStr(" at pc="); + panicPutHex(frame->pc); + panicPutStr(". Setting bp and returning..\r\n"); +#if CONFIG_ESP32_APPTRACE_ENABLE +#if CONFIG_SYSVIEW_ENABLE + SEGGER_RTT_ESP32_FlushNoLock(CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO); +#else + esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH, + APPTRACE_ONPANIC_HOST_FLUSH_TMO); +#endif +#endif + //Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger + //will kick in exactly at the context the error happened. + setFirstBreakpoint(frame->pc); + return; + } +#endif + panicPutStr(". Exception was unhandled.\r\n"); + } + commonErrorHandler(frame); +} + + +/* + If watchdogs are enabled, the panic handler runs the risk of getting aborted pre-emptively because + an overzealous watchdog decides to reset it. On the other hand, if we disable all watchdogs, we run + the risk of somehow halting in the panic handler and not resetting. That is why this routine kills + all watchdogs except the timer group 0 watchdog, and it reconfigures that to reset the chip after + one second. +*/ +static void reconfigureAllWdts() +{ + TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_feed = 1; + TIMERG0.wdt_config0.sys_reset_length = 7; //3.2uS + TIMERG0.wdt_config0.cpu_reset_length = 7; //3.2uS + TIMERG0.wdt_config0.stg0 = TIMG_WDT_STG_SEL_RESET_SYSTEM; //1st stage timeout: reset system + TIMERG0.wdt_config1.clk_prescale = 80 * 500; //Prescaler: wdt counts in ticks of 0.5mS + TIMERG0.wdt_config2 = 2000; //1 second before reset + TIMERG0.wdt_config0.en = 1; + TIMERG0.wdt_wprotect = 0; + //Disable wdt 1 + TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config0.en = 0; + TIMERG1.wdt_wprotect = 0; +} + +/* + This disables all the watchdogs for when we call the gdbstub. +*/ +static inline void disableAllWdts() +{ + TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_config0.en = 0; + TIMERG0.wdt_wprotect = 0; + TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config0.en = 0; + TIMERG1.wdt_wprotect = 0; +} + +static void esp_panic_wdt_start() +{ + if (REG_GET_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_EN)) { + return; + } + WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); + WRITE_PERI_REG(RTC_CNTL_WDTFEED_REG, 1); + REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_SYS_RESET_LENGTH, 7); + REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_CPU_RESET_LENGTH, 7); + REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_STG0, RTC_WDT_STG_SEL_RESET_SYSTEM); + // 64KB of core dump data (stacks of about 30 tasks) will produce ~85KB base64 data. + // @ 115200 UART speed it will take more than 6 sec to print them out. + WRITE_PERI_REG(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 7); + REG_SET_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_EN); + WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, 0); +} + +void esp_panic_wdt_stop() +{ + WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); + WRITE_PERI_REG(RTC_CNTL_WDTFEED_REG, 1); + REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_STG0, RTC_WDT_STG_SEL_OFF); + REG_CLR_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_EN); + WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, 0); +} + +static void esp_panic_dig_reset() __attribute__((noreturn)); + +static void esp_panic_dig_reset() +{ + // make sure all the panic handler output is sent from UART FIFO + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + // switch to XTAL (otherwise we will keep running from the PLL) + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + // reset the digital part + esp_cpu_unstall(PRO_CPU_NUM); + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST); + while (true) { + ; + } +} + +static void putEntry(uint32_t pc, uint32_t sp) +{ + if (pc & 0x80000000) { + pc = (pc & 0x3fffffff) | 0x40000000; + } + panicPutStr(" 0x"); + panicPutHex(pc); + panicPutStr(":0x"); + panicPutHex(sp); +} + +static void doBacktrace(XtExcFrame *frame) +{ + uint32_t i = 0, pc = frame->pc, sp = frame->a1; + panicPutStr("\r\nBacktrace:"); + /* Do not check sanity on first entry, PC could be smashed. */ + putEntry(pc, sp); + pc = frame->a0; + while (i++ < 100) { + uint32_t psp = sp; + if (!esp_stack_ptr_is_sane(sp) || i++ > 100) { + break; + } + sp = *((uint32_t *) (sp - 0x10 + 4)); + putEntry(pc - 3, sp); // stack frame addresses are return addresses, so subtract 3 to get the CALL address + pc = *((uint32_t *) (psp - 0x10)); + if (pc < 0x40000000) { + break; + } + } + panicPutStr("\r\n\r\n"); +} + +/* + * Dump registers and do backtrace. + */ +static void commonErrorHandler_dump(XtExcFrame *frame, int core_id) +{ + int *regs = (int *)frame; + int x, y; + const char *sdesc[] = { + "PC ", "PS ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ", + "A6 ", "A7 ", "A8 ", "A9 ", "A10 ", "A11 ", "A12 ", "A13 ", + "A14 ", "A15 ", "SAR ", "EXCCAUSE", "EXCVADDR", "LBEG ", "LEND ", "LCOUNT " + }; + + /* only dump registers for 'real' crashes, if crashing via abort() + the register window is no longer useful. + */ + if (!abort_called) { + panicPutStr("Core"); + panicPutDec(core_id); + panicPutStr(" register dump:\r\n"); + + for (x = 0; x < 24; x += 4) { + for (y = 0; y < 4; y++) { + if (sdesc[x + y][0] != 0) { + panicPutStr(sdesc[x + y]); + panicPutStr(": 0x"); + panicPutHex(regs[x + y + 1]); + panicPutStr(" "); + } + } + panicPutStr("\r\n"); + } + + if (xPortInterruptedFromISRContext() +#if !CONFIG_FREERTOS_UNICORE + && other_core_frame != frame +#endif //!CONFIG_FREERTOS_UNICORE + ) { + //If the core which triggers the interrupt watchdog was in ISR context, dump the epc registers. + uint32_t __value; + panicPutStr("Core"); + panicPutDec(core_id); + panicPutStr(" was running in ISR context:\r\n"); + + __asm__("rsr.epc1 %0" : "=a"(__value)); + panicPutStr("EPC1 : 0x"); + panicPutHex(__value); + + __asm__("rsr.epc2 %0" : "=a"(__value)); + panicPutStr(" EPC2 : 0x"); + panicPutHex(__value); + + __asm__("rsr.epc3 %0" : "=a"(__value)); + panicPutStr(" EPC3 : 0x"); + panicPutHex(__value); + + __asm__("rsr.epc4 %0" : "=a"(__value)); + panicPutStr(" EPC4 : 0x"); + panicPutHex(__value); + + panicPutStr("\r\n"); + } + + } + + /* With windowed ABI backtracing is easy, let's do it. */ + doBacktrace(frame); + +} + +/* + We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the + serial port and either jump to the gdb stub, halt the CPU or reboot. +*/ +static __attribute__((noreturn)) void commonErrorHandler(XtExcFrame *frame) +{ + + int core_id = xPortGetCoreID(); + // start panic WDT to restart system if we hang in this handler + esp_panic_wdt_start(); + + //Feed the watchdogs, so they will give us time to print out debug info + reconfigureAllWdts(); + + commonErrorHandler_dump(frame, core_id); +#if !CONFIG_FREERTOS_UNICORE + if (other_core_frame != NULL) { + commonErrorHandler_dump((XtExcFrame *)other_core_frame, (core_id ? 0 : 1)); + } +#endif //!CONFIG_FREERTOS_UNICORE + +#if CONFIG_ESP32_APPTRACE_ENABLE + disableAllWdts(); +#if CONFIG_SYSVIEW_ENABLE + SEGGER_RTT_ESP32_FlushNoLock(CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO); +#else + esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH, + APPTRACE_ONPANIC_HOST_FLUSH_TMO); +#endif + reconfigureAllWdts(); +#endif + +#if CONFIG_ESP32_PANIC_GDBSTUB + disableAllWdts(); + esp_panic_wdt_stop(); + panicPutStr("Entering gdb stub now.\r\n"); + esp_gdbstub_panic_handler(frame); +#else +#if CONFIG_ESP32_ENABLE_COREDUMP + static bool s_dumping_core; + if (s_dumping_core) { + panicPutStr("Re-entered core dump! Exception happened during core dump!\r\n"); + } else { + disableAllWdts(); + s_dumping_core = true; +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + esp_core_dump_to_flash(frame); +#endif +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART && !CONFIG_ESP32_PANIC_SILENT_REBOOT + esp_core_dump_to_uart(frame); +#endif + s_dumping_core = false; + reconfigureAllWdts(); + } +#endif /* CONFIG_ESP32_ENABLE_COREDUMP */ + esp_panic_wdt_stop(); +#if CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT + panicPutStr("Rebooting...\r\n"); + if (frame->exccause != PANIC_RSN_CACHEERR) { + esp_restart_noos(); + } else { + // The only way to clear invalid cache access interrupt is to reset the digital part + esp_panic_dig_reset(); + } +#else + disableAllWdts(); + panicPutStr("CPU halted.\r\n"); + while (1); +#endif /* CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT */ +#endif /* CONFIG_ESP32_PANIC_GDBSTUB */ +} + + +void esp_set_breakpoint_if_jtag(void *fn) +{ + if (esp_cpu_in_ocd_debug_mode()) { + setFirstBreakpoint((uint32_t)fn); + } +} + + +esp_err_t esp_set_watchpoint(int no, void *adr, int size, int flags) +{ + int x; + if (no < 0 || no > 1) { + return ESP_ERR_INVALID_ARG; + } + if (flags & (~0xC0000000)) { + return ESP_ERR_INVALID_ARG; + } + int dbreakc = 0x3F; + //We support watching 2^n byte values, from 1 to 64. Calculate the mask for that. + for (x = 0; x < 7; x++) { + if (size == (1 << x)) { + break; + } + dbreakc <<= 1; + } + if (x == 7) { + return ESP_ERR_INVALID_ARG; + } + //Mask mask and add in flags. + dbreakc = (dbreakc & 0x3f) | flags; + + if (no == 0) { + asm volatile( + "wsr.dbreaka0 %0\n" \ + "wsr.dbreakc0 %1\n" \ + ::"r"(adr), "r"(dbreakc)); + } else { + asm volatile( + "wsr.dbreaka1 %0\n" \ + "wsr.dbreakc1 %1\n" \ + ::"r"(adr), "r"(dbreakc)); + } + return ESP_OK; +} + +void esp_clear_watchpoint(int no) +{ + //Setting a dbreakc register to 0 makes it trigger on neither load nor store, effectively disabling it. + int dbreakc = 0; + if (no == 0) { + asm volatile( + "wsr.dbreakc0 %0\n" \ + ::"r"(dbreakc)); + } else { + asm volatile( + "wsr.dbreakc1 %0\n" \ + ::"r"(dbreakc)); + } +} + +void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression) +{ + ets_printf("ESP_ERROR_CHECK failed: esp_err_t 0x%x", rc); +#ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP + ets_printf(" (%s)", esp_err_to_name(rc)); +#endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP + ets_printf(" at 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3); + if (spi_flash_cache_enabled()) { // strings may be in flash cache + ets_printf("file: \"%s\" line %d\nfunc: %s\nexpression: %s\n", file, line, function, expression); + } + invoke_abort(); +} diff --git a/components/esp32s2beta/pm_esp32s2beta.c b/components/esp32s2beta/pm_esp32s2beta.c new file mode 100644 index 0000000000..64b960c6f7 --- /dev/null +++ b/components/esp32s2beta/pm_esp32s2beta.c @@ -0,0 +1,573 @@ +// Copyright 2016-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 +#include +#include +#include + +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_pm.h" +#include "esp_log.h" +#include "esp32s2beta/clk.h" +#include "esp_private/crosscore_int.h" + +#include "soc/rtc.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/xtensa_timer.h" +#include "xtensa/core-macros.h" + +#include "esp_private/pm_impl.h" +#include "esp_private/pm_trace.h" +#include "esp_private/esp_timer_impl.h" +#include "esp32/pm.h" + +/* CCOMPARE update timeout, in CPU cycles. Any value above ~600 cycles will work + * for the purpose of detecting a deadlock. + */ +#define CCOMPARE_UPDATE_TIMEOUT 1000000 + +/* When changing CCOMPARE, don't allow changes if the difference is less + * than this. This is to prevent setting CCOMPARE below CCOUNT. + */ +#define CCOMPARE_MIN_CYCLES_IN_FUTURE 1000 + +/* When light sleep is used, wake this number of microseconds earlier than + * the next tick. + */ +#define LIGHT_SLEEP_EARLY_WAKEUP_US 100 + +#ifdef CONFIG_PM_PROFILING +#define WITH_PROFILING +#endif + + +static portMUX_TYPE s_switch_lock = portMUX_INITIALIZER_UNLOCKED; +/* The following state variables are protected using s_switch_lock: */ +/* Current sleep mode; When switching, contains old mode until switch is complete */ +static pm_mode_t s_mode = PM_MODE_CPU_MAX; +/* True when switch is in progress */ +static volatile bool s_is_switching; +/* When switch is in progress, this is the mode we are switching into */ +static pm_mode_t s_new_mode = PM_MODE_CPU_MAX; +/* Number of times each mode was locked */ +static size_t s_mode_lock_counts[PM_MODE_COUNT]; +/* Bit mask of locked modes. BIT(i) is set iff s_mode_lock_counts[i] > 0. */ +static uint32_t s_mode_mask; + +/* Divider and multiplier used to adjust (ccompare - ccount) duration. + * Only set to non-zero values when switch is in progress. + */ +static uint32_t s_ccount_div; +static uint32_t s_ccount_mul; + +/* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU. + * Used in conjunction with cross-core interrupt to update CCOMPARE on the other CPU. + */ +static volatile bool s_need_update_ccompare[portNUM_PROCESSORS]; + +/* When no RTOS tasks are active, these locks are released to allow going into + * a lower power mode. Used by ISR hook and idle hook. + */ +static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS]; + +/* A flag indicating that Idle hook has run on a given CPU; + * Next interrupt on the same CPU will take s_rtos_lock_handle. + */ +static bool s_core_idle[portNUM_PROCESSORS]; + +/* g_ticks_us defined in ROM for PRO CPU */ +extern uint32_t g_ticks_per_us_pro; + +/* Lookup table of CPU frequencies to be used in each mode. + * Initialized by esp_pm_impl_init and modified by esp_pm_configure. + */ +rtc_cpu_freq_t s_cpu_freq_by_mode[PM_MODE_COUNT]; + +/* Lookup table of CPU ticks per microsecond for each RTC_CPU_FREQ_ value. + * Essentially the same as returned by rtc_clk_cpu_freq_value(), but without + * the function call. Not const because XTAL frequency is only known at run time. + */ +static uint32_t s_cpu_freq_to_ticks[] = { + [RTC_CPU_FREQ_XTAL] = 0, /* This is set by esp_pm_impl_init */ + [RTC_CPU_FREQ_80M] = 80, + [RTC_CPU_FREQ_160M] = 160, + [RTC_CPU_FREQ_240M] = 240, + [RTC_CPU_FREQ_2M] = 2 +}; + +/* Lookup table of names for each RTC_CPU_FREQ_ value. Used for logging only. */ +static const char* s_freq_names[] __attribute__((unused)) = { + [RTC_CPU_FREQ_XTAL] = "XTAL", + [RTC_CPU_FREQ_80M] = "80", + [RTC_CPU_FREQ_160M] = "160", + [RTC_CPU_FREQ_240M] = "240", + [RTC_CPU_FREQ_2M] = "2" +}; + +/* Whether automatic light sleep is enabled */ +static bool s_light_sleep_en = false; + +/* When configuration is changed, current frequency may not match the + * newly configured frequency for the current mode. This is an indicator + * to the mode switch code to get the actual current frequency instead of + * relying on the current mode. + */ +static bool s_config_changed = false; + +#ifdef WITH_PROFILING +/* Time, in microseconds, spent so far in each mode */ +static pm_time_t s_time_in_mode[PM_MODE_COUNT]; +/* Timestamp, in microseconds, when the mode switch last happened */ +static pm_time_t s_last_mode_change_time; +/* User-readable mode names, used by esp_pm_impl_dump_stats */ +static const char* s_mode_names[] = { + "SLEEP", + "APB_MIN", + "APB_MAX", + "CPU_MAX" +}; +#endif // WITH_PROFILING + + +static const char* TAG = "pm_esp32"; + +static void update_ccompare(); +static void do_switch(pm_mode_t new_mode); +static void leave_idle(); +static void on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_per_us); + + +pm_mode_t esp_pm_impl_get_mode(esp_pm_lock_type_t type, int arg) +{ + (void) arg; + if (type == ESP_PM_CPU_FREQ_MAX) { + return PM_MODE_CPU_MAX; + } else if (type == ESP_PM_APB_FREQ_MAX) { + return PM_MODE_APB_MAX; + } else if (type == ESP_PM_NO_LIGHT_SLEEP) { + return PM_MODE_APB_MIN; + } else { + // unsupported mode + abort(); + } +} + +/* rtc_cpu_freq_t enum is not ordered by frequency, so convert to MHz, + * figure out the maximum value, then convert back to rtc_cpu_freq_t. + */ +static rtc_cpu_freq_t max_freq_of(rtc_cpu_freq_t f1, rtc_cpu_freq_t f2) +{ + int f1_hz = rtc_clk_cpu_freq_value(f1); + int f2_hz = rtc_clk_cpu_freq_value(f2); + int f_max_hz = MAX(f1_hz, f2_hz); + rtc_cpu_freq_t result = RTC_CPU_FREQ_XTAL; + if (!rtc_clk_cpu_freq_from_mhz(f_max_hz/1000000, &result)) { + assert(false && "unsupported frequency"); + } + return result; +} + +esp_err_t esp_pm_configure(const void* vconfig) +{ +#ifndef CONFIG_PM_ENABLE + return ESP_ERR_NOT_SUPPORTED; +#endif + + const esp_pm_config_esp32_t* config = (const esp_pm_config_esp32_t*) vconfig; +#ifndef CONFIG_FREERTOS_USE_TICKLESS_IDLE + if (config->light_sleep_enable) { + return ESP_ERR_NOT_SUPPORTED; + } +#endif + + if (config->min_cpu_freq == RTC_CPU_FREQ_2M) { + /* Minimal APB frequency to achieve 1MHz REF_TICK frequency is 5 MHz */ + return ESP_ERR_NOT_SUPPORTED; + } + + rtc_cpu_freq_t min_freq = config->min_cpu_freq; + rtc_cpu_freq_t max_freq = config->max_cpu_freq; + int min_freq_mhz = rtc_clk_cpu_freq_value(min_freq); + int max_freq_mhz = rtc_clk_cpu_freq_value(max_freq); + if (min_freq_mhz > max_freq_mhz) { + return ESP_ERR_INVALID_ARG; + } + + rtc_cpu_freq_t apb_max_freq = max_freq; /* CPU frequency in APB_MAX mode */ + if (max_freq == RTC_CPU_FREQ_240M) { + /* We can't switch between 240 and 80/160 without disabling PLL, + * so use 240MHz CPU frequency when 80MHz APB frequency is requested. + */ + apb_max_freq = RTC_CPU_FREQ_240M; + } else if (max_freq == RTC_CPU_FREQ_160M || max_freq == RTC_CPU_FREQ_80M) { + /* Otherwise, can use 80MHz + * CPU frequency when 80MHz APB frequency is requested. + */ + apb_max_freq = RTC_CPU_FREQ_80M; + } + + apb_max_freq = max_freq_of(apb_max_freq, min_freq); + + ESP_LOGI(TAG, "Frequency switching config: " + "CPU_MAX: %s, APB_MAX: %s, APB_MIN: %s, Light sleep: %s", + s_freq_names[max_freq], + s_freq_names[apb_max_freq], + s_freq_names[min_freq], + config->light_sleep_enable ? "ENABLED" : "DISABLED"); + + portENTER_CRITICAL(&s_switch_lock); + s_cpu_freq_by_mode[PM_MODE_CPU_MAX] = max_freq; + s_cpu_freq_by_mode[PM_MODE_APB_MAX] = apb_max_freq; + s_cpu_freq_by_mode[PM_MODE_APB_MIN] = min_freq; + s_cpu_freq_by_mode[PM_MODE_LIGHT_SLEEP] = min_freq; + s_light_sleep_en = config->light_sleep_enable; + s_config_changed = true; + portEXIT_CRITICAL(&s_switch_lock); + + return ESP_OK; +} + +static pm_mode_t IRAM_ATTR get_lowest_allowed_mode() +{ + /* TODO: optimize using ffs/clz */ + if (s_mode_mask >= BIT(PM_MODE_CPU_MAX)) { + return PM_MODE_CPU_MAX; + } else if (s_mode_mask >= BIT(PM_MODE_APB_MAX)) { + return PM_MODE_APB_MAX; + } else if (s_mode_mask >= BIT(PM_MODE_APB_MIN) || !s_light_sleep_en) { + return PM_MODE_APB_MIN; + } else { + return PM_MODE_LIGHT_SLEEP; + } +} + +void IRAM_ATTR esp_pm_impl_switch_mode(pm_mode_t mode, + pm_mode_switch_t lock_or_unlock, pm_time_t now) +{ + bool need_switch = false; + uint32_t mode_mask = BIT(mode); + portENTER_CRITICAL(&s_switch_lock); + uint32_t count; + if (lock_or_unlock == MODE_LOCK) { + count = ++s_mode_lock_counts[mode]; + } else { + count = s_mode_lock_counts[mode]--; + } + if (count == 1) { + if (lock_or_unlock == MODE_LOCK) { + s_mode_mask |= mode_mask; + } else { + s_mode_mask &= ~mode_mask; + } + need_switch = true; + } + + pm_mode_t new_mode = s_mode; + if (need_switch) { + new_mode = get_lowest_allowed_mode(); +#ifdef WITH_PROFILING + if (s_last_mode_change_time != 0) { + pm_time_t diff = now - s_last_mode_change_time; + s_time_in_mode[s_mode] += diff; + } + s_last_mode_change_time = now; +#endif // WITH_PROFILING + } + portEXIT_CRITICAL(&s_switch_lock); + if (need_switch && new_mode != s_mode) { + do_switch(new_mode); + } +} + +/** + * @brief Update clock dividers in esp_timer and FreeRTOS, and adjust CCOMPARE + * values on both CPUs. + * @param old_ticks_per_us old CPU frequency + * @param ticks_per_us new CPU frequency + */ +static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_per_us) +{ + uint32_t old_apb_ticks_per_us = MIN(old_ticks_per_us, 80); + uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80); + /* Update APB frequency value used by the timer */ + if (old_apb_ticks_per_us != apb_ticks_per_us) { + esp_timer_impl_update_apb_freq(apb_ticks_per_us); + } + + /* Calculate new tick divisor */ + _xt_tick_divisor = ticks_per_us * 1000000 / XT_TICK_PER_SEC; + + int core_id = xPortGetCoreID(); + if (s_rtos_lock_handle[core_id] != NULL) { + ESP_PM_TRACE_ENTER(CCOMPARE_UPDATE, core_id); + /* ccount_div and ccount_mul are used in esp_pm_impl_update_ccompare + * to calculate new CCOMPARE value. + */ + s_ccount_div = old_ticks_per_us; + s_ccount_mul = ticks_per_us; + + /* Update CCOMPARE value on this CPU */ + update_ccompare(); + +#if portNUM_PROCESSORS == 2 + /* Send interrupt to the other CPU to update CCOMPARE value */ + int other_core_id = (core_id == 0) ? 1 : 0; + + s_need_update_ccompare[other_core_id] = true; + esp_crosscore_int_send_freq_switch(other_core_id); + + int timeout = 0; + while (s_need_update_ccompare[other_core_id]) { + if (++timeout == CCOMPARE_UPDATE_TIMEOUT) { + assert(false && "failed to update CCOMPARE, possible deadlock"); + } + } +#endif // portNUM_PROCESSORS == 2 + + s_ccount_mul = 0; + s_ccount_div = 0; + ESP_PM_TRACE_EXIT(CCOMPARE_UPDATE, core_id); + } +} + +/** + * Perform the switch to new power mode. + * Currently only changes the CPU frequency and adjusts clock dividers. + * No light sleep yet. + * @param new_mode mode to switch to + */ +static void IRAM_ATTR do_switch(pm_mode_t new_mode) +{ + const int core_id = xPortGetCoreID(); + + do { + portENTER_CRITICAL_ISR(&s_switch_lock); + if (!s_is_switching) { + break; + } + if (s_new_mode <= new_mode) { + portEXIT_CRITICAL_ISR(&s_switch_lock); + return; + } + if (s_need_update_ccompare[core_id]) { + s_need_update_ccompare[core_id] = false; + } + portEXIT_CRITICAL_ISR(&s_switch_lock); + } while (true); + s_new_mode = new_mode; + s_is_switching = true; + bool config_changed = s_config_changed; + s_config_changed = false; + portEXIT_CRITICAL_ISR(&s_switch_lock); + + rtc_cpu_freq_t new_freq = s_cpu_freq_by_mode[new_mode]; + rtc_cpu_freq_t old_freq; + if (!config_changed) { + old_freq = s_cpu_freq_by_mode[s_mode]; + } else { + old_freq = rtc_clk_cpu_freq_get(); + } + + if (new_freq != old_freq) { + uint32_t old_ticks_per_us = g_ticks_per_us_pro; + uint32_t new_ticks_per_us = s_cpu_freq_to_ticks[new_freq]; + + bool switch_down = new_ticks_per_us < old_ticks_per_us; + + ESP_PM_TRACE_ENTER(FREQ_SWITCH, core_id); + if (switch_down) { + on_freq_update(old_ticks_per_us, new_ticks_per_us); + } + rtc_clk_cpu_freq_set_fast(new_freq); + if (!switch_down) { + on_freq_update(old_ticks_per_us, new_ticks_per_us); + } + ESP_PM_TRACE_EXIT(FREQ_SWITCH, core_id); + } + + portENTER_CRITICAL_ISR(&s_switch_lock); + s_mode = new_mode; + s_is_switching = false; + portEXIT_CRITICAL_ISR(&s_switch_lock); +} + +/** + * @brief Calculate new CCOMPARE value based on s_ccount_{mul,div} + * + * Adjusts CCOMPARE value so that the interrupt happens at the same time as it + * would happen without the frequency change. + * Assumes that the new_frequency = old_frequency * s_ccount_mul / s_ccount_div. + */ +static void IRAM_ATTR update_ccompare() +{ + uint32_t ccount = XTHAL_GET_CCOUNT(); + uint32_t ccompare = XTHAL_GET_CCOMPARE(XT_TIMER_INDEX); + if ((ccompare - CCOMPARE_MIN_CYCLES_IN_FUTURE) - ccount < UINT32_MAX / 2) { + uint32_t diff = ccompare - ccount; + uint32_t diff_scaled = (diff * s_ccount_mul + s_ccount_div - 1) / s_ccount_div; + if (diff_scaled < _xt_tick_divisor) { + uint32_t new_ccompare = ccount + diff_scaled; + XTHAL_SET_CCOMPARE(XT_TIMER_INDEX, new_ccompare); + } + } +} + +static void IRAM_ATTR leave_idle() +{ + int core_id = xPortGetCoreID(); + if (s_core_idle[core_id]) { + // TODO: possible optimization: raise frequency here first + esp_pm_lock_acquire(s_rtos_lock_handle[core_id]); + s_core_idle[core_id] = false; + } +} + +void esp_pm_impl_idle_hook() +{ + int core_id = xPortGetCoreID(); + uint32_t state = portENTER_CRITICAL_NESTED(); + if (!s_core_idle[core_id]) { + esp_pm_lock_release(s_rtos_lock_handle[core_id]); + s_core_idle[core_id] = true; + } + portEXIT_CRITICAL_NESTED(state); + ESP_PM_TRACE_ENTER(IDLE, core_id); +} + +void IRAM_ATTR esp_pm_impl_isr_hook() +{ + int core_id = xPortGetCoreID(); + ESP_PM_TRACE_ENTER(ISR_HOOK, core_id); +#if portNUM_PROCESSORS == 2 + if (s_need_update_ccompare[core_id]) { + update_ccompare(); + s_need_update_ccompare[core_id] = false; + } else { + leave_idle(); + } +#else + leave_idle(); +#endif // portNUM_PROCESSORS == 2 + ESP_PM_TRACE_EXIT(ISR_HOOK, core_id); +} + +#if CONFIG_FREERTOS_USE_TICKLESS_IDLE + +bool IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) +{ + bool result = false; + portENTER_CRITICAL(&s_switch_lock); + if (s_mode == PM_MODE_LIGHT_SLEEP && !s_is_switching) { + /* Calculate how much we can sleep */ + int64_t next_esp_timer_alarm = esp_timer_get_next_alarm(); + int64_t now = esp_timer_get_time(); + int64_t time_until_next_alarm = next_esp_timer_alarm - now; + int64_t wakeup_delay_us = portTICK_PERIOD_MS * 1000LL * xExpectedIdleTime; + int64_t sleep_time_us = MIN(wakeup_delay_us, time_until_next_alarm); + if (sleep_time_us >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP * portTICK_PERIOD_MS * 1000LL) { + esp_sleep_enable_timer_wakeup(sleep_time_us - LIGHT_SLEEP_EARLY_WAKEUP_US); +#ifdef CONFIG_PM_TRACE + /* to force tracing GPIOs to keep state */ + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); +#endif + /* Enter sleep */ + int core_id = xPortGetCoreID(); + ESP_PM_TRACE_ENTER(SLEEP, core_id); + int64_t sleep_start = esp_timer_get_time(); + esp_light_sleep_start(); + int64_t slept_us = esp_timer_get_time() - sleep_start; + ESP_PM_TRACE_EXIT(SLEEP, core_id); + + uint32_t slept_ticks = slept_us / (portTICK_PERIOD_MS * 1000LL); + if (slept_ticks > 0) { + /* Adjust RTOS tick count based on the amount of time spent in sleep */ + vTaskStepTick(slept_ticks); + + /* Trigger tick interrupt, since sleep time was longer + * than portTICK_PERIOD_MS. Note that setting INTSET does not + * work for timer interrupt, and changing CCOMPARE would clear + * the interrupt flag. + */ + XTHAL_SET_CCOUNT(XTHAL_GET_CCOMPARE(XT_TIMER_INDEX) - 16); + while (!(XTHAL_GET_INTERRUPT() & BIT(XT_TIMER_INTNUM))) { + ; + } + } + result = true; + } + } + portEXIT_CRITICAL(&s_switch_lock); + return result; +} +#endif //CONFIG_FREERTOS_USE_TICKLESS_IDLE + +#ifdef WITH_PROFILING +void esp_pm_impl_dump_stats(FILE* out) +{ + pm_time_t time_in_mode[PM_MODE_COUNT]; + + portENTER_CRITICAL_ISR(&s_switch_lock); + memcpy(time_in_mode, s_time_in_mode, sizeof(time_in_mode)); + pm_time_t last_mode_change_time = s_last_mode_change_time; + pm_mode_t cur_mode = s_mode; + pm_time_t now = pm_get_time(); + portEXIT_CRITICAL_ISR(&s_switch_lock); + + time_in_mode[cur_mode] += now - last_mode_change_time; + + fprintf(out, "Mode stats:\n"); + for (int i = 0; i < PM_MODE_COUNT; ++i) { + if (i == PM_MODE_LIGHT_SLEEP && !s_light_sleep_en) { + /* don't display light sleep mode if it's not enabled */ + continue; + } + fprintf(out, "%8s %6s %12lld %2d%%\n", + s_mode_names[i], + s_freq_names[s_cpu_freq_by_mode[i]], + time_in_mode[i], + (int) (time_in_mode[i] * 100 / now)); + } +} +#endif // WITH_PROFILING + +void esp_pm_impl_init() +{ + s_cpu_freq_to_ticks[RTC_CPU_FREQ_XTAL] = rtc_clk_xtal_freq_get(); +#ifdef CONFIG_PM_TRACE + esp_pm_trace_init(); +#endif + ESP_ERROR_CHECK(esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "rtos0", + &s_rtos_lock_handle[0])); + ESP_ERROR_CHECK(esp_pm_lock_acquire(s_rtos_lock_handle[0])); +#if portNUM_PROCESSORS == 2 + ESP_ERROR_CHECK(esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "rtos1", + &s_rtos_lock_handle[1])); + ESP_ERROR_CHECK(esp_pm_lock_acquire(s_rtos_lock_handle[1])); +#endif // portNUM_PROCESSORS == 2 + + /* Configure all modes to use the default CPU frequency. + * This will be modified later by a call to esp_pm_configure. + */ + rtc_cpu_freq_t default_freq; + if (!rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ, &default_freq)) { + assert(false && "unsupported frequency"); + } + for (size_t i = 0; i < PM_MODE_COUNT; ++i) { + s_cpu_freq_by_mode[i] = default_freq; + } +} diff --git a/components/esp32s2beta/pm_trace.c b/components/esp32s2beta/pm_trace.c new file mode 100644 index 0000000000..b4cebf85fe --- /dev/null +++ b/components/esp32s2beta/pm_trace.c @@ -0,0 +1,52 @@ +// Copyright 2016-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 "esp_private/pm_trace.h" +#include "driver/gpio.h" +#include "soc/gpio_reg.h" + +/* GPIOs to use for tracing of esp_pm events. + * Two entries in the array for each type, one for each CPU. + * Feel free to change when debugging. + */ +static const int DRAM_ATTR s_trace_io[] = { + BIT(4), BIT(5), // ESP_PM_TRACE_IDLE + BIT(16), BIT(17), // ESP_PM_TRACE_TICK + BIT(18), BIT(18), // ESP_PM_TRACE_FREQ_SWITCH + BIT(19), BIT(19), // ESP_PM_TRACE_CCOMPARE_UPDATE + BIT(25), BIT(26), // ESP_PM_TRACE_ISR_HOOK + BIT(27), BIT(27), // ESP_PM_TRACE_SLEEP +}; + +void esp_pm_trace_init() +{ + for (size_t i = 0; i < sizeof(s_trace_io)/sizeof(s_trace_io[0]); ++i) { + int io = __builtin_ffs(s_trace_io[i]); + if (io == 0) { + continue; + } + gpio_set_direction(io - 1, GPIO_MODE_OUTPUT); + } +} + +void IRAM_ATTR esp_pm_trace_enter(esp_pm_trace_event_t event, int core_id) +{ + REG_WRITE(GPIO_OUT_W1TS_REG, s_trace_io[2 * event + core_id]); +} + +void IRAM_ATTR esp_pm_trace_exit(esp_pm_trace_event_t event, int core_id) +{ + REG_WRITE(GPIO_OUT_W1TC_REG, s_trace_io[2 * event + core_id]); +} diff --git a/components/esp32s2beta/sleep_modes.c b/components/esp32s2beta/sleep_modes.c new file mode 100644 index 0000000000..7820b2d8f6 --- /dev/null +++ b/components/esp32s2beta/sleep_modes.c @@ -0,0 +1,650 @@ +// 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 +#include +#include +#include "esp_attr.h" +#include "esp_sleep.h" +#include "esp_private/esp_timer_impl.h" +#include "esp_log.h" +#include "esp32s2beta/clk.h" +#include "esp_newlib.h" +#include "esp_spi_flash.h" +#include "esp32s2beta/rom/cache.h" +#include "esp32s2beta/rom/rtc.h" +#include "esp32s2beta/rom/uart.h" +#include "soc/cpu.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/apb_ctrl_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/spi_mem_reg.h" +#include "soc/sens_reg.h" +#include "soc/dport_reg.h" +#include "driver/rtc_io.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sdkconfig.h" + +// If light sleep time is less than that, don't power down flash +#define FLASH_PD_MIN_SLEEP_TIME_US 2000 + +// Time from VDD_SDIO power up to first flash read in ROM code +#define VDD_SDIO_POWERUP_TO_FLASH_READ_US 700 + +// Extra time it takes to enter and exit light sleep and deep sleep +// For deep sleep, this is until the wake stub runs (not the app). +#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL +#define LIGHT_SLEEP_TIME_OVERHEAD_US (650 + 30 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) +#define DEEP_SLEEP_TIME_OVERHEAD_US (650 + 100 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) +#else +#define LIGHT_SLEEP_TIME_OVERHEAD_US (250 + 30 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) +#define DEEP_SLEEP_TIME_OVERHEAD_US (250 + 100 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) +#endif // CONFIG_ESP32_RTC_CLOCK_SOURCE + +// Minimal amount of time we can sleep for +#define LIGHT_SLEEP_MIN_TIME_US 200 + +#define CHECK_SOURCE(source, value, mask) ((s_config.wakeup_triggers & mask) && \ + (source == value)) + +/** + * Internal structure which holds all requested deep sleep parameters + */ +typedef struct { + esp_sleep_pd_option_t pd_options[ESP_PD_DOMAIN_MAX]; + uint64_t sleep_duration; + uint32_t wakeup_triggers : 11; + uint32_t ext1_trigger_mode : 1; + uint32_t ext1_rtc_gpio_mask : 18; + uint32_t ext0_trigger_level : 1; + uint32_t ext0_rtc_gpio_num : 5; + uint32_t sleep_time_adjustment; + uint64_t rtc_ticks_at_sleep_start; +} sleep_config_t; + +static sleep_config_t s_config = { + .pd_options = { ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO }, + .wakeup_triggers = 0 +}; + +/* Updating RTC_MEMORY_CRC_REG register via set_rtc_memory_crc() + is not thread-safe. */ +static _lock_t lock_rtc_memory_crc; + +static const char* TAG = "sleep"; + +static uint32_t get_power_down_flags(); +static void ext0_wakeup_prepare(); +static void ext1_wakeup_prepare(); +static void timer_wakeup_prepare(); + +/* Wake from deep sleep stub + See esp_deepsleep.h esp_wake_deep_sleep() comments for details. +*/ +esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void) +{ + _lock_acquire(&lock_rtc_memory_crc); + uint32_t stored_crc = REG_READ(RTC_MEMORY_CRC_REG); + set_rtc_memory_crc(); + uint32_t calc_crc = REG_READ(RTC_MEMORY_CRC_REG); + REG_WRITE(RTC_MEMORY_CRC_REG, stored_crc); + _lock_release(&lock_rtc_memory_crc); + + if(stored_crc == calc_crc) { + return (esp_deep_sleep_wake_stub_fn_t)REG_READ(RTC_ENTRY_ADDR_REG); + } else { + return NULL; + } +} + +void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub) +{ + _lock_acquire(&lock_rtc_memory_crc); + REG_WRITE(RTC_ENTRY_ADDR_REG, (uint32_t)new_stub); + set_rtc_memory_crc(); + _lock_release(&lock_rtc_memory_crc); +} + +void RTC_IRAM_ATTR esp_default_wake_deep_sleep(void) { + /* Clear MMU for CPU 0 */ + _DPORT_REG_SET_BIT(DPORT_PRO_CACHE_IA_INT_EN_REG, DPORT_PRO_CACHE_INT_CLR); + _DPORT_REG_SET_BIT(DPORT_PRO_CACHE_IA_INT_EN_REG, DPORT_PRO_CACHE_DBG_EN); + +#if CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY > 0 + // ROM code has not started yet, so we need to set delay factor + // used by ets_delay_us first. + ets_update_cpu_frequency(ets_get_xtal_freq() / 1000000); + // This delay is configured in menuconfig, it can be used to give + // the flash chip some time to become ready. + ets_delay_us(CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY); +#endif +} + +void __attribute__((weak, alias("esp_default_wake_deep_sleep"))) esp_wake_deep_sleep(void); + +void esp_deep_sleep(uint64_t time_in_us) +{ + esp_sleep_enable_timer_wakeup(time_in_us); + esp_deep_sleep_start(); +} + +static void IRAM_ATTR suspend_uarts() +{ + for (int i = 0; i < 2; ++i) { + REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XOFF); + uart_tx_wait_idle(i); + } +} + +static void IRAM_ATTR resume_uarts() +{ + for (int i = 0; i < 2; ++i) { + REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XOFF); + REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XON); + REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XON); + } +} + +static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) +{ + // Stop UART output so that output is not lost due to APB frequency change + suspend_uarts(); + + // Save current frequency and switch to XTAL + rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get(); + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + + // Configure pins for external wakeup + if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) { + ext0_wakeup_prepare(); + } + if (s_config.wakeup_triggers & RTC_EXT1_TRIG_EN) { + ext1_wakeup_prepare(); + } + // Enable ULP wakeup + if (s_config.wakeup_triggers & RTC_ULP_TRIG_EN) { + //TODO, esp32s2 does not have this bit + //SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_WAKEUP_FORCE_EN); + } + + // Enter sleep + rtc_sleep_config_t config = RTC_SLEEP_CONFIG_DEFAULT(pd_flags); + rtc_sleep_init(config); + + // Configure timer wakeup + if ((s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) && + s_config.sleep_duration > 0) { + timer_wakeup_prepare(); + } + uint32_t result = rtc_sleep_start(s_config.wakeup_triggers, 0,0); + + // Restore CPU frequency + rtc_clk_cpu_freq_set(cpu_freq); + + // re-enable UART output + resume_uarts(); + + return result; +} + +void IRAM_ATTR esp_deep_sleep_start() +{ + // record current RTC time + s_config.rtc_ticks_at_sleep_start = rtc_time_get(); + esp_sync_counters_rtc_and_frc(); + // Configure wake stub + if (esp_get_deep_sleep_wake_stub() == NULL) { + esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep); + } + + // Decide which power domains can be powered down + uint32_t pd_flags = get_power_down_flags(); + + // Correct the sleep time + s_config.sleep_time_adjustment = DEEP_SLEEP_TIME_OVERHEAD_US; + + // Enter sleep + esp_sleep_start(RTC_SLEEP_PD_DIG | RTC_SLEEP_PD_VDDSDIO | pd_flags); + + // Because RTC is in a slower clock domain than the CPU, it + // can take several CPU cycles for the sleep mode to start. + while (1) { + ; + } +} + +static void rtc_wdt_enable(int time_ms) +{ + WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); + WRITE_PERI_REG(RTC_CNTL_WDTFEED_REG, 1); + REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_SYS_RESET_LENGTH, 7); + REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_CPU_RESET_LENGTH, 7); + REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_STG0, RTC_WDT_STG_SEL_RESET_RTC); + WRITE_PERI_REG(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * time_ms / 1000); + SET_PERI_REG_MASK(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_EN | RTC_CNTL_WDT_PAUSE_IN_SLP); + WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, 0); +} + +static void rtc_wdt_disable() +{ + WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); + WRITE_PERI_REG(RTC_CNTL_WDTFEED_REG, 1); + REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_STG0, RTC_WDT_STG_SEL_OFF); + REG_CLR_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_EN); + WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, 0); +} + +/** + * Helper function which handles entry to and exit from light sleep + * Placed into IRAM as flash may need some time to be powered on. + */ +static esp_err_t esp_light_sleep_inner(uint32_t pd_flags, + uint32_t flash_enable_time_us, + rtc_vddsdio_config_t vddsdio_config) IRAM_ATTR __attribute__((noinline)); + +static esp_err_t esp_light_sleep_inner(uint32_t pd_flags, + uint32_t flash_enable_time_us, + rtc_vddsdio_config_t vddsdio_config) +{ + // Enter sleep + esp_err_t err = esp_sleep_start(pd_flags); + + // If VDDSDIO regulator was controlled by RTC registers before sleep, + // restore the configuration. + if (vddsdio_config.force) { + rtc_vddsdio_set_config(vddsdio_config); + } + + // If SPI flash was powered down, wait for it to become ready + if (pd_flags & RTC_SLEEP_PD_VDDSDIO) { + // Wait for the flash chip to start up + ets_delay_us(flash_enable_time_us); + } + return err; +} + +esp_err_t esp_light_sleep_start() +{ + static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED; + portENTER_CRITICAL(&light_sleep_lock); + /* We will be calling esp_timer_impl_advance inside DPORT access critical + * section. Make sure the code on the other CPU is not holding esp_timer + * lock, otherwise there will be deadlock. + */ + esp_timer_impl_lock(); + s_config.rtc_ticks_at_sleep_start = rtc_time_get(); + uint64_t frc_time_at_start = esp_timer_get_time(); + DPORT_STALL_OTHER_CPU_START(); + + // Decide which power domains can be powered down + uint32_t pd_flags = get_power_down_flags(); + + // Amount of time to subtract from actual sleep time. + // This is spent on entering and leaving light sleep. + s_config.sleep_time_adjustment = LIGHT_SLEEP_TIME_OVERHEAD_US; + + // Decide if VDD_SDIO needs to be powered down; + // If it needs to be powered down, adjust sleep time. + const uint32_t flash_enable_time_us = VDD_SDIO_POWERUP_TO_FLASH_READ_US + + CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY; + +#ifndef CONFIG_SPIRAM_SUPPORT + const uint32_t vddsdio_pd_sleep_duration = MAX(FLASH_PD_MIN_SLEEP_TIME_US, + flash_enable_time_us + LIGHT_SLEEP_TIME_OVERHEAD_US + LIGHT_SLEEP_MIN_TIME_US); + + if (s_config.sleep_duration > vddsdio_pd_sleep_duration) { + pd_flags |= RTC_SLEEP_PD_VDDSDIO; + s_config.sleep_time_adjustment += flash_enable_time_us; + } +#endif //CONFIG_SPIRAM_SUPPORT + + rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config(); + + // Safety net: enable WDT in case exit from light sleep fails + rtc_wdt_enable(1000); + + // Enter sleep, then wait for flash to be ready on wakeup + esp_err_t err = esp_light_sleep_inner(pd_flags, + flash_enable_time_us, vddsdio_config); + + // FRC1 has been clock gated for the duration of the sleep, correct for that. + uint64_t rtc_ticks_at_end = rtc_time_get(); + uint64_t frc_time_at_end = esp_timer_get_time(); + + uint64_t rtc_time_diff = rtc_time_slowclk_to_us(rtc_ticks_at_end - s_config.rtc_ticks_at_sleep_start, + esp_clk_slowclk_cal_get()); + uint64_t frc_time_diff = frc_time_at_end - frc_time_at_start; + + int64_t time_diff = rtc_time_diff - frc_time_diff; + /* Small negative values (up to 1 RTC_SLOW clock period) are possible, + * for very small values of sleep_duration. Ignore those to keep esp_timer + * monotonic. + */ + if (time_diff > 0) { + esp_timer_impl_advance(time_diff); + } + esp_set_time_from_rtc(); + + esp_timer_impl_unlock(); + DPORT_STALL_OTHER_CPU_END(); + rtc_wdt_disable(); + portEXIT_CRITICAL(&light_sleep_lock); + return err; +} + +void system_deep_sleep(uint64_t) __attribute__((alias("esp_deep_sleep"))); + +esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source) +{ + // For most of sources it is enough to set trigger mask in local + // configuration structure. The actual RTC wake up options + // will be updated by esp_sleep_start(). + if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_TIMER, RTC_TIMER_TRIG_EN)) { + s_config.wakeup_triggers &= ~RTC_TIMER_TRIG_EN; + s_config.sleep_duration = 0; + } + else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_EXT0, RTC_EXT0_TRIG_EN)) { + s_config.ext0_rtc_gpio_num = 0; + s_config.ext0_trigger_level = 0; + s_config.wakeup_triggers &= ~RTC_EXT0_TRIG_EN; + } + else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_EXT1, RTC_EXT1_TRIG_EN)) { + s_config.ext1_rtc_gpio_mask = 0; + s_config.ext1_trigger_mode = 0; + s_config.wakeup_triggers &= ~RTC_EXT1_TRIG_EN; + } + else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_TOUCHPAD, RTC_TOUCH_TRIG_EN)) { + s_config.wakeup_triggers &= ~RTC_TOUCH_TRIG_EN; + } +#ifdef CONFIG_ULP_COPROC_ENABLED + else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_ULP, RTC_ULP_TRIG_EN)) { + s_config.wakeup_triggers &= ~RTC_ULP_TRIG_EN; + } +#endif + else { + ESP_LOGE(TAG, "Incorrect wakeup source (%d) to disable.", (int) source); + return ESP_ERR_INVALID_STATE; + } + return ESP_OK; +} + +esp_err_t esp_sleep_enable_ulp_wakeup() +{ +#ifdef CONFIG_ULP_COPROC_ENABLED + if(s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) { + ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0"); + return ESP_ERR_INVALID_STATE; + } + s_config.wakeup_triggers |= RTC_ULP_TRIG_EN; + return ESP_OK; +#else + return ESP_ERR_INVALID_STATE; +#endif +} + +esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us) +{ + s_config.wakeup_triggers |= RTC_TIMER_TRIG_EN; + s_config.sleep_duration = time_in_us; + return ESP_OK; +} + +static void timer_wakeup_prepare() +{ + uint32_t period = esp_clk_slowclk_cal_get(); + int64_t sleep_duration = (int64_t) s_config.sleep_duration - (int64_t) s_config.sleep_time_adjustment; + if (sleep_duration < 0) { + sleep_duration = 0; + } + int64_t rtc_count_delta = rtc_time_us_to_slowclk(sleep_duration, period); + + rtc_sleep_set_wakeup_time(s_config.rtc_ticks_at_sleep_start + rtc_count_delta); +} + +esp_err_t esp_sleep_enable_touchpad_wakeup() +{ + if (s_config.wakeup_triggers & (RTC_EXT0_TRIG_EN)) { + ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0"); + return ESP_ERR_INVALID_STATE; + } + s_config.wakeup_triggers |= RTC_TOUCH_TRIG_EN; + return ESP_OK; +} + +touch_pad_t esp_sleep_get_touchpad_wakeup_status() +{ + if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TOUCHPAD) { + return TOUCH_PAD_MAX; + } + uint32_t touch_mask = REG_GET_FIELD(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_EN); + assert(touch_mask != 0 && "wakeup reason is RTC_TOUCH_TRIG_EN but SENS_TOUCH_MEAS_EN is zero"); + return (touch_pad_t) (__builtin_ffs(touch_mask) - 1); +} + +esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level) +{ + if (level < 0 || level > 1) { + return ESP_ERR_INVALID_ARG; + } + if (!RTC_GPIO_IS_VALID_GPIO(gpio_num)) { + return ESP_ERR_INVALID_ARG; + } + if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_ULP_TRIG_EN)) { + ESP_LOGE(TAG, "Conflicting wake-up triggers: touch / ULP"); + return ESP_ERR_INVALID_STATE; + } + s_config.ext0_rtc_gpio_num = rtc_gpio_desc[gpio_num].rtc_num; + s_config.ext0_trigger_level = level; + s_config.wakeup_triggers |= RTC_EXT0_TRIG_EN; + return ESP_OK; +} + +static void ext0_wakeup_prepare() +{ + int rtc_gpio_num = s_config.ext0_rtc_gpio_num; + // Set GPIO to be used for wakeup + REG_SET_FIELD(RTC_IO_EXT_WAKEUP0_REG, RTC_IO_EXT_WAKEUP0_SEL, rtc_gpio_num); + // Set level which will trigger wakeup + SET_PERI_REG_BITS(RTC_CNTL_EXT_WAKEUP_CONF_REG, 0x1, + s_config.ext0_trigger_level, RTC_CNTL_EXT_WAKEUP0_LV_S); + // Find GPIO descriptor in the rtc_gpio_desc table and configure the pad + for (size_t gpio_num = 0; gpio_num < GPIO_PIN_COUNT; ++gpio_num) { + const rtc_gpio_desc_t* desc = &rtc_gpio_desc[gpio_num]; + if (desc->rtc_num == rtc_gpio_num) { + REG_SET_BIT(desc->reg, desc->mux); + SET_PERI_REG_BITS(desc->reg, 0x3, 0, desc->func); + REG_SET_BIT(desc->reg, desc->ie); + break; + } + } +} + +esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode) +{ + if (mode > ESP_EXT1_WAKEUP_ANY_HIGH) { + return ESP_ERR_INVALID_ARG; + } + // Translate bit map of GPIO numbers into the bit map of RTC IO numbers + uint32_t rtc_gpio_mask = 0; + for (int gpio = 0; mask; ++gpio, mask >>= 1) { + if ((mask & 1) == 0) { + continue; + } + if (!RTC_GPIO_IS_VALID_GPIO(gpio)) { + ESP_LOGE(TAG, "Not an RTC IO: GPIO%d", gpio); + return ESP_ERR_INVALID_ARG; + } + rtc_gpio_mask |= BIT(rtc_gpio_desc[gpio].rtc_num); + } + s_config.ext1_rtc_gpio_mask = rtc_gpio_mask; + s_config.ext1_trigger_mode = mode; + s_config.wakeup_triggers |= RTC_EXT1_TRIG_EN; + return ESP_OK; +} + +static void ext1_wakeup_prepare() +{ + // Configure all RTC IOs selected as ext1 wakeup inputs + uint32_t rtc_gpio_mask = s_config.ext1_rtc_gpio_mask; + for (int gpio = 0; gpio < GPIO_PIN_COUNT && rtc_gpio_mask != 0; ++gpio) { + int rtc_pin = rtc_gpio_desc[gpio].rtc_num; + if ((rtc_gpio_mask & BIT(rtc_pin)) == 0) { + continue; + } + const rtc_gpio_desc_t* desc = &rtc_gpio_desc[gpio]; + // Route pad to RTC + REG_SET_BIT(desc->reg, desc->mux); + SET_PERI_REG_BITS(desc->reg, 0x3, 0, desc->func); + // set input enable in sleep mode + REG_SET_BIT(desc->reg, desc->ie); + // Pad configuration depends on RTC_PERIPH state in sleep mode + if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] != ESP_PD_OPTION_ON) { + // RTC_PERIPH will be powered down, so RTC_IO_ registers will + // loose their state. Lock pad configuration. + // Pullups/pulldowns also need to be disabled. + REG_CLR_BIT(desc->reg, desc->pulldown); + REG_CLR_BIT(desc->reg, desc->pullup); + } + // Keep track of pins which are processed to bail out early + rtc_gpio_mask &= ~BIT(rtc_pin); + } + // Clear state from previous wakeup + REG_SET_BIT(RTC_CNTL_EXT_WAKEUP1_REG, RTC_CNTL_EXT_WAKEUP1_STATUS_CLR); + // Set pins to be used for wakeup + REG_SET_FIELD(RTC_CNTL_EXT_WAKEUP1_REG, RTC_CNTL_EXT_WAKEUP1_SEL, s_config.ext1_rtc_gpio_mask); + // Set logic function (any low, all high) + SET_PERI_REG_BITS(RTC_CNTL_EXT_WAKEUP_CONF_REG, 0x1, + s_config.ext1_trigger_mode, RTC_CNTL_EXT_WAKEUP1_LV_S); +} + +uint64_t esp_sleep_get_ext1_wakeup_status() +{ + if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_EXT1) { + return 0; + } + uint32_t status = REG_GET_FIELD(RTC_CNTL_EXT_WAKEUP1_STATUS_REG, RTC_CNTL_EXT_WAKEUP1_STATUS); + // Translate bit map of RTC IO numbers into the bit map of GPIO numbers + uint64_t gpio_mask = 0; + for (int gpio = 0; gpio < GPIO_PIN_COUNT; ++gpio) { + if (!RTC_GPIO_IS_VALID_GPIO(gpio)) { + continue; + } + int rtc_pin = rtc_gpio_desc[gpio].rtc_num; + if ((status & BIT(rtc_pin)) == 0) { + continue; + } + gpio_mask |= 1ULL << gpio; + } + return gpio_mask; +} + +esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause() +{ + if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET) { + return ESP_SLEEP_WAKEUP_UNDEFINED; + } + + uint32_t wakeup_cause = REG_GET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_CAUSE); + if (wakeup_cause & RTC_EXT0_TRIG_EN) { + return ESP_SLEEP_WAKEUP_EXT0; + } else if (wakeup_cause & RTC_EXT1_TRIG_EN) { + return ESP_SLEEP_WAKEUP_EXT1; + } else if (wakeup_cause & RTC_TIMER_TRIG_EN) { + return ESP_SLEEP_WAKEUP_TIMER; + } else if (wakeup_cause & RTC_TOUCH_TRIG_EN) { + return ESP_SLEEP_WAKEUP_TOUCHPAD; + } else if (wakeup_cause & RTC_ULP_TRIG_EN) { + return ESP_SLEEP_WAKEUP_ULP; + } else { + return ESP_SLEEP_WAKEUP_UNDEFINED; + } +} + +esp_err_t esp_sleep_pd_config(esp_sleep_pd_domain_t domain, + esp_sleep_pd_option_t option) +{ + if (domain >= ESP_PD_DOMAIN_MAX || option > ESP_PD_OPTION_AUTO) { + return ESP_ERR_INVALID_ARG; + } + s_config.pd_options[domain] = option; + return ESP_OK; +} + +static uint32_t get_power_down_flags() +{ + // Where needed, convert AUTO options to ON. Later interpret AUTO as OFF. + + // RTC_SLOW_MEM is needed for the ULP, so keep RTC_SLOW_MEM powered up if ULP + // is used and RTC_SLOW_MEM is Auto. + // If there is any data placed into .rtc.data or .rtc.bss segments, and + // RTC_SLOW_MEM is Auto, keep it powered up as well. + + // These labels are defined in the linker script: + extern int _rtc_data_start, _rtc_data_end, _rtc_bss_start, _rtc_bss_end; + + if ((s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] == ESP_PD_OPTION_AUTO) && + (&_rtc_data_end > &_rtc_data_start || &_rtc_bss_end > &_rtc_bss_start || + (s_config.wakeup_triggers & RTC_ULP_TRIG_EN))) { + s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] = ESP_PD_OPTION_ON; + } + + // RTC_FAST_MEM is needed for deep sleep stub. + // If RTC_FAST_MEM is Auto, keep it powered on, so that deep sleep stub + // can run. + // In the new chip revision, deep sleep stub will be optional, + // and this can be changed. + if (s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] == ESP_PD_OPTION_AUTO) { + s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] = ESP_PD_OPTION_ON; + } + + // RTC_PERIPH is needed for EXT0 wakeup. + // If RTC_PERIPH is auto, and EXT0 isn't enabled, power down RTC_PERIPH. + if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] == ESP_PD_OPTION_AUTO) { + if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) { + s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_ON; + } else if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_ULP_TRIG_EN)) { + // In both rev. 0 and rev. 1 of ESP32, forcing power up of RTC_PERIPH + // prevents ULP timer and touch FSMs from working correctly. + s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_OFF; + } + } + + if (s_config.pd_options[ESP_PD_DOMAIN_XTAL] == ESP_PD_OPTION_AUTO) { + s_config.pd_options[ESP_PD_DOMAIN_XTAL] = ESP_PD_OPTION_OFF; + } + + const char* option_str[] = {"OFF", "ON", "AUTO(OFF)" /* Auto works as OFF */}; + ESP_LOGD(TAG, "RTC_PERIPH: %s, RTC_SLOW_MEM: %s, RTC_FAST_MEM: %s", + option_str[s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH]], + option_str[s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM]], + option_str[s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM]]); + + // Prepare flags based on the selected options + uint32_t pd_flags = 0; + if (s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] != ESP_PD_OPTION_ON) { + pd_flags |= RTC_SLEEP_PD_RTC_FAST_MEM; + } + if (s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] != ESP_PD_OPTION_ON) { + pd_flags |= RTC_SLEEP_PD_RTC_SLOW_MEM; + } + if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] != ESP_PD_OPTION_ON) { + pd_flags |= RTC_SLEEP_PD_RTC_PERIPH; + } +// if (s_config.pd_options[ESP_PD_DOMAIN_XTAL] != ESP_PD_OPTION_ON) { +// pd_flags |= RTC_SLEEP_PD_XTAL; +// } + return pd_flags; +} diff --git a/components/esp32s2beta/spiram.c b/components/esp32s2beta/spiram.c new file mode 100644 index 0000000000..3d421d04de --- /dev/null +++ b/components/esp32s2beta/spiram.c @@ -0,0 +1,367 @@ +/* +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 +#include +#include +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_err.h" +#include "esp32s2beta/spiram.h" +#include "spiram_psram.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/xtensa_api.h" +#include "soc/soc.h" +#include "esp_heap_caps_init.h" +#include "soc/soc_memory_layout.h" +#include "soc/dport_reg.h" +#include "esp32s2beta/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 +#define PSRAM_SPEED PSRAM_CACHE_F20M_S20M +#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_HIGH - CONFIG_SPIRAM_SIZE); + size_t p; + size_t s=CONFIG_SPIRAM_SIZE; + int errct=0; + int initial_err=-1; + + if ((SOC_EXTRAM_DATA_HIGH - SOC_EXTRAM_DATA_LOW) < CONFIG_SPIRAM_SIZE) { + ESP_EARLY_LOGW(TAG, "Only test spiram from %08x to %08x\n", SOC_EXTRAM_DATA_LOW, SOC_EXTRAM_DATA_HIGH); + spiram=(volatile int*)SOC_EXTRAM_DATA_LOW; + s = SOC_EXTRAM_DATA_HIGH - SOC_EXTRAM_DATA_LOW; + } + 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 < 4) { + ESP_EARLY_LOGE(TAG, "SPI SRAM error@%08x:%08x/%08x \n", &spiram[p], spiram[p], p^0xAAAAAAAA); + } + } + } + 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; + } +} + +#define DRAM0_ONLY_CACHE_SIZE BUS_IRAM0_CACHE_SIZE +#define DRAM0_DRAM1_CACHE_SIZE (BUS_IRAM0_CACHE_SIZE + BUS_IRAM1_CACHE_SIZE) +#define DRAM0_DRAM1_DPORT_CACHE_SIZE (BUS_IRAM0_CACHE_SIZE + BUS_IRAM1_CACHE_SIZE + BUS_DPORT_CACHE_SIZE) +#define DBUS3_ONLY_CACHE_SIZE BUS_AHB_DBUS3_CACHE_SIZE +#define DRAM0_DRAM1_DPORT_DBUS3_CACHE_SIZE (DRAM0_DRAM1_DPORT_CACHE_SIZE + DBUS3_ONLY_CACHE_SIZE) + +#define SPIRAM_SIZE_EXC_DRAM0_DRAM1_DPORT (CONFIG_SPIRAM_SIZE - DRAM0_DRAM1_DPORT_CACHE_SIZE) +#define SPIRAM_SIZE_EXC_DATA_CACHE (CONFIG_SPIRAM_SIZE - DRAM0_DRAM1_DPORT_DBUS3_CACHE_SIZE) + +#define SPIRAM_SMALL_SIZE_MAP_VADDR (DRAM0_CACHE_ADDRESS_HIGH - CONFIG_SPIRAM_SIZE) +#define SPIRAM_SMALL_SIZE_MAP_PADDR 0 +#define SPIRAM_SMALL_SIZE_MAP_SIZE CONFIG_SPIRAM_SIZE + +#define SPIRAM_MID_SIZE_MAP_VADDR (AHB_DBUS3_ADDRESS_HIGH - SPIRAM_SIZE_EXC_DRAM0_DRAM1_DPORT) +#define SPIRAM_MID_SIZE_MAP_PADDR 0 +#define SPIRAM_MID_SIZE_MAP_SIZE (SPIRAM_SIZE_EXC_DRAM0_DRAM1_DPORT) + +#define SPIRAM_BIG_SIZE_MAP_VADDR AHB_DBUS3_ADDRESS_LOW +#define SPIRAM_BIG_SIZE_MAP_PADDR (AHB_DBUS3_ADDRESS_HIGH - DRAM0_DRAM1_DPORT_DBUS3_CACHE_SIZE) +#define SPIRAM_BIG_SIZE_MAP_SIZE DBUS3_ONLY_CACHE_SIZE + +#define SPIRAM_MID_BIG_SIZE_MAP_VADDR DPORT_CACHE_ADDRESS_LOW +#define SPIRAM_MID_BIG_SIZE_MAP_PADDR SPIRAM_SIZE_EXC_DRAM0_DRAM1_DPORT +#define SPIRAM_MID_BIG_SIZE_MAP_SIZE DRAM0_DRAM1_DPORT_DBUS3_CACHE_SIZE + + +void IRAM_ATTR esp_spiram_init_cache() +{ + Cache_Suspend_DCache(); + /* map the address from SPIRAM end to the start, map the address in order: DRAM1, DRAM1, DPORT, DBUS3 */ +#if CONFIG_SPIRAM_SIZE <= DRAM0_ONLY_CACHE_SIZE + /* cache size <= 3MB + 576 KB, only map DRAM0 bus */ + Cache_Dbus_MMU_Set(DPORT_MMU_ACCESS_SPIRAM, SPIRAM_SMALL_SIZE_MAP_VADDR, SPIRAM_SMALL_SIZE_MAP_PADDR, 64, SPIRAM_SMALL_SIZE_MAP_SIZE >> 16, 0); + REG_SET_BIT(DPORT_CACHE_SOURCE_1_REG, DPORT_PRO_CACHE_D_SOURCE_PRO_DRAM0); + REG_CLR_BIT(DPORT_PRO_DCACHE_CTRL1_REG, DPORT_PRO_DCACHE_MASK_DRAM0); +#elif CONFIG_SPIRAM_SIZE <= DRAM0_DRAM1_CACHE_SIZE + /* cache size <= 7MB + 576KB, only map DRAM0 and DRAM1 bus */ + Cache_Dbus_MMU_Set(DPORT_MMU_ACCESS_SPIRAM, SPIRAM_SMALL_SIZE_MAP_VADDR, SPIRAM_SMALL_SIZE_MAP_PADDR, 64, SPIRAM_SMALL_SIZE_MAP_SIZE >> 16, 0); + REG_SET_BIT(DPORT_CACHE_SOURCE_1_REG, DPORT_PRO_CACHE_D_SOURCE_PRO_DRAM0); + REG_CLR_BIT(DPORT_PRO_DCACHE_CTRL1_REG, DPORT_PRO_DCACHE_MASK_DRAM1 | DPORT_PRO_DCACHE_MASK_DRAM0); +#elif CONFIG_SPIRAM_SIZE <= DRAM0_DRAM1_DPORT_CACHE_SIZE + /* cache size <= 10MB + 576KB, map DRAM0, DRAM1, DPORT bus */ + Cache_Dbus_MMU_Set(DPORT_MMU_ACCESS_SPIRAM, SPIRAM_SMALL_SIZE_MAP_VADDR, SPIRAM_SMALL_SIZE_MAP_PADDR, 64, SPIRAM_SMALL_SIZE_MAP_SIZE >> 16, 0); + REG_SET_BIT(DPORT_CACHE_SOURCE_1_REG, DPORT_PRO_CACHE_D_SOURCE_PRO_DPORT | DPORT_PRO_CACHE_D_SOURCE_PRO_DRAM0); + REG_CLR_BIT(DPORT_PRO_DCACHE_CTRL1_REG, DPORT_PRO_DCACHE_MASK_DRAM1 | DPORT_PRO_DCACHE_MASK_DRAM0 | DPORT_PRO_DCACHE_MASK_DPORT); +#else +#if CONFIG_USE_AHB_DBUS3_ACCESS_SPIRAM +#if CONFIG_SPIRAM_SIZE <= DRAM0_DRAM1_DPORT_DBUS3_CACHE_SIZE + /* cache size <= 14MB + 576KB, map DRAM0, DRAM1, DPORT bus, as well as data bus3 */ + Cache_Dbus_MMU_Set(DPORT_MMU_ACCESS_SPIRAM, SPIRAM_MID_SIZE_MAP_VADDR, SPIRAM_MID_SIZE_MAP_PADDR, 64, SPIRAM_MID_SIZE_MAP_SIZE >> 16, 0); +#else + /* cache size > 14MB + 576KB, map DRAM0, DRAM1, DPORT bus, as well as data bus3 */ + Cache_Dbus_MMU_Set(DPORT_MMU_ACCESS_SPIRAM, SPIRAM_BIG_SIZE_MAP_VADDR, SPIRAM_BIG_SIZE_MAP_PADDR, 64, SPIRAM_BIG_SIZE_MAP_SIZE >> 16, 0); +#endif + Cache_Dbus_MMU_Set(DPORT_MMU_ACCESS_SPIRAM, SPIRAM_MID_BIG_SIZE_MAP_VADDR, SPIRAM_MID_BIG_SIZE_MAP_PADDR, 64, SPIRAM_MID_BIG_SIZE_MAP_SIZE >> 16, 0); + REG_SET_BIT(DPORT_CACHE_SOURCE_1_REG, DPORT_PRO_CACHE_D_SOURCE_PRO_DPORT | DPORT_PRO_CACHE_D_SOURCE_PRO_DRAM0); + REG_CLR_BIT(DPORT_CACHE_SOURCE_1_REG, DPORT_PRO_CACHE_D_SOURCE_PRO_DROM0); + REG_CLR_BIT(DPORT_PRO_DCACHE_CTRL1_REG, DPORT_PRO_DCACHE_MASK_DRAM1 | DPORT_PRO_DCACHE_MASK_DRAM0 | DPORT_PRO_DCACHE_MASK_DPORT | DPORT_PRO_DCACHE_MASK_BUS3); +#else + /* cache size > 10MB + 576KB, map DRAM0, DRAM1, DPORT bus , only remap 0x3f500000 ~ 0x3ff90000*/ + Cache_Dbus_MMU_Set(DPORT_MMU_ACCESS_SPIRAM, SPIRAM_MID_BIG_SIZE_MAP_VADDR, SPIRAM_MID_BIG_SIZE_MAP_PADDR, 64, SPIRAM_MID_BIG_SIZE_MAP_SIZE >> 16, 0); + REG_SET_BIT(DPORT_CACHE_SOURCE_1_REG, DPORT_PRO_CACHE_D_SOURCE_PRO_DPORT | DPORT_PRO_CACHE_D_SOURCE_PRO_DRAM0); + REG_CLR_BIT(DPORT_PRO_DCACHE_CTRL1_REG, DPORT_PRO_DCACHE_MASK_DRAM1 | DPORT_PRO_DCACHE_MASK_DRAM0 | DPORT_PRO_DCACHE_MASK_DPORT); +#endif +#endif +} + +static uint32_t pages_for_flash = 0; +static uint32_t page0_mapped = 0; +static uint32_t page0_page = 0xffff; +static uint32_t instrcution_in_spiram = 0; +static uint32_t rodata_in_spiram = 0; + +uint32_t esp_spiram_instruction_access_enabled() +{ + return instrcution_in_spiram; +} + +uint32_t esp_spiram_rodata_access_enabled() +{ + return rodata_in_spiram; +} + +esp_err_t esp_spiram_enable_instruction_access(void) +{ + uint32_t pages_in_flash = 0; + pages_in_flash += Cache_Count_Flash_Pages(PRO_CACHE_IBUS0, &page0_mapped); + pages_in_flash += Cache_Count_Flash_Pages(PRO_CACHE_IBUS1, &page0_mapped); + pages_in_flash += Cache_Count_Flash_Pages(PRO_CACHE_IBUS2, &page0_mapped); + if ((pages_in_flash + pages_for_flash) > (CONFIG_SPIRAM_SIZE >> 16)) { + ESP_EARLY_LOGE(TAG, "SPI RAM space not enough for the instructions, has %d pages, need %d pages.", (CONFIG_SPIRAM_SIZE >> 16), (pages_in_flash + pages_for_flash)); + return ESP_FAIL; + } + ESP_EARLY_LOGI(TAG, "Instructions copied and mapped to SPIRAM"); + pages_for_flash = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_IBUS0, IRAM0_ADDRESS_LOW, pages_for_flash, &page0_page); + pages_for_flash = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_IBUS1, IRAM1_ADDRESS_LOW, pages_for_flash, &page0_page); + pages_for_flash = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_IBUS2, IROM0_ADDRESS_LOW, pages_for_flash, &page0_page); + instrcution_in_spiram = 1; + return ESP_OK; +} + +esp_err_t esp_spiram_enable_rodata_access(void) +{ + uint32_t pages_in_flash = 0; + if (Cache_Drom0_Using_ICache()) { + pages_in_flash += Cache_Count_Flash_Pages(PRO_CACHE_IBUS3, &page0_mapped); + } else { + pages_in_flash += Cache_Count_Flash_Pages(PRO_CACHE_DBUS3, &page0_mapped); + } + pages_in_flash += Cache_Count_Flash_Pages(PRO_CACHE_DBUS0, &page0_mapped); + pages_in_flash += Cache_Count_Flash_Pages(PRO_CACHE_DBUS1, &page0_mapped); + pages_in_flash += Cache_Count_Flash_Pages(PRO_CACHE_DBUS2, &page0_mapped); + + if ((pages_in_flash + pages_for_flash) > (CONFIG_SPIRAM_SIZE >> 16)) { + ESP_EARLY_LOGE(TAG, "SPI RAM space not enough for the read only data."); + return ESP_FAIL; + } + + ESP_EARLY_LOGI(TAG, "Read only data copied and mapped to SPIRAM"); + if (Cache_Drom0_Using_ICache()) { + pages_for_flash = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_IBUS3, DROM0_ADDRESS_LOW, pages_for_flash, &page0_page); + } else { + pages_for_flash = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_DBUS3, DROM0_ADDRESS_LOW, pages_for_flash, &page0_page); + } + pages_for_flash = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_DBUS0, DRAM0_ADDRESS_LOW, pages_for_flash, &page0_page); + pages_for_flash = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_DBUS1, DRAM1_ADDRESS_LOW, pages_for_flash, &page0_page); + pages_for_flash = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_DBUS2, DPORT_ADDRESS_LOW, pages_for_flash, &page0_page); + rodata_in_spiram = 1; + return ESP_OK; +} + +esp_err_t esp_spiram_init() +{ + esp_err_t r; + r = psram_enable(PSRAM_SPEED, PSRAM_MODE); + if (r != ESP_OK) { +#if CONFIG_SPIRAM_IGNORE_NOTFOUND + ESP_EARLY_LOGE(TAG, "SPI RAM enabled but initialization failed. Bailing out."); +#endif + 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" : "flash 20m sram 20m"); + 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; +} + + +esp_err_t esp_spiram_add_to_heapalloc() +{ + uint32_t size_for_flash = (pages_for_flash << 16); + ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", (CONFIG_SPIRAM_SIZE - (pages_for_flash << 16))/1024); + //Add entire external RAM region to heap allocator. Heap allocator knows the capabilities of this type of memory, so there's + //no need to explicitly specify them. + +#if CONFIG_SPIRAM_SIZE <= DRAM0_DRAM1_DPORT_CACHE_SIZE + /* cache size <= 10MB + 576KB, map DRAM0, DRAM1, DPORT bus */ + return heap_caps_add_region((intptr_t)SPIRAM_SMALL_SIZE_MAP_VADDR + size_for_flash, (intptr_t)SPIRAM_SMALL_SIZE_MAP_VADDR + SPIRAM_SMALL_SIZE_MAP_SIZE -1); +#else +#if CONFIG_USE_AHB_DBUS3_ACCESS_SPIRAM +#if CONFIG_SPIRAM_SIZE <= DRAM0_DRAM1_DPORT_DBUS3_CACHE_SIZE + /* cache size <= 14MB + 576KB, map DRAM0, DRAM1, DPORT bus, as well as data bus3 */ + if (size_for_flash <= SPIRAM_MID_SIZE_MAP_SIZE) { + esp_err_t err = heap_caps_add_region((intptr_t)SPIRAM_MID_SIZE_MAP_VADDR + size_for_flash, (intptr_t)SPIRAM_MID_SIZE_MAP_VADDR + SPIRAM_MID_SIZE_MAP_SIZE -1); + if (err) { + return err; + } + return heap_caps_add_region((intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR, (intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR + SPIRAM_MID_BIG_SIZE_MAP_SIZE -1); + } else { + return heap_caps_add_region((intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR + size_for_flash - SPIRAM_MID_SIZE_MAP_SIZE, (intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR + SPIRAM_MID_BIG_SIZE_MAP_SIZE -1); + } +#else + if (size_for_flash <= SPIRAM_SIZE_EXC_DATA_CACHE) { + esp_err_t err = heap_caps_add_region((intptr_t)SPIRAM_BIG_SIZE_MAP_VADDR, (intptr_t)SPIRAM_BIG_SIZE_MAP_VADDR + SPIRAM_BIG_SIZE_MAP_SIZE -1); + if (err) { + return err; + } + return heap_caps_add_region((intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR, (intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR + SPIRAM_MID_BIG_SIZE_MAP_SIZE -1); + } else if (size_for_flash <= SPIRAM_SIZE_EXC_DRAM0_DRAM1_DPORT) { + esp_err_t err = heap_caps_add_region((intptr_t)SPIRAM_BIG_SIZE_MAP_VADDR + size_for_flash - SPIRAM_SIZE_EXC_DATA_CACHE, (intptr_t)SPIRAM_MID_SIZE_MAP_VADDR + SPIRAM_MID_SIZE_MAP_SIZE -1); + if (err) { + return err; + } + return heap_caps_add_region((intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR, (intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR + SPIRAM_MID_BIG_SIZE_MAP_SIZE -1); + } else { + return heap_caps_add_region((intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR + size_for_flash - SPIRAM_SIZE_EXC_DRAM0_DRAM1_DPORT, (intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR + SPIRAM_MID_BIG_SIZE_MAP_SIZE -1); + } +#endif +#else + Cache_Dbus_MMU_Set(DPORT_MMU_ACCESS_SPIRAM, SPIRAM_MID_BIG_SIZE_MAP_VADDR, SPIRAM_MID_BIG_SIZE_MAP_PADDR, 64, SPIRAM_MID_BIG_SIZE_MAP_SIZE >> 16, 0); + if (size_for_flash <= SPIRAM_SIZE_EXC_DRAM0_DRAM1_DPORT) { + return heap_caps_add_region((intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR, (intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR + SPIRAM_MID_BIG_SIZE_MAP_SIZE -1); + } else { + return heap_caps_add_region((intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR + size_for_flash, (intptr_t)SPIRAM_MID_BIG_SIZE_MAP_VADDR + SPIRAM_MID_BIG_SIZE_MAP_SIZE -1); + } +#endif +#endif +} + + +static uint8_t *dma_heap; + +esp_err_t esp_spiram_reserve_dma_pool(size_t size) { + if (size==0) return ESP_OK; //no-op + ESP_EARLY_LOGI(TAG, "Reserving pool of %dK of internal memory for DMA/internal allocations", size/1024); + dma_heap=heap_caps_malloc(size, MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL); + if (!dma_heap) return ESP_ERR_NO_MEM; + uint32_t caps[]={MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}; + return heap_caps_add_region_with_caps(caps, (intptr_t) dma_heap, (intptr_t) dma_heap+size-1); +} + +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() +{ + extern void Cache_WriteBack_All(void); + 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_DCACHE_CTRL_REG, DPORT_PRO_DCACHE_ENABLE)==0) { + cache_was_disabled|=(1<<0); + DPORT_SET_PERI_REG_BITS(DPORT_PRO_DCACHE_CTRL_REG, 1, 1, DPORT_PRO_DCACHE_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 + + Cache_WriteBack_All(); + + if (cache_was_disabled&(1<<0)) { +#ifdef DPORT_CODE_COMPLETE + while (DPORT_GET_PERI_REG_BITS2(DPORT_PRO_DCACHE_DBUG2_REG, DPORT_PRO_CACHE_STATE, DPORT_PRO_CACHE_STATE_S) != 1) ; +#endif + DPORT_SET_PERI_REG_BITS(DPORT_PRO_DCACHE_CTRL_REG, 1, 0, DPORT_PRO_DCACHE_ENABLE_S); + } +#ifndef CONFIG_FREERTOS_UNICORE + if (cache_was_disabled&(1<<1)) { + while (DPORT_GET_PERI_REG_BITS2(DPORT_APP_DCACHE_DBUG2_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 diff --git a/components/esp32s2beta/spiram_psram.c b/components/esp32s2beta/spiram_psram.c new file mode 100644 index 0000000000..d1ba2ca2bc --- /dev/null +++ b/components/esp32s2beta/spiram_psram.c @@ -0,0 +1,903 @@ +/* + 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 "esp_log.h" +#include "spiram_psram.h" +#include "esp32s2beta/rom/ets_sys.h" +#include "esp32s2beta/rom/spi_flash.h" +#include "esp32s2beta/rom/gpio.h" +#include "esp32s2beta/rom/cache.h" +#include "soc/io_mux_reg.h" +#include "soc/dport_reg.h" +#include "soc/apb_ctrl_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/efuse_reg.h" +#include "driver/gpio.h" +#include "driver/spi_common.h" +#include "driver/periph_ctrl.h" + +#if CONFIG_SPIRAM_SUPPORT +#include "soc/rtc.h" + +//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_FAST_READ_QUAD_DUMMY 0x5 +#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 + +typedef enum { + PSRAM_CLK_MODE_NORM = 0, /*!< Normal SPI mode */ + PSRAM_CLK_MODE_DCLK = 1, /*!< Two extra clock cycles after CS is set high level */ +} psram_clk_mode_t; + +#define PSRAM_ID_KGD_M 0xff +#define PSRAM_ID_KGD_S 8 +#define PSRAM_ID_KGD 0x5d +#define PSRAM_ID_EID_M 0xff +#define PSRAM_ID_EID_S 16 + +#define PSRAM_KGD(id) (((id) >> PSRAM_ID_KGD_S) & PSRAM_ID_KGD_M) +#define PSRAM_EID(id) (((id) >> PSRAM_ID_EID_S) & PSRAM_ID_EID_M) +#define PSRAM_IS_VALID(id) (PSRAM_KGD(id) == PSRAM_ID_KGD) + +// PSRAM_EID = 0x26 or 0x4x ----> 64MBit psram +// PSRAM_EID = 0x20 ------------> 32MBit psram +#define PSRAM_IS_64MBIT(id) ((PSRAM_EID(id) == 0x26) || ((PSRAM_EID(id) & 0xf0) == 0x40)) +#define PSRAM_IS_32MBIT_VER0(id) (PSRAM_EID(id) == 0x20) + +// 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 SPI_CLK_GPIO_NUM //Psram clock is a delayed version of this in 40MHz mode +#define FLASH_CS_IO SPI_CS0_GPIO_NUM +#define PSRAM_CS_IO 26 +#define PSRAM_SPIQ_IO SPI_Q_GPIO_NUM +#define PSRAM_SPID_IO SPI_D_GPIO_NUM +#define PSRAM_SPIWP_IO SPI_WP_GPIO_NUM +#define PSRAM_SPIHD_IO SPI_HD_GPIO_NUM +#define PSRAM_INTERNAL_IO_28 28 +#define PSRAM_INTERNAL_IO_29 29 +#define PSRAM_IO_MATRIX_DUMMY_20M 0 +#define PSRAM_IO_MATRIX_DUMMY_40M 0 +#define PSRAM_IO_MATRIX_DUMMY_80M 0 + +#define _SPI_CACHE_PORT 0 +#define _SPI_FLASH_PORT 1 +#define _SPI_80M_CLK_DIV 1 +#define _SPI_40M_CLK_DIV 2 +#define _SPI_20M_CLK_DIV 4 + +static const char* TAG = "psram"; +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; +static psram_clk_mode_t s_clk_mode = PSRAM_CLK_MODE_DCLK; +static uint32_t s_psram_id = 0; + +/* 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_MEM_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_MEM_USER_REG(spi_num), SPI_MEM_FWRITE_QIO); + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_FCMD_QUAD_M); + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_FWRITE_DIO); + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_FWRITE_QUAD); + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_FWRITE_DUAL); +} +//set QPI write mode +static void psram_set_qio_write_mode(psram_spi_num_t spi_num) +{ + SET_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_FWRITE_QIO); + SET_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_FCMD_QUAD_M); + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_FWRITE_DIO); + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_FWRITE_QUAD); + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_FWRITE_DUAL); +} +//set QPI read mode +static void psram_set_qio_read_mode(psram_spi_num_t spi_num) +{ + SET_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_FREAD_QIO); + SET_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_FCMD_QUAD_M); + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_FREAD_QUAD); + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_FREAD_DUAL); + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_FREAD_DIO); +} +//set SPI read mode +static void psram_set_basic_read_mode(psram_spi_num_t spi_num) +{ + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_FREAD_QIO); + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_FCMD_QUAD_M); + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_FREAD_QUAD); + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_FREAD_DUAL); + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_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_MEM_MISC_REG(PSRAM_SPI_1), SPI_MEM_CS1_DIS_M); + SET_PERI_REG_MASK(SPI_MEM_MISC_REG(PSRAM_SPI_1), SPI_MEM_CS0_DIS_M); + + uint32_t mode_backup = (READ_PERI_REG(SPI_MEM_USER_REG(spi_num)) >> SPI_MEM_FWRITE_DUAL_S) & 0xf; +#ifdef FAKE_QPI + uint32_t rd_mode_backup = READ_PERI_REG(SPI_MEM_CTRL_REG(spi_num)) & (SPI_MEM_FREAD_DIO_M | SPI_MEM_FREAD_DUAL_M | SPI_MEM_FREAD_QUAD_M | SPI_MEM_FREAD_QIO_M); +#else + uint32_t rd_mode_backup = READ_PERI_REG(SPI_MEM_CTRL_REG(spi_num)) & (SPI_MEM_FREAD_DIO_M | SPI_MEM_FREAD_DUAL_M | SPI_MEM_FREAD_QUAD_M | SPI_MEM_FREAD_QIO_M | SPI_MEM_FCMD_QUAD); +#endif + 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); + } + + // Start send data + SET_PERI_REG_MASK(SPI_MEM_CMD_REG(spi_num), SPI_MEM_USR); + while ((READ_PERI_REG(SPI_MEM_CMD_REG(spi_num)) & SPI_MEM_USR)); + + //recover spi mode + SET_PERI_REG_BITS(SPI_MEM_USER_REG(spi_num), (pRxData?SPI_MEM_FWRITE_DUAL_M:0xf), mode_backup, SPI_MEM_FWRITE_DUAL_S); +#ifdef FAKE_QPI + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), (SPI_MEM_FREAD_DIO_M|SPI_MEM_FREAD_DUAL_M|SPI_MEM_FREAD_QUAD_M|SPI_MEM_FREAD_QIO_M)); +#else + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), (SPI_MEM_FREAD_DIO_M|SPI_MEM_FREAD_DUAL_M|SPI_MEM_FREAD_QUAD_M|SPI_MEM_FREAD_QIO_M|SPI_MEM_FCMD_QUAD)); +#endif + SET_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), rd_mode_backup); + + //return cs to cs0 + SET_PERI_REG_MASK(SPI_MEM_MISC_REG(PSRAM_SPI_1), SPI_MEM_CS1_DIS_M); + CLEAR_PERI_REG_MASK(SPI_MEM_MISC_REG(PSRAM_SPI_1), SPI_MEM_CS0_DIS_M); + + if (pRxData) { + int idx = 0; + // Read data out + do { + *pRxData++ = READ_PERI_REG(SPI_MEM_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_MEM_CMD_REG(spi_num)) & SPI_MEM_USR); + backup_usr[spi_num]=READ_PERI_REG(SPI_MEM_USER_REG(spi_num)); + backup_usr1[spi_num]=READ_PERI_REG(SPI_MEM_USER1_REG(spi_num)); + backup_usr2[spi_num]=READ_PERI_REG(SPI_MEM_USER2_REG(spi_num)); + // Set command by user. + if (pInData->cmdBitLen != 0) { + // Max command length 16 bits. + SET_PERI_REG_BITS(SPI_MEM_USER2_REG(spi_num), SPI_MEM_USR_COMMAND_BITLEN, pInData->cmdBitLen - 1, + SPI_MEM_USR_COMMAND_BITLEN_S); + // Enable command + SET_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_USR_COMMAND); + // Load command,bit15-0 is cmd value. + SET_PERI_REG_BITS(SPI_MEM_USER2_REG(spi_num), SPI_MEM_USR_COMMAND_VALUE, pInData->cmd, SPI_MEM_USR_COMMAND_VALUE_S); + } else { + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_USR_COMMAND); + SET_PERI_REG_BITS(SPI_MEM_USER2_REG(spi_num), SPI_MEM_USR_COMMAND_BITLEN, 0, SPI_MEM_USR_COMMAND_BITLEN_S); + } + // Set Address by user. + if (pInData->addrBitLen != 0) { + SET_PERI_REG_BITS(SPI_MEM_USER1_REG(spi_num), SPI_MEM_USR_ADDR_BITLEN, (pInData->addrBitLen - 1), SPI_MEM_USR_ADDR_BITLEN_S); + // Enable address + SET_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_USR_ADDR); + // Set address + WRITE_PERI_REG(SPI_MEM_ADDR_REG(spi_num), *pInData->addr); + } else { + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_USR_ADDR); + SET_PERI_REG_BITS(SPI_MEM_USER1_REG(spi_num), SPI_MEM_USR_ADDR_BITLEN, 0, SPI_MEM_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_MEM_USER_REG(spi_num), SPI_MEM_USR_MOSI); + // Load send buffer + int len = (pInData->txDataBitLen + 31) / 32; + if (p_tx_val != NULL) { + memcpy((void*)SPI_MEM_W0_REG(spi_num), p_tx_val, len * 4); + } + // Set data send buffer length.Max data length 64 bytes. + SET_PERI_REG_BITS(SPI_MEM_MOSI_DLEN_REG(spi_num), SPI_MEM_USR_MOSI_DBITLEN, (pInData->txDataBitLen - 1), + SPI_MEM_USR_MOSI_DBITLEN_S); + } else { + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_USR_MOSI); + SET_PERI_REG_BITS(SPI_MEM_MOSI_DLEN_REG(spi_num), SPI_MEM_USR_MOSI_DBITLEN, 0, SPI_MEM_USR_MOSI_DBITLEN_S); + } + // Set rx data by user. + if (pInData->rxDataBitLen != 0) { + // Enable MOSI + SET_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_USR_MISO); + // Set data send buffer length.Max data length 64 bytes. + SET_PERI_REG_BITS(SPI_MEM_MISO_DLEN_REG(spi_num), SPI_MEM_USR_MISO_DBITLEN, (pInData->rxDataBitLen - 1), + SPI_MEM_USR_MISO_DBITLEN_S); + } else { + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_USR_MISO); + SET_PERI_REG_BITS(SPI_MEM_MISO_DLEN_REG(spi_num), SPI_MEM_USR_MISO_DBITLEN, 0, SPI_MEM_USR_MISO_DBITLEN_S); + } + if (pInData->dummyBitLen != 0) { + SET_PERI_REG_MASK(SPI_MEM_USER_REG(PSRAM_SPI_1), SPI_MEM_USR_DUMMY); // dummy en + SET_PERI_REG_BITS(SPI_MEM_USER1_REG(PSRAM_SPI_1), SPI_MEM_USR_DUMMY_CYCLELEN_V, pInData->dummyBitLen - 1, + SPI_MEM_USR_DUMMY_CYCLELEN_S); //DUMMY + } else { + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(PSRAM_SPI_1), SPI_MEM_USR_DUMMY); // dummy en + SET_PERI_REG_BITS(SPI_MEM_USER1_REG(PSRAM_SPI_1), SPI_MEM_USR_DUMMY_CYCLELEN_V, 0, SPI_MEM_USR_DUMMY_CYCLELEN_S); //DUMMY + } + return 0; +} + +void psram_cmd_end(int spi_num) { + while (READ_PERI_REG(SPI_MEM_CMD_REG(spi_num)) & SPI_MEM_USR); + WRITE_PERI_REG(SPI_MEM_USER_REG(spi_num), backup_usr[spi_num]); + WRITE_PERI_REG(SPI_MEM_USER1_REG(spi_num), backup_usr1[spi_num]); + WRITE_PERI_REG(SPI_MEM_USER2_REG(spi_num), backup_usr2[spi_num]); +} + +#ifdef FAKE_QPI +//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; + cmd_exit_qpi = PSRAM_EXIT_QMODE; + ps_cmd.txDataBitLen = 8; + if (s_clk_mode == PSRAM_CLK_MODE_DCLK) { + switch (s_psram_mode) { + case PSRAM_CACHE_F80M_S80M: + 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 dummy_bits = 0 + extra_dummy; + psram_cmd_t ps_cmd; + + uint32_t addr = 0; + ps_cmd.addrBitLen = 3 * 8; + ps_cmd.cmd = PSRAM_DEVICE_ID; + ps_cmd.cmdBitLen = 8; + if (s_clk_mode == PSRAM_CLK_MODE_DCLK) { + switch (s_psram_mode) { + case PSRAM_CACHE_F80M_S80M: + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + ps_cmd.cmdBitLen = 2; //this two bits is used to delay 2 clock cycle + ps_cmd.cmd = 0; + addr = (PSRAM_DEVICE_ID << 24) | 0; + ps_cmd.addrBitLen = 4 * 8; + break; + } + } + ps_cmd.addr = &addr; + 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; + + ps_cmd.cmdBitLen = 0; + if (s_clk_mode == PSRAM_CLK_MODE_DCLK) { + switch (s_psram_mode) { + case PSRAM_CACHE_F80M_S80M: + 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; +} +#else /* FAKE_QPI */ +//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; + ps_cmd.txData = NULL; + ps_cmd.txDataBitLen = 0; + ps_cmd.cmd = PSRAM_EXIT_QMODE; + ps_cmd.cmdBitLen = 8; + 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); +} + + +//switch psram burst length(32 bytes or 1024 bytes) +//datasheet says it should be 1024 bytes by default +static void psram_set_wrap_burst_length(psram_spi_num_t spi_num, psram_cmd_mode_t mode) +{ + psram_cmd_t ps_cmd; + ps_cmd.cmd = 0xC0; + ps_cmd.cmdBitLen = 8; + ps_cmd.addr = 0; + ps_cmd.addrBitLen = 0; + 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, mode); + psram_cmd_end(spi_num); +} + +//send reset command to psram, in spi mode +static void psram_reset_mode(psram_spi_num_t spi_num) +{ + psram_cmd_t ps_cmd; + ps_cmd.txData = NULL; + ps_cmd.txDataBitLen = 0; + ps_cmd.addr = NULL; + ps_cmd.addrBitLen = 0; + ps_cmd.cmd = PSRAM_RESET_EN; + ps_cmd.cmdBitLen = 8; + 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); + + memset(&ps_cmd, 0, sizeof(ps_cmd)); + ps_cmd.txData = NULL; + ps_cmd.txDataBitLen = 0; + ps_cmd.addr = NULL; + ps_cmd.addrBitLen = 0; + ps_cmd.cmd = PSRAM_RESET; + ps_cmd.cmdBitLen = 8; + 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); +} + +esp_err_t psram_enable_wrap(uint32_t wrap_size) +{ + switch (wrap_size) { + case 32: + psram_set_wrap_burst_length(PSRAM_SPI_1, PSRAM_CMD_QPI); + return ESP_OK; + case 16: + case 64: + default: + return ESP_FAIL; + } +} + +bool psram_support_wrap_size(uint32_t wrap_size) +{ + switch (wrap_size) { + case 0: + case 32: + return true; + case 16: + case 64: + default: + return false; + } + +} + +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 dummy_bits = 0; + uint32_t addr = 0; + psram_cmd_t ps_cmd; + switch (s_psram_mode) { + case PSRAM_CACHE_F80M_S80M: + dummy_bits = 0 + extra_dummy; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + case PSRAM_CACHE_F26M_S26M: + case PSRAM_CACHE_F20M_S20M: + default: + dummy_bits = 0 + extra_dummy; + break; + } + ps_cmd.cmd = PSRAM_DEVICE_ID; + ps_cmd.cmdBitLen = 8; + ps_cmd.addr = &addr; + ps_cmd.addrBitLen = 24; + ps_cmd.txDataBitLen = 0; + ps_cmd.txData = NULL; + ps_cmd.rxDataBitLen = 3 * 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; + ps_cmd.cmd = PSRAM_ENTER_QMODE; + ps_cmd.cmdBitLen = 8; //this two bits is used to delay 2 clock cycle + ps_cmd.addr = NULL; + ps_cmd.addrBitLen = 0; + 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; +} +#endif /* FAKE_QPI */ + +//spi param init for psram +void IRAM_ATTR psram_spi_init(psram_spi_num_t spi_num, psram_cache_mode_t mode) +{ + uint8_t k; + SET_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_CS_SETUP); +#if 0 + // SPI_CPOL & SPI_CPHA + CLEAR_PERI_REG_MASK(SPI_MEM_MISC_REG(spi_num), SPI_MEM_CK_IDLE_EDGE); + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_CK_OUT_EDGE); + // SPI bit order + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_WR_BIT_ORDER); + CLEAR_PERI_REG_MASK(SPI_MEM_CTRL_REG(spi_num), SPI_MEM_RD_BIT_ORDER); + // SPI bit order + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_DOUTDIN); +#endif + // May be not must to do. + WRITE_PERI_REG(SPI_MEM_USER1_REG(spi_num), 0); +#if 0 + // SPI mode type + CLEAR_PERI_REG_MASK(SPI_MEM_SLAVE_REG(spi_num), SPI_MEM_SLAVE_MODE); +#endif + // Set SPI speed for non-80M mode. (80M mode uses APB clock directly.) + if (mode!=PSRAM_CACHE_F80M_S80M) { + k = 2; //Main divider. Divide by 2 so we get 40MHz + //clear bit 31, set SPI clock div + CLEAR_PERI_REG_MASK(SPI_MEM_CLOCK_REG(spi_num), SPI_MEM_CLK_EQU_SYSCLK); + WRITE_PERI_REG(SPI_MEM_CLOCK_REG(spi_num), + (((k - 1) & SPI_MEM_CLKCNT_N) << SPI_MEM_CLKCNT_N_S) | + ((((k + 1) / 2 - 1) & SPI_MEM_CLKCNT_H) << SPI_MEM_CLKCNT_H_S) | //50% duty cycle + (((k - 1) & SPI_MEM_CLKCNT_L) << SPI_MEM_CLKCNT_L_S)); + } + // Enable MOSI + SET_PERI_REG_MASK(SPI_MEM_USER_REG(spi_num), SPI_MEM_CS_SETUP | SPI_MEM_CS_HOLD | SPI_MEM_USR_MOSI); + memset((void*)SPI_MEM_W0_REG(spi_num), 0, 16 * 4); +} + +/* + * Psram mode init will overwrite original flash speed mode, so that it is possible to change psram and flash speed after OTA. + * Flash read mode(QIO/QOUT/DIO/DOUT) will not be changed in app bin. It is decided by bootloader, OTA can not change this mode. + */ +static void IRAM_ATTR psram_gpio_config(psram_cache_mode_t mode) +{ + int spi_cache_dummy = 0; + uint32_t rd_mode_reg = READ_PERI_REG(SPI_MEM_CTRL_REG(0)); + if (rd_mode_reg & (SPI_MEM_FREAD_QIO_M | SPI_MEM_FREAD_DIO_M)) { + spi_cache_dummy = SPI0_R_QIO_DUMMY_CYCLELEN; + } else if (rd_mode_reg & (SPI_MEM_FREAD_QUAD_M | SPI_MEM_FREAD_DUAL_M)) { + spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN; + } else { + spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN; + } + // In bootloader, all the signals are already configured, + // We keep the following code in case the bootloader is some older version. + 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[_SPI_CACHE_PORT] = PSRAM_IO_MATRIX_DUMMY_80M; + g_rom_spiflash_dummy_len_plus[_SPI_FLASH_PORT] = PSRAM_IO_MATRIX_DUMMY_40M; + SET_PERI_REG_BITS(SPI_MEM_USER1_REG(_SPI_CACHE_PORT), SPI_MEM_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + PSRAM_IO_MATRIX_DUMMY_80M, SPI_MEM_USR_DUMMY_CYCLELEN_S); //DUMMY + esp_rom_spiflash_config_clk(_SPI_80M_CLK_DIV, _SPI_CACHE_PORT); + esp_rom_spiflash_config_clk(_SPI_40M_CLK_DIV, _SPI_FLASH_PORT); + break; + case PSRAM_CACHE_F80M_S80M: + extra_dummy = PSRAM_IO_MATRIX_DUMMY_80M; +#if 0 + g_rom_spiflash_dummy_len_plus[_SPI_CACHE_PORT] = PSRAM_IO_MATRIX_DUMMY_80M; + g_rom_spiflash_dummy_len_plus[_SPI_FLASH_PORT] = PSRAM_IO_MATRIX_DUMMY_80M; + SET_PERI_REG_BITS(SPI_MEM_USER1_REG(_SPI_CACHE_PORT), SPI_MEM_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + PSRAM_IO_MATRIX_DUMMY_80M, SPI_MEM_USR_DUMMY_CYCLELEN_S); //DUMMY + + CLEAR_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, SPI_MEM_FREAD_QIO | SPI_MEM_FREAD_QUAD | SPI_MEM_FREAD_DIO | SPI_MEM_FREAD_DUAL | SPI_MEM_FASTRD_MODE); + esp_rom_spiflash_config_clk(_SPI_80M_CLK_DIV, _SPI_CACHE_PORT); + CLEAR_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, SPI_MEM_FREAD_QIO | SPI_MEM_FREAD_QUAD | SPI_MEM_FREAD_DIO | SPI_MEM_FREAD_DUAL | SPI_MEM_FASTRD_MODE); + esp_rom_spiflash_config_clk(_SPI_80M_CLK_DIV, _SPI_FLASH_PORT); + +#endif + break; + case PSRAM_CACHE_F40M_S40M: + extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M; +#if 0 + g_rom_spiflash_dummy_len_plus[_SPI_CACHE_PORT] = PSRAM_IO_MATRIX_DUMMY_40M; + g_rom_spiflash_dummy_len_plus[_SPI_FLASH_PORT] = PSRAM_IO_MATRIX_DUMMY_40M; + SET_PERI_REG_BITS(SPI_MEM_USER1_REG(_SPI_CACHE_PORT), SPI_MEM_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + PSRAM_IO_MATRIX_DUMMY_40M, SPI_MEM_USR_DUMMY_CYCLELEN_S); //DUMMY + + CLEAR_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, SPI_MEM_FREAD_QIO | SPI_MEM_FREAD_QUAD | SPI_MEM_FREAD_DIO | SPI_MEM_FREAD_DUAL | SPI_MEM_FASTRD_MODE); + esp_rom_spiflash_config_clk(_SPI_40M_CLK_DIV, _SPI_CACHE_PORT); + + CLEAR_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, SPI_MEM_FREAD_QIO | SPI_MEM_FREAD_QUAD | SPI_MEM_FREAD_DIO | SPI_MEM_FREAD_DUAL | SPI_MEM_FASTRD_MODE); + esp_rom_spiflash_config_clk(_SPI_40M_CLK_DIV, _SPI_FLASH_PORT); +#endif + + break; + case PSRAM_CACHE_F26M_S26M: + case PSRAM_CACHE_F20M_S20M: + extra_dummy = PSRAM_IO_MATRIX_DUMMY_20M; +#if 0 + g_rom_spiflash_dummy_len_plus[_SPI_CACHE_PORT] = PSRAM_IO_MATRIX_DUMMY_20M; + g_rom_spiflash_dummy_len_plus[_SPI_FLASH_PORT] = PSRAM_IO_MATRIX_DUMMY_20M; + SET_PERI_REG_BITS(SPI_MEM_USER1_REG(_SPI_CACHE_PORT), SPI_MEM_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + PSRAM_IO_MATRIX_DUMMY_20M, SPI_MEM_USR_DUMMY_CYCLELEN_S); //DUMMY + + CLEAR_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, SPI_MEM_FREAD_QIO | SPI_MEM_FREAD_QUAD | SPI_MEM_FREAD_DIO | SPI_MEM_FREAD_DUAL | SPI_MEM_FASTRD_MODE); + esp_rom_spiflash_config_clk(_SPI_20M_CLK_DIV, _SPI_CACHE_PORT); + CLEAR_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, SPI_MEM_FREAD_QIO | SPI_MEM_FREAD_QUAD | SPI_MEM_FREAD_DIO | SPI_MEM_FREAD_DUAL | SPI_MEM_FASTRD_MODE); + esp_rom_spiflash_config_clk(_SPI_20M_CLK_DIV, _SPI_FLASH_PORT); +#endif + + default: + break; + } + SET_PERI_REG_MASK(SPI_MEM_USER_REG(0), SPI_MEM_USR_DUMMY); // dummy en + //select pin function gpio + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SPIHD_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SPIWP_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SPICS0_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SPIQ_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SPID_U, PIN_FUNC_GPIO); + // flash clock signal should come from IO MUX. + // set drive ability for clock + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SPICLK_U, FUNC_SPICLK_SPICLK); +} + +psram_size_t psram_get_size() +{ + if (PSRAM_IS_32MBIT_VER0(s_psram_id)) { + return PSRAM_SIZE_32MBITS; + } else if (PSRAM_IS_64MBIT(s_psram_id)) { + return PSRAM_SIZE_64MBITS; + } else { + return PSRAM_SIZE_MAX; + } +} + +//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 +{ + + assert(mode < PSRAM_CACHE_MAX && "we don't support any other mode for now."); + s_psram_mode = mode; + + periph_module_enable(PERIPH_SPI_MODULE); +#if 0 + WRITE_PERI_REG(SPI_MEM_EXT3_REG(0), 0x1); + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(PSRAM_SPI_1), SPI_MEM_USR_PREP_HOLD_M); +#endif + + + switch (mode) { + case PSRAM_CACHE_F80M_S80M: + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + case PSRAM_CACHE_F26M_S26M: + case PSRAM_CACHE_F20M_S20M: + default: + psram_spi_init(PSRAM_SPI_1, mode); + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(PSRAM_SPI_1), SPI_MEM_CS_HOLD); + gpio_matrix_out(PSRAM_CS_IO, SPICS1_OUT_IDX, 0, 0); +#ifdef FAKE_QPI + /* 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 220/221, 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: + SPI CLK --> GPIO28 --> signal220(in then out) --> internal GPIO29 --> signal221(in then out) --> GPIO17(PSRAM CLK) + */ + gpio_matrix_out(PSRAM_INTERNAL_IO_28, SPICLK_OUT_IDX, 0, 0); + gpio_matrix_in(PSRAM_INTERNAL_IO_28, SIG_IN_FUNC220_IDX, 0); + gpio_matrix_out(PSRAM_INTERNAL_IO_29, SIG_IN_FUNC220_IDX, 0, 0); + gpio_matrix_in(PSRAM_INTERNAL_IO_29, SIG_IN_FUNC221_IDX, 0); + gpio_matrix_out(PSRAM_CLK_IO, SIG_IN_FUNC221_IDX, 0, 0); +#else + REG_SET_FIELD(SPI_MEM_SRAM_CMD_REG(0), SPI_MEM_SCLK_MODE, 1); + REG_SET_FIELD(SPI_MEM_CTRL1_REG(1), SPI_MEM_CLK_MODE, 1); +#endif + + break; + } + + #if CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V + // For flash 80Mhz, we must update ldo voltage in case older version of bootloader didn't do this. + rtc_vddsdio_config_t cfg = rtc_vddsdio_get_config(); + if (cfg.enable == 1 && cfg.tieh == RTC_VDDSDIO_TIEH_1_8V) { // VDDSDIO regulator is enabled @ 1.8V + cfg.drefh = 3; + cfg.drefm = 3; + cfg.drefl = 3; + cfg.force = 1; + rtc_vddsdio_set_config(cfg); + ets_delay_us(10); // wait for regulator to become stable + } + #endif + CLEAR_PERI_REG_MASK(SPI_MEM_USER_REG(PSRAM_SPI_1), SPI_MEM_CS_SETUP_M); + psram_gpio_config(mode); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CS_IO], PIN_FUNC_GPIO); + psram_read_id(&s_psram_id); + if (!PSRAM_IS_VALID(s_psram_id)) { + return ESP_FAIL; + } + uint32_t flash_id = g_rom_flashchip.device_id; + if (flash_id == FLASH_ID_GD25LQ32C) { + // Set drive ability for 1.8v flash in 80Mhz. + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SPIHD_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SPIWP_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SPICS0_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SPICLK_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SPIQ_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SPID_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(GPIO_PIN_MUX_REG[PSRAM_CS_IO], FUN_DRV, 3, FUN_DRV_S); + } + if (PSRAM_IS_64MBIT(s_psram_id)) { + // For this psram, we don't need any extra clock cycles after cs get back to high level + s_clk_mode = PSRAM_CLK_MODE_NORM; + REG_SET_FIELD(SPI_MEM_SRAM_CMD_REG(0), SPI_MEM_SCLK_MODE, 0); + REG_SET_FIELD(SPI_MEM_CTRL1_REG(1), SPI_MEM_CLK_MODE, 0); + } else if (PSRAM_IS_32MBIT_VER0(s_psram_id)) { + s_clk_mode = PSRAM_CLK_MODE_DCLK; + if (mode == PSRAM_CACHE_F80M_S80M) { + } + } + psram_reset_mode(PSRAM_SPI_1); + psram_enable_qio_mode(PSRAM_SPI_1); + psram_cache_init(mode, vaddrmode); + return ESP_OK; +} + +static void IRAM_ATTR psram_clock_set(psram_spi_num_t spi_num, int8_t freqdiv) +{ + uint32_t freqbits; + if (1 >= freqdiv) { + WRITE_PERI_REG(SPI_MEM_SRAM_CLK_REG(spi_num), SPI_MEM_SCLK_EQU_SYSCLK); + } else { + freqbits = (((freqdiv-1)< + +#include "esp_system.h" +#include "esp_attr.h" +#include "esp_wifi.h" +#include "esp_private/wifi.h" +#include "esp_log.h" +#include "sdkconfig.h" +#include "esp32s2beta/rom/efuse.h" +#include "esp32s2beta/rom/cache.h" +#include "esp32s2beta/rom/uart.h" +#include "soc/dport_reg.h" +#include "soc/gpio_reg.h" +#include "soc/efuse_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_reg.h" +#include "soc/timer_group_struct.h" +#include "soc/cpu.h" +#include "soc/rtc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/xtensa_api.h" +#include "esp_heap_caps.h" +#include "soc/syscon_reg.h" + +static const char* TAG = "system_api"; + +static uint8_t base_mac_addr[6] = { 0 }; + +#define SHUTDOWN_HANDLERS_NO 2 +static shutdown_handler_t shutdown_handlers[SHUTDOWN_HANDLERS_NO]; + +void system_init() +{ +} + +esp_err_t esp_base_mac_addr_set(uint8_t *mac) +{ + if (mac == NULL) { + ESP_LOGE(TAG, "Base MAC address is NULL"); + abort(); + } + + memcpy(base_mac_addr, mac, 6); + + return ESP_OK; +} + +esp_err_t esp_base_mac_addr_get(uint8_t *mac) +{ + uint8_t null_mac[6] = {0}; + + if (memcmp(base_mac_addr, null_mac, 6) == 0) { + ESP_LOGI(TAG, "Base MAC address is not set, read default base MAC address from BLK0 of EFUSE"); + return ESP_ERR_INVALID_MAC; + } + + memcpy(mac, base_mac_addr, 6); + + return ESP_OK; +} + +esp_err_t esp_efuse_mac_get_custom(uint8_t *mac) +{ + return ESP_ERR_NOT_SUPPORTED; // TODO: read from MAC block in efuse +} + +esp_err_t esp_efuse_mac_get_default(uint8_t* mac) +{ + uint32_t mac_low; + uint32_t mac_high; + uint8_t efuse_crc; + uint8_t calc_crc; + +// mac_low = REG_READ(EFUSE_BLK0_RDATA1_REG); +// mac_high = REG_READ(EFUSE_BLK0_RDATA2_REG); + mac_low = REG_READ(EFUSE_RD_MAC_SPI_8M_0_REG); + mac_high = REG_GET_BIT(EFUSE_RD_MAC_SPI_8M_1_REG,EFUSE_MAC_1); + + mac[0] = mac_high >> 8; + mac[1] = mac_high; + mac[2] = mac_low >> 24; + mac[3] = mac_low >> 16; + mac[4] = mac_low >> 8; + mac[5] = mac_low; + + //efuse_crc = mac_high >> 16; + + //calc_crc = esp_crc8(mac, 6); + + //if (efuse_crc != calc_crc) { + // Small range of MAC addresses are accepted even if CRC is invalid. + // These addresses are reserved for Espressif internal use. + // if ((mac_high & 0xFFFF) == 0x18fe) { + // if ((mac_low >= 0x346a85c7) && (mac_low <= 0x346a85f8)) { + // return ESP_OK; + // } + // } else { + // ESP_LOGE(TAG, "Base MAC address from BLK0 of EFUSE CRC error, efuse_crc = 0x%02x; calc_crc = 0x%02x", efuse_crc, calc_crc); + // abort(); + // } + //} + return ESP_OK; +} + +esp_err_t system_efuse_read_mac(uint8_t *mac) __attribute__((alias("esp_efuse_mac_get_default"))); +esp_err_t esp_efuse_read_mac(uint8_t *mac) __attribute__((alias("esp_efuse_mac_get_default"))); + +esp_err_t esp_derive_mac(uint8_t* local_mac, const uint8_t* universal_mac) +{ + uint8_t idx; + + if (local_mac == NULL || universal_mac == NULL) { + ESP_LOGE(TAG, "mac address param is NULL"); + return ESP_ERR_INVALID_ARG; + } + + memcpy(local_mac, universal_mac, 6); + for (idx = 0; idx < 64; idx++) { + local_mac[0] = universal_mac[0] | 0x02; + local_mac[0] ^= idx << 2; + + if (memcmp(local_mac, universal_mac, 6)) { + break; + } + } + + return ESP_OK; +} + +esp_err_t esp_read_mac(uint8_t* mac, esp_mac_type_t type) +{ + uint8_t efuse_mac[6]; + + if (mac == NULL) { + ESP_LOGE(TAG, "mac address param is NULL"); + return ESP_ERR_INVALID_ARG; + } + + if (type < ESP_MAC_WIFI_STA || type > ESP_MAC_ETH) { + ESP_LOGE(TAG, "mac type is incorrect"); + return ESP_ERR_INVALID_ARG; + } + + _Static_assert(UNIVERSAL_MAC_ADDR_NUM == FOUR_UNIVERSAL_MAC_ADDR \ + || UNIVERSAL_MAC_ADDR_NUM == TWO_UNIVERSAL_MAC_ADDR, \ + "incorrect NUM_MAC_ADDRESS_FROM_EFUSE value"); + + if (esp_base_mac_addr_get(efuse_mac) != ESP_OK) { + esp_efuse_mac_get_default(efuse_mac); + } + + switch (type) { + case ESP_MAC_WIFI_STA: + memcpy(mac, efuse_mac, 6); + break; + case ESP_MAC_WIFI_SOFTAP: + if (UNIVERSAL_MAC_ADDR_NUM == FOUR_UNIVERSAL_MAC_ADDR) { + memcpy(mac, efuse_mac, 6); + mac[5] += 1; + } + else if (UNIVERSAL_MAC_ADDR_NUM == TWO_UNIVERSAL_MAC_ADDR) { + esp_derive_mac(mac, efuse_mac); + } + break; + case ESP_MAC_BT: + memcpy(mac, efuse_mac, 6); + if (UNIVERSAL_MAC_ADDR_NUM == FOUR_UNIVERSAL_MAC_ADDR) { + mac[5] += 2; + } + else if (UNIVERSAL_MAC_ADDR_NUM == TWO_UNIVERSAL_MAC_ADDR) { + mac[5] += 1; + } + break; + case ESP_MAC_ETH: + if (UNIVERSAL_MAC_ADDR_NUM == FOUR_UNIVERSAL_MAC_ADDR) { + memcpy(mac, efuse_mac, 6); + mac[5] += 3; + } + else if (UNIVERSAL_MAC_ADDR_NUM == TWO_UNIVERSAL_MAC_ADDR) { + efuse_mac[5] += 1; + esp_derive_mac(mac, efuse_mac); + } + break; + default: + ESP_LOGW(TAG, "incorrect mac type"); + break; + } + + return ESP_OK; +} + +esp_err_t esp_register_shutdown_handler(shutdown_handler_t handler) +{ + int i; + for (i = 0; i < SHUTDOWN_HANDLERS_NO; i++) { + if (shutdown_handlers[i] == NULL) { + shutdown_handlers[i] = handler; + return ESP_OK; + } + } + return ESP_FAIL; +} + +void esp_restart_noos() __attribute__ ((noreturn)); + +void IRAM_ATTR esp_restart(void) +{ + int i; + for (i = 0; i < SHUTDOWN_HANDLERS_NO; i++) { + if (shutdown_handlers[i]) { + shutdown_handlers[i](); + } + } + + // Disable scheduler on this core. + vTaskSuspendAll(); + + esp_restart_noos(); +} + +/* "inner" restart function for after RTOS, interrupts & anything else on this + * core are already stopped. Stalls other core, resets hardware, + * triggers restart. +*/ +void IRAM_ATTR esp_restart_noos() +{ + // Disable interrupts + xt_ints_off(0xFFFFFFFF); + + // Enable RTC watchdog for 1 second + REG_WRITE(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); + REG_WRITE(RTC_CNTL_WDTCONFIG0_REG, + RTC_CNTL_WDT_FLASHBOOT_MOD_EN_M | + (RTC_WDT_STG_SEL_RESET_SYSTEM << RTC_CNTL_WDT_STG0_S) | + (RTC_WDT_STG_SEL_RESET_RTC << RTC_CNTL_WDT_STG1_S) | + (1 << RTC_CNTL_WDT_SYS_RESET_LENGTH_S) | + (1 << RTC_CNTL_WDT_CPU_RESET_LENGTH_S) ); + REG_WRITE(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 1); + + // Reset and stall the other CPU. + // CPU must be reset before stalling, in case it was running a s32c1i + // instruction. This would cause memory pool to be locked by arbiter + // to the stalled CPU, preventing current CPU from accessing this pool. + const uint32_t core_id = xPortGetCoreID(); +#if !CONFIG_FREERTOS_UNICORE + const uint32_t other_core_id = (core_id == 0) ? 1 : 0; + esp_cpu_reset(other_core_id); + esp_cpu_stall(other_core_id); +#endif + + // Other core is now stalled, can access DPORT registers directly + esp_dport_access_int_abort(); + + // Disable TG0/TG1 watchdogs + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_config0.en = 0; + TIMERG0.wdt_wprotect=0; + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config0.en = 0; + TIMERG1.wdt_wprotect=0; + + // Flush any data left in UART FIFOs + uart_tx_wait_idle(0); + uart_tx_wait_idle(1); + // Disable cache + Cache_Disable_ICache(); + Cache_Disable_DCache(); + + // 2nd stage bootloader reconfigures SPI flash signals. + // Reset them to the defaults expected by ROM. + WRITE_PERI_REG(GPIO_FUNC0_IN_SEL_CFG_REG, 0x30); + WRITE_PERI_REG(GPIO_FUNC1_IN_SEL_CFG_REG, 0x30); + WRITE_PERI_REG(GPIO_FUNC2_IN_SEL_CFG_REG, 0x30); + WRITE_PERI_REG(GPIO_FUNC3_IN_SEL_CFG_REG, 0x30); + WRITE_PERI_REG(GPIO_FUNC4_IN_SEL_CFG_REG, 0x30); + WRITE_PERI_REG(GPIO_FUNC5_IN_SEL_CFG_REG, 0x30); + + // Reset wifi/bluetooth/ethernet/sdio (bb/mac) + DPORT_SET_PERI_REG_MASK(DPORT_CORE_RST_EN_REG, + DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST | + DPORT_BT_RST | DPORT_BTMAC_RST | DPORT_SDIO_RST | + DPORT_SDIO_HOST_RST | DPORT_EMAC_RST | DPORT_MACPWR_RST | + DPORT_RW_BTMAC_RST | DPORT_RW_BTLP_RST); + DPORT_REG_WRITE(DPORT_CORE_RST_EN_REG, 0); + + // Reset timer/spi/uart + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, + DPORT_TIMERS_RST | DPORT_SPI01_RST | DPORT_UART_RST); + DPORT_REG_WRITE(DPORT_PERIP_RST_EN_REG, 0); + + // Set CPU back to XTAL source, no PLL, same as hard reset + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + +#if !CONFIG_FREERTOS_UNICORE + // Clear entry point for APP CPU + DPORT_REG_WRITE(DPORT_APPCPU_CTRL_D_REG, 0); +#endif + + // Reset CPUs + if (core_id == 0) { + // Running on PRO CPU: APP CPU is stalled. Can reset both CPUs. +#if !CONFIG_FREERTOS_UNICORE + esp_cpu_reset(1); +#endif + esp_cpu_reset(0); + } +#if !CONFIG_FREERTOS_UNICORE + else { + // Running on APP CPU: need to reset PRO CPU and unstall it, + // then reset APP CPU + esp_cpu_reset(0); + esp_cpu_unstall(0); + esp_cpu_reset(1); + } +#endif + while(true) { + ; + } +} + +void system_restart(void) __attribute__((alias("esp_restart"))); + +uint32_t esp_get_free_heap_size( void ) +{ + return heap_caps_get_free_size( MALLOC_CAP_DEFAULT ); +} + +uint32_t esp_get_minimum_free_heap_size( void ) +{ + return heap_caps_get_minimum_free_size( MALLOC_CAP_DEFAULT ); +} + +uint32_t system_get_free_heap_size(void) __attribute__((alias("esp_get_free_heap_size"))); + +const char* system_get_sdk_version(void) +{ + return "master"; +} + +const char* esp_get_idf_version(void) +{ + return IDF_VER; +} + +void esp_chip_info(esp_chip_info_t* out_info) +{ + memset(out_info, 0, sizeof(*out_info)); + + out_info->model = CHIP_ESP32S2BETA; + out_info->cores = 1; + out_info->features = CHIP_FEATURE_WIFI_BGN; + + // FIXME: other features? +} diff --git a/components/esp32s2beta/task_wdt.c b/components/esp32s2beta/task_wdt.c new file mode 100644 index 0000000000..e3a37d4791 --- /dev/null +++ b/components/esp32s2beta/task_wdt.c @@ -0,0 +1,414 @@ +// 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. + +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOSConfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "esp_attr.h" +#include "esp_freertos_hooks.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "esp_log.h" +#include "driver/timer.h" +#include "driver/periph_ctrl.h" +#include "esp_task_wdt.h" + +//Assertion macro where, if 'cond' is false, will exit the critical section and return 'ret' +#define ASSERT_EXIT_CRIT_RETURN(cond, ret) ({ \ + if(!(cond)){ \ + portEXIT_CRITICAL(&twdt_spinlock); \ + return ret; \ + } \ +}) + +//Empty define used in ASSERT_EXIT_CRIT_RETURN macro when returning in void +#define VOID_RETURN + +//Structure used for each subscribed task +typedef struct twdt_task_t twdt_task_t; +struct twdt_task_t { + TaskHandle_t task_handle; + bool has_reset; + twdt_task_t *next; +}; + +//Structure used to hold run time configuration of the TWDT +typedef struct twdt_config_t twdt_config_t; +struct twdt_config_t { + twdt_task_t *list; //Linked list of subscribed tasks + uint32_t timeout; //Timeout period of TWDT + bool panic; //Flag to trigger panic when TWDT times out + intr_handle_t intr_handle; +}; + +static twdt_config_t *twdt_config = NULL; +static portMUX_TYPE twdt_spinlock = portMUX_INITIALIZER_UNLOCKED; + +/* + * Idle hook callback for Idle Tasks to reset the TWDT. This callback will only + * be registered to the Idle Hook of a particular core when the corresponding + * Idle Task subscribes to the TWDT. + */ +static bool idle_hook_cb(void) +{ + esp_task_wdt_reset(); + return true; +} + +/* + * Internal function that looks for the target task in the TWDT task list. + * Returns the list item if found and returns null if not found. Also checks if + * all the other tasks have reset. Should be called within critical. + */ +static twdt_task_t *find_task_in_twdt_list(TaskHandle_t handle, bool *all_reset) +{ + twdt_task_t *target = NULL; + *all_reset = true; + for(twdt_task_t *task = twdt_config->list; task != NULL; task = task->next){ + if(task->task_handle == handle){ + target = task; //Get pointer to target task list member + }else{ + if(task->has_reset == false){ //If a task has yet to reset + *all_reset = false; + } + } + } + return target; +} + +/* + * Resets the hardware timer and has_reset flags of each task on the list. + * Called within critical + */ +static void reset_hw_timer() +{ + //All tasks have reset; time to reset the hardware timer. + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_wprotect=0; + //Clear all has_reset flags in list + for (twdt_task_t *task = twdt_config->list; task != NULL; task = task->next){ + task->has_reset=false; + } +} + +/* + * ISR for when TWDT times out. Checks for which tasks have not reset. Also + * triggers panic if configured to do so + */ +static void task_wdt_isr(void *arg) +{ + portENTER_CRITICAL(&twdt_spinlock); + twdt_task_t *twdttask; + const char *cpu; + //Reset hardware timer so that 2nd stage timeout is not reached (will trigger system reset) + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_wprotect=0; + //Acknowledge interrupt + TIMERG0.int_clr.wdt=1; + //We are taking a spinlock while doing I/O (ets_printf) here. Normally, that is a pretty + //bad thing, possibly (temporarily) hanging up the 2nd core and stopping FreeRTOS. In this case, + //something bad already happened and reporting this is considered more important + //than the badness caused by a spinlock here. + + //Return immediately if no tasks have been added to task list + ASSERT_EXIT_CRIT_RETURN((twdt_config->list != NULL), VOID_RETURN); + + //Watchdog got triggered because at least one task did not reset in time. + ets_printf("Task watchdog got triggered. The following tasks did not reset the watchdog in time:\n"); + for (twdttask=twdt_config->list; twdttask!=NULL; twdttask=twdttask->next) { + if (!twdttask->has_reset) { + cpu=xTaskGetAffinity(twdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1"); + if (xTaskGetAffinity(twdttask->task_handle)==tskNO_AFFINITY) cpu=DRAM_STR("CPU 0/1"); + ets_printf(" - %s (%s)\n", pcTaskGetTaskName(twdttask->task_handle), cpu); + } + } + ets_printf(DRAM_STR("Tasks currently running:\n")); + for (int x=0; xpanic){ //Trigger Panic if configured to do so + ets_printf("Aborting.\n"); + portEXIT_CRITICAL(&twdt_spinlock); + abort(); + } + + portEXIT_CRITICAL(&twdt_spinlock); +} + +/* + * Initializes the TWDT by allocating memory for the config data + * structure, obtaining the idle task handles/registering idle hooks, and + * setting the hardware timer registers. If reconfiguring, it will just modify + * wdt_config and reset the hardware timer. + */ +esp_err_t esp_task_wdt_init(uint32_t timeout, bool panic) +{ + portENTER_CRITICAL(&twdt_spinlock); + if(twdt_config == NULL){ //TWDT not initialized yet + //Allocate memory for wdt_config + twdt_config = calloc(1, sizeof(twdt_config_t)); + ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NO_MEM); + + twdt_config->list = NULL; + twdt_config->timeout = timeout; + twdt_config->panic = panic; + + //Register Interrupt and ISR + ESP_ERROR_CHECK(esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, &twdt_config->intr_handle)) + + //Configure hardware timer + periph_module_enable(PERIPH_TIMG0_MODULE); + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; //Disable write protection + TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS + TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS + TIMERG0.wdt_config0.level_int_en=1; + TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt + TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system + TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS + TIMERG0.wdt_config2=twdt_config->timeout*2000; //Set timeout before interrupt + TIMERG0.wdt_config3=twdt_config->timeout*4000; //Set timeout before reset + TIMERG0.wdt_config0.en=1; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_wprotect=0; //Enable write protection + + }else{ //twdt_config previously initialized + //Reconfigure task wdt + twdt_config->panic = panic; + twdt_config->timeout = timeout; + + //Reconfigure hardware timer + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; //Disable write protection + TIMERG0.wdt_config0.en=0; //Disable timer + TIMERG0.wdt_config2=twdt_config->timeout*2000; //Set timeout before interrupt + TIMERG0.wdt_config3=twdt_config->timeout*4000; //Set timeout before reset + TIMERG0.wdt_config0.en=1; //Renable timer + TIMERG0.wdt_feed=1; //Reset timer + TIMERG0.wdt_wprotect=0; //Enable write protection + } + portEXIT_CRITICAL(&twdt_spinlock); + return ESP_OK; +} + +esp_err_t esp_task_wdt_deinit() +{ + portENTER_CRITICAL(&twdt_spinlock); + //TWDT must already be initialized + ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NOT_FOUND); + //Task list must be empty + ASSERT_EXIT_CRIT_RETURN((twdt_config->list == NULL), ESP_ERR_INVALID_STATE); + + //Disable hardware timer + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; //Disable write protection + TIMERG0.wdt_config0.en=0; //Disable timer + TIMERG0.wdt_wprotect=0; //Enable write protection + + ESP_ERROR_CHECK(esp_intr_free(twdt_config->intr_handle)) //Unregister interrupt + free(twdt_config); //Free twdt_config + twdt_config = NULL; + portEXIT_CRITICAL(&twdt_spinlock); + return ESP_OK; +} + +esp_err_t esp_task_wdt_add(TaskHandle_t handle) +{ + portENTER_CRITICAL(&twdt_spinlock); + //TWDT must already be initialized + ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE); + + twdt_task_t *target_task; + bool all_reset; + if (handle == NULL){ //Get handle of current task if none is provided + handle = xTaskGetCurrentTaskHandle(); + } + //Check if tasks exists in task list, and if all other tasks have reset + target_task = find_task_in_twdt_list(handle, &all_reset); + //task cannot be already subscribed + ASSERT_EXIT_CRIT_RETURN((target_task == NULL), ESP_ERR_INVALID_ARG); + + //Add target task to TWDT task list + target_task = calloc(1,sizeof(twdt_task_t)); + ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_NO_MEM); + target_task->task_handle = handle; + target_task->has_reset = true; + target_task->next = NULL; + if (twdt_config->list == NULL) { //Adding to empty list + twdt_config->list = target_task; + } else { //Adding to tail of list + twdt_task_t *task; + for (task = twdt_config->list; task->next != NULL; task = task->next){ + ; //point task to current tail of TWDT task list + } + task->next = target_task; + } + + //If idle task, register the idle hook callback to appropriate core + for(int i = 0; i < portNUM_PROCESSORS; i++){ + if(handle == xTaskGetIdleTaskHandleForCPU(i)){ + ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_hook_cb, i)) + break; + } + } + + if(all_reset){ //Reset hardware timer if all other tasks in list have reset in + reset_hw_timer(); + } + + portEXIT_CRITICAL(&twdt_spinlock); //Nested critical if Legacy + return ESP_OK; +} + +esp_err_t esp_task_wdt_reset() +{ + portENTER_CRITICAL(&twdt_spinlock); + //TWDT must already be initialized + ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE); + + TaskHandle_t handle = xTaskGetCurrentTaskHandle(); + twdt_task_t *target_task; + bool all_reset; + + //Check if task exists in task list, and if all other tasks have reset + target_task = find_task_in_twdt_list(handle, &all_reset); + //Return error if trying to reset task that is not on the task list + ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_NOT_FOUND); + + target_task->has_reset = true; //Reset the task if it's on the task list + if(all_reset){ //Reset if all other tasks in list have reset in + reset_hw_timer(); + } + + portEXIT_CRITICAL(&twdt_spinlock); + return ESP_OK; +} + +esp_err_t esp_task_wdt_delete(TaskHandle_t handle) +{ + if(handle == NULL){ + handle = xTaskGetCurrentTaskHandle(); + } + portENTER_CRITICAL(&twdt_spinlock); + //Return error if twdt has not been initialized + ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NOT_FOUND); + + twdt_task_t *target_task; + bool all_reset; + target_task = find_task_in_twdt_list(handle, &all_reset); + //Task doesn't exist on list. Return error + ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_INVALID_ARG); + + if(target_task == twdt_config->list){ //target_task is head of list. Delete + twdt_config->list = target_task->next; + free(target_task); + }else{ //target_task not head of list. Delete + twdt_task_t *prev; + for (prev = twdt_config->list; prev->next != target_task; prev = prev->next){ + ; //point prev to task preceding target_task + } + prev->next = target_task->next; + free(target_task); + } + + //If idle task, deregister idle hook callback form appropriate core + for(int i = 0; i < portNUM_PROCESSORS; i++){ + if(handle == xTaskGetIdleTaskHandleForCPU(i)){ + esp_deregister_freertos_idle_hook_for_cpu(idle_hook_cb, i); + break; + } + } + + if(all_reset){ //Reset hardware timer if all remaining tasks have reset + reset_hw_timer(); + } + + portEXIT_CRITICAL(&twdt_spinlock); + return ESP_OK; +} + +esp_err_t esp_task_wdt_status(TaskHandle_t handle) +{ + if(handle == NULL){ + handle = xTaskGetCurrentTaskHandle(); + } + + portENTER_CRITICAL(&twdt_spinlock); + //Return if TWDT is not initialized + ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE); + + twdt_task_t *task; + for(task = twdt_config->list; task!=NULL; task=task->next){ + //Return ESP_OK if task is found + ASSERT_EXIT_CRIT_RETURN((task->task_handle != handle), ESP_OK); + } + + //Task could not be found + portEXIT_CRITICAL(&twdt_spinlock); + return ESP_ERR_NOT_FOUND; +} + +void esp_task_wdt_feed() +{ + portENTER_CRITICAL(&twdt_spinlock); + //Return immediately if TWDT has not been initialized + ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), VOID_RETURN); + + //Check if task is on list + TaskHandle_t handle = xTaskGetCurrentTaskHandle(); + bool all_reset; + twdt_task_t *target_task = find_task_in_twdt_list(handle, &all_reset); + + //reset the task if it's on the list, then return + if(target_task != NULL){ + target_task->has_reset = true; + if(all_reset){ + reset_hw_timer(); //Reset hardware timer if all other tasks have reset + } + portEXIT_CRITICAL(&twdt_spinlock); + return; + } + + //Add task if it's has not on list + target_task = calloc(1, sizeof(twdt_task_t)); + ASSERT_EXIT_CRIT_RETURN((target_task != NULL), VOID_RETURN); //If calloc failed + target_task->task_handle = handle; + target_task->has_reset = true; + target_task->next = NULL; + + if (twdt_config->list == NULL) { //Adding to empty list + twdt_config->list = target_task; + } else { //Adding to tail of list + twdt_task_t *task; + for (task = twdt_config->list; task->next != NULL; task = task->next){ + ; //point task to current tail of wdt task list + } + task->next = target_task; + } + + portEXIT_CRITICAL(&twdt_spinlock); +} + + diff --git a/components/esp_wifi/src/phy_init.c b/components/esp_wifi/src/phy_init.c index b36b5754f3..bd990c8fcd 100644 --- a/components/esp_wifi/src/phy_init.c +++ b/components/esp_wifi/src/phy_init.c @@ -16,13 +16,10 @@ #include #include #include - #include -#include "esp32/rom/ets_sys.h" -#include "esp32/rom/rtc.h" #include "soc/rtc.h" - +#include "soc/dport_reg.h" #include "esp_err.h" #include "esp_phy_init.h" #include "esp_system.h" @@ -30,7 +27,6 @@ #include "nvs.h" #include "nvs_flash.h" #include "sdkconfig.h" - #include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" #include "phy.h" @@ -39,6 +35,14 @@ #include "driver/periph_ctrl.h" #include "esp_private/wifi.h" +#if CONFIG_IDF_TARGET_ESP32 +#include "esp32/rom/ets_sys.h" +#include "esp32/rom/rtc.h" +#elif CONFIG_IDF_TARGET_ESP32S2BETA +#include "esp32s2beta/rom/ets_sys.h" +#include "esp32s2beta/rom/rtc.h" +#endif + extern wifi_mac_time_update_cb_t s_wifi_mac_time_update_cb; static const char* TAG = "phy_init"; @@ -100,7 +104,7 @@ static inline void phy_update_wifi_mac_time(bool en_clock_stopped, int64_t now) } } -esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data, esp_phy_calibration_mode_t mode, +esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data, esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data, phy_rf_module_t module) { /* 3 modules may call phy_init: Wi-Fi, BT, Modem Sleep */ @@ -123,9 +127,9 @@ esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data, esp_phy_calibrat } else { /* If Wi-Fi, BT all disabled, modem sleep should not take effect; - * If either Wi-Fi or BT is enabled, should allow modem sleep requires + * If either Wi-Fi or BT is enabled, should allow modem sleep requires * to enter sleep; - * If Wi-Fi, BT co-exist, it is disallowed that only one module + * If Wi-Fi, BT co-exist, it is disallowed that only one module * support modem sleep, E,g. BT support modem sleep but Wi-Fi not * support modem sleep; */ @@ -577,7 +581,7 @@ static esp_err_t store_cal_data_to_nvs_handle(nvs_handle_t handle, if (err != ESP_OK) { ESP_LOGE(TAG, "%s: store calibration nvs commit failed(0x%x)\n", __func__, err); } - + return err; } @@ -585,10 +589,10 @@ static esp_err_t store_cal_data_to_nvs_handle(nvs_handle_t handle, static void esp_phy_reduce_tx_power(esp_phy_init_data_t* init_data) { uint8_t i; - + for(i = 0; i < PHY_TX_POWER_NUM; i++) { // LOWEST_PHY_TX_POWER is the lowest tx power - init_data->params[PHY_TX_POWER_OFFSET+i] = PHY_TX_POWER_LOWEST; + init_data->params[PHY_TX_POWER_OFFSET+i] = PHY_TX_POWER_LOWEST; } } #endif diff --git a/components/mbedtls/port/esp32s2beta/aes.c b/components/mbedtls/port/esp32s2beta/aes.c new file mode 100644 index 0000000000..9384b12a7c --- /dev/null +++ b/components/mbedtls/port/esp32s2beta/aes.c @@ -0,0 +1,381 @@ +/** + * \brief AES block cipher, ESP32 hardware accelerated version + * Based on mbedTLS FIPS-197 compliant version. + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * Additions Copyright (C) 2016-2017, Espressif Systems (Shanghai) PTE Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + */ +/* + * The AES block cipher was designed by Vincent Rijmen and Joan Daemen. + * + * http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf + * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + */ +#include +#include "mbedtls/aes.h" +#include "hwcrypto/aes.h" +#include "soc/dport_reg.h" +#include "soc/hwcrypto_reg.h" +#include + +#include + +#include "soc/cpu.h" +#include + +#define AES_BLOCK_BYTES 16 + +/* AES uses a spinlock mux not a lock as the underlying block operation + only takes a small number of cycles, much less than using + a mutex for this. + + For CBC, CFB, etc. this may mean that interrupts are disabled for a longer + period of time for bigger data lengths. +*/ +static portMUX_TYPE aes_spinlock = portMUX_INITIALIZER_UNLOCKED; + +void esp_aes_acquire_hardware( void ) +{ + /* newlib locks lazy initialize on ESP-IDF */ + portENTER_CRITICAL(&aes_spinlock); + + /* Enable AES hardware */ + REG_SET_BIT(DPORT_PERI_CLK_EN_REG, DPORT_PERI_EN_AES); + /* Clear reset on digital signature unit, + otherwise AES unit is held in reset also. */ + REG_CLR_BIT(DPORT_PERI_RST_EN_REG, + DPORT_PERI_EN_AES + | DPORT_PERI_EN_DIGITAL_SIGNATURE); +} + +void esp_aes_release_hardware( void ) +{ + /* Disable AES hardware */ + REG_SET_BIT(DPORT_PERI_RST_EN_REG, DPORT_PERI_EN_AES); + /* Don't return other units to reset, as this pulls + reset on RSA & SHA units, respectively. */ + REG_CLR_BIT(DPORT_PERI_CLK_EN_REG, DPORT_PERI_EN_AES); + + portEXIT_CRITICAL(&aes_spinlock); +} + +void esp_aes_init( esp_aes_context *ctx ) +{ + bzero( ctx, sizeof( esp_aes_context ) ); +} + +void esp_aes_free( esp_aes_context *ctx ) +{ + if ( ctx == NULL ) { + return; + } + + bzero( ctx, sizeof( esp_aes_context ) ); +} + +/* + * AES key schedule (same for encryption or decryption, as hardware handles schedule) + * + */ +int esp_aes_setkey( esp_aes_context *ctx, const unsigned char *key, + unsigned int keybits ) +{ + if (keybits != 128 && keybits != 192 && keybits != 256) { + return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH; + } + ctx->key_bytes = keybits / 8; + memcpy(ctx->key, key, ctx->key_bytes); + return 0; +} + +/* + * Helper function to copy key from esp_aes_context buffer + * to hardware key registers. + * + * Call only while holding esp_aes_acquire_hardware(). + */ +static inline void esp_aes_setkey_hardware( esp_aes_context *ctx, int mode) +{ + const uint32_t MODE_DECRYPT_BIT = 4; + unsigned mode_reg_base = (mode == ESP_AES_ENCRYPT) ? 0 : MODE_DECRYPT_BIT; + + memcpy((uint32_t *)AES_KEY_BASE, ctx->key, ctx->key_bytes); + REG_WRITE(AES_MODE_REG, mode_reg_base + ((ctx->key_bytes / 8) - 2)); +} + +/* Run a single 16 byte block of AES, using the hardware engine. + * + * Call only while holding esp_aes_acquire_hardware(). + */ +static inline void esp_aes_block(const void *input, void *output) +{ + memcpy((void *)AES_TEXT_IN_BASE, input, AES_BLOCK_BYTES); + + REG_WRITE(AES_TRIGGER_REG, 1); + while (REG_READ(AES_STATE_REG) != 0) { } + + memcpy(output, (void *)AES_TEXT_OUT_BASE, AES_BLOCK_BYTES); +} + +/* + * AES-ECB block encryption + */ +int esp_internal_aes_encrypt( esp_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16] ) +{ + esp_aes_acquire_hardware(); + esp_aes_setkey_hardware(ctx, ESP_AES_ENCRYPT); + esp_aes_block(input, output); + esp_aes_release_hardware(); + return 0; +} + +void esp_aes_encrypt( esp_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16] ) +{ + esp_internal_aes_encrypt(ctx, input, output); +} + +/* + * AES-ECB block decryption + */ + +int esp_internal_aes_decrypt( esp_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16] ) +{ + esp_aes_acquire_hardware(); + esp_aes_setkey_hardware(ctx, ESP_AES_DECRYPT); + esp_aes_block(input, output); + esp_aes_release_hardware(); + return 0; +} + +void esp_aes_decrypt( esp_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16] ) +{ + esp_internal_aes_decrypt(ctx, input, output); +} + + +/* + * AES-ECB block encryption/decryption + */ +int esp_aes_crypt_ecb( esp_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ + esp_aes_acquire_hardware(); + esp_aes_setkey_hardware(ctx, mode); + esp_aes_block(input, output); + esp_aes_release_hardware(); + + return 0; +} + + +/* + * AES-CBC buffer encryption/decryption + */ +int esp_aes_crypt_cbc( esp_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int i; + uint32_t *output_words = (uint32_t *)output; + const uint32_t *input_words = (const uint32_t *)input; + uint32_t *iv_words = (uint32_t *)iv; + unsigned char temp[16]; + + if ( length % 16 ) { + return ( ERR_ESP_AES_INVALID_INPUT_LENGTH ); + } + + esp_aes_acquire_hardware(); + + esp_aes_setkey_hardware(ctx, mode); + + if ( mode == ESP_AES_DECRYPT ) { + while ( length > 0 ) { + memcpy(temp, input_words, 16); + esp_aes_block(input_words, output_words); + + for ( i = 0; i < 4; i++ ) { + output_words[i] = output_words[i] ^ iv_words[i]; + } + + memcpy( iv_words, temp, 16 ); + + input_words += 4; + output_words += 4; + length -= 16; + } + } else { // ESP_AES_ENCRYPT + while ( length > 0 ) { + + for ( i = 0; i < 4; i++ ) { + output_words[i] = input_words[i] ^ iv_words[i]; + } + + esp_aes_block(output_words, output_words); + memcpy( iv_words, output_words, 16 ); + + input_words += 4; + output_words += 4; + length -= 16; + } + } + + esp_aes_release_hardware(); + + return 0; +} + +/* + * AES-CFB128 buffer encryption/decryption + */ +int esp_aes_crypt_cfb128( esp_aes_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int c; + size_t n = *iv_off; + + esp_aes_acquire_hardware(); + + esp_aes_setkey_hardware(ctx, ESP_AES_ENCRYPT); + + if ( mode == ESP_AES_DECRYPT ) { + while ( length-- ) { + if ( n == 0 ) { + esp_aes_block(iv, iv ); + } + + c = *input++; + *output++ = (unsigned char)( c ^ iv[n] ); + iv[n] = (unsigned char) c; + + n = ( n + 1 ) & 0x0F; + } + } else { + while ( length-- ) { + if ( n == 0 ) { + esp_aes_block(iv, iv ); + } + + iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); + + n = ( n + 1 ) & 0x0F; + } + } + + *iv_off = n; + + esp_aes_release_hardware(); + + return 0; +} + +/* + * AES-CFB8 buffer encryption/decryption + */ +int esp_aes_crypt_cfb8( esp_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + unsigned char c; + unsigned char ov[17]; + + esp_aes_acquire_hardware(); + + esp_aes_setkey_hardware(ctx, ESP_AES_ENCRYPT); + + while ( length-- ) { + memcpy( ov, iv, 16 ); + esp_aes_block(iv, iv); + + if ( mode == ESP_AES_DECRYPT ) { + ov[16] = *input; + } + + c = *output++ = (unsigned char)( iv[0] ^ *input++ ); + + if ( mode == ESP_AES_ENCRYPT ) { + ov[16] = c; + } + + memcpy( iv, ov + 1, 16 ); + } + + esp_aes_release_hardware(); + + return 0; +} + +/* + * AES-CTR buffer encryption/decryption + */ +int esp_aes_crypt_ctr( esp_aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output ) +{ + int c, i; + size_t n = *nc_off; + + esp_aes_acquire_hardware(); + + esp_aes_setkey_hardware(ctx, ESP_AES_ENCRYPT); + + while ( length-- ) { + if ( n == 0 ) { + esp_aes_block(nonce_counter, stream_block); + + for ( i = 16; i > 0; i-- ) + if ( ++nonce_counter[i - 1] != 0 ) { + break; + } + } + c = *input++; + *output++ = (unsigned char)( c ^ stream_block[n] ); + + n = ( n + 1 ) & 0x0F; + } + + *nc_off = n; + + esp_aes_release_hardware(); + + return 0; +} diff --git a/components/mbedtls/port/esp32s2beta/sha.c b/components/mbedtls/port/esp32s2beta/sha.c new file mode 100644 index 0000000000..e31beaa3c8 --- /dev/null +++ b/components/mbedtls/port/esp32s2beta/sha.c @@ -0,0 +1,184 @@ +/* + * ESP32 hardware accelerated SHA1/256/512 implementation + * based on mbedTLS FIPS-197 compliant version. + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * Additions Copyright (C) 2016, Espressif Systems (Shanghai) PTE Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + */ +/* + * The SHA-1 standard was published by NIST in 1993. + * + * http://www.itl.nist.gov/fipspubs/fip180-1.htm + */ + +#include +#include +#include +#include +#include + +#include "hwcrypto/sha.h" +#include "rom/ets_sys.h" +#include "soc/dport_reg.h" +#include "soc/hwcrypto_reg.h" + +/* Single lock for SHA engine +*/ +static _lock_t s_sha_lock; + +/* This API was designed for ESP32, which has seperate + engines for SHA1,256,512. ESP32C has a single engine. +*/ + +/* Return block size (in bytes) for a given SHA type */ +inline static size_t block_length(esp_sha_type type) { + switch(type) { + case SHA1: + case SHA2_224: + case SHA2_256: + return 64; + case SHA2_384: + case SHA2_512: + return 128; + default: + return 0; + } +} + +/* Return state size (in bytes) for a given SHA type */ +inline static size_t state_length(esp_sha_type type) { + switch(type) { + case SHA1: + return 160/8; + case SHA2_224: + case SHA2_256: + return 256/8; + case SHA2_384: + case SHA2_512: + return 512/8; + default: + return 0; + } +} + +/* Copy words in memory (to/from a memory block), byte swapping as we go. */ +static void memcpy_endianswap(void *to, const void *from, size_t num_bytes) +{ + uint32_t *to_words = (uint32_t *)to; + const uint32_t *from_words = (const uint32_t *)from; + assert(num_bytes % 4 == 0); + for (int i = 0; i < num_bytes / 4; i++) { + to_words[i] = __bswap_32(from_words[i]); + } + asm volatile ("memw"); +} + +static void memcpy_swapwords(void *to, const void *from, size_t num_bytes) +{ + uint32_t *to_words = (uint32_t *)to; + const uint32_t *from_words = (const uint32_t *)from; + assert(num_bytes % 8 == 0); + for (int i = 0; i < num_bytes / 4; i += 2) { + to_words[i] = from_words[i+1]; + to_words[i+1] = from_words[i]; + } + asm volatile ("memw"); +} + +static void esp_sha_lock_engine_inner(void); + +bool esp_sha_try_lock_engine(esp_sha_type sha_type) +{ + if(_lock_try_acquire(&s_sha_lock) != 0) { + /* SHA engine is already in use */ + return false; + } else { + esp_sha_lock_engine_inner(); + return true; + } +} + +void esp_sha_lock_engine(esp_sha_type sha_type) +{ + _lock_acquire(&s_sha_lock); + esp_sha_lock_engine_inner(); +} + +static void esp_sha_lock_engine_inner(void) +{ + ets_sha_enable(); +} + +void esp_sha_unlock_engine(esp_sha_type sha_type) +{ + ets_sha_disable(); + _lock_release(&s_sha_lock); +} + +void esp_sha_wait_idle(void) +{ + while(DPORT_REG_READ(SHA_BUSY_REG) != 0) { } +} + +void esp_sha_read_digest_state(esp_sha_type sha_type, void *digest_state) +{ + /* engine should be locked */ + esp_sha_wait_idle(); + if (sha_type != SHA2_512 && sha_type != SHA2_384) { + /* , which includes this file, for more details. + */ + +/* Xtensa processor core configuration information. + + Copyright (c) 1999-2018 Tensilica Inc. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _XTENSA_CORE_CONFIGURATION_H +#define _XTENSA_CORE_CONFIGURATION_H + + +/**************************************************************************** + Parameters Useful for Any Code, USER or PRIVILEGED + ****************************************************************************/ + +/* + * Note: Macros of the form XCHAL_HAVE_*** have a value of 1 if the option is + * configured, and a value of 0 otherwise. These macros are always defined. + */ + + +/*---------------------------------------------------------------------- + ISA + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_BE 0 /* big-endian byte ordering */ +#define XCHAL_HAVE_WINDOWED 1 /* windowed registers option */ +#define XCHAL_NUM_AREGS 64 /* num of physical addr regs */ +#define XCHAL_NUM_AREGS_LOG2 6 /* log2(XCHAL_NUM_AREGS) */ +#define XCHAL_MAX_INSTRUCTION_SIZE 3 /* max instr bytes (3..8) */ +#define XCHAL_HAVE_DEBUG 1 /* debug option */ +#define XCHAL_HAVE_DENSITY 1 /* 16-bit instructions */ +#define XCHAL_HAVE_LOOPS 0 /* zero-overhead loops */ +#define XCHAL_LOOP_BUFFER_SIZE 0 /* zero-ov. loop instr buffer size */ +#define XCHAL_HAVE_NSA 1 /* NSA/NSAU instructions */ +#define XCHAL_HAVE_MINMAX 1 /* MIN/MAX instructions */ +#define XCHAL_HAVE_SEXT 1 /* SEXT instruction */ +#define XCHAL_HAVE_DEPBITS 0 /* DEPBITS instruction */ +#define XCHAL_HAVE_CLAMPS 1 /* CLAMPS instruction */ +#define XCHAL_HAVE_MUL16 1 /* MUL16S/MUL16U instructions */ +#define XCHAL_HAVE_MUL32 1 /* MULL instruction */ +#define XCHAL_HAVE_MUL32_HIGH 1 /* MULUH/MULSH instructions */ +#define XCHAL_HAVE_DIV32 1 /* QUOS/QUOU/REMS/REMU instructions */ +#define XCHAL_HAVE_L32R 1 /* L32R instruction */ +#define XCHAL_HAVE_ABSOLUTE_LITERALS 0 /* non-PC-rel (extended) L32R */ +#define XCHAL_HAVE_CONST16 0 /* CONST16 instruction */ +#define XCHAL_HAVE_ADDX 1 /* ADDX#/SUBX# instructions */ +#define XCHAL_HAVE_EXCLUSIVE 0 /* L32EX/S32EX instructions */ +#define XCHAL_HAVE_WIDE_BRANCHES 0 /* B*.W18 or B*.W15 instr's */ +#define XCHAL_HAVE_PREDICTED_BRANCHES 0 /* B[EQ/EQZ/NE/NEZ]T instr's */ +#define XCHAL_HAVE_CALL4AND12 1 /* (obsolete option) */ +#define XCHAL_HAVE_ABS 1 /* ABS instruction */ +/*#define XCHAL_HAVE_POPC 0*/ /* POPC instruction */ +/*#define XCHAL_HAVE_CRC 0*/ /* CRC instruction */ +#define XCHAL_HAVE_RELEASE_SYNC 1 /* L32AI/S32RI instructions */ +#define XCHAL_HAVE_S32C1I 0 /* S32C1I instruction */ +#define XCHAL_HAVE_SPECULATION 0 /* speculation */ +#define XCHAL_HAVE_FULL_RESET 1 /* all regs/state reset */ +#define XCHAL_NUM_CONTEXTS 1 /* */ +#define XCHAL_NUM_MISC_REGS 4 /* num of scratch regs (0..4) */ +#define XCHAL_HAVE_TAP_MASTER 0 /* JTAG TAP control instr's */ +#define XCHAL_HAVE_PRID 1 /* processor ID register */ +#define XCHAL_HAVE_EXTERN_REGS 1 /* WER/RER instructions */ +#define XCHAL_HAVE_MX 0 /* MX core (Tensilica internal) */ +#define XCHAL_HAVE_MP_INTERRUPTS 0 /* interrupt distributor port */ +#define XCHAL_HAVE_MP_RUNSTALL 0 /* core RunStall control port */ +#define XCHAL_HAVE_PSO 0 /* Power Shut-Off */ +#define XCHAL_HAVE_PSO_CDM 0 /* core/debug/mem pwr domains */ +#define XCHAL_HAVE_PSO_FULL_RETENTION 0 /* all regs preserved on PSO */ +#define XCHAL_HAVE_THREADPTR 1 /* THREADPTR register */ +#define XCHAL_HAVE_BOOLEANS 0 /* boolean registers */ +#define XCHAL_HAVE_CP 1 /* CPENABLE reg (coprocessor) */ +#define XCHAL_CP_MAXCFG 8 /* max allowed cp id plus one */ +#define XCHAL_HAVE_MAC16 0 /* MAC16 package */ + +#define XCHAL_HAVE_FUSION 0 /* Fusion*/ +#define XCHAL_HAVE_FUSION_FP 0 /* Fusion FP option */ +#define XCHAL_HAVE_FUSION_LOW_POWER 0 /* Fusion Low Power option */ +#define XCHAL_HAVE_FUSION_AES 0 /* Fusion BLE/Wifi AES-128 CCM option */ +#define XCHAL_HAVE_FUSION_CONVENC 0 /* Fusion Conv Encode option */ +#define XCHAL_HAVE_FUSION_LFSR_CRC 0 /* Fusion LFSR-CRC option */ +#define XCHAL_HAVE_FUSION_BITOPS 0 /* Fusion Bit Operations Support option */ +#define XCHAL_HAVE_FUSION_AVS 0 /* Fusion AVS option */ +#define XCHAL_HAVE_FUSION_16BIT_BASEBAND 0 /* Fusion 16-bit Baseband option */ +#define XCHAL_HAVE_FUSION_VITERBI 0 /* Fusion Viterbi option */ +#define XCHAL_HAVE_FUSION_SOFTDEMAP 0 /* Fusion Soft Bit Demap option */ +#define XCHAL_HAVE_HIFIPRO 0 /* HiFiPro Audio Engine pkg */ +#define XCHAL_HAVE_HIFI4 0 /* HiFi4 Audio Engine pkg */ +#define XCHAL_HAVE_HIFI4_VFPU 0 /* HiFi4 Audio Engine VFPU option */ +#define XCHAL_HAVE_HIFI3 0 /* HiFi3 Audio Engine pkg */ +#define XCHAL_HAVE_HIFI3_VFPU 0 /* HiFi3 Audio Engine VFPU option */ +#define XCHAL_HAVE_HIFI3Z 0 /* HiFi3Z Audio Engine pkg */ +#define XCHAL_HAVE_HIFI3Z_VFPU 0 /* HiFi3Z Audio Engine VFPU option */ +#define XCHAL_HAVE_HIFI2 0 /* HiFi2 Audio Engine pkg */ +#define XCHAL_HAVE_HIFI2EP 0 /* HiFi2EP */ +#define XCHAL_HAVE_HIFI_MINI 0 + + + +#define XCHAL_HAVE_VECTORFPU2005 0 /* vector floating-point pkg */ +#define XCHAL_HAVE_USER_DPFPU 0 /* user DP floating-point pkg */ +#define XCHAL_HAVE_USER_SPFPU 0 /* user SP floating-point pkg */ +#define XCHAL_HAVE_FP 0 /* single prec floating point */ +#define XCHAL_HAVE_FP_DIV 0 /* FP with DIV instructions */ +#define XCHAL_HAVE_FP_RECIP 0 /* FP with RECIP instructions */ +#define XCHAL_HAVE_FP_SQRT 0 /* FP with SQRT instructions */ +#define XCHAL_HAVE_FP_RSQRT 0 /* FP with RSQRT instructions */ +#define XCHAL_HAVE_DFP 0 /* double precision FP pkg */ +#define XCHAL_HAVE_DFP_DIV 0 /* DFP with DIV instructions */ +#define XCHAL_HAVE_DFP_RECIP 0 /* DFP with RECIP instructions*/ +#define XCHAL_HAVE_DFP_SQRT 0 /* DFP with SQRT instructions */ +#define XCHAL_HAVE_DFP_RSQRT 0 /* DFP with RSQRT instructions*/ +#define XCHAL_HAVE_DFP_ACCEL 0 /* double precision FP acceleration pkg */ +#define XCHAL_HAVE_DFP_accel XCHAL_HAVE_DFP_ACCEL /* for backward compatibility */ + +#define XCHAL_HAVE_DFPU_SINGLE_ONLY 0 /* DFPU Coprocessor, single precision only */ +#define XCHAL_HAVE_DFPU_SINGLE_DOUBLE 0 /* DFPU Coprocessor, single and double precision */ +#define XCHAL_HAVE_VECTRA1 0 /* Vectra I pkg */ +#define XCHAL_HAVE_VECTRALX 0 /* Vectra LX pkg */ + +#define XCHAL_HAVE_FUSIONG 0 /* FusionG */ +#define XCHAL_HAVE_FUSIONG3 0 /* FusionG3 */ +#define XCHAL_HAVE_FUSIONG6 0 /* FusionG6 */ +#define XCHAL_HAVE_FUSIONG_SP_VFPU 0 /* sp_vfpu option on FusionG */ +#define XCHAL_HAVE_FUSIONG_DP_VFPU 0 /* dp_vfpu option on FusionG */ +#define XCHAL_FUSIONG_SIMD32 0 /* simd32 for FusionG */ + +#define XCHAL_HAVE_PDX 0 /* PDX */ +#define XCHAL_PDX_SIMD32 0 /* simd32 for PDX */ +#define XCHAL_HAVE_PDX4 0 /* PDX4 */ +#define XCHAL_HAVE_PDX8 0 /* PDX8 */ +#define XCHAL_HAVE_PDX16 0 /* PDX16 */ + +#define XCHAL_HAVE_CONNXD2 0 /* ConnX D2 pkg */ +#define XCHAL_HAVE_CONNXD2_DUALLSFLIX 0 /* ConnX D2 & Dual LoadStore Flix */ +#define XCHAL_HAVE_BBE16 0 /* ConnX BBE16 pkg */ +#define XCHAL_HAVE_BBE16_RSQRT 0 /* BBE16 & vector recip sqrt */ +#define XCHAL_HAVE_BBE16_VECDIV 0 /* BBE16 & vector divide */ +#define XCHAL_HAVE_BBE16_DESPREAD 0 /* BBE16 & despread */ +#define XCHAL_HAVE_BBENEP 0 /* ConnX BBENEP pkgs */ +#define XCHAL_HAVE_BBENEP_SP_VFPU 0 /* sp_vfpu option on BBE-EP */ +#define XCHAL_HAVE_BSP3 0 /* ConnX BSP3 pkg */ +#define XCHAL_HAVE_BSP3_TRANSPOSE 0 /* BSP3 & transpose32x32 */ +#define XCHAL_HAVE_SSP16 0 /* ConnX SSP16 pkg */ +#define XCHAL_HAVE_SSP16_VITERBI 0 /* SSP16 & viterbi */ +#define XCHAL_HAVE_TURBO16 0 /* ConnX Turbo16 pkg */ +#define XCHAL_HAVE_BBP16 0 /* ConnX BBP16 pkg */ +#define XCHAL_HAVE_FLIX3 0 /* basic 3-way FLIX option */ +#define XCHAL_HAVE_GRIVPEP 0 /* General Release of IVPEP */ +#define XCHAL_HAVE_GRIVPEP_HISTOGRAM 0 /* Histogram option on GRIVPEP */ + +#define XCHAL_HAVE_VISION 0 /* Vision P5/P6 */ +#define XCHAL_VISION_SIMD16 0 /* simd16 for Vision P5/P6 */ +#define XCHAL_VISION_TYPE 0 /* Vision P5, P6, or P3 */ +#define XCHAL_VISION_QUAD_MAC_TYPE 0 /* quad_mac option on Vision P6 */ +#define XCHAL_HAVE_VISION_HISTOGRAM 0 /* histogram option on Vision P5/P6 */ +#define XCHAL_HAVE_VISION_SP_VFPU 0 /* sp_vfpu option on Vision P5/P6 */ +#define XCHAL_HAVE_VISION_HP_VFPU 0 /* hp_vfpu option on Vision P6 */ + +#define XCHAL_HAVE_VISIONC 0 /* Vision C */ + +/*---------------------------------------------------------------------- + MISC + ----------------------------------------------------------------------*/ + +#define XCHAL_NUM_LOADSTORE_UNITS 1 /* load/store units */ +#define XCHAL_NUM_WRITEBUFFER_ENTRIES 4 /* size of write buffer */ +#define XCHAL_INST_FETCH_WIDTH 4 /* instr-fetch width in bytes */ +#define XCHAL_DATA_WIDTH 4 /* data width in bytes */ +#define XCHAL_DATA_PIPE_DELAY 2 /* d-side pipeline delay + (1 = 5-stage, 2 = 7-stage) */ +#define XCHAL_CLOCK_GATING_GLOBAL 0 /* global clock gating */ +#define XCHAL_CLOCK_GATING_FUNCUNIT 0 /* funct. unit clock gating */ +/* In T1050, applies to selected core load and store instructions (see ISA): */ +#define XCHAL_UNALIGNED_LOAD_EXCEPTION 0 /* unaligned loads cause exc. */ +#define XCHAL_UNALIGNED_STORE_EXCEPTION 0 /* unaligned stores cause exc.*/ +#define XCHAL_UNALIGNED_LOAD_HW 1 /* unaligned loads work in hw */ +#define XCHAL_UNALIGNED_STORE_HW 1 /* unaligned stores work in hw*/ + +#define XCHAL_SW_VERSION 1200008 /* sw version of this header */ + +#define XCHAL_CORE_ID "test_0731_1_TIE_GPIO_f" /* alphanum core name + (CoreID) set in the Xtensa + Processor Generator */ + +#define XCHAL_BUILD_UNIQUE_ID 0x00075F76 /* 22-bit sw build ID */ + +/* + * These definitions describe the hardware targeted by this software. + */ +#define XCHAL_HW_CONFIGID0 0xC2ECFAFE /* ConfigID hi 32 bits*/ +#define XCHAL_HW_CONFIGID1 0x22075F76 /* ConfigID lo 32 bits*/ +#define XCHAL_HW_VERSION_NAME "LX7.0.8" /* full version name */ +#define XCHAL_HW_VERSION_MAJOR 2700 /* major ver# of targeted hw */ +#define XCHAL_HW_VERSION_MINOR 8 /* minor ver# of targeted hw */ +#define XCHAL_HW_VERSION 270008 /* major*100+minor */ +#define XCHAL_HW_REL_LX7 1 +#define XCHAL_HW_REL_LX7_0 1 +#define XCHAL_HW_REL_LX7_0_8 1 +#define XCHAL_HW_CONFIGID_RELIABLE 1 +/* If software targets a *range* of hardware versions, these are the bounds: */ +#define XCHAL_HW_MIN_VERSION_MAJOR 2700 /* major v of earliest tgt hw */ +#define XCHAL_HW_MIN_VERSION_MINOR 8 /* minor v of earliest tgt hw */ +#define XCHAL_HW_MIN_VERSION 270008 /* earliest targeted hw */ +#define XCHAL_HW_MAX_VERSION_MAJOR 2700 /* major v of latest tgt hw */ +#define XCHAL_HW_MAX_VERSION_MINOR 8 /* minor v of latest tgt hw */ +#define XCHAL_HW_MAX_VERSION 270008 /* latest targeted hw */ + + +/*---------------------------------------------------------------------- + CACHE + ----------------------------------------------------------------------*/ + +#define XCHAL_ICACHE_LINESIZE 4 /* I-cache line size in bytes */ +#define XCHAL_DCACHE_LINESIZE 4 /* D-cache line size in bytes */ +#define XCHAL_ICACHE_LINEWIDTH 2 /* log2(I line size in bytes) */ +#define XCHAL_DCACHE_LINEWIDTH 2 /* log2(D line size in bytes) */ + +#define XCHAL_ICACHE_SIZE 0 /* I-cache size in bytes or 0 */ +#define XCHAL_DCACHE_SIZE 0 /* D-cache size in bytes or 0 */ + +#define XCHAL_DCACHE_IS_WRITEBACK 0 /* writeback feature */ +#define XCHAL_DCACHE_IS_COHERENT 0 /* MP coherence feature */ + +#define XCHAL_HAVE_PREFETCH 0 /* PREFCTL register */ +#define XCHAL_HAVE_PREFETCH_L1 0 /* prefetch to L1 dcache */ +#define XCHAL_PREFETCH_CASTOUT_LINES 0 /* dcache pref. castout bufsz */ +#define XCHAL_PREFETCH_ENTRIES 0 /* cache prefetch entries */ +#define XCHAL_PREFETCH_BLOCK_ENTRIES 0 /* prefetch block streams */ +#define XCHAL_HAVE_CACHE_BLOCKOPS 0 /* block prefetch for caches */ +#define XCHAL_HAVE_ICACHE_TEST 0 /* Icache test instructions */ +#define XCHAL_HAVE_DCACHE_TEST 0 /* Dcache test instructions */ +#define XCHAL_HAVE_ICACHE_DYN_WAYS 0 /* Icache dynamic way support */ +#define XCHAL_HAVE_DCACHE_DYN_WAYS 0 /* Dcache dynamic way support */ + + + + +/**************************************************************************** + Parameters Useful for PRIVILEGED (Supervisory or Non-Virtualized) Code + ****************************************************************************/ + + +#ifndef XTENSA_HAL_NON_PRIVILEGED_ONLY + +/*---------------------------------------------------------------------- + CACHE + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_PIF 1 /* any outbound bus present */ + +#define XCHAL_HAVE_AXI 0 /* AXI bus */ +#define XCHAL_HAVE_AXI_ECC 0 /* ECC on AXI bus */ +#define XCHAL_HAVE_ACELITE 0 /* ACELite bus */ + +#define XCHAL_HAVE_PIF_WR_RESP 0 /* pif write response */ +#define XCHAL_HAVE_PIF_REQ_ATTR 1 /* pif attribute */ + +/* If present, cache size in bytes == (ways * 2^(linewidth + setwidth)). */ + +/* Number of cache sets in log2(lines per way): */ +#define XCHAL_ICACHE_SETWIDTH 0 +#define XCHAL_DCACHE_SETWIDTH 0 + +/* Cache set associativity (number of ways): */ +#define XCHAL_ICACHE_WAYS 1 +#define XCHAL_DCACHE_WAYS 1 + +/* Cache features: */ +#define XCHAL_ICACHE_LINE_LOCKABLE 0 +#define XCHAL_DCACHE_LINE_LOCKABLE 0 +#define XCHAL_ICACHE_ECC_PARITY 0 +#define XCHAL_DCACHE_ECC_PARITY 0 + +/* Cache access size in bytes (affects operation of SICW instruction): */ +#define XCHAL_ICACHE_ACCESS_SIZE 1 +#define XCHAL_DCACHE_ACCESS_SIZE 1 + +#define XCHAL_DCACHE_BANKS 0 /* number of banks */ + +/* Number of encoded cache attr bits (see for decoded bits): */ +#define XCHAL_CA_BITS 4 + + +/*---------------------------------------------------------------------- + INTERNAL I/D RAM/ROMs and XLMI + ----------------------------------------------------------------------*/ +#define XCHAL_NUM_INSTROM 1 /* number of core instr. ROMs */ +#define XCHAL_NUM_INSTRAM 2 /* number of core instr. RAMs */ +#define XCHAL_NUM_DATAROM 1 /* number of core data ROMs */ +#define XCHAL_NUM_DATARAM 2 /* number of core data RAMs */ +#define XCHAL_NUM_URAM 0 /* number of core unified RAMs*/ +#define XCHAL_NUM_XLMI 1 /* number of core XLMI ports */ + +/* Instruction ROM 0: */ +#define XCHAL_INSTROM0_VADDR 0x40800000 /* virtual address */ +#define XCHAL_INSTROM0_PADDR 0x40800000 /* physical address */ +#define XCHAL_INSTROM0_SIZE 4194304 /* size in bytes */ +#define XCHAL_INSTROM0_ECC_PARITY 0 /* ECC/parity type, 0=none */ + +/* Instruction RAM 0: */ +#define XCHAL_INSTRAM0_VADDR 0x40000000 /* virtual address */ +#define XCHAL_INSTRAM0_PADDR 0x40000000 /* physical address */ +#define XCHAL_INSTRAM0_SIZE 4194304 /* size in bytes */ +#define XCHAL_INSTRAM0_ECC_PARITY 0 /* ECC/parity type, 0=none */ +#define XCHAL_HAVE_INSTRAM0 1 +#define XCHAL_INSTRAM0_HAVE_IDMA 0 /* idma supported by this local memory */ + +/* Instruction RAM 1: */ +#define XCHAL_INSTRAM1_VADDR 0x40400000 /* virtual address */ +#define XCHAL_INSTRAM1_PADDR 0x40400000 /* physical address */ +#define XCHAL_INSTRAM1_SIZE 4194304 /* size in bytes */ +#define XCHAL_INSTRAM1_ECC_PARITY 0 /* ECC/parity type, 0=none */ +#define XCHAL_HAVE_INSTRAM1 1 +#define XCHAL_INSTRAM1_HAVE_IDMA 0 /* idma supported by this local memory */ + +/* Data ROM 0: */ +#define XCHAL_DATAROM0_VADDR 0x3F400000 /* virtual address */ +#define XCHAL_DATAROM0_PADDR 0x3F400000 /* physical address */ +#define XCHAL_DATAROM0_SIZE 4194304 /* size in bytes */ +#define XCHAL_DATAROM0_ECC_PARITY 0 /* ECC/parity type, 0=none */ +#define XCHAL_DATAROM0_BANKS 1 /* number of banks */ + +/* Data RAM 0: */ +#define XCHAL_DATARAM0_VADDR 0x3FF80000 /* virtual address */ +#define XCHAL_DATARAM0_PADDR 0x3FF80000 /* physical address */ +#define XCHAL_DATARAM0_SIZE 524288 /* size in bytes */ +#define XCHAL_DATARAM0_ECC_PARITY 0 /* ECC/parity type, 0=none */ +#define XCHAL_DATARAM0_BANKS 1 /* number of banks */ +#define XCHAL_HAVE_DATARAM0 1 +#define XCHAL_DATARAM0_HAVE_IDMA 0 /* idma supported by this local memory */ + +/* Data RAM 1: */ +#define XCHAL_DATARAM1_VADDR 0x3F800000 /* virtual address */ +#define XCHAL_DATARAM1_PADDR 0x3F800000 /* physical address */ +#define XCHAL_DATARAM1_SIZE 4194304 /* size in bytes */ +#define XCHAL_DATARAM1_ECC_PARITY 0 /* ECC/parity type, 0=none */ +#define XCHAL_DATARAM1_BANKS 1 /* number of banks */ +#define XCHAL_HAVE_DATARAM1 1 +#define XCHAL_DATARAM1_HAVE_IDMA 0 /* idma supported by this local memory */ + +/* XLMI Port 0: */ +#define XCHAL_XLMI0_VADDR 0x3FE00000 /* virtual address */ +#define XCHAL_XLMI0_PADDR 0x3FE00000 /* physical address */ +#define XCHAL_XLMI0_SIZE 1048576 /* size in bytes */ +#define XCHAL_XLMI0_ECC_PARITY 0 /* ECC/parity type, 0=none */ + +#define XCHAL_HAVE_IDMA 0 +#define XCHAL_HAVE_IDMA_TRANSPOSE 0 + +#define XCHAL_HAVE_IMEM_LOADSTORE 1 /* can load/store to IROM/IRAM*/ + + +/*---------------------------------------------------------------------- + INTERRUPTS and TIMERS + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_INTERRUPTS 1 /* interrupt option */ +#define XCHAL_HAVE_HIGHPRI_INTERRUPTS 1 /* med/high-pri. interrupts */ +#define XCHAL_HAVE_NMI 1 /* non-maskable interrupt */ +#define XCHAL_HAVE_CCOUNT 1 /* CCOUNT reg. (timer option) */ +#define XCHAL_NUM_TIMERS 3 /* number of CCOMPAREn regs */ +#define XCHAL_NUM_INTERRUPTS 32 /* number of interrupts */ +#define XCHAL_NUM_INTERRUPTS_LOG2 5 /* ceil(log2(NUM_INTERRUPTS)) */ +#define XCHAL_NUM_EXTINTERRUPTS 26 /* num of external interrupts */ +#define XCHAL_NUM_INTLEVELS 6 /* number of interrupt levels + (not including level zero) */ +#define XCHAL_EXCM_LEVEL 3 /* level masked by PS.EXCM */ + /* (always 1 in XEA1; levels 2 .. EXCM_LEVEL are "medium priority") */ + +/* Masks of interrupts at each interrupt level: */ +#define XCHAL_INTLEVEL1_MASK 0x000637FF +#define XCHAL_INTLEVEL2_MASK 0x00380000 +#define XCHAL_INTLEVEL3_MASK 0x28C08800 +#define XCHAL_INTLEVEL4_MASK 0x53000000 +#define XCHAL_INTLEVEL5_MASK 0x84010000 +#define XCHAL_INTLEVEL6_MASK 0x00000000 +#define XCHAL_INTLEVEL7_MASK 0x00004000 + +/* Masks of interrupts at each range 1..n of interrupt levels: */ +#define XCHAL_INTLEVEL1_ANDBELOW_MASK 0x000637FF +#define XCHAL_INTLEVEL2_ANDBELOW_MASK 0x003E37FF +#define XCHAL_INTLEVEL3_ANDBELOW_MASK 0x28FEBFFF +#define XCHAL_INTLEVEL4_ANDBELOW_MASK 0x7BFEBFFF +#define XCHAL_INTLEVEL5_ANDBELOW_MASK 0xFFFFBFFF +#define XCHAL_INTLEVEL6_ANDBELOW_MASK 0xFFFFBFFF +#define XCHAL_INTLEVEL7_ANDBELOW_MASK 0xFFFFFFFF + +/* Level of each interrupt: */ +#define XCHAL_INT0_LEVEL 1 +#define XCHAL_INT1_LEVEL 1 +#define XCHAL_INT2_LEVEL 1 +#define XCHAL_INT3_LEVEL 1 +#define XCHAL_INT4_LEVEL 1 +#define XCHAL_INT5_LEVEL 1 +#define XCHAL_INT6_LEVEL 1 +#define XCHAL_INT7_LEVEL 1 +#define XCHAL_INT8_LEVEL 1 +#define XCHAL_INT9_LEVEL 1 +#define XCHAL_INT10_LEVEL 1 +#define XCHAL_INT11_LEVEL 3 +#define XCHAL_INT12_LEVEL 1 +#define XCHAL_INT13_LEVEL 1 +#define XCHAL_INT14_LEVEL 7 +#define XCHAL_INT15_LEVEL 3 +#define XCHAL_INT16_LEVEL 5 +#define XCHAL_INT17_LEVEL 1 +#define XCHAL_INT18_LEVEL 1 +#define XCHAL_INT19_LEVEL 2 +#define XCHAL_INT20_LEVEL 2 +#define XCHAL_INT21_LEVEL 2 +#define XCHAL_INT22_LEVEL 3 +#define XCHAL_INT23_LEVEL 3 +#define XCHAL_INT24_LEVEL 4 +#define XCHAL_INT25_LEVEL 4 +#define XCHAL_INT26_LEVEL 5 +#define XCHAL_INT27_LEVEL 3 +#define XCHAL_INT28_LEVEL 4 +#define XCHAL_INT29_LEVEL 3 +#define XCHAL_INT30_LEVEL 4 +#define XCHAL_INT31_LEVEL 5 +#define XCHAL_DEBUGLEVEL 6 /* debug interrupt level */ +#define XCHAL_HAVE_DEBUG_EXTERN_INT 1 /* OCD external db interrupt */ +#define XCHAL_NMILEVEL 7 /* NMI "level" (for use with + EXCSAVE/EPS/EPC_n, RFI n) */ + +/* Type of each interrupt: */ +#define XCHAL_INT0_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT1_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT2_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT3_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT4_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT5_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT6_TYPE XTHAL_INTTYPE_TIMER +#define XCHAL_INT7_TYPE XTHAL_INTTYPE_SOFTWARE +#define XCHAL_INT8_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT9_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT10_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT11_TYPE XTHAL_INTTYPE_PROFILING +#define XCHAL_INT12_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT13_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT14_TYPE XTHAL_INTTYPE_NMI +#define XCHAL_INT15_TYPE XTHAL_INTTYPE_TIMER +#define XCHAL_INT16_TYPE XTHAL_INTTYPE_TIMER +#define XCHAL_INT17_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT18_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT19_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT20_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT21_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT22_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT23_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT24_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT25_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT26_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT27_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT28_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT29_TYPE XTHAL_INTTYPE_SOFTWARE +#define XCHAL_INT30_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT31_TYPE XTHAL_INTTYPE_EXTERN_LEVEL + +/* Masks of interrupts for each type of interrupt: */ +#define XCHAL_INTTYPE_MASK_UNCONFIGURED 0x00000000 +#define XCHAL_INTTYPE_MASK_SOFTWARE 0x20000080 +#define XCHAL_INTTYPE_MASK_EXTERN_EDGE 0x50400400 +#define XCHAL_INTTYPE_MASK_EXTERN_LEVEL 0x8FBE333F +#define XCHAL_INTTYPE_MASK_TIMER 0x00018040 +#define XCHAL_INTTYPE_MASK_NMI 0x00004000 +#define XCHAL_INTTYPE_MASK_WRITE_ERROR 0x00000000 +#define XCHAL_INTTYPE_MASK_PROFILING 0x00000800 +#define XCHAL_INTTYPE_MASK_IDMA_DONE 0x00000000 +#define XCHAL_INTTYPE_MASK_IDMA_ERR 0x00000000 +#define XCHAL_INTTYPE_MASK_GS_ERR 0x00000000 + +/* Interrupt numbers assigned to specific interrupt sources: */ +#define XCHAL_TIMER0_INTERRUPT 6 /* CCOMPARE0 */ +#define XCHAL_TIMER1_INTERRUPT 15 /* CCOMPARE1 */ +#define XCHAL_TIMER2_INTERRUPT 16 /* CCOMPARE2 */ +#define XCHAL_TIMER3_INTERRUPT XTHAL_TIMER_UNCONFIGURED +#define XCHAL_NMI_INTERRUPT 14 /* non-maskable interrupt */ +#define XCHAL_PROFILING_INTERRUPT 11 + +/* Interrupt numbers for levels at which only one interrupt is configured: */ +#define XCHAL_INTLEVEL7_NUM 14 +/* (There are many interrupts each at level(s) 1, 2, 3, 4, 5.) */ + + +/* + * External interrupt mapping. + * These macros describe how Xtensa processor interrupt numbers + * (as numbered internally, eg. in INTERRUPT and INTENABLE registers) + * map to external BInterrupt pins, for those interrupts + * configured as external (level-triggered, edge-triggered, or NMI). + * See the Xtensa processor databook for more details. + */ + +/* Core interrupt numbers mapped to each EXTERNAL BInterrupt pin number: */ +#define XCHAL_EXTINT0_NUM 0 /* (intlevel 1) */ +#define XCHAL_EXTINT1_NUM 1 /* (intlevel 1) */ +#define XCHAL_EXTINT2_NUM 2 /* (intlevel 1) */ +#define XCHAL_EXTINT3_NUM 3 /* (intlevel 1) */ +#define XCHAL_EXTINT4_NUM 4 /* (intlevel 1) */ +#define XCHAL_EXTINT5_NUM 5 /* (intlevel 1) */ +#define XCHAL_EXTINT6_NUM 8 /* (intlevel 1) */ +#define XCHAL_EXTINT7_NUM 9 /* (intlevel 1) */ +#define XCHAL_EXTINT8_NUM 10 /* (intlevel 1) */ +#define XCHAL_EXTINT9_NUM 12 /* (intlevel 1) */ +#define XCHAL_EXTINT10_NUM 13 /* (intlevel 1) */ +#define XCHAL_EXTINT11_NUM 14 /* (intlevel 7) */ +#define XCHAL_EXTINT12_NUM 17 /* (intlevel 1) */ +#define XCHAL_EXTINT13_NUM 18 /* (intlevel 1) */ +#define XCHAL_EXTINT14_NUM 19 /* (intlevel 2) */ +#define XCHAL_EXTINT15_NUM 20 /* (intlevel 2) */ +#define XCHAL_EXTINT16_NUM 21 /* (intlevel 2) */ +#define XCHAL_EXTINT17_NUM 22 /* (intlevel 3) */ +#define XCHAL_EXTINT18_NUM 23 /* (intlevel 3) */ +#define XCHAL_EXTINT19_NUM 24 /* (intlevel 4) */ +#define XCHAL_EXTINT20_NUM 25 /* (intlevel 4) */ +#define XCHAL_EXTINT21_NUM 26 /* (intlevel 5) */ +#define XCHAL_EXTINT22_NUM 27 /* (intlevel 3) */ +#define XCHAL_EXTINT23_NUM 28 /* (intlevel 4) */ +#define XCHAL_EXTINT24_NUM 30 /* (intlevel 4) */ +#define XCHAL_EXTINT25_NUM 31 /* (intlevel 5) */ +/* EXTERNAL BInterrupt pin numbers mapped to each core interrupt number: */ +#define XCHAL_INT0_EXTNUM 0 /* (intlevel 1) */ +#define XCHAL_INT1_EXTNUM 1 /* (intlevel 1) */ +#define XCHAL_INT2_EXTNUM 2 /* (intlevel 1) */ +#define XCHAL_INT3_EXTNUM 3 /* (intlevel 1) */ +#define XCHAL_INT4_EXTNUM 4 /* (intlevel 1) */ +#define XCHAL_INT5_EXTNUM 5 /* (intlevel 1) */ +#define XCHAL_INT8_EXTNUM 6 /* (intlevel 1) */ +#define XCHAL_INT9_EXTNUM 7 /* (intlevel 1) */ +#define XCHAL_INT10_EXTNUM 8 /* (intlevel 1) */ +#define XCHAL_INT12_EXTNUM 9 /* (intlevel 1) */ +#define XCHAL_INT13_EXTNUM 10 /* (intlevel 1) */ +#define XCHAL_INT14_EXTNUM 11 /* (intlevel 7) */ +#define XCHAL_INT17_EXTNUM 12 /* (intlevel 1) */ +#define XCHAL_INT18_EXTNUM 13 /* (intlevel 1) */ +#define XCHAL_INT19_EXTNUM 14 /* (intlevel 2) */ +#define XCHAL_INT20_EXTNUM 15 /* (intlevel 2) */ +#define XCHAL_INT21_EXTNUM 16 /* (intlevel 2) */ +#define XCHAL_INT22_EXTNUM 17 /* (intlevel 3) */ +#define XCHAL_INT23_EXTNUM 18 /* (intlevel 3) */ +#define XCHAL_INT24_EXTNUM 19 /* (intlevel 4) */ +#define XCHAL_INT25_EXTNUM 20 /* (intlevel 4) */ +#define XCHAL_INT26_EXTNUM 21 /* (intlevel 5) */ +#define XCHAL_INT27_EXTNUM 22 /* (intlevel 3) */ +#define XCHAL_INT28_EXTNUM 23 /* (intlevel 4) */ +#define XCHAL_INT30_EXTNUM 24 /* (intlevel 4) */ +#define XCHAL_INT31_EXTNUM 25 /* (intlevel 5) */ + + +/*---------------------------------------------------------------------- + EXCEPTIONS and VECTORS + ----------------------------------------------------------------------*/ + +#define XCHAL_XEA_VERSION 2 /* Xtensa Exception Architecture + number: 1 == XEA1 (old) + 2 == XEA2 (new) + 0 == XEAX (extern) or TX */ +#define XCHAL_HAVE_XEA1 0 /* Exception Architecture 1 */ +#define XCHAL_HAVE_XEA2 1 /* Exception Architecture 2 */ +#define XCHAL_HAVE_XEAX 0 /* External Exception Arch. */ +#define XCHAL_HAVE_EXCEPTIONS 1 /* exception option */ +#define XCHAL_HAVE_HALT 0 /* halt architecture option */ +#define XCHAL_HAVE_BOOTLOADER 0 /* boot loader (for TX) */ +#define XCHAL_HAVE_MEM_ECC_PARITY 0 /* local memory ECC/parity */ +#define XCHAL_HAVE_VECTOR_SELECT 1 /* relocatable vectors */ +#define XCHAL_HAVE_VECBASE 1 /* relocatable vectors */ +#define XCHAL_VECBASE_RESET_VADDR 0x40000000 /* VECBASE reset value */ +#define XCHAL_VECBASE_RESET_PADDR 0x40000000 +#define XCHAL_RESET_VECBASE_OVERLAP 0 + +#define XCHAL_RESET_VECTOR0_VADDR 0x50000000 +#define XCHAL_RESET_VECTOR0_PADDR 0x50000000 +#define XCHAL_RESET_VECTOR1_VADDR 0x40000400 +#define XCHAL_RESET_VECTOR1_PADDR 0x40000400 +#define XCHAL_RESET_VECTOR_VADDR 0x40000400 +#define XCHAL_RESET_VECTOR_PADDR 0x40000400 +#define XCHAL_USER_VECOFS 0x00000340 +#define XCHAL_USER_VECTOR_VADDR 0x40000340 +#define XCHAL_USER_VECTOR_PADDR 0x40000340 +#define XCHAL_KERNEL_VECOFS 0x00000300 +#define XCHAL_KERNEL_VECTOR_VADDR 0x40000300 +#define XCHAL_KERNEL_VECTOR_PADDR 0x40000300 +#define XCHAL_DOUBLEEXC_VECOFS 0x000003C0 +#define XCHAL_DOUBLEEXC_VECTOR_VADDR 0x400003C0 +#define XCHAL_DOUBLEEXC_VECTOR_PADDR 0x400003C0 +#define XCHAL_WINDOW_OF4_VECOFS 0x00000000 +#define XCHAL_WINDOW_UF4_VECOFS 0x00000040 +#define XCHAL_WINDOW_OF8_VECOFS 0x00000080 +#define XCHAL_WINDOW_UF8_VECOFS 0x000000C0 +#define XCHAL_WINDOW_OF12_VECOFS 0x00000100 +#define XCHAL_WINDOW_UF12_VECOFS 0x00000140 +#define XCHAL_WINDOW_VECTORS_VADDR 0x40000000 +#define XCHAL_WINDOW_VECTORS_PADDR 0x40000000 +#define XCHAL_INTLEVEL2_VECOFS 0x00000180 +#define XCHAL_INTLEVEL2_VECTOR_VADDR 0x40000180 +#define XCHAL_INTLEVEL2_VECTOR_PADDR 0x40000180 +#define XCHAL_INTLEVEL3_VECOFS 0x000001C0 +#define XCHAL_INTLEVEL3_VECTOR_VADDR 0x400001C0 +#define XCHAL_INTLEVEL3_VECTOR_PADDR 0x400001C0 +#define XCHAL_INTLEVEL4_VECOFS 0x00000200 +#define XCHAL_INTLEVEL4_VECTOR_VADDR 0x40000200 +#define XCHAL_INTLEVEL4_VECTOR_PADDR 0x40000200 +#define XCHAL_INTLEVEL5_VECOFS 0x00000240 +#define XCHAL_INTLEVEL5_VECTOR_VADDR 0x40000240 +#define XCHAL_INTLEVEL5_VECTOR_PADDR 0x40000240 +#define XCHAL_INTLEVEL6_VECOFS 0x00000280 +#define XCHAL_INTLEVEL6_VECTOR_VADDR 0x40000280 +#define XCHAL_INTLEVEL6_VECTOR_PADDR 0x40000280 +#define XCHAL_DEBUG_VECOFS XCHAL_INTLEVEL6_VECOFS +#define XCHAL_DEBUG_VECTOR_VADDR XCHAL_INTLEVEL6_VECTOR_VADDR +#define XCHAL_DEBUG_VECTOR_PADDR XCHAL_INTLEVEL6_VECTOR_PADDR +#define XCHAL_NMI_VECOFS 0x000002C0 +#define XCHAL_NMI_VECTOR_VADDR 0x400002C0 +#define XCHAL_NMI_VECTOR_PADDR 0x400002C0 +#define XCHAL_INTLEVEL7_VECOFS XCHAL_NMI_VECOFS +#define XCHAL_INTLEVEL7_VECTOR_VADDR XCHAL_NMI_VECTOR_VADDR +#define XCHAL_INTLEVEL7_VECTOR_PADDR XCHAL_NMI_VECTOR_PADDR + + +/*---------------------------------------------------------------------- + DEBUG MODULE + ----------------------------------------------------------------------*/ + +/* Misc */ +#define XCHAL_HAVE_DEBUG_ERI 1 /* ERI to debug module */ +#define XCHAL_HAVE_DEBUG_APB 0 /* APB to debug module */ +#define XCHAL_HAVE_DEBUG_JTAG 1 /* JTAG to debug module */ + +/* On-Chip Debug (OCD) */ +#define XCHAL_HAVE_OCD 1 /* OnChipDebug option */ +#define XCHAL_NUM_IBREAK 2 /* number of IBREAKn regs */ +#define XCHAL_NUM_DBREAK 2 /* number of DBREAKn regs */ +#define XCHAL_HAVE_OCD_DIR_ARRAY 0 /* faster OCD option (to LX4) */ +#define XCHAL_HAVE_OCD_LS32DDR 1 /* L32DDR/S32DDR (faster OCD) */ + +/* TRAX (in core) */ +#define XCHAL_HAVE_TRAX 1 /* TRAX in debug module */ +#define XCHAL_TRAX_MEM_SIZE 16384 /* TRAX memory size in bytes */ +#define XCHAL_TRAX_MEM_SHAREABLE 1 /* start/end regs; ready sig. */ +#define XCHAL_TRAX_ATB_WIDTH 0 /* ATB width (bits), 0=no ATB */ +#define XCHAL_TRAX_TIME_WIDTH 0 /* timestamp bitwidth, 0=none */ + +/* Perf counters */ +#define XCHAL_NUM_PERF_COUNTERS 2 /* performance counters */ + + +/*---------------------------------------------------------------------- + MMU + ----------------------------------------------------------------------*/ + +/* See core-matmap.h header file for more details. */ + +#define XCHAL_HAVE_TLBS 1 /* inverse of HAVE_CACHEATTR */ +#define XCHAL_HAVE_SPANNING_WAY 1 /* one way maps I+D 4GB vaddr */ +#define XCHAL_SPANNING_WAY 0 /* TLB spanning way number */ +#define XCHAL_HAVE_IDENTITY_MAP 1 /* vaddr == paddr always */ +#define XCHAL_HAVE_CACHEATTR 0 /* CACHEATTR register present */ +#define XCHAL_HAVE_MIMIC_CACHEATTR 1 /* region protection */ +#define XCHAL_HAVE_XLT_CACHEATTR 0 /* region prot. w/translation */ +#define XCHAL_HAVE_PTP_MMU 0 /* full MMU (with page table + [autorefill] and protection) + usable for an MMU-based OS */ + +/* If none of the above last 5 are set, it's a custom TLB configuration. */ + +#define XCHAL_MMU_ASID_BITS 0 /* number of bits in ASIDs */ +#define XCHAL_MMU_RINGS 1 /* number of rings (1..4) */ +#define XCHAL_MMU_RING_BITS 0 /* num of bits in RING field */ + +/*---------------------------------------------------------------------- + MPU + ----------------------------------------------------------------------*/ +#define XCHAL_HAVE_MPU 0 +#define XCHAL_MPU_ENTRIES 0 + +#define XCHAL_MPU_ALIGN_REQ 1 /* MPU requires alignment of entries to background map */ +#define XCHAL_MPU_BACKGROUND_ENTRIES 0 /* number of entries in bg map*/ +#define XCHAL_MPU_BG_CACHEADRDIS 0 /* default CACHEADRDIS for bg */ + +#define XCHAL_MPU_ALIGN_BITS 0 +#define XCHAL_MPU_ALIGN 0 + +#endif /* !XTENSA_HAL_NON_PRIVILEGED_ONLY */ + + +#endif /* _XTENSA_CORE_CONFIGURATION_H */ + diff --git a/components/xtensa/esp32s2beta/include/xtensa/config/core-matmap.h b/components/xtensa/esp32s2beta/include/xtensa/config/core-matmap.h new file mode 100644 index 0000000000..f2c7999887 --- /dev/null +++ b/components/xtensa/esp32s2beta/include/xtensa/config/core-matmap.h @@ -0,0 +1,322 @@ +/* + * xtensa/config/core-matmap.h -- Memory access and translation mapping + * parameters (CHAL) of the Xtensa processor core configuration. + * + * If you are using Xtensa Tools, see (which includes + * this file) for more details. + * + * In the Xtensa processor products released to date, all parameters + * defined in this file are derivable (at least in theory) from + * information contained in the core-isa.h header file. + * In particular, the following core configuration parameters are relevant: + * XCHAL_HAVE_CACHEATTR + * XCHAL_HAVE_MIMIC_CACHEATTR + * XCHAL_HAVE_XLT_CACHEATTR + * XCHAL_HAVE_PTP_MMU + * XCHAL_ITLB_ARF_ENTRIES_LOG2 + * XCHAL_DTLB_ARF_ENTRIES_LOG2 + * XCHAL_DCACHE_IS_WRITEBACK + * XCHAL_ICACHE_SIZE (presence of I-cache) + * XCHAL_DCACHE_SIZE (presence of D-cache) + * XCHAL_HW_VERSION_MAJOR + * XCHAL_HW_VERSION_MINOR + */ + +/* Copyright (c) 1999-2018 Tensilica Inc. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + + +#ifndef XTENSA_CONFIG_CORE_MATMAP_H +#define XTENSA_CONFIG_CORE_MATMAP_H + + +/*---------------------------------------------------------------------- + CACHE (MEMORY ACCESS) ATTRIBUTES + ----------------------------------------------------------------------*/ + + + +/* Cache Attribute encodings -- lists of access modes for each cache attribute: */ +#define XCHAL_FCA_LIST XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_BYPASS XCHAL_SEP \ + XTHAL_FAM_BYPASS XCHAL_SEP \ + XTHAL_FAM_BYPASS XCHAL_SEP \ + XTHAL_FAM_BYPASS XCHAL_SEP \ + XTHAL_FAM_BYPASS XCHAL_SEP \ + XTHAL_FAM_BYPASS XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_EXCEPTION +#define XCHAL_LCA_LIST XTHAL_LAM_BYPASSG XCHAL_SEP \ + XTHAL_LAM_BYPASSG XCHAL_SEP \ + XTHAL_LAM_BYPASSG XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_BYPASSG XCHAL_SEP \ + XTHAL_LAM_BYPASSG XCHAL_SEP \ + XTHAL_LAM_BYPASSG XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_EXCEPTION +#define XCHAL_SCA_LIST XTHAL_SAM_BYPASS XCHAL_SEP \ + XTHAL_SAM_BYPASS XCHAL_SEP \ + XTHAL_SAM_BYPASS XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_BYPASS XCHAL_SEP \ + XTHAL_SAM_BYPASS XCHAL_SEP \ + XTHAL_SAM_BYPASS XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION + +#define XCHAL_CA_R (0xC0 | 0x40000000) +#define XCHAL_CA_RX (0xD0 | 0x40000000) +#define XCHAL_CA_RW (0xE0 | 0x40000000) +#define XCHAL_CA_RWX (0xF0 | 0x40000000) + +/* + * Specific encoded cache attribute values of general interest. + * If a specific cache mode is not available, the closest available + * one is returned instead (eg. writethru instead of writeback, + * bypass instead of writethru). + */ +#define XCHAL_CA_BYPASS 2 /* cache disabled (bypassed) mode */ +#define XCHAL_CA_BYPASSBUF 6 /* cache disabled (bypassed) bufferable mode */ +#define XCHAL_CA_WRITETHRU 1 /* cache enabled (write-through) mode */ +#define XCHAL_CA_WRITEBACK 2 /* cache enabled (write-back) mode */ +#define XCHAL_HAVE_CA_WRITEBACK_NOALLOC 0 /* write-back no-allocate availability */ +#define XCHAL_CA_WRITEBACK_NOALLOC 2 /* cache enabled (write-back no-allocate) mode */ +#define XCHAL_CA_BYPASS_RW 0 /* cache disabled (bypassed) mode (no exec) */ +#define XCHAL_CA_WRITETHRU_RW 0 /* cache enabled (write-through) mode (no exec) (FALLBACK) */ +#define XCHAL_CA_WRITEBACK_RW 0 /* cache enabled (write-back) mode (no exec) */ +#define XCHAL_CA_WRITEBACK_NOALLOC_RW 0 /* cache enabled (write-back no-allocate) mode (no exec) */ +#define XCHAL_CA_ILLEGAL 15 /* no access allowed (all cause exceptions) mode */ +#define XCHAL_CA_ISOLATE 0 /* cache isolate (accesses go to cache not memory) mode */ + +/*---------------------------------------------------------------------- + MMU + ----------------------------------------------------------------------*/ + +/* + * General notes on MMU parameters. + * + * Terminology: + * ASID = address-space ID (acts as an "extension" of virtual addresses) + * VPN = virtual page number + * PPN = physical page number + * CA = encoded cache attribute (access modes) + * TLB = translation look-aside buffer (term is stretched somewhat here) + * I = instruction (fetch accesses) + * D = data (load and store accesses) + * way = each TLB (ITLB and DTLB) consists of a number of "ways" + * that simultaneously match the virtual address of an access; + * a TLB successfully translates a virtual address if exactly + * one way matches the vaddr; if none match, it is a miss; + * if multiple match, one gets a "multihit" exception; + * each way can be independently configured in terms of number of + * entries, page sizes, which fields are writable or constant, etc. + * set = group of contiguous ways with exactly identical parameters + * ARF = auto-refill; hardware services a 1st-level miss by loading a PTE + * from the page table and storing it in one of the auto-refill ways; + * if this PTE load also misses, a miss exception is posted for s/w. + * min-wired = a "min-wired" way can be used to map a single (minimum-sized) + * page arbitrarily under program control; it has a single entry, + * is non-auto-refill (some other way(s) must be auto-refill), + * all its fields (VPN, PPN, ASID, CA) are all writable, and it + * supports the XCHAL_MMU_MIN_PTE_PAGE_SIZE page size (a current + * restriction is that this be the only page size it supports). + * + * TLB way entries are virtually indexed. + * TLB ways that support multiple page sizes: + * - must have all writable VPN and PPN fields; + * - can only use one page size at any given time (eg. setup at startup), + * selected by the respective ITLBCFG or DTLBCFG special register, + * whose bits n*4+3 .. n*4 index the list of page sizes for way n + * (XCHAL_xTLB_SETm_PAGESZ_LOG2_LIST for set m corresponding to way n); + * this list may be sparse for auto-refill ways because auto-refill + * ways have independent lists of supported page sizes sharing a + * common encoding with PTE entries; the encoding is the index into + * this list; unsupported sizes for a given way are zero in the list; + * selecting unsupported sizes results in undefine hardware behaviour; + * - is only possible for ways 0 thru 7 (due to ITLBCFG/DTLBCFG definition). + */ + +#define XCHAL_MMU_ASID_INVALID 0 /* ASID value indicating invalid address space */ +#define XCHAL_MMU_ASID_KERNEL 0 /* ASID value indicating kernel (ring 0) address space */ +#define XCHAL_MMU_SR_BITS 0 /* number of size-restriction bits supported */ +#define XCHAL_MMU_CA_BITS 4 /* number of bits needed to hold cache attribute encoding */ +#define XCHAL_MMU_MAX_PTE_PAGE_SIZE 29 /* max page size in a PTE structure (log2) */ +#define XCHAL_MMU_MIN_PTE_PAGE_SIZE 29 /* min page size in a PTE structure (log2) */ + + +/*** Instruction TLB: ***/ + +#define XCHAL_ITLB_WAY_BITS 0 /* number of bits holding the ways */ +#define XCHAL_ITLB_WAYS 1 /* number of ways (n-way set-associative TLB) */ +#define XCHAL_ITLB_ARF_WAYS 0 /* number of auto-refill ways */ +#define XCHAL_ITLB_SETS 1 /* number of sets (groups of ways with identical settings) */ + +/* Way set to which each way belongs: */ +#define XCHAL_ITLB_WAY0_SET 0 + +/* Ways sets that are used by hardware auto-refill (ARF): */ +#define XCHAL_ITLB_ARF_SETS 0 /* number of auto-refill sets */ + +/* Way sets that are "min-wired" (see terminology comment above): */ +#define XCHAL_ITLB_MINWIRED_SETS 0 /* number of "min-wired" sets */ + + +/* ITLB way set 0 (group of ways 0 thru 0): */ +#define XCHAL_ITLB_SET0_WAY 0 /* index of first way in this way set */ +#define XCHAL_ITLB_SET0_WAYS 1 /* number of (contiguous) ways in this way set */ +#define XCHAL_ITLB_SET0_ENTRIES_LOG2 3 /* log2(number of entries in this way) */ +#define XCHAL_ITLB_SET0_ENTRIES 8 /* number of entries in this way (always a power of 2) */ +#define XCHAL_ITLB_SET0_ARF 0 /* 1=autorefill by h/w, 0=non-autorefill (wired/constant/static) */ +#define XCHAL_ITLB_SET0_PAGESIZES 1 /* number of supported page sizes in this way */ +#define XCHAL_ITLB_SET0_PAGESZ_BITS 0 /* number of bits to encode the page size */ +#define XCHAL_ITLB_SET0_PAGESZ_LOG2_MIN 29 /* log2(minimum supported page size) */ +#define XCHAL_ITLB_SET0_PAGESZ_LOG2_MAX 29 /* log2(maximum supported page size) */ +#define XCHAL_ITLB_SET0_PAGESZ_LOG2_LIST 29 /* list of log2(page size)s, separated by XCHAL_SEP; + 2^PAGESZ_BITS entries in list, unsupported entries are zero */ +#define XCHAL_ITLB_SET0_ASID_CONSTMASK 0 /* constant ASID bits; 0 if all writable */ +#define XCHAL_ITLB_SET0_VPN_CONSTMASK 0x00000000 /* constant VPN bits, not including entry index bits; 0 if all writable */ +#define XCHAL_ITLB_SET0_PPN_CONSTMASK 0xE0000000 /* constant PPN bits, including entry index bits; 0 if all writable */ +#define XCHAL_ITLB_SET0_CA_CONSTMASK 0 /* constant CA bits; 0 if all writable */ +#define XCHAL_ITLB_SET0_ASID_RESET 0 /* 1 if ASID reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET0_VPN_RESET 0 /* 1 if VPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET0_PPN_RESET 0 /* 1 if PPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET0_CA_RESET 1 /* 1 if CA reset values defined (and all writable); 0 otherwise */ +/* Constant VPN values for each entry of ITLB way set 0 (because VPN_CONSTMASK is non-zero): */ +#define XCHAL_ITLB_SET0_E0_VPN_CONST 0x00000000 +#define XCHAL_ITLB_SET0_E1_VPN_CONST 0x20000000 +#define XCHAL_ITLB_SET0_E2_VPN_CONST 0x40000000 +#define XCHAL_ITLB_SET0_E3_VPN_CONST 0x60000000 +#define XCHAL_ITLB_SET0_E4_VPN_CONST 0x80000000 +#define XCHAL_ITLB_SET0_E5_VPN_CONST 0xA0000000 +#define XCHAL_ITLB_SET0_E6_VPN_CONST 0xC0000000 +#define XCHAL_ITLB_SET0_E7_VPN_CONST 0xE0000000 +/* Constant PPN values for each entry of ITLB way set 0 (because PPN_CONSTMASK is non-zero): */ +#define XCHAL_ITLB_SET0_E0_PPN_CONST 0x00000000 +#define XCHAL_ITLB_SET0_E1_PPN_CONST 0x20000000 +#define XCHAL_ITLB_SET0_E2_PPN_CONST 0x40000000 +#define XCHAL_ITLB_SET0_E3_PPN_CONST 0x60000000 +#define XCHAL_ITLB_SET0_E4_PPN_CONST 0x80000000 +#define XCHAL_ITLB_SET0_E5_PPN_CONST 0xA0000000 +#define XCHAL_ITLB_SET0_E6_PPN_CONST 0xC0000000 +#define XCHAL_ITLB_SET0_E7_PPN_CONST 0xE0000000 +/* Reset CA values for each entry of ITLB way set 0 (because SET0_CA_RESET is non-zero): */ +#define XCHAL_ITLB_SET0_E0_CA_RESET 0x02 +#define XCHAL_ITLB_SET0_E1_CA_RESET 0x02 +#define XCHAL_ITLB_SET0_E2_CA_RESET 0x02 +#define XCHAL_ITLB_SET0_E3_CA_RESET 0x02 +#define XCHAL_ITLB_SET0_E4_CA_RESET 0x02 +#define XCHAL_ITLB_SET0_E5_CA_RESET 0x02 +#define XCHAL_ITLB_SET0_E6_CA_RESET 0x02 +#define XCHAL_ITLB_SET0_E7_CA_RESET 0x02 + + +/*** Data TLB: ***/ + +#define XCHAL_DTLB_WAY_BITS 0 /* number of bits holding the ways */ +#define XCHAL_DTLB_WAYS 1 /* number of ways (n-way set-associative TLB) */ +#define XCHAL_DTLB_ARF_WAYS 0 /* number of auto-refill ways */ +#define XCHAL_DTLB_SETS 1 /* number of sets (groups of ways with identical settings) */ + +/* Way set to which each way belongs: */ +#define XCHAL_DTLB_WAY0_SET 0 + +/* Ways sets that are used by hardware auto-refill (ARF): */ +#define XCHAL_DTLB_ARF_SETS 0 /* number of auto-refill sets */ + +/* Way sets that are "min-wired" (see terminology comment above): */ +#define XCHAL_DTLB_MINWIRED_SETS 0 /* number of "min-wired" sets */ + + +/* DTLB way set 0 (group of ways 0 thru 0): */ +#define XCHAL_DTLB_SET0_WAY 0 /* index of first way in this way set */ +#define XCHAL_DTLB_SET0_WAYS 1 /* number of (contiguous) ways in this way set */ +#define XCHAL_DTLB_SET0_ENTRIES_LOG2 3 /* log2(number of entries in this way) */ +#define XCHAL_DTLB_SET0_ENTRIES 8 /* number of entries in this way (always a power of 2) */ +#define XCHAL_DTLB_SET0_ARF 0 /* 1=autorefill by h/w, 0=non-autorefill (wired/constant/static) */ +#define XCHAL_DTLB_SET0_PAGESIZES 1 /* number of supported page sizes in this way */ +#define XCHAL_DTLB_SET0_PAGESZ_BITS 0 /* number of bits to encode the page size */ +#define XCHAL_DTLB_SET0_PAGESZ_LOG2_MIN 29 /* log2(minimum supported page size) */ +#define XCHAL_DTLB_SET0_PAGESZ_LOG2_MAX 29 /* log2(maximum supported page size) */ +#define XCHAL_DTLB_SET0_PAGESZ_LOG2_LIST 29 /* list of log2(page size)s, separated by XCHAL_SEP; + 2^PAGESZ_BITS entries in list, unsupported entries are zero */ +#define XCHAL_DTLB_SET0_ASID_CONSTMASK 0 /* constant ASID bits; 0 if all writable */ +#define XCHAL_DTLB_SET0_VPN_CONSTMASK 0x00000000 /* constant VPN bits, not including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET0_PPN_CONSTMASK 0xE0000000 /* constant PPN bits, including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET0_CA_CONSTMASK 0 /* constant CA bits; 0 if all writable */ +#define XCHAL_DTLB_SET0_ASID_RESET 0 /* 1 if ASID reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET0_VPN_RESET 0 /* 1 if VPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET0_PPN_RESET 0 /* 1 if PPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET0_CA_RESET 1 /* 1 if CA reset values defined (and all writable); 0 otherwise */ +/* Constant VPN values for each entry of DTLB way set 0 (because VPN_CONSTMASK is non-zero): */ +#define XCHAL_DTLB_SET0_E0_VPN_CONST 0x00000000 +#define XCHAL_DTLB_SET0_E1_VPN_CONST 0x20000000 +#define XCHAL_DTLB_SET0_E2_VPN_CONST 0x40000000 +#define XCHAL_DTLB_SET0_E3_VPN_CONST 0x60000000 +#define XCHAL_DTLB_SET0_E4_VPN_CONST 0x80000000 +#define XCHAL_DTLB_SET0_E5_VPN_CONST 0xA0000000 +#define XCHAL_DTLB_SET0_E6_VPN_CONST 0xC0000000 +#define XCHAL_DTLB_SET0_E7_VPN_CONST 0xE0000000 +/* Constant PPN values for each entry of DTLB way set 0 (because PPN_CONSTMASK is non-zero): */ +#define XCHAL_DTLB_SET0_E0_PPN_CONST 0x00000000 +#define XCHAL_DTLB_SET0_E1_PPN_CONST 0x20000000 +#define XCHAL_DTLB_SET0_E2_PPN_CONST 0x40000000 +#define XCHAL_DTLB_SET0_E3_PPN_CONST 0x60000000 +#define XCHAL_DTLB_SET0_E4_PPN_CONST 0x80000000 +#define XCHAL_DTLB_SET0_E5_PPN_CONST 0xA0000000 +#define XCHAL_DTLB_SET0_E6_PPN_CONST 0xC0000000 +#define XCHAL_DTLB_SET0_E7_PPN_CONST 0xE0000000 +/* Reset CA values for each entry of DTLB way set 0 (because SET0_CA_RESET is non-zero): */ +#define XCHAL_DTLB_SET0_E0_CA_RESET 0x02 +#define XCHAL_DTLB_SET0_E1_CA_RESET 0x02 +#define XCHAL_DTLB_SET0_E2_CA_RESET 0x02 +#define XCHAL_DTLB_SET0_E3_CA_RESET 0x02 +#define XCHAL_DTLB_SET0_E4_CA_RESET 0x02 +#define XCHAL_DTLB_SET0_E5_CA_RESET 0x02 +#define XCHAL_DTLB_SET0_E6_CA_RESET 0x02 +#define XCHAL_DTLB_SET0_E7_CA_RESET 0x02 + + + + +#endif /*XTENSA_CONFIG_CORE_MATMAP_H*/ + diff --git a/components/xtensa/esp32s2beta/include/xtensa/config/core.h b/components/xtensa/esp32s2beta/include/xtensa/config/core.h new file mode 100644 index 0000000000..f5bb44faf2 --- /dev/null +++ b/components/xtensa/esp32s2beta/include/xtensa/config/core.h @@ -0,0 +1,1408 @@ +/* + * xtensa/config/core.h -- HAL definitions dependent on CORE configuration + * + * This header file is sometimes referred to as the "compile-time HAL" or CHAL. + * It pulls definitions tailored for a specific Xtensa processor configuration. + * + * Sources for binaries meant to be configuration-independent generally avoid + * including this file (they may use the configuration-specific HAL library). + * It is normal for the HAL library source itself to include this file. + */ + +/* + * Copyright (c) 2005-2015 Cadence Design Systems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifndef XTENSA_CONFIG_CORE_H +#define XTENSA_CONFIG_CORE_H + +/* CONFIGURATION INDEPENDENT DEFINITIONS: */ +#ifdef __XTENSA__ +#include +#include +#else +#include "../hal.h" +#include "../xtensa-versions.h" +#endif + +/* CONFIGURATION SPECIFIC DEFINITIONS: */ +#ifdef __XTENSA__ +#include +#include +#include +#else +#include "core-isa.h" +#include "core-matmap.h" +#include "tie.h" +#endif + +#if defined (_ASMLANGUAGE) || defined (__ASSEMBLER__) +#ifdef __XTENSA__ +#include +#else +#include "tie-asm.h" +#endif +#endif /*_ASMLANGUAGE or __ASSEMBLER__*/ + + +/*---------------------------------------------------------------------- + GENERAL + ----------------------------------------------------------------------*/ + +/* + * Separators for macros that expand into arrays. + * These can be predefined by files that #include this one, + * when different separators are required. + */ +/* Element separator for macros that expand into 1-dimensional arrays: */ +#ifndef XCHAL_SEP +#define XCHAL_SEP , +#endif +/* Array separator for macros that expand into 2-dimensional arrays: */ +#ifndef XCHAL_SEP2 +#define XCHAL_SEP2 },{ +#endif + + +/*---------------------------------------------------------------------- + ERRATA + ----------------------------------------------------------------------*/ + +/* + * Erratum T1020.H13, T1030.H7, T1040.H10, T1050.H4 (fixed in T1040.3 and T1050.1; + * relevant only in XEA1, kernel-vector mode, level-one interrupts and overflows enabled): + */ +#define XCHAL_MAYHAVE_ERRATUM_XEA1KWIN (XCHAL_HAVE_XEA1 && \ + (XCHAL_HW_RELEASE_AT_OR_BELOW(1040,2) != 0 \ + || XCHAL_HW_RELEASE_AT(1050,0))) +/* + * Erratum 453 present in RE-2013.2 up to RF-2014.0, fixed in RF-2014.1. + * Applies to specific set of configuration options. + * Part of the workaround is to add ISYNC at certain points in the code. + * The workaround gated by this macro can be disabled if not needed, e.g. if + * zero-overhead loop buffer will be disabled, by defining _NO_ERRATUM_453. + */ +#if ( XCHAL_HW_MAX_VERSION >= XTENSA_HWVERSION_RE_2013_2 && \ + XCHAL_HW_MIN_VERSION <= XTENSA_HWVERSION_RF_2014_0 && \ + XCHAL_ICACHE_SIZE != 0 && XCHAL_HAVE_PIF /*covers also AXI/AHB*/ && \ + XCHAL_HAVE_LOOPS && XCHAL_LOOP_BUFFER_SIZE != 0 && \ + XCHAL_CLOCK_GATING_GLOBAL && !defined(_NO_ERRATUM_453) ) +#define XCHAL_ERRATUM_453 1 +#else +#define XCHAL_ERRATUM_453 0 +#endif + +/* + * Erratum 497 present in RE-2012.2 up to RG/RF-2015.2 + * Applies to specific set of configuration options. + * Workaround is to add MEMWs after at most 8 cache WB instructions + */ +#if ( ((XCHAL_HW_MAX_VERSION >= XTENSA_HWVERSION_RE_2012_0 && \ + XCHAL_HW_MIN_VERSION <= XTENSA_HWVERSION_RF_2015_2) || \ + (XCHAL_HW_MAX_VERSION >= XTENSA_HWVERSION_RG_2015_0 && \ + XCHAL_HW_MIN_VERSION <= XTENSA_HWVERSION_RG_2015_2) \ + ) && \ + XCHAL_DCACHE_IS_WRITEBACK && \ + XCHAL_HAVE_AXI && \ + XCHAL_HAVE_PIF_WR_RESP && \ + XCHAL_HAVE_PIF_REQ_ATTR && !defined(_NO_ERRATUM_497) \ + ) +#define XCHAL_ERRATUM_497 1 +#else +#define XCHAL_ERRATUM_497 0 +#endif + + +/*---------------------------------------------------------------------- + ISA + ----------------------------------------------------------------------*/ + +#if XCHAL_HAVE_BE +# define XCHAL_HAVE_LE 0 +# define XCHAL_MEMORY_ORDER XTHAL_BIGENDIAN +#else +# define XCHAL_HAVE_LE 1 +# define XCHAL_MEMORY_ORDER XTHAL_LITTLEENDIAN +#endif + + + +/*---------------------------------------------------------------------- + INTERRUPTS + ----------------------------------------------------------------------*/ + +/* Indexing macros: */ +#define _XCHAL_INTLEVEL_MASK(n) XCHAL_INTLEVEL ## n ## _MASK +#define XCHAL_INTLEVEL_MASK(n) _XCHAL_INTLEVEL_MASK(n) /* n = 0 .. 15 */ +#define _XCHAL_INTLEVEL_ANDBELOWMASK(n) XCHAL_INTLEVEL ## n ## _ANDBELOW_MASK +#define XCHAL_INTLEVEL_ANDBELOW_MASK(n) _XCHAL_INTLEVEL_ANDBELOWMASK(n) /* n = 0 .. 15 */ +#define _XCHAL_INTLEVEL_NUM(n) XCHAL_INTLEVEL ## n ## _NUM +#define XCHAL_INTLEVEL_NUM(n) _XCHAL_INTLEVEL_NUM(n) /* n = 0 .. 15 */ +#define _XCHAL_INT_LEVEL(n) XCHAL_INT ## n ## _LEVEL +#define XCHAL_INT_LEVEL(n) _XCHAL_INT_LEVEL(n) /* n = 0 .. 31 */ +#define _XCHAL_INT_TYPE(n) XCHAL_INT ## n ## _TYPE +#define XCHAL_INT_TYPE(n) _XCHAL_INT_TYPE(n) /* n = 0 .. 31 */ +#define _XCHAL_TIMER_INTERRUPT(n) XCHAL_TIMER ## n ## _INTERRUPT +#define XCHAL_TIMER_INTERRUPT(n) _XCHAL_TIMER_INTERRUPT(n) /* n = 0 .. 3 */ + + +#define XCHAL_HAVE_HIGHLEVEL_INTERRUPTS XCHAL_HAVE_HIGHPRI_INTERRUPTS +#define XCHAL_NUM_LOWPRI_LEVELS 1 /* number of low-priority interrupt levels (always 1) */ +#define XCHAL_FIRST_HIGHPRI_LEVEL (XCHAL_NUM_LOWPRI_LEVELS+1) /* level of first high-priority interrupt (always 2) */ +/* Note: 1 <= LOWPRI_LEVELS <= EXCM_LEVEL < DEBUGLEVEL <= NUM_INTLEVELS < NMILEVEL <= 15 */ + +/* These values are constant for existing Xtensa processor implementations: */ +#define XCHAL_INTLEVEL0_MASK 0x00000000 +#define XCHAL_INTLEVEL8_MASK 0x00000000 +#define XCHAL_INTLEVEL9_MASK 0x00000000 +#define XCHAL_INTLEVEL10_MASK 0x00000000 +#define XCHAL_INTLEVEL11_MASK 0x00000000 +#define XCHAL_INTLEVEL12_MASK 0x00000000 +#define XCHAL_INTLEVEL13_MASK 0x00000000 +#define XCHAL_INTLEVEL14_MASK 0x00000000 +#define XCHAL_INTLEVEL15_MASK 0x00000000 + +/* Array of masks of interrupts at each interrupt level: */ +#define XCHAL_INTLEVEL_MASKS XCHAL_INTLEVEL0_MASK \ + XCHAL_SEP XCHAL_INTLEVEL1_MASK \ + XCHAL_SEP XCHAL_INTLEVEL2_MASK \ + XCHAL_SEP XCHAL_INTLEVEL3_MASK \ + XCHAL_SEP XCHAL_INTLEVEL4_MASK \ + XCHAL_SEP XCHAL_INTLEVEL5_MASK \ + XCHAL_SEP XCHAL_INTLEVEL6_MASK \ + XCHAL_SEP XCHAL_INTLEVEL7_MASK \ + XCHAL_SEP XCHAL_INTLEVEL8_MASK \ + XCHAL_SEP XCHAL_INTLEVEL9_MASK \ + XCHAL_SEP XCHAL_INTLEVEL10_MASK \ + XCHAL_SEP XCHAL_INTLEVEL11_MASK \ + XCHAL_SEP XCHAL_INTLEVEL12_MASK \ + XCHAL_SEP XCHAL_INTLEVEL13_MASK \ + XCHAL_SEP XCHAL_INTLEVEL14_MASK \ + XCHAL_SEP XCHAL_INTLEVEL15_MASK + +/* These values are constant for existing Xtensa processor implementations: */ +#define XCHAL_INTLEVEL0_ANDBELOW_MASK 0x00000000 +#define XCHAL_INTLEVEL8_ANDBELOW_MASK XCHAL_INTLEVEL7_ANDBELOW_MASK +#define XCHAL_INTLEVEL9_ANDBELOW_MASK XCHAL_INTLEVEL7_ANDBELOW_MASK +#define XCHAL_INTLEVEL10_ANDBELOW_MASK XCHAL_INTLEVEL7_ANDBELOW_MASK +#define XCHAL_INTLEVEL11_ANDBELOW_MASK XCHAL_INTLEVEL7_ANDBELOW_MASK +#define XCHAL_INTLEVEL12_ANDBELOW_MASK XCHAL_INTLEVEL7_ANDBELOW_MASK +#define XCHAL_INTLEVEL13_ANDBELOW_MASK XCHAL_INTLEVEL7_ANDBELOW_MASK +#define XCHAL_INTLEVEL14_ANDBELOW_MASK XCHAL_INTLEVEL7_ANDBELOW_MASK +#define XCHAL_INTLEVEL15_ANDBELOW_MASK XCHAL_INTLEVEL7_ANDBELOW_MASK + +/* Mask of all low-priority interrupts: */ +#define XCHAL_LOWPRI_MASK XCHAL_INTLEVEL1_ANDBELOW_MASK + +/* Mask of all interrupts masked by PS.EXCM (or CEXCM): */ +#define XCHAL_EXCM_MASK XCHAL_INTLEVEL_ANDBELOW_MASK(XCHAL_EXCM_LEVEL) + +/* Array of masks of interrupts at each range 1..n of interrupt levels: */ +#define XCHAL_INTLEVEL_ANDBELOW_MASKS XCHAL_INTLEVEL0_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL1_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL2_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL3_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL4_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL5_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL6_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL7_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL8_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL9_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL10_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL11_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL12_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL13_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL14_ANDBELOW_MASK \ + XCHAL_SEP XCHAL_INTLEVEL15_ANDBELOW_MASK + +#if 0 /*XCHAL_HAVE_NMI*/ +/* NMI "interrupt level" (for use with EXCSAVE_n, EPS_n, EPC_n, RFI n): */ +# define XCHAL_NMILEVEL (XCHAL_NUM_INTLEVELS+1) +#endif + +/* Array of levels of each possible interrupt: */ +#define XCHAL_INT_LEVELS XCHAL_INT0_LEVEL \ + XCHAL_SEP XCHAL_INT1_LEVEL \ + XCHAL_SEP XCHAL_INT2_LEVEL \ + XCHAL_SEP XCHAL_INT3_LEVEL \ + XCHAL_SEP XCHAL_INT4_LEVEL \ + XCHAL_SEP XCHAL_INT5_LEVEL \ + XCHAL_SEP XCHAL_INT6_LEVEL \ + XCHAL_SEP XCHAL_INT7_LEVEL \ + XCHAL_SEP XCHAL_INT8_LEVEL \ + XCHAL_SEP XCHAL_INT9_LEVEL \ + XCHAL_SEP XCHAL_INT10_LEVEL \ + XCHAL_SEP XCHAL_INT11_LEVEL \ + XCHAL_SEP XCHAL_INT12_LEVEL \ + XCHAL_SEP XCHAL_INT13_LEVEL \ + XCHAL_SEP XCHAL_INT14_LEVEL \ + XCHAL_SEP XCHAL_INT15_LEVEL \ + XCHAL_SEP XCHAL_INT16_LEVEL \ + XCHAL_SEP XCHAL_INT17_LEVEL \ + XCHAL_SEP XCHAL_INT18_LEVEL \ + XCHAL_SEP XCHAL_INT19_LEVEL \ + XCHAL_SEP XCHAL_INT20_LEVEL \ + XCHAL_SEP XCHAL_INT21_LEVEL \ + XCHAL_SEP XCHAL_INT22_LEVEL \ + XCHAL_SEP XCHAL_INT23_LEVEL \ + XCHAL_SEP XCHAL_INT24_LEVEL \ + XCHAL_SEP XCHAL_INT25_LEVEL \ + XCHAL_SEP XCHAL_INT26_LEVEL \ + XCHAL_SEP XCHAL_INT27_LEVEL \ + XCHAL_SEP XCHAL_INT28_LEVEL \ + XCHAL_SEP XCHAL_INT29_LEVEL \ + XCHAL_SEP XCHAL_INT30_LEVEL \ + XCHAL_SEP XCHAL_INT31_LEVEL + +/* Array of types of each possible interrupt: */ +#define XCHAL_INT_TYPES XCHAL_INT0_TYPE \ + XCHAL_SEP XCHAL_INT1_TYPE \ + XCHAL_SEP XCHAL_INT2_TYPE \ + XCHAL_SEP XCHAL_INT3_TYPE \ + XCHAL_SEP XCHAL_INT4_TYPE \ + XCHAL_SEP XCHAL_INT5_TYPE \ + XCHAL_SEP XCHAL_INT6_TYPE \ + XCHAL_SEP XCHAL_INT7_TYPE \ + XCHAL_SEP XCHAL_INT8_TYPE \ + XCHAL_SEP XCHAL_INT9_TYPE \ + XCHAL_SEP XCHAL_INT10_TYPE \ + XCHAL_SEP XCHAL_INT11_TYPE \ + XCHAL_SEP XCHAL_INT12_TYPE \ + XCHAL_SEP XCHAL_INT13_TYPE \ + XCHAL_SEP XCHAL_INT14_TYPE \ + XCHAL_SEP XCHAL_INT15_TYPE \ + XCHAL_SEP XCHAL_INT16_TYPE \ + XCHAL_SEP XCHAL_INT17_TYPE \ + XCHAL_SEP XCHAL_INT18_TYPE \ + XCHAL_SEP XCHAL_INT19_TYPE \ + XCHAL_SEP XCHAL_INT20_TYPE \ + XCHAL_SEP XCHAL_INT21_TYPE \ + XCHAL_SEP XCHAL_INT22_TYPE \ + XCHAL_SEP XCHAL_INT23_TYPE \ + XCHAL_SEP XCHAL_INT24_TYPE \ + XCHAL_SEP XCHAL_INT25_TYPE \ + XCHAL_SEP XCHAL_INT26_TYPE \ + XCHAL_SEP XCHAL_INT27_TYPE \ + XCHAL_SEP XCHAL_INT28_TYPE \ + XCHAL_SEP XCHAL_INT29_TYPE \ + XCHAL_SEP XCHAL_INT30_TYPE \ + XCHAL_SEP XCHAL_INT31_TYPE + +/* Array of masks of interrupts for each type of interrupt: */ +#define XCHAL_INTTYPE_MASKS XCHAL_INTTYPE_MASK_UNCONFIGURED \ + XCHAL_SEP XCHAL_INTTYPE_MASK_SOFTWARE \ + XCHAL_SEP XCHAL_INTTYPE_MASK_EXTERN_EDGE \ + XCHAL_SEP XCHAL_INTTYPE_MASK_EXTERN_LEVEL \ + XCHAL_SEP XCHAL_INTTYPE_MASK_TIMER \ + XCHAL_SEP XCHAL_INTTYPE_MASK_NMI \ + XCHAL_SEP XCHAL_INTTYPE_MASK_WRITE_ERROR \ + XCHAL_SEP XCHAL_INTTYPE_MASK_IDMA_DONE \ + XCHAL_SEP XCHAL_INTTYPE_MASK_IDMA_ERR \ + XCHAL_SEP XCHAL_INTTYPE_MASK_GS_ERR + +/* Interrupts that can be cleared using the INTCLEAR special register: */ +#define XCHAL_INTCLEARABLE_MASK (XCHAL_INTTYPE_MASK_SOFTWARE+XCHAL_INTTYPE_MASK_EXTERN_EDGE+XCHAL_INTTYPE_MASK_WRITE_ERROR) +/* Interrupts that can be triggered using the INTSET special register: */ +#define XCHAL_INTSETTABLE_MASK XCHAL_INTTYPE_MASK_SOFTWARE + +/* Array of interrupts assigned to each timer (CCOMPARE0 to CCOMPARE3): */ +#define XCHAL_TIMER_INTERRUPTS XCHAL_TIMER0_INTERRUPT \ + XCHAL_SEP XCHAL_TIMER1_INTERRUPT \ + XCHAL_SEP XCHAL_TIMER2_INTERRUPT \ + XCHAL_SEP XCHAL_TIMER3_INTERRUPT + + + +/* For backward compatibility and for the array macros, define macros for + * each unconfigured interrupt number (unfortunately, the value of + * XTHAL_INTTYPE_UNCONFIGURED is not zero): */ +#if XCHAL_NUM_INTERRUPTS == 0 +# define XCHAL_INT0_LEVEL 0 +# define XCHAL_INT0_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 1 +# define XCHAL_INT1_LEVEL 0 +# define XCHAL_INT1_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 2 +# define XCHAL_INT2_LEVEL 0 +# define XCHAL_INT2_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 3 +# define XCHAL_INT3_LEVEL 0 +# define XCHAL_INT3_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 4 +# define XCHAL_INT4_LEVEL 0 +# define XCHAL_INT4_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 5 +# define XCHAL_INT5_LEVEL 0 +# define XCHAL_INT5_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 6 +# define XCHAL_INT6_LEVEL 0 +# define XCHAL_INT6_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 7 +# define XCHAL_INT7_LEVEL 0 +# define XCHAL_INT7_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 8 +# define XCHAL_INT8_LEVEL 0 +# define XCHAL_INT8_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 9 +# define XCHAL_INT9_LEVEL 0 +# define XCHAL_INT9_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 10 +# define XCHAL_INT10_LEVEL 0 +# define XCHAL_INT10_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 11 +# define XCHAL_INT11_LEVEL 0 +# define XCHAL_INT11_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 12 +# define XCHAL_INT12_LEVEL 0 +# define XCHAL_INT12_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 13 +# define XCHAL_INT13_LEVEL 0 +# define XCHAL_INT13_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 14 +# define XCHAL_INT14_LEVEL 0 +# define XCHAL_INT14_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 15 +# define XCHAL_INT15_LEVEL 0 +# define XCHAL_INT15_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 16 +# define XCHAL_INT16_LEVEL 0 +# define XCHAL_INT16_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 17 +# define XCHAL_INT17_LEVEL 0 +# define XCHAL_INT17_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 18 +# define XCHAL_INT18_LEVEL 0 +# define XCHAL_INT18_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 19 +# define XCHAL_INT19_LEVEL 0 +# define XCHAL_INT19_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 20 +# define XCHAL_INT20_LEVEL 0 +# define XCHAL_INT20_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 21 +# define XCHAL_INT21_LEVEL 0 +# define XCHAL_INT21_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 22 +# define XCHAL_INT22_LEVEL 0 +# define XCHAL_INT22_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 23 +# define XCHAL_INT23_LEVEL 0 +# define XCHAL_INT23_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 24 +# define XCHAL_INT24_LEVEL 0 +# define XCHAL_INT24_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 25 +# define XCHAL_INT25_LEVEL 0 +# define XCHAL_INT25_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 26 +# define XCHAL_INT26_LEVEL 0 +# define XCHAL_INT26_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 27 +# define XCHAL_INT27_LEVEL 0 +# define XCHAL_INT27_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 28 +# define XCHAL_INT28_LEVEL 0 +# define XCHAL_INT28_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 29 +# define XCHAL_INT29_LEVEL 0 +# define XCHAL_INT29_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 30 +# define XCHAL_INT30_LEVEL 0 +# define XCHAL_INT30_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif +#if XCHAL_NUM_INTERRUPTS <= 31 +# define XCHAL_INT31_LEVEL 0 +# define XCHAL_INT31_TYPE XTHAL_INTTYPE_UNCONFIGURED +#endif + + +/* + * Masks and levels corresponding to each *external* interrupt. + */ + +#define XCHAL_EXTINT0_MASK (1 << XCHAL_EXTINT0_NUM) +#define XCHAL_EXTINT0_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT0_NUM) +#define XCHAL_EXTINT1_MASK (1 << XCHAL_EXTINT1_NUM) +#define XCHAL_EXTINT1_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT1_NUM) +#define XCHAL_EXTINT2_MASK (1 << XCHAL_EXTINT2_NUM) +#define XCHAL_EXTINT2_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT2_NUM) +#define XCHAL_EXTINT3_MASK (1 << XCHAL_EXTINT3_NUM) +#define XCHAL_EXTINT3_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT3_NUM) +#define XCHAL_EXTINT4_MASK (1 << XCHAL_EXTINT4_NUM) +#define XCHAL_EXTINT4_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT4_NUM) +#define XCHAL_EXTINT5_MASK (1 << XCHAL_EXTINT5_NUM) +#define XCHAL_EXTINT5_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT5_NUM) +#define XCHAL_EXTINT6_MASK (1 << XCHAL_EXTINT6_NUM) +#define XCHAL_EXTINT6_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT6_NUM) +#define XCHAL_EXTINT7_MASK (1 << XCHAL_EXTINT7_NUM) +#define XCHAL_EXTINT7_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT7_NUM) +#define XCHAL_EXTINT8_MASK (1 << XCHAL_EXTINT8_NUM) +#define XCHAL_EXTINT8_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT8_NUM) +#define XCHAL_EXTINT9_MASK (1 << XCHAL_EXTINT9_NUM) +#define XCHAL_EXTINT9_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT9_NUM) +#define XCHAL_EXTINT10_MASK (1 << XCHAL_EXTINT10_NUM) +#define XCHAL_EXTINT10_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT10_NUM) +#define XCHAL_EXTINT11_MASK (1 << XCHAL_EXTINT11_NUM) +#define XCHAL_EXTINT11_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT11_NUM) +#define XCHAL_EXTINT12_MASK (1 << XCHAL_EXTINT12_NUM) +#define XCHAL_EXTINT12_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT12_NUM) +#define XCHAL_EXTINT13_MASK (1 << XCHAL_EXTINT13_NUM) +#define XCHAL_EXTINT13_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT13_NUM) +#define XCHAL_EXTINT14_MASK (1 << XCHAL_EXTINT14_NUM) +#define XCHAL_EXTINT14_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT14_NUM) +#define XCHAL_EXTINT15_MASK (1 << XCHAL_EXTINT15_NUM) +#define XCHAL_EXTINT15_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT15_NUM) +#define XCHAL_EXTINT16_MASK (1 << XCHAL_EXTINT16_NUM) +#define XCHAL_EXTINT16_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT16_NUM) +#define XCHAL_EXTINT17_MASK (1 << XCHAL_EXTINT17_NUM) +#define XCHAL_EXTINT17_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT17_NUM) +#define XCHAL_EXTINT18_MASK (1 << XCHAL_EXTINT18_NUM) +#define XCHAL_EXTINT18_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT18_NUM) +#define XCHAL_EXTINT19_MASK (1 << XCHAL_EXTINT19_NUM) +#define XCHAL_EXTINT19_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT19_NUM) +#define XCHAL_EXTINT20_MASK (1 << XCHAL_EXTINT20_NUM) +#define XCHAL_EXTINT20_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT20_NUM) +#define XCHAL_EXTINT21_MASK (1 << XCHAL_EXTINT21_NUM) +#define XCHAL_EXTINT21_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT21_NUM) +#define XCHAL_EXTINT22_MASK (1 << XCHAL_EXTINT22_NUM) +#define XCHAL_EXTINT22_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT22_NUM) +#define XCHAL_EXTINT23_MASK (1 << XCHAL_EXTINT23_NUM) +#define XCHAL_EXTINT23_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT23_NUM) +#define XCHAL_EXTINT24_MASK (1 << XCHAL_EXTINT24_NUM) +#define XCHAL_EXTINT24_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT24_NUM) +#define XCHAL_EXTINT25_MASK (1 << XCHAL_EXTINT25_NUM) +#define XCHAL_EXTINT25_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT25_NUM) +#define XCHAL_EXTINT26_MASK (1 << XCHAL_EXTINT26_NUM) +#define XCHAL_EXTINT26_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT26_NUM) +#define XCHAL_EXTINT27_MASK (1 << XCHAL_EXTINT27_NUM) +#define XCHAL_EXTINT27_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT27_NUM) +#define XCHAL_EXTINT28_MASK (1 << XCHAL_EXTINT28_NUM) +#define XCHAL_EXTINT28_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT28_NUM) +#define XCHAL_EXTINT29_MASK (1 << XCHAL_EXTINT29_NUM) +#define XCHAL_EXTINT29_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT29_NUM) +#define XCHAL_EXTINT30_MASK (1 << XCHAL_EXTINT30_NUM) +#define XCHAL_EXTINT30_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT30_NUM) +#define XCHAL_EXTINT31_MASK (1 << XCHAL_EXTINT31_NUM) +#define XCHAL_EXTINT31_LEVEL XCHAL_INT_LEVEL(XCHAL_EXTINT31_NUM) + + +/*---------------------------------------------------------------------- + EXCEPTIONS and VECTORS + ----------------------------------------------------------------------*/ + +/* For backward compatibility ONLY -- DO NOT USE (will be removed in future release): */ +#define XCHAL_HAVE_OLD_EXC_ARCH XCHAL_HAVE_XEA1 /* (DEPRECATED) 1 if old exception architecture (XEA1), 0 otherwise (eg. XEA2) */ +#define XCHAL_HAVE_EXCM XCHAL_HAVE_XEA2 /* (DEPRECATED) 1 if PS.EXCM bit exists (currently equals XCHAL_HAVE_TLBS) */ +#ifdef XCHAL_USER_VECTOR_VADDR +#define XCHAL_PROGRAMEXC_VECTOR_VADDR XCHAL_USER_VECTOR_VADDR +#define XCHAL_USEREXC_VECTOR_VADDR XCHAL_USER_VECTOR_VADDR +#endif +#ifdef XCHAL_USER_VECTOR_PADDR +# define XCHAL_PROGRAMEXC_VECTOR_PADDR XCHAL_USER_VECTOR_PADDR +# define XCHAL_USEREXC_VECTOR_PADDR XCHAL_USER_VECTOR_PADDR +#endif +#ifdef XCHAL_KERNEL_VECTOR_VADDR +# define XCHAL_STACKEDEXC_VECTOR_VADDR XCHAL_KERNEL_VECTOR_VADDR +# define XCHAL_KERNELEXC_VECTOR_VADDR XCHAL_KERNEL_VECTOR_VADDR +#endif +#ifdef XCHAL_KERNEL_VECTOR_PADDR +# define XCHAL_STACKEDEXC_VECTOR_PADDR XCHAL_KERNEL_VECTOR_PADDR +# define XCHAL_KERNELEXC_VECTOR_PADDR XCHAL_KERNEL_VECTOR_PADDR +#endif + +#if 0 +#if XCHAL_HAVE_DEBUG +# define XCHAL_DEBUG_VECTOR_VADDR XCHAL_INTLEVEL_VECTOR_VADDR(XCHAL_DEBUGLEVEL) +/* This one should only get defined if the corresponding intlevel paddr macro exists: */ +# define XCHAL_DEBUG_VECTOR_PADDR XCHAL_INTLEVEL_VECTOR_PADDR(XCHAL_DEBUGLEVEL) +#endif +#endif + +/* Indexing macros: */ +#define _XCHAL_INTLEVEL_VECTOR_VADDR(n) XCHAL_INTLEVEL ## n ## _VECTOR_VADDR +#define XCHAL_INTLEVEL_VECTOR_VADDR(n) _XCHAL_INTLEVEL_VECTOR_VADDR(n) /* n = 0 .. 15 */ + +/* + * General Exception Causes + * (values of EXCCAUSE special register set by general exceptions, + * which vector to the user, kernel, or double-exception vectors). + * + * DEPRECATED. Please use the equivalent EXCCAUSE_xxx macros + * defined in . (Note that these have slightly + * different names, they don't just have the XCHAL_ prefix removed.) + */ +#define XCHAL_EXCCAUSE_ILLEGAL_INSTRUCTION 0 /* Illegal Instruction */ +#define XCHAL_EXCCAUSE_SYSTEM_CALL 1 /* System Call */ +#define XCHAL_EXCCAUSE_INSTRUCTION_FETCH_ERROR 2 /* Instruction Fetch Error */ +#define XCHAL_EXCCAUSE_LOAD_STORE_ERROR 3 /* Load Store Error */ +#define XCHAL_EXCCAUSE_LEVEL1_INTERRUPT 4 /* Level 1 Interrupt */ +#define XCHAL_EXCCAUSE_ALLOCA 5 /* Stack Extension Assist */ +#define XCHAL_EXCCAUSE_INTEGER_DIVIDE_BY_ZERO 6 /* Integer Divide by Zero */ +#define XCHAL_EXCCAUSE_SPECULATION 7 /* Speculation */ +#define XCHAL_EXCCAUSE_PRIVILEGED 8 /* Privileged Instruction */ +#define XCHAL_EXCCAUSE_UNALIGNED 9 /* Unaligned Load Store */ +/*10..15 reserved*/ +#define XCHAL_EXCCAUSE_ITLB_MISS 16 /* ITlb Miss Exception */ +#define XCHAL_EXCCAUSE_ITLB_MULTIHIT 17 /* ITlb Mutltihit Exception */ +#define XCHAL_EXCCAUSE_ITLB_PRIVILEGE 18 /* ITlb Privilege Exception */ +#define XCHAL_EXCCAUSE_ITLB_SIZE_RESTRICTION 19 /* ITlb Size Restriction Exception */ +#define XCHAL_EXCCAUSE_FETCH_CACHE_ATTRIBUTE 20 /* Fetch Cache Attribute Exception */ +/*21..23 reserved*/ +#define XCHAL_EXCCAUSE_DTLB_MISS 24 /* DTlb Miss Exception */ +#define XCHAL_EXCCAUSE_DTLB_MULTIHIT 25 /* DTlb Multihit Exception */ +#define XCHAL_EXCCAUSE_DTLB_PRIVILEGE 26 /* DTlb Privilege Exception */ +#define XCHAL_EXCCAUSE_DTLB_SIZE_RESTRICTION 27 /* DTlb Size Restriction Exception */ +#define XCHAL_EXCCAUSE_LOAD_CACHE_ATTRIBUTE 28 /* Load Cache Attribute Exception */ +#define XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE 29 /* Store Cache Attribute Exception */ +/*30..31 reserved*/ +#define XCHAL_EXCCAUSE_COPROCESSOR0_DISABLED 32 /* Coprocessor 0 disabled */ +#define XCHAL_EXCCAUSE_COPROCESSOR1_DISABLED 33 /* Coprocessor 1 disabled */ +#define XCHAL_EXCCAUSE_COPROCESSOR2_DISABLED 34 /* Coprocessor 2 disabled */ +#define XCHAL_EXCCAUSE_COPROCESSOR3_DISABLED 35 /* Coprocessor 3 disabled */ +#define XCHAL_EXCCAUSE_COPROCESSOR4_DISABLED 36 /* Coprocessor 4 disabled */ +#define XCHAL_EXCCAUSE_COPROCESSOR5_DISABLED 37 /* Coprocessor 5 disabled */ +#define XCHAL_EXCCAUSE_COPROCESSOR6_DISABLED 38 /* Coprocessor 6 disabled */ +#define XCHAL_EXCCAUSE_COPROCESSOR7_DISABLED 39 /* Coprocessor 7 disabled */ +/*40..63 reserved*/ + + +/* + * Miscellaneous special register fields. + * + * For each special register, and each field within each register: + * XCHAL__VALIDMASK is the set of bits defined in the register. + * XCHAL___BITS is the number of bits in the field. + * XCHAL___NUM is 2^bits, the number of possible values + * of the field. + * XCHAL___SHIFT is the position of the field within + * the register, starting from the least significant bit. + * + * DEPRECATED. Please use the equivalent macros defined in + * . (Note that these have different names.) + */ + +/* DBREAKC (special register number 160): */ +#define XCHAL_DBREAKC_VALIDMASK 0xC000003F +#define XCHAL_DBREAKC_MASK_BITS 6 +#define XCHAL_DBREAKC_MASK_NUM 64 +#define XCHAL_DBREAKC_MASK_SHIFT 0 +#define XCHAL_DBREAKC_MASK_MASK 0x0000003F +#define XCHAL_DBREAKC_LOADBREAK_BITS 1 +#define XCHAL_DBREAKC_LOADBREAK_NUM 2 +#define XCHAL_DBREAKC_LOADBREAK_SHIFT 30 +#define XCHAL_DBREAKC_LOADBREAK_MASK 0x40000000 +#define XCHAL_DBREAKC_STOREBREAK_BITS 1 +#define XCHAL_DBREAKC_STOREBREAK_NUM 2 +#define XCHAL_DBREAKC_STOREBREAK_SHIFT 31 +#define XCHAL_DBREAKC_STOREBREAK_MASK 0x80000000 +/* PS (special register number 230): */ +#define XCHAL_PS_VALIDMASK 0x00070F3F +#define XCHAL_PS_INTLEVEL_BITS 4 +#define XCHAL_PS_INTLEVEL_NUM 16 +#define XCHAL_PS_INTLEVEL_SHIFT 0 +#define XCHAL_PS_INTLEVEL_MASK 0x0000000F +#define XCHAL_PS_EXCM_BITS 1 +#define XCHAL_PS_EXCM_NUM 2 +#define XCHAL_PS_EXCM_SHIFT 4 +#define XCHAL_PS_EXCM_MASK 0x00000010 +#define XCHAL_PS_UM_BITS 1 +#define XCHAL_PS_UM_NUM 2 +#define XCHAL_PS_UM_SHIFT 5 +#define XCHAL_PS_UM_MASK 0x00000020 +#define XCHAL_PS_RING_BITS 2 +#define XCHAL_PS_RING_NUM 4 +#define XCHAL_PS_RING_SHIFT 6 +#define XCHAL_PS_RING_MASK 0x000000C0 +#define XCHAL_PS_OWB_BITS 4 +#define XCHAL_PS_OWB_NUM 16 +#define XCHAL_PS_OWB_SHIFT 8 +#define XCHAL_PS_OWB_MASK 0x00000F00 +#define XCHAL_PS_CALLINC_BITS 2 +#define XCHAL_PS_CALLINC_NUM 4 +#define XCHAL_PS_CALLINC_SHIFT 16 +#define XCHAL_PS_CALLINC_MASK 0x00030000 +#define XCHAL_PS_WOE_BITS 1 +#define XCHAL_PS_WOE_NUM 2 +#define XCHAL_PS_WOE_SHIFT 18 +#define XCHAL_PS_WOE_MASK 0x00040000 +/* EXCCAUSE (special register number 232): */ +#define XCHAL_EXCCAUSE_VALIDMASK 0x0000003F +#define XCHAL_EXCCAUSE_BITS 6 +#define XCHAL_EXCCAUSE_NUM 64 +#define XCHAL_EXCCAUSE_SHIFT 0 +#define XCHAL_EXCCAUSE_MASK 0x0000003F +/* DEBUGCAUSE (special register number 233): */ +#define XCHAL_DEBUGCAUSE_VALIDMASK 0x0000003F +#define XCHAL_DEBUGCAUSE_ICOUNT_BITS 1 +#define XCHAL_DEBUGCAUSE_ICOUNT_NUM 2 +#define XCHAL_DEBUGCAUSE_ICOUNT_SHIFT 0 +#define XCHAL_DEBUGCAUSE_ICOUNT_MASK 0x00000001 +#define XCHAL_DEBUGCAUSE_IBREAK_BITS 1 +#define XCHAL_DEBUGCAUSE_IBREAK_NUM 2 +#define XCHAL_DEBUGCAUSE_IBREAK_SHIFT 1 +#define XCHAL_DEBUGCAUSE_IBREAK_MASK 0x00000002 +#define XCHAL_DEBUGCAUSE_DBREAK_BITS 1 +#define XCHAL_DEBUGCAUSE_DBREAK_NUM 2 +#define XCHAL_DEBUGCAUSE_DBREAK_SHIFT 2 +#define XCHAL_DEBUGCAUSE_DBREAK_MASK 0x00000004 +#define XCHAL_DEBUGCAUSE_BREAK_BITS 1 +#define XCHAL_DEBUGCAUSE_BREAK_NUM 2 +#define XCHAL_DEBUGCAUSE_BREAK_SHIFT 3 +#define XCHAL_DEBUGCAUSE_BREAK_MASK 0x00000008 +#define XCHAL_DEBUGCAUSE_BREAKN_BITS 1 +#define XCHAL_DEBUGCAUSE_BREAKN_NUM 2 +#define XCHAL_DEBUGCAUSE_BREAKN_SHIFT 4 +#define XCHAL_DEBUGCAUSE_BREAKN_MASK 0x00000010 +#define XCHAL_DEBUGCAUSE_DEBUGINT_BITS 1 +#define XCHAL_DEBUGCAUSE_DEBUGINT_NUM 2 +#define XCHAL_DEBUGCAUSE_DEBUGINT_SHIFT 5 +#define XCHAL_DEBUGCAUSE_DEBUGINT_MASK 0x00000020 + + + + +/*---------------------------------------------------------------------- + TIMERS + ----------------------------------------------------------------------*/ + +/*#define XCHAL_HAVE_TIMERS XCHAL_HAVE_CCOUNT*/ + + + +/*---------------------------------------------------------------------- + INTERNAL I/D RAM/ROMs and XLMI + ----------------------------------------------------------------------*/ + +#define XCHAL_NUM_IROM XCHAL_NUM_INSTROM /* (DEPRECATED) */ +#define XCHAL_NUM_IRAM XCHAL_NUM_INSTRAM /* (DEPRECATED) */ +#define XCHAL_NUM_DROM XCHAL_NUM_DATAROM /* (DEPRECATED) */ +#define XCHAL_NUM_DRAM XCHAL_NUM_DATARAM /* (DEPRECATED) */ + +#define XCHAL_IROM0_VADDR XCHAL_INSTROM0_VADDR /* (DEPRECATED) */ +#define XCHAL_IROM0_PADDR XCHAL_INSTROM0_PADDR /* (DEPRECATED) */ +#define XCHAL_IROM0_SIZE XCHAL_INSTROM0_SIZE /* (DEPRECATED) */ +#define XCHAL_IROM1_VADDR XCHAL_INSTROM1_VADDR /* (DEPRECATED) */ +#define XCHAL_IROM1_PADDR XCHAL_INSTROM1_PADDR /* (DEPRECATED) */ +#define XCHAL_IROM1_SIZE XCHAL_INSTROM1_SIZE /* (DEPRECATED) */ +#define XCHAL_IRAM0_VADDR XCHAL_INSTRAM0_VADDR /* (DEPRECATED) */ +#define XCHAL_IRAM0_PADDR XCHAL_INSTRAM0_PADDR /* (DEPRECATED) */ +#define XCHAL_IRAM0_SIZE XCHAL_INSTRAM0_SIZE /* (DEPRECATED) */ +#define XCHAL_IRAM1_VADDR XCHAL_INSTRAM1_VADDR /* (DEPRECATED) */ +#define XCHAL_IRAM1_PADDR XCHAL_INSTRAM1_PADDR /* (DEPRECATED) */ +#define XCHAL_IRAM1_SIZE XCHAL_INSTRAM1_SIZE /* (DEPRECATED) */ +#define XCHAL_DROM0_VADDR XCHAL_DATAROM0_VADDR /* (DEPRECATED) */ +#define XCHAL_DROM0_PADDR XCHAL_DATAROM0_PADDR /* (DEPRECATED) */ +#define XCHAL_DROM0_SIZE XCHAL_DATAROM0_SIZE /* (DEPRECATED) */ +#define XCHAL_DROM1_VADDR XCHAL_DATAROM1_VADDR /* (DEPRECATED) */ +#define XCHAL_DROM1_PADDR XCHAL_DATAROM1_PADDR /* (DEPRECATED) */ +#define XCHAL_DROM1_SIZE XCHAL_DATAROM1_SIZE /* (DEPRECATED) */ +#define XCHAL_DRAM0_VADDR XCHAL_DATARAM0_VADDR /* (DEPRECATED) */ +#define XCHAL_DRAM0_PADDR XCHAL_DATARAM0_PADDR /* (DEPRECATED) */ +#define XCHAL_DRAM0_SIZE XCHAL_DATARAM0_SIZE /* (DEPRECATED) */ +#define XCHAL_DRAM1_VADDR XCHAL_DATARAM1_VADDR /* (DEPRECATED) */ +#define XCHAL_DRAM1_PADDR XCHAL_DATARAM1_PADDR /* (DEPRECATED) */ +#define XCHAL_DRAM1_SIZE XCHAL_DATARAM1_SIZE /* (DEPRECATED) */ + + + +/*---------------------------------------------------------------------- + CACHE + ----------------------------------------------------------------------*/ + + +/* Default PREFCTL value to enable prefetch. */ +#if XCHAL_HW_MIN_VERSION < XTENSA_HWVERSION_RE_2012_0 +#define XCHAL_CACHE_PREFCTL_DEFAULT 0x00044 /* enabled, not aggressive */ +#elif XCHAL_HW_MIN_VERSION < XTENSA_HWVERSION_RF_2014_0 +#define XCHAL_CACHE_PREFCTL_DEFAULT 0x01044 /* + enable prefetch to L1 */ +#elif ((XCHAL_PREFETCH_ENTRIES >= 16) && XCHAL_HAVE_CACHE_BLOCKOPS) +#define XCHAL_CACHE_PREFCTL_DEFAULT 0x81044 /* 12 entries for block ops */ +#elif ((XCHAL_PREFETCH_ENTRIES >= 8) && XCHAL_HAVE_CACHE_BLOCKOPS) +#define XCHAL_CACHE_PREFCTL_DEFAULT 0x51044 /* 5 entries for block ops */ +#else +#define XCHAL_CACHE_PREFCTL_DEFAULT 0x01044 /* 0 entries for block ops */ +#endif + + +/* Max for both I-cache and D-cache (used for general alignment): */ +#if XCHAL_ICACHE_LINESIZE > XCHAL_DCACHE_LINESIZE +# define XCHAL_CACHE_LINEWIDTH_MAX XCHAL_ICACHE_LINEWIDTH +# define XCHAL_CACHE_LINESIZE_MAX XCHAL_ICACHE_LINESIZE +#else +# define XCHAL_CACHE_LINEWIDTH_MAX XCHAL_DCACHE_LINEWIDTH +# define XCHAL_CACHE_LINESIZE_MAX XCHAL_DCACHE_LINESIZE +#endif + +#define XCHAL_ICACHE_SETSIZE (1< XCHAL_DCACHE_SETWIDTH +# define XCHAL_CACHE_SETWIDTH_MAX XCHAL_ICACHE_SETWIDTH +# define XCHAL_CACHE_SETSIZE_MAX XCHAL_ICACHE_SETSIZE +#else +# define XCHAL_CACHE_SETWIDTH_MAX XCHAL_DCACHE_SETWIDTH +# define XCHAL_CACHE_SETSIZE_MAX XCHAL_DCACHE_SETSIZE +#endif + +/* Instruction cache tag bits: */ +#define XCHAL_ICACHE_TAG_V_SHIFT 0 +#define XCHAL_ICACHE_TAG_V 0x1 /* valid bit */ +#if XCHAL_ICACHE_WAYS > 1 +# define XCHAL_ICACHE_TAG_F_SHIFT 1 +# define XCHAL_ICACHE_TAG_F 0x2 /* fill (LRU) bit */ +#else +# define XCHAL_ICACHE_TAG_F_SHIFT 0 +# define XCHAL_ICACHE_TAG_F 0 /* no fill (LRU) bit */ +#endif +#if XCHAL_ICACHE_LINE_LOCKABLE +# define XCHAL_ICACHE_TAG_L_SHIFT (XCHAL_ICACHE_TAG_F_SHIFT+1) +# define XCHAL_ICACHE_TAG_L (1 << XCHAL_ICACHE_TAG_L_SHIFT) /* lock bit */ +#else +# define XCHAL_ICACHE_TAG_L_SHIFT XCHAL_ICACHE_TAG_F_SHIFT +# define XCHAL_ICACHE_TAG_L 0 /* no lock bit */ +#endif +/* Data cache tag bits: */ +#define XCHAL_DCACHE_TAG_V_SHIFT 0 +#define XCHAL_DCACHE_TAG_V 0x1 /* valid bit */ +#if XCHAL_DCACHE_WAYS > 1 +# define XCHAL_DCACHE_TAG_F_SHIFT 1 +# define XCHAL_DCACHE_TAG_F 0x2 /* fill (LRU) bit */ +#else +# define XCHAL_DCACHE_TAG_F_SHIFT 0 +# define XCHAL_DCACHE_TAG_F 0 /* no fill (LRU) bit */ +#endif +#if XCHAL_DCACHE_IS_WRITEBACK +# define XCHAL_DCACHE_TAG_D_SHIFT (XCHAL_DCACHE_TAG_F_SHIFT+1) +# define XCHAL_DCACHE_TAG_D (1 << XCHAL_DCACHE_TAG_D_SHIFT) /* dirty bit */ +#else +# define XCHAL_DCACHE_TAG_D_SHIFT XCHAL_DCACHE_TAG_F_SHIFT +# define XCHAL_DCACHE_TAG_D 0 /* no dirty bit */ +#endif +#if XCHAL_DCACHE_LINE_LOCKABLE +# define XCHAL_DCACHE_TAG_L_SHIFT (XCHAL_DCACHE_TAG_D_SHIFT+1) +# define XCHAL_DCACHE_TAG_L (1 << XCHAL_DCACHE_TAG_L_SHIFT) /* lock bit */ +#else +# define XCHAL_DCACHE_TAG_L_SHIFT XCHAL_DCACHE_TAG_D_SHIFT +# define XCHAL_DCACHE_TAG_L 0 /* no lock bit */ +#endif + +/* Whether MEMCTL register has anything useful */ +#define XCHAL_USE_MEMCTL (((XCHAL_LOOP_BUFFER_SIZE > 0) || \ + XCHAL_DCACHE_IS_COHERENT || \ + XCHAL_HAVE_ICACHE_DYN_WAYS || \ + XCHAL_HAVE_DCACHE_DYN_WAYS) && \ + (XCHAL_HW_MIN_VERSION >= XTENSA_HWVERSION_RE_2012_0)) + +#if XCHAL_DCACHE_IS_COHERENT +#define _MEMCTL_SNOOP_EN 0x02 /* Enable snoop */ +#else +#define _MEMCTL_SNOOP_EN 0x00 /* Don't enable snoop */ +#endif + +#if (XCHAL_LOOP_BUFFER_SIZE == 0) || XCHAL_ERRATUM_453 +#define _MEMCTL_L0IBUF_EN 0x00 /* No loop buffer or don't enable */ +#else +#define _MEMCTL_L0IBUF_EN 0x01 /* Enable loop buffer */ +#endif + +/* Default MEMCTL values: */ +#if XCHAL_HAVE_ICACHE_DYN_WAYS || XCHAL_HAVE_DCACHE_DYN_WAYS +#define XCHAL_CACHE_MEMCTL_DEFAULT (0xFFFFFF00 | _MEMCTL_L0IBUF_EN) +#else +#define XCHAL_CACHE_MEMCTL_DEFAULT (0x00000000 | _MEMCTL_L0IBUF_EN) +#endif + +#define XCHAL_SNOOP_LB_MEMCTL_DEFAULT (_MEMCTL_SNOOP_EN | _MEMCTL_L0IBUF_EN) + + +/*---------------------------------------------------------------------- + MMU + ----------------------------------------------------------------------*/ + +/* See for more details. */ + +/* Has different semantic in open source headers (where it means HAVE_PTP_MMU), + so comment out starting with RB-2008.3 release; later, might get + get reintroduced as a synonym for XCHAL_HAVE_PTP_MMU instead: */ +/*#define XCHAL_HAVE_MMU XCHAL_HAVE_TLBS*/ /* (DEPRECATED; use XCHAL_HAVE_TLBS instead) */ + +/* Indexing macros: */ +#define _XCHAL_ITLB_SET(n,_what) XCHAL_ITLB_SET ## n ## _what +#define XCHAL_ITLB_SET(n,what) _XCHAL_ITLB_SET(n, _ ## what ) +#define _XCHAL_ITLB_SET_E(n,i,_what) XCHAL_ITLB_SET ## n ## _E ## i ## _what +#define XCHAL_ITLB_SET_E(n,i,what) _XCHAL_ITLB_SET_E(n,i, _ ## what ) +#define _XCHAL_DTLB_SET(n,_what) XCHAL_DTLB_SET ## n ## _what +#define XCHAL_DTLB_SET(n,what) _XCHAL_DTLB_SET(n, _ ## what ) +#define _XCHAL_DTLB_SET_E(n,i,_what) XCHAL_DTLB_SET ## n ## _E ## i ## _what +#define XCHAL_DTLB_SET_E(n,i,what) _XCHAL_DTLB_SET_E(n,i, _ ## what ) +/* + * Example use: XCHAL_ITLB_SET(XCHAL_ITLB_ARF_SET0,ENTRIES) + * to get the value of XCHAL_ITLB_SET_ENTRIES where is the first auto-refill set. + */ + +/* Number of entries per autorefill way: */ +#define XCHAL_ITLB_ARF_ENTRIES (1< 0 && XCHAL_DTLB_ARF_WAYS > 0 && XCHAL_MMU_RINGS >= 2 +# define XCHAL_HAVE_PTP_MMU 1 /* have full MMU (with page table [autorefill] and protection) */ +#else +# define XCHAL_HAVE_PTP_MMU 0 /* don't have full MMU */ +#endif +#endif + +/* + * For full MMUs, report kernel RAM segment and kernel I/O segment static page mappings: + */ +#if XCHAL_HAVE_PTP_MMU && !XCHAL_HAVE_SPANNING_WAY +#define XCHAL_KSEG_CACHED_VADDR 0xD0000000 /* virt.addr of kernel RAM cached static map */ +#define XCHAL_KSEG_CACHED_PADDR 0x00000000 /* phys.addr of kseg_cached */ +#define XCHAL_KSEG_CACHED_SIZE 0x08000000 /* size in bytes of kseg_cached (assumed power of 2!!!) */ +#define XCHAL_KSEG_BYPASS_VADDR 0xD8000000 /* virt.addr of kernel RAM bypass (uncached) static map */ +#define XCHAL_KSEG_BYPASS_PADDR 0x00000000 /* phys.addr of kseg_bypass */ +#define XCHAL_KSEG_BYPASS_SIZE 0x08000000 /* size in bytes of kseg_bypass (assumed power of 2!!!) */ + +#define XCHAL_KIO_CACHED_VADDR 0xE0000000 /* virt.addr of kernel I/O cached static map */ +#define XCHAL_KIO_CACHED_PADDR 0xF0000000 /* phys.addr of kio_cached */ +#define XCHAL_KIO_CACHED_SIZE 0x10000000 /* size in bytes of kio_cached (assumed power of 2!!!) */ +#define XCHAL_KIO_BYPASS_VADDR 0xF0000000 /* virt.addr of kernel I/O bypass (uncached) static map */ +#define XCHAL_KIO_BYPASS_PADDR 0xF0000000 /* phys.addr of kio_bypass */ +#define XCHAL_KIO_BYPASS_SIZE 0x10000000 /* size in bytes of kio_bypass (assumed power of 2!!!) */ + +#define XCHAL_SEG_MAPPABLE_VADDR 0x00000000 /* start of largest non-static-mapped virtual addr area */ +#define XCHAL_SEG_MAPPABLE_SIZE 0xD0000000 /* size in bytes of " */ +/* define XCHAL_SEG_MAPPABLE2_xxx if more areas present, sorted in order of descending size. */ +#endif + + +/*---------------------------------------------------------------------- + MISC + ----------------------------------------------------------------------*/ + +/* Data alignment required if used for instructions: */ +#if XCHAL_INST_FETCH_WIDTH > XCHAL_DATA_WIDTH +# define XCHAL_ALIGN_MAX XCHAL_INST_FETCH_WIDTH +#else +# define XCHAL_ALIGN_MAX XCHAL_DATA_WIDTH +#endif + +/* + * Names kept for backward compatibility. + * (Here "RELEASE" is now a misnomer; these are product *versions*, not the releases + * under which they are released. In the T10##.# era there was no distinction.) + */ +#define XCHAL_HW_RELEASE_MAJOR XCHAL_HW_VERSION_MAJOR +#define XCHAL_HW_RELEASE_MINOR XCHAL_HW_VERSION_MINOR +#define XCHAL_HW_RELEASE_NAME XCHAL_HW_VERSION_NAME + + + + +/*---------------------------------------------------------------------- + COPROCESSORS and EXTRA STATE + ----------------------------------------------------------------------*/ + +#define XCHAL_EXTRA_SA_SIZE XCHAL_NCP_SA_SIZE +#define XCHAL_EXTRA_SA_ALIGN XCHAL_NCP_SA_ALIGN +#define XCHAL_CPEXTRA_SA_SIZE XCHAL_TOTAL_SA_SIZE +#define XCHAL_CPEXTRA_SA_ALIGN XCHAL_TOTAL_SA_ALIGN + +#if defined (_ASMLANGUAGE) || defined (__ASSEMBLER__) + + /* Invoked at start of save area load/store sequence macro to setup macro + * internal offsets. Not usually invoked directly. + * continue 0 for 1st sequence, 1 for subsequent consecutive ones. + * totofs offset from original ptr to next load/store location. + */ + .macro xchal_sa_start continue totofs + .ifeq \continue + .set .Lxchal_pofs_, 0 /* offset from original ptr to current \ptr */ + .set .Lxchal_ofs_, 0 /* offset from current \ptr to next load/store location */ + .endif + .if \totofs + 1 /* if totofs specified (not -1) */ + .set .Lxchal_ofs_, \totofs - .Lxchal_pofs_ /* specific offset from original ptr */ + .endif + .endm + + /* Align portion of save area and bring ptr in range if necessary. + * Used by save area load/store sequences. Not usually invoked directly. + * Allows combining multiple (sub-)sequences arbitrarily. + * ptr pointer to save area (may be off, see .Lxchal_pofs_) + * minofs,maxofs range of offset from cur ptr to next load/store loc; + * minofs <= 0 <= maxofs (0 must always be valid offset) + * range must be within +/- 30kB or so. + * ofsalign alignment granularity of minofs .. maxofs (pow of 2) + * (restriction on offset from ptr to next load/store loc) + * totalign align from orig ptr to next load/store loc (pow of 2) + */ + .macro xchal_sa_align ptr minofs maxofs ofsalign totalign + /* First align where we start accessing the next register + * per \totalign relative to original ptr (i.e. start of the save area): + */ + .set .Lxchal_ofs_, ((.Lxchal_pofs_ + .Lxchal_ofs_ + \totalign - 1) & -\totalign) - .Lxchal_pofs_ + /* If necessary, adjust \ptr to bring .Lxchal_ofs_ in acceptable range: */ + .if (((\maxofs) - .Lxchal_ofs_) & 0xC0000000) | ((.Lxchal_ofs_ - (\minofs)) & 0xC0000000) | (.Lxchal_ofs_ & (\ofsalign-1)) + .set .Ligmask, 0xFFFFFFFF /* TODO: optimize to addmi, per aligns and .Lxchal_ofs_ */ + addi \ptr, \ptr, (.Lxchal_ofs_ & .Ligmask) + .set .Lxchal_pofs_, .Lxchal_pofs_ + (.Lxchal_ofs_ & .Ligmask) + .set .Lxchal_ofs_, (.Lxchal_ofs_ & ~.Ligmask) + .endif + .endm + /* + * We could optimize for addi to expand to only addmi instead of + * "addmi;addi", where possible. Here's a partial example how: + * .set .Lmaxmask, -(\ofsalign) & -(\totalign) + * .if (((\maxofs) + ~.Lmaxmask + 1) & 0xFFFFFF00) && ((.Lxchal_ofs_ & ~.Lmaxmask) == 0) + * .set .Ligmask, 0xFFFFFF00 + * .elif ... ditto for negative ofs range ... + * .set .Ligmask, 0xFFFFFF00 + * .set ... adjust per offset ... + * .else + * .set .Ligmask, 0xFFFFFFFF + * .endif + */ + + /* Invoke this after xchal_XXX_{load,store} macros to restore \ptr. */ + .macro xchal_sa_ptr_restore ptr + .if .Lxchal_pofs_ + addi \ptr, \ptr, - .Lxchal_pofs_ + .set .Lxchal_ofs_, .Lxchal_ofs_ + .Lxchal_pofs_ + .set .Lxchal_pofs_, 0 + .endif + .endm + + /* + * Use as eg: + * xchal_atmps_store a1, SOMEOFS, XCHAL_SA_NUM_ATMPS, a4, a5 + * xchal_ncp_load a2, a0,a3,a4,a5 + * xchal_atmps_load a1, SOMEOFS, XCHAL_SA_NUM_ATMPS, a4, a5 + * + * Specify only the ARs you *haven't* saved/restored already, up to 4. + * They *must* be the *last* ARs (in same order) specified to save area + * load/store sequences. In the example above, a0 and a3 were already + * saved/restored and unused (thus available) but a4 and a5 were not. + */ +#define xchal_atmps_store xchal_atmps_loadstore s32i, +#define xchal_atmps_load xchal_atmps_loadstore l32i, + .macro xchal_atmps_loadstore inst ptr offset nreq aa=0 ab=0 ac=0 ad=0 + .set .Lnsaved_, 0 + .irp reg,\aa,\ab,\ac,\ad + .ifeq 0x\reg ; .set .Lnsaved_,.Lnsaved_+1 ; .endif + .endr + .set .Laofs_, 0 + .irp reg,\aa,\ab,\ac,\ad + .ifgt (\nreq)-.Lnsaved_ + \inst \reg, \ptr, .Laofs_+\offset + .set .Laofs_,.Laofs_+4 + .set .Lnsaved_,.Lnsaved_+1 + .endif + .endr + .endm + +/*#define xchal_ncp_load_a2 xchal_ncp_load a2,a3,a4,a5,a6*/ +/*#define xchal_ncp_store_a2 xchal_ncp_store a2,a3,a4,a5,a6*/ +#define xchal_extratie_load xchal_ncptie_load +#define xchal_extratie_store xchal_ncptie_store +#define xchal_extratie_load_a2 xchal_ncptie_load a2,a3,a4,a5,a6 +#define xchal_extratie_store_a2 xchal_ncptie_store a2,a3,a4,a5,a6 +#define xchal_extra_load xchal_ncp_load +#define xchal_extra_store xchal_ncp_store +#define xchal_extra_load_a2 xchal_ncp_load a2,a3,a4,a5,a6 +#define xchal_extra_store_a2 xchal_ncp_store a2,a3,a4,a5,a6 +#define xchal_extra_load_funcbody xchal_ncp_load a2,a3,a4,a5,a6 +#define xchal_extra_store_funcbody xchal_ncp_store a2,a3,a4,a5,a6 +#define xchal_cp0_store_a2 xchal_cp0_store a2,a3,a4,a5,a6 +#define xchal_cp0_load_a2 xchal_cp0_load a2,a3,a4,a5,a6 +#define xchal_cp1_store_a2 xchal_cp1_store a2,a3,a4,a5,a6 +#define xchal_cp1_load_a2 xchal_cp1_load a2,a3,a4,a5,a6 +#define xchal_cp2_store_a2 xchal_cp2_store a2,a3,a4,a5,a6 +#define xchal_cp2_load_a2 xchal_cp2_load a2,a3,a4,a5,a6 +#define xchal_cp3_store_a2 xchal_cp3_store a2,a3,a4,a5,a6 +#define xchal_cp3_load_a2 xchal_cp3_load a2,a3,a4,a5,a6 +#define xchal_cp4_store_a2 xchal_cp4_store a2,a3,a4,a5,a6 +#define xchal_cp4_load_a2 xchal_cp4_load a2,a3,a4,a5,a6 +#define xchal_cp5_store_a2 xchal_cp5_store a2,a3,a4,a5,a6 +#define xchal_cp5_load_a2 xchal_cp5_load a2,a3,a4,a5,a6 +#define xchal_cp6_store_a2 xchal_cp6_store a2,a3,a4,a5,a6 +#define xchal_cp6_load_a2 xchal_cp6_load a2,a3,a4,a5,a6 +#define xchal_cp7_store_a2 xchal_cp7_store a2,a3,a4,a5,a6 +#define xchal_cp7_load_a2 xchal_cp7_load a2,a3,a4,a5,a6 + +/* Empty placeholder macros for undefined coprocessors: */ +#if (XCHAL_CP_MASK & ~XCHAL_CP_PORT_MASK) == 0 +# if XCHAL_CP0_SA_SIZE == 0 + .macro xchal_cp0_store p a b c d continue=0 ofs=-1 select=-1 ; .endm + .macro xchal_cp0_load p a b c d continue=0 ofs=-1 select=-1 ; .endm +# endif +# if XCHAL_CP1_SA_SIZE == 0 + .macro xchal_cp1_store p a b c d continue=0 ofs=-1 select=-1 ; .endm + .macro xchal_cp1_load p a b c d continue=0 ofs=-1 select=-1 ; .endm +# endif +# if XCHAL_CP2_SA_SIZE == 0 + .macro xchal_cp2_store p a b c d continue=0 ofs=-1 select=-1 ; .endm + .macro xchal_cp2_load p a b c d continue=0 ofs=-1 select=-1 ; .endm +# endif +# if XCHAL_CP3_SA_SIZE == 0 + .macro xchal_cp3_store p a b c d continue=0 ofs=-1 select=-1 ; .endm + .macro xchal_cp3_load p a b c d continue=0 ofs=-1 select=-1 ; .endm +# endif +# if XCHAL_CP4_SA_SIZE == 0 + .macro xchal_cp4_store p a b c d continue=0 ofs=-1 select=-1 ; .endm + .macro xchal_cp4_load p a b c d continue=0 ofs=-1 select=-1 ; .endm +# endif +# if XCHAL_CP5_SA_SIZE == 0 + .macro xchal_cp5_store p a b c d continue=0 ofs=-1 select=-1 ; .endm + .macro xchal_cp5_load p a b c d continue=0 ofs=-1 select=-1 ; .endm +# endif +# if XCHAL_CP6_SA_SIZE == 0 + .macro xchal_cp6_store p a b c d continue=0 ofs=-1 select=-1 ; .endm + .macro xchal_cp6_load p a b c d continue=0 ofs=-1 select=-1 ; .endm +# endif +# if XCHAL_CP7_SA_SIZE == 0 + .macro xchal_cp7_store p a b c d continue=0 ofs=-1 select=-1 ; .endm + .macro xchal_cp7_load p a b c d continue=0 ofs=-1 select=-1 ; .endm +# endif +#endif + + /******************** + * Macros to create functions that save and restore the state of *any* TIE + * coprocessor (by dynamic index). + */ + + /* + * Macro that expands to the body of a function + * that stores the selected coprocessor's state (registers etc). + * Entry: a2 = ptr to save area in which to save cp state + * a3 = coprocessor number + * Exit: any register a2-a15 (?) may have been clobbered. + */ + .macro xchal_cpi_store_funcbody +#if (XCHAL_CP_MASK & ~XCHAL_CP_PORT_MASK) +# if XCHAL_CP0_SA_SIZE + bnez a3, 99f + xchal_cp0_store_a2 + j 90f +99: +# endif +# if XCHAL_CP1_SA_SIZE + bnei a3, 1, 99f + xchal_cp1_store_a2 + j 90f +99: +# endif +# if XCHAL_CP2_SA_SIZE + bnei a3, 2, 99f + xchal_cp2_store_a2 + j 90f +99: +# endif +# if XCHAL_CP3_SA_SIZE + bnei a3, 3, 99f + xchal_cp3_store_a2 + j 90f +99: +# endif +# if XCHAL_CP4_SA_SIZE + bnei a3, 4, 99f + xchal_cp4_store_a2 + j 90f +99: +# endif +# if XCHAL_CP5_SA_SIZE + bnei a3, 5, 99f + xchal_cp5_store_a2 + j 90f +99: +# endif +# if XCHAL_CP6_SA_SIZE + bnei a3, 6, 99f + xchal_cp6_store_a2 + j 90f +99: +# endif +# if XCHAL_CP7_SA_SIZE + bnei a3, 7, 99f + xchal_cp7_store_a2 + j 90f +99: +# endif +90: +#endif + .endm + + /* + * Macro that expands to the body of a function + * that loads the selected coprocessor's state (registers etc). + * Entry: a2 = ptr to save area from which to restore cp state + * a3 = coprocessor number + * Exit: any register a2-a15 (?) may have been clobbered. + */ + .macro xchal_cpi_load_funcbody +#if (XCHAL_CP_MASK & ~XCHAL_CP_PORT_MASK) +# if XCHAL_CP0_SA_SIZE + bnez a3, 99f + xchal_cp0_load_a2 + j 90f +99: +# endif +# if XCHAL_CP1_SA_SIZE + bnei a3, 1, 99f + xchal_cp1_load_a2 + j 90f +99: +# endif +# if XCHAL_CP2_SA_SIZE + bnei a3, 2, 99f + xchal_cp2_load_a2 + j 90f +99: +# endif +# if XCHAL_CP3_SA_SIZE + bnei a3, 3, 99f + xchal_cp3_load_a2 + j 90f +99: +# endif +# if XCHAL_CP4_SA_SIZE + bnei a3, 4, 99f + xchal_cp4_load_a2 + j 90f +99: +# endif +# if XCHAL_CP5_SA_SIZE + bnei a3, 5, 99f + xchal_cp5_load_a2 + j 90f +99: +# endif +# if XCHAL_CP6_SA_SIZE + bnei a3, 6, 99f + xchal_cp6_load_a2 + j 90f +99: +# endif +# if XCHAL_CP7_SA_SIZE + bnei a3, 7, 99f + xchal_cp7_load_a2 + j 90f +99: +# endif +90: +#endif + .endm + +#endif /*_ASMLANGUAGE or __ASSEMBLER__*/ + + +/* Other default macros for undefined coprocessors: */ +#ifndef XCHAL_CP0_NAME +# define XCHAL_CP0_NAME 0 +# define XCHAL_CP0_SA_CONTENTS_LIBDB_NUM 0 +# define XCHAL_CP0_SA_CONTENTS_LIBDB /* empty */ +#endif +#ifndef XCHAL_CP1_NAME +# define XCHAL_CP1_NAME 0 +# define XCHAL_CP1_SA_CONTENTS_LIBDB_NUM 0 +# define XCHAL_CP1_SA_CONTENTS_LIBDB /* empty */ +#endif +#ifndef XCHAL_CP2_NAME +# define XCHAL_CP2_NAME 0 +# define XCHAL_CP2_SA_CONTENTS_LIBDB_NUM 0 +# define XCHAL_CP2_SA_CONTENTS_LIBDB /* empty */ +#endif +#ifndef XCHAL_CP3_NAME +# define XCHAL_CP3_NAME 0 +# define XCHAL_CP3_SA_CONTENTS_LIBDB_NUM 0 +# define XCHAL_CP3_SA_CONTENTS_LIBDB /* empty */ +#endif +#ifndef XCHAL_CP4_NAME +# define XCHAL_CP4_NAME 0 +# define XCHAL_CP4_SA_CONTENTS_LIBDB_NUM 0 +# define XCHAL_CP4_SA_CONTENTS_LIBDB /* empty */ +#endif +#ifndef XCHAL_CP5_NAME +# define XCHAL_CP5_NAME 0 +# define XCHAL_CP5_SA_CONTENTS_LIBDB_NUM 0 +# define XCHAL_CP5_SA_CONTENTS_LIBDB /* empty */ +#endif +#ifndef XCHAL_CP6_NAME +# define XCHAL_CP6_NAME 0 +# define XCHAL_CP6_SA_CONTENTS_LIBDB_NUM 0 +# define XCHAL_CP6_SA_CONTENTS_LIBDB /* empty */ +#endif +#ifndef XCHAL_CP7_NAME +# define XCHAL_CP7_NAME 0 +# define XCHAL_CP7_SA_CONTENTS_LIBDB_NUM 0 +# define XCHAL_CP7_SA_CONTENTS_LIBDB /* empty */ +#endif + +#if XCHAL_CP_MASK == 0 +/* Filler info for unassigned coprocessors, to simplify arrays etc: */ +#define XCHAL_CP0_SA_SIZE 0 +#define XCHAL_CP0_SA_ALIGN 1 +#define XCHAL_CP1_SA_SIZE 0 +#define XCHAL_CP1_SA_ALIGN 1 +#define XCHAL_CP2_SA_SIZE 0 +#define XCHAL_CP2_SA_ALIGN 1 +#define XCHAL_CP3_SA_SIZE 0 +#define XCHAL_CP3_SA_ALIGN 1 +#define XCHAL_CP4_SA_SIZE 0 +#define XCHAL_CP4_SA_ALIGN 1 +#define XCHAL_CP5_SA_SIZE 0 +#define XCHAL_CP5_SA_ALIGN 1 +#define XCHAL_CP6_SA_SIZE 0 +#define XCHAL_CP6_SA_ALIGN 1 +#define XCHAL_CP7_SA_SIZE 0 +#define XCHAL_CP7_SA_ALIGN 1 +#endif + + +/* Indexing macros: */ +#define _XCHAL_CP_SA_SIZE(n) XCHAL_CP ## n ## _SA_SIZE +#define XCHAL_CP_SA_SIZE(n) _XCHAL_CP_SA_SIZE(n) /* n = 0 .. 7 */ +#define _XCHAL_CP_SA_ALIGN(n) XCHAL_CP ## n ## _SA_ALIGN +#define XCHAL_CP_SA_ALIGN(n) _XCHAL_CP_SA_ALIGN(n) /* n = 0 .. 7 */ + +#define XCHAL_CPEXTRA_SA_SIZE_TOR2 XCHAL_CPEXTRA_SA_SIZE /* Tor2Beta only - do not use */ + +/* Link-time HAL global variables that report coprocessor numbers by name + (names are case-preserved from the original TIE): */ +#if !defined(_ASMLANGUAGE) && !defined(_NOCLANGUAGE) && !defined(__ASSEMBLER__) +# define _XCJOIN(a,b) a ## b +# define XCJOIN(a,b) _XCJOIN(a,b) +# ifdef XCHAL_CP0_NAME +extern const unsigned char XCJOIN(Xthal_cp_id_,XCHAL_CP0_IDENT); +extern const unsigned int XCJOIN(Xthal_cp_mask_,XCHAL_CP0_IDENT); +# endif +# ifdef XCHAL_CP1_NAME +extern const unsigned char XCJOIN(Xthal_cp_id_,XCHAL_CP1_IDENT); +extern const unsigned int XCJOIN(Xthal_cp_mask_,XCHAL_CP1_IDENT); +# endif +# ifdef XCHAL_CP2_NAME +extern const unsigned char XCJOIN(Xthal_cp_id_,XCHAL_CP2_IDENT); +extern const unsigned int XCJOIN(Xthal_cp_mask_,XCHAL_CP2_IDENT); +# endif +# ifdef XCHAL_CP3_NAME +extern const unsigned char XCJOIN(Xthal_cp_id_,XCHAL_CP3_IDENT); +extern const unsigned int XCJOIN(Xthal_cp_mask_,XCHAL_CP3_IDENT); +# endif +# ifdef XCHAL_CP4_NAME +extern const unsigned char XCJOIN(Xthal_cp_id_,XCHAL_CP4_IDENT); +extern const unsigned int XCJOIN(Xthal_cp_mask_,XCHAL_CP4_IDENT); +# endif +# ifdef XCHAL_CP5_NAME +extern const unsigned char XCJOIN(Xthal_cp_id_,XCHAL_CP5_IDENT); +extern const unsigned int XCJOIN(Xthal_cp_mask_,XCHAL_CP5_IDENT); +# endif +# ifdef XCHAL_CP6_NAME +extern const unsigned char XCJOIN(Xthal_cp_id_,XCHAL_CP6_IDENT); +extern const unsigned int XCJOIN(Xthal_cp_mask_,XCHAL_CP6_IDENT); +# endif +# ifdef XCHAL_CP7_NAME +extern const unsigned char XCJOIN(Xthal_cp_id_,XCHAL_CP7_IDENT); +extern const unsigned int XCJOIN(Xthal_cp_mask_,XCHAL_CP7_IDENT); +# endif +#endif + + + + +/*---------------------------------------------------------------------- + DERIVED + ----------------------------------------------------------------------*/ + +#if XCHAL_HAVE_BE +#define XCHAL_INST_ILLN 0xD60F /* 2-byte illegal instruction, msb-first */ +#define XCHAL_INST_ILLN_BYTE0 0xD6 /* 2-byte illegal instruction, 1st byte */ +#define XCHAL_INST_ILLN_BYTE1 0x0F /* 2-byte illegal instruction, 2nd byte */ +#else +#define XCHAL_INST_ILLN 0xF06D /* 2-byte illegal instruction, lsb-first */ +#define XCHAL_INST_ILLN_BYTE0 0x6D /* 2-byte illegal instruction, 1st byte */ +#define XCHAL_INST_ILLN_BYTE1 0xF0 /* 2-byte illegal instruction, 2nd byte */ +#endif +/* Belongs in xtensa/hal.h: */ +#define XTHAL_INST_ILL 0x000000 /* 3-byte illegal instruction */ + + +/* + * Because information as to exactly which hardware version is targeted + * by a given software build is not always available, compile-time HAL + * Hardware-Release "_AT" macros are fuzzy (return 0, 1, or XCHAL_MAYBE): + * (Here "RELEASE" is now a misnomer; these are product *versions*, not the releases + * under which they are released. In the T10##.# era there was no distinction.) + */ +#if XCHAL_HW_CONFIGID_RELIABLE +# define XCHAL_HW_RELEASE_AT_OR_BELOW(major,minor) (XTHAL_REL_LE( XCHAL_HW_VERSION_MAJOR,XCHAL_HW_VERSION_MINOR, major,minor ) ? 1 : 0) +# define XCHAL_HW_RELEASE_AT_OR_ABOVE(major,minor) (XTHAL_REL_GE( XCHAL_HW_VERSION_MAJOR,XCHAL_HW_VERSION_MINOR, major,minor ) ? 1 : 0) +# define XCHAL_HW_RELEASE_AT(major,minor) (XTHAL_REL_EQ( XCHAL_HW_VERSION_MAJOR,XCHAL_HW_VERSION_MINOR, major,minor ) ? 1 : 0) +# define XCHAL_HW_RELEASE_MAJOR_AT(major) ((XCHAL_HW_VERSION_MAJOR == (major)) ? 1 : 0) +#else +# define XCHAL_HW_RELEASE_AT_OR_BELOW(major,minor) ( ((major) < 1040 && XCHAL_HAVE_XEA2) ? 0 \ + : ((major) > 1050 && XCHAL_HAVE_XEA1) ? 1 \ + : XTHAL_MAYBE ) +# define XCHAL_HW_RELEASE_AT_OR_ABOVE(major,minor) ( ((major) >= 2000 && XCHAL_HAVE_XEA1) ? 0 \ + : (XTHAL_REL_LE(major,minor, 1040,0) && XCHAL_HAVE_XEA2) ? 1 \ + : XTHAL_MAYBE ) +# define XCHAL_HW_RELEASE_AT(major,minor) ( (((major) < 1040 && XCHAL_HAVE_XEA2) || \ + ((major) >= 2000 && XCHAL_HAVE_XEA1)) ? 0 : XTHAL_MAYBE) +# define XCHAL_HW_RELEASE_MAJOR_AT(major) XCHAL_HW_RELEASE_AT(major,0) +#endif + + +#endif /*XTENSA_CONFIG_CORE_H*/ + diff --git a/components/xtensa/esp32s2beta/include/xtensa/config/defs.h b/components/xtensa/esp32s2beta/include/xtensa/config/defs.h new file mode 100644 index 0000000000..842d0d4dee --- /dev/null +++ b/components/xtensa/esp32s2beta/include/xtensa/config/defs.h @@ -0,0 +1,37 @@ +/* Definitions for Xtensa instructions, types, and protos. */ + +/* Copyright (c) 2003-2004 Tensilica Inc. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +/* NOTE: This file exists only for backward compatibility with T1050 + and earlier Xtensa releases. It includes only a subset of the + available header files. */ + +#ifndef _XTENSA_BASE_HEADER +#define _XTENSA_BASE_HEADER + +#ifdef __XTENSA__ + +#include +#include + +#endif /* __XTENSA__ */ +#endif /* !_XTENSA_BASE_HEADER */ diff --git a/components/xtensa/esp32s2beta/include/xtensa/config/specreg.h b/components/xtensa/esp32s2beta/include/xtensa/config/specreg.h new file mode 100644 index 0000000000..48736efec6 --- /dev/null +++ b/components/xtensa/esp32s2beta/include/xtensa/config/specreg.h @@ -0,0 +1,103 @@ +/* + * Xtensa Special Register symbolic names + */ + +/* $Id: //depot/rel/Foxhill/dot.8/Xtensa/SWConfig/hal/specreg.h.tpp#1 $ */ + +/* Copyright (c) 1998-2002 Tensilica Inc. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef XTENSA_SPECREG_H +#define XTENSA_SPECREG_H + +/* Include these special register bitfield definitions, for historical reasons: */ +#include + + +/* Special registers: */ +#define SAR 3 +#define WINDOWBASE 72 +#define WINDOWSTART 73 +#define IBREAKENABLE 96 +#define DDR 104 +#define IBREAKA_0 128 +#define IBREAKA_1 129 +#define DBREAKA_0 144 +#define DBREAKA_1 145 +#define DBREAKC_0 160 +#define DBREAKC_1 161 +#define EPC_1 177 +#define EPC_2 178 +#define EPC_3 179 +#define EPC_4 180 +#define EPC_5 181 +#define EPC_6 182 +#define EPC_7 183 +#define DEPC 192 +#define EPS_2 194 +#define EPS_3 195 +#define EPS_4 196 +#define EPS_5 197 +#define EPS_6 198 +#define EPS_7 199 +#define EXCSAVE_1 209 +#define EXCSAVE_2 210 +#define EXCSAVE_3 211 +#define EXCSAVE_4 212 +#define EXCSAVE_5 213 +#define EXCSAVE_6 214 +#define EXCSAVE_7 215 +#define CPENABLE 224 +#define INTERRUPT 226 +#define INTENABLE 228 +#define PS 230 +#define VECBASE 231 +#define EXCCAUSE 232 +#define DEBUGCAUSE 233 +#define CCOUNT 234 +#define PRID 235 +#define ICOUNT 236 +#define ICOUNTLEVEL 237 +#define EXCVADDR 238 +#define CCOMPARE_0 240 +#define CCOMPARE_1 241 +#define CCOMPARE_2 242 +#define MISC_REG_0 244 +#define MISC_REG_1 245 +#define MISC_REG_2 246 +#define MISC_REG_3 247 + +/* Special cases (bases of special register series): */ +#define IBREAKA 128 +#define DBREAKA 144 +#define DBREAKC 160 +#define EPC 176 +#define EPS 192 +#define EXCSAVE 208 +#define CCOMPARE 240 + +/* Special names for read-only and write-only interrupt registers: */ +#define INTREAD 226 +#define INTSET 226 +#define INTCLEAR 227 + +#endif /* XTENSA_SPECREG_H */ + diff --git a/components/xtensa/esp32s2beta/include/xtensa/config/system.h b/components/xtensa/esp32s2beta/include/xtensa/config/system.h new file mode 100644 index 0000000000..43a7b8b13b --- /dev/null +++ b/components/xtensa/esp32s2beta/include/xtensa/config/system.h @@ -0,0 +1,277 @@ +/* + * xtensa/config/system.h -- HAL definitions that are dependent on SYSTEM configuration + * + * NOTE: The location and contents of this file are highly subject to change. + * + * Source for configuration-independent binaries (which link in a + * configuration-specific HAL library) must NEVER include this file. + * The HAL itself has historically included this file in some instances, + * but this is not appropriate either, because the HAL is meant to be + * core-specific but system independent. + */ + +/* Copyright (c) 2000-2010 Tensilica Inc. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + + +#ifndef XTENSA_CONFIG_SYSTEM_H +#define XTENSA_CONFIG_SYSTEM_H + +/*#include */ + + + +/*---------------------------------------------------------------------- + CONFIGURED SOFTWARE OPTIONS + ----------------------------------------------------------------------*/ + +#define XSHAL_USE_ABSOLUTE_LITERALS 0 /* (sw-only option, whether software uses absolute literals) */ +#define XSHAL_HAVE_TEXT_SECTION_LITERALS 1 /* Set if there is some memory that allows both code and literals. */ + +#define XSHAL_ABI XTHAL_ABI_WINDOWED /* (sw-only option, selected ABI) */ +/* The above maps to one of the following constants: */ +#define XTHAL_ABI_WINDOWED 0 +#define XTHAL_ABI_CALL0 1 +/* Alternatives: */ +/*#define XSHAL_WINDOWED_ABI 1*/ /* set if windowed ABI selected */ +/*#define XSHAL_CALL0_ABI 0*/ /* set if call0 ABI selected */ + +#define XSHAL_CLIB XTHAL_CLIB_NEWLIB /* (sw-only option, selected C library) */ +/* The above maps to one of the following constants: */ +#define XTHAL_CLIB_NEWLIB 0 +#define XTHAL_CLIB_UCLIBC 1 +#define XTHAL_CLIB_XCLIB 2 +/* Alternatives: */ +/*#define XSHAL_NEWLIB 1*/ /* set if newlib C library selected */ +/*#define XSHAL_UCLIBC 0*/ /* set if uCLibC C library selected */ +/*#define XSHAL_XCLIB 0*/ /* set if Xtensa C library selected */ + +#define XSHAL_USE_FLOATING_POINT 1 + +#define XSHAL_FLOATING_POINT_ABI 0 + +/* SW workarounds enabled for HW errata: */ + +/* SW options for functional safety: */ +#define XSHAL_FUNC_SAFETY_ENABLED 0 + +/*---------------------------------------------------------------------- + DEVICE ADDRESSES + ----------------------------------------------------------------------*/ + +/* + * Strange place to find these, but the configuration GUI + * allows moving these around to account for various core + * configurations. Specific boards (and their BSP software) + * will have specific meanings for these components. + */ + +/* I/O Block areas: */ +#define XSHAL_IOBLOCK_CACHED_VADDR 0x70000000 +#define XSHAL_IOBLOCK_CACHED_PADDR 0x70000000 +#define XSHAL_IOBLOCK_CACHED_SIZE 0x0E000000 + +#define XSHAL_IOBLOCK_BYPASS_VADDR 0x90000000 +#define XSHAL_IOBLOCK_BYPASS_PADDR 0x90000000 +#define XSHAL_IOBLOCK_BYPASS_SIZE 0x0E000000 + +/* System ROM: */ +#define XSHAL_ROM_VADDR 0x50000000 +#define XSHAL_ROM_PADDR 0x50000000 +#define XSHAL_ROM_SIZE 0x01000000 +/* Largest available area (free of vectors): */ +#define XSHAL_ROM_AVAIL_VADDR 0x50000000 +#define XSHAL_ROM_AVAIL_VSIZE 0x01000000 + +/* System RAM: */ +#define XSHAL_RAM_VADDR 0x60000000 +#define XSHAL_RAM_PADDR 0x60000000 +#define XSHAL_RAM_VSIZE 0x20000000 +#define XSHAL_RAM_PSIZE 0x20000000 +#define XSHAL_RAM_SIZE XSHAL_RAM_PSIZE +/* Largest available area (free of vectors): */ +#define XSHAL_RAM_AVAIL_VADDR 0x60000000 +#define XSHAL_RAM_AVAIL_VSIZE 0x20000000 + +/* + * Shadow system RAM (same device as system RAM, at different address). + * (Emulation boards need this for the SONIC Ethernet driver + * when data caches are configured for writeback mode.) + * NOTE: on full MMU configs, this points to the BYPASS virtual address + * of system RAM, ie. is the same as XSHAL_RAM_* except that virtual + * addresses are viewed through the BYPASS static map rather than + * the CACHED static map. + */ +#define XSHAL_RAM_BYPASS_VADDR 0xA0000000 +#define XSHAL_RAM_BYPASS_PADDR 0xA0000000 +#define XSHAL_RAM_BYPASS_PSIZE 0x20000000 + +/* Alternate system RAM (different device than system RAM): */ +/*#define XSHAL_ALTRAM_[VP]ADDR ...not configured...*/ +/*#define XSHAL_ALTRAM_SIZE ...not configured...*/ + +/* Some available location in which to place devices in a simulation (eg. XTMP): */ +#define XSHAL_SIMIO_CACHED_VADDR 0xC0000000 +#define XSHAL_SIMIO_BYPASS_VADDR 0xC0000000 +#define XSHAL_SIMIO_PADDR 0xC0000000 +#define XSHAL_SIMIO_SIZE 0x20000000 + + +/*---------------------------------------------------------------------- + * For use by reference testbench exit and diagnostic routines. + */ +#define XSHAL_MAGIC_EXIT 0x0 + +/*---------------------------------------------------------------------- + * DEVICE-ADDRESS DEPENDENT... + * + * Values written to CACHEATTR special register (or its equivalent) + * to enable and disable caches in various modes. + *----------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------- + BACKWARD COMPATIBILITY ... + ----------------------------------------------------------------------*/ + +/* + * NOTE: the following two macros are DEPRECATED. Use the latter + * board-specific macros instead, which are specially tuned for the + * particular target environments' memory maps. + */ +#define XSHAL_CACHEATTR_BYPASS XSHAL_XT2000_CACHEATTR_BYPASS /* disable caches in bypass mode */ +#define XSHAL_CACHEATTR_DEFAULT XSHAL_XT2000_CACHEATTR_DEFAULT /* default setting to enable caches (no writeback!) */ + +/*---------------------------------------------------------------------- + GENERIC + ----------------------------------------------------------------------*/ + +/* For the following, a 512MB region is used if it contains a system (PIF) RAM, + * system (PIF) ROM, local memory, or XLMI. */ + +/* These set any unused 512MB region to cache-BYPASS attribute: */ +#define XSHAL_ALLVALID_CACHEATTR_WRITEBACK 0x22221112 /* enable caches in write-back mode */ +#define XSHAL_ALLVALID_CACHEATTR_WRITEALLOC 0x22221112 /* enable caches in write-allocate mode */ +#define XSHAL_ALLVALID_CACHEATTR_WRITETHRU 0x22221112 /* enable caches in write-through mode */ +#define XSHAL_ALLVALID_CACHEATTR_BYPASS 0x22222222 /* disable caches in bypass mode */ +#define XSHAL_ALLVALID_CACHEATTR_DEFAULT XSHAL_ALLVALID_CACHEATTR_WRITEBACK /* default setting to enable caches */ + +/* These set any unused 512MB region to ILLEGAL attribute: */ +#define XSHAL_STRICT_CACHEATTR_WRITEBACK 0xFFFF111F /* enable caches in write-back mode */ +#define XSHAL_STRICT_CACHEATTR_WRITEALLOC 0xFFFF111F /* enable caches in write-allocate mode */ +#define XSHAL_STRICT_CACHEATTR_WRITETHRU 0xFFFF111F /* enable caches in write-through mode */ +#define XSHAL_STRICT_CACHEATTR_BYPASS 0xFFFF222F /* disable caches in bypass mode */ +#define XSHAL_STRICT_CACHEATTR_DEFAULT XSHAL_STRICT_CACHEATTR_WRITEBACK /* default setting to enable caches */ + +/* These set the first 512MB, if unused, to ILLEGAL attribute to help catch + * NULL-pointer dereference bugs; all other unused 512MB regions are set + * to cache-BYPASS attribute: */ +#define XSHAL_TRAPNULL_CACHEATTR_WRITEBACK 0x2222111F /* enable caches in write-back mode */ +#define XSHAL_TRAPNULL_CACHEATTR_WRITEALLOC 0x2222111F /* enable caches in write-allocate mode */ +#define XSHAL_TRAPNULL_CACHEATTR_WRITETHRU 0x2222111F /* enable caches in write-through mode */ +#define XSHAL_TRAPNULL_CACHEATTR_BYPASS 0x2222222F /* disable caches in bypass mode */ +#define XSHAL_TRAPNULL_CACHEATTR_DEFAULT XSHAL_TRAPNULL_CACHEATTR_WRITEBACK /* default setting to enable caches */ + +/*---------------------------------------------------------------------- + ISS (Instruction Set Simulator) SPECIFIC ... + ----------------------------------------------------------------------*/ + +/* For now, ISS defaults to the TRAPNULL settings: */ +#define XSHAL_ISS_CACHEATTR_WRITEBACK XSHAL_TRAPNULL_CACHEATTR_WRITEBACK +#define XSHAL_ISS_CACHEATTR_WRITEALLOC XSHAL_TRAPNULL_CACHEATTR_WRITEALLOC +#define XSHAL_ISS_CACHEATTR_WRITETHRU XSHAL_TRAPNULL_CACHEATTR_WRITETHRU +#define XSHAL_ISS_CACHEATTR_BYPASS XSHAL_TRAPNULL_CACHEATTR_BYPASS +#define XSHAL_ISS_CACHEATTR_DEFAULT XSHAL_TRAPNULL_CACHEATTR_WRITEBACK + +#define XSHAL_ISS_PIPE_REGIONS 0 +#define XSHAL_ISS_SDRAM_REGIONS 0 + + +/*---------------------------------------------------------------------- + XT2000 BOARD SPECIFIC ... + ----------------------------------------------------------------------*/ + +/* For the following, a 512MB region is used if it contains any system RAM, + * system ROM, local memory, XLMI, or other XT2000 board device or memory. + * Regions containing devices are forced to cache-BYPASS mode regardless + * of whether the macro is _WRITEBACK vs. _BYPASS etc. */ + +/* These set any 512MB region unused on the XT2000 to ILLEGAL attribute: */ +#define XSHAL_XT2000_CACHEATTR_WRITEBACK 0xFF22111F /* enable caches in write-back mode */ +#define XSHAL_XT2000_CACHEATTR_WRITEALLOC 0xFF22111F /* enable caches in write-allocate mode */ +#define XSHAL_XT2000_CACHEATTR_WRITETHRU 0xFF22111F /* enable caches in write-through mode */ +#define XSHAL_XT2000_CACHEATTR_BYPASS 0xFF22222F /* disable caches in bypass mode */ +#define XSHAL_XT2000_CACHEATTR_DEFAULT XSHAL_XT2000_CACHEATTR_WRITEBACK /* default setting to enable caches */ + +#define XSHAL_XT2000_PIPE_REGIONS 0x00000000 /* BusInt pipeline regions */ +#define XSHAL_XT2000_SDRAM_REGIONS 0x00000440 /* BusInt SDRAM regions */ + + +/*---------------------------------------------------------------------- + VECTOR INFO AND SIZES + ----------------------------------------------------------------------*/ + +#define XSHAL_VECTORS_PACKED 0 +#define XSHAL_STATIC_VECTOR_SELECT 1 +#define XSHAL_RESET_VECTOR_VADDR 0x40000400 +#define XSHAL_RESET_VECTOR_PADDR 0x40000400 + +/* + * Sizes allocated to vectors by the system (memory map) configuration. + * These sizes are constrained by core configuration (eg. one vector's + * code cannot overflow into another vector) but are dependent on the + * system or board (or LSP) memory map configuration. + * + * Whether or not each vector happens to be in a system ROM is also + * a system configuration matter, sometimes useful, included here also: + */ +#define XSHAL_RESET_VECTOR_SIZE 0x00000300 +#define XSHAL_RESET_VECTOR_ISROM 0 +#define XSHAL_USER_VECTOR_SIZE 0x00000038 +#define XSHAL_USER_VECTOR_ISROM 0 +#define XSHAL_PROGRAMEXC_VECTOR_SIZE XSHAL_USER_VECTOR_SIZE /* for backward compatibility */ +#define XSHAL_USEREXC_VECTOR_SIZE XSHAL_USER_VECTOR_SIZE /* for backward compatibility */ +#define XSHAL_KERNEL_VECTOR_SIZE 0x00000038 +#define XSHAL_KERNEL_VECTOR_ISROM 0 +#define XSHAL_STACKEDEXC_VECTOR_SIZE XSHAL_KERNEL_VECTOR_SIZE /* for backward compatibility */ +#define XSHAL_KERNELEXC_VECTOR_SIZE XSHAL_KERNEL_VECTOR_SIZE /* for backward compatibility */ +#define XSHAL_DOUBLEEXC_VECTOR_SIZE 0x00000040 +#define XSHAL_DOUBLEEXC_VECTOR_ISROM 0 +#define XSHAL_WINDOW_VECTORS_SIZE 0x00000178 +#define XSHAL_WINDOW_VECTORS_ISROM 0 +#define XSHAL_INTLEVEL2_VECTOR_SIZE 0x00000038 +#define XSHAL_INTLEVEL2_VECTOR_ISROM 0 +#define XSHAL_INTLEVEL3_VECTOR_SIZE 0x00000038 +#define XSHAL_INTLEVEL3_VECTOR_ISROM 0 +#define XSHAL_INTLEVEL4_VECTOR_SIZE 0x00000038 +#define XSHAL_INTLEVEL4_VECTOR_ISROM 0 +#define XSHAL_INTLEVEL5_VECTOR_SIZE 0x00000038 +#define XSHAL_INTLEVEL5_VECTOR_ISROM 0 +#define XSHAL_INTLEVEL6_VECTOR_SIZE 0x00000038 +#define XSHAL_INTLEVEL6_VECTOR_ISROM 0 +#define XSHAL_DEBUG_VECTOR_SIZE XSHAL_INTLEVEL6_VECTOR_SIZE +#define XSHAL_DEBUG_VECTOR_ISROM XSHAL_INTLEVEL6_VECTOR_ISROM +#define XSHAL_NMI_VECTOR_SIZE 0x00000038 +#define XSHAL_NMI_VECTOR_ISROM 0 +#define XSHAL_INTLEVEL7_VECTOR_SIZE XSHAL_NMI_VECTOR_SIZE + + +#endif /*XTENSA_CONFIG_SYSTEM_H*/ + diff --git a/components/xtensa/esp32s2beta/include/xtensa/config/tie-asm.h b/components/xtensa/esp32s2beta/include/xtensa/config/tie-asm.h new file mode 100644 index 0000000000..0da6fe7efa --- /dev/null +++ b/components/xtensa/esp32s2beta/include/xtensa/config/tie-asm.h @@ -0,0 +1,130 @@ +/* + * tie-asm.h -- compile-time HAL assembler definitions dependent on CORE & TIE + * + * NOTE: This header file is not meant to be included directly. + */ + +/* This header file contains assembly-language definitions (assembly + macros, etc.) for this specific Xtensa processor's TIE extensions + and options. It is customized to this Xtensa processor configuration. + + Copyright (c) 1999-2018 Cadence Design Systems Inc. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _XTENSA_CORE_TIE_ASM_H +#define _XTENSA_CORE_TIE_ASM_H + +/* Selection parameter values for save-area save/restore macros: */ +/* Option vs. TIE: */ +#define XTHAL_SAS_TIE 0x0001 /* custom extension or coprocessor */ +#define XTHAL_SAS_OPT 0x0002 /* optional (and not a coprocessor) */ +#define XTHAL_SAS_ANYOT 0x0003 /* both of the above */ +/* Whether used automatically by compiler: */ +#define XTHAL_SAS_NOCC 0x0004 /* not used by compiler w/o special opts/code */ +#define XTHAL_SAS_CC 0x0008 /* used by compiler without special opts/code */ +#define XTHAL_SAS_ANYCC 0x000C /* both of the above */ +/* ABI handling across function calls: */ +#define XTHAL_SAS_CALR 0x0010 /* caller-saved */ +#define XTHAL_SAS_CALE 0x0020 /* callee-saved */ +#define XTHAL_SAS_GLOB 0x0040 /* global across function calls (in thread) */ +#define XTHAL_SAS_ANYABI 0x0070 /* all of the above three */ +/* Misc */ +#define XTHAL_SAS_ALL 0xFFFF /* include all default NCP contents */ +#define XTHAL_SAS3(optie,ccuse,abi) ( ((optie) & XTHAL_SAS_ANYOT) \ + | ((ccuse) & XTHAL_SAS_ANYCC) \ + | ((abi) & XTHAL_SAS_ANYABI) ) + + + /* + * Macro to store all non-coprocessor (extra) custom TIE and optional state + * (not including zero-overhead loop registers). + * Required parameters: + * ptr Save area pointer address register (clobbered) + * (register must contain a 4 byte aligned address). + * at1..at4 Four temporary address registers (first XCHAL_NCP_NUM_ATMPS + * registers are clobbered, the remaining are unused). + * Optional parameters: + * continue If macro invoked as part of a larger store sequence, set to 1 + * if this is not the first in the sequence. Defaults to 0. + * ofs Offset from start of larger sequence (from value of first ptr + * in sequence) at which to store. Defaults to next available space + * (or 0 if is 0). + * select Select what category(ies) of registers to store, as a bitmask + * (see XTHAL_SAS_xxx constants). Defaults to all registers. + * alloc Select what category(ies) of registers to allocate; if any + * category is selected here that is not in , space for + * the corresponding registers is skipped without doing any load. + */ + .macro xchal_ncp_load ptr at1 at2 at3 at4 continue=0 ofs=-1 select=XTHAL_SAS_ALL alloc=0 + xchal_sa_start \continue, \ofs + // Optional global registers used by default by the compiler: + .ifeq (XTHAL_SAS_OPT | XTHAL_SAS_CC | XTHAL_SAS_GLOB) & ~(\select) + xchal_sa_align \ptr, 0, 1016, 4, 4 + l32i \at1, \ptr, .Lxchal_ofs_+0 + wur.THREADPTR \at1 // threadptr option + .set .Lxchal_ofs_, .Lxchal_ofs_ + 4 + .elseif ((XTHAL_SAS_OPT | XTHAL_SAS_CC | XTHAL_SAS_GLOB) & ~(\alloc)) == 0 + xchal_sa_align \ptr, 0, 1016, 4, 4 + .set .Lxchal_ofs_, .Lxchal_ofs_ + 4 + .endif + .endm // xchal_ncp_load + + +#define XCHAL_NCP_NUM_ATMPS 1 + +#define XCHAL_SA_NUM_ATMPS 1 + +#endif /*_XTENSA_CORE_TIE_ASM_H*/ + diff --git a/components/xtensa/esp32s2beta/include/xtensa/config/tie.h b/components/xtensa/esp32s2beta/include/xtensa/config/tie.h new file mode 100644 index 0000000000..d117631997 --- /dev/null +++ b/components/xtensa/esp32s2beta/include/xtensa/config/tie.h @@ -0,0 +1,130 @@ +/* + * tie.h -- compile-time HAL definitions dependent on CORE & TIE configuration + * + * NOTE: This header file is not meant to be included directly. + */ + +/* This header file describes this specific Xtensa processor's TIE extensions + that extend basic Xtensa core functionality. It is customized to this + Xtensa processor configuration. + + Copyright (c) 1999-2018 Cadence Design Systems Inc. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _XTENSA_CORE_TIE_H +#define _XTENSA_CORE_TIE_H + +#define XCHAL_CP_NUM 0 /* number of coprocessors */ +#define XCHAL_CP_MAX 0 /* max CP ID + 1 (0 if none) */ +#define XCHAL_CP_MASK 0x00 /* bitmask of all CPs by ID */ +#define XCHAL_CP_PORT_MASK 0x00 /* bitmask of only port CPs */ + +/* Save area for non-coprocessor optional and custom (TIE) state: */ +#define XCHAL_NCP_SA_SIZE 4 +#define XCHAL_NCP_SA_ALIGN 4 + +/* Total save area for optional and custom state (NCP + CPn): */ +#define XCHAL_TOTAL_SA_SIZE 16 /* with 16-byte align padding */ +#define XCHAL_TOTAL_SA_ALIGN 4 /* actual minimum alignment */ + +/* + * Detailed contents of save areas. + * NOTE: caller must define the XCHAL_SA_REG macro (not defined here) + * before expanding the XCHAL_xxx_SA_LIST() macros. + * + * XCHAL_SA_REG(s,ccused,abikind,kind,opt,name,galign,align,asize, + * dbnum,base,regnum,bitsz,gapsz,reset,x...) + * + * s = passed from XCHAL_*_LIST(s), eg. to select how to expand + * ccused = set if used by compiler without special options or code + * abikind = 0 (caller-saved), 1 (callee-saved), or 2 (thread-global) + * kind = 0 (special reg), 1 (TIE user reg), or 2 (TIE regfile reg) + * opt = 0 (custom TIE extension or coprocessor), or 1 (optional reg) + * name = lowercase reg name (no quotes) + * galign = group byte alignment (power of 2) (galign >= align) + * align = register byte alignment (power of 2) + * asize = allocated size in bytes (asize*8 == bitsz + gapsz + padsz) + * (not including any pad bytes required to galign this or next reg) + * dbnum = unique target number f/debug (see ) + * base = reg shortname w/o index (or sr=special, ur=TIE user reg) + * regnum = reg index in regfile, or special/TIE-user reg number + * bitsz = number of significant bits (regfile width, or ur/sr mask bits) + * gapsz = intervening bits, if bitsz bits not stored contiguously + * (padsz = pad bits at end [TIE regfile] or at msbits [ur,sr] of asize) + * reset = register reset value (or 0 if undefined at reset) + * x = reserved for future use (0 until then) + * + * To filter out certain registers, e.g. to expand only the non-global + * registers used by the compiler, you can do something like this: + * + * #define XCHAL_SA_REG(s,ccused,p...) SELCC##ccused(p) + * #define SELCC0(p...) + * #define SELCC1(abikind,p...) SELAK##abikind(p) + * #define SELAK0(p...) REG(p) + * #define SELAK1(p...) REG(p) + * #define SELAK2(p...) + * #define REG(kind,tie,name,galn,aln,asz,csz,dbnum,base,rnum,bsz,rst,x...) \ + * ...what you want to expand... + */ + +#define XCHAL_NCP_SA_NUM 1 +#define XCHAL_NCP_SA_LIST(s) \ + XCHAL_SA_REG(s,1,2,1,1, threadptr, 4, 4, 4,0x03E7, ur,231, 32,0,0,0) + +#define XCHAL_CP0_SA_NUM 0 +#define XCHAL_CP0_SA_LIST(s) /* empty */ + +#define XCHAL_CP1_SA_NUM 0 +#define XCHAL_CP1_SA_LIST(s) /* empty */ + +#define XCHAL_CP2_SA_NUM 0 +#define XCHAL_CP2_SA_LIST(s) /* empty */ + +#define XCHAL_CP3_SA_NUM 0 +#define XCHAL_CP3_SA_LIST(s) /* empty */ + +#define XCHAL_CP4_SA_NUM 0 +#define XCHAL_CP4_SA_LIST(s) /* empty */ + +#define XCHAL_CP5_SA_NUM 0 +#define XCHAL_CP5_SA_LIST(s) /* empty */ + +#define XCHAL_CP6_SA_NUM 0 +#define XCHAL_CP6_SA_LIST(s) /* empty */ + +#define XCHAL_CP7_SA_NUM 0 +#define XCHAL_CP7_SA_LIST(s) /* empty */ + +/* Byte length of instruction from its first nibble (op0 field), per FLIX. */ +#define XCHAL_OP0_FORMAT_LENGTHS 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3 +/* Byte length of instruction from its first byte, per FLIX. */ +#define XCHAL_BYTE0_FORMAT_LENGTHS \ + 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3, 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3,\ + 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3, 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3,\ + 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3, 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3,\ + 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3, 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3,\ + 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3, 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3,\ + 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3, 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3,\ + 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3, 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3,\ + 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3, 3,3,3,3,3,3,3,3,2,2,2,2,2,2,3,3 + +#endif /*_XTENSA_CORE_TIE_H*/ + diff --git a/components/xtensa/esp32s2beta/libhal.a b/components/xtensa/esp32s2beta/libhal.a new file mode 100644 index 0000000000000000000000000000000000000000..42a19a23fab95d1d4a55f812992528bce57bff57 GIT binary patch literal 517178 zcmY$iNi0gvu;bEKKm~@T#-`?$7N&;A3JL~bDP&SX!P3Oo1T3tOz{SA8n9aao&qg}- zb7NpA_F`bDVME}ySqu#5cytj&hVH8s@4Bmt>FuVz4U}QK4!5pg?7-3j? z7Xu?UY_7(@Xcxr5=$Z$?KF1gsK{!N+fic>NfiWqGfibg>fw5o}17rCu2F7|m2F5l$ z2F6}52F59E42-ikF)%KVV_@9C#K5>?9RuURUkr?=%@`Ojr7rg{&iwtVku)_;-1C8B(#fxN%9>7lOi7jlZF)olYSTjlUW-BlkFk~CYMVL zOg?N3OuGdOc`AaO!?~=n92?@Fx4?JFtr*%aPKPyCJ>%Bje%*t76a4r zbqq|awlOfR-N(SR2=#y0MRe=}8s?)7xzfOy52+F#S_v zU}kk0s;V3yj%z^r(VfmxG@f!RQbf!W-Tf!QvLf!TEy1GCRI2Ik;<49rpA z7?_jv7?|_Z7?_Ll7??}T7?^9eF)%m(VqospV_=>X$G|+th=F=(LBY#Vmgn3#m0?+#pxac zi#HPkORyLNOXNNVmV__{mb8BiEV5$K#K797$G|%C5CiMtYYeQbsu)-|$}zBR-^9Rr$c{98 z!;69Su^0ruPh(&O;a{H^*jVN`)cIOuZ+v7e4wpT(7Y#){}uzlBJ zVEem|ft|&Pft~9b1G``p1H1S;26nk326k0826pW(26iJQ26l^O4D9xP4D7DY7}$Nv z7}$fpF|bFrF|a4FF|emiV_?q}V_+{{#=u^s#=zdNje))0jDfxP7z6thHwN}u*BICr zhB2@&f5yPRE{%bG%P$7@-E|D?htwF@PwZk~KVQbceocvi{jOO=Nk(E$d`fa+az<)= zPG(+ed`^CHc4AUaDg#6+6SovpS7vd1c~NFbYEoiyHdGT-rZ}?-%@`yRsG9P`%3`Q4 zs8vWJP&LJ=CFPkZB^jvd&_tnXL4H881!^XmD3V$%27?qsge$QgVvo^U9%8Ny+&Il~6W_X=r2&<>jX4CKpsfqXA?! z+&2)*GZM>EVP1xaROIAl#v{cCm|u{Xl2QaU1uRg8B#@F=l30|Oi>wJIgscxHhM_ed zQ)_;1Jcic%+<0WInR&$}Xtu(HkhQ|ZFtnoD3RZ?@D@+VoD<}v-0RRmP5Etq%bdmgA zBoVMti5MbqH7O-IN%4tAY4ONe2_%MvAD@(20?&XTHE8-WL1IYyV0>hKxw)nBxtV$K z1tqER1&QgY$Z-djORT^tmz;=hQgIQwNKs~9IyNzwyFqp&7H6i!XQvkBrRG3G3NDJ| zLvR8u$w`9g1oOd}7?iW28o`1UIVJH}C2})!Gn27PWu~O&m1LGw#^)v$Ae&WOkeHVT z@aLG(Dgn7CgTHa))7(j)RN;7j(;!E=~ z3rkbuGgF|Vpac)8J4(~iQj6kK^Gb>`Q&A$PATtfB7#uAn8AYjyDFr3)AOV#)#l}X- zhMCC9U=A&=%u7ZVFDQcB4K^+>7e!k}W_m_WYFTOyBvOh>3*dPJtQ;Iw$f6agiLfw7 zwkEYAIkli9Gd~Z-E5!w=$)!1oAaP{HX((wWw=~Dl3|TriF&R}LIVUl<0M+2siV_qp znR&U1704oa#fiw=ocw%L6O!`tb5avgotu)HSDaY_Pq3hXF3-$M$uCcZMPF`eZhlc^ ze11_%Dl9%gF`8JEnqC~Alb>z`wG%D~R7mqwftAWeA8 z%tOdQ8)=C-pxg)xL#TaN<*;fk%>y-PF||R&v1);oNTAj%BtDQVgGIOmAft$EW6j`UCKWBP1Ol z#4(%(YCnSvf+l-ZOVK2;YsQQ+ur`n|b`6l |Xf0=p(0!2-4lE(h`m7IRVL2^fzd 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 C)P%qQ literal 0 HcmV?d00001