Skip to content

Commit 6c5a7da

Browse files
Merge branch 'feature/unicore_bootloader_can_run_multicore_app' into 'master'
esp_system: Fix case when multicore app can not be run if bootloader is unicore Closes IDFGH-9336 See merge request espressif/esp-idf!22664
2 parents ce9d466 + 444c27e commit 6c5a7da

File tree

14 files changed

+237
-1
lines changed

14 files changed

+237
-1
lines changed

components/bootloader_support/src/bootloader_init.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,12 @@ void bootloader_print_banner(void)
9696
#ifndef CONFIG_APP_REPRODUCIBLE_BUILD
9797
ESP_EARLY_LOGI(TAG, "compile time " __DATE__ " " __TIME__);
9898
#endif
99+
100+
#if CONFIG_FREERTOS_UNICORE
101+
#if (SOC_CPU_CORES_NUM > 1)
102+
ESP_EARLY_LOGW(TAG, "Unicore bootloader");
103+
#endif
104+
#else
105+
ESP_EARLY_LOGI(TAG, "Multicore bootloader");
106+
#endif
99107
}

components/esp_system/port/cpu_start.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "soc/assist_debug_reg.h"
4343
#include "soc/system_reg.h"
4444
#include "esp32s3/rom/opi_flash.h"
45+
#include "hal/cache_hal.h"
4546
#elif CONFIG_IDF_TARGET_ESP32C3
4647
#include "esp32c3/rtc.h"
4748
#include "esp32c3/rom/cache.h"
@@ -84,6 +85,7 @@
8485
#include "esp_private/sleep_gpio.h"
8586
#include "hal/wdt_hal.h"
8687
#include "soc/rtc.h"
88+
#include "hal/cache_ll.h"
8789
#include "hal/efuse_ll.h"
8890
#include "soc/periph_defs.h"
8991
#include "esp_cpu.h"
@@ -256,6 +258,37 @@ static void start_other_core(void)
256258
esp_rom_delay_us(100);
257259
}
258260
}
261+
262+
// This function is needed to make the multicore app runnable on a unicore bootloader (built with FREERTOS UNICORE).
263+
// It does some cache settings for other CPUs.
264+
void IRAM_ATTR do_multicore_settings(void)
265+
{
266+
// We intentionally do not check the cache settings before changing them,
267+
// because it helps to get the application to run on older bootloaders.
268+
#ifdef CONFIG_IDF_TARGET_ESP32
269+
if (!efuse_ll_get_disable_app_cpu()) {
270+
Cache_Read_Disable(1);
271+
Cache_Flush(1);
272+
DPORT_REG_SET_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR);
273+
DPORT_REG_CLR_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR);
274+
// We do not enable cache for CPU1 now because it will be done later in start_other_core().
275+
}
276+
#endif
277+
278+
cache_bus_mask_t cache_bus_mask_core0 = cache_ll_l1_get_enabled_bus(0);
279+
#ifndef CONFIG_IDF_TARGET_ESP32
280+
// 1. disable the cache before changing its settings.
281+
cache_hal_disable(CACHE_TYPE_ALL);
282+
#endif
283+
for (unsigned core = 1; core < SOC_CPU_CORES_NUM; core++) {
284+
// 2. change cache settings. All cores must have the same settings.
285+
cache_ll_l1_enable_bus(core, cache_bus_mask_core0);
286+
}
287+
#ifndef CONFIG_IDF_TARGET_ESP32
288+
// 3. enable the cache after changing its settings.
289+
cache_hal_enable(CACHE_TYPE_ALL);
290+
#endif
291+
}
259292
#endif // !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
260293

261294
/*
@@ -312,6 +345,16 @@ void IRAM_ATTR call_start_cpu0(void)
312345
}
313346
#endif
314347

348+
#if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
349+
#if CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
350+
ESP_EARLY_LOGI(TAG, "Unicore app");
351+
#else
352+
ESP_EARLY_LOGI(TAG, "Multicore app");
353+
// It helps to fix missed cache settings for other cores. It happens when bootloader is unicore.
354+
do_multicore_settings();
355+
#endif
356+
#endif // !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
357+
315358
// When the APP is loaded into ram for execution, some hardware initialization behaviors
316359
// in the bootloader are still necessary
317360
#if CONFIG_APP_BUILD_TYPE_RAM

components/hal/esp32/include/hal/cache_ll.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,38 @@ static inline void cache_ll_l1_enable_bus(uint32_t cache_id, cache_bus_mask_t ma
9999
}
100100
}
101101

102+
/**
103+
* Returns enabled buses for a given core
104+
*
105+
* @param cache_id cache ID (when l1 cache is per core)
106+
*
107+
* @return State of enabled buses
108+
*/
109+
__attribute__((always_inline))
110+
static inline cache_bus_mask_t cache_ll_l1_get_enabled_bus(uint32_t cache_id)
111+
{
112+
cache_bus_mask_t mask = 0;
113+
HAL_ASSERT(cache_id == 0 || cache_id == 1);
114+
if (cache_id == 0) {
115+
uint32_t bus_mask= DPORT_REG_READ(DPORT_PRO_CACHE_CTRL1_REG);
116+
mask |= (!(bus_mask & DPORT_PRO_CACHE_MASK_IRAM0)) ? CACHE_BUS_IBUS0 : 0;
117+
mask |= (!(bus_mask & DPORT_PRO_CACHE_MASK_IRAM1)) ? CACHE_BUS_IBUS1 : 0;
118+
mask |= (!(bus_mask & DPORT_PRO_CACHE_MASK_IROM0)) ? CACHE_BUS_IBUS2 : 0;
119+
120+
mask |= (!(bus_mask & DPORT_PRO_CACHE_MASK_DROM0)) ? CACHE_BUS_DBUS0 : 0;
121+
mask |= (!(bus_mask & DPORT_PRO_CACHE_MASK_DRAM1)) ? CACHE_BUS_DBUS1 : 0;
122+
} else {
123+
uint32_t bus_mask= DPORT_REG_READ(DPORT_APP_CACHE_CTRL1_REG);
124+
mask |= (!(bus_mask & DPORT_APP_CACHE_MASK_IRAM0)) ? CACHE_BUS_IBUS0 : 0;
125+
mask |= (!(bus_mask & DPORT_APP_CACHE_MASK_IRAM1)) ? CACHE_BUS_IBUS1 : 0;
126+
mask |= (!(bus_mask & DPORT_APP_CACHE_MASK_IROM0)) ? CACHE_BUS_IBUS2 : 0;
127+
128+
mask |= (!(bus_mask & DPORT_APP_CACHE_MASK_DROM0)) ? CACHE_BUS_DBUS0 : 0;
129+
mask |= (!(bus_mask & DPORT_APP_CACHE_MASK_DRAM1)) ? CACHE_BUS_DBUS1 : 0;
130+
}
131+
return mask;
132+
}
133+
102134
/**
103135
* Disable the Cache Buses
104136
*

components/hal/esp32s3/include/hal/cache_ll.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -100,6 +100,37 @@ static inline void cache_ll_l1_enable_bus(uint32_t cache_id, cache_bus_mask_t ma
100100
REG_CLR_BIT(EXTMEM_DCACHE_CTRL1_REG, dbus_mask);
101101
}
102102

103+
/**
104+
* Returns enabled buses for a given core
105+
*
106+
* @param cache_id cache ID (when l1 cache is per core)
107+
*
108+
* @return State of enabled buses
109+
*/
110+
__attribute__((always_inline))
111+
static inline cache_bus_mask_t cache_ll_l1_get_enabled_bus(uint32_t cache_id)
112+
{
113+
cache_bus_mask_t mask = 0;
114+
HAL_ASSERT(cache_id == 0 || cache_id == 1);
115+
//On esp32s3, only `CACHE_BUS_IBUS0` and `CACHE_BUS_DBUS0` are supported. Use `cache_ll_l1_get_bus()` to get your bus first
116+
117+
uint32_t ibus_mask = REG_READ(EXTMEM_ICACHE_CTRL1_REG);
118+
if (cache_id == 0) {
119+
mask |= (!(ibus_mask & EXTMEM_ICACHE_SHUT_CORE0_BUS)) ? CACHE_BUS_IBUS0 : 0;
120+
} else {
121+
mask |= (!(ibus_mask & EXTMEM_ICACHE_SHUT_CORE1_BUS)) ? CACHE_BUS_IBUS0 : 0;
122+
}
123+
124+
uint32_t dbus_mask = REG_READ(EXTMEM_DCACHE_CTRL1_REG);
125+
if (cache_id == 1) {
126+
mask |= (!(dbus_mask & EXTMEM_DCACHE_SHUT_CORE0_BUS)) ? CACHE_BUS_DBUS0 : 0;
127+
} else {
128+
mask |= (!(dbus_mask & EXTMEM_DCACHE_SHUT_CORE1_BUS)) ? CACHE_BUS_DBUS0 : 0;
129+
}
130+
131+
return mask;
132+
}
133+
103134
/**
104135
* Disable the Cache Buses
105136
*

tools/test_apps/.build-test-rules.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,8 @@ tools/test_apps/system/startup:
179179
tools/test_apps/system/test_watchpoint:
180180
enable:
181181
- if: IDF_TARGET in ["esp32", "esp32c3"] # Just one test per architecture
182+
183+
tools/test_apps/system/unicore_bootloader:
184+
enable:
185+
- if: SOC_CPU_CORES_NUM > 1
186+
reason: the test should be run on multicore chips
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# The following lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.16)
4+
5+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
project(test_unicore_bootloader)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| Supported Targets | ESP32 | ESP32-S3 |
2+
| ----------------- | ----- | -------- |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
This project tests if the app can start up in a certain configuration.
2+
Multicore app can start up even if the bootloader is unicore.
3+
4+
The test is only for Multicore chips.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
import logging
5+
import os
6+
7+
import pytest
8+
from _pytest.fixtures import FixtureRequest
9+
from _pytest.monkeypatch import MonkeyPatch
10+
from pytest_embedded_idf.app import FlashFile
11+
from pytest_embedded_idf.serial import IdfSerial
12+
13+
14+
# This is a custom IdfSerial class to support custom functionality
15+
# which is required only for this test
16+
class FlashBootloader(IdfSerial):
17+
def bootloader_flash(self, binary_path: str) -> None:
18+
"""
19+
Flash bootloader.
20+
21+
:return: None
22+
"""
23+
logging.info('Flashing bootloader')
24+
bootloader_path = os.path.join(binary_path, 'bootloader', 'bootloader.bin')
25+
logging.info(bootloader_path)
26+
offs = int(self.app.sdkconfig.get('BOOTLOADER_OFFSET_IN_FLASH', 0))
27+
logging.info('bootloader offset is {0}'.format(hex(offs)))
28+
prev_flash_files = self.app.flash_files
29+
flash_files = []
30+
flash_files.append(
31+
FlashFile(
32+
offs,
33+
bootloader_path,
34+
False,
35+
)
36+
)
37+
self.app.flash_files = flash_files
38+
self.flash()
39+
# Restore self.app.flash files to original value
40+
self.app.flash_files = prev_flash_files
41+
42+
43+
@pytest.fixture(scope='module')
44+
def monkeypatch_module(request: FixtureRequest) -> MonkeyPatch:
45+
mp = MonkeyPatch()
46+
request.addfinalizer(mp.undo)
47+
return mp
48+
49+
50+
@pytest.fixture(scope='module', autouse=True)
51+
def replace_dut_class(monkeypatch_module: MonkeyPatch) -> None:
52+
monkeypatch_module.setattr('pytest_embedded_idf.IdfSerial', FlashBootloader)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
idf_component_register(SRCS "main.c")
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stdio.h>
8+
9+
void app_main(void)
10+
{
11+
printf("App is running\n");
12+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: CC0-1.0
3+
4+
import os
5+
6+
import pytest
7+
from pytest_embedded import Dut
8+
9+
10+
@pytest.mark.esp32
11+
@pytest.mark.esp32s3
12+
@pytest.mark.generic
13+
@pytest.mark.parametrize('config', ['multicore'], indirect=True)
14+
def test_multicore_app_and_unicore_bootloader(dut: Dut) -> None:
15+
dut.expect('Multicore bootloader')
16+
dut.expect('Multicore app')
17+
dut.expect('App is running')
18+
19+
path_to_unicore_build = os.path.join(dut.app.app_path, f'build_{dut.target}_unicore')
20+
dut.serial.bootloader_flash(path_to_unicore_build)
21+
dut.expect('Unicore bootloader')
22+
dut.expect('Multicore app')
23+
dut.expect('App is running')
24+
25+
26+
@pytest.mark.esp32
27+
@pytest.mark.esp32s3
28+
@pytest.mark.generic
29+
@pytest.mark.parametrize('config', ['unicore'], indirect=True)
30+
def test_unicore_app_and_multicore_bootloader(dut: Dut) -> None:
31+
dut.expect('Unicore bootloader')
32+
dut.expect('Unicore app')
33+
dut.expect('App is running')
34+
35+
path_to_unicore_build = os.path.join(dut.app.app_path, f'build_{dut.target}_multicore')
36+
dut.serial.bootloader_flash(path_to_unicore_build)
37+
dut.expect('Multicore bootloader')
38+
dut.expect('Unicore app')
39+
dut.expect('App is running')

tools/test_apps/system/unicore_bootloader/sdkconfig.ci.multicore

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CONFIG_FREERTOS_UNICORE=y

0 commit comments

Comments
 (0)