esp32s3: clk, memory layout

This commit is contained in:
morris 2020-07-23 13:40:10 +08:00
parent 1e229881f7
commit 19761e3113
36 changed files with 5684 additions and 13 deletions

View File

@ -1,4 +1,74 @@
idf_build_get_property(target IDF_TARGET)
idf_build_get_property(sdkconfig_header SDKCONFIG_HEADER)
if(NOT "${target}" STREQUAL "esp32s3")
return()
endif()
if(BOOTLOADER_BUILD)
# For bootloader, all we need from esp32s3 is headers
idf_component_register(INCLUDE_DIRS include)
target_linker_script(${COMPONENT_LIB} INTERFACE "ld/esp32s3.peripherals.ld")
else()
# Regular app build
set(srcs "cache_err_int.c"
"clk.c"
"crosscore_int.c"
"dport_access.c"
"esp_crypto_lock.c"
"hw_random.c"
"intr_alloc.c"
"memprot.c"
"pm_esp32s3.c"
"pm_trace.c"
"sleep_modes.c"
"system_api_esp32s3.c")
set(include_dirs "include")
set(requires driver efuse soc) #unfortunately rom/uart uses SOC registers directly
# driver is a public requirement because esp_sleep.h uses gpio_num_t & touch_pad_t
# app_update is added here because cpu_start.c uses esp_ota_get_app_description() function.
# esp_timer is added here because cpu_start.c uses esp_timer
set(priv_requires app_trace app_update bootloader_support log mbedtls nvs_flash pthread
spi_flash vfs espcoredump esp_common perfmon esp_timer esp_ipc)
set(fragments linker.lf ld/esp32s3_fragments.lf)
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}"
LDFRAGMENTS "${fragments}"
REQUIRES "${requires}"
PRIV_REQUIRES "${priv_requires}"
REQUIRED_IDF_TARGETS esp32s3)
target_linker_script(${COMPONENT_LIB} INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/esp32s3_out.ld")
# Process the template file through the linker script generation mechanism, and use the output for linking the
# final binary
target_linker_script(${COMPONENT_LIB} INTERFACE "${CMAKE_CURRENT_LIST_DIR}/ld/esp32s3.project.ld.in"
PROCESS "${CMAKE_CURRENT_BINARY_DIR}/ld/esp32s3.project.ld")
target_linker_script(${COMPONENT_LIB} INTERFACE "ld/esp32s3.peripherals.ld")
target_link_libraries(${COMPONENT_LIB} PUBLIC gcc)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u call_user_start_cpu0")
idf_build_get_property(config_dir CONFIG_DIR)
# Preprocess esp32s3.ld linker script to include configuration, becomes esp32s3_out.ld
set(LD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ld)
add_custom_command(
OUTPUT esp32s3_out.ld
COMMAND "${CMAKE_C_COMPILER}" -C -P -x c -E -o esp32s3_out.ld -I ${config_dir} ${LD_DIR}/esp32s3.ld
MAIN_DEPENDENCY ${LD_DIR}/esp32s3.ld ${sdkconfig_header}
COMMENT "Generating linker script..."
VERBATIM)
add_custom_target(esp32s3_linker_script DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/esp32s3_out.ld)
add_dependencies(${COMPONENT_LIB} esp32s3_linker_script)
# disable stack protection in files which are involved in initialization of that feature
set_source_files_properties(
cpu_start.c
PROPERTIES COMPILE_FLAGS
-fno-stack-protector)
endif()

View File

@ -0,0 +1,75 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @file cache_err_int.c
* @brief The cache has an interrupt that can be raised as soon as an access to a cached
* region (Flash, PSRAM) is done without the cache being enabled.
* We use that here to panic the CPU, which from a debugging perspective,
* is better than grabbing bad data from the bus.
*/
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_attr.h"
#include "esp_intr_alloc.h"
#include "soc/soc.h"
#include "soc/extmem_reg.h"
#include "soc/periph_defs.h"
#include "hal/cpu_hal.h"
#include "esp32s3/dport_access.h"
#include "esp32s3/rom/ets_sys.h"
void esp_cache_err_int_init(void)
{
uint32_t core_id = cpu_hal_get_core_id();
ESP_INTR_DISABLE(ETS_CACHEERR_INUM);
// We do not register a handler for the interrupt because it is interrupt
// level 4 which is not serviceable from C. Instead, xtensa_vectors.S has
// a call to the panic handler for this interrupt.
intr_matrix_set(core_id, ETS_CACHE_IA_INTR_SOURCE, ETS_CACHEERR_INUM);
// Enable invalid cache access interrupt when the cache is disabled.
// When the interrupt happens, we can not determine the CPU where the
// invalid cache access has occurred. We enable the interrupt to catch
// invalid access on both CPUs, but the interrupt is connected to the
// CPU which happens to call this function.
// For this reason, panic handler backtrace will not be correct if the
// interrupt is connected to PRO CPU and invalid access happens on the APP CPU.
SET_PERI_REG_MASK(EXTMEM_CACHE_ILG_INT_CLR_REG,
EXTMEM_MMU_ENTRY_FAULT_INT_CLR |
EXTMEM_DCACHE_WRITE_FLASH_INT_CLR |
EXTMEM_DCACHE_PRELOAD_OP_FAULT_INT_CLR |
EXTMEM_DCACHE_SYNC_OP_FAULT_INT_CLR |
EXTMEM_ICACHE_PRELOAD_OP_FAULT_INT_CLR |
EXTMEM_ICACHE_SYNC_OP_FAULT_INT_CLR);
SET_PERI_REG_MASK(EXTMEM_CACHE_ILG_INT_ENA_REG,
EXTMEM_MMU_ENTRY_FAULT_INT_ENA |
EXTMEM_DCACHE_WRITE_FLASH_INT_ENA |
EXTMEM_DCACHE_PRELOAD_OP_FAULT_INT_ENA |
EXTMEM_DCACHE_SYNC_OP_FAULT_INT_ENA |
EXTMEM_ICACHE_PRELOAD_OP_FAULT_INT_ENA |
EXTMEM_ICACHE_SYNC_OP_FAULT_INT_ENA);
ESP_INTR_ENABLE(ETS_CACHEERR_INUM);
}
int IRAM_ATTR esp_cache_err_get_cpuid(void)
{
// FIXME
return -1;
}

52
components/esp32s3/clk.c Normal file
View File

@ -0,0 +1,52 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "esp32s3/clk.h"
#include "soc/rtc.h"
#define MHZ (1000000)
// g_ticks_us defined in ROMs for PRO and APP CPU
extern uint32_t g_ticks_per_us_pro;
#ifndef CONFIG_FREERTOS_UNICORE
extern uint32_t g_ticks_per_us_app;
#endif
int IRAM_ATTR esp_clk_cpu_freq(void)
{
return g_ticks_per_us_pro * MHZ;
}
int IRAM_ATTR esp_clk_apb_freq(void)
{
return MIN(g_ticks_per_us_pro, 80) * MHZ;
}
int IRAM_ATTR esp_clk_xtal_freq(void)
{
return rtc_clk_xtal_freq_get() * MHZ;
}
void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
{
/* Update scale factors used by ets_delay_us */
g_ticks_per_us_pro = ticks_per_us;
#ifndef CONFIG_FREERTOS_UNICORE
g_ticks_per_us_app = ticks_per_us;
#endif
}

View File

@ -0,0 +1,113 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_intr_alloc.h"
#include "esp_debug_helpers.h"
#include "soc/periph_defs.h"
#include "soc/system_reg.h"
#include "hal/cpu_hal.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#define REASON_YIELD BIT(0)
#define REASON_FREQ_SWITCH BIT(1)
#define REASON_PRINT_BACKTRACE BIT(2)
static portMUX_TYPE reason_spinlock = portMUX_INITIALIZER_UNLOCKED;
static volatile uint32_t reason[portNUM_PROCESSORS];
static inline void IRAM_ATTR esp_crosscore_isr_handle_yield(void)
{
portYIELD_FROM_ISR();
}
static void IRAM_ATTR esp_crosscore_isr(void *arg)
{
uint32_t my_reason_val;
//A pointer to the correct reason array item is passed to this ISR.
volatile uint32_t *my_reason = arg;
//Clear the interrupt first.
if (cpu_hal_get_core_id() == 0) {
WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG, 0);
} else {
WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_1_REG, 0);
}
//Grab the reason and clear it.
portENTER_CRITICAL_ISR(&reason_spinlock);
my_reason_val = *my_reason;
*my_reason = 0;
portEXIT_CRITICAL_ISR(&reason_spinlock);
//Check what we need to do.
if (my_reason_val & REASON_YIELD) {
esp_crosscore_isr_handle_yield();
}
if (my_reason_val & REASON_FREQ_SWITCH) {
/* Nothing to do here; the frequency switch event was already
* handled by a hook in xtensa_vectors.S. Could be used in the future
* to allow DFS features without the extra latency of the ISR hook.
*/
}
if (my_reason_val & REASON_PRINT_BACKTRACE) {
esp_backtrace_print(100);
}
}
// Initialize the crosscore interrupt on this core.
void esp_crosscore_int_init(void)
{
portENTER_CRITICAL(&reason_spinlock);
reason[cpu_hal_get_core_id()] = 0;
portEXIT_CRITICAL(&reason_spinlock);
esp_err_t err;
if (cpu_hal_get_core_id() == 0) {
err = esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void *)&reason[0], NULL);
} else {
err = esp_intr_alloc(ETS_FROM_CPU_INTR1_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void *)&reason[1], NULL);
}
assert(err == ESP_OK);
}
static void IRAM_ATTR esp_crosscore_int_send(int core_id, uint32_t reason_mask)
{
assert(core_id < portNUM_PROCESSORS);
//Mark the reason we interrupt the other CPU
portENTER_CRITICAL(&reason_spinlock);
reason[core_id] |= reason_mask;
portEXIT_CRITICAL(&reason_spinlock);
//Poke the other CPU.
if (core_id == 0) {
WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG, SYSTEM_CPU_INTR_FROM_CPU_0);
} else {
WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_1_REG, SYSTEM_CPU_INTR_FROM_CPU_1);
}
}
void IRAM_ATTR esp_crosscore_int_send_yield(int core_id)
{
esp_crosscore_int_send(core_id, REASON_YIELD);
}
void IRAM_ATTR esp_crosscore_int_send_freq_switch(int core_id)
{
esp_crosscore_int_send(core_id, REASON_FREQ_SWITCH);
}
void IRAM_ATTR esp_crosscore_int_send_print_backtrace(int core_id)
{
esp_crosscore_int_send(core_id, REASON_PRINT_BACKTRACE);
}

View File

@ -0,0 +1,25 @@
// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <string.h>
#include "soc/dport_access.h"
// Read a sequence of DPORT registers to the buffer.
void esp_dport_access_read_buffer(uint32_t *buff_out, uint32_t address, uint32_t num_words)
{
for (uint32_t i = 0; i < num_words; ++i) {
buff_out[i] = DPORT_SEQUENCE_REG_READ(address + i * 4);
}
}

View File

@ -0,0 +1,70 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "esp32s3/clk.h"
#include "soc/wdev_reg.h"
#include "freertos/FreeRTOSConfig.h"
#include "xtensa/core-macros.h"
uint32_t IRAM_ATTR esp_random(void)
{
/* The PRNG which implements WDEV_RANDOM register gets 2 bits
* of extra entropy from a hardware randomness source every APB clock cycle
* (provided WiFi or BT are enabled). To make sure entropy is not drained
* faster than it is added, this function needs to wait for at least 16 APB
* clock cycles after reading previous word. This implementation may actually
* wait a bit longer due to extra time spent in arithmetic and branch statements.
*
* As a (probably unncessary) precaution to avoid returning the
* RNG state as-is, the result is XORed with additional
* WDEV_RND_REG reads while waiting.
*/
/* This code does not run in a critical section, so CPU frequency switch may
* happens while this code runs (this will not happen in the current
* implementation, but possible in the future). However if that happens,
* the number of cycles spent on frequency switching will certainly be more
* than the number of cycles we need to wait here.
*/
uint32_t cpu_to_apb_freq_ratio = esp_clk_cpu_freq() / esp_clk_apb_freq();
static uint32_t last_ccount = 0;
uint32_t ccount;
uint32_t result = 0;
do {
ccount = XTHAL_GET_CCOUNT();
result ^= REG_READ(WDEV_RND_REG);
} while (ccount - last_ccount < cpu_to_apb_freq_ratio * 16);
last_ccount = ccount;
return result ^ REG_READ(WDEV_RND_REG);
}
void esp_fill_random(void *buf, size_t len)
{
assert(buf != NULL);
uint8_t *buf_bytes = (uint8_t *)buf;
while (len > 0) {
uint32_t word = esp_random();
uint32_t to_copy = MIN(sizeof(word), len);
memcpy(buf_bytes, &word, to_copy);
buf_bytes += to_copy;
len -= to_copy;
}
}

View File

@ -0,0 +1,21 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP_BROWNOUT_H
#define __ESP_BROWNOUT_H
void esp_brownout_init(void);
#endif

View File

@ -0,0 +1,33 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief initialize cache invalid access interrupt
*
* This function enables cache invalid access interrupt source and connects it
* to interrupt input number ETS_CACHEERR_INUM (see soc/soc.h). It is called
* from the startup code.
*/
void esp_cache_err_int_init(void);
/**
* @brief get the CPU which caused cache invalid access interrupt
* @return
* - PRO_CPU_NUM, if PRO_CPU has caused cache IA interrupt
* - APP_CPU_NUM, if APP_CPU has caused cache IA interrupt
* - (-1) otherwise
*/
int esp_cache_err_get_cpuid(void);

View File

@ -0,0 +1,84 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file esp_clk.h
*
* This file contains declarations of clock related functions.
*/
/**
* @brief Get the calibration value of RTC slow clock
*
* The value is in the same format as returned by rtc_clk_cal (microseconds,
* in Q13.19 fixed-point format).
*
* @return the calibration value obtained using rtc_clk_cal, at startup time
*/
uint32_t esp_clk_slowclk_cal_get(void);
/**
* @brief Update the calibration value of RTC slow clock
*
* The value has to be in the same format as returned by rtc_clk_cal (microseconds,
* in Q13.19 fixed-point format).
* This value is used by timekeeping functions (such as gettimeofday) to
* calculate current time based on RTC counter value.
* @param value calibration value obtained using rtc_clk_cal
*/
void esp_clk_slowclk_cal_set(uint32_t value);
/**
* @brief Return current CPU clock frequency
* When frequency switching is performed, this frequency may change.
* However it is guaranteed that the frequency never changes with a critical
* section.
*
* @return CPU clock frequency, in Hz
*/
int esp_clk_cpu_freq(void);
/**
* @brief Return current APB clock frequency
*
* When frequency switching is performed, this frequency may change.
* However it is guaranteed that the frequency never changes with a critical
* section.
*
* @return APB clock frequency, in Hz
*/
int esp_clk_apb_freq(void);
/**
* @brief Read value of RTC counter, converting it to microseconds
* @attention The value returned by this function may change abruptly when
* calibration value of RTC counter is updated via esp_clk_slowclk_cal_set
* function. This should not happen unless application calls esp_clk_slowclk_cal_set.
* In ESP-IDF, esp_clk_slowclk_cal_set is only called in startup code.
*
* @return Value or RTC counter, expressed in microseconds
*/
uint64_t esp_clk_rtc_time(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,40 @@
// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_DPORT_ACCESS_H_
#define _ESP_DPORT_ACCESS_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Read a sequence of DPORT registers to the buffer.
*
* @param[out] buff_out Contains the read data.
* @param[in] address Initial address for reading registers.
* @param[in] num_words The number of words.
*/
void esp_dport_access_read_buffer(uint32_t *buff_out, uint32_t address, uint32_t num_words);
#define DPORT_STALL_OTHER_CPU_START()
#define DPORT_STALL_OTHER_CPU_END()
#define DPORT_INTERRUPT_DISABLE()
#define DPORT_INTERRUPT_RESTORE()
#ifdef __cplusplus
}
#endif
#endif /* _ESP_DPORT_ACCESS_H_ */

View File

@ -0,0 +1,353 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* INTERNAL API
* generic interface to MMU memory protection features
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
MEMPROT_IRAM0 = 0x00000000,
MEMPROT_DRAM0 = 0x00000001,
MEMPROT_UNKNOWN
} mem_type_prot_t;
/**
* @brief Returns splitting address for required memory region
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*
* @return Splitting address for the memory region required.
* The address is given by region-specific global symbol exported from linker script,
* it is not read out from related configuration register.
*/
uint32_t *IRAM_ATTR esp_memprot_get_split_addr(mem_type_prot_t mem_type);
/**
* @brief Initializes illegal memory access control (MMU) for required memory section.
*
* All memory access interrupts share ETS_MEMACCESS_ERR_INUM input channel, it is caller's
* responsibility to properly detect actual intr. source as well as possible prioritization in case
* of multiple source reported during one intr.handling routine run
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*/
void esp_memprot_intr_init(mem_type_prot_t mem_type);
/**
* @brief Enable/disable the memory protection interrupt
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
* @param enable enable/disable
*/
void esp_memprot_intr_ena(mem_type_prot_t mem_type, bool enable);
/**
* @brief Detects whether any of the memory protection interrupts is active
*
* @return true/false
*/
bool esp_memprot_is_assoc_intr_any(void);
/**
* @brief Detects whether specific memory protection interrupt is active
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*
* @return true/false
*/
bool esp_memprot_is_assoc_intr(mem_type_prot_t mem_type);
/**
* @brief Sets a request for clearing interrupt-on flag for specified memory region (register write)
*
* @note When called without actual interrupt-on flag set, subsequent occurrence of related interrupt is ignored.
* Should be used only after the real interrupt appears, typically as the last step in interrupt handler's routine.
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*/
void esp_memprot_clear_intr(mem_type_prot_t mem_type);
/**
* @brief Detects which memory protection interrupt is active, check order: IRAM0, DRAM0
*
* @return Memory protection area type (see mem_type_prot_t enum)
*/
mem_type_prot_t IRAM_ATTR esp_memprot_get_intr_memtype(void);
/**
* @brief Gets interrupt status register contents for specified memory region
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*
* @return Contents of status register
*/
uint32_t esp_memprot_get_fault_reg(mem_type_prot_t mem_type);
/**
* @brief Get details of given interrupt status
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
* @param faulting_address Faulting address causing the interrupt [out]
* @param op_type Operation being processed at the faulting address [out]
* IRAM0: 0 - read, 1 - write
* DRAM0: 0 - read, 1 - write
* @param op_subtype Additional info for op_type [out]
* IRAM0: 0 - instruction segment access, 1 - data segment access
* DRAM0: 0 - non-atomic operation, 1 - atomic operation
*/
void IRAM_ATTR esp_memprot_get_fault_status(mem_type_prot_t mem_type, uint32_t **faulting_address, uint32_t *op_type, uint32_t *op_subtype);
/**
* @brief Gets string representation of required memory region identifier
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*
* @return mem_type as string
*/
const char *IRAM_ATTR esp_memprot_type_to_str(mem_type_prot_t mem_type);
/**
* @brief Detects whether any of the interrupt locks is active (requires digital system reset to unlock)
*
* @return true/false
*/
bool esp_memprot_is_locked_any(void);
/**
* @brief Sets lock for specified memory region.
*
* Locks can be unlocked only by digital system reset
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*/
void esp_memprot_set_lock(mem_type_prot_t mem_type);
/**
* @brief Gets lock status for required memory region
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*
* @return true/false (locked/unlocked)
*/
bool esp_memprot_get_lock(mem_type_prot_t mem_type);
/**
* @brief Gets interrupt permission control register contents for required memory region
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*
* @return Permission control register contents
*/
uint32_t esp_memprot_get_ena_reg(mem_type_prot_t mem_type);
/**
* @brief Gets interrupt permission settings for unified management block
*
* Gets interrupt permission settings register contents for required memory region, returns settings for unified management blocks
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*
* @return Permission settings register contents
*/
uint32_t esp_memprot_get_perm_uni_reg(mem_type_prot_t mem_type);
/**
* @brief Gets interrupt permission settings for split management block
*
* Gets interrupt permission settings register contents for required memory region, returns settings for split management blocks
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*
* @return Permission settings register contents
*/
uint32_t esp_memprot_get_perm_split_reg(mem_type_prot_t mem_type);
/**
* @brief Detects whether any of the memory protection interrupts is enabled
*
* @return true/false
*/
bool esp_memprot_is_intr_ena_any(void);
/**
* @brief Gets interrupt-enabled flag for given memory region
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*
* @return Interrupt-enabled value
*/
uint32_t esp_memprot_get_intr_ena_bit(mem_type_prot_t mem_type);
/**
* @brief Gets interrupt-active flag for given memory region
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*
* @return Interrupt-active value
*/
uint32_t esp_memprot_get_intr_on_bit(mem_type_prot_t mem_type);
/**
* @brief Gets interrupt-clear request flag for given memory region
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
*
* @return Interrupt-clear request value
*/
uint32_t esp_memprot_get_intr_clr_bit(mem_type_prot_t mem_type);
/**
* @brief Gets read permission value for specified block and memory region
*
* Returns read permission bit value for required unified-management block (0-3) in given memory region.
* Applicable to all memory types.
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
* @param block Memory block identifier (0-3)
*
* @return Read permission value for required block
*/
uint32_t esp_memprot_get_uni_block_read_bit(mem_type_prot_t mem_type, uint32_t block);
/**
* @brief Gets write permission value for specified block and memory region
*
* Returns write permission bit value for required unified-management block (0-3) in given memory region.
* Applicable to all memory types.
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
* @param block Memory block identifier (0-3)
*
* @return Write permission value for required block
*/
uint32_t esp_memprot_get_uni_block_write_bit(mem_type_prot_t mem_type, uint32_t block);
/**
* @brief Gets execute permission value for specified block and memory region
*
* Returns execute permission bit value for required unified-management block (0-3) in given memory region.
* Applicable only to IRAM memory types
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
* @param block Memory block identifier (0-3)
*
* @return Execute permission value for required block
*/
uint32_t esp_memprot_get_uni_block_exec_bit(mem_type_prot_t mem_type, uint32_t block);
/**
* @brief Sets permissions for specified block in DRAM region
*
* Sets Read and Write permission for specified unified-management block (0-3) in given memory region.
* Applicable only to DRAM memory types
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
* @param block Memory block identifier (0-3)
* @param write_perm Write permission flag
* @param read_perm Read permission flag
*/
void esp_memprot_set_uni_block_perm_dram(mem_type_prot_t mem_type, uint32_t block, bool write_perm, bool read_perm);
/**
* @brief Sets permissions for high and low memory segment in DRAM region
*
* Sets Read and Write permission for both low and high memory segments given by splitting address.
* The splitting address must be equal to or higher then beginning of block 5
* Applicable only to DRAM memory types
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
* @param split_addr Address to split the memory region to lower and higher segment
* @param lw Low segment Write permission flag
* @param lr Low segment Read permission flag
* @param hw High segment Write permission flag
* @param hr High segment Read permission flag
*/
void esp_memprot_set_prot_dram(mem_type_prot_t mem_type, uint32_t *split_addr, bool lw, bool lr, bool hw, bool hr);
/**
* @brief Sets permissions for specified block in IRAM region
*
* Sets Read, Write and Execute permission for specified unified-management block (0-3) in given memory region.
* Applicable only to IRAM memory types
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
* @param block Memory block identifier (0-3)
* @param write_perm Write permission flag
* @param exec_perm Execute permission flag
*/
void esp_memprot_set_uni_block_perm_iram(mem_type_prot_t mem_type, uint32_t block, bool write_perm, bool read_perm, bool exec_perm);
/**
* @brief Sets permissions for high and low memory segment in IRAM region
*
* Sets Read, Write and Execute permission for both low and high memory segments given by splitting address.
* The splitting address must be equal to or higher then beginning of block 5
* Applicable only to IRAM memory types
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
* @param split_addr Address to split the memory region to lower and higher segment
* @param lw Low segment Write permission flag
* @param lr Low segment Read permission flag
* @param lx Low segment Execute permission flag
* @param hw High segment Write permission flag
* @param hr High segment Read permission flag
* @param hx High segment Execute permission flag
*/
void esp_memprot_set_prot_iram(mem_type_prot_t mem_type, uint32_t *split_addr, bool lw, bool lr, bool lx, bool hw, bool hr, bool hx);
/**
* @brief Activates memory protection for all supported memory region types
*
* @note The feature is disabled when JTAG interface is connected
*
* @param invoke_panic_handler map mem.prot interrupt to ETS_MEMACCESS_ERR_INUM and thus invokes panic handler when fired ('true' not suitable for testing)
* @param lock_feature sets LOCK bit, see esp_memprot_set_lock() ('true' not suitable for testing)
*/
void esp_memprot_set_prot(bool invoke_panic_handler, bool lock_feature);
/**
* @brief Get permission settings bits for IRAM split mgmt based on current split address
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
* @param lw Low segment Write permission flag
* @param lr Low segment Read permission flag
* @param lx Low segment Execute permission flag
* @param hw High segment Write permission flag
* @param hr High segment Read permission flag
* @param hx High segment Execute permission flag
*/
void esp_memprot_get_perm_split_bits_iram(mem_type_prot_t mem_type, bool *lw, bool *lr, bool *lx, bool *hw, bool *hr, bool *hx);
/**
* @brief Get permission settings bits for DRAM split mgmt based on current split address
*
* @param mem_type Memory protection area type (see mem_type_prot_t enum)
* @param lw Low segment Write permission flag
* @param lr Low segment Read permission flag
* @param hw High segment Write permission flag
* @param hr High segment Read permission flag
*/
void esp_memprot_get_perm_split_bits_dram(mem_type_prot_t mem_type, bool *lw, bool *lr, bool *hw, bool *hr);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,42 @@
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "soc/rtc.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Power management config for ESP32
*
* Pass a pointer to this structure as an argument to esp_pm_configure function.
*/
typedef struct {
int max_freq_mhz; /*!< Maximum CPU frequency, in MHz */
int min_freq_mhz; /*!< Minimum CPU frequency to use when no locks are taken, in MHz */
bool light_sleep_enable; /*!< Enter light sleep when no locks are taken */
} esp_pm_config_esp32s3_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,91 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP_SPIRAM_H
#define __ESP_SPIRAM_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
/**
* @brief Initialize spiram interface/hardware. Normally called from cpu_start.c.
*
* @return ESP_OK on success
*/
esp_err_t esp_spiram_init(void);
/**
* @brief Configure Cache/MMU for access to external SPI RAM.
*
* Normally this function is called from cpu_start, if CONFIG_SPIRAM_BOOT_INIT
* option is enabled. Applications which need to enable SPI RAM at run time
* can disable CONFIG_SPIRAM_BOOT_INIT, and call this function later.
*
* @attention this function must be called with flash cache disabled.
*/
void esp_spiram_init_cache(void);
/**
* @brief Memory test for SPI RAM. Should be called after SPI RAM is initialized and
* (in case of a dual-core system) the app CPU is online. This test overwrites the
* memory with crap, so do not call after e.g. the heap allocator has stored important
* stuff in SPI RAM.
*
* @return true on success, false on failed memory test
*/
bool esp_spiram_test(void);
/**
* @brief Add the initialized SPI RAM to the heap allocator.
*/
esp_err_t esp_spiram_add_to_heapalloc(void);
/**
* @brief Get the size of the attached SPI RAM chip selected in menuconfig
*
* @return Size in bytes, or 0 if no external RAM chip support compiled in.
*/
size_t esp_spiram_get_size(void);
/**
* @brief Force a writeback of the data in the SPI RAM cache. This is to be called whenever
* cache is disabled, because disabling cache on the ESP32 discards the data in the SPI
* RAM cache.
*
* This is meant for use from within the SPI flash code.
*/
void esp_spiram_writeback_cache(void);
/**
* @brief Reserve a pool of internal memory for specific DMA/internal allocations
*
* @param size Size of reserved pool in bytes
*
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM when no memory available for pool
*/
esp_err_t esp_spiram_reserve_dma_pool(size_t size);
#endif

View File

@ -0,0 +1,77 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
/**
* @file esp_clk.h
*
* This file contains declarations of clock related functions.
*/
/**
* @brief Get the calibration value of RTC slow clock
*
* The value is in the same format as returned by rtc_clk_cal (microseconds,
* in Q13.19 fixed-point format).
*
* @return the calibration value obtained using rtc_clk_cal, at startup time
*/
uint32_t esp_clk_slowclk_cal_get(void);
/**
* @brief Update the calibration value of RTC slow clock
*
* The value has to be in the same format as returned by rtc_clk_cal (microseconds,
* in Q13.19 fixed-point format).
* This value is used by timekeeping functions (such as gettimeofday) to
* calculate current time based on RTC counter value.
* @param value calibration value obtained using rtc_clk_cal
*/
void esp_clk_slowclk_cal_set(uint32_t value);
/**
* @brief Return current CPU clock frequency
* When frequency switching is performed, this frequency may change.
* However it is guaranteed that the frequency never changes with a critical
* section.
*
* @return CPU clock frequency, in Hz
*/
int esp_clk_cpu_freq(void);
/**
* @brief Return current APB clock frequency
*
* When frequency switching is performed, this frequency may change.
* However it is guaranteed that the frequency never changes with a critical
* section.
*
* @return APB clock frequency, in Hz
*/
int esp_clk_apb_freq(void);
/**
* @brief Read value of RTC counter, converting it to microseconds
* @attention The value returned by this function may change abruptly when
* calibration value of RTC counter is updated via esp_clk_slowclk_cal_set
* function. This should not happen unless application calls esp_clk_slowclk_cal_set.
* In ESP-IDF, esp_clk_slowclk_cal_set is only called in startup code.
*
* @return Value or RTC counter, expressed in microseconds
*/
uint64_t esp_clk_rtc_time(void);

View File

@ -0,0 +1,299 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP_INTR_ALLOC_H__
#define __ESP_INTR_ALLOC_H__
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "freertos/xtensa_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @addtogroup Intr_Alloc
* @{
*/
/** @brief Interrupt allocation flags
*
* These flags can be used to specify which interrupt qualities the
* code calling esp_intr_alloc* needs.
*
*/
//Keep the LEVELx values as they are here; they match up with (1<<level)
#define ESP_INTR_FLAG_LEVEL1 (1<<1) ///< Accept a Level 1 interrupt vector (lowest priority)
#define ESP_INTR_FLAG_LEVEL2 (1<<2) ///< Accept a Level 2 interrupt vector
#define ESP_INTR_FLAG_LEVEL3 (1<<3) ///< Accept a Level 3 interrupt vector
#define ESP_INTR_FLAG_LEVEL4 (1<<4) ///< Accept a Level 4 interrupt vector
#define ESP_INTR_FLAG_LEVEL5 (1<<5) ///< Accept a Level 5 interrupt vector
#define ESP_INTR_FLAG_LEVEL6 (1<<6) ///< Accept a Level 6 interrupt vector
#define ESP_INTR_FLAG_NMI (1<<7) ///< Accept a Level 7 interrupt vector (highest priority)
#define ESP_INTR_FLAG_SHARED (1<<8) ///< Interrupt can be shared between ISRs
#define ESP_INTR_FLAG_EDGE (1<<9) ///< Edge-triggered interrupt
#define ESP_INTR_FLAG_IRAM (1<<10) ///< ISR can be called if cache is disabled
#define ESP_INTR_FLAG_INTRDISABLED (1<<11) ///< Return with this interrupt disabled
#define ESP_INTR_FLAG_LOWMED (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3) ///< Low and medium prio interrupts. These can be handled in C.
#define ESP_INTR_FLAG_HIGH (ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6|ESP_INTR_FLAG_NMI) ///< High level interrupts. Need to be handled in assembly.
#define ESP_INTR_FLAG_LEVELMASK (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3| \
ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6| \
ESP_INTR_FLAG_NMI) ///< Mask for all level flags
/**@}*/
/** @addtogroup Intr_Alloc_Pseudo_Src
* @{
*/
/**
* The esp_intr_alloc* functions can allocate an int for all ETS_*_INTR_SOURCE interrupt sources that
* are routed through the interrupt mux. Apart from these sources, each core also has some internal
* sources that do not pass through the interrupt mux. To allocate an interrupt for these sources,
* pass these pseudo-sources to the functions.
*/
#define ETS_INTERNAL_TIMER0_INTR_SOURCE -1 ///< Xtensa timer 0 interrupt source
#define ETS_INTERNAL_TIMER1_INTR_SOURCE -2 ///< Xtensa timer 1 interrupt source
#define ETS_INTERNAL_TIMER2_INTR_SOURCE -3 ///< Xtensa timer 2 interrupt source
#define ETS_INTERNAL_SW0_INTR_SOURCE -4 ///< Software int source 1
#define ETS_INTERNAL_SW1_INTR_SOURCE -5 ///< Software int source 2
#define ETS_INTERNAL_PROFILING_INTR_SOURCE -6 ///< Int source for profiling
/**@}*/
// This is used to provide SystemView with positive IRQ IDs, otherwise sheduler events are not shown properly
#define ETS_INTERNAL_INTR_SOURCE_OFF (-ETS_INTERNAL_PROFILING_INTR_SOURCE)
#define ESP_INTR_ENABLE(inum) xt_ints_on((1<<inum))
#define ESP_INTR_DISABLE(inum) xt_ints_off((1<<inum))
typedef void (*intr_handler_t)(void *arg);
typedef struct intr_handle_data_t intr_handle_data_t;
typedef intr_handle_data_t *intr_handle_t ;
/**
* @brief Mark an interrupt as a shared interrupt
*
* This will mark a certain interrupt on the specified CPU as
* an interrupt that can be used to hook shared interrupt handlers
* to.
*
* @param intno The number of the interrupt (0-31)
* @param cpu CPU on which the interrupt should be marked as shared (0 or 1)
* @param is_in_iram Shared interrupt is for handlers that reside in IRAM and
* the int can be left enabled while the flash cache is disabled.
*
* @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
* ESP_OK otherwise
*/
esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_in_iram);
/**
* @brief Reserve an interrupt to be used outside of this framework
*
* This will mark a certain interrupt on the specified CPU as
* reserved, not to be allocated for any reason.
*
* @param intno The number of the interrupt (0-31)
* @param cpu CPU on which the interrupt should be marked as shared (0 or 1)
*
* @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
* ESP_OK otherwise
*/
esp_err_t esp_intr_reserve(int intno, int cpu);
/**
* @brief Allocate an interrupt with the given parameters.
*
* This finds an interrupt that matches the restrictions as given in the flags
* parameter, maps the given interrupt source to it and hooks up the given
* interrupt handler (with optional argument) as well. If needed, it can return
* a handle for the interrupt as well.
*
* The interrupt will always be allocated on the core that runs this function.
*
* If ESP_INTR_FLAG_IRAM flag is used, and handler address is not in IRAM or
* RTC_FAST_MEM, then ESP_ERR_INVALID_ARG is returned.
*
* @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux
* sources, as defined in soc/soc.h, or one of the internal
* ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header.
* @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
* choice of interrupts that this routine can choose from. If this value
* is 0, it will default to allocating a non-shared interrupt of level
* 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared
* interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return
* from this function with the interrupt disabled.
* @param handler The interrupt handler. Must be NULL when an interrupt of level >3
* is requested, because these types of interrupts aren't C-callable.
* @param arg Optional argument for passed to the interrupt handler
* @param ret_handle Pointer to an intr_handle_t to store a handle that can later be
* used to request details or free the interrupt. Can be NULL if no handle
* is required.
*
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
* ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
* ESP_OK otherwise
*/
esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle);
/**
* @brief Allocate an interrupt with the given parameters.
*
*
* This essentially does the same as esp_intr_alloc, but allows specifying a register and mask
* combo. For shared interrupts, the handler is only called if a read from the specified
* register, ANDed with the mask, returns non-zero. By passing an interrupt status register
* address and a fitting mask, this can be used to accelerate interrupt handling in the case
* a shared interrupt is triggered; by checking the interrupt statuses first, the code can
* decide which ISRs can be skipped
*
* @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux
* sources, as defined in soc/soc.h, or one of the internal
* ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header.
* @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
* choice of interrupts that this routine can choose from. If this value
* is 0, it will default to allocating a non-shared interrupt of level
* 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared
* interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return
* from this function with the interrupt disabled.
* @param intrstatusreg The address of an interrupt status register
* @param intrstatusmask A mask. If a read of address intrstatusreg has any of the bits
* that are 1 in the mask set, the ISR will be called. If not, it will be
* skipped.
* @param handler The interrupt handler. Must be NULL when an interrupt of level >3
* is requested, because these types of interrupts aren't C-callable.
* @param arg Optional argument for passed to the interrupt handler
* @param ret_handle Pointer to an intr_handle_t to store a handle that can later be
* used to request details or free the interrupt. Can be NULL if no handle
* is required.
*
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
* ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
* ESP_OK otherwise
*/
esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, void *arg, intr_handle_t *ret_handle);
/**
* @brief Disable and free an interrupt.
*
* Use an interrupt handle to disable the interrupt and release the resources
* associated with it.
*
* @note
* When the handler shares its source with other handlers, the interrupt status
* bits it's responsible for should be managed properly before freeing it. see
* ``esp_intr_disable`` for more details.
*
* @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
*
* @return ESP_ERR_INVALID_ARG if handle is invalid, or esp_intr_free runs on another core than
* where the interrupt is allocated on.
* ESP_OK otherwise
*/
esp_err_t esp_intr_free(intr_handle_t handle);
/**
* @brief Get CPU number an interrupt is tied to
*
* @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
*
* @return The core number where the interrupt is allocated
*/
int esp_intr_get_cpu(intr_handle_t handle);
/**
* @brief Get the allocated interrupt for a certain handle
*
* @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
*
* @return The interrupt number
*/
int esp_intr_get_intno(intr_handle_t handle);
/**
* @brief Disable the interrupt associated with the handle
*
* @note
* 1. For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the
* CPU the interrupt is allocated on. Other interrupts have no such restriction.
* 2. When several handlers sharing a same interrupt source, interrupt status bits, which are
* handled in the handler to be disabled, should be masked before the disabling, or handled
* in other enabled interrupts properly. Miss of interrupt status handling will cause infinite
* interrupt calls and finally system crash.
*
* @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
*
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
* ESP_OK otherwise
*/
esp_err_t esp_intr_disable(intr_handle_t handle);
/**
* @brief Enable the interrupt associated with the handle
*
* @note For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the
* CPU the interrupt is allocated on. Other interrupts have no such restriction.
*
* @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
*
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
* ESP_OK otherwise
*/
esp_err_t esp_intr_enable(intr_handle_t handle);
/**
* @brief Set the "in IRAM" status of the handler.
*
* @note Does not work on shared interrupts.
*
* @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
* @param is_in_iram Whether the handler associated with this handle resides in IRAM.
* Handlers residing in IRAM can be called when cache is disabled.
*
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
* ESP_OK otherwise
*/
esp_err_t esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram);
/**
* @brief Disable interrupts that aren't specifically marked as running from IRAM
*/
void esp_intr_noniram_disable(void);
/**
* @brief Re-enable interrupts disabled by esp_intr_noniram_disable
*/
void esp_intr_noniram_enable(void);
/**@}*/
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,358 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "driver/gpio.h"
#include "driver/touch_pad.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Logic function used for EXT1 wakeup mode.
*/
typedef enum {
ESP_EXT1_WAKEUP_ALL_LOW = 0, //!< Wake the chip when all selected GPIOs go low
ESP_EXT1_WAKEUP_ANY_HIGH = 1 //!< Wake the chip when any of the selected GPIOs go high
} esp_sleep_ext1_wakeup_mode_t;
/**
* @brief Power domains which can be powered down in sleep mode
*/
typedef enum {
ESP_PD_DOMAIN_RTC_PERIPH, //!< RTC IO, sensors and ULP co-processor
ESP_PD_DOMAIN_RTC_SLOW_MEM, //!< RTC slow memory
ESP_PD_DOMAIN_RTC_FAST_MEM, //!< RTC fast memory
ESP_PD_DOMAIN_XTAL, //!< XTAL oscillator
ESP_PD_DOMAIN_MAX //!< Number of domains
} esp_sleep_pd_domain_t;
/**
* @brief Power down options
*/
typedef enum {
ESP_PD_OPTION_OFF, //!< Power down the power domain in sleep mode
ESP_PD_OPTION_ON, //!< Keep power domain enabled during sleep mode
ESP_PD_OPTION_AUTO //!< Keep power domain enabled in sleep mode, if it is needed by one of the wakeup options. Otherwise power it down.
} esp_sleep_pd_option_t;
/**
* @brief Sleep wakeup cause
*/
typedef enum {
ESP_SLEEP_WAKEUP_UNDEFINED, //!< In case of deep sleep, reset was not caused by exit from deep sleep
ESP_SLEEP_WAKEUP_ALL, //!< Not a wakeup cause, used to disable all wakeup sources with esp_sleep_disable_wakeup_source
ESP_SLEEP_WAKEUP_EXT0, //!< Wakeup caused by external signal using RTC_IO
ESP_SLEEP_WAKEUP_EXT1, //!< Wakeup caused by external signal using RTC_CNTL
ESP_SLEEP_WAKEUP_TIMER, //!< Wakeup caused by timer
ESP_SLEEP_WAKEUP_TOUCHPAD, //!< Wakeup caused by touchpad
ESP_SLEEP_WAKEUP_ULP, //!< Wakeup caused by ULP program
ESP_SLEEP_WAKEUP_GPIO, //!< Wakeup caused by GPIO (light sleep only)
ESP_SLEEP_WAKEUP_UART, //!< Wakeup caused by UART (light sleep only)
ESP_SLEEP_WAKEUP_WIFI, //!< Wakeup caused by WIFI (light sleep only)
ESP_SLEEP_WAKEUP_COCPU, //!< Wakeup caused by COCPU int
ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG, //!< Wakeup caused by COCPU crash
} esp_sleep_source_t;
/* Leave this type define for compatibility */
typedef esp_sleep_source_t esp_sleep_wakeup_cause_t;
/**
* @brief Disable wakeup source
*
* This function is used to deactivate wake up trigger for source
* defined as parameter of the function.
*
* @note This function does not modify wake up configuration in RTC.
* It will be performed in esp_sleep_start function.
*
* See docs/sleep-modes.rst for details.
*
* @param source - number of source to disable of type esp_sleep_source_t
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE if trigger was not active
*/
esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source);
/**
* @brief Enable wakeup by ULP coprocessor
* @note In revisions 0 and 1 of the ESP32, ULP wakeup source
* can not be used when RTC_PERIPH power domain is forced
* to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup
* source is used.
* @return
* - ESP_OK on success
* - ESP_ERR_NOT_SUPPORTED if additional current by touch (CONFIG_ESP32_RTC_EXT_CRYST_ADDIT_CURRENT) is enabled.
* - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled or if wakeup triggers conflict
*/
esp_err_t esp_sleep_enable_ulp_wakeup(void);
/**
* @brief Enable wakeup by timer
* @param time_in_us time before wakeup, in microseconds
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if value is out of range (TBD)
*/
esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us);
/**
* @brief Enable wakeup by touch sensor
*
* @note In revisions 0 and 1 of the ESP32, touch wakeup source
* can not be used when RTC_PERIPH power domain is forced
* to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup
* source is used.
*
* @note The FSM mode of the touch button should be configured
* as the timer trigger mode.
*
* @return
* - ESP_OK on success
* - ESP_ERR_NOT_SUPPORTED if additional current by touch (CONFIG_ESP32_RTC_EXT_CRYST_ADDIT_CURRENT) is enabled.
* - ESP_ERR_INVALID_STATE if wakeup triggers conflict
*/
esp_err_t esp_sleep_enable_touchpad_wakeup(void);
/**
* @brief Get the touch pad which caused wakeup
*
* If wakeup was caused by another source, this function will return TOUCH_PAD_MAX;
*
* @return touch pad which caused wakeup
*/
touch_pad_t esp_sleep_get_touchpad_wakeup_status(void);
/**
* @brief Enable wakeup using a pin
*
* This function uses external wakeup feature of RTC_IO peripheral.
* It will work only if RTC peripherals are kept on during sleep.
*
* This feature can monitor any pin which is an RTC IO. Once the pin transitions
* into the state given by level argument, the chip will be woken up.
*
* @note This function does not modify pin configuration. The pin is
* configured in esp_sleep_start, immediately before entering sleep mode.
*
* @note In revisions 0 and 1 of the ESP32, ext0 wakeup source
* can not be used together with touch or ULP wakeup sources.
*
* @param gpio_num GPIO number used as wakeup source. Only GPIOs which are have RTC
* functionality can be used: 0,2,4,12-15,25-27,32-39.
* @param level input level which will trigger wakeup (0=low, 1=high)
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if the selected GPIO is not an RTC GPIO,
* or the mode is invalid
* - ESP_ERR_INVALID_STATE if wakeup triggers conflict
*/
esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level);
/**
* @brief Enable wakeup using multiple pins
*
* This function uses external wakeup feature of RTC controller.
* It will work even if RTC peripherals are shut down during sleep.
*
* This feature can monitor any number of pins which are in RTC IOs.
* Once any of the selected pins goes into the state given by mode argument,
* the chip will be woken up.
*
* @note This function does not modify pin configuration. The pins are
* configured in esp_sleep_start, immediately before
* entering sleep mode.
*
* @note internal pullups and pulldowns don't work when RTC peripherals are
* shut down. In this case, external resistors need to be added.
* Alternatively, RTC peripherals (and pullups/pulldowns) may be
* kept enabled using esp_sleep_pd_config function.
*
* @param mask bit mask of GPIO numbers which will cause wakeup. Only GPIOs
* which are have RTC functionality can be used in this bit map:
* 0,2,4,12-15,25-27,32-39.
* @param mode select logic function used to determine wakeup condition:
* - ESP_EXT1_WAKEUP_ALL_LOW: wake up when all selected GPIOs are low
* - ESP_EXT1_WAKEUP_ANY_HIGH: wake up when any of the selected GPIOs is high
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if any of the selected GPIOs is not an RTC GPIO,
* or mode is invalid
*/
esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode);
/**
* @brief Enable wakeup from light sleep using GPIOs
*
* Each GPIO supports wakeup function, which can be triggered on either low level
* or high level. Unlike EXT0 and EXT1 wakeup sources, this method can be used
* both for all IOs: RTC IOs and digital IOs. It can only be used to wakeup from
* light sleep though.
*
* To enable wakeup, first call gpio_wakeup_enable, specifying gpio number and
* wakeup level, for each GPIO which is used for wakeup.
* Then call this function to enable wakeup feature.
*
* @note In revisions 0 and 1 of the ESP32, GPIO wakeup source
* can not be used together with touch or ULP wakeup sources.
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE if wakeup triggers conflict
*/
esp_err_t esp_sleep_enable_gpio_wakeup(void);
/**
* @brief Enable wakeup from light sleep using UART
*
* Use uart_set_wakeup_threshold function to configure UART wakeup threshold.
*
* Wakeup from light sleep takes some time, so not every character sent
* to the UART can be received by the application.
*
* @param uart_num UART port to wake up from
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if wakeup from given UART is not supported
*/
esp_err_t esp_sleep_enable_uart_wakeup(int uart_num);
/**
* @brief Get the bit mask of GPIOs which caused wakeup (ext1)
*
* If wakeup was caused by another source, this function will return 0.
*
* @return bit mask, if GPIOn caused wakeup, BIT(n) will be set
*/
uint64_t esp_sleep_get_ext1_wakeup_status(void);
/**
* @brief Set power down mode for an RTC power domain in sleep mode
*
* If not set set using this API, all power domains default to ESP_PD_OPTION_AUTO.
*
* @param domain power domain to configure
* @param option power down option (ESP_PD_OPTION_OFF, ESP_PD_OPTION_ON, or ESP_PD_OPTION_AUTO)
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if either of the arguments is out of range
*/
esp_err_t esp_sleep_pd_config(esp_sleep_pd_domain_t domain,
esp_sleep_pd_option_t option);
/**
* @brief Enter deep sleep with the configured wakeup options
*
* This function does not return.
*/
void esp_deep_sleep_start(void) __attribute__((noreturn));
/**
* @brief Enter light sleep with the configured wakeup options
*
* @return
* - ESP_OK on success (returned after wakeup)
* - ESP_ERR_INVALID_STATE if WiFi or BT is not stopped
*/
esp_err_t esp_light_sleep_start(void);
/**
* @brief Enter deep-sleep mode
*
* The device will automatically wake up after the deep-sleep time
* Upon waking up, the device calls deep sleep wake stub, and then proceeds
* to load application.
*
* Call to this function is equivalent to a call to esp_deep_sleep_enable_timer_wakeup
* followed by a call to esp_deep_sleep_start.
*
* esp_deep_sleep does not shut down WiFi, BT, and higher level protocol
* connections gracefully.
* Make sure relevant WiFi and BT stack functions are called to close any
* connections and deinitialize the peripherals. These include:
* - esp_bluedroid_disable
* - esp_bt_controller_disable
* - esp_wifi_stop
*
* This function does not return.
*
* @param time_in_us deep-sleep time, unit: microsecond
*/
void esp_deep_sleep(uint64_t time_in_us) __attribute__((noreturn));
/**
* @brief Get the wakeup source which caused wakeup from sleep
*
* @return cause of wake up from last sleep (deep sleep or light sleep)
*/
esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void);
/**
* @brief Default stub to run on wake from deep sleep.
*
* Allows for executing code immediately on wake from sleep, before
* the software bootloader or ESP-IDF app has started up.
*
* This function is weak-linked, so you can implement your own version
* to run code immediately when the chip wakes from
* sleep.
*
* See docs/deep-sleep-stub.rst for details.
*/
void esp_wake_deep_sleep(void);
/**
* @brief Function type for stub to run on wake from sleep.
*
*/
typedef void (*esp_deep_sleep_wake_stub_fn_t)(void);
/**
* @brief Install a new stub at runtime to run on wake from deep sleep
*
* If implementing esp_wake_deep_sleep() then it is not necessary to
* call this function.
*
* However, it is possible to call this function to substitute a
* different deep sleep stub. Any function used as a deep sleep stub
* must be marked RTC_IRAM_ATTR, and must obey the same rules given
* for esp_wake_deep_sleep().
*/
void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub);
/**
* @brief Get current wake from deep sleep stub
* @return Return current wake from deep sleep stub, or NULL if
* no stub is installed.
*/
esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void);
/**
* @brief The default esp-idf-provided esp_wake_deep_sleep() stub.
*
* See docs/deep-sleep-stub.rst for details.
*/
void esp_default_wake_deep_sleep(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,90 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP_SPIRAM_H
#define __ESP_SPIRAM_H
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
/**
* @brief Initialize spiram interface/hardware. Normally called from cpu_start.c.
*
* @return ESP_OK on success
*/
esp_err_t esp_spiram_init(void);
/**
* @brief Configure Cache/MMU for access to external SPI RAM.
*
* Normally this function is called from cpu_start, if CONFIG_SPIRAM_BOOT_INIT
* option is enabled. Applications which need to enable SPI RAM at run time
* can disable CONFIG_SPIRAM_BOOT_INIT, and call this function later.
*
* @attention this function must be called with flash cache disabled.
*/
void esp_spiram_init_cache(void);
/**
* @brief Memory test for SPI RAM. Should be called after SPI RAM is initialized and
* (in case of a dual-core system) the app CPU is online. This test overwrites the
* memory with crap, so do not call after e.g. the heap allocator has stored important
* stuff in SPI RAM.
*
* @return true on success, false on failed memory test
*/
bool esp_spiram_test(void);
/**
* @brief Add the initialized SPI RAM to the heap allocator.
*/
esp_err_t esp_spiram_add_to_heapalloc(void);
/**
* @brief Get the size of the attached SPI RAM chip selected in menuconfig
*
* @return Size in bytes, or 0 if no external RAM chip support compiled in.
*/
size_t esp_spiram_get_size(void);
/**
* @brief Force a writeback of the data in the SPI RAM cache. This is to be called whenever
* cache is disabled, because disabling cache on the ESP32 discards the data in the SPI
* RAM cache.
*
* This is meant for use from within the SPI flash code.
*/
void esp_spiram_writeback_cache(void);
/**
* @brief Reserve a pool of internal memory for specific DMA/internal allocations
*
* @param size Size of reserved pool in bytes
*
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM when no memory available for pool
*/
esp_err_t esp_spiram_reserve_dma_pool(size_t size);
#endif

View File

@ -0,0 +1,954 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_intr_alloc.h"
#include "esp_attr.h"
#include "soc/soc.h"
static const char *TAG = "intr_alloc";
#define ETS_INTERNAL_TIMER0_INTR_NO 6
#define ETS_INTERNAL_TIMER1_INTR_NO 15
#define ETS_INTERNAL_TIMER2_INTR_NO 16
#define ETS_INTERNAL_SW0_INTR_NO 7
#define ETS_INTERNAL_SW1_INTR_NO 29
#define ETS_INTERNAL_PROFILING_INTR_NO 11
/*
Define this to debug the choices made when allocating the interrupt. This leads to much debugging
output within a critical region, which can lead to weird effects like e.g. the interrupt watchdog
being triggered, that is why it is separate from the normal LOG* scheme.
*/
//define DEBUG_INT_ALLOC_DECISIONS
#ifdef DEBUG_INT_ALLOC_DECISIONS
# define ALCHLOG(...) ESP_EARLY_LOGD(TAG, __VA_ARGS__)
#else
# define ALCHLOG(...) do {} while (0)
#endif
typedef enum {
INTDESC_NORMAL = 0,
INTDESC_RESVD,
INTDESC_SPECIAL //for xtensa timers / software ints
} int_desc_flag_t;
typedef enum {
INTTP_LEVEL = 0,
INTTP_EDGE,
INTTP_NA
} int_type_t;
typedef struct {
int level;
int_type_t type;
int_desc_flag_t cpuflags[2];
} int_desc_t;
//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer
//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in
//the table below.
#if CONFIG_FREERTOS_CORETIMER_0
#define INT6RES INTDESC_RESVD
#else
#define INT6RES INTDESC_SPECIAL
#endif
#if CONFIG_FREERTOS_CORETIMER_1
#define INT15RES INTDESC_RESVD
#else
#define INT15RES INTDESC_SPECIAL
#endif
//This is basically a software-readable version of the interrupt usage table in include/soc/soc.h
const static int_desc_t int_desc[32] = {
{ 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //0
{ 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //1
{ 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //2
{ 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //3
{ 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //4
{ 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //5
{ 1, INTTP_NA, {INT6RES, INT6RES } }, //6
{ 1, INTTP_NA, {INTDESC_SPECIAL, INTDESC_SPECIAL}}, //7
{ 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //8
{ 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //9
{ 1, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //10
{ 3, INTTP_NA, {INTDESC_SPECIAL, INTDESC_SPECIAL}}, //11
{ 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //12
{ 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //13
{ 7, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //14, NMI
{ 3, INTTP_NA, {INT15RES, INT15RES } }, //15
{ 5, INTTP_NA, {INTDESC_SPECIAL, INTDESC_SPECIAL} }, //16
{ 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //17
{ 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //18
{ 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //19
{ 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //20
{ 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //21
{ 3, INTTP_EDGE, {INTDESC_RESVD, INTDESC_NORMAL} }, //22
{ 3, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //23
{ 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //24
{ 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //25
{ 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //26
{ 3, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //27
{ 4, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //28
{ 3, INTTP_NA, {INTDESC_SPECIAL, INTDESC_SPECIAL}}, //29
{ 4, INTTP_EDGE, {INTDESC_RESVD, INTDESC_RESVD } }, //30
{ 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //31
};
typedef struct shared_vector_desc_t shared_vector_desc_t;
typedef struct vector_desc_t vector_desc_t;
struct shared_vector_desc_t {
int disabled: 1;
int source: 8;
volatile uint32_t *statusreg;
uint32_t statusmask;
intr_handler_t isr;
void *arg;
shared_vector_desc_t *next;
};
#define VECDESC_FL_RESERVED (1<<0)
#define VECDESC_FL_INIRAM (1<<1)
#define VECDESC_FL_SHARED (1<<2)
#define VECDESC_FL_NONSHARED (1<<3)
//Pack using bitfields for better memory use
struct vector_desc_t {
int flags: 16; //OR of VECDESC_FLAG_* defines
unsigned int cpu: 1;
unsigned int intno: 5;
int source: 8; //Interrupt mux flags, used when not shared
shared_vector_desc_t *shared_vec_info; //used when VECDESC_FL_SHARED
vector_desc_t *next;
};
struct intr_handle_data_t {
vector_desc_t *vector_desc;
shared_vector_desc_t *shared_vector_desc;
};
typedef struct non_shared_isr_arg_t non_shared_isr_arg_t;
struct non_shared_isr_arg_t {
intr_handler_t isr;
void *isr_arg;
int source;
};
//Linked list of vector descriptions, sorted by cpu.intno value
static vector_desc_t *vector_desc_head = NULL;
//This bitmask has an 1 if the int should be disabled when the flash is disabled.
static uint32_t non_iram_int_mask[portNUM_PROCESSORS];
//This bitmask has 1 in it if the int was disabled using esp_intr_noniram_disable.
static uint32_t non_iram_int_disabled[portNUM_PROCESSORS];
static bool non_iram_int_disabled_flag[portNUM_PROCESSORS];
#if CONFIG_SYSVIEW_ENABLE
extern uint32_t port_switch_flag[];
#endif
static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
//Inserts an item into vector_desc list so that the list is sorted
//with an incrementing cpu.intno value.
static void insert_vector_desc(vector_desc_t *to_insert)
{
vector_desc_t *vd = vector_desc_head;
vector_desc_t *prev = NULL;
while (vd != NULL) {
if (vd->cpu > to_insert->cpu) {
break;
}
if (vd->cpu == to_insert->cpu && vd->intno >= to_insert->intno) {
break;
}
prev = vd;
vd = vd->next;
}
if ((vector_desc_head == NULL) || (prev == NULL)) {
//First item
to_insert->next = vd;
vector_desc_head = to_insert;
} else {
prev->next = to_insert;
to_insert->next = vd;
}
}
//Returns a vector_desc entry for an intno/cpu, or NULL if none exists.
static vector_desc_t *find_desc_for_int(int intno, int cpu)
{
vector_desc_t *vd = vector_desc_head;
while (vd != NULL) {
if (vd->cpu == cpu && vd->intno == intno) {
break;
}
vd = vd->next;
}
return vd;
}
//Returns a vector_desc entry for an intno/cpu.
//Either returns a preexisting one or allocates a new one and inserts
//it into the list. Returns NULL on malloc fail.
static vector_desc_t *get_desc_for_int(int intno, int cpu)
{
vector_desc_t *vd = find_desc_for_int(intno, cpu);
if (vd == NULL) {
vector_desc_t *newvd = heap_caps_malloc(sizeof(vector_desc_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (newvd == NULL) {
return NULL;
}
memset(newvd, 0, sizeof(vector_desc_t));
newvd->intno = intno;
newvd->cpu = cpu;
insert_vector_desc(newvd);
return newvd;
} else {
return vd;
}
}
//Returns a vector_desc entry for an source, the cpu parameter is used to tell GPIO_INT and GPIO_NMI from different CPUs
static vector_desc_t *find_desc_for_source(int source, int cpu)
{
vector_desc_t *vd = vector_desc_head;
while (vd != NULL) {
if ( !(vd->flags & VECDESC_FL_SHARED) ) {
if ( vd->source == source && cpu == vd->cpu ) {
break;
}
} else if ( vd->cpu == cpu ) {
// check only shared vds for the correct cpu, otherwise skip
bool found = false;
shared_vector_desc_t *svd = vd->shared_vec_info;
assert(svd != NULL );
while (svd) {
if ( svd->source == source ) {
found = true;
break;
}
svd = svd->next;
}
if ( found ) {
break;
}
}
vd = vd->next;
}
return vd;
}
esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram)
{
if (intno > 31) {
return ESP_ERR_INVALID_ARG;
}
if (cpu >= portNUM_PROCESSORS) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL(&spinlock);
vector_desc_t *vd = get_desc_for_int(intno, cpu);
if (vd == NULL) {
portEXIT_CRITICAL(&spinlock);
return ESP_ERR_NO_MEM;
}
vd->flags = VECDESC_FL_SHARED;
if (is_int_ram) {
vd->flags |= VECDESC_FL_INIRAM;
}
portEXIT_CRITICAL(&spinlock);
return ESP_OK;
}
esp_err_t esp_intr_reserve(int intno, int cpu)
{
if (intno > 31) {
return ESP_ERR_INVALID_ARG;
}
if (cpu >= portNUM_PROCESSORS) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL(&spinlock);
vector_desc_t *vd = get_desc_for_int(intno, cpu);
if (vd == NULL) {
portEXIT_CRITICAL(&spinlock);
return ESP_ERR_NO_MEM;
}
vd->flags = VECDESC_FL_RESERVED;
portEXIT_CRITICAL(&spinlock);
return ESP_OK;
}
//Interrupt handler table and unhandled uinterrupt routine. Duplicated
//from xtensa_intr.c... it's supposed to be private, but we need to look
//into it in order to see if someone allocated an int using
//xt_set_interrupt_handler.
typedef struct xt_handler_table_entry {
void *handler;
void *arg;
} xt_handler_table_entry;
extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS * portNUM_PROCESSORS];
extern void xt_unhandled_interrupt(void *arg);
//Returns true if handler for interrupt is not the default unhandled interrupt handler
static bool int_has_handler(int intr, int cpu)
{
return (_xt_interrupt_table[intr * portNUM_PROCESSORS + cpu].handler != xt_unhandled_interrupt);
}
static bool is_vect_desc_usable(vector_desc_t *vd, int flags, int cpu, int force)
{
//Check if interrupt is not reserved by design
int x = vd->intno;
if (int_desc[x].cpuflags[cpu] == INTDESC_RESVD) {
ALCHLOG("....Unusable: reserved");
return false;
}
if (int_desc[x].cpuflags[cpu] == INTDESC_SPECIAL && force == -1) {
ALCHLOG("....Unusable: special-purpose int");
return false;
}
//Check if the interrupt level is acceptable
if (!(flags & (1 << int_desc[x].level))) {
ALCHLOG("....Unusable: incompatible level");
return false;
}
//check if edge/level type matches what we want
if (((flags & ESP_INTR_FLAG_EDGE) && (int_desc[x].type == INTTP_LEVEL)) ||
(((!(flags & ESP_INTR_FLAG_EDGE)) && (int_desc[x].type == INTTP_EDGE)))) {
ALCHLOG("....Unusable: incompatible trigger type");
return false;
}
//check if interrupt is reserved at runtime
if (vd->flags & VECDESC_FL_RESERVED) {
ALCHLOG("....Unusable: reserved at runtime.");
return false;
}
//Ints can't be both shared and non-shared.
assert(!((vd->flags & VECDESC_FL_SHARED) && (vd->flags & VECDESC_FL_NONSHARED)));
//check if interrupt already is in use by a non-shared interrupt
if (vd->flags & VECDESC_FL_NONSHARED) {
ALCHLOG("....Unusable: already in (non-shared) use.");
return false;
}
// check shared interrupt flags
if (vd->flags & VECDESC_FL_SHARED ) {
if (flags & ESP_INTR_FLAG_SHARED) {
bool in_iram_flag = ((flags & ESP_INTR_FLAG_IRAM) != 0);
bool desc_in_iram_flag = ((vd->flags & VECDESC_FL_INIRAM) != 0);
//Bail out if int is shared, but iram property doesn't match what we want.
if ((vd->flags & VECDESC_FL_SHARED) && (desc_in_iram_flag != in_iram_flag)) {
ALCHLOG("....Unusable: shared but iram prop doesn't match");
return false;
}
} else {
//We need an unshared IRQ; can't use shared ones; bail out if this is shared.
ALCHLOG("...Unusable: int is shared, we need non-shared.");
return false;
}
} else if (int_has_handler(x, cpu)) {
//Check if interrupt already is allocated by xt_set_interrupt_handler
ALCHLOG("....Unusable: already allocated");
return false;
}
return true;
}
//Locate a free interrupt compatible with the flags given.
//The 'force' argument can be -1, or 0-31 to force checking a certain interrupt.
//When a CPU is forced, the INTDESC_SPECIAL marked interrupts are also accepted.
static int get_available_int(int flags, int cpu, int force, int source)
{
int x;
int best = -1;
int bestLevel = 9;
int bestSharedCt = INT_MAX;
//Default vector desc, for vectors not in the linked list
vector_desc_t empty_vect_desc;
memset(&empty_vect_desc, 0, sizeof(vector_desc_t));
//Level defaults to any low/med interrupt
if (!(flags & ESP_INTR_FLAG_LEVELMASK)) {
flags |= ESP_INTR_FLAG_LOWMED;
}
ALCHLOG("get_available_int: try to find existing. Cpu: %d, Source: %d", cpu, source);
vector_desc_t *vd = find_desc_for_source(source, cpu);
if ( vd ) {
// if existing vd found, don't need to search any more.
ALCHLOG("get_avalible_int: existing vd found. intno: %d", vd->intno);
if ( force != -1 && force != vd->intno ) {
ALCHLOG("get_avalible_int: intr forced but not matach existing. existing intno: %d, force: %d", vd->intno, force);
} else if ( !is_vect_desc_usable(vd, flags, cpu, force) ) {
ALCHLOG("get_avalible_int: existing vd invalid.");
} else {
best = vd->intno;
}
return best;
}
if (force != -1) {
ALCHLOG("get_available_int: try to find force. Cpu: %d, Source: %d, Force: %d", cpu, source, force);
//if force assigned, don't need to search any more.
vd = find_desc_for_int(force, cpu);
if (vd == NULL ) {
//if existing vd not found, just check the default state for the intr.
empty_vect_desc.intno = force;
vd = &empty_vect_desc;
}
if ( is_vect_desc_usable(vd, flags, cpu, force) ) {
best = vd->intno;
} else {
ALCHLOG("get_avalible_int: forced vd invalid.");
}
return best;
}
ALCHLOG("get_free_int: start looking. Current cpu: %d", cpu);
//No allocated handlers as well as forced intr, iterate over the 32 possible interrupts
for (x = 0; x < 32; x++) {
//Grab the vector_desc for this vector.
vd = find_desc_for_int(x, cpu);
if (vd == NULL) {
empty_vect_desc.intno = x;
vd = &empty_vect_desc;
}
ALCHLOG("Int %d reserved %d level %d %s hasIsr %d",
x, int_desc[x].cpuflags[cpu] == INTDESC_RESVD, int_desc[x].level,
int_desc[x].type == INTTP_LEVEL ? "LEVEL" : "EDGE", int_has_handler(x, cpu));
if ( !is_vect_desc_usable(vd, flags, cpu, force) ) {
continue;
}
if (flags & ESP_INTR_FLAG_SHARED) {
//We're allocating a shared int.
//See if int already is used as a shared interrupt.
if (vd->flags & VECDESC_FL_SHARED) {
//We can use this already-marked-as-shared interrupt. Count the already attached isrs in order to see
//how useful it is.
int no = 0;
shared_vector_desc_t *svdesc = vd->shared_vec_info;
while (svdesc != NULL) {
no++;
svdesc = svdesc->next;
}
if (no < bestSharedCt || bestLevel > int_desc[x].level) {
//Seems like this shared vector is both okay and has the least amount of ISRs already attached to it.
best = x;
bestSharedCt = no;
bestLevel = int_desc[x].level;
ALCHLOG("...int %d more usable as a shared int: has %d existing vectors", x, no);
} else {
ALCHLOG("...worse than int %d", best);
}
} else {
if (best == -1) {
//We haven't found a feasible shared interrupt yet. This one is still free and usable, even if
//not marked as shared.
//Remember it in case we don't find any other shared interrupt that qualifies.
if (bestLevel > int_desc[x].level) {
best = x;
bestLevel = int_desc[x].level;
ALCHLOG("...int %d usable as a new shared int", x);
}
} else {
ALCHLOG("...already have a shared int");
}
}
} else {
//Seems this interrupt is feasible. Select it and break out of the loop; no need to search further.
if (bestLevel > int_desc[x].level) {
best = x;
bestLevel = int_desc[x].level;
} else {
ALCHLOG("...worse than int %d", best);
}
}
}
ALCHLOG("get_available_int: using int %d", best);
//Okay, by now we have looked at all potential interrupts and hopefully have selected the best one in best.
return best;
}
//Common shared isr handler. Chain-call all ISRs.
static void IRAM_ATTR shared_intr_isr(void *arg)
{
vector_desc_t *vd = (vector_desc_t *)arg;
shared_vector_desc_t *sh_vec = vd->shared_vec_info;
portENTER_CRITICAL(&spinlock);
while (sh_vec) {
if (!sh_vec->disabled) {
if ((sh_vec->statusreg == NULL) || (*sh_vec->statusreg & sh_vec->statusmask)) {
#if CONFIG_SYSVIEW_ENABLE
traceISR_ENTER(sh_vec->source + ETS_INTERNAL_INTR_SOURCE_OFF);
#endif
sh_vec->isr(sh_vec->arg);
#if CONFIG_SYSVIEW_ENABLE
// check if we will return to scheduler or to interrupted task after ISR
if (!port_switch_flag[xPortGetCoreID()]) {
traceISR_EXIT();
}
#endif
}
}
sh_vec = sh_vec->next;
}
portEXIT_CRITICAL(&spinlock);
}
#if CONFIG_SYSVIEW_ENABLE
//Common non-shared isr handler wrapper.
static void IRAM_ATTR non_shared_intr_isr(void *arg)
{
non_shared_isr_arg_t *ns_isr_arg = (non_shared_isr_arg_t *)arg;
portENTER_CRITICAL(&spinlock);
traceISR_ENTER(ns_isr_arg->source + ETS_INTERNAL_INTR_SOURCE_OFF);
// FIXME: can we call ISR and check port_switch_flag after releasing spinlock?
// when CONFIG_SYSVIEW_ENABLE = 0 ISRs for non-shared IRQs are called without spinlock
ns_isr_arg->isr(ns_isr_arg->isr_arg);
// check if we will return to scheduler or to interrupted task after ISR
if (!port_switch_flag[xPortGetCoreID()]) {
traceISR_EXIT();
}
portEXIT_CRITICAL(&spinlock);
}
#endif
//We use ESP_EARLY_LOG* here because this can be called before the scheduler is running.
esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler,
void *arg, intr_handle_t *ret_handle)
{
intr_handle_data_t *ret = NULL;
int force = -1;
ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): checking args", xPortGetCoreID());
//Shared interrupts should be level-triggered.
if ((flags & ESP_INTR_FLAG_SHARED) && (flags & ESP_INTR_FLAG_EDGE)) {
return ESP_ERR_INVALID_ARG;
}
//You can't set an handler / arg for a non-C-callable interrupt.
if ((flags & ESP_INTR_FLAG_HIGH) && (handler)) {
return ESP_ERR_INVALID_ARG;
}
//Shared ints should have handler and non-processor-local source
if ((flags & ESP_INTR_FLAG_SHARED) && (!handler || source < 0)) {
return ESP_ERR_INVALID_ARG;
}
//Statusreg should have a mask
if (intrstatusreg && !intrstatusmask) {
return ESP_ERR_INVALID_ARG;
}
//If the ISR is marked to be IRAM-resident, the handler must not be in the cached region
if ((flags & ESP_INTR_FLAG_IRAM) &&
(ptrdiff_t) handler >= SOC_RTC_IRAM_HIGH &&
(ptrdiff_t) handler < SOC_RTC_DATA_LOW ) {
return ESP_ERR_INVALID_ARG;
}
//Default to prio 1 for shared interrupts. Default to prio 1, 2 or 3 for non-shared interrupts.
if ((flags & ESP_INTR_FLAG_LEVELMASK) == 0) {
if (flags & ESP_INTR_FLAG_SHARED) {
flags |= ESP_INTR_FLAG_LEVEL1;
} else {
flags |= ESP_INTR_FLAG_LOWMED;
}
}
ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): Args okay. Resulting flags 0x%X", xPortGetCoreID(), flags);
//Check 'special' interrupt sources. These are tied to one specific interrupt, so we
//have to force get_free_int to only look at that.
if (source == ETS_INTERNAL_TIMER0_INTR_SOURCE) {
force = ETS_INTERNAL_TIMER0_INTR_NO;
}
if (source == ETS_INTERNAL_TIMER1_INTR_SOURCE) {
force = ETS_INTERNAL_TIMER1_INTR_NO;
}
if (source == ETS_INTERNAL_TIMER2_INTR_SOURCE) {
force = ETS_INTERNAL_TIMER2_INTR_NO;
}
if (source == ETS_INTERNAL_SW0_INTR_SOURCE) {
force = ETS_INTERNAL_SW0_INTR_NO;
}
if (source == ETS_INTERNAL_SW1_INTR_SOURCE) {
force = ETS_INTERNAL_SW1_INTR_NO;
}
if (source == ETS_INTERNAL_PROFILING_INTR_SOURCE) {
force = ETS_INTERNAL_PROFILING_INTR_NO;
}
//Allocate a return handle. If we end up not needing it, we'll free it later on.
ret = heap_caps_malloc(sizeof(intr_handle_data_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (ret == NULL) {
return ESP_ERR_NO_MEM;
}
portENTER_CRITICAL(&spinlock);
int cpu = xPortGetCoreID();
//See if we can find an interrupt that matches the flags.
int intr = get_available_int(flags, cpu, force, source);
if (intr == -1) {
//None found. Bail out.
portEXIT_CRITICAL(&spinlock);
free(ret);
return ESP_ERR_NOT_FOUND;
}
//Get an int vector desc for int.
vector_desc_t *vd = get_desc_for_int(intr, cpu);
if (vd == NULL) {
portEXIT_CRITICAL(&spinlock);
free(ret);
return ESP_ERR_NO_MEM;
}
//Allocate that int!
if (flags & ESP_INTR_FLAG_SHARED) {
//Populate vector entry and add to linked list.
shared_vector_desc_t *sh_vec = malloc(sizeof(shared_vector_desc_t));
if (sh_vec == NULL) {
portEXIT_CRITICAL(&spinlock);
free(ret);
return ESP_ERR_NO_MEM;
}
memset(sh_vec, 0, sizeof(shared_vector_desc_t));
sh_vec->statusreg = (uint32_t *)intrstatusreg;
sh_vec->statusmask = intrstatusmask;
sh_vec->isr = handler;
sh_vec->arg = arg;
sh_vec->next = vd->shared_vec_info;
sh_vec->source = source;
sh_vec->disabled = 0;
vd->shared_vec_info = sh_vec;
vd->flags |= VECDESC_FL_SHARED;
//(Re-)set shared isr handler to new value.
xt_set_interrupt_handler(intr, shared_intr_isr, vd);
} else {
//Mark as unusable for other interrupt sources. This is ours now!
vd->flags = VECDESC_FL_NONSHARED;
if (handler) {
#if CONFIG_SYSVIEW_ENABLE
non_shared_isr_arg_t *ns_isr_arg = malloc(sizeof(non_shared_isr_arg_t));
if (!ns_isr_arg) {
portEXIT_CRITICAL(&spinlock);
free(ret);
return ESP_ERR_NO_MEM;
}
ns_isr_arg->isr = handler;
ns_isr_arg->isr_arg = arg;
ns_isr_arg->source = source;
xt_set_interrupt_handler(intr, non_shared_intr_isr, ns_isr_arg);
#else
xt_set_interrupt_handler(intr, handler, arg);
#endif
}
if (flags & ESP_INTR_FLAG_EDGE) {
xthal_set_intclear(1 << intr);
}
vd->source = source;
}
if (flags & ESP_INTR_FLAG_IRAM) {
vd->flags |= VECDESC_FL_INIRAM;
non_iram_int_mask[cpu] &= ~(1 << intr);
} else {
vd->flags &= ~VECDESC_FL_INIRAM;
non_iram_int_mask[cpu] |= (1 << intr);
}
if (source >= 0) {
intr_matrix_set(cpu, source, intr);
}
//Fill return handle data.
ret->vector_desc = vd;
ret->shared_vector_desc = vd->shared_vec_info;
//Enable int at CPU-level;
ESP_INTR_ENABLE(intr);
//If interrupt has to be started disabled, do that now; ints won't be enabled for real until the end
//of the critical section.
if (flags & ESP_INTR_FLAG_INTRDISABLED) {
esp_intr_disable(ret);
}
portEXIT_CRITICAL(&spinlock);
//Fill return handle if needed, otherwise free handle.
if (ret_handle != NULL) {
*ret_handle = ret;
} else {
free(ret);
}
ESP_EARLY_LOGD(TAG, "Connected src %d to int %d (cpu %d)", source, intr, cpu);
return ESP_OK;
}
esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle)
{
/*
As an optimization, we can create a table with the possible interrupt status registers and masks for every single
source there is. We can then add code here to look up an applicable value and pass that to the
esp_intr_alloc_intrstatus function.
*/
return esp_intr_alloc_intrstatus(source, flags, 0, 0, handler, arg, ret_handle);
}
esp_err_t IRAM_ATTR esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram)
{
if (!handle) {
return ESP_ERR_INVALID_ARG;
}
vector_desc_t *vd = handle->vector_desc;
if (vd->flags & VECDESC_FL_SHARED) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL(&spinlock);
uint32_t mask = (1 << vd->intno);
if (is_in_iram) {
vd->flags |= VECDESC_FL_INIRAM;
non_iram_int_mask[vd->cpu] &= ~mask;
} else {
vd->flags &= ~VECDESC_FL_INIRAM;
non_iram_int_mask[vd->cpu] |= mask;
}
portEXIT_CRITICAL(&spinlock);
return ESP_OK;
}
esp_err_t esp_intr_free(intr_handle_t handle)
{
bool free_shared_vector = false;
if (!handle) {
return ESP_ERR_INVALID_ARG;
}
//This routine should be called from the interrupt the task is scheduled on.
if (handle->vector_desc->cpu != xPortGetCoreID()) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL(&spinlock);
esp_intr_disable(handle);
if (handle->vector_desc->flags & VECDESC_FL_SHARED) {
//Find and kill the shared int
shared_vector_desc_t *svd = handle->vector_desc->shared_vec_info;
shared_vector_desc_t *prevsvd = NULL;
assert(svd); //should be something in there for a shared int
while (svd != NULL) {
if (svd == handle->shared_vector_desc) {
//Found it. Now kill it.
if (prevsvd) {
prevsvd->next = svd->next;
} else {
handle->vector_desc->shared_vec_info = svd->next;
}
free(svd);
break;
}
prevsvd = svd;
svd = svd->next;
}
//If nothing left, disable interrupt.
if (handle->vector_desc->shared_vec_info == NULL) {
free_shared_vector = true;
}
ESP_LOGV(TAG, "esp_intr_free: Deleting shared int: %s. Shared int is %s", svd ? "not found or last one" : "deleted", free_shared_vector ? "empty now." : "still in use");
}
if ((handle->vector_desc->flags & VECDESC_FL_NONSHARED) || free_shared_vector) {
ESP_LOGV(TAG, "esp_intr_free: Disabling int, killing handler");
#if CONFIG_SYSVIEW_ENABLE
if (!free_shared_vector) {
void *isr_arg = xt_get_interrupt_handler_arg(handle->vector_desc->intno);
if (isr_arg) {
free(isr_arg);
}
}
#endif
//Reset to normal handler
xt_set_interrupt_handler(handle->vector_desc->intno, xt_unhandled_interrupt, (void *)((int)handle->vector_desc->intno));
//Theoretically, we could free the vector_desc... not sure if that's worth the few bytes of memory
//we save.(We can also not use the same exit path for empty shared ints anymore if we delete
//the desc.) For now, just mark it as free.
handle->vector_desc->flags &= !(VECDESC_FL_NONSHARED | VECDESC_FL_RESERVED);
//Also kill non_iram mask bit.
non_iram_int_mask[handle->vector_desc->cpu] &= ~(1 << (handle->vector_desc->intno));
}
portEXIT_CRITICAL(&spinlock);
free(handle);
return ESP_OK;
}
int esp_intr_get_intno(intr_handle_t handle)
{
return handle->vector_desc->intno;
}
int esp_intr_get_cpu(intr_handle_t handle)
{
return handle->vector_desc->cpu;
}
/*
Interrupt disabling strategy:
If the source is >=0 (meaning a muxed interrupt), we disable it by muxing the interrupt to a non-connected
interrupt. If the source is <0 (meaning an internal, per-cpu interrupt), we disable it using ESP_INTR_DISABLE.
This allows us to, for the muxed CPUs, disable an int from the other core. It also allows disabling shared
interrupts.
*/
//Muxing an interrupt source to interrupt 6, 7, 11, 15, 16 or 29 cause the interrupt to effectively be disabled.
#define INT_MUX_DISABLED_INTNO 6
esp_err_t IRAM_ATTR esp_intr_enable(intr_handle_t handle)
{
if (!handle) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL_SAFE(&spinlock);
int source;
if (handle->shared_vector_desc) {
handle->shared_vector_desc->disabled = 0;
source = handle->shared_vector_desc->source;
} else {
source = handle->vector_desc->source;
}
if (source >= 0) {
//Disabled using int matrix; re-connect to enable
intr_matrix_set(handle->vector_desc->cpu, source, handle->vector_desc->intno);
} else {
//Re-enable using cpu int ena reg
if (handle->vector_desc->cpu != xPortGetCoreID()) {
return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu
}
ESP_INTR_ENABLE(handle->vector_desc->intno);
}
portEXIT_CRITICAL_SAFE(&spinlock);
return ESP_OK;
}
esp_err_t IRAM_ATTR esp_intr_disable(intr_handle_t handle)
{
if (!handle) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL_SAFE(&spinlock);
int source;
bool disabled = 1;
if (handle->shared_vector_desc) {
handle->shared_vector_desc->disabled = 1;
source = handle->shared_vector_desc->source;
shared_vector_desc_t *svd = handle->vector_desc->shared_vec_info;
assert( svd != NULL );
while ( svd ) {
if ( svd->source == source && svd->disabled == 0 ) {
disabled = 0;
break;
}
svd = svd->next;
}
} else {
source = handle->vector_desc->source;
}
if (source >= 0) {
if ( disabled ) {
//Disable using int matrix
intr_matrix_set(handle->vector_desc->cpu, source, INT_MUX_DISABLED_INTNO);
}
} else {
//Disable using per-cpu regs
if (handle->vector_desc->cpu != xPortGetCoreID()) {
portEXIT_CRITICAL_SAFE(&spinlock);
return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu
}
ESP_INTR_DISABLE(handle->vector_desc->intno);
}
portEXIT_CRITICAL_SAFE(&spinlock);
return ESP_OK;
}
void IRAM_ATTR esp_intr_noniram_disable(void)
{
int oldint;
int cpu = xPortGetCoreID();
int intmask = ~non_iram_int_mask[cpu];
if (non_iram_int_disabled_flag[cpu]) {
abort();
}
non_iram_int_disabled_flag[cpu] = true;
asm volatile (
"movi %0,0\n"
"xsr %0,INTENABLE\n" //disable all ints first
"rsync\n"
"and a3,%0,%1\n" //mask ints that need disabling
"wsr a3,INTENABLE\n" //write back
"rsync\n"
:"=&r"(oldint):"r"(intmask):"a3");
//Save which ints we did disable
non_iram_int_disabled[cpu] = oldint & non_iram_int_mask[cpu];
}
void IRAM_ATTR esp_intr_noniram_enable(void)
{
int cpu = xPortGetCoreID();
int intmask = non_iram_int_disabled[cpu];
if (!non_iram_int_disabled_flag[cpu]) {
abort();
}
non_iram_int_disabled_flag[cpu] = false;
asm volatile (
"movi a3,0\n"
"xsr a3,INTENABLE\n"
"rsync\n"
"or a3,a3,%0\n"
"wsr a3,INTENABLE\n"
"rsync\n"
::"r"(intmask):"a3");
}
//These functions are provided in ROM, but the ROM-based functions use non-multicore-capable
//virtualized interrupt levels. Thus, we disable them in the ld file and provide working
//equivalents here.
void IRAM_ATTR ets_isr_unmask(unsigned int mask)
{
xt_ints_on(mask);
}
void IRAM_ATTR ets_isr_mask(unsigned int mask)
{
xt_ints_off(mask);
}

View File

@ -0,0 +1,111 @@
/**
* ESP32-S3 Linker Script Memory Layout
* This file describes the memory layout (memory blocks) by virtual memory addresses.
* This linker script is passed through the C preprocessor to include configuration options.
* Please use preprocessor features sparingly!
* Restrict to simple macros with numeric values, and/or #if/#endif blocks.
*/
#include "sdkconfig.h"
#define SRAM_IRAM_START 0x40370000
#define SRAM_DRAM_START 0x3FC80000
#define I_D_SRAM_OFFSET (SRAM_IRAM_START - SRAM_DRAM_START)
#define SRAM_DRAM_END 0x40054000 - I_D_SRAM_OFFSET /* 2nd stage bootloader iram_loader_seg start address */
#define SRAM_IRAM_ORG (SRAM_IRAM_START + CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE)
#define SRAM_DRAM_ORG (SRAM_DRAM_START + CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE)
#define I_D_SRAM_SIZE SRAM_DRAM_END - SRAM_DRAM_ORG
#if CONFIG_ESP32S3_USE_FIXED_STATIC_RAM_SIZE
ASSERT((CONFIG_ESP32S3_FIXED_STATIC_RAM_SIZE <= I_D_SRAM_SIZE), "Fixed static ram data does not fit.")
#define DRAM0_0_SEG_LEN CONFIG_ESP32S3_FIXED_STATIC_RAM_SIZE
#else
#define DRAM0_0_SEG_LEN I_D_SRAM_SIZE
#endif // CONFIG_ESP32S3_USE_FIXED_STATIC_RAM_SIZE
MEMORY
{
/**
* All these values assume the flash cache is on, and have the blocks this uses subtracted from the length
* of the various regions. The 'data access port' dram/drom regions map to the same iram/irom regions but
* are connected to the data port of the CPU and eg allow byte-wise access.
*/
/* IRAM for PRO CPU. */
iram0_0_seg (RX) : org = SRAM_IRAM_ORG, len = I_D_SRAM_SIZE
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
/* Flash mapped instruction data */
iram0_2_seg (RX) : org = 0x42000020, len = 0x8000000-0x20
/**
* (0x20 offset above is a convenience for the app binary image generation.
* Flash cache has 64KB pages. The .bin file which is flashed to the chip
* has a 0x18 byte file header, and each segment has a 0x08 byte segment
* header. Setting this offset makes it simple to meet the flash cache MMU's
* constraint that (paddr % 64KB == vaddr % 64KB).)
*/
#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS
/**
* Shared data RAM, excluding memory reserved for ROM bss/data/stack.
* Enabling Bluetooth & Trace Memory features in menuconfig will decrease the amount of RAM available.
*/
dram0_0_seg (RW) : org = SRAM_DRAM_ORG, len = DRAM0_0_SEG_LEN
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
/* Flash mapped constant data */
drom0_0_seg (R) : org = 0x3C000020, len = 0x8000000-0x20
/* (See iram0_2_seg for meaning of 0x20 offset in the above.) */
#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS
/**
* RTC fast memory (executable). Persists over deep sleep.
*/
rtc_iram_seg(RWX) : org = 0x600fe000, len = 0x2000
/**
* RTC fast memory (same block as above), viewed from data bus
*/
rtc_data_seg(RW) : org = 0x3ff80000, len = 0x2000
/**
* RTC slow memory (data accessible). Persists over deep sleep.
* Start of RTC slow memory is reserved for ULP co-processor code + data, if enabled.
*/
rtc_slow_seg(RW) : org = 0x50000000 + CONFIG_ESP32S3_ULP_COPROC_RESERVE_MEM,
len = 0x1000 - CONFIG_ESP32S3_ULP_COPROC_RESERVE_MEM
}
#if CONFIG_ESP32S3_USE_FIXED_STATIC_RAM_SIZE
/* static data ends at defined address */
_static_data_end = 0x3FCA0000 + DRAM0_0_SEG_LEN;
#else
_static_data_end = _bss_end;
#endif // CONFIG_ESP32S3_USE_FIXED_STATIC_RAM_SIZE
/* Heap ends at top of dram0_0_seg */
_heap_end = 0x40000000;
_data_seg_org = ORIGIN(rtc_data_seg);
#if CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM
REGION_ALIAS("rtc_data_location", rtc_slow_seg );
#else
REGION_ALIAS("rtc_data_location", rtc_data_seg );
#endif // CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
REGION_ALIAS("default_code_seg", iram0_2_seg);
#else
REGION_ALIAS("default_code_seg", iram0_0_seg);
#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
REGION_ALIAS("default_rodata_seg", drom0_0_seg);
#else
REGION_ALIAS("default_rodata_seg", dram0_0_seg);
#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS

View File

@ -0,0 +1,31 @@
PROVIDE ( UART0 = 0x60000000 );
PROVIDE ( SPIMEM1 = 0x60002000 );
PROVIDE ( SPIMEM0 = 0x60003000 );
PROVIDE ( GPIO = 0x60004000 );
PROVIDE ( SIGMADELTA = 0x60004f00 );
PROVIDE ( RTCCNTL = 0x60008000 );
PROVIDE ( RTCIO = 0x60008400 );
PROVIDE ( SENS = 0x60008800 );
PROVIDE ( HINF = 0x6000B000 );
PROVIDE ( I2S0 = 0x6000F000 );
PROVIDE ( UART1 = 0x60010000 );
PROVIDE ( I2C0 = 0x60013000 );
PROVIDE ( UHCI0 = 0x60014000 );
PROVIDE ( UHCI1 = 0x60014000 );
PROVIDE ( HOST = 0x60015000 );
PROVIDE ( RMT = 0x60016000 );
PROVIDE ( RMTMEM = 0x60016800 );
PROVIDE ( PCNT = 0x60017000 );
PROVIDE ( SLC = 0x60018000 );
PROVIDE ( LEDC = 0x60019000 );
PROVIDE ( MCP = 0x600c3000 );
PROVIDE ( TIMERG0 = 0x6001F000 );
PROVIDE ( TIMERG1 = 0x60020000 );
PROVIDE ( GPSPI2 = 0x60024000 );
PROVIDE ( GPSPI3 = 0x60025000 );
PROVIDE ( SYSCON = 0x60026000 );
PROVIDE ( I2C1 = 0x60027000 );
PROVIDE ( GPSPI4 = 0x60037000 );
PROVIDE ( UART2 = 0x60010000 );
PROVIDE ( APB_SARADC = 0x60040000 );
PROVIDE ( LCD_CAM = 0x60041000 );

View File

@ -0,0 +1,420 @@
/* Default entry point */
ENTRY(call_start_cpu0);
SECTIONS
{
/**
* RTC fast memory holds RTC wake stub code,
* including from any source file named rtc_wake_stub*.c
*/
.rtc.text :
{
. = ALIGN(4);
mapping[rtc_text]
*rtc_wake_stub*.*(.literal .text .literal.* .text.*)
_rtc_text_end = ABSOLUTE(.);
} > rtc_iram_seg
/**
* This section is required to skip rtc.text area because rtc_iram_seg and
* rtc_data_seg are reflect the same address space on different buses.
*/
.rtc.dummy :
{
_rtc_dummy_start = ABSOLUTE(.);
_rtc_fast_start = ABSOLUTE(.);
. = SIZEOF(.rtc.text);
_rtc_dummy_end = ABSOLUTE(.);
} > rtc_data_seg
/**
* This section located in RTC FAST Memory area.
* It holds data marked with RTC_FAST_ATTR attribute.
* See the file "esp_attr.h" for more information.
*/
.rtc.force_fast :
{
. = ALIGN(4);
_rtc_force_fast_start = ABSOLUTE(.);
_coredump_rtc_fast_start = ABSOLUTE(.);
mapping[rtc_fast_coredump]
_coredump_rtc_fast_end = ABSOLUTE(.);
*(.rtc.force_fast .rtc.force_fast.*)
. = ALIGN(4) ;
_rtc_force_fast_end = ABSOLUTE(.);
} > rtc_data_seg
/**
* RTC data section holds RTC wake stub
* data/rodata, including from any source file
* named rtc_wake_stub*.c and the data marked with
* RTC_DATA_ATTR, RTC_RODATA_ATTR attributes.
* The memory location of the data is dependent on
* CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM option.
*/
.rtc.data :
{
_rtc_data_start = ABSOLUTE(.);
/* coredump mapping */
_coredump_rtc_start = ABSOLUTE(.);
mapping[rtc_coredump]
_coredump_rtc_end = ABSOLUTE(.);
/* should be placed after coredump mapping */
mapping[rtc_data]
*rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .bss .bss.*)
_rtc_data_end = ABSOLUTE(.);
} > rtc_data_location
/* RTC bss, from any source file named rtc_wake_stub*.c */
.rtc.bss (NOLOAD) :
{
_rtc_bss_start = ABSOLUTE(.);
*rtc_wake_stub*.*(.bss .bss.*)
*rtc_wake_stub*.*(COMMON)
mapping[rtc_bss]
_rtc_bss_end = ABSOLUTE(.);
} > rtc_data_location
/**
* This section holds data that should not be initialized at power up
* and will be retained during deep sleep.
* User data marked with RTC_NOINIT_ATTR will be placed
* into this section. See the file "esp_attr.h" for more information.
* The memory location of the data is dependent on CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM option.
*/
.rtc_noinit (NOLOAD):
{
. = ALIGN(4);
_rtc_noinit_start = ABSOLUTE(.);
*(.rtc_noinit .rtc_noinit.*)
. = ALIGN(4) ;
_rtc_noinit_end = ABSOLUTE(.);
} > rtc_data_location
/**
* This section located in RTC SLOW Memory area.
* It holds data marked with RTC_SLOW_ATTR attribute.
* See the file "esp_attr.h" for more information.
*/
.rtc.force_slow :
{
. = ALIGN(4);
_rtc_force_slow_start = ABSOLUTE(.);
*(.rtc.force_slow .rtc.force_slow.*)
. = ALIGN(4) ;
_rtc_force_slow_end = ABSOLUTE(.);
} > rtc_slow_seg
/* Get size of rtc slow data based on rtc_data_location alias */
_rtc_slow_length = (ORIGIN(rtc_slow_seg) == ORIGIN(rtc_data_location))
? (_rtc_force_slow_end - _rtc_data_start)
: (_rtc_force_slow_end - _rtc_force_slow_start);
_rtc_fast_length = (ORIGIN(rtc_slow_seg) == ORIGIN(rtc_data_location))
? (_rtc_force_fast_end - _rtc_fast_start)
: (_rtc_noinit_end - _rtc_fast_start);
ASSERT((_rtc_slow_length <= LENGTH(rtc_slow_seg)),
"RTC_SLOW segment data does not fit.")
ASSERT((_rtc_fast_length <= LENGTH(rtc_data_seg)),
"RTC_FAST segment data does not fit.")
/* Send .iram0 code to iram */
.iram0.vectors :
{
_iram_start = ABSOLUTE(.);
/* Vectors go to IRAM */
_init_start = ABSOLUTE(.);
. = 0x0;
KEEP(*(.WindowVectors.text));
. = 0x180;
KEEP(*(.Level2InterruptVector.text));
. = 0x1c0;
KEEP(*(.Level3InterruptVector.text));
. = 0x200;
KEEP(*(.Level4InterruptVector.text));
. = 0x240;
KEEP(*(.Level5InterruptVector.text));
. = 0x280;
KEEP(*(.DebugExceptionVector.text));
. = 0x2c0;
KEEP(*(.NMIExceptionVector.text));
. = 0x300;
KEEP(*(.KernelExceptionVector.text));
. = 0x340;
KEEP(*(.UserExceptionVector.text));
. = 0x3C0;
KEEP(*(.DoubleExceptionVector.text));
. = 0x400;
_invalid_pc_placeholder = ABSOLUTE(.);
*(.*Vector.literal)
*(.UserEnter.literal);
*(.UserEnter.text);
. = ALIGN (16);
*(.entry.text)
*(.init.literal)
*(.init)
_init_end = ABSOLUTE(.);
} > iram0_0_seg
.iram0.text :
{
/* Code marked as running out of IRAM */
_iram_text_start = ABSOLUTE(.);
mapping[iram0_text]
} > iram0_0_seg
/**
* This section is required to skip .iram0.text area because iram0_0_seg and
* dram0_0_seg reflect the same address space on different buses.
*/
.dram0.dummy (NOLOAD):
{
. = ORIGIN(dram0_0_seg) + _iram_end - _iram_start;
} > dram0_0_seg
.dram0.data :
{
_data_start = ABSOLUTE(.);
_bt_data_start = ABSOLUTE(.);
*libbt.a:(.data .data.*)
. = ALIGN (4);
_bt_data_end = ABSOLUTE(.);
_btdm_data_start = ABSOLUTE(.);
*libbtdm_app.a:(.data .data.*)
. = ALIGN (4);
_btdm_data_end = ABSOLUTE(.);
_nimble_data_start = ABSOLUTE(.);
*libnimble.a:(.data .data.*)
. = ALIGN (4);
_nimble_data_end = ABSOLUTE(.);
*(.gnu.linkonce.d.*)
*(.data1)
*(.sdata)
*(.sdata.*)
*(.gnu.linkonce.s.*)
*(.sdata2)
*(.sdata2.*)
*(.gnu.linkonce.s2.*)
*(.jcr)
/* coredump mapping */
_coredump_dram_start = ABSOLUTE(.);
mapping[dram_coredump]
_coredump_dram_end = ABSOLUTE(.);
/* should be placed after coredump mapping */
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT(.esp_system_init_fn) SORT(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
mapping[dram0_data]
_data_end = ABSOLUTE(.);
. = ALIGN(4);
} > dram0_0_seg
/**
* This section holds data that should not be initialized at power up.
* The section located in Internal SRAM memory region. The macro _NOINIT
* can be used as attribute to place data into this section.
* See the "esp_attr.h" file for more information.
*/
.noinit (NOLOAD):
{
. = ALIGN(4);
_noinit_start = ABSOLUTE(.);
*(.noinit .noinit.*)
. = ALIGN(4) ;
_noinit_end = ABSOLUTE(.);
} > dram0_0_seg
/* Shared RAM */
.dram0.bss (NOLOAD) :
{
. = ALIGN (8);
_bss_start = ABSOLUTE(.);
*(.ext_ram.bss*)
_bt_bss_start = ABSOLUTE(.);
*libbt.a:(.bss .bss.* COMMON)
. = ALIGN (4);
_bt_bss_end = ABSOLUTE(.);
_btdm_bss_start = ABSOLUTE(.);
*libbtdm_app.a:(.bss .bss.* COMMON)
. = ALIGN (4);
_btdm_bss_end = ABSOLUTE(.);
_nimble_bss_start = ABSOLUTE(.);
*libnimble.a:(.bss .bss.* COMMON)
. = ALIGN (4);
_nimble_bss_end = ABSOLUTE(.);
mapping[dram0_bss]
*(.dynsbss)
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
*(.scommon)
*(.sbss2)
*(.sbss2.*)
*(.gnu.linkonce.sb2.*)
*(.dynbss)
*(.share.mem)
*(.gnu.linkonce.b.*)
. = ALIGN (8);
_bss_end = ABSOLUTE(.);
} > dram0_0_seg
ASSERT(((_bss_end - ORIGIN(dram0_0_seg)) <= LENGTH(dram0_0_seg)), "DRAM segment data does not fit.")
.flash.text :
{
_stext = .;
_text_start = ABSOLUTE(.);
mapping[flash_text]
*(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
*(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */
*(.fini.literal)
*(.fini)
*(.gnu.version)
_text_end = ABSOLUTE(.);
_etext = .;
/**
* Similar to _iram_start, this symbol goes here so it is
* resolved by addr2line in preference to the first symbol in
* the flash.text segment.
*/
_flash_cache_start = ABSOLUTE(0);
} > default_code_seg
.flash_rodata_dummy (NOLOAD):
{
. = SIZEOF(.flash.text);
. = ALIGN(0x10000) + 0x20;
} > drom0_0_seg
/* When modifying the alignment, don't forget to update tls_section_alignment in pxPortInitialiseStack */
.flash.rodata : ALIGN(0x10)
{
_rodata_start = ABSOLUTE(.);
*(.rodata_desc .rodata_desc.*) /* Should be the first. App version info. DO NOT PUT ANYTHING BEFORE IT! */
*(.rodata_custom_desc .rodata_custom_desc.*) /* Should be the second. Custom app version info. DO NOT PUT ANYTHING BEFORE IT! */
mapping[flash_rodata]
*(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
*(.gnu.linkonce.r.*)
*(.rodata1)
__XT_EXCEPTION_TABLE_ = ABSOLUTE(.);
*(.xt_except_table)
*(.gcc_except_table .gcc_except_table.*)
*(.gnu.linkonce.e.*)
*(.gnu.version_r)
. = (. + 3) & ~ 3;
__eh_frame = ABSOLUTE(.);
KEEP(*(.eh_frame))
. = (. + 7) & ~ 3;
/* C++ constructor and destructor tables */
/* Don't include anything from crtbegin.o or crtend.o, as IDF doesn't use toolchain crt */
__init_array_start = ABSOLUTE(.);
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .ctors .ctors.*))
__init_array_end = ABSOLUTE(.);
KEEP (*crtbegin.*(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.*) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
/* C++ exception handlers table: */
__XT_EXCEPTION_DESCS_ = ABSOLUTE(.);
*(.xt_except_desc)
*(.gnu.linkonce.h.*)
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
*(.xt_except_desc_end)
*(.dynamic)
*(.gnu.version_d)
/* Addresses of memory regions reserved via SOC_RESERVE_MEMORY_REGION() */
soc_reserved_memory_region_start = ABSOLUTE(.);
KEEP (*(.reserved_memory_address))
soc_reserved_memory_region_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
/* Literals are also RO data. */
_lit4_start = ABSOLUTE(.);
*(*.lit4)
*(.lit4.*)
*(.gnu.linkonce.lit4.*)
_lit4_end = ABSOLUTE(.);
. = ALIGN(4);
_thread_local_start = ABSOLUTE(.);
*(.tdata)
*(.tdata.*)
*(.tbss)
*(.tbss.*)
_thread_local_end = ABSOLUTE(.);
. = ALIGN(4);
} > default_rodata_seg
/* Marks the end of IRAM code segment */
.iram0.text_end (NOLOAD) :
{
. = ALIGN (4);
_iram_text_end = ABSOLUTE(.);
} > iram0_0_seg
.iram0.data :
{
. = ALIGN(4);
_iram_data_start = ABSOLUTE(.);
/* coredump mapping */
_coredump_iram_start = ABSOLUTE(.);
mapping[iram_coredump]
_coredump_iram_end = ABSOLUTE(.);
/* should be placed after coredump mapping */
mapping[iram0_data]
_iram_data_end = ABSOLUTE(.);
} > iram0_0_seg
.iram0.bss (NOLOAD) :
{
. = ALIGN(4);
_iram_bss_start = ABSOLUTE(.);
mapping[iram0_bss]
_iram_bss_end = ABSOLUTE(.);
. = ALIGN(4);
_iram_end = ABSOLUTE(.);
} > iram0_0_seg
/* Marks the end of data, bss and possibly rodata */
.dram0.heap_start (NOLOAD) :
{
. = ALIGN (8);
_heap_start = ABSOLUTE(.);
} > dram0_0_seg
}
ASSERT(((_iram_end - ORIGIN(iram0_0_seg)) <= LENGTH(iram0_0_seg)),
"IRAM0 segment data does not fit.")
ASSERT(((_heap_start - ORIGIN(dram0_0_seg)) <= LENGTH(dram0_0_seg)),
"DRAM segment data does not fit.")

View File

@ -0,0 +1,132 @@
[sections:text]
entries:
.text+
.literal+
[sections:data]
entries:
.data+
[sections:bss]
entries:
.bss+
[sections:common]
entries:
COMMON
[sections:rodata]
entries:
.rodata+
[sections:rtc_text]
entries:
.rtc.text+
.rtc.literal
[sections:rtc_data]
entries:
.rtc.data+
[sections:rtc_rodata]
entries:
.rtc.rodata+
[sections:rtc_bss]
entries:
.rtc.bss
[sections:rtc_fast_coredump]
entries:
.rtc.fast.coredump+
[sections:rtc_coredump]
entries:
.rtc.coredump+
[sections:dram_coredump]
entries:
.dram1.coredump+
[sections:iram_coredump]
entries:
.iram.data.coredump+
[sections:iram]
entries:
.iram1+
[sections:iram_data]
entries:
.iram.data+
[sections:iram_bss]
entries:
.iram.bss+
[sections:dram]
entries:
.dram1+
[sections:wifi_iram]
entries:
.wifi0iram+
[sections:wifi_rx_iram]
entries:
.wifirxiram+
[scheme:default]
entries:
if APP_BUILD_USE_FLASH_SECTIONS = y:
text -> flash_text
rodata -> flash_rodata
else:
text -> iram0_text
rodata -> dram0_data
data -> dram0_data
bss -> dram0_bss
common -> dram0_bss
iram -> iram0_text
iram_data -> iram0_data
iram_bss -> iram0_bss
dram -> dram0_data
rtc_text -> rtc_text
rtc_data -> rtc_data
rtc_rodata -> rtc_data
rtc_bss -> rtc_bss
wifi_iram -> flash_text
wifi_rx_iram -> flash_text
dram_coredump -> dram_coredump
iram_coredump -> iram_coredump
rtc_coredump -> rtc_coredump
rtc_fast_coredump -> rtc_fast_coredump
[scheme:rtc]
entries:
text -> rtc_text
data -> rtc_data
rodata -> rtc_data
bss -> rtc_bss
common -> rtc_bss
[scheme:noflash]
entries:
text -> iram0_text
rodata -> dram0_data
[scheme:noflash_data]
entries:
rodata -> dram0_data
[scheme:noflash_text]
entries:
text -> iram0_text
[scheme:wifi_iram]
entries:
wifi_iram -> iram0_text
[scheme:wifi_rx_iram]
entries:
wifi_rx_iram -> iram0_text

View File

@ -0,0 +1,9 @@
[mapping:gcc]
archive: libgcc.a
entries:
lib2funcs (noflash_text)
[mapping:gcov]
archive: libgcov.a
entries:
* (noflash)

View File

@ -0,0 +1,24 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* INTERNAL API
* implementation of generic interface to MMU memory protection features
*/
#include <stdbool.h>
bool esp_memprot_is_assoc_intr_any()
{
return true;
}

View File

@ -0,0 +1,643 @@
// Copyright 2016-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_pm.h"
#include "esp_log.h"
#include "esp32s3/clk.h"
#include "esp_private/crosscore_int.h"
#include "soc/rtc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/xtensa_timer.h"
#include "xtensa/core-macros.h"
#include "esp_private/pm_impl.h"
#include "esp_private/pm_trace.h"
#include "esp_private/esp_timer_private.h"
#include "esp32s3/pm.h"
#include "esp_sleep.h"
/* CCOMPARE update timeout, in CPU cycles. Any value above ~600 cycles will work
* for the purpose of detecting a deadlock.
*/
#define CCOMPARE_UPDATE_TIMEOUT 1000000
/* When changing CCOMPARE, don't allow changes if the difference is less
* than this. This is to prevent setting CCOMPARE below CCOUNT.
*/
#define CCOMPARE_MIN_CYCLES_IN_FUTURE 1000
/* When light sleep is used, wake this number of microseconds earlier than
* the next tick.
*/
#define LIGHT_SLEEP_EARLY_WAKEUP_US 100
/* Minimal divider at which REF_CLK_FREQ can be obtained */
#define REF_CLK_DIV_MIN 2
#ifdef CONFIG_PM_PROFILING
#define WITH_PROFILING
#endif
static portMUX_TYPE s_switch_lock = portMUX_INITIALIZER_UNLOCKED;
/* The following state variables are protected using s_switch_lock: */
/* Current sleep mode; When switching, contains old mode until switch is complete */
static pm_mode_t s_mode = PM_MODE_CPU_MAX;
/* True when switch is in progress */
static volatile bool s_is_switching;
/* When switch is in progress, this is the mode we are switching into */
static pm_mode_t s_new_mode = PM_MODE_CPU_MAX;
/* Number of times each mode was locked */
static size_t s_mode_lock_counts[PM_MODE_COUNT];
/* Bit mask of locked modes. BIT(i) is set iff s_mode_lock_counts[i] > 0. */
static uint32_t s_mode_mask;
/* Divider and multiplier used to adjust (ccompare - ccount) duration.
* Only set to non-zero values when switch is in progress.
*/
static uint32_t s_ccount_div;
static uint32_t s_ccount_mul;
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
#define PERIPH_SKIP_LIGHT_SLEEP_NO 1
/* Indicates if light sleep shoule be skipped by peripherals. */
static skip_light_sleep_cb_t s_periph_skip_light_sleep_cb[PERIPH_SKIP_LIGHT_SLEEP_NO];
/* Indicates if light sleep entry was skipped in vApplicationSleep for given CPU.
* This in turn gets used in IDLE hook to decide if `waiti` needs
* to be invoked or not.
*/
static bool s_skipped_light_sleep[portNUM_PROCESSORS];
#if portNUM_PROCESSORS == 2
/* When light sleep is finished on one CPU, it is possible that the other CPU
* will enter light sleep again very soon, before interrupts on the first CPU
* get a chance to run. To avoid such situation, set a flag for the other CPU to
* skip light sleep attempt.
*/
static bool s_skip_light_sleep[portNUM_PROCESSORS];
#endif // portNUM_PROCESSORS == 2
#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
/* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU.
* Used in conjunction with cross-core interrupt to update CCOMPARE on the other CPU.
*/
static volatile bool s_need_update_ccompare[portNUM_PROCESSORS];
/* A flag indicating that Idle hook has run on a given CPU;
* Next interrupt on the same CPU will take s_rtos_lock_handle.
*/
static bool s_core_idle[portNUM_PROCESSORS];
/* When no RTOS tasks are active, these locks are released to allow going into
* a lower power mode. Used by ISR hook and idle hook.
*/
static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS];
/* Lookup table of CPU frequency configs to be used in each mode.
* Initialized by esp_pm_impl_init and modified by esp_pm_configure.
*/
rtc_cpu_freq_config_t s_cpu_freq_by_mode[PM_MODE_COUNT];
/* Whether automatic light sleep is enabled */
static bool s_light_sleep_en = false;
/* When configuration is changed, current frequency may not match the
* newly configured frequency for the current mode. This is an indicator
* to the mode switch code to get the actual current frequency instead of
* relying on the current mode.
*/
static bool s_config_changed = false;
#ifdef WITH_PROFILING
/* Time, in microseconds, spent so far in each mode */
static pm_time_t s_time_in_mode[PM_MODE_COUNT];
/* Timestamp, in microseconds, when the mode switch last happened */
static pm_time_t s_last_mode_change_time;
/* User-readable mode names, used by esp_pm_impl_dump_stats */
static const char *s_mode_names[] = {
"SLEEP",
"APB_MIN",
"APB_MAX",
"CPU_MAX"
};
#endif // WITH_PROFILING
static const char *TAG = "pm_esp32s3";
static void update_ccompare(void);
static void do_switch(pm_mode_t new_mode);
static void leave_idle(void);
static void on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_per_us);
pm_mode_t esp_pm_impl_get_mode(esp_pm_lock_type_t type, int arg)
{
(void) arg;
if (type == ESP_PM_CPU_FREQ_MAX) {
return PM_MODE_CPU_MAX;
} else if (type == ESP_PM_APB_FREQ_MAX) {
return PM_MODE_APB_MAX;
} else if (type == ESP_PM_NO_LIGHT_SLEEP) {
return PM_MODE_APB_MIN;
} else {
// unsupported mode
abort();
}
}
esp_err_t esp_pm_configure(const void *vconfig)
{
#ifndef CONFIG_PM_ENABLE
return ESP_ERR_NOT_SUPPORTED;
#endif
const esp_pm_config_esp32s3_t *config = (const esp_pm_config_esp32s3_t *) vconfig;
#ifndef CONFIG_FREERTOS_USE_TICKLESS_IDLE
if (config->light_sleep_enable) {
return ESP_ERR_NOT_SUPPORTED;
}
#endif
int min_freq_mhz = config->min_freq_mhz;
int max_freq_mhz = config->max_freq_mhz;
if (min_freq_mhz > max_freq_mhz) {
return ESP_ERR_INVALID_ARG;
}
rtc_cpu_freq_config_t freq_config;
if (!rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &freq_config)) {
ESP_LOGW(TAG, "invalid min_freq_mhz value (%d)", min_freq_mhz);
return ESP_ERR_INVALID_ARG;
}
int xtal_freq_mhz = (int) rtc_clk_xtal_freq_get();
if (min_freq_mhz < xtal_freq_mhz && min_freq_mhz * MHZ / REF_CLK_FREQ < REF_CLK_DIV_MIN) {
ESP_LOGW(TAG, "min_freq_mhz should be >= %d", REF_CLK_FREQ * REF_CLK_DIV_MIN / MHZ);
return ESP_ERR_INVALID_ARG;
}
if (!rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &freq_config)) {
ESP_LOGW(TAG, "invalid max_freq_mhz value (%d)", max_freq_mhz);
return ESP_ERR_INVALID_ARG;
}
int apb_max_freq = MIN(max_freq_mhz, 80); /* CPU frequency in APB_MAX mode */
apb_max_freq = MAX(apb_max_freq, min_freq_mhz);
ESP_LOGI(TAG, "Frequency switching config: "
"CPU_MAX: %d, APB_MAX: %d, APB_MIN: %d, Light sleep: %s",
max_freq_mhz,
apb_max_freq,
min_freq_mhz,
config->light_sleep_enable ? "ENABLED" : "DISABLED");
portENTER_CRITICAL(&s_switch_lock);
bool res;
res = rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_CPU_MAX]);
assert(res);
res = rtc_clk_cpu_freq_mhz_to_config(apb_max_freq, &s_cpu_freq_by_mode[PM_MODE_APB_MAX]);
assert(res);
res = rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_APB_MIN]);
assert(res);
s_cpu_freq_by_mode[PM_MODE_LIGHT_SLEEP] = s_cpu_freq_by_mode[PM_MODE_APB_MIN];
s_light_sleep_en = config->light_sleep_enable;
s_config_changed = true;
portEXIT_CRITICAL(&s_switch_lock);
return ESP_OK;
}
static pm_mode_t IRAM_ATTR get_lowest_allowed_mode(void)
{
/* TODO: optimize using ffs/clz */
if (s_mode_mask >= BIT(PM_MODE_CPU_MAX)) {
return PM_MODE_CPU_MAX;
} else if (s_mode_mask >= BIT(PM_MODE_APB_MAX)) {
return PM_MODE_APB_MAX;
} else if (s_mode_mask >= BIT(PM_MODE_APB_MIN) || !s_light_sleep_en) {
return PM_MODE_APB_MIN;
} else {
return PM_MODE_LIGHT_SLEEP;
}
}
void IRAM_ATTR esp_pm_impl_switch_mode(pm_mode_t mode,
pm_mode_switch_t lock_or_unlock, pm_time_t now)
{
bool need_switch = false;
uint32_t mode_mask = BIT(mode);
portENTER_CRITICAL_SAFE(&s_switch_lock);
uint32_t count;
if (lock_or_unlock == MODE_LOCK) {
count = ++s_mode_lock_counts[mode];
} else {
count = s_mode_lock_counts[mode]--;
}
if (count == 1) {
if (lock_or_unlock == MODE_LOCK) {
s_mode_mask |= mode_mask;
} else {
s_mode_mask &= ~mode_mask;
}
need_switch = true;
}
pm_mode_t new_mode = s_mode;
if (need_switch) {
new_mode = get_lowest_allowed_mode();
#ifdef WITH_PROFILING
if (s_last_mode_change_time != 0) {
pm_time_t diff = now - s_last_mode_change_time;
s_time_in_mode[s_mode] += diff;
}
s_last_mode_change_time = now;
#endif // WITH_PROFILING
}
portEXIT_CRITICAL_SAFE(&s_switch_lock);
if (need_switch && new_mode != s_mode) {
do_switch(new_mode);
}
}
/**
* @brief Update clock dividers in esp_timer and FreeRTOS, and adjust CCOMPARE
* values on both CPUs.
* @param old_ticks_per_us old CPU frequency
* @param ticks_per_us new CPU frequency
*/
static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_per_us)
{
uint32_t old_apb_ticks_per_us = MIN(old_ticks_per_us, 80);
uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80);
/* Update APB frequency value used by the timer */
if (old_apb_ticks_per_us != apb_ticks_per_us) {
esp_timer_private_update_apb_freq(apb_ticks_per_us);
}
/* Calculate new tick divisor */
_xt_tick_divisor = ticks_per_us * MHZ / XT_TICK_PER_SEC;
int core_id = xPortGetCoreID();
if (s_rtos_lock_handle[core_id] != NULL) {
ESP_PM_TRACE_ENTER(CCOMPARE_UPDATE, core_id);
/* ccount_div and ccount_mul are used in esp_pm_impl_update_ccompare
* to calculate new CCOMPARE value.
*/
s_ccount_div = old_ticks_per_us;
s_ccount_mul = ticks_per_us;
/* Update CCOMPARE value on this CPU */
update_ccompare();
#if portNUM_PROCESSORS == 2
/* Send interrupt to the other CPU to update CCOMPARE value */
int other_core_id = (core_id == 0) ? 1 : 0;
s_need_update_ccompare[other_core_id] = true;
esp_crosscore_int_send_freq_switch(other_core_id);
int timeout = 0;
while (s_need_update_ccompare[other_core_id]) {
if (++timeout == CCOMPARE_UPDATE_TIMEOUT) {
assert(false && "failed to update CCOMPARE, possible deadlock");
}
}
#endif // portNUM_PROCESSORS == 2
s_ccount_mul = 0;
s_ccount_div = 0;
ESP_PM_TRACE_EXIT(CCOMPARE_UPDATE, core_id);
}
}
/**
* Perform the switch to new power mode.
* Currently only changes the CPU frequency and adjusts clock dividers.
* No light sleep yet.
* @param new_mode mode to switch to
*/
static void IRAM_ATTR do_switch(pm_mode_t new_mode)
{
const int core_id = xPortGetCoreID();
do {
portENTER_CRITICAL_ISR(&s_switch_lock);
if (!s_is_switching) {
break;
}
if (s_new_mode <= new_mode) {
portEXIT_CRITICAL_ISR(&s_switch_lock);
return;
}
if (s_need_update_ccompare[core_id]) {
s_need_update_ccompare[core_id] = false;
}
portEXIT_CRITICAL_ISR(&s_switch_lock);
} while (true);
s_new_mode = new_mode;
s_is_switching = true;
bool config_changed = s_config_changed;
s_config_changed = false;
portEXIT_CRITICAL_ISR(&s_switch_lock);
rtc_cpu_freq_config_t new_config = s_cpu_freq_by_mode[new_mode];
rtc_cpu_freq_config_t old_config;
if (!config_changed) {
old_config = s_cpu_freq_by_mode[s_mode];
} else {
rtc_clk_cpu_freq_get_config(&old_config);
}
if (new_config.freq_mhz != old_config.freq_mhz) {
uint32_t old_ticks_per_us = old_config.freq_mhz;
uint32_t new_ticks_per_us = new_config.freq_mhz;
bool switch_down = new_ticks_per_us < old_ticks_per_us;
ESP_PM_TRACE_ENTER(FREQ_SWITCH, core_id);
if (switch_down) {
on_freq_update(old_ticks_per_us, new_ticks_per_us);
}
rtc_clk_cpu_freq_set_config_fast(&new_config);
if (!switch_down) {
on_freq_update(old_ticks_per_us, new_ticks_per_us);
}
ESP_PM_TRACE_EXIT(FREQ_SWITCH, core_id);
}
portENTER_CRITICAL_ISR(&s_switch_lock);
s_mode = new_mode;
s_is_switching = false;
portEXIT_CRITICAL_ISR(&s_switch_lock);
}
/**
* @brief Calculate new CCOMPARE value based on s_ccount_{mul,div}
*
* Adjusts CCOMPARE value so that the interrupt happens at the same time as it
* would happen without the frequency change.
* Assumes that the new_frequency = old_frequency * s_ccount_mul / s_ccount_div.
*/
static void IRAM_ATTR update_ccompare(void)
{
uint32_t ccount = XTHAL_GET_CCOUNT();
uint32_t ccompare = XTHAL_GET_CCOMPARE(XT_TIMER_INDEX);
if ((ccompare - CCOMPARE_MIN_CYCLES_IN_FUTURE) - ccount < UINT32_MAX / 2) {
uint32_t diff = ccompare - ccount;
uint32_t diff_scaled = (diff * s_ccount_mul + s_ccount_div - 1) / s_ccount_div;
if (diff_scaled < _xt_tick_divisor) {
uint32_t new_ccompare = ccount + diff_scaled;
XTHAL_SET_CCOMPARE(XT_TIMER_INDEX, new_ccompare);
}
}
}
static void IRAM_ATTR leave_idle(void)
{
int core_id = xPortGetCoreID();
if (s_core_idle[core_id]) {
// TODO: possible optimization: raise frequency here first
esp_pm_lock_acquire(s_rtos_lock_handle[core_id]);
s_core_idle[core_id] = false;
}
}
void esp_pm_impl_idle_hook(void)
{
int core_id = xPortGetCoreID();
uint32_t state = portENTER_CRITICAL_NESTED();
if (!s_core_idle[core_id]) {
esp_pm_lock_release(s_rtos_lock_handle[core_id]);
s_core_idle[core_id] = true;
}
portEXIT_CRITICAL_NESTED(state);
ESP_PM_TRACE_ENTER(IDLE, core_id);
}
void IRAM_ATTR esp_pm_impl_isr_hook(void)
{
int core_id = xPortGetCoreID();
ESP_PM_TRACE_ENTER(ISR_HOOK, core_id);
/* Prevent higher level interrupts (than the one this function was called from)
* from happening in this section, since they will also call into esp_pm_impl_isr_hook.
*/
uint32_t state = portENTER_CRITICAL_NESTED();
#if portNUM_PROCESSORS == 2
if (s_need_update_ccompare[core_id]) {
update_ccompare();
s_need_update_ccompare[core_id] = false;
} else {
leave_idle();
}
#else
leave_idle();
#endif // portNUM_PROCESSORS == 2
portEXIT_CRITICAL_NESTED(state);
ESP_PM_TRACE_EXIT(ISR_HOOK, core_id);
}
void esp_pm_impl_waiti(void)
{
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
int core_id = xPortGetCoreID();
if (s_skipped_light_sleep[core_id]) {
asm("waiti 0");
/* Interrupt took the CPU out of waiti and s_rtos_lock_handle[core_id]
* is now taken. However since we are back to idle task, we can release
* the lock so that vApplicationSleep can attempt to enter light sleep.
*/
esp_pm_impl_idle_hook();
s_skipped_light_sleep[core_id] = false;
}
#else
asm("waiti 0");
#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
}
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
esp_err_t esp_pm_register_skip_light_sleep_callback(skip_light_sleep_cb_t cb)
{
for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) {
if (s_periph_skip_light_sleep_cb[i] == cb) {
return ESP_OK;
} else if (s_periph_skip_light_sleep_cb[i] == NULL) {
s_periph_skip_light_sleep_cb[i] = cb;
return ESP_OK;
}
}
return ESP_ERR_NO_MEM;
}
esp_err_t esp_pm_unregister_skip_light_sleep_callback(skip_light_sleep_cb_t cb)
{
for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) {
if (s_periph_skip_light_sleep_cb[i] == cb) {
s_periph_skip_light_sleep_cb[i] = NULL;
return ESP_OK;
}
}
return ESP_ERR_INVALID_STATE;
}
static inline bool IRAM_ATTR periph_should_skip_light_sleep(void)
{
for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) {
if (s_periph_skip_light_sleep_cb[i]) {
if (s_periph_skip_light_sleep_cb[i]() == true) {
return true;
}
}
}
return false;
}
static inline bool IRAM_ATTR should_skip_light_sleep(int core_id)
{
#if portNUM_PROCESSORS == 2
if (s_skip_light_sleep[core_id]) {
s_skip_light_sleep[core_id] = false;
s_skipped_light_sleep[core_id] = true;
return true;
}
#endif // portNUM_PROCESSORS == 2
if (s_mode != PM_MODE_LIGHT_SLEEP || s_is_switching || periph_should_skip_light_sleep()) {
s_skipped_light_sleep[core_id] = true;
} else {
s_skipped_light_sleep[core_id] = false;
}
return s_skipped_light_sleep[core_id];
}
static inline void IRAM_ATTR other_core_should_skip_light_sleep(int core_id)
{
#if portNUM_PROCESSORS == 2
s_skip_light_sleep[!core_id] = true;
#endif
}
void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
{
portENTER_CRITICAL(&s_switch_lock);
int core_id = xPortGetCoreID();
if (!should_skip_light_sleep(core_id)) {
/* Calculate how much we can sleep */
int64_t next_esp_timer_alarm = esp_timer_get_next_alarm();
int64_t now = esp_timer_get_time();
int64_t time_until_next_alarm = next_esp_timer_alarm - now;
int64_t wakeup_delay_us = portTICK_PERIOD_MS * 1000LL * xExpectedIdleTime;
int64_t sleep_time_us = MIN(wakeup_delay_us, time_until_next_alarm);
if (sleep_time_us >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP * portTICK_PERIOD_MS * 1000LL) {
esp_sleep_enable_timer_wakeup(sleep_time_us - LIGHT_SLEEP_EARLY_WAKEUP_US);
#ifdef CONFIG_PM_TRACE
/* to force tracing GPIOs to keep state */
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#endif
/* Enter sleep */
ESP_PM_TRACE_ENTER(SLEEP, core_id);
int64_t sleep_start = esp_timer_get_time();
esp_light_sleep_start();
int64_t slept_us = esp_timer_get_time() - sleep_start;
ESP_PM_TRACE_EXIT(SLEEP, core_id);
uint32_t slept_ticks = slept_us / (portTICK_PERIOD_MS * 1000LL);
if (slept_ticks > 0) {
/* Adjust RTOS tick count based on the amount of time spent in sleep */
vTaskStepTick(slept_ticks);
/* Trigger tick interrupt, since sleep time was longer
* than portTICK_PERIOD_MS. Note that setting INTSET does not
* work for timer interrupt, and changing CCOMPARE would clear
* the interrupt flag.
*/
XTHAL_SET_CCOUNT(XTHAL_GET_CCOMPARE(XT_TIMER_INDEX) - 16);
while (!(XTHAL_GET_INTERRUPT() & BIT(XT_TIMER_INTNUM))) {
;
}
}
other_core_should_skip_light_sleep(core_id);
}
}
portEXIT_CRITICAL(&s_switch_lock);
}
#endif //CONFIG_FREERTOS_USE_TICKLESS_IDLE
#ifdef WITH_PROFILING
void esp_pm_impl_dump_stats(FILE *out)
{
pm_time_t time_in_mode[PM_MODE_COUNT];
portENTER_CRITICAL_ISR(&s_switch_lock);
memcpy(time_in_mode, s_time_in_mode, sizeof(time_in_mode));
pm_time_t last_mode_change_time = s_last_mode_change_time;
pm_mode_t cur_mode = s_mode;
pm_time_t now = pm_get_time();
portEXIT_CRITICAL_ISR(&s_switch_lock);
time_in_mode[cur_mode] += now - last_mode_change_time;
fprintf(out, "Mode stats:\n");
for (int i = 0; i < PM_MODE_COUNT; ++i) {
if (i == PM_MODE_LIGHT_SLEEP && !s_light_sleep_en) {
/* don't display light sleep mode if it's not enabled */
continue;
}
fprintf(out, "%8s %3dM %12lld %2d%%\n",
s_mode_names[i],
s_cpu_freq_by_mode[i].freq_mhz,
time_in_mode[i],
(int) (time_in_mode[i] * 100 / now));
}
}
#endif // WITH_PROFILING
void esp_pm_impl_init(void)
{
#ifdef CONFIG_PM_TRACE
esp_pm_trace_init();
#endif
ESP_ERROR_CHECK(esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "rtos0",
&s_rtos_lock_handle[0]));
ESP_ERROR_CHECK(esp_pm_lock_acquire(s_rtos_lock_handle[0]));
#if portNUM_PROCESSORS == 2
ESP_ERROR_CHECK(esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "rtos1",
&s_rtos_lock_handle[1]));
ESP_ERROR_CHECK(esp_pm_lock_acquire(s_rtos_lock_handle[1]));
#endif // portNUM_PROCESSORS == 2
/* Configure all modes to use the default CPU frequency.
* This will be modified later by a call to esp_pm_configure.
*/
rtc_cpu_freq_config_t default_config;
if (!rtc_clk_cpu_freq_mhz_to_config(CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ, &default_config)) {
assert(false && "unsupported frequency");
}
for (size_t i = 0; i < PM_MODE_COUNT; ++i) {
s_cpu_freq_by_mode[i] = default_config;
}
}

View File

@ -0,0 +1,51 @@
// Copyright 2016-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp_private/pm_trace.h"
#include "driver/gpio.h"
#include "soc/gpio_reg.h"
/* GPIOs to use for tracing of esp_pm events.
* Two entries in the array for each type, one for each CPU.
* Feel free to change when debugging.
*/
static const int DRAM_ATTR s_trace_io[] = {
BIT(4), BIT(5), // ESP_PM_TRACE_IDLE
BIT(16), BIT(17), // ESP_PM_TRACE_TICK
BIT(18), BIT(18), // ESP_PM_TRACE_FREQ_SWITCH
BIT(19), BIT(19), // ESP_PM_TRACE_CCOMPARE_UPDATE
BIT(25), BIT(26), // ESP_PM_TRACE_ISR_HOOK
BIT(27), BIT(27), // ESP_PM_TRACE_SLEEP
};
void esp_pm_trace_init(void)
{
for (size_t i = 0; i < sizeof(s_trace_io) / sizeof(s_trace_io[0]); ++i) {
int io = __builtin_ffs(s_trace_io[i]);
if (io == 0) {
continue;
}
gpio_set_direction(io - 1, GPIO_MODE_OUTPUT);
}
}
void IRAM_ATTR esp_pm_trace_enter(esp_pm_trace_event_t event, int core_id)
{
REG_WRITE(GPIO_OUT_W1TS_REG, s_trace_io[2 * event + core_id]);
}
void IRAM_ATTR esp_pm_trace_exit(esp_pm_trace_event_t event, int core_id)
{
REG_WRITE(GPIO_OUT_W1TC_REG, s_trace_io[2 * event + core_id]);
}

View File

@ -0,0 +1,717 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stddef.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "esp_sleep.h"
#include "esp_private/esp_timer_private.h"
#include "esp_log.h"
#include "esp32s3/clk.h"
#include "esp_newlib.h"
#include "esp_spi_flash.h"
#include "esp32s3/rom/cache.h"
#include "esp32s3/rom/rtc.h"
#include "esp32s3/rom/ets_sys.h"
#include "esp_rom_uart.h"
#include "soc/cpu.h"
#include "soc/rtc.h"
#include "soc/spi_periph.h"
#include "soc/dport_reg.h"
#include "soc/extmem_reg.h"
#include "soc/soc_memory_layout.h"
#include "soc/uart_caps.h"
#include "hal/wdt_hal.h"
#include "hal/clk_gate_ll.h"
#include "driver/rtc_io.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
// If light sleep time is less than that, don't power down flash
#define FLASH_PD_MIN_SLEEP_TIME_US 2000
// Time from VDD_SDIO power up to first flash read in ROM code
#define VDD_SDIO_POWERUP_TO_FLASH_READ_US 700
// Extra time it takes to enter and exit light sleep and deep sleep
// For deep sleep, this is until the wake stub runs (not the app).
#ifdef CONFIG_ESP32S3_RTC_CLK_SRC_EXT_CRYS
#define LIGHT_SLEEP_TIME_OVERHEAD_US (650 + 30 * 240 / CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ)
#define DEEP_SLEEP_TIME_OVERHEAD_US (650 + 100 * 240 / CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ)
#else
#define LIGHT_SLEEP_TIME_OVERHEAD_US (250 + 30 * 240 / CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ)
#define DEEP_SLEEP_TIME_OVERHEAD_US (250 + 100 * 240 / CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ)
#endif // CONFIG_ESP32S3_RTC_CLK_SRC_EXT_CRYS
// Minimal amount of time we can sleep for
#define LIGHT_SLEEP_MIN_TIME_US 200
#define CHECK_SOURCE(source, value, mask) ((s_config.wakeup_triggers & mask) && \
(source == value))
/**
* Internal structure which holds all requested deep sleep parameters
*/
typedef struct {
esp_sleep_pd_option_t pd_options[ESP_PD_DOMAIN_MAX];
uint64_t sleep_duration;
uint32_t wakeup_triggers : 15;
uint32_t ext1_trigger_mode : 1;
uint32_t ext1_rtc_gpio_mask : 18;
uint32_t ext0_trigger_level : 1;
uint32_t ext0_rtc_gpio_num : 5;
uint32_t sleep_time_adjustment;
uint64_t rtc_ticks_at_sleep_start;
} sleep_config_t;
static sleep_config_t s_config = {
.pd_options = { ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO },
.wakeup_triggers = 0
};
/* Internal variable used to track if light sleep wakeup sources are to be
expected when determining wakeup cause. */
static bool s_light_sleep_wakeup = false;
/* Updating RTC_MEMORY_CRC_REG register via set_rtc_memory_crc()
is not thread-safe. */
static _lock_t lock_rtc_memory_crc;
static const char *TAG = "sleep";
static uint32_t get_power_down_flags(void);
static void ext0_wakeup_prepare(void);
static void ext1_wakeup_prepare(void);
static void timer_wakeup_prepare(void);
static void touch_wakeup_prepare(void);
/* Wake from deep sleep stub
See esp_deepsleep.h esp_wake_deep_sleep() comments for details.
*/
esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void)
{
_lock_acquire(&lock_rtc_memory_crc);
uint32_t stored_crc = REG_READ(RTC_MEMORY_CRC_REG);
set_rtc_memory_crc();
uint32_t calc_crc = REG_READ(RTC_MEMORY_CRC_REG);
REG_WRITE(RTC_MEMORY_CRC_REG, stored_crc);
_lock_release(&lock_rtc_memory_crc);
if (stored_crc != calc_crc) {
return NULL;
}
esp_deep_sleep_wake_stub_fn_t stub_ptr = (esp_deep_sleep_wake_stub_fn_t) REG_READ(RTC_ENTRY_ADDR_REG);
if (!esp_ptr_executable(stub_ptr)) {
return NULL;
}
return stub_ptr;
}
void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub)
{
_lock_acquire(&lock_rtc_memory_crc);
REG_WRITE(RTC_ENTRY_ADDR_REG, (uint32_t)new_stub);
set_rtc_memory_crc();
_lock_release(&lock_rtc_memory_crc);
}
void RTC_IRAM_ATTR esp_default_wake_deep_sleep(void)
{
REG_SET_BIT(EXTMEM_CACHE_CONF_MISC_REG, EXTMEM_CACHE_TRACE_ENA);
}
void __attribute__((weak, alias("esp_default_wake_deep_sleep"))) esp_wake_deep_sleep(void);
void esp_deep_sleep(uint64_t time_in_us)
{
esp_sleep_enable_timer_wakeup(time_in_us);
esp_deep_sleep_start();
}
static void IRAM_ATTR flush_uarts(void)
{
for (int i = 0; i < SOC_UART_NUM; ++i) {
if (periph_ll_periph_enabled(PERIPH_UART0_MODULE + i)) {
esp_rom_uart_tx_wait_idle(i);
}
}
}
static void IRAM_ATTR suspend_uarts(void)
{
for (int i = 0; i < SOC_UART_NUM; ++i) {
if (periph_ll_periph_enabled(PERIPH_UART0_MODULE + i)) {
REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XON);
REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_SW_FLOW_CON_EN | UART_FORCE_XOFF);
while (REG_GET_FIELD(UART_FSM_STATUS_REG(i), UART_ST_UTX_OUT) != 0) {
;
}
}
}
}
static void IRAM_ATTR resume_uarts(void)
{
for (int i = 0; i < SOC_UART_NUM; ++i) {
if (periph_ll_periph_enabled(PERIPH_UART0_MODULE + i)) {
REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XOFF);
REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XON);
REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_SW_FLOW_CON_EN | UART_FORCE_XON);
}
}
}
static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
{
// Stop UART output so that output is not lost due to APB frequency change.
// For light sleep, suspend UART output — it will resume after wakeup.
// For deep sleep, wait for the contents of UART FIFO to be sent.
if (pd_flags & RTC_SLEEP_PD_DIG) {
flush_uarts();
} else {
suspend_uarts();
}
// Save current frequency and switch to XTAL
// Save current frequency and switch to XTAL
rtc_cpu_freq_config_t cpu_freq_config;
rtc_clk_cpu_freq_get_config(&cpu_freq_config);
rtc_clk_cpu_freq_set_xtal();
// Configure pins for external wakeup
if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) {
ext0_wakeup_prepare();
}
if (s_config.wakeup_triggers & RTC_EXT1_TRIG_EN) {
ext1_wakeup_prepare();
}
// Enable ULP wakeup
if (s_config.wakeup_triggers & RTC_ULP_TRIG_EN) {
// no-op for esp32s3
}
// Enable Touch wakeup
if (s_config.wakeup_triggers & RTC_TOUCH_TRIG_EN) {
touch_wakeup_prepare();
}
uint32_t reject_triggers = 0;
if ((pd_flags & RTC_SLEEP_PD_DIG) == 0) {
/* Light sleep, enable sleep reject for faster return from this function,
* in case the wakeup is already triggerred.
*/
reject_triggers = s_config.wakeup_triggers;
}
// Enter sleep
rtc_sleep_config_t config = RTC_SLEEP_CONFIG_DEFAULT(pd_flags);
rtc_sleep_init(config);
// Configure timer wakeup
if ((s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) &&
s_config.sleep_duration > 0) {
timer_wakeup_prepare();
}
uint32_t result = rtc_sleep_start(s_config.wakeup_triggers, reject_triggers, 1);
// Restore CPU frequency
rtc_clk_cpu_freq_set_config(&cpu_freq_config);
// re-enable UART output
resume_uarts();
return result;
}
void IRAM_ATTR esp_deep_sleep_start(void)
{
// record current RTC time
s_config.rtc_ticks_at_sleep_start = rtc_time_get();
esp_sync_counters_rtc_and_frc();
// Configure wake stub
if (esp_get_deep_sleep_wake_stub() == NULL) {
esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep);
}
// Decide which power domains can be powered down
uint32_t pd_flags = get_power_down_flags();
// Correct the sleep time
s_config.sleep_time_adjustment = DEEP_SLEEP_TIME_OVERHEAD_US;
// Enter sleep
esp_sleep_start(RTC_SLEEP_PD_DIG | RTC_SLEEP_PD_VDDSDIO | pd_flags);
// Because RTC is in a slower clock domain than the CPU, it
// can take several CPU cycles for the sleep mode to start.
while (1) {
;
}
}
/**
* Helper function which handles entry to and exit from light sleep
* Placed into IRAM as flash may need some time to be powered on.
*/
static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
uint32_t flash_enable_time_us,
rtc_vddsdio_config_t vddsdio_config) IRAM_ATTR __attribute__((noinline));
static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
uint32_t flash_enable_time_us,
rtc_vddsdio_config_t vddsdio_config)
{
// Enter sleep
esp_err_t err = esp_sleep_start(pd_flags);
// If VDDSDIO regulator was controlled by RTC registers before sleep,
// restore the configuration.
if (vddsdio_config.force) {
rtc_vddsdio_set_config(vddsdio_config);
}
// If SPI flash was powered down, wait for it to become ready
if (pd_flags & RTC_SLEEP_PD_VDDSDIO) {
// Wait for the flash chip to start up
ets_delay_us(flash_enable_time_us);
}
return err;
}
esp_err_t esp_light_sleep_start(void)
{
static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&light_sleep_lock);
/* We will be calling esp_timer_private_advance inside DPORT access critical
* section. Make sure the code on the other CPU is not holding esp_timer
* lock, otherwise there will be deadlock.
*/
esp_timer_private_lock();
s_config.rtc_ticks_at_sleep_start = rtc_time_get();
uint64_t frc_time_at_start = esp_timer_get_time();
DPORT_STALL_OTHER_CPU_START();
// Decide which power domains can be powered down
uint32_t pd_flags = get_power_down_flags();
// Amount of time to subtract from actual sleep time.
// This is spent on entering and leaving light sleep.
s_config.sleep_time_adjustment = LIGHT_SLEEP_TIME_OVERHEAD_US;
// Decide if VDD_SDIO needs to be powered down;
// If it needs to be powered down, adjust sleep time.
const uint32_t flash_enable_time_us = VDD_SDIO_POWERUP_TO_FLASH_READ_US;
#ifndef CONFIG_SPIRAM
const uint32_t vddsdio_pd_sleep_duration = MAX(FLASH_PD_MIN_SLEEP_TIME_US,
flash_enable_time_us + LIGHT_SLEEP_TIME_OVERHEAD_US + LIGHT_SLEEP_MIN_TIME_US);
if (s_config.sleep_duration > vddsdio_pd_sleep_duration) {
pd_flags |= RTC_SLEEP_PD_VDDSDIO;
s_config.sleep_time_adjustment += flash_enable_time_us;
}
#endif //CONFIG_SPIRAM
rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config();
// Safety net: enable WDT in case exit from light sleep fails
wdt_hal_context_t rtc_wdt_ctx = {.inst = WDT_RWDT, .rwdt_dev = &RTCCNTL};
bool wdt_was_enabled = wdt_hal_is_enabled(&rtc_wdt_ctx); // If WDT was enabled in the user code, then do not change it here.
if (!wdt_was_enabled) {
wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false);
uint32_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL);
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC);
wdt_hal_enable(&rtc_wdt_ctx);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
}
// Enter sleep, then wait for flash to be ready on wakeup
esp_err_t err = esp_light_sleep_inner(pd_flags,
flash_enable_time_us, vddsdio_config);
s_light_sleep_wakeup = true;
// FRC1 has been clock gated for the duration of the sleep, correct for that.
uint64_t rtc_ticks_at_end = rtc_time_get();
uint64_t frc_time_at_end = esp_timer_get_time();
uint64_t rtc_time_diff = rtc_time_slowclk_to_us(rtc_ticks_at_end - s_config.rtc_ticks_at_sleep_start,
esp_clk_slowclk_cal_get());
uint64_t frc_time_diff = frc_time_at_end - frc_time_at_start;
int64_t time_diff = rtc_time_diff - frc_time_diff;
/* Small negative values (up to 1 RTC_SLOW clock period) are possible,
* for very small values of sleep_duration. Ignore those to keep esp_timer
* monotonic.
*/
if (time_diff > 0) {
esp_timer_private_advance(time_diff);
}
esp_set_time_from_rtc();
esp_timer_private_unlock();
DPORT_STALL_OTHER_CPU_END();
if (!wdt_was_enabled) {
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_disable(&rtc_wdt_ctx);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
}
portEXIT_CRITICAL(&light_sleep_lock);
return err;
}
esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source)
{
// For most of sources it is enough to set trigger mask in local
// configuration structure. The actual RTC wake up options
// will be updated by esp_sleep_start().
if (source == ESP_SLEEP_WAKEUP_ALL) {
s_config.wakeup_triggers = 0;
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_TIMER, RTC_TIMER_TRIG_EN)) {
s_config.wakeup_triggers &= ~RTC_TIMER_TRIG_EN;
s_config.sleep_duration = 0;
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_EXT0, RTC_EXT0_TRIG_EN)) {
s_config.ext0_rtc_gpio_num = 0;
s_config.ext0_trigger_level = 0;
s_config.wakeup_triggers &= ~RTC_EXT0_TRIG_EN;
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_EXT1, RTC_EXT1_TRIG_EN)) {
s_config.ext1_rtc_gpio_mask = 0;
s_config.ext1_trigger_mode = 0;
s_config.wakeup_triggers &= ~RTC_EXT1_TRIG_EN;
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_TOUCHPAD, RTC_TOUCH_TRIG_EN)) {
s_config.wakeup_triggers &= ~RTC_TOUCH_TRIG_EN;
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_GPIO, RTC_GPIO_TRIG_EN)) {
s_config.wakeup_triggers &= ~RTC_GPIO_TRIG_EN;
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_UART, (RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN))) {
s_config.wakeup_triggers &= ~(RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN);
}
#ifdef CONFIG_ESP32S3_ULP_COPROC_ENABLED
else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_ULP, RTC_ULP_TRIG_EN)) {
s_config.wakeup_triggers &= ~RTC_ULP_TRIG_EN;
}
#endif
else {
ESP_LOGE(TAG, "Incorrect wakeup source (%d) to disable.", (int) source);
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
esp_err_t esp_sleep_enable_ulp_wakeup(void)
{
s_config.wakeup_triggers |= (RTC_ULP_TRIG_EN | RTC_COCPU_TRIG_EN | RTC_COCPU_TRAP_TRIG_EN);
return ESP_OK;
}
esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us)
{
s_config.wakeup_triggers |= RTC_TIMER_TRIG_EN;
s_config.sleep_duration = time_in_us;
return ESP_OK;
}
static void timer_wakeup_prepare(void)
{
uint32_t period = esp_clk_slowclk_cal_get();
int64_t sleep_duration = (int64_t) s_config.sleep_duration - (int64_t) s_config.sleep_time_adjustment;
if (sleep_duration < 0) {
sleep_duration = 0;
}
int64_t rtc_count_delta = rtc_time_us_to_slowclk(sleep_duration, period);
rtc_sleep_set_wakeup_time(s_config.rtc_ticks_at_sleep_start + rtc_count_delta);
SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG, RTC_CNTL_MAIN_TIMER_INT_CLR_M);
SET_PERI_REG_MASK(RTC_CNTL_SLP_TIMER1_REG, RTC_CNTL_MAIN_TIMER_ALARM_EN_M);
}
/* In deep sleep mode, only the sleep channel is supported, and other touch channels should be turned off. */
static void touch_wakeup_prepare(void)
{
touch_pad_sleep_channel_t slp_config;
touch_pad_fsm_stop();
touch_pad_clear_channel_mask(SOC_TOUCH_SENSOR_BIT_MASK_MAX);
touch_pad_sleep_channel_get_info(&slp_config);
touch_pad_set_channel_mask(BIT(slp_config.touch_num));
touch_pad_fsm_start();
}
esp_err_t esp_sleep_enable_touchpad_wakeup(void)
{
if (s_config.wakeup_triggers & (RTC_EXT0_TRIG_EN)) {
ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0");
return ESP_ERR_INVALID_STATE;
}
s_config.wakeup_triggers |= RTC_TOUCH_TRIG_EN;
return ESP_OK;
}
touch_pad_t esp_sleep_get_touchpad_wakeup_status(void)
{
if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TOUCHPAD) {
return TOUCH_PAD_MAX;
}
touch_pad_t pad_num;
esp_err_t ret = touch_pad_get_wakeup_status(&pad_num); //TODO 723diff commit id:fda9ada1b
assert(ret == ESP_OK && "wakeup reason is RTC_TOUCH_TRIG_EN but SENS_TOUCH_MEAS_EN is zero");
return pad_num;
}
esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level)
{
if (level < 0 || level > 1) {
return ESP_ERR_INVALID_ARG;
}
if (!RTC_GPIO_IS_VALID_GPIO(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_ULP_TRIG_EN)) {
ESP_LOGE(TAG, "Conflicting wake-up triggers: touch / ULP");
return ESP_ERR_INVALID_STATE;
}
s_config.ext0_rtc_gpio_num = rtc_io_number_get(gpio_num);
s_config.ext0_trigger_level = level;
s_config.wakeup_triggers |= RTC_EXT0_TRIG_EN;
return ESP_OK;
}
static void ext0_wakeup_prepare(void)
{
int rtc_gpio_num = s_config.ext0_rtc_gpio_num;
// Set GPIO to be used for wakeup
REG_SET_FIELD(RTC_IO_EXT_WAKEUP0_REG, RTC_IO_EXT_WAKEUP0_SEL, rtc_gpio_num);
// Set level which will trigger wakeup
SET_PERI_REG_BITS(RTC_CNTL_EXT_WAKEUP_CONF_REG, 0x1,
s_config.ext0_trigger_level, RTC_CNTL_EXT_WAKEUP0_LV_S);
// Find GPIO descriptor in the rtc_io_desc table and configure the pad
const rtc_io_desc_t *desc = &rtc_io_desc[rtc_gpio_num];
REG_SET_BIT(desc->reg, desc->mux);
SET_PERI_REG_BITS(desc->reg, 0x3, 0, desc->func);
REG_SET_BIT(desc->reg, desc->ie);
}
esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode)
{
if (mode > ESP_EXT1_WAKEUP_ANY_HIGH) {
return ESP_ERR_INVALID_ARG;
}
// Translate bit map of GPIO numbers into the bit map of RTC IO numbers
uint32_t rtc_gpio_mask = 0;
for (int gpio = 0; mask; ++gpio, mask >>= 1) {
if ((mask & 1) == 0) {
continue;
}
if (!RTC_GPIO_IS_VALID_GPIO(gpio)) {
ESP_LOGE(TAG, "Not an RTC IO: GPIO%d", gpio);
return ESP_ERR_INVALID_ARG;
}
rtc_gpio_mask |= BIT(rtc_io_number_get(gpio));
}
s_config.ext1_rtc_gpio_mask = rtc_gpio_mask;
s_config.ext1_trigger_mode = mode;
s_config.wakeup_triggers |= RTC_EXT1_TRIG_EN;
return ESP_OK;
}
static void ext1_wakeup_prepare(void)
{
// Configure all RTC IOs selected as ext1 wakeup inputs
uint32_t rtc_gpio_mask = s_config.ext1_rtc_gpio_mask;
for (int gpio = 0; gpio < GPIO_PIN_COUNT && rtc_gpio_mask != 0; ++gpio) {
int rtc_pin = rtc_io_number_get(gpio);
if ((rtc_gpio_mask & BIT(rtc_pin)) == 0) {
continue;
}
const rtc_io_desc_t *desc = &rtc_io_desc[rtc_pin];
// Route pad to RTC
REG_SET_BIT(desc->reg, desc->mux);
SET_PERI_REG_BITS(desc->reg, 0x3, 0, desc->func);
// set input enable in sleep mode
REG_SET_BIT(desc->reg, desc->ie);
// Pad configuration depends on RTC_PERIPH state in sleep mode
if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] != ESP_PD_OPTION_ON) {
// RTC_PERIPH will be powered down, so RTC_IO_ registers will
// loose their state. Lock pad configuration.
// Pullups/pulldowns also need to be disabled.
REG_CLR_BIT(desc->reg, desc->pulldown);
REG_CLR_BIT(desc->reg, desc->pullup);
REG_SET_BIT(RTC_CNTL_PAD_HOLD_REG, desc->hold_force);
}
// Keep track of pins which are processed to bail out early
rtc_gpio_mask &= ~BIT(rtc_pin);
}
// Clear state from previous wakeup
REG_SET_BIT(RTC_CNTL_EXT_WAKEUP1_REG, RTC_CNTL_EXT_WAKEUP1_STATUS_CLR);
// Set pins to be used for wakeup
REG_SET_FIELD(RTC_CNTL_EXT_WAKEUP1_REG, RTC_CNTL_EXT_WAKEUP1_SEL, s_config.ext1_rtc_gpio_mask);
// Set logic function (any low, all high)
SET_PERI_REG_BITS(RTC_CNTL_EXT_WAKEUP_CONF_REG, 0x1,
s_config.ext1_trigger_mode, RTC_CNTL_EXT_WAKEUP1_LV_S);
}
uint64_t esp_sleep_get_ext1_wakeup_status(void)
{
if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_EXT1) {
return 0;
}
uint32_t status = REG_GET_FIELD(RTC_CNTL_EXT_WAKEUP1_STATUS_REG, RTC_CNTL_EXT_WAKEUP1_STATUS);
// Translate bit map of RTC IO numbers into the bit map of GPIO numbers
uint64_t gpio_mask = 0;
for (int gpio = 0; gpio < GPIO_PIN_COUNT; ++gpio) {
if (!RTC_GPIO_IS_VALID_GPIO(gpio)) {
continue;
}
int rtc_pin = rtc_io_number_get(gpio);
if ((status & BIT(rtc_pin)) == 0) {
continue;
}
gpio_mask |= 1ULL << gpio;
}
return gpio_mask;
}
esp_err_t esp_sleep_enable_gpio_wakeup(void)
{
if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_ULP_TRIG_EN)) {
ESP_LOGE(TAG, "Conflicting wake-up triggers: touch / ULP");
return ESP_ERR_INVALID_STATE;
}
s_config.wakeup_triggers |= RTC_GPIO_TRIG_EN;
return ESP_OK;
}
esp_err_t esp_sleep_enable_uart_wakeup(int uart_num)
{
if (uart_num == 0) {
s_config.wakeup_triggers |= RTC_UART0_TRIG_EN;
} else if (uart_num == 1) {
s_config.wakeup_triggers |= RTC_UART1_TRIG_EN;
} else {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
esp_err_t esp_sleep_enable_wifi_wakeup(void)
{
s_config.wakeup_triggers |= RTC_MAC_TRIG_EN;
return ESP_OK;
}
esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void)
{
if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET && !s_light_sleep_wakeup) {
return ESP_SLEEP_WAKEUP_UNDEFINED;
}
uint32_t wakeup_cause = REG_GET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_CAUSE);
if (wakeup_cause & RTC_EXT0_TRIG_EN) {
return ESP_SLEEP_WAKEUP_EXT0;
} else if (wakeup_cause & RTC_EXT1_TRIG_EN) {
return ESP_SLEEP_WAKEUP_EXT1;
} else if (wakeup_cause & RTC_TIMER_TRIG_EN) {
return ESP_SLEEP_WAKEUP_TIMER;
} else if (wakeup_cause & RTC_TOUCH_TRIG_EN) {
return ESP_SLEEP_WAKEUP_TOUCHPAD;
} else if (wakeup_cause & RTC_ULP_TRIG_EN) {
return ESP_SLEEP_WAKEUP_ULP;
} else if (wakeup_cause & RTC_GPIO_TRIG_EN) {
return ESP_SLEEP_WAKEUP_GPIO;
} else if (wakeup_cause & (RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN)) {
return ESP_SLEEP_WAKEUP_UART;
} else if (wakeup_cause & RTC_MAC_TRIG_EN) {
return ESP_SLEEP_WAKEUP_WIFI;
} else if (wakeup_cause & RTC_COCPU_TRIG_EN) {
return ESP_SLEEP_WAKEUP_ULP;
} else if (wakeup_cause & RTC_COCPU_TRAP_TRIG_EN) {
return ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG;
} else {
return ESP_SLEEP_WAKEUP_UNDEFINED;
}
}
esp_err_t esp_sleep_pd_config(esp_sleep_pd_domain_t domain,
esp_sleep_pd_option_t option)
{
if (domain >= ESP_PD_DOMAIN_MAX || option > ESP_PD_OPTION_AUTO) {
return ESP_ERR_INVALID_ARG;
}
s_config.pd_options[domain] = option;
return ESP_OK;
}
static uint32_t get_power_down_flags(void)
{
// Where needed, convert AUTO options to ON. Later interpret AUTO as OFF.
// RTC_SLOW_MEM is needed for the ULP, so keep RTC_SLOW_MEM powered up if ULP
// is used and RTC_SLOW_MEM is Auto.
// If there is any data placed into .rtc.data or .rtc.bss segments, and
// RTC_SLOW_MEM is Auto, keep it powered up as well.
// Labels are defined in the linker script, see esp32s3.ld.
extern int _rtc_slow_length;
if ((s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] == ESP_PD_OPTION_AUTO) &&
((size_t) &_rtc_slow_length > 0 ||
(s_config.wakeup_triggers & RTC_ULP_TRIG_EN))) {
s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] = ESP_PD_OPTION_ON;
}
// RTC_FAST_MEM is needed for deep sleep stub.
// If RTC_FAST_MEM is Auto, keep it powered on, so that deep sleep stub
// can run.
// In the new chip revision, deep sleep stub will be optional,
// and this can be changed.
if (s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] == ESP_PD_OPTION_AUTO) {
s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] = ESP_PD_OPTION_ON;
}
// RTC_PERIPH is needed for EXT0 wakeup and GPIO wakeup.
// If RTC_PERIPH is auto, and EXT0/GPIO aren't enabled, power down RTC_PERIPH.
if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] == ESP_PD_OPTION_AUTO) {
if (s_config.wakeup_triggers & (RTC_EXT0_TRIG_EN | RTC_GPIO_TRIG_EN)) {
s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_ON;
} else if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_ULP_TRIG_EN)) {
// In both rev. 0 and rev. 1 of ESP32, forcing power up of RTC_PERIPH
// prevents ULP timer and touch FSMs from working correctly.
s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_OFF;
}
}
if (s_config.pd_options[ESP_PD_DOMAIN_XTAL] == ESP_PD_OPTION_AUTO) {
s_config.pd_options[ESP_PD_DOMAIN_XTAL] = ESP_PD_OPTION_OFF;
}
const char *option_str[] = {"OFF", "ON", "AUTO(OFF)" /* Auto works as OFF */};
ESP_LOGD(TAG, "RTC_PERIPH: %s, RTC_SLOW_MEM: %s, RTC_FAST_MEM: %s",
option_str[s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH]],
option_str[s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM]],
option_str[s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM]]);
// Prepare flags based on the selected options
uint32_t pd_flags = 0;
if (s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] != ESP_PD_OPTION_ON) {
pd_flags |= RTC_SLEEP_PD_RTC_FAST_MEM;
}
if (s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] != ESP_PD_OPTION_ON) {
pd_flags |= RTC_SLEEP_PD_RTC_SLOW_MEM;
}
if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] != ESP_PD_OPTION_ON) {
pd_flags |= RTC_SLEEP_PD_RTC_PERIPH;
}
return pd_flags;
}

View File

@ -0,0 +1,143 @@
// Copyright 2013-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include "sdkconfig.h"
#include "esp_system.h"
#include "esp_private/system_internal.h"
#include "esp_attr.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp32s3/rom/cache.h"
#include "esp_rom_uart.h"
#include "soc/dport_reg.h"
#include "soc/gpio_reg.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/timer_group_reg.h"
#include "soc/cpu.h"
#include "soc/rtc.h"
#include "soc/syscon_reg.h"
#include "hal/wdt_hal.h"
#include "freertos/xtensa_api.h"
/* "inner" restart function for after RTOS, interrupts & anything else on this
* core are already stopped. Stalls other core, resets hardware,
* triggers restart.
*/
void IRAM_ATTR esp_restart_noos(void)
{
// Disable interrupts
xt_ints_off(0xFFFFFFFF);
// Enable RTC watchdog for 1 second
wdt_hal_context_t rtc_wdt_ctx;
wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false);
uint32_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL);
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_SYSTEM);
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE1, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC);
//Enable flash boot mode so that flash booting after restart is protected by the RTC WDT.
wdt_hal_set_flashboot_en(&rtc_wdt_ctx, true);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
// Reset and stall the other CPU.
// CPU must be reset before stalling, in case it was running a s32c1i
// instruction. This would cause memory pool to be locked by arbiter
// to the stalled CPU, preventing current CPU from accessing this pool.
const uint32_t core_id = xPortGetCoreID();
#if !CONFIG_FREERTOS_UNICORE
const uint32_t other_core_id = (core_id == 0) ? 1 : 0;
esp_cpu_reset(other_core_id);
esp_cpu_stall(other_core_id);
#endif
// Disable TG0/TG1 watchdogs
wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0};
wdt_hal_write_protect_disable(&wdt0_context);
wdt_hal_disable(&wdt0_context);
wdt_hal_write_protect_enable(&wdt0_context);
wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1};
wdt_hal_write_protect_disable(&wdt1_context);
wdt_hal_disable(&wdt1_context);
wdt_hal_write_protect_enable(&wdt1_context);
// Flush any data left in UART FIFOs
esp_rom_uart_tx_wait_idle(0);
esp_rom_uart_tx_wait_idle(1);
// Disable cache
Cache_Disable_ICache();
Cache_Disable_DCache();
// 2nd stage bootloader reconfigures SPI flash signals.
// Reset them to the defaults expected by ROM.
WRITE_PERI_REG(GPIO_FUNC0_IN_SEL_CFG_REG, 0x30);
WRITE_PERI_REG(GPIO_FUNC1_IN_SEL_CFG_REG, 0x30);
WRITE_PERI_REG(GPIO_FUNC2_IN_SEL_CFG_REG, 0x30);
WRITE_PERI_REG(GPIO_FUNC3_IN_SEL_CFG_REG, 0x30);
WRITE_PERI_REG(GPIO_FUNC4_IN_SEL_CFG_REG, 0x30);
WRITE_PERI_REG(GPIO_FUNC5_IN_SEL_CFG_REG, 0x30);
// Reset wifi/bluetooth/ethernet/sdio (bb/mac)
SET_PERI_REG_MASK(SYSTEM_CORE_RST_EN_REG,
SYSTEM_BB_RST | SYSTEM_FE_RST | SYSTEM_MAC_RST |
SYSTEM_BT_RST | SYSTEM_BTMAC_RST | SYSTEM_SDIO_RST |
SYSTEM_SDIO_HOST_RST | SYSTEM_EMAC_RST | SYSTEM_MACPWR_RST |
SYSTEM_RW_BTMAC_RST | SYSTEM_RW_BTLP_RST);
REG_WRITE(SYSTEM_CORE_RST_EN_REG, 0);
// Reset timer/spi/uart
SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN0_REG,
SYSTEM_TIMERS_RST | SYSTEM_SPI01_RST | SYSTEM_UART_RST);
REG_WRITE(SYSTEM_PERIP_RST_EN0_REG, 0);
// Set CPU back to XTAL source, no PLL, same as hard reset
#if !CONFIG_IDF_ENV_FPGA
rtc_clk_cpu_freq_set_xtal();
#endif
#if !CONFIG_FREERTOS_UNICORE
// Clear entry point for APP CPU
REG_WRITE(SYSTEM_CORE_1_CONTROL_1_REG, 0);
#endif
// Reset CPUs
if (core_id == 0) {
// Running on PRO CPU: APP CPU is stalled. Can reset both CPUs.
#if !CONFIG_FREERTOS_UNICORE
esp_cpu_reset(1);
#endif
esp_cpu_reset(0);
}
#if !CONFIG_FREERTOS_UNICORE
else {
// Running on APP CPU: need to reset PRO CPU and unstall it,
// then reset APP CPU
esp_cpu_reset(0);
esp_cpu_unstall(0);
esp_cpu_reset(1);
}
#endif
while (true) {
;
}
}
void esp_chip_info(esp_chip_info_t *out_info)
{
memset(out_info, 0, sizeof(*out_info));
out_info->model = CHIP_ESP32S3;
out_info->cores = 2;
out_info->features = CHIP_FEATURE_WIFI_BGN;
}

View File

@ -0,0 +1,320 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/time.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp32s3/clk.h"
#include "esp_clk_internal.h"
#include "esp32s3/rom/ets_sys.h"
#include "esp32s3/rom/rtc.h"
#include "esp_rom_uart.h"
#include "soc/system_reg.h"
#include "soc/dport_access.h"
#include "soc/soc.h"
#include "soc/rtc.h"
#include "hal/wdt_hal.h"
#include "soc/rtc_periph.h"
#include "soc/i2s_reg.h"
#include "driver/periph_ctrl.h"
#include "xtensa/core-macros.h"
#include "bootloader_clock.h"
#include "soc/syscon_reg.h"
static const char *TAG = "clk";
/* Number of cycles to wait from the 32k XTAL oscillator to consider it running.
* Larger values increase startup delay. Smaller values may cause false positive
* detection (i.e. oscillator runs for a few cycles and then stops).
*/
#define SLOW_CLK_CAL_CYCLES CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES
#ifdef CONFIG_ESP32S3_RTC_XTAL_CAL_RETRY
#define RTC_XTAL_CAL_RETRY CONFIG_ESP32S3_RTC_XTAL_CAL_RETRY
#else
#define RTC_XTAL_CAL_RETRY 1
#endif
/* Lower threshold for a reasonably-looking calibration value for a 32k XTAL.
* The ideal value (assuming 32768 Hz frequency) is 1000000/32768*(2**19) = 16*10^6.
*/
#define MIN_32K_XTAL_CAL_VAL 15000000L
/* Indicates that this 32k oscillator gets input from external oscillator, rather
* than a crystal.
*/
#define EXT_OSC_FLAG BIT(3)
/* This is almost the same as rtc_slow_freq_t, except that we define
* an extra enum member for the external 32k oscillator.
* For convenience, lower 2 bits should correspond to rtc_slow_freq_t values.
*/
typedef enum {
SLOW_CLK_RTC = RTC_SLOW_FREQ_RTC, //!< Internal 90 kHz RC oscillator
SLOW_CLK_32K_XTAL = RTC_SLOW_FREQ_32K_XTAL, //!< External 32 kHz XTAL
SLOW_CLK_8MD256 = RTC_SLOW_FREQ_8MD256, //!< Internal 8 MHz RC oscillator, divided by 256
SLOW_CLK_32K_EXT_OSC = RTC_SLOW_FREQ_32K_XTAL | EXT_OSC_FLAG //!< External 32k oscillator connected to 32K_XP pin
} slow_clk_sel_t;
static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
void esp_clk_init(void)
{
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
rtc_init(cfg);
assert(rtc_clk_xtal_freq_get() == RTC_XTAL_FREQ_40M);
rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M);
#ifdef CONFIG_BOOTLOADER_WDT_ENABLE
// WDT uses a SLOW_CLK clock source. After a function select_rtc_slow_clk a frequency of this source can changed.
// If the frequency changes from 90kHz to 32kHz, then the timeout set for the WDT will increase 2.8 times.
// Therefore, for the time of frequency change, set a new lower timeout value (1.6 sec).
// This prevents excessive delay before resetting in case the supply voltage is drawdown.
// (If frequency is changed from 90kHz to 32kHz then WDT timeout will increased to 1.6sec * 90/32 = 4.5 sec).
wdt_hal_context_t rtc_wdt_ctx = {.inst = WDT_RWDT, .rwdt_dev = &RTCCNTL};
uint32_t stage_timeout_ticks = (uint32_t)(1600ULL * rtc_clk_slow_freq_get_hz() / 1000ULL);
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_feed(&rtc_wdt_ctx);
//Bootloader has enabled RTC WDT until now. We're only modifying timeout, so keep the stage and timeout action the same
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
#endif
#if defined(CONFIG_ESP32S3_RTC_CLK_SRC_EXT_CRYS)
select_rtc_slow_clk(SLOW_CLK_32K_XTAL);
#elif defined(CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC)
select_rtc_slow_clk(SLOW_CLK_32K_EXT_OSC);
#elif defined(CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256)
select_rtc_slow_clk(SLOW_CLK_8MD256);
#else
select_rtc_slow_clk(RTC_SLOW_FREQ_RTC);
#endif
#ifdef CONFIG_BOOTLOADER_WDT_ENABLE
// After changing a frequency WDT timeout needs to be set for new frequency.
stage_timeout_ticks = (uint32_t)((uint64_t)CONFIG_BOOTLOADER_WDT_TIME_MS * rtc_clk_slow_freq_get_hz() / 1000ULL);
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_feed(&rtc_wdt_ctx);
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
#endif
rtc_cpu_freq_config_t old_config, new_config;
rtc_clk_cpu_freq_get_config(&old_config);
const uint32_t old_freq_mhz = old_config.freq_mhz;
const uint32_t new_freq_mhz = CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ;
bool res = rtc_clk_cpu_freq_mhz_to_config(new_freq_mhz, &new_config);
assert(res);
// Wait for UART TX to finish, otherwise some UART output will be lost
// when switching APB frequency
if (CONFIG_ESP_CONSOLE_UART_NUM >= 0) {
esp_rom_uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
}
rtc_clk_cpu_freq_set_config(&new_config);
// Re calculate the ccount to make time calculation correct.
XTHAL_SET_CCOUNT( (uint64_t)XTHAL_GET_CCOUNT() * new_freq_mhz / old_freq_mhz );
}
static void select_rtc_slow_clk(slow_clk_sel_t slow_clk)
{
rtc_slow_freq_t rtc_slow_freq = slow_clk & RTC_CNTL_ANA_CLK_RTC_SEL_V;
uint32_t cal_val = 0;
/* number of times to repeat 32k XTAL calibration
* before giving up and switching to the internal RC
*/
int retry_32k_xtal = RTC_XTAL_CAL_RETRY;
do {
if (rtc_slow_freq == RTC_SLOW_FREQ_32K_XTAL) {
/* 32k XTAL oscillator needs to be enabled and running before it can
* be used. Hardware doesn't have a direct way of checking if the
* oscillator is running. Here we use rtc_clk_cal function to count
* the number of main XTAL cycles in the given number of 32k XTAL
* oscillator cycles. If the 32k XTAL has not started up, calibration
* will time out, returning 0.
*/
ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up");
if (slow_clk == SLOW_CLK_32K_XTAL) {
rtc_clk_32k_enable(true);
} else if (slow_clk == SLOW_CLK_32K_EXT_OSC) {
rtc_clk_32k_enable_external();
}
// When SLOW_CLK_CAL_CYCLES is set to 0, clock calibration will not be performed at startup.
if (SLOW_CLK_CAL_CYCLES > 0) {
cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, SLOW_CLK_CAL_CYCLES);
if (cal_val == 0 || cal_val < MIN_32K_XTAL_CAL_VAL) {
if (retry_32k_xtal-- > 0) {
continue;
}
ESP_EARLY_LOGW(TAG, "32 kHz XTAL not found, switching to internal 90 kHz oscillator");
rtc_slow_freq = RTC_SLOW_FREQ_RTC;
}
}
} else if (rtc_slow_freq == RTC_SLOW_FREQ_8MD256) {
rtc_clk_8m_enable(true, true);
}
rtc_clk_slow_freq_set(rtc_slow_freq);
if (SLOW_CLK_CAL_CYCLES > 0) {
/* TODO: 32k XTAL oscillator has some frequency drift at startup.
* Improve calibration routine to wait until the frequency is stable.
*/
cal_val = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES);
} else {
const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL;
cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz());
}
} while (cal_val == 0);
ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", cal_val);
esp_clk_slowclk_cal_set(cal_val);
}
void rtc_clk_select_rtc_slow_clk(void)
{
select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL);
}
/* This function is not exposed as an API at this point.
* All peripheral clocks are default enabled after chip is powered on.
* This function disables some peripheral clocks when cpu starts.
* These peripheral clocks are enabled when the peripherals are initialized
* and disabled when they are de-initialized.
*/
void esp_perip_clk_init(void)
{
uint32_t common_perip_clk, hwcrypto_perip_clk, wifi_bt_sdio_clk = 0;
uint32_t common_perip_clk1 = 0;
#if CONFIG_FREERTOS_UNICORE
RESET_REASON rst_reas[1];
#else
RESET_REASON rst_reas[2];
#endif
rst_reas[0] = rtc_get_reset_reason(0);
#if !CONFIG_FREERTOS_UNICORE
rst_reas[1] = rtc_get_reset_reason(1);
#endif
/* For reason that only reset CPU, do not disable the clocks
* that have been enabled before reset.
*/
if ((rst_reas[0] >= TG0WDT_CPU_RESET && rst_reas[0] <= TG0WDT_CPU_RESET && rst_reas[0] != RTCWDT_BROWN_OUT_RESET)
#if !CONFIG_FREERTOS_UNICORE
|| (rst_reas[1] >= TG0WDT_CPU_RESET && rst_reas[1] <= RTCWDT_CPU_RESET)
#endif
) {
common_perip_clk = ~READ_PERI_REG(SYSTEM_PERIP_CLK_EN0_REG);
hwcrypto_perip_clk = ~READ_PERI_REG(SYSTEM_PERIP_CLK_EN1_REG);
wifi_bt_sdio_clk = ~READ_PERI_REG(SYSTEM_WIFI_CLK_EN_REG);
} else {
common_perip_clk = SYSTEM_WDG_CLK_EN |
SYSTEM_I2S0_CLK_EN |
#if CONFIG_CONSOLE_UART_NUM != 0
SYSTEM_UART_CLK_EN |
#endif
#if CONFIG_CONSOLE_UART_NUM != 1
SYSTEM_UART1_CLK_EN |
#endif
#if CONFIG_CONSOLE_UART_NUM != 2
SYSTEM_UART2_CLK_EN |
#endif
SYSTEM_USB_CLK_EN |
SYSTEM_SPI2_CLK_EN |
SYSTEM_I2C_EXT0_CLK_EN |
SYSTEM_UHCI0_CLK_EN |
SYSTEM_RMT_CLK_EN |
SYSTEM_PCNT_CLK_EN |
SYSTEM_LEDC_CLK_EN |
SYSTEM_TIMERGROUP1_CLK_EN |
SYSTEM_SPI3_CLK_EN |
SYSTEM_SPI4_CLK_EN |
SYSTEM_PWM0_CLK_EN |
SYSTEM_CAN_CLK_EN |
SYSTEM_PWM1_CLK_EN |
SYSTEM_I2S1_CLK_EN |
SYSTEM_SPI2_DMA_CLK_EN |
SYSTEM_SPI3_DMA_CLK_EN |
SYSTEM_PWM2_CLK_EN |
SYSTEM_PWM3_CLK_EN;
common_perip_clk1 = 0;
hwcrypto_perip_clk = SYSTEM_CRYPTO_AES_CLK_EN |
SYSTEM_CRYPTO_SHA_CLK_EN |
SYSTEM_CRYPTO_RSA_CLK_EN;
wifi_bt_sdio_clk = SYSTEM_WIFI_CLK_WIFI_EN |
SYSTEM_WIFI_CLK_BT_EN_M |
SYSTEM_WIFI_CLK_UNUSED_BIT5 |
SYSTEM_WIFI_CLK_UNUSED_BIT12 |
SYSTEM_WIFI_CLK_SDIO_HOST_EN;
}
//Reset the communication peripherals like I2C, SPI, UART, I2S and bring them to known state.
common_perip_clk |= SYSTEM_I2S0_CLK_EN |
#if CONFIG_CONSOLE_UART_NUM != 0
SYSTEM_UART_CLK_EN |
#endif
#if CONFIG_CONSOLE_UART_NUM != 1
SYSTEM_UART1_CLK_EN |
#endif
#if CONFIG_CONSOLE_UART_NUM != 2
SYSTEM_UART2_CLK_EN |
#endif
SYSTEM_USB_CLK_EN |
SYSTEM_SPI2_CLK_EN |
SYSTEM_I2C_EXT0_CLK_EN |
SYSTEM_UHCI0_CLK_EN |
SYSTEM_RMT_CLK_EN |
SYSTEM_UHCI1_CLK_EN |
SYSTEM_SPI3_CLK_EN |
SYSTEM_SPI4_CLK_EN |
SYSTEM_I2C_EXT1_CLK_EN |
SYSTEM_I2S1_CLK_EN |
SYSTEM_SPI2_DMA_CLK_EN |
SYSTEM_SPI3_DMA_CLK_EN;
common_perip_clk1 = 0;
/* Change I2S clock to audio PLL first. Because if I2S uses 160MHz clock,
* the current is not reduced when disable I2S clock.
*/
REG_SET_FIELD(I2S_CLKM_CONF_REG(0), I2S_CLK_SEL, I2S_CLK_AUDIO_PLL);
REG_SET_FIELD(I2S_CLKM_CONF_REG(1), I2S_CLK_SEL, I2S_CLK_AUDIO_PLL);
/* Disable some peripheral clocks. */
CLEAR_PERI_REG_MASK(SYSTEM_PERIP_CLK_EN0_REG, common_perip_clk);
SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN0_REG, common_perip_clk);
CLEAR_PERI_REG_MASK(SYSTEM_PERIP_CLK_EN1_REG, common_perip_clk1);
SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN1_REG, common_perip_clk1);
/* Disable hardware crypto clocks. */
CLEAR_PERI_REG_MASK(SYSTEM_PERIP_CLK_EN1_REG, hwcrypto_perip_clk);
SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN1_REG, hwcrypto_perip_clk);
/* Disable WiFi/BT/SDIO clocks. */
CLEAR_PERI_REG_MASK(SYSTEM_WIFI_CLK_EN_REG, wifi_bt_sdio_clk);
/* Enable RNG clock. */
periph_module_enable(PERIPH_RNG_MODULE);
}

View File

@ -0,0 +1,120 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp_system.h"
#include "esp32s3/rom/rtc.h"
#include "esp_private/system_internal.h"
#include "soc/rtc_periph.h"
static void esp_reset_reason_clear_hint(void);
static esp_reset_reason_t s_reset_reason;
static esp_reset_reason_t get_reset_reason(RESET_REASON rtc_reset_reason, esp_reset_reason_t reset_reason_hint)
{
switch (rtc_reset_reason) {
case POWERON_RESET:
return ESP_RST_POWERON;
case RTC_SW_CPU_RESET:
case RTC_SW_SYS_RESET:
if (reset_reason_hint == ESP_RST_PANIC ||
reset_reason_hint == ESP_RST_BROWNOUT ||
reset_reason_hint == ESP_RST_TASK_WDT ||
reset_reason_hint == ESP_RST_INT_WDT) {
return reset_reason_hint;
}
return ESP_RST_SW;
case DEEPSLEEP_RESET:
return ESP_RST_DEEPSLEEP;
case TG0WDT_SYS_RESET:
return ESP_RST_TASK_WDT;
case TG1WDT_SYS_RESET:
return ESP_RST_INT_WDT;
case RTCWDT_SYS_RESET:
case RTCWDT_RTC_RESET:
case SUPER_WDT_RESET:
case RTCWDT_CPU_RESET: /* unused */
case TG0WDT_CPU_RESET: /* unused */
case TG1WDT_CPU_RESET: /* unused */
return ESP_RST_WDT;
case RTCWDT_BROWN_OUT_RESET:
return ESP_RST_BROWNOUT;
case INTRUSION_RESET: /* unused */
default:
return ESP_RST_UNKNOWN;
}
}
static void __attribute__((constructor)) esp_reset_reason_init(void)
{
esp_reset_reason_t hint = esp_reset_reason_get_hint();
s_reset_reason = get_reset_reason(rtc_get_reset_reason(PRO_CPU_NUM),
hint);
if (hint != ESP_RST_UNKNOWN) {
esp_reset_reason_clear_hint();
}
}
esp_reset_reason_t esp_reset_reason(void)
{
return s_reset_reason;
}
/* Reset reason hint is stored in RTC_RESET_CAUSE_REG, a.k.a. RTC_CNTL_STORE6_REG,
* a.k.a. RTC_ENTRY_ADDR_REG. It is safe to use this register both for the
* deep sleep wake stub entry address and for reset reason hint, since wake stub
* is only used for deep sleep reset, and in this case the reason provided by
* rtc_get_reset_reason is unambiguous.
*
* Same layout is used as for RTC_APB_FREQ_REG (a.k.a. RTC_CNTL_STORE5_REG):
* the value is replicated in low and high half-words. In addition to that,
* MSB is set to 1, which doesn't happen when RTC_CNTL_STORE6_REG contains
* deep sleep wake stub address.
*/
#define RST_REASON_BIT 0x80000000
#define RST_REASON_MASK 0x7FFF
#define RST_REASON_SHIFT 16
/* in IRAM, can be called from panic handler */
void IRAM_ATTR esp_reset_reason_set_hint(esp_reset_reason_t hint)
{
assert((hint & (~RST_REASON_MASK)) == 0);
uint32_t val = hint | (hint << RST_REASON_SHIFT) | RST_REASON_BIT;
REG_WRITE(RTC_RESET_CAUSE_REG, val);
}
/* in IRAM, can be called from panic handler */
esp_reset_reason_t IRAM_ATTR esp_reset_reason_get_hint(void)
{
uint32_t reset_reason_hint = REG_READ(RTC_RESET_CAUSE_REG);
uint32_t high = (reset_reason_hint >> RST_REASON_SHIFT) & RST_REASON_MASK;
uint32_t low = reset_reason_hint & RST_REASON_MASK;
if ((reset_reason_hint & RST_REASON_BIT) == 0 || high != low) {
return ESP_RST_UNKNOWN;
}
return (esp_reset_reason_t) low;
}
static void esp_reset_reason_clear_hint(void)
{
REG_WRITE(RTC_RESET_CAUSE_REG, 0);
}

View File

@ -547,7 +547,9 @@ void start_app_other_cores(void)
#endif
esp_crosscore_int_init();
#if CONFIG_IDF_TARGET_ESP32
esp_dport_access_int_init();
#endif
ESP_EARLY_LOGI(TAG, "Starting scheduler on APP CPU.");
xPortStartScheduler();
@ -570,7 +572,9 @@ void start_app(void)
esp_crosscore_int_init();
#ifndef CONFIG_FREERTOS_UNICORE
#if CONFIG_IDF_TARGET_ESP32
esp_dport_access_int_init();
#endif
#endif
portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, "main",

View File

@ -47,11 +47,7 @@
#endif /* MBEDTLS_PLATFORM_C */
#endif /* MBEDTLS_SELF_TEST */
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/sha.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/sha.h"
#endif
/* Implementation that should never be optimized out by the compiler */
static void mbedtls_zeroize( void *v, size_t n ) {

View File

@ -48,11 +48,7 @@
#endif /* MBEDTLS_PLATFORM_C */
#endif /* MBEDTLS_SELF_TEST */
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/sha.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/sha.h"
#endif
/* Implementation that should never be optimized out by the compiler */
static void mbedtls_zeroize( void *v, size_t n ) {

View File

@ -54,11 +54,7 @@
#endif /* MBEDTLS_PLATFORM_C */
#endif /* MBEDTLS_SELF_TEST */
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/sha.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/sha.h"
#endif
inline static esp_sha_type sha_type(const mbedtls_sha512_context *ctx)
{

View File

@ -46,7 +46,7 @@ _Static_assert(sizeof(systimer_counter_value_t) == 8, "systimer_counter_value_t
typedef enum {
SYSTIMER_COUNTER_0, /*!< systimer counter 0 */
#if SOC_SYSTIMER_COUNTER_NUM > 1
SYSTIEMR_COUNTER_1, /*!< systimer counter 1 */
SYSTIMER_COUNTER_1, /*!< systimer counter 1 */
#endif
} systimer_counter_id_t;

View File

@ -0,0 +1,111 @@
// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BOOTLOADER_BUILD
#include <stdint.h>
#include <stdlib.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "soc/soc.h"
#include "soc/soc_memory_layout.h"
#include "esp_heap_caps.h"
/**
* @brief Memory type descriptors. These describe the capabilities of a type of memory in the SoC.
* Each type of memory map consists of one or more regions in the address space.
* Each type contains an array of prioritized capabilities.
* Types with later entries are only taken if earlier ones can't fulfill the memory request.
*
* - For a normal malloc (MALLOC_CAP_DEFAULT), give away the DRAM-only memory first, then pass off any dual-use IRAM regions, finally eat into the application memory.
* - For a malloc where 32-bit-aligned-only access is okay, first allocate IRAM, then DRAM, finally application IRAM.
* - Application mallocs (PIDx) will allocate IRAM first, if possible, then DRAM.
* - Most other malloc caps only fit in one region anyway.
*
*/
const soc_memory_type_desc_t soc_memory_types[] = {
// Type 0: DRAM
{ "DRAM", { MALLOC_CAP_8BIT | MALLOC_CAP_DEFAULT, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_32BIT, 0 }, false, false},
// Type 1: DRAM used for startup stacks
{ "STACK/DRAM", { MALLOC_CAP_8BIT | MALLOC_CAP_DEFAULT, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_32BIT, 0 }, false, true},
// Type 2: DRAM which has an alias on the I-port
{ "D/IRAM", { 0, MALLOC_CAP_DMA | MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DEFAULT, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC }, true, false},
// Type 3: IRAM
{ "IRAM", { MALLOC_CAP_EXEC | MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL, 0, 0 }, false, false},
// Type 4: SPI SRAM data
{ "SPIRAM", { MALLOC_CAP_SPIRAM | MALLOC_CAP_DEFAULT, 0, MALLOC_CAP_8BIT | MALLOC_CAP_32BIT}, false, false},
};
const size_t soc_memory_type_count = sizeof(soc_memory_types) / sizeof(soc_memory_type_desc_t);
/**
* @brief Region descriptors. These describe all regions of memory available, and map them to a type in the above type.
*
* @note Because of requirements in the coalescing code which merges adjacent regions,
* this list should always be sorted from low to high by start address.
*
*/
const soc_memory_region_t soc_memory_regions[] = {
#ifdef CONFIG_SPIRAM
{ SOC_EXTRAM_DATA_LOW, SOC_EXTRAM_DATA_HIGH - SOC_EXTRAM_DATA_LOW, 4, 0}, //SPI SRAM, if available
#endif
#if CONFIG_ESP32S3_INSTRUCTION_CACHE_16KB
{ 0x40374000, 0x4000, 3, 0}, //Level 1, IRAM
#endif
{ 0x3FC88000, 0x8000, 2, 0x40378000}, //Level 2, IDRAM, can be used as trace memroy
{ 0x3FC90000, 0x10000, 2, 0x40380000}, //Level 3, IDRAM, can be used as trace memroy
{ 0x3FCA0000, 0x10000, 2, 0x40390000}, //Level 4, IDRAM, can be used as trace memroy
{ 0x3FCB0000, 0x10000, 2, 0x403A0000}, //Level 5, IDRAM, can be used as trace memroy
{ 0x3FCC0000, 0x10000, 2, 0x403B0000}, //Level 6, IDRAM, can be used as trace memroy
{ 0x3FCD0000, 0x10000, 2, 0x403C0000}, //Level 7, IDRAM, can be used as trace memroy
{ 0x3FCE0000, 0x10000, 1, 0}, //Level 8, IDRAM, can be used as trace memroy, contains stacks used by startup flow, recycled by heap allocator in app_main task
#if CONFIG_ESP32S3_DATA_CACHE_32KB
{ 0x3FCF0000, 0x8000, 0, 0}, //Level 9, DRAM
#endif
};
const size_t soc_memory_region_count = sizeof(soc_memory_regions) / sizeof(soc_memory_region_t);
extern int _dram0_rtos_reserved_start; // defined in esp32s3.rom.ld
extern int _data_start, _heap_start, _iram_start, _iram_end; // defined in esp32s3.project.ld.in
/**
* Reserved memory regions.
* These are removed from the soc_memory_regions array when heaps are created.
*
*/
//ROM data region
SOC_RESERVE_MEMORY_REGION((intptr_t)&_dram0_rtos_reserved_start, SOC_DIRAM_DRAM_HIGH, rom_data_region);
// Static data region. DRAM used by data+bss and possibly rodata
SOC_RESERVE_MEMORY_REGION((intptr_t)&_data_start, (intptr_t)&_heap_start, dram_data);
// ESP32S3 has a big D/IRAM region, the part used by code is reserved
// The address of the D/I bus are in the same order, directly shift IRAM address to get reserved DRAM address
#define I_D_OFFSET (SOC_DIRAM_IRAM_LOW - SOC_DIRAM_DRAM_LOW)
#if CONFIG_ESP32S3_INSTRUCTION_CACHE_16KB
SOC_RESERVE_MEMORY_REGION((intptr_t)&_iram_start, (intptr_t)&_iram_start + 0x4000, iram_code_1);
SOC_RESERVE_MEMORY_REGION((intptr_t)&_iram_start + 0x4000 - I_D_OFFSET, (intptr_t)&_iram_end - I_D_OFFSET, iram_code_2);
#else
SOC_RESERVE_MEMORY_REGION((intptr_t)&_iram_start - I_D_OFFSET, (intptr_t)&_iram_end - I_D_OFFSET, iram_code);
#endif
#ifdef CONFIG_SPIRAM
SOC_RESERVE_MEMORY_REGION( SOC_EXTRAM_DATA_LOW, SOC_EXTRAM_DATA_HIGH, extram_data_region); //SPI RAM gets added later if needed, in spiram.c; reserve it for now
#endif
#if CONFIG_ESP32S3_TRACEMEM_RESERVE_DRAM > 0
SOC_RESERVE_MEMORY_REGION(0x3fffc000 - CONFIG_ESP32S3_TRACEMEM_RESERVE_DRAM, 0x3fffc000, trace_mem);
#endif
#endif // BOOTLOADER_BUILD