diff --git a/components/esp_adc/Kconfig b/components/esp_adc/Kconfig index 80ec475dbb..d6af0751f0 100644 --- a/components/esp_adc/Kconfig +++ b/components/esp_adc/Kconfig @@ -2,6 +2,7 @@ menu "ADC and ADC Calibration" config ADC_ONESHOT_CTRL_FUNC_IN_IRAM bool "Place ISR version ADC oneshot mode read function into IRAM" + select ESP_PERIPH_CTRL_FUNC_IN_IRAM default n help Place ISR version ADC oneshot mode read function into IRAM. diff --git a/components/esp_driver_parlio/Kconfig b/components/esp_driver_parlio/Kconfig index 345e5f4cf1..6d4562eebd 100644 --- a/components/esp_driver_parlio/Kconfig +++ b/components/esp_driver_parlio/Kconfig @@ -6,6 +6,7 @@ menu "ESP-Driver:Parallel IO Configurations" default y select PARLIO_OBJ_CACHE_SAFE select GDMA_CTRL_FUNC_IN_IRAM + select ESP_PERIPH_CTRL_FUNC_IN_IRAM help Place Parallel IO TX ISR handler in IRAM to reduce latency caused by cache miss. diff --git a/components/esp_driver_spi/Kconfig b/components/esp_driver_spi/Kconfig index abfb6d4600..da78416f8c 100644 --- a/components/esp_driver_spi/Kconfig +++ b/components/esp_driver_spi/Kconfig @@ -23,6 +23,7 @@ menu "ESP-Driver:SPI Configurations" bool "Place SPI master ISR function into IRAM" default y depends on !HEAP_PLACE_FUNCTION_INTO_FLASH + select ESP_PERIPH_CTRL_FUNC_IN_IRAM select ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM select GDMA_CTRL_FUNC_IN_IRAM if SOC_GDMA_SUPPORTED help @@ -51,6 +52,7 @@ menu "ESP-Driver:SPI Configurations" config SPI_SLAVE_ISR_IN_IRAM bool "Place SPI slave ISR function into IRAM" default y + select ESP_PERIPH_CTRL_FUNC_IN_IRAM select GDMA_CTRL_FUNC_IN_IRAM if SOC_GDMA_SUPPORTED help Place the SPI slave ISR in to IRAM to avoid possible cache miss. diff --git a/components/esp_hw_support/Kconfig b/components/esp_hw_support/Kconfig index 1079a3334d..59a4a6d6a9 100644 --- a/components/esp_hw_support/Kconfig +++ b/components/esp_hw_support/Kconfig @@ -70,6 +70,10 @@ menu "Hardware Settings" # regardless of power management configuration. config ESP_SLEEP_POWER_DOWN_FLASH bool "Power down flash in light sleep when there is no SPIRAM or SPIRAM has independent power supply" + # TODO: PM-383 + select PM_SLP_IRAM_OPT if (!IDF_TARGET_ESP32H21 && !IDF_TARGET_ESP32H4) + select ESP_PERIPH_CTRL_FUNC_IN_IRAM + select ESP_REGI2C_CTRL_FUNC_IN_IRAM depends on !SPIRAM || ESP_LDO_RESERVE_PSRAM depends on !(IDF_TARGET_ESP32P4 && (ESP32P4_REV_MIN_FULL < 100)) default n @@ -219,12 +223,19 @@ menu "Hardware Settings" endmenu menu "Peripheral Control" - config PERIPH_CTRL_FUNC_IN_IRAM + config ESP_PERIPH_CTRL_FUNC_IN_IRAM bool "Place peripheral control functions into IRAM" default n help Place peripheral control functions (e.g. periph_module_reset) into IRAM, so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context. + + config ESP_REGI2C_CTRL_FUNC_IN_IRAM + bool "Place regi2c control functions into IRAM" + default y + help + Place analog i2c master control functions (e.g. regi2c_ctrl_read_reg, regi2c_ctrl_write_reg) into IRAM, + so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context. endmenu menu "ETM Configuration" diff --git a/components/esp_hw_support/esp_clk.c b/components/esp_hw_support/esp_clk.c index 2ebd827631..ec85b60f00 100644 --- a/components/esp_hw_support/esp_clk.c +++ b/components/esp_hw_support/esp_clk.c @@ -26,6 +26,12 @@ #define MHZ (1000000) +#if CONFIG_PM_SLP_IRAM_OPT +# define ESP_CLK_FN_ATTR IRAM_ATTR +#else +# define ESP_CLK_FN_ATTR +#endif + // g_ticks_us defined in ROMs for PRO and APP CPU extern uint32_t g_ticks_per_us_pro; @@ -48,7 +54,7 @@ _Static_assert(offsetof(retain_mem_t, checksum) == sizeof(retain_mem_t) - sizeof #if !NON_OS_BUILD static __attribute__((section(".rtc_timer_data_in_rtc_mem"))) retain_mem_t s_rtc_timer_retain_mem; -static uint32_t calc_checksum(void) +static ESP_CLK_FN_ATTR uint32_t calc_checksum(void) { uint32_t checksum = 0; uint32_t *data = (uint32_t*) &s_rtc_timer_retain_mem; diff --git a/components/esp_hw_support/linker.lf b/components/esp_hw_support/linker.lf index afb2231606..c87f73229b 100644 --- a/components/esp_hw_support/linker.lf +++ b/components/esp_hw_support/linker.lf @@ -12,36 +12,61 @@ entries: cpu: esp_cpu_clear_watchpoint (noflash) cpu: esp_cpu_compare_and_set (noflash) esp_memory_utils (noflash) - rtc_clk (noflash) clk_utils (noflash) + if PM_SLP_IRAM_OPT = y: + rtc_clk (noflash) + rtc_time (noflash_text) if SOC_CONFIGURABLE_VDDSDIO_SUPPORTED = y: rtc_init:rtc_vddsdio_get_config (noflash) rtc_init:rtc_vddsdio_set_config (noflash) if IDF_TARGET_ESP32 = y || IDF_TARGET_ESP32S2 = y || IDF_TARGET_ESP32S3 = y || IDF_TARGET_ESP32C2 = y || IDF_TARGET_ESP32C3 = y: - rtc_sleep (noflash_text) - rtc_time (noflash_text) + rtc_sleep:rtc_sleep_start (noflash) + if PM_SLP_IRAM_OPT = y: + rtc_sleep:rtc_sleep_get_default_config (noflash) + rtc_sleep:rtc_sleep_init (noflash) + rtc_sleep:rtc_sleep_low_init (noflash) + if IDF_TARGET_ESP32 = y || IDF_TARGET_ESP32S2 = y: + rtc_sleep:rtc_sleep_pd (noflash) + if IDF_TARGET_ESP32S3 = y || IDF_TARGET_ESP32C2 = y || IDF_TARGET_ESP32C3 = y: + rtc_sleep:rtc_sleep_pu (noflash) if SOC_PMU_SUPPORTED = y && SOC_LIGHT_SLEEP_SUPPORTED = y: pmu_sleep (noflash) - if SPIRAM_FLASH_LOAD_TO_PSRAM = y: - pmu_init (noflash) - pmu_param (noflash) - if SOC_USB_SERIAL_JTAG_SUPPORTED = y: + if SPIRAM_FLASH_LOAD_TO_PSRAM = y: + pmu_init (noflash) + pmu_param (noflash) + elif PM_SLP_IRAM_OPT = y && IDF_TARGET_ESP32P4 != y: + pmu_param:get_act_hp_dbias (noflash) + pmu_param:get_act_lp_dbias (noflash) + if PM_SLP_IRAM_OPT = y && SOC_USB_SERIAL_JTAG_SUPPORTED = y: sleep_console (noflash) - if SOC_USB_OTG_SUPPORTED && SOC_PM_SUPPORT_CNNT_PD = y: + if PM_SLP_IRAM_OPT = y && SOC_USB_OTG_SUPPORTED && SOC_PM_SUPPORT_CNNT_PD = y: sleep_usb (noflash) if IDF_TARGET_ESP32 = y || IDF_TARGET_ESP32S2 = y: rtc_wdt (noflash_text) - if PERIPH_CTRL_FUNC_IN_IRAM = y: - periph_ctrl: periph_module_reset (noflash) - if PERIPH_CTRL_FUNC_IN_IRAM = y && ESP_WIFI_ENABLED = y: + if ESP_PERIPH_CTRL_FUNC_IN_IRAM = y: + periph_ctrl:periph_module_reset (noflash) + periph_ctrl:periph_rcc_enter (noflash) + periph_ctrl:periph_rcc_exit (noflash) + periph_ctrl:periph_rcc_acquire_enter (noflash) + periph_ctrl:periph_rcc_acquire_exit (noflash) + periph_ctrl:periph_rcc_release_enter (noflash) + periph_ctrl:periph_rcc_release_exit (noflash) + if ESP_PERIPH_CTRL_FUNC_IN_IRAM = y && ESP_WIFI_ENABLED = y: periph_ctrl: wifi_module_enable (noflash) periph_ctrl: wifi_module_disable (noflash) + if ESP_REGI2C_CTRL_FUNC_IN_IRAM = y: + regi2c_ctrl:regi2c_ctrl_read_reg (noflash) + regi2c_ctrl:regi2c_ctrl_read_reg_mask (noflash) + regi2c_ctrl:regi2c_ctrl_write_reg (noflash) + regi2c_ctrl:regi2c_ctrl_write_reg_mask (noflash) + regi2c_ctrl:regi2c_enter_critical (noflash) + regi2c_ctrl:regi2c_exit_critical (noflash) if SOC_SYSTIMER_SUPPORTED = y: systimer (noflash) if SOC_ADC_SHARED_POWER = y: if ADC_ONESHOT_CTRL_FUNC_IN_IRAM = y: sar_periph_ctrl (noflash) - else: + elif PM_SLP_IRAM_OPT = y: sar_periph_ctrl: sar_periph_ctrl_power_enable (noflash) [mapping:soc_pm] diff --git a/components/esp_hw_support/periph_ctrl.c b/components/esp_hw_support/periph_ctrl.c index 29152555d9..3aeacc32fc 100644 --- a/components/esp_hw_support/periph_ctrl.c +++ b/components/esp_hw_support/periph_ctrl.c @@ -21,35 +21,35 @@ static portMUX_TYPE periph_spinlock = portMUX_INITIALIZER_UNLOCKED; static uint8_t ref_counts[PERIPH_MODULE_MAX] = {0}; -IRAM_ATTR void periph_rcc_enter(void) +void periph_rcc_enter(void) { portENTER_CRITICAL_SAFE(&periph_spinlock); } -IRAM_ATTR void periph_rcc_exit(void) +void periph_rcc_exit(void) { portEXIT_CRITICAL_SAFE(&periph_spinlock); } -IRAM_ATTR uint8_t periph_rcc_acquire_enter(periph_module_t periph) +uint8_t periph_rcc_acquire_enter(periph_module_t periph) { periph_rcc_enter(); return ref_counts[periph]; } -IRAM_ATTR void periph_rcc_acquire_exit(periph_module_t periph, uint8_t ref_count) +void periph_rcc_acquire_exit(periph_module_t periph, uint8_t ref_count) { ref_counts[periph] = ++ref_count; periph_rcc_exit(); } -IRAM_ATTR uint8_t periph_rcc_release_enter(periph_module_t periph) +uint8_t periph_rcc_release_enter(periph_module_t periph) { periph_rcc_enter(); return ref_counts[periph] - 1; } -IRAM_ATTR void periph_rcc_release_exit(periph_module_t periph, uint8_t ref_count) +void periph_rcc_release_exit(periph_module_t periph, uint8_t ref_count) { ref_counts[periph] = ref_count; periph_rcc_exit(); diff --git a/components/esp_hw_support/port/esp32/rtc_sleep.c b/components/esp_hw_support/port/esp32/rtc_sleep.c index 7116f3eefa..1e51ac04ac 100644 --- a/components/esp_hw_support/port/esp32/rtc_sleep.c +++ b/components/esp_hw_support/port/esp32/rtc_sleep.c @@ -65,7 +65,7 @@ typedef struct { * Configure whether certain peripherals are powered down in deep sleep * @param cfg power down flags as rtc_sleep_pd_config_t structure */ -static void rtc_sleep_pd(rtc_sleep_pd_config_t cfg) +void rtc_sleep_pd(rtc_sleep_pd_config_t cfg) { REG_SET_FIELD(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_LSLP_MEM_FORCE_PU, ~cfg.dig_pd); REG_SET_FIELD(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_LPU, ~cfg.rtc_pd); @@ -345,7 +345,7 @@ uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt) return rtc_sleep_finish(); } -static uint32_t rtc_sleep_finish(void) +static IRAM_ATTR uint32_t rtc_sleep_finish(void) { /* In deep sleep mode, we never get here */ uint32_t reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_REG, RTC_CNTL_SLP_REJECT_INT_RAW); diff --git a/components/esp_hw_support/port/esp32c2/rtc_sleep.c b/components/esp_hw_support/port/esp32c2/rtc_sleep.c index 4fc7e0142a..e59b3840c0 100644 --- a/components/esp_hw_support/port/esp32c2/rtc_sleep.c +++ b/components/esp_hw_support/port/esp32c2/rtc_sleep.c @@ -222,7 +222,7 @@ uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp return rtc_sleep_finish(lslp_mem_inf_fpu); } -static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu) +static IRAM_ATTR uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu) { /* In deep sleep mode, we never get here */ uint32_t reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_REG, RTC_CNTL_SLP_REJECT_INT_RAW); diff --git a/components/esp_hw_support/port/esp32c3/rtc_sleep.c b/components/esp_hw_support/port/esp32c3/rtc_sleep.c index 0c02bd0411..564915bab5 100644 --- a/components/esp_hw_support/port/esp32c3/rtc_sleep.c +++ b/components/esp_hw_support/port/esp32c3/rtc_sleep.c @@ -352,7 +352,7 @@ uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt) return rtc_sleep_finish(0); } -static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu) +static IRAM_ATTR uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu) { /* In deep sleep mode, we never get here */ uint32_t reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_REG, RTC_CNTL_SLP_REJECT_INT_RAW); diff --git a/components/esp_hw_support/port/esp32s2/rtc_sleep.c b/components/esp_hw_support/port/esp32s2/rtc_sleep.c index 741156e597..a34b3ceb0e 100644 --- a/components/esp_hw_support/port/esp32s2/rtc_sleep.c +++ b/components/esp_hw_support/port/esp32s2/rtc_sleep.c @@ -361,7 +361,7 @@ uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt) return rtc_sleep_finish(0); } -static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu) +static IRAM_ATTR uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu) { /* In deep sleep mode, we never get here */ uint32_t reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_REG, RTC_CNTL_SLP_REJECT_INT_RAW); diff --git a/components/esp_hw_support/port/esp32s3/rtc_sleep.c b/components/esp_hw_support/port/esp32s3/rtc_sleep.c index 1203c95d64..da565067ae 100644 --- a/components/esp_hw_support/port/esp32s3/rtc_sleep.c +++ b/components/esp_hw_support/port/esp32s3/rtc_sleep.c @@ -284,7 +284,7 @@ __attribute__((weak)) uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t rej return rtc_sleep_finish(lslp_mem_inf_fpu); } -static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu) +static IRAM_ATTR uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu) { /* In deep sleep mode, we never get here */ uint32_t reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_REG, RTC_CNTL_SLP_REJECT_INT_RAW); diff --git a/components/esp_hw_support/regi2c_ctrl.c b/components/esp_hw_support/regi2c_ctrl.c index 38ad924d45..b25c954356 100644 --- a/components/esp_hw_support/regi2c_ctrl.c +++ b/components/esp_hw_support/regi2c_ctrl.c @@ -18,7 +18,7 @@ static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; static DRAM_ATTR __attribute__((unused)) const char *TAG = "REGI2C"; -uint8_t IRAM_ATTR regi2c_ctrl_read_reg(uint8_t block, uint8_t host_id, uint8_t reg_add) +uint8_t regi2c_ctrl_read_reg(uint8_t block, uint8_t host_id, uint8_t reg_add) { REGI2C_CLOCK_ENABLE(); portENTER_CRITICAL_SAFE(&mux); @@ -28,7 +28,7 @@ uint8_t IRAM_ATTR regi2c_ctrl_read_reg(uint8_t block, uint8_t host_id, uint8_t r return value; } -uint8_t IRAM_ATTR regi2c_ctrl_read_reg_mask(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t msb, uint8_t lsb) +uint8_t regi2c_ctrl_read_reg_mask(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t msb, uint8_t lsb) { REGI2C_CLOCK_ENABLE(); portENTER_CRITICAL_SAFE(&mux); @@ -38,7 +38,7 @@ uint8_t IRAM_ATTR regi2c_ctrl_read_reg_mask(uint8_t block, uint8_t host_id, uint return value; } -void IRAM_ATTR regi2c_ctrl_write_reg(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t data) +void regi2c_ctrl_write_reg(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t data) { REGI2C_CLOCK_ENABLE(); portENTER_CRITICAL_SAFE(&mux); @@ -47,7 +47,7 @@ void IRAM_ATTR regi2c_ctrl_write_reg(uint8_t block, uint8_t host_id, uint8_t reg REGI2C_CLOCK_DISABLE(); } -void IRAM_ATTR regi2c_ctrl_write_reg_mask(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t msb, uint8_t lsb, uint8_t data) +void regi2c_ctrl_write_reg_mask(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t msb, uint8_t lsb, uint8_t data) { REGI2C_CLOCK_ENABLE(); portENTER_CRITICAL_SAFE(&mux); @@ -56,12 +56,12 @@ void IRAM_ATTR regi2c_ctrl_write_reg_mask(uint8_t block, uint8_t host_id, uint8_ REGI2C_CLOCK_DISABLE(); } -void IRAM_ATTR regi2c_enter_critical(void) +void regi2c_enter_critical(void) { portENTER_CRITICAL_SAFE(&mux); } -void IRAM_ATTR regi2c_exit_critical(void) +void regi2c_exit_critical(void) { portEXIT_CRITICAL_SAFE(&mux); } diff --git a/components/esp_hw_support/sar_periph_ctrl_common.c b/components/esp_hw_support/sar_periph_ctrl_common.c index 4e64ee0cc1..f8099358ca 100644 --- a/components/esp_hw_support/sar_periph_ctrl_common.c +++ b/components/esp_hw_support/sar_periph_ctrl_common.c @@ -33,6 +33,12 @@ static const char *TAG_TSENS = "temperature_sensor"; #define TSENS_RCC_ATOMIC() #endif +#if CONFIG_PM_SLP_IRAM_OPT +# define SAR_PERIPH_CTRL_COMMON_FN_ATTR IRAM_ATTR +#else +# define SAR_PERIPH_CTRL_COMMON_FN_ATTR +#endif + static int s_record_min = INT_NOT_USED; static int s_record_max = INT_NOT_USED; static int s_temperature_sensor_power_cnt; @@ -79,7 +85,7 @@ void temperature_sensor_power_release(void) portEXIT_CRITICAL(&rtc_spinlock); } -static int temperature_sensor_get_raw_value(void) +static SAR_PERIPH_CTRL_COMMON_FN_ATTR int temperature_sensor_get_raw_value(void) { int raw_value = temperature_sensor_ll_get_raw_value(); return (TEMPERATURE_SENSOR_LL_ADC_FACTOR * raw_value - TEMPERATURE_SENSOR_LL_DAC_FACTOR * temperature_sensor_attributes[s_tsens_idx].offset - TEMPERATURE_SENSOR_LL_OFFSET_FACTOR); diff --git a/components/esp_hw_support/sdkconfig.rename b/components/esp_hw_support/sdkconfig.rename index 7addeba065..e2083287ef 100644 --- a/components/esp_hw_support/sdkconfig.rename +++ b/components/esp_hw_support/sdkconfig.rename @@ -7,3 +7,5 @@ CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS CONFIG_ESP32_UNIVERSAL_M CONFIG_ESP_SYSTEM_PD_FLASH CONFIG_ESP_SLEEP_POWER_DOWN_FLASH CONFIG_ESP_SYSTEM_BROWNOUT_INTR CONFIG_ESP_BROWNOUT_USE_INTR + +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM diff --git a/components/esp_hw_support/sleep_gpio.c b/components/esp_hw_support/sleep_gpio.c index 19b3b8a8ed..8d9d1b1417 100644 --- a/components/esp_hw_support/sleep_gpio.c +++ b/components/esp_hw_support/sleep_gpio.c @@ -14,6 +14,7 @@ #include "esp_log.h" #include "esp_memory_utils.h" #include "soc/soc_caps.h" +#include "soc/uart_pins.h" #include "sdkconfig.h" @@ -151,6 +152,23 @@ IRAM_ATTR void esp_sleep_isolate_digital_gpio(void) /* isolate digital IO that is not held(keep the configuration of digital IOs held by users) */ for (gpio_num_t gpio_num = GPIO_NUM_0; gpio_num < GPIO_NUM_MAX; gpio_num++) { if (GPIO_IS_VALID_DIGITAL_IO_PAD(gpio_num) && !gpio_hal_is_digital_io_hold(&gpio_hal, gpio_num)) { + + bool is_mspi_io_pad = false; + esp_mspi_io_t mspi_ios[] = { ESP_MSPI_IO_CS0, ESP_MSPI_IO_CLK, ESP_MSPI_IO_Q, ESP_MSPI_IO_D, ESP_MSPI_IO_HD, ESP_MSPI_IO_WP }; + for (int i = 0; i < sizeof(mspi_ios) / sizeof(mspi_ios[0]); i++) { + if (esp_mspi_get_io(mspi_ios[i]) == gpio_num) { + is_mspi_io_pad = true; + break; + } + } + // Ignore MSPI and default Console UART io pads, When the CPU executes + // the following instructions to configure the MSPI IO PAD, access on + // the MSPI signal lines (as CPU instruction execution and MSPI access + // operations are asynchronous) may cause the SoC to hang. + if (is_mspi_io_pad || gpio_num == U0RXD_GPIO_NUM || gpio_num == U0TXD_GPIO_NUM) { + continue; + } + /* disable I/O */ gpio_hal_input_disable(&gpio_hal, gpio_num); gpio_hal_output_disable(&gpio_hal, gpio_num); diff --git a/components/esp_hw_support/sleep_modem.c b/components/esp_hw_support/sleep_modem.c index c9beb449d1..12269b777c 100644 --- a/components/esp_hw_support/sleep_modem.c +++ b/components/esp_hw_support/sleep_modem.c @@ -77,7 +77,7 @@ esp_err_t esp_unregister_mac_bb_pd_callback(mac_bb_power_down_cb_t cb) return ESP_ERR_INVALID_STATE; } -void IRAM_ATTR mac_bb_power_down_cb_execute(void) +void mac_bb_power_down_cb_execute(void) { for (int i = 0; i < MAC_BB_POWER_DOWN_CB_NO; i++) { if (s_mac_bb_power_down_cb[i]) { @@ -118,7 +118,7 @@ esp_err_t esp_unregister_mac_bb_pu_callback(mac_bb_power_up_cb_t cb) return ESP_ERR_INVALID_STATE; } -void IRAM_ATTR mac_bb_power_up_cb_execute(void) +void mac_bb_power_up_cb_execute(void) { for (int i = 0; i < MAC_BB_POWER_UP_CB_NO; i++) { if (s_mac_bb_power_up_cb[i]) { @@ -221,7 +221,7 @@ bool modem_domain_pd_allowed(void) #endif } -uint32_t IRAM_ATTR sleep_modem_reject_triggers(void) +uint32_t sleep_modem_reject_triggers(void) { uint32_t reject_triggers = 0; #if SOC_PM_SUPPORT_PMU_MODEM_STATE diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index a0e8f8fd16..d137502922 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -162,6 +162,12 @@ #elif CONFIG_IDF_TARGET_ESP32C2 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (118) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (9) +# if !CONFIG_PM_SLP_IRAM_OPT + #undef DEFAULT_SLEEP_OUT_OVERHEAD_US + #define DEFAULT_SLEEP_OUT_OVERHEAD_US (2779) + #undef DEFAULT_HARDWARE_OUT_OVERHEAD_US + #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (157) +# endif #elif CONFIG_IDF_TARGET_ESP32C6 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (318) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (56) @@ -216,6 +222,12 @@ #define CHECK_SOURCE(source, value, mask) ((s_config.wakeup_triggers & mask) && \ (source == value)) +#if CONFIG_PM_SLP_IRAM_OPT +# define SLEEP_FN_ATTR IRAM_ATTR +#else +# define SLEEP_FN_ATTR +#endif + #define MAX_DSLP_HOOKS 3 static esp_deep_sleep_cb_t s_dslp_cb[MAX_DSLP_HOOKS] = {0}; @@ -525,7 +537,7 @@ static uint32_t s_stopped_tgwdt_bmap = 0; #endif // Must be called from critical sections. -static void IRAM_ATTR suspend_timers(uint32_t sleep_flags) { +static SLEEP_FN_ATTR void suspend_timers(uint32_t sleep_flags) { if (!(sleep_flags & RTC_SLEEP_PD_XTAL)) { #if SOC_SLEEP_TGWDT_STOP_WORKAROUND /* If timegroup implemented task watchdog or interrupt watchdog is running, we have to stop it. */ @@ -547,7 +559,7 @@ static void IRAM_ATTR suspend_timers(uint32_t sleep_flags) { } // Must be called from critical sections. -static void IRAM_ATTR resume_timers(uint32_t sleep_flags) { +static SLEEP_FN_ATTR void resume_timers(uint32_t sleep_flags) { if (!(sleep_flags & RTC_SLEEP_PD_XTAL)) { #if SOC_SLEEP_SYSTIMER_STALL_WORKAROUND for (uint32_t counter_id = 0; counter_id < SOC_SYSTIMER_COUNTER_NUM; ++counter_id) { @@ -567,7 +579,7 @@ static void IRAM_ATTR resume_timers(uint32_t sleep_flags) { } // [refactor-todo] provide target logic for body of uart functions below -static void IRAM_ATTR flush_uarts(void) +static SLEEP_FN_ATTR void flush_uarts(void) { for (int i = 0; i < SOC_UART_HP_NUM; ++i) { if (uart_ll_is_enabled(i)) { @@ -582,7 +594,7 @@ static uint32_t s_suspended_uarts_bmap = 0; * Suspend enabled uarts and return suspended uarts bit map. * Must be called from critical sections. */ -FORCE_INLINE_ATTR void suspend_uarts(void) +static SLEEP_FN_ATTR void suspend_uarts(void) { s_suspended_uarts_bmap = 0; for (int i = 0; i < SOC_UART_HP_NUM; ++i) { @@ -603,7 +615,7 @@ FORCE_INLINE_ATTR void suspend_uarts(void) } // Must be called from critical sections -FORCE_INLINE_ATTR void resume_uarts(void) +static SLEEP_FN_ATTR void resume_uarts(void) { for (int i = 0; i < SOC_UART_HP_NUM; ++i) { if (s_suspended_uarts_bmap & 0x1) { @@ -629,7 +641,7 @@ FORCE_INLINE_ATTR void resume_uarts(void) completion time has exceeded the wakeup time, we should abandon the flush, skip the sleep and return ESP_ERR_SLEEP_REJECT. */ -FORCE_INLINE_ATTR bool light_sleep_uart_prepare(uint32_t sleep_flags, int64_t sleep_duration) +static SLEEP_FN_ATTR bool light_sleep_uart_prepare(uint32_t sleep_flags, int64_t sleep_duration) { bool should_skip_sleep = false; #if !SOC_PM_SUPPORT_TOP_PD || !CONFIG_ESP_CONSOLE_UART @@ -662,7 +674,7 @@ FORCE_INLINE_ATTR bool light_sleep_uart_prepare(uint32_t sleep_flags, int64_t sl /** * These save-restore workaround should be moved to lower layer */ -FORCE_INLINE_ATTR void misc_modules_sleep_prepare(uint32_t sleep_flags, bool deep_sleep) +static SLEEP_FN_ATTR void misc_modules_sleep_prepare(uint32_t sleep_flags, bool deep_sleep) { if (deep_sleep){ for (int n = 0; n < MAX_DSLP_HOOKS; n++) { @@ -716,7 +728,7 @@ FORCE_INLINE_ATTR void misc_modules_sleep_prepare(uint32_t sleep_flags, bool dee /** * These save-restore workaround should be moved to lower layer */ -FORCE_INLINE_ATTR void misc_modules_wake_prepare(uint32_t sleep_flags) +static SLEEP_FN_ATTR void misc_modules_wake_prepare(uint32_t sleep_flags) { #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP if (sleep_flags & PMU_SLEEP_PD_TOP) { @@ -760,7 +772,7 @@ FORCE_INLINE_ATTR void misc_modules_wake_prepare(uint32_t sleep_flags) #endif } -static IRAM_ATTR void sleep_low_power_clock_calibration(bool is_dslp) +static SLEEP_FN_ATTR void sleep_low_power_clock_calibration(bool is_dslp) { // Calibrate rtc slow clock #ifdef CONFIG_ESP_SYSTEM_RTC_EXT_XTAL @@ -800,7 +812,133 @@ static IRAM_ATTR void sleep_low_power_clock_calibration(bool is_dslp) inline static uint32_t call_rtc_sleep_start(uint32_t reject_triggers, uint32_t lslp_mem_inf_fpu, bool dslp); -static esp_err_t IRAM_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t clk_flags, esp_sleep_mode_t mode, bool allow_sleep_rejection) +#if SOC_PMU_SUPPORTED +static esp_err_t IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint32_t reject_triggers, bool deep_sleep, pmu_sleep_config_t *config) +#else +static esp_err_t IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint32_t reject_triggers, bool deep_sleep, rtc_sleep_config_t *config) +#endif +{ + esp_err_t result = ESP_OK; +#if CONFIG_ESP_SLEEP_DEBUG + if (s_sleep_ctx != NULL) { + s_sleep_ctx->wakeup_triggers = s_config.wakeup_triggers; + } +#endif + if (deep_sleep) { +#if SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP + esp_sleep_isolate_digital_gpio(); +#endif + +#if ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB && SOC_DEEP_SLEEP_SUPPORTED +#if SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY + esp_set_deep_sleep_wake_stub_default_entry(); +#elif !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP && SOC_RTC_FAST_MEM_SUPPORTED + /* If not possible stack is in RTC FAST memory, use the ROM function to calculate the CRC and save ~140 bytes IRAM */ + set_rtc_memory_crc(); +#endif // SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY +#endif + +#if SOC_DCDC_SUPPORTED + uint64_t ldo_increased_us = rtc_time_slowclk_to_us(rtc_time_get() - s_config.rtc_ticks_at_ldo_prepare, s_config.rtc_clk_cal_period); + if (ldo_increased_us < LDO_POWER_TAKEOVER_PREPARATION_TIME_US) { + esp_rom_delay_us(LDO_POWER_TAKEOVER_PREPARATION_TIME_US - ldo_increased_us); + } + pmu_sleep_shutdown_dcdc(); +#endif + + // Enter Deep Sleep +#if!ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB || SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY || !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP +#if SOC_PMU_SUPPORTED + result = call_rtc_sleep_start(reject_triggers, config->power.hp_sys.dig_power.mem_dslp, deep_sleep); +#else + result = call_rtc_sleep_start(reject_triggers, config->lslp_mem_inf_fpu, deep_sleep); +#endif +#else + /* Otherwise, need to call the dedicated soc function for this */ + result = rtc_deep_sleep_start(s_config.wakeup_triggers, reject_triggers); +#endif + } else { + suspend_timers(sleep_flags); + /* Cache Suspend 1: will wait cache idle in cache suspend */ + suspend_cache(); + /* On esp32c6, only the lp_aon pad hold function can only hold the GPIO state in the active mode. + In order to avoid the leakage of the SPI cs pin, hold it here */ + +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + if(!(sleep_flags & RTC_SLEEP_PD_VDDSDIO) && (sleep_flags & PMU_SLEEP_PD_TOP)) { +#if CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND + /* Cache suspend also means SPI bus IDLE, then we can hold SPI CS pin safely */ +#if !CONFIG_IDF_TARGET_ESP32H2 // ESP32H2 TODO IDF-7359 + gpio_ll_hold_en(&GPIO, MSPI_IOMUX_PIN_NUM_CS0); +#endif +#endif +#if CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND && CONFIG_SPIRAM + /* Cache suspend also means SPI bus IDLE, then we can hold SPI CS pin safely */ + gpio_ll_hold_en(&GPIO, MSPI_IOMUX_PIN_NUM_CS1); +#endif + } +#endif + +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD + if (sleep_flags & PMU_SLEEP_PD_TOP) { + esp_sleep_mmu_retention(true); + } +#endif + +#if SOC_DCDC_SUPPORTED && !CONFIG_ESP_SLEEP_KEEP_DCDC_ALWAYS_ON + uint64_t ldo_increased_us = rtc_time_slowclk_to_us(rtc_time_get() - s_config.rtc_ticks_at_ldo_prepare, s_config.rtc_clk_cal_period); + if (ldo_increased_us < LDO_POWER_TAKEOVER_PREPARATION_TIME_US) { + esp_rom_delay_us(LDO_POWER_TAKEOVER_PREPARATION_TIME_US - ldo_increased_us); + } + pmu_sleep_shutdown_dcdc(); +#endif + +#if SOC_PMU_SUPPORTED +#if SOC_PM_CPU_RETENTION_BY_SW && ESP_SLEEP_POWER_DOWN_CPU + esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_GOTO_SLEEP, (void *)0); + if (sleep_flags & (PMU_SLEEP_PD_CPU | PMU_SLEEP_PD_TOP)) { + result = esp_sleep_cpu_retention(pmu_sleep_start, s_config.wakeup_triggers, reject_triggers, config->power.hp_sys.dig_power.mem_dslp, deep_sleep); + } else +#endif + { +#if !CONFIG_FREERTOS_UNICORE && ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW + // Skip smp retention if CPU power domain power-down is not allowed + esp_sleep_cpu_skip_retention(); +#endif + result = call_rtc_sleep_start(reject_triggers, config->power.hp_sys.dig_power.mem_dslp, deep_sleep); + } + esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_EXIT_SLEEP, (void *)0); +#else + result = call_rtc_sleep_start(reject_triggers, config->lslp_mem_inf_fpu, deep_sleep); +#endif + +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD + if (sleep_flags & PMU_SLEEP_PD_TOP) { + esp_sleep_mmu_retention(false); + } +#endif + +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + /* Unhold the SPI CS pin */ + if(!(sleep_flags & RTC_SLEEP_PD_VDDSDIO) && (sleep_flags & PMU_SLEEP_PD_TOP)) { +#if CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND +#if !CONFIG_IDF_TARGET_ESP32H2 // ESP32H2 TODO IDF-7359 + gpio_ll_hold_dis(&GPIO, MSPI_IOMUX_PIN_NUM_CS0); +#endif +#endif +#if CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND && CONFIG_SPIRAM + gpio_ll_hold_dis(&GPIO, MSPI_IOMUX_PIN_NUM_CS1); +#endif + } +#endif + /* Cache Resume 1: Resume cache for continue running*/ + resume_cache(); + resume_timers(sleep_flags); + } + return result; +} + +static esp_err_t SLEEP_FN_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t clk_flags, esp_sleep_mode_t mode, bool allow_sleep_rejection) { // Stop UART output so that output is not lost due to APB frequency change. // For light sleep, suspend UART output — it will resume after wakeup. @@ -980,123 +1118,7 @@ static esp_err_t IRAM_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t clk_fl esp_sleep_cpu_skip_retention(); #endif } else { -#if CONFIG_ESP_SLEEP_DEBUG - if (s_sleep_ctx != NULL) { - s_sleep_ctx->wakeup_triggers = s_config.wakeup_triggers; - } -#endif - if (deep_sleep) { -#if SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP - esp_sleep_isolate_digital_gpio(); -#endif - -#if ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB && SOC_DEEP_SLEEP_SUPPORTED -#if SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY - esp_set_deep_sleep_wake_stub_default_entry(); -#elif !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP && SOC_RTC_FAST_MEM_SUPPORTED - /* If not possible stack is in RTC FAST memory, use the ROM function to calculate the CRC and save ~140 bytes IRAM */ - set_rtc_memory_crc(); -#endif // SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY -#endif - -#if SOC_DCDC_SUPPORTED - uint64_t ldo_increased_us = rtc_time_slowclk_to_us(rtc_time_get() - s_config.rtc_ticks_at_ldo_prepare, s_config.rtc_clk_cal_period); - if (ldo_increased_us < LDO_POWER_TAKEOVER_PREPARATION_TIME_US) { - esp_rom_delay_us(LDO_POWER_TAKEOVER_PREPARATION_TIME_US - ldo_increased_us); - } - pmu_sleep_shutdown_dcdc(); -#endif - - // Enter Deep Sleep -#if!ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB || SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY || !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP -#if SOC_PMU_SUPPORTED - result = call_rtc_sleep_start(reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); -#else - result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); -#endif -#else - /* Otherwise, need to call the dedicated soc function for this */ - result = rtc_deep_sleep_start(s_config.wakeup_triggers, reject_triggers); -#endif - } else { - suspend_timers(sleep_flags); - /* Cache Suspend 1: will wait cache idle in cache suspend */ - suspend_cache(); - /* On esp32c6, only the lp_aon pad hold function can only hold the GPIO state in the active mode. - In order to avoid the leakage of the SPI cs pin, hold it here */ - -#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP - if(!(sleep_flags & RTC_SLEEP_PD_VDDSDIO) && (sleep_flags & PMU_SLEEP_PD_TOP)) { -#if CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND - /* Cache suspend also means SPI bus IDLE, then we can hold SPI CS pin safely */ -#if !CONFIG_IDF_TARGET_ESP32H2 // ESP32H2 TODO IDF-7359 - gpio_ll_hold_en(&GPIO, MSPI_IOMUX_PIN_NUM_CS0); -#endif -#endif -#if CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND && CONFIG_SPIRAM - /* Cache suspend also means SPI bus IDLE, then we can hold SPI CS pin safely */ - gpio_ll_hold_en(&GPIO, MSPI_IOMUX_PIN_NUM_CS1); -#endif - } -#endif - -#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD - if (sleep_flags & PMU_SLEEP_PD_TOP) { - esp_sleep_mmu_retention(true); - } -#endif - -#if SOC_DCDC_SUPPORTED && !CONFIG_ESP_SLEEP_KEEP_DCDC_ALWAYS_ON - uint64_t ldo_increased_us = rtc_time_slowclk_to_us(rtc_time_get() - s_config.rtc_ticks_at_ldo_prepare, s_config.rtc_clk_cal_period); - if (ldo_increased_us < LDO_POWER_TAKEOVER_PREPARATION_TIME_US) { - esp_rom_delay_us(LDO_POWER_TAKEOVER_PREPARATION_TIME_US - ldo_increased_us); - } - pmu_sleep_shutdown_dcdc(); -#endif - -#if SOC_PMU_SUPPORTED -#if SOC_PM_CPU_RETENTION_BY_SW && ESP_SLEEP_POWER_DOWN_CPU - esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_GOTO_SLEEP, (void *)0); - if (sleep_flags & (PMU_SLEEP_PD_CPU | PMU_SLEEP_PD_TOP)) { - result = esp_sleep_cpu_retention(pmu_sleep_start, s_config.wakeup_triggers, reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); - } else -#endif - { -#if !CONFIG_FREERTOS_UNICORE && ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW - // Skip smp retention if CPU power domain power-down is not allowed - esp_sleep_cpu_skip_retention(); -#endif - result = call_rtc_sleep_start(reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); - } - esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_EXIT_SLEEP, (void *)0); -#else - result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); -#endif - -#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD - if (sleep_flags & PMU_SLEEP_PD_TOP) { - esp_sleep_mmu_retention(false); - } -#endif - -#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP - /* Unhold the SPI CS pin */ - if(!(sleep_flags & RTC_SLEEP_PD_VDDSDIO) && (sleep_flags & PMU_SLEEP_PD_TOP)) { -#if CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND -#if !CONFIG_IDF_TARGET_ESP32H2 // ESP32H2 TODO IDF-7359 - gpio_ll_hold_dis(&GPIO, MSPI_IOMUX_PIN_NUM_CS0); -#endif -#endif -#if CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND && CONFIG_SPIRAM - gpio_ll_hold_dis(&GPIO, MSPI_IOMUX_PIN_NUM_CS1); -#endif - } -#endif - - /* Cache Resume 1: Resume cache for continue running*/ - resume_cache(); - resume_timers(sleep_flags); - } + result = esp_sleep_start_safe(sleep_flags, reject_triggers, deep_sleep, &config); } #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION if (sleep_flags & RTC_SLEEP_PD_VDDSDIO) { @@ -1119,6 +1141,21 @@ static esp_err_t IRAM_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t clk_fl if (!deep_sleep) { if (result == ESP_OK) { +#if !CONFIG_PM_SLP_IRAM_OPT && !CONFIG_IDF_TARGET_ESP32 +#if CONFIG_SPIRAM +# if CONFIG_IDF_TARGET_ESP32P4 + cache_ll_writeback_all(CACHE_LL_LEVEL_ALL, CACHE_TYPE_DATA, CACHE_LL_ID_ALL); +# else + Cache_WriteBack_All(); +# endif +#endif + /* When the IRAM optimization for the sleep flow is disabled, all + * cache contents are forcibly invalidated before exiting the sleep + * flow. This ensures that the code execution time of sleep exit + * flow remains consistent, allowing the use of ccount to + * dynamically calculate the sleep adjustment time. */ + cache_ll_invalidate_all(CACHE_LL_LEVEL_ALL, CACHE_TYPE_ALL, CACHE_LL_ID_ALL); +#endif s_config.ccount_ticks_record = esp_cpu_get_cycle_count(); #if SOC_PM_RETENTION_SW_TRIGGER_REGDMA if (sleep_flags & PMU_SLEEP_PD_TOP) { @@ -1254,9 +1291,9 @@ esp_err_t IRAM_ATTR esp_deep_sleep_try_to_start(void) * Placed into IRAM as flash may need some time to be powered on. */ static esp_err_t esp_light_sleep_inner(uint32_t sleep_flags, uint32_t clk_flags, - uint32_t flash_enable_time_us) IRAM_ATTR __attribute__((noinline)); + uint32_t flash_enable_time_us) __attribute__((noinline)); -static esp_err_t esp_light_sleep_inner(uint32_t sleep_flags, uint32_t clk_flags, +static SLEEP_FN_ATTR esp_err_t esp_light_sleep_inner(uint32_t sleep_flags, uint32_t clk_flags, uint32_t flash_enable_time_us) { #if SOC_CONFIGURABLE_VDDSDIO_SUPPORTED @@ -1687,7 +1724,7 @@ esp_err_t esp_sleep_enable_vad_wakeup(void) } #endif -static esp_err_t timer_wakeup_prepare(int64_t sleep_duration) +static SLEEP_FN_ATTR esp_err_t timer_wakeup_prepare(int64_t sleep_duration) { if (sleep_duration < 0) { sleep_duration = 0; @@ -2325,7 +2362,7 @@ FORCE_INLINE_ATTR bool top_domain_pd_allowed(void) { } #endif -static uint32_t get_power_down_flags(void) +static SLEEP_FN_ATTR uint32_t get_power_down_flags(void) { // Where needed, convert AUTO options to ON. Later interpret AUTO as OFF. @@ -2502,7 +2539,7 @@ static uint32_t get_power_down_flags(void) return pd_flags; } -static uint32_t get_sleep_flags(uint32_t sleep_flags, bool deepsleep) +static SLEEP_FN_ATTR uint32_t get_sleep_flags(uint32_t sleep_flags, bool deepsleep) { // Override user-configured FOSC power modes. if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_RTC_USE_RC_FAST_MODE]) { @@ -2554,7 +2591,7 @@ static uint32_t get_sleep_flags(uint32_t sleep_flags, bool deepsleep) return sleep_flags; } -static uint32_t get_sleep_clock_icg_flags(void) +static SLEEP_FN_ATTR uint32_t get_sleep_clock_icg_flags(void) { uint32_t clk_flags = 0; diff --git a/components/esp_lcd/Kconfig b/components/esp_lcd/Kconfig index a53f8514a9..0cc7d93381 100644 --- a/components/esp_lcd/Kconfig +++ b/components/esp_lcd/Kconfig @@ -10,6 +10,7 @@ menu "ESP-Driver:LCD Controller Configurations" config LCD_RGB_ISR_IRAM_SAFE bool "RGB LCD ISR IRAM-Safe" select GDMA_ISR_HANDLER_IN_IRAM # bounce buffer mode relies on GDMA EOF interrupt + select ESP_PERIPH_CTRL_FUNC_IN_IRAM default n help Ensure the LCD interrupt is IRAM-Safe by allowing the interrupt handler to be diff --git a/components/esp_pm/Kconfig b/components/esp_pm/Kconfig index 63340d08db..012bca0a22 100644 --- a/components/esp_pm/Kconfig +++ b/components/esp_pm/Kconfig @@ -1,4 +1,14 @@ menu "Power Management" + + config PM_SLEEP_FUNC_IN_IRAM + bool "Place Power Management module functions in IRAM" if IDF_TARGET_ESP32C2 + default y + select PM_SLP_IRAM_OPT if SOC_LIGHT_SLEEP_SUPPORTED + select PM_RTOS_IDLE_OPT if FREERTOS_USE_TICKLESS_IDLE + select ESP_PERIPH_CTRL_FUNC_IN_IRAM + select ESP_REGI2C_CTRL_FUNC_IN_IRAM + + config PM_ENABLE bool "Support for power management" # SMP FreeRTOS currently does not support power management IDF-4997 @@ -49,7 +59,8 @@ menu "Power Management" config PM_SLP_IRAM_OPT bool "Put lightsleep related codes in internal RAM" - depends on SOC_LIGHT_SLEEP_SUPPORTED && ESP_TIMER_IN_IRAM + depends on SOC_LIGHT_SLEEP_SUPPORTED + select ESP_TIMER_IN_IRAM help If enabled, about 2.1KB of lightsleep related source code would be in IRAM and chip would sleep longer for 310us at 160MHz CPU frequency most each time. diff --git a/components/esp_pm/linker.lf b/components/esp_pm/linker.lf index a27799567b..95a192d0c6 100644 --- a/components/esp_pm/linker.lf +++ b/components/esp_pm/linker.lf @@ -7,44 +7,45 @@ entries: if PM_SLP_IRAM_OPT = y: pm_impl:esp_pm_impl_get_cpu_freq (noflash) + if FREERTOS_USE_TICKLESS_IDLE = y: + pm_impl:vApplicationSleep (noflash) [mapping:esp_hw_support_pm] archive: libesp_hw_support.a entries: if PM_SLP_IRAM_OPT = y: - sleep_modes:esp_light_sleep_start (noflash) - sleep_modes:esp_sleep_enable_timer_wakeup (noflash) - sleep_modes:timer_wakeup_prepare (noflash) - sleep_modes:get_power_down_flags (noflash) - sleep_modes:get_sleep_flags (noflash) - sleep_modes:get_sleep_clock_icg_flags (noflash) + if SOC_LIGHT_SLEEP_SUPPORTED = y: + sleep_modes:esp_light_sleep_start (noflash) + sleep_modes:esp_sleep_enable_timer_wakeup (noflash) + sleep_modem:modem_domain_pd_allowed (noflash) + sleep_modem:periph_inform_out_light_sleep_overhead (noflash) + sleep_modem:sleep_modem_reject_triggers (noflash) + if ESP_PHY_MAC_BB_PD = y: + sleep_modem:mac_bb_power_down_cb_execute (noflash) + sleep_modem:mac_bb_power_up_cb_execute (noflash) + if GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL = y: + sleep_gpio:gpio_sleep_mode_config_apply (noflash) + if SOC_PM_SUPPORT_TOP_PD = y: + sleep_clock:clock_domain_pd_allowed (noflash) + sleep_system_peripheral:peripheral_domain_pd_allowed (noflash) + if SOC_PM_CPU_RETENTION_BY_RTCCNTL = y && (PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP = y || SOC_PM_SUPPORT_TAGMEM_PD = y): + sleep_cpu:sleep_enable_cpu_retention (noflash) + if PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP = y || (SOC_CPU_IN_TOP_DOMAIN = y && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP = y): + sleep_cpu:cpu_domain_pd_allowed (noflash) + if SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD = y && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP = y: + sleep_mmu:mmu_domain_pd_allowed (noflash) esp_clk:esp_clk_slowclk_cal_set (noflash) esp_clk:esp_clk_slowclk_cal_get (noflash) esp_clk:esp_rtc_get_time_us (noflash) esp_clk:esp_clk_private_lock (noflash) esp_clk:esp_clk_private_unlock (noflash) - if SOC_RTC_MEM_SUPPORTED = y: - esp_clk:calc_checksum (noflash) if SOC_SYSTIMER_SUPPORTED = y: systimer (noflash) - if GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL = y: - sleep_gpio:gpio_sleep_mode_config_apply (noflash) - if SOC_PM_CPU_RETENTION_BY_RTCCNTL = y && (PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP = y || SOC_PM_SUPPORT_TAGMEM_PD = y): - sleep_cpu:sleep_enable_cpu_retention (noflash) - if PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP = y || (SOC_CPU_IN_TOP_DOMAIN = y && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP = y): - sleep_cpu:cpu_domain_pd_allowed (noflash) - if SOC_PM_SUPPORT_TOP_PD = y: - sleep_clock:clock_domain_pd_allowed (noflash) - sleep_system_peripheral:peripheral_domain_pd_allowed (noflash) - if SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD = y && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP = y: - sleep_mmu:mmu_domain_pd_allowed (noflash) - sleep_modem:modem_domain_pd_allowed (noflash) - sleep_modem:periph_inform_out_light_sleep_overhead (noflash) - sar_periph_ctrl:sar_periph_ctrl_power_disable (noflash) + if IDF_TARGET_ESP32H21 != y && IDF_TARGET_ESP32H4 != y: + sar_periph_ctrl:sar_periph_ctrl_power_disable (noflash) if SOC_TEMP_SENSOR_SUPPORTED = y: sar_periph_ctrl_common:temperature_sensor_power_acquire (noflash) sar_periph_ctrl_common:temperature_sensor_power_release (noflash) - sar_periph_ctrl_common:temperature_sensor_get_raw_value (noflash) sar_periph_ctrl_common:temp_sensor_get_raw_value (noflash) regi2c_ctrl:regi2c_saradc_enable (noflash) regi2c_ctrl:regi2c_saradc_disable (noflash) @@ -57,17 +58,6 @@ archive: libesp_system.a entries: if PM_RTOS_IDLE_OPT = y: freertos_hooks:esp_vApplicationIdleHook (noflash) - if PM_SLP_IRAM_OPT = y: - task_wdt:idle_hook_cb (noflash) - task_wdt:task_wdt_timer_feed (noflash) - task_wdt:find_entry_and_check_all_reset (noflash) - task_wdt:find_entry_from_task_handle_and_check_all_reset (noflash) - task_wdt:esp_task_wdt_reset (noflash) - task_wdt:esp_task_wdt_reset_user (noflash) - if ESP_TASK_WDT_USE_ESP_TIMER = y: - task_wdt_impl_esp_timer:esp_task_wdt_impl_timer_feed (noflash) - else: - task_wdt_impl_timergroup:esp_task_wdt_impl_timer_feed (noflash) [mapping:esp_timer_pm] archive: libesp_timer.a diff --git a/components/esp_pm/pm_impl.c b/components/esp_pm/pm_impl.c index 4d5031a360..d320bd150b 100644 --- a/components/esp_pm/pm_impl.c +++ b/components/esp_pm/pm_impl.c @@ -803,7 +803,7 @@ static inline void IRAM_ATTR other_core_should_skip_light_sleep(int core_id) #endif } -void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) +void vApplicationSleep( TickType_t xExpectedIdleTime ) { portENTER_CRITICAL(&s_switch_lock); int core_id = xPortGetCoreID(); diff --git a/components/esp_system/task_wdt/task_wdt.c b/components/esp_system/task_wdt/task_wdt.c index 4b374c467d..60e256f121 100644 --- a/components/esp_system/task_wdt/task_wdt.c +++ b/components/esp_system/task_wdt/task_wdt.c @@ -36,6 +36,12 @@ #define BACKTRACE_MSG "backtrace" #endif +#if CONFIG_PM_RTOS_IDLE_OPT +# define TASK_WDT_FN_ATTR IRAM_ATTR +#else +# define TASK_WDT_FN_ATTR +#endif + /* We will use this function in order to simulate an `abort()` occurring in * a different context than the one it's called from. */ extern void xt_unhandled_exception(void *frame); @@ -96,7 +102,7 @@ static char core_user_names[CONFIG_FREERTOS_NUMBER_OF_CORES][CORE_USER_NAME_LEN] * @brief Reset the timer and reset flags of each entry * When entering this function, the spinlock has already been taken, no need to take it back. */ -static void task_wdt_timer_feed(void) +static TASK_WDT_FN_ATTR void task_wdt_timer_feed(void) { esp_task_wdt_impl_timer_feed(p_twdt_obj->impl_ctx); @@ -114,7 +120,7 @@ static void task_wdt_timer_feed(void) * @param[out] all_reset Whether all entries have been reset * @return Whether the user entry exists */ -static bool find_entry_and_check_all_reset(twdt_entry_t *user_entry, bool *all_reset) +static TASK_WDT_FN_ATTR bool find_entry_and_check_all_reset(twdt_entry_t *user_entry, bool *all_reset) { bool found_user_entry = false; bool found_non_reset = false; @@ -139,7 +145,7 @@ static bool find_entry_and_check_all_reset(twdt_entry_t *user_entry, bool *all_r * @param[out] all_reset Whether all entries have been reset * @return Task entry, or NULL if not found */ -static twdt_entry_t *find_entry_from_task_handle_and_check_all_reset(TaskHandle_t handle, bool *all_reset) +static TASK_WDT_FN_ATTR twdt_entry_t *find_entry_from_task_handle_and_check_all_reset(TaskHandle_t handle, bool *all_reset) { twdt_entry_t *target = NULL; bool found_non_reset = false; @@ -448,7 +454,7 @@ static void task_wdt_timeout_handling(int cores_fail, bool panic) * * @return Whether the idle tasks should continue idling */ -static bool idle_hook_cb(void) +static TASK_WDT_FN_ATTR bool idle_hook_cb(void) { #if CONFIG_FREERTOS_SMP esp_task_wdt_reset_user(core_user_handles[xPortGetCoreID()]); @@ -687,7 +693,7 @@ esp_err_t esp_task_wdt_add_user(const char *user_name, esp_task_wdt_user_handle_ return ret; } -esp_err_t esp_task_wdt_reset(void) +esp_err_t TASK_WDT_FN_ATTR esp_task_wdt_reset(void) { ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized"); esp_err_t ret; @@ -711,7 +717,7 @@ err: return ret; } -esp_err_t esp_task_wdt_reset_user(esp_task_wdt_user_handle_t user_handle) +esp_err_t TASK_WDT_FN_ATTR esp_task_wdt_reset_user(esp_task_wdt_user_handle_t user_handle) { ESP_RETURN_ON_FALSE(user_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized"); diff --git a/components/esp_system/task_wdt/task_wdt_impl_esp_timer.c b/components/esp_system/task_wdt/task_wdt_impl_esp_timer.c index d5219dcfab..d6441f7d43 100644 --- a/components/esp_system/task_wdt/task_wdt_impl_esp_timer.c +++ b/components/esp_system/task_wdt/task_wdt_impl_esp_timer.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +17,12 @@ #include "esp_timer.h" #include "esp_private/esp_task_wdt_impl.h" +#if CONFIG_PM_SLP_IRAM_OPT +# define TASK_WDT_FN_ATTR IRAM_ATTR +#else +# define TASK_WDT_FN_ATTR +#endif + /** * Context for the software implementation of the Task WatchDog Timer. * This will be passed as a parameter to public functions below. */ @@ -50,7 +56,7 @@ esp_err_t esp_task_wdt_impl_timer_allocate(const esp_task_wdt_config_t *config, esp_err_t ret = esp_timer_create(&timer_args, &ctx->sw_timer); ESP_GOTO_ON_FALSE((ret == ESP_OK), ret, reterr, TAG, "could not start periodic timer"); - /* Configure it as a periodic timer, so that we check the Tasks everytime it is triggered. + /* Configure it as a periodic timer, so that we check the Tasks every time it is triggered. * No need to start the timer here, it will be started later with `esp_task_wdt_impl_timer_restart` */ ctx->period_ms = config->timeout_ms; @@ -89,7 +95,7 @@ void esp_task_wdt_impl_timer_free(twdt_ctx_t obj) } } -esp_err_t esp_task_wdt_impl_timer_feed(twdt_ctx_t obj) +esp_err_t TASK_WDT_FN_ATTR esp_task_wdt_impl_timer_feed(twdt_ctx_t obj) { esp_err_t ret = ESP_OK; const twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj; diff --git a/components/esp_system/task_wdt/task_wdt_impl_timergroup.c b/components/esp_system/task_wdt/task_wdt_impl_timergroup.c index c602f20186..ec408377f6 100644 --- a/components/esp_system/task_wdt/task_wdt_impl_timergroup.c +++ b/components/esp_system/task_wdt/task_wdt_impl_timergroup.c @@ -32,6 +32,12 @@ #define TWDT_TIMER_GROUP 0 #define TWDT_INTR_SOURCE SYS_TG0_WDT_INTR_SOURCE +#if CONFIG_PM_SLP_IRAM_OPT +# define TASK_WDT_FN_ATTR IRAM_ATTR +#else +# define TASK_WDT_FN_ATTR +#endif + /** * Context for the software implementation of the Task WatchDog Timer. * This will be passed as a parameter to public functions below. */ @@ -177,7 +183,7 @@ void esp_task_wdt_impl_timer_free(twdt_ctx_t obj) } } -esp_err_t esp_task_wdt_impl_timer_feed(twdt_ctx_t obj) +esp_err_t TASK_WDT_FN_ATTR esp_task_wdt_impl_timer_feed(twdt_ctx_t obj) { esp_err_t ret = ESP_OK; twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj; diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index 12bb27c372..0793d53fdf 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -343,7 +343,8 @@ menu "Wi-Fi" config ESP_WIFI_SLP_IRAM_OPT bool "WiFi SLP IRAM speed optimization" select PM_SLP_DEFAULT_PARAMS_OPT - select PERIPH_CTRL_FUNC_IN_IRAM + select PM_SLEEP_FUNC_IN_IRAM + select ESP_PERIPH_CTRL_FUNC_IN_IRAM default y if SOC_WIFI_HE_SUPPORT help Select this option to place called Wi-Fi library TBTT process and receive beacon functions in IRAM. diff --git a/docs/en/api-guides/low-power-mode/low-power-mode-soc.rst b/docs/en/api-guides/low-power-mode/low-power-mode-soc.rst index 460a5c7bf5..f4eec662d7 100644 --- a/docs/en/api-guides/low-power-mode/low-power-mode-soc.rst +++ b/docs/en/api-guides/low-power-mode/low-power-mode-soc.rst @@ -285,6 +285,28 @@ This section introduces the recommended configuration and configuration steps fo Due to the shared power pins between flash and PSRAM, cutting power to PSRAM would result in data loss. Therefore, to ensure light sleep does not disrupt program execution, enabling this option requires that the system does not utilize PSRAM. +.. only:: esp32c2 + + - To reduce the on-chip RAM usage of the Power Management module (:ref:CONFIG_PM_SLEEP_FUNC_IN_IRAM), the options in the table below are used to control whether individual components of the Power Management module enable or disable on-chip RAM optimization when :ref:CONFIG_PM_SLEEP_FUNC_IN_IRAM is disabled. + + .. list-table:: + :header-rows: 1 + :widths: 30 60 + + * - Configuration Name + - Configuration Description + + * - :ref:`CONFIG_PM_SLP_IRAM_OPT` + - When this option is enabled, the software flow for entering and exiting light/deep sleep is linked to on-chip RAM, which shortens the sleep transition time but consumes more on-chip RAM resources. When this option is disabled, the light/deep sleep software flow is compiled into flash memory instead, resulting in longer sleep transition time but saving on-chip RAM resources. + + * - :ref:`CONFIG_PM_RTOS_IDLE_OPT` + - In auto light sleep mode, when this option is enabled, the FreeRTOS system tick, IDLE task hook functions, and the FreeRTOS tickless idle software context are linked to on-chip RAM. Otherwise, they will be linked to flash memory. + + * - :ref:`CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM` + - When this option is enabled, the software implementation related to peripheral clock and reset control will be linked to on-chip RAM. Otherwise, it will be linked to flash memory. + + * - :ref:`CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM` + - When this option is enabled, the software implementation related to Analog I2C read/write access will be linked to on-chip RAM. Otherwise, it will be linked to flash memory. Configuration Steps: diff --git a/docs/zh_CN/api-guides/low-power-mode/low-power-mode-soc.rst b/docs/zh_CN/api-guides/low-power-mode/low-power-mode-soc.rst index 24c36f23d4..8463c03272 100644 --- a/docs/zh_CN/api-guides/low-power-mode/low-power-mode-soc.rst +++ b/docs/zh_CN/api-guides/low-power-mode/low-power-mode-soc.rst @@ -285,6 +285,28 @@ Light-sleep 模式配置 由于 flash 和 PSRAM 共用供电管脚,PSRAM 关闭供电将会导致数据丢失,因此,为保证 light sleep 不破坏程序运行状态,启用该选项的前提是系统没有使用 PSRAM。 +.. only:: esp32c2 + + - 减少 Power Managemet 模块片内 RAM 内存资源使用量 (:ref:`CONFIG_PM_SLEEP_FUNC_IN_IRAM`),当 :ref:`CONFIG_PM_SLEEP_FUNC_IN_IRAM` 禁止时,下面表格中选项分别用于控制 Power Management 模块各部分组件启用或禁止片内 RAM 优化 + + .. list-table:: + :header-rows: 1 + :widths: 60 30 + + * - 配置项 + - 描述 + + * - :ref:`CONFIG_PM_SLP_IRAM_OPT` + - 使能该选项后执行 light / deep sleep 软件流程上下文被链接到片内 RAM 空间,这意味着系统进出睡眠时间变短,消耗更多的片内 RAM 资源;禁止该选项,light / deep sleep 软件流程上下文将被编译到 Flash 空间,这意味着系统进出睡眠时间变长,但会节约片内 RAM 资源。 + + * - :ref:`CONFIG_PM_RTOS_IDLE_OPT` + - auto light sleep 模式下,使能该选项后,FreeRTOS 系统滴答,IDLE 任务回调函数及 FreeRTOS Tickless idle 软件上下文被链接到片内 RAM 空间,反之将被链接到 Flash 空间。 + + * - :ref:`CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM` + - 使能该选项后外设时钟及复位控制相关的软件实现将被链接到片内 RAM 空间,反之将被链接到 Flash 空间。 + + * - :ref:`CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM` + - 使能该选项后 Analog I2C 读写访问相关的软件实现将被链接到片内 RAM 空间,反之被链接到 Flash 空间。 配置方法: diff --git a/tools/test_apps/configs/sdkconfig.flash_auto_suspend_iram_reduction b/tools/test_apps/configs/sdkconfig.flash_auto_suspend_iram_reduction index 440bf6a236..32298fa74b 100644 --- a/tools/test_apps/configs/sdkconfig.flash_auto_suspend_iram_reduction +++ b/tools/test_apps/configs/sdkconfig.flash_auto_suspend_iram_reduction @@ -19,3 +19,9 @@ CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=n CONFIG_ESP_INTR_IN_IRAM=n CONFIG_LOG_IN_IRAM=n CONFIG_ESP_ROM_PRINT_IN_IRAM=n +# Low power related options +CONFIG_PM_SLEEP_FUNC_IN_IRAM=n +CONFIG_PM_SLP_IRAM_OPT=n +CONFIG_PM_RTOS_IDLE_OPT=n +CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=n +CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=n