mirror of
https://github.com/esphome/esphome.git
synced 2026-09-18 02:28:42 +00:00
[esp32] Add a flash chip option that drops the unused flash vendor drivers (#19217)
This commit is contained in:
@@ -111,6 +111,7 @@ CONF_ENGINEERING_SAMPLE = "engineering_sample"
|
||||
CONF_INCLUDE_BUILTIN_IDF_COMPONENTS = "include_builtin_idf_components"
|
||||
CONF_ENABLE_LWIP_ASSERT = "enable_lwip_assert"
|
||||
CONF_EXECUTE_FROM_PSRAM = "execute_from_psram"
|
||||
CONF_FLASH_CHIP = "flash_chip"
|
||||
CONF_KEY_ID = "key_id"
|
||||
CONF_MINIMUM_CHIP_REVISION = "minimum_chip_revision"
|
||||
CONF_NVS_ENCRYPTION = "nvs_encryption"
|
||||
@@ -464,6 +465,20 @@ ESP32_CHIP_REVISIONS = {
|
||||
"3.1": "CONFIG_ESP32_REV_MIN_3_1",
|
||||
}
|
||||
|
||||
# Flash vendor drivers ESP-IDF can link; each costs IRAM plus a 124 B table in DRAM
|
||||
# and only the one matching the flash ID is ever used
|
||||
ESP32_FLASH_CHIPS = {
|
||||
"gd": "CONFIG_SPI_FLASH_SUPPORT_GD_CHIP",
|
||||
"issi": "CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP",
|
||||
"mxic": "CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP",
|
||||
"winbond": "CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP",
|
||||
"boya": "CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP",
|
||||
"th": "CONFIG_SPI_FLASH_SUPPORT_TH_CHIP",
|
||||
"mxic_opi": "CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP",
|
||||
}
|
||||
FLASH_CHIP_GENERIC = "generic"
|
||||
FLASH_CHIP_OPI = "mxic_opi" # the octal driver, ESP32-S3 only
|
||||
|
||||
# Socket limit configuration for ESP-IDF
|
||||
# ESP-IDF CONFIG_LWIP_MAX_SOCKETS has range 1-253, default 10
|
||||
DEFAULT_MAX_SOCKETS = 10 # ESP-IDF default
|
||||
@@ -1533,6 +1548,25 @@ def final_validate(config) -> None:
|
||||
path=[CONF_FRAMEWORK, CONF_ADVANCED, CONF_SRAM1_AS_IRAM],
|
||||
)
|
||||
)
|
||||
if (flash_chip := advanced.get(CONF_FLASH_CHIP)) is not None:
|
||||
opi = flash_chip == FLASH_CHIP_OPI
|
||||
if opi and config[CONF_VARIANT] != VARIANT_ESP32S3:
|
||||
errs.append(
|
||||
cv.Invalid(
|
||||
f"'{CONF_FLASH_CHIP}: {flash_chip}' is only supported on {VARIANT_ESP32S3}",
|
||||
path=[CONF_FRAMEWORK, CONF_ADVANCED, CONF_FLASH_CHIP],
|
||||
)
|
||||
)
|
||||
elif opi != (config.get(CONF_FLASH_MODE) == "opi"):
|
||||
errs.append(
|
||||
cv.Invalid(
|
||||
f"'{CONF_FLASH_CHIP}: {flash_chip}' requires '{CONF_FLASH_MODE}: opi'"
|
||||
if opi
|
||||
else f"'{CONF_FLASH_CHIP}: {flash_chip}' does not match "
|
||||
f"'{CONF_FLASH_MODE}: opi'; octal flash uses {FLASH_CHIP_OPI}",
|
||||
path=[CONF_FRAMEWORK, CONF_ADVANCED, CONF_FLASH_CHIP],
|
||||
)
|
||||
)
|
||||
if (
|
||||
config[CONF_VARIANT] != VARIANT_ESP32P4
|
||||
and config.get(CONF_ENGINEERING_SAMPLE) is not None
|
||||
@@ -1971,6 +2005,9 @@ FRAMEWORK_SCHEMA = cv.Schema(
|
||||
*ESP32_CHIP_REVISIONS, string=True
|
||||
),
|
||||
cv.Optional(CONF_SRAM1_AS_IRAM, default=False): cv.boolean,
|
||||
cv.Optional(CONF_FLASH_CHIP): cv.one_of(
|
||||
FLASH_CHIP_GENERIC, *ESP32_FLASH_CHIPS, lower=True
|
||||
),
|
||||
# DHCP server is needed for WiFi AP mode. When WiFi component is used,
|
||||
# it will handle disabling DHCP server when AP is not configured.
|
||||
# Default to false (disabled) when WiFi is not used.
|
||||
@@ -2765,6 +2802,11 @@ async def to_code(config):
|
||||
add_idf_sdkconfig_option(flag, rev == min_rev)
|
||||
cg.add_define("USE_ESP32_MIN_CHIP_REVISION_SET")
|
||||
|
||||
# Keep only the flash vendor driver the board needs; the boot log names it
|
||||
if (flash_chip := conf[CONF_ADVANCED].get(CONF_FLASH_CHIP)) is not None:
|
||||
for chip, flag in ESP32_FLASH_CHIPS.items():
|
||||
add_idf_sdkconfig_option(flag, chip == flash_chip)
|
||||
|
||||
# Use SRAM1 region as IRAM on ESP32 (original) variant
|
||||
# This provides an additional 40KB of IRAM by using SRAM1 memory that was previously
|
||||
# reserved for bootloader DRAM. Requires a bootloader from ESP-IDF v5.1 or later.
|
||||
|
||||
@@ -11,6 +11,19 @@
|
||||
#include <esp_chip_info.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_bootloader_desc.h>
|
||||
#include <esp_flash.h>
|
||||
#if __has_include(<esp_flash_chips/spi_flash_chip_generic.h>)
|
||||
#include <esp_flash_chips/spi_flash_chip_generic.h> // ESP-IDF 6
|
||||
#include <esp_flash_chips/spi_flash_chip_mxic.h>
|
||||
#else
|
||||
#include <spi_flash_chip_generic.h>
|
||||
#include <spi_flash_chip_mxic.h>
|
||||
#endif
|
||||
// Vendor flash drivers linked next to the generic one; sdkconfig defines each as 1 or not at all
|
||||
#define ESPHOME_FLASH_VENDOR_DRIVERS \
|
||||
(CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP + CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP + CONFIG_SPI_FLASH_SUPPORT_GD_CHIP + \
|
||||
CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP + CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP + CONFIG_SPI_FLASH_SUPPORT_TH_CHIP + \
|
||||
CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP)
|
||||
#endif
|
||||
#include "esphome/core/version.h"
|
||||
#include "esphome/core/hal.h"
|
||||
@@ -157,8 +170,25 @@ void Application::process_dump_config_() {
|
||||
esp_chip_info(&chip_info);
|
||||
ESP_LOGI(TAG, "ESP32 Chip: %s rev%d.%d, %d core(s)", ESPHOME_VARIANT, chip_info.revision / 100,
|
||||
chip_info.revision % 100, chip_info.cores);
|
||||
#if defined(USE_ESP32_VARIANT_ESP32) && (!defined(USE_ESP32_MIN_CHIP_REVISION_SET) || !defined(USE_ESP32_SRAM1_AS_IRAM))
|
||||
static const char *const ESP32_ADVANCED_PATH = "under esp32 > framework > advanced";
|
||||
[[maybe_unused]] static const char *const ESP32_ADVANCED_PATH = "under esp32 > framework > advanced";
|
||||
#if ESPHOME_FLASH_VENDOR_DRIVERS > 0
|
||||
{
|
||||
// Only the driver in use earns its IRAM; with several linked at least one is idle
|
||||
const spi_flash_chip_t *flash_driver = esp_flash_default_chip->chip_drv;
|
||||
#if ESPHOME_FLASH_VENDOR_DRIVERS > 1
|
||||
constexpr bool idle_driver = true;
|
||||
#else
|
||||
const bool idle_driver = flash_driver == &esp_flash_chip_generic;
|
||||
#endif
|
||||
if (idle_driver) {
|
||||
const char *value = flash_driver->name;
|
||||
#ifdef CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP
|
||||
if (flash_driver == &esp_flash_chip_mxic_opi)
|
||||
value = "mxic_opi";
|
||||
#endif
|
||||
ESP_LOGW(TAG, "Set flash_chip: %s %s to save IRAM", value, ESP32_ADVANCED_PATH);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(USE_ESP32_VARIANT_ESP32) && !defined(USE_ESP32_MIN_CHIP_REVISION_SET)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
@@ -0,0 +1,9 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
@@ -0,0 +1,10 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
variant: esp32s3
|
||||
flash_mode: opi
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
flash_chip: mxic_opi
|
||||
@@ -10,6 +10,7 @@ from typing import Any
|
||||
import pytest
|
||||
|
||||
from esphome.components.esp32 import (
|
||||
ESP32_FLASH_CHIPS,
|
||||
KEY_FATFS_REQUIRED,
|
||||
KEY_MBEDTLS_TLS_EXTRAS_REQUIRED,
|
||||
KEY_MBEDTLS_TLS_SERVER_REQUIRED,
|
||||
@@ -252,6 +253,41 @@ def test_esp32_rejects_unsupported_cli_toolchain(
|
||||
r"value must be at most 5 .* @ data\['framework'\]\['advanced'\]\['nvs_encryption'\]\['key_id'\]",
|
||||
id="nvs_encryption_key_id_out_of_range",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"variant": "esp32",
|
||||
"board": "esp32dev",
|
||||
"framework": {
|
||||
"type": "esp-idf",
|
||||
"advanced": {"flash_chip": "mxic_opi"},
|
||||
},
|
||||
},
|
||||
r"'flash_chip: mxic_opi' is only supported on ESP32S3 @ data\['framework'\]\['advanced'\]\['flash_chip'\]",
|
||||
id="flash_chip_mxic_opi_only_on_s3",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"variant": "esp32s3",
|
||||
"flash_mode": "opi",
|
||||
"framework": {
|
||||
"type": "esp-idf",
|
||||
"advanced": {"flash_chip": "gd"},
|
||||
},
|
||||
},
|
||||
r"'flash_chip: gd' does not match 'flash_mode: opi'; octal flash uses mxic_opi @ data\['framework'\]\['advanced'\]\['flash_chip'\]",
|
||||
id="flash_chip_must_match_opi_mode",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"variant": "esp32s3",
|
||||
"framework": {
|
||||
"type": "esp-idf",
|
||||
"advanced": {"flash_chip": "mxic_opi"},
|
||||
},
|
||||
},
|
||||
r"'flash_chip: mxic_opi' requires 'flash_mode: opi' @ data\['framework'\]\['advanced'\]\['flash_chip'\]",
|
||||
id="flash_chip_mxic_opi_requires_opi_mode",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"variant": "esp32",
|
||||
@@ -719,6 +755,43 @@ def test_flash_mode_sets_sdkconfig_and_pio_option(
|
||||
assert CORE.platformio_options.get("board_build.f_flash") == "80000000L"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("config_file", "enabled"),
|
||||
[
|
||||
pytest.param("flash_chip_gd.yaml", "CONFIG_SPI_FLASH_SUPPORT_GD_CHIP", id="gd"),
|
||||
pytest.param("flash_chip_generic.yaml", None, id="generic"),
|
||||
pytest.param(
|
||||
"flash_chip_mxic_opi_s3.yaml",
|
||||
"CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP",
|
||||
id="mxic_opi_s3",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_flash_chip_keeps_one_vendor_driver(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
config_file: str,
|
||||
enabled: str | None,
|
||||
) -> None:
|
||||
"""flash_chip enables only the chosen vendor driver."""
|
||||
generate_main(component_config_path(config_file))
|
||||
sdkconfig = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS]
|
||||
vendors = {
|
||||
k: v for k, v in sdkconfig.items() if k.startswith("CONFIG_SPI_FLASH_SUPPORT_")
|
||||
}
|
||||
assert vendors == {flag: flag == enabled for flag in ESP32_FLASH_CHIPS.values()}
|
||||
|
||||
|
||||
def test_flash_chip_unset_keeps_idf_defaults(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
"""Without flash_chip every vendor driver stays at its ESP-IDF default."""
|
||||
generate_main(component_config_path("flash_mode_default.yaml"))
|
||||
sdkconfig = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS]
|
||||
assert not any(key.startswith("CONFIG_SPI_FLASH_SUPPORT_") for key in sdkconfig)
|
||||
|
||||
|
||||
def test_flash_mode_opi_enables_octal_flash(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
|
||||
@@ -22,6 +22,7 @@ esp32:
|
||||
disable_regi2c_in_iram: true
|
||||
disable_fatfs: true
|
||||
sram1_as_iram: true
|
||||
flash_chip: gd
|
||||
watchdog_timeout: 7s
|
||||
|
||||
wifi:
|
||||
|
||||
@@ -9,6 +9,7 @@ esp32:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
execute_from_psram: true
|
||||
flash_chip: gd
|
||||
disable_libc_locks_in_iram: true # Test default RAM optimization enabled
|
||||
disable_debug_stubs: true
|
||||
disable_ocd_aware: true
|
||||
|
||||
Reference in New Issue
Block a user