diff --git a/.gitlab/ci/target-test.yml b/.gitlab/ci/target-test.yml index 1c00fdfc49..6bd24dcf71 100644 --- a/.gitlab/ci/target-test.yml +++ b/.gitlab/ci/target-test.yml @@ -374,6 +374,12 @@ test_app_test_005: - ESP32C3 - Example_GENERIC +test_app_test_006: + extends: .test_app_esp32s3_template + tags: + - ESP32S3 + - Example_GENERIC + test_app_test_esp32_generic: extends: .test_app_esp32_template parallel: 5 diff --git a/components/bootloader_support/src/bootloader_init.c b/components/bootloader_support/src/bootloader_init.c index 932cac265c..3fc18415d7 100644 --- a/components/bootloader_support/src/bootloader_init.c +++ b/components/bootloader_support/src/bootloader_init.c @@ -94,4 +94,12 @@ void bootloader_print_banner(void) { ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER); ESP_LOGI(TAG, "compile time " __TIME__); + +#if CONFIG_FREERTOS_UNICORE +#if (SOC_CPU_CORES_NUM > 1) + ESP_EARLY_LOGW(TAG, "Unicore bootloader"); +#endif +#else + ESP_EARLY_LOGI(TAG, "Multicore bootloader"); +#endif } diff --git a/components/esp_system/port/cpu_start.c b/components/esp_system/port/cpu_start.c index 71287988ba..4fe84665a1 100644 --- a/components/esp_system/port/cpu_start.c +++ b/components/esp_system/port/cpu_start.c @@ -70,6 +70,10 @@ #include "hal/wdt_hal.h" #include "soc/rtc.h" #include "soc/efuse_reg.h" +#if (SOC_CPU_CORES_NUM > 1) +#include "hal/cache_ll.h" +#endif +#include "hal/efuse_ll.h" #include "soc/periph_defs.h" #include "soc/cpu.h" #include "soc/rtc.h" @@ -244,6 +248,39 @@ static void start_other_core(void) esp_rom_delay_us(100); } } + +// This function is needed to make the multicore app runnable on a unicore bootloader (built with FREERTOS UNICORE). +// It does some cache settings for other CPUs. +void IRAM_ATTR do_multicore_settings(void) +{ + // We intentionally do not check the cache settings before changing them, + // because it helps to get the application to run on older bootloaders. +#ifdef CONFIG_IDF_TARGET_ESP32 + if (!efuse_ll_get_disable_app_cpu()) { + Cache_Read_Disable(1); + Cache_Flush(1); + DPORT_REG_SET_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR); + DPORT_REG_CLR_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR); + // We do not enable cache for CPU1 now because it will be done later in start_other_core(). + } +#endif + + cache_bus_mask_t cache_bus_mask_core0 = cache_ll_l1_get_enabled_bus(0); +#ifndef CONFIG_IDF_TARGET_ESP32 + // 1. disable the cache before changing its settings. + Cache_Disable_ICache(); + Cache_Disable_DCache(); +#endif + for (unsigned core = 1; core < SOC_CPU_CORES_NUM; core++) { + // 2. change cache settings. All cores must have the same settings. + cache_ll_l1_enable_bus(core, cache_bus_mask_core0); + } +#ifndef CONFIG_IDF_TARGET_ESP32 + // 3. enable the cache after changing its settings. + Cache_Enable_ICache(0); + Cache_Enable_DCache(0); +#endif +} #endif // !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE /* @@ -312,6 +349,14 @@ void IRAM_ATTR call_start_cpu0(void) memset(&_rtc_bss_start, 0, (&_rtc_bss_end - &_rtc_bss_start) * sizeof(_rtc_bss_start)); } +#if CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE + ESP_EARLY_LOGI(TAG, "Unicore app"); +#else + ESP_EARLY_LOGI(TAG, "Multicore app"); + // It helps to fix missed cache settings for other cores. It happens when bootloader is unicore. + do_multicore_settings(); +#endif + #if CONFIG_IDF_TARGET_ESP32S2 /* Configure the mode of instruction cache : cache size, cache associated ways, cache line size. */ extern void esp_config_instruction_cache_mode(void); diff --git a/components/hal/esp32/include/hal/cache_ll.h b/components/hal/esp32/include/hal/cache_ll.h new file mode 100644 index 0000000000..ec7dd87d94 --- /dev/null +++ b/components/hal/esp32/include/hal/cache_ll.h @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The LL layer for Cache register operations + +#pragma once + +#include +#include "soc/dport_reg.h" +#include "hal/cache_types.h" +#include "hal/assert.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Enable the Cache Buses + * + * @param cache_id cache ID (when l1 cache is per core) + * @param mask To know which buses should be enabled + * @param enable 1: enable; 0: disable + */ +#if !BOOTLOADER_BUILD +__attribute__((always_inline)) +#endif +static inline void cache_ll_l1_enable_bus(uint32_t cache_id, cache_bus_mask_t mask) +{ + (void) mask; + HAL_ASSERT(cache_id == 0 || cache_id == 1); + + uint32_t bus_mask = 0; + if (cache_id == 0) { + bus_mask |= (mask & CACHE_BUS_IBUS0) ? DPORT_PRO_CACHE_MASK_IRAM0 : 0; + bus_mask |= (mask & CACHE_BUS_IBUS1) ? DPORT_PRO_CACHE_MASK_IRAM1 : 0; + bus_mask |= (mask & CACHE_BUS_IBUS2) ? DPORT_PRO_CACHE_MASK_IROM0 : 0; + + bus_mask |= (mask & CACHE_BUS_DBUS0) ? DPORT_PRO_CACHE_MASK_DROM0 : 0; + bus_mask |= (mask & CACHE_BUS_DBUS1) ? DPORT_PRO_CACHE_MASK_DRAM1 : 0; + + DPORT_REG_CLR_BIT(DPORT_PRO_CACHE_CTRL1_REG, bus_mask); + } else { + bus_mask |= (mask & CACHE_BUS_IBUS0) ? DPORT_APP_CACHE_MASK_IRAM0 : 0; + bus_mask |= (mask & CACHE_BUS_IBUS1) ? DPORT_APP_CACHE_MASK_IRAM1 : 0; + bus_mask |= (mask & CACHE_BUS_IBUS2) ? DPORT_APP_CACHE_MASK_IROM0 : 0; + + bus_mask |= (mask & CACHE_BUS_DBUS0) ? DPORT_APP_CACHE_MASK_DROM0 : 0; + bus_mask |= (mask & CACHE_BUS_DBUS1) ? DPORT_APP_CACHE_MASK_DRAM1 : 0; + + DPORT_REG_CLR_BIT(DPORT_APP_CACHE_CTRL1_REG, bus_mask); + } +} + +/** + * Returns enabled buses for a given core + * + * @param cache_id cache ID (when l1 cache is per core) + * + * @return State of enabled buses + */ +__attribute__((always_inline)) +static inline cache_bus_mask_t cache_ll_l1_get_enabled_bus(uint32_t cache_id) +{ + cache_bus_mask_t mask = 0; + HAL_ASSERT(cache_id == 0 || cache_id == 1); + if (cache_id == 0) { + uint32_t bus_mask= DPORT_REG_READ(DPORT_PRO_CACHE_CTRL1_REG); + mask |= (!(bus_mask & DPORT_PRO_CACHE_MASK_IRAM0)) ? CACHE_BUS_IBUS0 : 0; + mask |= (!(bus_mask & DPORT_PRO_CACHE_MASK_IRAM1)) ? CACHE_BUS_IBUS1 : 0; + mask |= (!(bus_mask & DPORT_PRO_CACHE_MASK_IROM0)) ? CACHE_BUS_IBUS2 : 0; + + mask |= (!(bus_mask & DPORT_PRO_CACHE_MASK_DROM0)) ? CACHE_BUS_DBUS0 : 0; + mask |= (!(bus_mask & DPORT_PRO_CACHE_MASK_DRAM1)) ? CACHE_BUS_DBUS1 : 0; + } else { + uint32_t bus_mask= DPORT_REG_READ(DPORT_APP_CACHE_CTRL1_REG); + mask |= (!(bus_mask & DPORT_APP_CACHE_MASK_IRAM0)) ? CACHE_BUS_IBUS0 : 0; + mask |= (!(bus_mask & DPORT_APP_CACHE_MASK_IRAM1)) ? CACHE_BUS_IBUS1 : 0; + mask |= (!(bus_mask & DPORT_APP_CACHE_MASK_IROM0)) ? CACHE_BUS_IBUS2 : 0; + + mask |= (!(bus_mask & DPORT_APP_CACHE_MASK_DROM0)) ? CACHE_BUS_DBUS0 : 0; + mask |= (!(bus_mask & DPORT_APP_CACHE_MASK_DRAM1)) ? CACHE_BUS_DBUS1 : 0; + } + return mask; +} + +/** + * Disable the Cache Buses + * + * @param cache_id cache ID (when l1 cache is per core) + * @param mask To know which buses should be enabled + * @param enable 1: enable; 0: disable + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_disable_bus(uint32_t cache_id, cache_bus_mask_t mask) +{ + (void) mask; + HAL_ASSERT(cache_id == 0 || cache_id == 1); + + uint32_t bus_mask = 0; + if (cache_id == 0) { + bus_mask |= (mask & CACHE_BUS_IBUS0) ? DPORT_PRO_CACHE_MASK_IRAM0 : 0; + bus_mask |= (mask & CACHE_BUS_IBUS1) ? DPORT_PRO_CACHE_MASK_IRAM1 : 0; + bus_mask |= (mask & CACHE_BUS_IBUS2) ? DPORT_PRO_CACHE_MASK_IROM0 : 0; + + bus_mask |= (mask & CACHE_BUS_DBUS0) ? DPORT_PRO_CACHE_MASK_DROM0 : 0; + bus_mask |= (mask & CACHE_BUS_DBUS1) ? DPORT_PRO_CACHE_MASK_DRAM1 : 0; + + DPORT_REG_SET_BIT(DPORT_PRO_CACHE_CTRL1_REG, bus_mask); + } else { + bus_mask |= (mask & CACHE_BUS_IBUS0) ? DPORT_APP_CACHE_MASK_IRAM0 : 0; + bus_mask |= (mask & CACHE_BUS_IBUS1) ? DPORT_APP_CACHE_MASK_IRAM1 : 0; + bus_mask |= (mask & CACHE_BUS_IBUS2) ? DPORT_APP_CACHE_MASK_IROM0 : 0; + + bus_mask |= (mask & CACHE_BUS_DBUS0) ? DPORT_APP_CACHE_MASK_DROM0 : 0; + bus_mask |= (mask & CACHE_BUS_DBUS1) ? DPORT_APP_CACHE_MASK_DRAM1 : 0; + + DPORT_REG_SET_BIT(DPORT_APP_CACHE_CTRL1_REG, bus_mask); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s3/include/hal/cache_ll.h b/components/hal/esp32s3/include/hal/cache_ll.h new file mode 100644 index 0000000000..58125d0a3f --- /dev/null +++ b/components/hal/esp32s3/include/hal/cache_ll.h @@ -0,0 +1,115 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The LL layer for Cache register operations + +#pragma once + +#include "soc/extmem_reg.h" +#include "hal/cache_types.h" +#include "hal/assert.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Enable the Cache Buses + * + * @param cache_id cache ID (when l1 cache is per core) + * @param mask To know which buses should be enabled + */ +#if !BOOTLOADER_BUILD +__attribute__((always_inline)) +#endif +static inline void cache_ll_l1_enable_bus(uint32_t cache_id, cache_bus_mask_t mask) +{ + HAL_ASSERT(cache_id == 0 || cache_id == 1); + //On esp32s3, only `CACHE_BUS_IBUS0` and `CACHE_BUS_DBUS0` are supported. Use `cache_ll_l1_get_bus()` to get your bus first + HAL_ASSERT((mask & (CACHE_BUS_IBUS1 | CACHE_BUS_IBUS2| CACHE_BUS_DBUS1 | CACHE_BUS_DBUS2)) == 0); + + uint32_t ibus_mask = 0; + if (cache_id == 0) { + ibus_mask |= (mask & CACHE_BUS_IBUS0) ? EXTMEM_ICACHE_SHUT_CORE0_BUS : 0; + } else { + ibus_mask |= (mask & CACHE_BUS_IBUS0) ? EXTMEM_ICACHE_SHUT_CORE1_BUS : 0; + } + REG_CLR_BIT(EXTMEM_ICACHE_CTRL1_REG, ibus_mask); + + uint32_t dbus_mask = 0; + if (cache_id == 1) { + dbus_mask |= (mask & CACHE_BUS_DBUS0) ? EXTMEM_DCACHE_SHUT_CORE0_BUS : 0; + } else { + dbus_mask |= (mask & CACHE_BUS_DBUS0) ? EXTMEM_DCACHE_SHUT_CORE1_BUS : 0; + } + REG_CLR_BIT(EXTMEM_DCACHE_CTRL1_REG, dbus_mask); +} + +/** + * Returns enabled buses for a given core + * + * @param cache_id cache ID (when l1 cache is per core) + * + * @return State of enabled buses + */ +__attribute__((always_inline)) +static inline cache_bus_mask_t cache_ll_l1_get_enabled_bus(uint32_t cache_id) +{ + cache_bus_mask_t mask = 0; + HAL_ASSERT(cache_id == 0 || cache_id == 1); + //On esp32s3, only `CACHE_BUS_IBUS0` and `CACHE_BUS_DBUS0` are supported. Use `cache_ll_l1_get_bus()` to get your bus first + + uint32_t ibus_mask = REG_READ(EXTMEM_ICACHE_CTRL1_REG); + if (cache_id == 0) { + mask |= (!(ibus_mask & EXTMEM_ICACHE_SHUT_CORE0_BUS)) ? CACHE_BUS_IBUS0 : 0; + } else { + mask |= (!(ibus_mask & EXTMEM_ICACHE_SHUT_CORE1_BUS)) ? CACHE_BUS_IBUS0 : 0; + } + + uint32_t dbus_mask = REG_READ(EXTMEM_DCACHE_CTRL1_REG); + if (cache_id == 1) { + mask |= (!(dbus_mask & EXTMEM_DCACHE_SHUT_CORE0_BUS)) ? CACHE_BUS_DBUS0 : 0; + } else { + mask |= (!(dbus_mask & EXTMEM_DCACHE_SHUT_CORE1_BUS)) ? CACHE_BUS_DBUS0 : 0; + } + + return mask; +} + +/** + * Disable the Cache Buses + * + * @param cache_id cache ID (when l1 cache is per core) + * @param mask To know which buses should be disabled + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_disable_bus(uint32_t cache_id, cache_bus_mask_t mask) +{ + HAL_ASSERT(cache_id == 0 || cache_id == 1); + //On esp32s3, only `CACHE_BUS_IBUS0` and `CACHE_BUS_DBUS0` are supported. Use `cache_ll_l1_get_bus()` to get your bus first + HAL_ASSERT((mask & (CACHE_BUS_IBUS1 | CACHE_BUS_IBUS2| CACHE_BUS_DBUS1 | CACHE_BUS_DBUS2)) == 0); + + uint32_t ibus_mask = 0; + if (cache_id == 0) { + ibus_mask |= (mask & CACHE_BUS_IBUS0) ? EXTMEM_ICACHE_SHUT_CORE0_BUS : 0; + } else { + ibus_mask |= (mask & CACHE_BUS_IBUS0) ? EXTMEM_ICACHE_SHUT_CORE1_BUS : 0; + } + REG_SET_BIT(EXTMEM_ICACHE_CTRL1_REG, ibus_mask); + + uint32_t dbus_mask = 0; + if (cache_id == 1) { + dbus_mask |= (mask & CACHE_BUS_DBUS0) ? EXTMEM_DCACHE_SHUT_CORE0_BUS : 0; + } else { + dbus_mask |= (mask & CACHE_BUS_DBUS0) ? EXTMEM_DCACHE_SHUT_CORE1_BUS : 0; + } + REG_SET_BIT(EXTMEM_DCACHE_CTRL1_REG, dbus_mask); +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/cache_types.h b/components/hal/include/hal/cache_types.h new file mode 100644 index 0000000000..0a7cee8fe8 --- /dev/null +++ b/components/hal/include/hal/cache_types.h @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2010-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_bit_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Ibuses and Dbuses. + * + * @note + * These enumurations are abstract concepts. Virtual address reside in one of these buses. + * Therefore, use `cache_ll_l1_get_bus(cache_id, vaddr_start, len)` to convert your vaddr into buses first + */ +typedef enum { + CACHE_BUS_IBUS0 = BIT(0), + CACHE_BUS_IBUS1 = BIT(1), + CACHE_BUS_IBUS2 = BIT(2), + CACHE_BUS_DBUS0 = BIT(3), + CACHE_BUS_DBUS1 = BIT(4), + CACHE_BUS_DBUS2 = BIT(5), +} cache_bus_mask_t; + +#ifdef __cplusplus +} +#endif diff --git a/tools/ci/python_packages/ttfw_idf/IDFDUT.py b/tools/ci/python_packages/ttfw_idf/IDFDUT.py index 5828eaabdb..ed5268985b 100644 --- a/tools/ci/python_packages/ttfw_idf/IDFDUT.py +++ b/tools/ci/python_packages/ttfw_idf/IDFDUT.py @@ -391,13 +391,14 @@ class IDFDUT(DUT.SerialDUT): for (_, f) in encrypt_offs_files: f.close() - def bootloader_flash(self): + def bootloader_flash(self, binary_path=None): """ download bootloader. :return: None """ - bootloader_path = os.path.join(self.app.binary_path, 'bootloader', 'bootloader.bin') + binary_path = self.app.binary_path if binary_path is None else binary_path + bootloader_path = os.path.join(binary_path, 'bootloader', 'bootloader.bin') offs = int(self.app.get_sdkconfig()['CONFIG_BOOTLOADER_OFFSET_IN_FLASH'], 0) flash_files = [(offs, bootloader_path)] self.write_flash(flash_files) diff --git a/tools/test_apps/system/unicore_bootloader/CMakeLists.txt b/tools/test_apps/system/unicore_bootloader/CMakeLists.txt new file mode 100644 index 0000000000..4b7ab3fd72 --- /dev/null +++ b/tools/test_apps/system/unicore_bootloader/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_unicore_bootloader) diff --git a/tools/test_apps/system/unicore_bootloader/README.md b/tools/test_apps/system/unicore_bootloader/README.md new file mode 100644 index 0000000000..5ab630aafd --- /dev/null +++ b/tools/test_apps/system/unicore_bootloader/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-S3 | +| ----------------- | ----- | -------- | diff --git a/tools/test_apps/system/unicore_bootloader/README.txt b/tools/test_apps/system/unicore_bootloader/README.txt new file mode 100644 index 0000000000..875b2c9400 --- /dev/null +++ b/tools/test_apps/system/unicore_bootloader/README.txt @@ -0,0 +1,4 @@ +This project tests if the app can start up in a certain configuration. +Multicore app can start up even if the bootloader is unicore. + +The test is only for Multicore chips. diff --git a/tools/test_apps/system/unicore_bootloader/app_test.py b/tools/test_apps/system/unicore_bootloader/app_test.py new file mode 100644 index 0000000000..438aa825e3 --- /dev/null +++ b/tools/test_apps/system/unicore_bootloader/app_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +from typing import Any + +import ttfw_idf + + +@ttfw_idf.idf_custom_test(env_tag='Example_GENERIC', group='test-apps', target=['esp32', 'esp32s3']) +def test_multicore_app_and_unicore_bootloader(env, _): # type: (Any, Any) -> None + dut = env.get_dut('unicore_bootloader', 'tools/test_apps/system/unicore_bootloader', app_config_name='multicore_cfg') + dut.start_app() + dut.expect('Multicore bootloader') + dut.expect('Multicore app') + dut.expect('App is running') + env.close_dut(dut.name) + + dut = env.get_dut('unicore_bootloader', 'tools/test_apps/system/unicore_bootloader', app_config_name='unicore_cfg') + dut.bootloader_flash() + dut.expect('Unicore bootloader') + dut.expect('Multicore app') + dut.expect('App is running') + + +@ttfw_idf.idf_custom_test(env_tag='Example_GENERIC', group='test-apps', target=['esp32', 'esp32s3']) +def test_unicore_app_and_multicore_bootloader(env, _): # type: (Any, Any) -> None + dut = env.get_dut('unicore_bootloader', 'tools/test_apps/system/unicore_bootloader', app_config_name='unicore_cfg') + dut.start_app() + dut.expect('Unicore bootloader') + dut.expect('Unicore app') + dut.expect('App is running') + env.close_dut(dut.name) + + dut = env.get_dut('unicore_bootloader', 'tools/test_apps/system/unicore_bootloader', app_config_name='multicore_cfg') + dut.bootloader_flash() + dut.expect('Multicore bootloader') + dut.expect('Unicore app') + dut.expect('App is running') + + +if __name__ == '__main__': + test_multicore_app_and_unicore_bootloader() + test_unicore_app_and_multicore_bootloader() diff --git a/tools/test_apps/system/unicore_bootloader/main/CMakeLists.txt b/tools/test_apps/system/unicore_bootloader/main/CMakeLists.txt new file mode 100644 index 0000000000..34613e8e3c --- /dev/null +++ b/tools/test_apps/system/unicore_bootloader/main/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(SRCS "main.c") diff --git a/tools/test_apps/system/unicore_bootloader/main/main.c b/tools/test_apps/system/unicore_bootloader/main/main.c new file mode 100644 index 0000000000..50c39ad016 --- /dev/null +++ b/tools/test_apps/system/unicore_bootloader/main/main.c @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +void app_main(void) +{ + printf("App is running\n"); +} diff --git a/tools/test_apps/system/unicore_bootloader/sdkconfig.ci.multicore_cfg b/tools/test_apps/system/unicore_bootloader/sdkconfig.ci.multicore_cfg new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/test_apps/system/unicore_bootloader/sdkconfig.ci.unicore_cfg b/tools/test_apps/system/unicore_bootloader/sdkconfig.ci.unicore_cfg new file mode 100644 index 0000000000..f0b0b5e03d --- /dev/null +++ b/tools/test_apps/system/unicore_bootloader/sdkconfig.ci.unicore_cfg @@ -0,0 +1 @@ +CONFIG_FREERTOS_UNICORE=y