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 371023789..3620f4379 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 000000000..d4eed8a23 --- /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 000000000..a55e23fd5 --- /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 000000000..9f92c377d --- /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 000000000..a48dd4b8e --- /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 000000000..b57eb157b --- /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 000000000..9fefa402a --- /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 000000000..a2f7dc179 --- /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 000000000..99f92843c --- /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 000000000..3bfcc60bf --- /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 000000000..a329ec7d5 --- /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 000000000..38d87c0d8 --- /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 000000000..b2c641849 --- /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 000000000..5a0b1aec0 --- /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 000000000..bcbd63e79 --- /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 000000000..6526aa927 --- /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 000000000..d5da5b8d8 --- /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 000000000..a7cbf0eac --- /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 000000000..9663dcddc --- /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 000000000..5bf9a2292 --- /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 000000000..6526aa927 --- /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 000000000..579eb6353 --- /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 000000000..8e50c7e20 --- /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 000000000..9663dcddc --- /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 000000000..02893ff41 --- /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 000000000..34a10d66c --- /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 000000000..e6f4f10fe --- /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 000000000..8195d2320 --- /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 000000000..077d8d0cd --- /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 000000000..af516099c --- /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 000000000..b0d99f221 --- /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 000000000..3109faf11 --- /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 000000000..64b960c6f --- /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 000000000..b4cebf85f --- /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 000000000..7820b2d8f --- /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 000000000..3d421d04d --- /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 000000000..d1ba2ca2b --- /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 000000000..e3a37d479 --- /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 b36b5754f..bd990c8fc 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 000000000..9384b12a7 --- /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 000000000..e31beaa3c --- /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 000000000..f2c799988 --- /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 000000000..f5bb44faf --- /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 000000000..842d0d4de --- /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 000000000..48736efec --- /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 000000000..43a7b8b13 --- /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 000000000..0da6fe7ef --- /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 000000000..d11763199 --- /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 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 D3)F