mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-10 04:43:33 +00:00
Merge branch 'master' into feature/btdm_bluedroid
1.update esptool submodule 2.new esp32 lib and new phy lib 3.new bt lib 4.soc.h add comment
This commit is contained in:
@@ -63,14 +63,28 @@ config MEMMAP_TRACEMEM
|
||||
of memory that can't be used for general purposes anymore. Disable this if you do not know
|
||||
what this is.
|
||||
|
||||
config MEMMAP_TRACEMEM_TWOBANKS
|
||||
bool "Reserve memory for tracing both pro as well as app cpu execution"
|
||||
default "n"
|
||||
depends on MEMMAP_TRACEMEM && MEMMAP_SMP
|
||||
help
|
||||
The ESP32 contains a feature which allows you to trace the execution path the processor
|
||||
has taken through the program. This is stored in a chunk of 32K (16K for single-processor)
|
||||
of memory that can't be used for general purposes anymore. Disable this if you do not know
|
||||
what this is.
|
||||
|
||||
|
||||
# Memory to reverse for trace, used in linker script
|
||||
config TRACEMEM_RESERVE_DRAM
|
||||
hex
|
||||
default 0x8000 if MEMMAP_TRACEMEM
|
||||
default 0x8000 if MEMMAP_TRACEMEM && MEMMAP_TRACEMEM_TWOBANKS
|
||||
default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS
|
||||
default 0x0
|
||||
|
||||
# Not implemented and/or needs new silicon rev to work
|
||||
config MEMMAP_SPISRAM
|
||||
bool "Use external SPI SRAM chip as main memory"
|
||||
depends on ESP32_NEEDS_NEW_SILICON_REV
|
||||
default "n"
|
||||
help
|
||||
The ESP32 can control an external SPI SRAM chip, adding the memory it contains to the
|
||||
@@ -140,4 +154,171 @@ config ULP_COPROC_RESERVE_MEM
|
||||
default 0
|
||||
depends on !ULP_COPROC_ENABLED
|
||||
|
||||
|
||||
choice ESP32_PANIC
|
||||
prompt "Panic handler behaviour"
|
||||
default ESP32_PANIC_PRINT_REBOOT
|
||||
help
|
||||
If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is
|
||||
invoked. Configure the panic handlers action here.
|
||||
|
||||
config ESP32_PANIC_PRINT_HALT
|
||||
bool "Print registers and halt"
|
||||
help
|
||||
Outputs the relevant registers over the serial port and halt the
|
||||
processor. Needs a manual reset to restart.
|
||||
|
||||
config ESP32_PANIC_PRINT_REBOOT
|
||||
bool "Print registers and reboot"
|
||||
help
|
||||
Outputs the relevant registers over the serial port and immediately
|
||||
reset the processor.
|
||||
|
||||
config ESP32_PANIC_SILENT_REBOOT
|
||||
bool "Silent reboot"
|
||||
help
|
||||
Just resets the processor without outputting anything
|
||||
|
||||
config ESP32_PANIC_GDBSTUB
|
||||
bool "Invoke GDBStub"
|
||||
help
|
||||
Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem
|
||||
of the crash.
|
||||
endchoice
|
||||
|
||||
config ESP32_DEBUG_OCDAWARE
|
||||
bool "Make exception and panic handlers JTAG/OCD aware"
|
||||
default y
|
||||
help
|
||||
The FreeRTOS panic and unhandled exception handers can detect a JTAG OCD debugger and
|
||||
instead of panicking, have the debugger stop on the offending instruction.
|
||||
|
||||
|
||||
config INT_WDT
|
||||
bool "Interrupt watchdog"
|
||||
default y
|
||||
help
|
||||
This watchdog timer can detect if the FreeRTOS tick interrupt has not been called for a certain time,
|
||||
either because a task turned off interrupts and did not turn them on for a long time, or because an
|
||||
interrupt handler did not return. It will try to invoke the panic handler first and failing that
|
||||
reset the SoC.
|
||||
|
||||
config INT_WDT_TIMEOUT_MS
|
||||
int "Interrupt watchdog timeout (ms)"
|
||||
depends on INT_WDT
|
||||
default 300
|
||||
range 10 10000
|
||||
help
|
||||
The timeout of the watchdog, in miliseconds. Make this higher than the FreeRTOS tick rate.
|
||||
|
||||
config INT_WDT_CHECK_CPU1
|
||||
bool "Also watch CPU1 tick interrupt"
|
||||
depends on INT_WDT && !FREERTOS_UNICORE
|
||||
default y
|
||||
help
|
||||
Also detect if interrupts on CPU 1 are disabled for too long.
|
||||
|
||||
config TASK_WDT
|
||||
bool "Task watchdog"
|
||||
default y
|
||||
help
|
||||
This watchdog timer can be used to make sure individual tasks are still running.
|
||||
|
||||
config TASK_WDT_PANIC
|
||||
bool "Invoke panic handler when Task Watchdog is triggered"
|
||||
depends on TASK_WDT
|
||||
default n
|
||||
help
|
||||
Normally, the Task Watchdog will only print out a warning if it detects it has not
|
||||
been fed. If this is enabled, it will invoke the panic handler instead, which
|
||||
can then halt or reboot the chip.
|
||||
|
||||
config TASK_WDT_TIMEOUT_S
|
||||
int "Task watchdog timeout (seconds)"
|
||||
depends on TASK_WDT
|
||||
range 1 60
|
||||
default 5
|
||||
help
|
||||
Timeout for the task WDT, in seconds.
|
||||
|
||||
config TASK_WDT_CHECK_IDLE_TASK
|
||||
bool "Task watchdog watches CPU0 idle task"
|
||||
depends on TASK_WDT
|
||||
default y
|
||||
help
|
||||
With this turned on, the task WDT can detect if the idle task is not called within the task
|
||||
watchdog timeout period. The idle task not being called usually is a symptom of another
|
||||
task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the
|
||||
idle task getting some runtime every now and then. Take Care: With this disabled, this
|
||||
watchdog will trigger if no tasks register themselves within the timeout value.
|
||||
|
||||
config TASK_WDT_CHECK_IDLE_TASK_CPU1
|
||||
bool "Task watchdog also watches CPU1 idle task"
|
||||
depends on TASK_WDT_CHECK_IDLE_TASK && !FREERTOS_UNICORE
|
||||
default y
|
||||
help
|
||||
Also check the idle task that runs on CPU1.
|
||||
|
||||
#The brownout detector code is disabled (by making it depend on a nonexisting symbol) because the current revision of ESP32
|
||||
#silicon has a bug in the brown-out detector, rendering it unusable for resetting the CPU.
|
||||
config BROWNOUT_DET
|
||||
bool "Hardware brownout detect & reset"
|
||||
default y
|
||||
depends on NEEDS_ESP32_NEW_SILICON_REV
|
||||
help
|
||||
The ESP32 has a built-in brownout detector which can detect if the voltage is lower than
|
||||
a specific value. If this happens, it will reset the chip in order to prevent unintended
|
||||
behaviour.
|
||||
|
||||
choice BROWNOUT_DET_LVL_SEL
|
||||
prompt "Brownout voltage level"
|
||||
depends on BROWNOUT_DET
|
||||
default BROWNOUT_DET_LVL_SEL_25
|
||||
help
|
||||
The brownout detector will reset the chip when the supply voltage is below this level.
|
||||
|
||||
#The voltage levels here are estimates, more work needs to be done to figure out the exact voltages
|
||||
#of the brownout threshold levels.
|
||||
config BROWNOUT_DET_LVL_SEL_0
|
||||
bool "2.1V"
|
||||
config BROWNOUT_DET_LVL_SEL_1
|
||||
bool "2.2V"
|
||||
config BROWNOUT_DET_LVL_SEL_2
|
||||
bool "2.3V"
|
||||
config BROWNOUT_DET_LVL_SEL_3
|
||||
bool "2.4V"
|
||||
config BROWNOUT_DET_LVL_SEL_4
|
||||
bool "2.5V"
|
||||
config BROWNOUT_DET_LVL_SEL_5
|
||||
bool "2.6V"
|
||||
config BROWNOUT_DET_LVL_SEL_6
|
||||
bool "2.7V"
|
||||
config BROWNOUT_DET_LVL_SEL_7
|
||||
bool "2.8V"
|
||||
endchoice
|
||||
|
||||
config BROWNOUT_DET_LVL
|
||||
int
|
||||
default 0 if BROWNOUT_DET_LVL_SEL_0
|
||||
default 1 if BROWNOUT_DET_LVL_SEL_1
|
||||
default 2 if BROWNOUT_DET_LVL_SEL_2
|
||||
default 3 if BROWNOUT_DET_LVL_SEL_3
|
||||
default 4 if BROWNOUT_DET_LVL_SEL_4
|
||||
default 5 if BROWNOUT_DET_LVL_SEL_5
|
||||
default 6 if BROWNOUT_DET_LVL_SEL_6
|
||||
default 7 if BROWNOUT_DET_LVL_SEL_7
|
||||
|
||||
|
||||
config BROWNOUT_DET_RESETDELAY
|
||||
int "Brownout reset delay (in uS)"
|
||||
depends on BROWNOUT_DET
|
||||
range 0 6820
|
||||
default 1000
|
||||
help
|
||||
The brownout detector can reset the chip after a certain delay, in order to make sure e.g. a voltage dip has entirely passed
|
||||
before trying to restart the chip. You can set the delay here.
|
||||
|
||||
|
||||
|
||||
|
||||
endmenu
|
||||
|
39
components/esp32/brownout.c
Normal file
39
components/esp32/brownout.c
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2015-2016 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 "sdkconfig.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
|
||||
|
||||
#if CONFIG_BROWNOUT_DET
|
||||
/*
|
||||
This file is included in esp-idf, but the menuconfig option for this is disabled because a silicon bug
|
||||
prohibits the brownout detector from functioning correctly on the ESP32.
|
||||
*/
|
||||
|
||||
void esp_brownout_init() {
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG,
|
||||
RTC_CNTL_BROWN_OUT_ENA | (CONFIG_BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S) |
|
||||
RTC_CNTL_BROWN_OUT_RST_ENA | (((CONFIG_BROWNOUT_DET_RESETDELAY*150)/1000) << RTC_CNTL_BROWN_OUT_RST_WAIT_S) |
|
||||
RTC_CNTL_BROWN_OUT_PD_RF_ENA|RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -6,7 +6,7 @@
|
||||
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
|
||||
# please read the esp-idf build system document if you need to do this.
|
||||
#
|
||||
-include $(PROJECT_PATH)/build/include/config/auto.conf
|
||||
-include include/config/auto.conf
|
||||
|
||||
COMPONENT_SRCDIRS := . hwcrypto
|
||||
|
||||
@@ -44,6 +44,8 @@ $(COMPONENT_LIBRARY): $(ALL_LIB_FILES)
|
||||
# saves us from having to add the target to a Makefile.projbuild
|
||||
$(COMPONENT_LIBRARY): esp32_out.ld
|
||||
|
||||
# .. is BUILD_DIR_BASE here, as component makefiles
|
||||
# are evaluated with CWD=component build dir
|
||||
esp32_out.ld: $(COMPONENT_PATH)/ld/esp32.ld ../include/sdkconfig.h
|
||||
$(CC) -I ../include -C -P -x c -E $< -o $@
|
||||
|
||||
|
@@ -41,7 +41,14 @@
|
||||
#include "esp_event.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_ipc.h"
|
||||
#include "esp_crosscore_int.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs_dev.h"
|
||||
#include "esp_newlib.h"
|
||||
#include "esp_brownout.h"
|
||||
#include "esp_int_wdt.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "trax.h"
|
||||
|
||||
void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default")));
|
||||
void start_cpu0_default(void) IRAM_ATTR;
|
||||
@@ -54,7 +61,6 @@ static bool app_cpu_started = false;
|
||||
|
||||
static void do_global_ctors(void);
|
||||
static void main_task(void* args);
|
||||
extern void ets_setup_syscalls(void);
|
||||
extern void app_main(void);
|
||||
|
||||
extern int _bss_start;
|
||||
@@ -97,6 +103,10 @@ void IRAM_ATTR call_start_cpu0()
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
ESP_EARLY_LOGI(TAG, "Starting app cpu, entry point is %p", call_start_cpu1);
|
||||
|
||||
//Un-stall the app cpu; the panic handler may have stalled it.
|
||||
CLEAR_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, RTC_CNTL_SW_STALL_APPCPU_C1_M);
|
||||
CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_APPCPU_C0_M);
|
||||
//Enable clock gating and reset the app cpu.
|
||||
SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN);
|
||||
CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_C_REG, DPORT_APPCPU_RUNSTALL);
|
||||
SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_A_REG, DPORT_APPCPU_RESETTING);
|
||||
@@ -131,12 +141,40 @@ void IRAM_ATTR call_start_cpu1()
|
||||
|
||||
void start_cpu0_default(void)
|
||||
{
|
||||
//Enable trace memory and immediately start trace.
|
||||
#if CONFIG_MEMMAP_TRACEMEM
|
||||
#if CONFIG_MEMMAP_TRACEMEM_TWOBANKS
|
||||
trax_enable(TRAX_ENA_PRO_APP);
|
||||
#else
|
||||
trax_enable(TRAX_ENA_PRO);
|
||||
#endif
|
||||
trax_start_trace(TRAX_DOWNCOUNT_WORDS);
|
||||
#endif
|
||||
esp_set_cpu_freq(); // set CPU frequency configured in menuconfig
|
||||
uart_div_modify(0, (APB_CLK_FREQ << 4) / 115200);
|
||||
ets_setup_syscalls();
|
||||
#if CONFIG_BROWNOUT_DET
|
||||
esp_brownout_init();
|
||||
#endif
|
||||
#if CONFIG_INT_WDT
|
||||
esp_int_wdt_init();
|
||||
#endif
|
||||
#if CONFIG_TASK_WDT
|
||||
esp_task_wdt_init();
|
||||
#endif
|
||||
esp_setup_syscalls();
|
||||
esp_vfs_dev_uart_register();
|
||||
esp_reent_init(_GLOBAL_REENT);
|
||||
const char* default_uart_dev = "/dev/uart/0";
|
||||
_GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w");
|
||||
_GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w");
|
||||
_GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r");
|
||||
do_global_ctors();
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
esp_crosscore_int_init();
|
||||
#endif
|
||||
esp_ipc_init();
|
||||
spi_flash_init();
|
||||
|
||||
xTaskCreatePinnedToCore(&main_task, "main",
|
||||
ESP_TASK_MAIN_STACK, NULL,
|
||||
ESP_TASK_MAIN_PRIO, NULL, 0);
|
||||
@@ -147,10 +185,14 @@ void start_cpu0_default(void)
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
void start_cpu1_default(void)
|
||||
{
|
||||
#if CONFIG_MEMMAP_TRACEMEM_TWOBANKS
|
||||
trax_start_trace(TRAX_DOWNCOUNT_WORDS);
|
||||
#endif
|
||||
// Wait for FreeRTOS initialization to finish on PRO CPU
|
||||
while (port_xSchedulerRunning[0] == 0) {
|
||||
;
|
||||
}
|
||||
esp_crosscore_int_init();
|
||||
ESP_LOGI(TAG, "Starting scheduler on APP CPU.");
|
||||
xPortStartScheduler();
|
||||
}
|
||||
|
103
components/esp32/crosscore_int.c
Normal file
103
components/esp32/crosscore_int.c
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2015-2016 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 "esp_attr.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr.h"
|
||||
|
||||
#include "rom/ets_sys.h"
|
||||
#include "rom/uart.h"
|
||||
|
||||
#include "soc/cpu.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/portmacro.h"
|
||||
|
||||
|
||||
#define REASON_YIELD (1<<0)
|
||||
|
||||
static portMUX_TYPE reasonSpinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
static volatile uint32_t reason[ portNUM_PROCESSORS ];
|
||||
|
||||
|
||||
/*
|
||||
ToDo: There is a small chance the CPU already has yielded when this ISR is serviced. In that case, it's running the intended task but
|
||||
the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that.
|
||||
*/
|
||||
static void esp_crosscore_isr(void *arg) {
|
||||
uint32_t myReasonVal;
|
||||
#if 0
|
||||
//A pointer to the correct reason array item is passed to this ISR.
|
||||
volatile uint32_t *myReason=arg;
|
||||
#else
|
||||
//The previous line does not work yet, the interrupt code needs work to understand two separate interrupt and argument
|
||||
//tables... this is a valid but slightly less optimal replacement.
|
||||
volatile uint32_t *myReason=&reason[xPortGetCoreID()];
|
||||
#endif
|
||||
//Clear the interrupt first.
|
||||
if (xPortGetCoreID()==0) {
|
||||
WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0);
|
||||
} else {
|
||||
WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0);
|
||||
}
|
||||
//Grab the reason and clear it.
|
||||
portENTER_CRITICAL(&reasonSpinlock);
|
||||
myReasonVal=*myReason;
|
||||
*myReason=0;
|
||||
portEXIT_CRITICAL(&reasonSpinlock);
|
||||
|
||||
//Check what we need to do.
|
||||
if (myReasonVal&REASON_YIELD) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
//Initialize the crosscore interrupt on this core. Call this once
|
||||
//on each active core.
|
||||
void esp_crosscore_int_init() {
|
||||
portENTER_CRITICAL(&reasonSpinlock);
|
||||
reason[xPortGetCoreID()]=0;
|
||||
portEXIT_CRITICAL(&reasonSpinlock);
|
||||
ESP_INTR_DISABLE(ETS_FROM_CPU_INUM);
|
||||
if (xPortGetCoreID()==0) {
|
||||
intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR0_SOURCE, ETS_FROM_CPU_INUM);
|
||||
} else {
|
||||
intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR1_SOURCE, ETS_FROM_CPU_INUM);
|
||||
}
|
||||
xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]);
|
||||
ESP_INTR_ENABLE(ETS_FROM_CPU_INUM);
|
||||
}
|
||||
|
||||
void esp_crosscore_int_send_yield(int coreId) {
|
||||
assert(coreId<portNUM_PROCESSORS);
|
||||
//Mark the reason we interrupt the other CPU
|
||||
portENTER_CRITICAL(&reasonSpinlock);
|
||||
reason[coreId]|=REASON_YIELD;
|
||||
portEXIT_CRITICAL(&reasonSpinlock);
|
||||
//Poke the other CPU.
|
||||
if (coreId==0) {
|
||||
WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, DPORT_CPU_INTR_FROM_CPU_0);
|
||||
} else {
|
||||
WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, DPORT_CPU_INTR_FROM_CPU_1);
|
||||
}
|
||||
}
|
||||
|
365
components/esp32/gdbstub.c
Normal file
365
components/esp32/gdbstub.c
Normal file
@@ -0,0 +1,365 @@
|
||||
// Copyright 2015-2016 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.
|
||||
|
||||
/******************************************************************************
|
||||
* Description: A stub to make the ESP32 debuggable by GDB over the serial
|
||||
* port, at least enough to do a backtrace on panic. This gdbstub is read-only:
|
||||
* it allows inspecting the ESP32 state
|
||||
*******************************************************************************/
|
||||
|
||||
//ToDo: Clean up includes and sync to real rtos
|
||||
#include "rom/ets_sys.h"
|
||||
|
||||
#include "soc/uart_reg.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
|
||||
#include "esp_gdbstub.h"
|
||||
|
||||
//Length of buffer used to reserve GDB commands. Has to be at least able to fit the G command, which
|
||||
//implies a minimum size of about 320 bytes.
|
||||
#define PBUFLEN 512
|
||||
|
||||
static unsigned char cmd[PBUFLEN]; //GDB command input buffer
|
||||
static char chsum; //Running checksum of the output packet
|
||||
|
||||
#define ATTR_GDBFN
|
||||
|
||||
static void ATTR_GDBFN keepWDTalive() {
|
||||
//ToDo for esp31/32
|
||||
}
|
||||
|
||||
|
||||
//Receive a char from the uart. Uses polling and feeds the watchdog.
|
||||
static int ATTR_GDBFN gdbRecvChar() {
|
||||
int i;
|
||||
while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT)==0) {
|
||||
keepWDTalive();
|
||||
}
|
||||
i=READ_PERI_REG(UART_FIFO_REG(0));
|
||||
return i;
|
||||
}
|
||||
|
||||
//Send a char to the uart.
|
||||
static void ATTR_GDBFN gdbSendChar(char c) {
|
||||
while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ;
|
||||
WRITE_PERI_REG(UART_FIFO_REG(0), c);
|
||||
}
|
||||
|
||||
//Send the start of a packet; reset checksum calculation.
|
||||
static void ATTR_GDBFN gdbPacketStart() {
|
||||
chsum=0;
|
||||
gdbSendChar('$');
|
||||
}
|
||||
|
||||
//Send a char as part of a packet
|
||||
static void ATTR_GDBFN gdbPacketChar(char c) {
|
||||
if (c=='#' || c=='$' || c=='}' || c=='*') {
|
||||
gdbSendChar('}');
|
||||
gdbSendChar(c^0x20);
|
||||
chsum+=(c^0x20)+'}';
|
||||
} else {
|
||||
gdbSendChar(c);
|
||||
chsum+=c;
|
||||
}
|
||||
}
|
||||
|
||||
//Send a string as part of a packet
|
||||
static void ATTR_GDBFN gdbPacketStr(char *c) {
|
||||
while (*c!=0) {
|
||||
gdbPacketChar(*c);
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
//Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent.
|
||||
static void ATTR_GDBFN gdbPacketHex(int val, int bits) {
|
||||
char hexChars[]="0123456789abcdef";
|
||||
int i;
|
||||
for (i=bits; i>0; i-=4) {
|
||||
gdbPacketChar(hexChars[(val>>(i-4))&0xf]);
|
||||
}
|
||||
}
|
||||
|
||||
//Finish sending a packet.
|
||||
static void ATTR_GDBFN gdbPacketEnd() {
|
||||
gdbSendChar('#');
|
||||
gdbPacketHex(chsum, 8);
|
||||
}
|
||||
|
||||
//Error states used by the routines that grab stuff from the incoming gdb packet
|
||||
#define ST_ENDPACKET -1
|
||||
#define ST_ERR -2
|
||||
#define ST_OK -3
|
||||
#define ST_CONT -4
|
||||
|
||||
//Grab a hex value from the gdb packet. Ptr will get positioned on the end
|
||||
//of the hex string, as far as the routine has read into it. Bits/4 indicates
|
||||
//the max amount of hex chars it gobbles up. Bits can be -1 to eat up as much
|
||||
//hex chars as possible.
|
||||
static long ATTR_GDBFN gdbGetHexVal(unsigned char **ptr, int bits) {
|
||||
int i;
|
||||
int no;
|
||||
unsigned int v=0;
|
||||
char c;
|
||||
no=bits/4;
|
||||
if (bits==-1) no=64;
|
||||
for (i=0; i<no; i++) {
|
||||
c=**ptr;
|
||||
(*ptr)++;
|
||||
if (c>='0' && c<='9') {
|
||||
v<<=4;
|
||||
v|=(c-'0');
|
||||
} else if (c>='A' && c<='F') {
|
||||
v<<=4;
|
||||
v|=(c-'A')+10;
|
||||
} else if (c>='a' && c<='f') {
|
||||
v<<=4;
|
||||
v|=(c-'a')+10;
|
||||
} else if (c=='#') {
|
||||
if (bits==-1) {
|
||||
(*ptr)--;
|
||||
return v;
|
||||
}
|
||||
return ST_ENDPACKET;
|
||||
} else {
|
||||
if (bits==-1) {
|
||||
(*ptr)--;
|
||||
return v;
|
||||
}
|
||||
return ST_ERR;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
//Swap an int into the form gdb wants it
|
||||
static int ATTR_GDBFN iswap(int i) {
|
||||
int r;
|
||||
r=((i>>24)&0xff);
|
||||
r|=((i>>16)&0xff)<<8;
|
||||
r|=((i>>8)&0xff)<<16;
|
||||
r|=((i>>0)&0xff)<<24;
|
||||
return r;
|
||||
}
|
||||
|
||||
//Read a byte from ESP32 memory.
|
||||
static unsigned char ATTR_GDBFN readbyte(unsigned int p) {
|
||||
int *i=(int*)(p&(~3));
|
||||
if (p<0x20000000 || p>=0x80000000) return -1;
|
||||
return *i>>((p&3)*8);
|
||||
}
|
||||
|
||||
|
||||
//Register file in the format exp108 gdb port expects it.
|
||||
//Inspired by gdb/regformats/reg-xtensa.dat
|
||||
typedef struct {
|
||||
uint32_t pc;
|
||||
uint32_t a[64];
|
||||
uint32_t lbeg;
|
||||
uint32_t lend;
|
||||
uint32_t lcount;
|
||||
uint32_t sar;
|
||||
uint32_t windowbase;
|
||||
uint32_t windowstart;
|
||||
uint32_t configid0;
|
||||
uint32_t configid1;
|
||||
uint32_t ps;
|
||||
uint32_t threadptr;
|
||||
uint32_t br;
|
||||
uint32_t scompare1;
|
||||
uint32_t acclo;
|
||||
uint32_t acchi;
|
||||
uint32_t m0;
|
||||
uint32_t m1;
|
||||
uint32_t m2;
|
||||
uint32_t m3;
|
||||
uint32_t expstate; //I'm going to assume this is exccause...
|
||||
uint32_t f64r_lo;
|
||||
uint32_t f64r_hi;
|
||||
uint32_t f64s;
|
||||
uint32_t f[16];
|
||||
uint32_t fcr;
|
||||
uint32_t fsr;
|
||||
} GdbRegFile;
|
||||
|
||||
|
||||
GdbRegFile gdbRegFile;
|
||||
|
||||
/*
|
||||
//Register format as the Xtensa HAL has it:
|
||||
STRUCT_FIELD (long, 4, XT_STK_EXIT, exit)
|
||||
STRUCT_FIELD (long, 4, XT_STK_PC, pc)
|
||||
STRUCT_FIELD (long, 4, XT_STK_PS, ps)
|
||||
STRUCT_FIELD (long, 4, XT_STK_A0, a0)
|
||||
[..]
|
||||
STRUCT_FIELD (long, 4, XT_STK_A15, a15)
|
||||
STRUCT_FIELD (long, 4, XT_STK_SAR, sar)
|
||||
STRUCT_FIELD (long, 4, XT_STK_EXCCAUSE, exccause)
|
||||
STRUCT_FIELD (long, 4, XT_STK_EXCVADDR, excvaddr)
|
||||
STRUCT_FIELD (long, 4, XT_STK_LBEG, lbeg)
|
||||
STRUCT_FIELD (long, 4, XT_STK_LEND, lend)
|
||||
STRUCT_FIELD (long, 4, XT_STK_LCOUNT, lcount)
|
||||
// Temporary space for saving stuff during window spill
|
||||
STRUCT_FIELD (long, 4, XT_STK_TMP0, tmp0)
|
||||
STRUCT_FIELD (long, 4, XT_STK_TMP1, tmp1)
|
||||
STRUCT_FIELD (long, 4, XT_STK_TMP2, tmp2)
|
||||
STRUCT_FIELD (long, 4, XT_STK_VPRI, vpri)
|
||||
STRUCT_FIELD (long, 4, XT_STK_OVLY, ovly)
|
||||
#endif
|
||||
STRUCT_END(XtExcFrame)
|
||||
*/
|
||||
|
||||
|
||||
static void dumpHwToRegfile(XtExcFrame *frame) {
|
||||
int i;
|
||||
long *frameAregs=&frame->a0;
|
||||
gdbRegFile.pc=frame->pc;
|
||||
for (i=0; i<16; i++) gdbRegFile.a[i]=frameAregs[i];
|
||||
for (i=16; i<64; i++) gdbRegFile.a[i]=0xDEADBEEF;
|
||||
gdbRegFile.lbeg=frame->lbeg;
|
||||
gdbRegFile.lend=frame->lend;
|
||||
gdbRegFile.lcount=frame->lcount;
|
||||
gdbRegFile.sar=frame->sar;
|
||||
//All windows have been spilled to the stack by the ISR routines. The following values should indicate that.
|
||||
gdbRegFile.sar=frame->sar;
|
||||
gdbRegFile.windowbase=0; //0
|
||||
gdbRegFile.windowstart=0x1; //1
|
||||
gdbRegFile.configid0=0xdeadbeef; //ToDo
|
||||
gdbRegFile.configid1=0xdeadbeef; //ToDo
|
||||
gdbRegFile.ps=frame->ps-PS_EXCM_MASK;
|
||||
gdbRegFile.threadptr=0xdeadbeef; //ToDo
|
||||
gdbRegFile.br=0xdeadbeef; //ToDo
|
||||
gdbRegFile.scompare1=0xdeadbeef; //ToDo
|
||||
gdbRegFile.acclo=0xdeadbeef; //ToDo
|
||||
gdbRegFile.acchi=0xdeadbeef; //ToDo
|
||||
gdbRegFile.m0=0xdeadbeef; //ToDo
|
||||
gdbRegFile.m1=0xdeadbeef; //ToDo
|
||||
gdbRegFile.m2=0xdeadbeef; //ToDo
|
||||
gdbRegFile.m3=0xdeadbeef; //ToDo
|
||||
gdbRegFile.expstate=frame->exccause; //ToDo
|
||||
}
|
||||
|
||||
|
||||
//Send the reason execution is stopped to GDB.
|
||||
static void sendReason() {
|
||||
//exception-to-signal mapping
|
||||
char exceptionSignal[]={4,31,11,11,2,6,8,0,6,7,0,0,7,7,7,7};
|
||||
int i=0;
|
||||
gdbPacketStart();
|
||||
gdbPacketChar('T');
|
||||
i=gdbRegFile.expstate&0x7f;
|
||||
if (i<sizeof(exceptionSignal)) return gdbPacketHex(exceptionSignal[i], 8); else gdbPacketHex(11, 8);
|
||||
gdbPacketEnd();
|
||||
}
|
||||
|
||||
//Handle a command as received from GDB.
|
||||
static int gdbHandleCommand(unsigned char *cmd, int len) {
|
||||
//Handle a command
|
||||
int i, j, k;
|
||||
unsigned char *data=cmd+1;
|
||||
if (cmd[0]=='g') { //send all registers to gdb
|
||||
int *p=(int*)&gdbRegFile;
|
||||
gdbPacketStart();
|
||||
for (i=0; i<sizeof(GdbRegFile)/4; i++) gdbPacketHex(iswap(*p++), 32);
|
||||
gdbPacketEnd();
|
||||
} else if (cmd[0]=='G') { //receive content for all registers from gdb
|
||||
int *p=(int*)&gdbRegFile;
|
||||
for (i=0; i<sizeof(GdbRegFile)/4; i++) *p++=iswap(gdbGetHexVal(&data, 32));;
|
||||
gdbPacketStart();
|
||||
gdbPacketStr("OK");
|
||||
gdbPacketEnd();
|
||||
} else if (cmd[0]=='m') { //read memory to gdb
|
||||
i=gdbGetHexVal(&data, -1);
|
||||
data++;
|
||||
j=gdbGetHexVal(&data, -1);
|
||||
gdbPacketStart();
|
||||
for (k=0; k<j; k++) {
|
||||
gdbPacketHex(readbyte(i++), 8);
|
||||
}
|
||||
gdbPacketEnd();
|
||||
} else if (cmd[0]=='?') { //Reply with stop reason
|
||||
sendReason();
|
||||
} else {
|
||||
//We don't recognize or support whatever GDB just sent us.
|
||||
gdbPacketStart();
|
||||
gdbPacketEnd();
|
||||
return ST_ERR;
|
||||
}
|
||||
return ST_OK;
|
||||
}
|
||||
|
||||
|
||||
//Lower layer: grab a command packet and check the checksum
|
||||
//Calls gdbHandleCommand on the packet if the checksum is OK
|
||||
//Returns ST_OK on success, ST_ERR when checksum fails, a
|
||||
//character if it is received instead of the GDB packet
|
||||
//start char.
|
||||
static int gdbReadCommand() {
|
||||
unsigned char c;
|
||||
unsigned char chsum=0, rchsum;
|
||||
unsigned char sentchs[2];
|
||||
int p=0;
|
||||
unsigned char *ptr;
|
||||
c=gdbRecvChar();
|
||||
if (c!='$') return c;
|
||||
while(1) {
|
||||
c=gdbRecvChar();
|
||||
if (c=='#') { //end of packet, checksum follows
|
||||
cmd[p]=0;
|
||||
break;
|
||||
}
|
||||
chsum+=c;
|
||||
if (c=='$') {
|
||||
//Wut, restart packet?
|
||||
chsum=0;
|
||||
p=0;
|
||||
continue;
|
||||
}
|
||||
if (c=='}') { //escape the next char
|
||||
c=gdbRecvChar();
|
||||
chsum+=c;
|
||||
c^=0x20;
|
||||
}
|
||||
cmd[p++]=c;
|
||||
if (p>=PBUFLEN) return ST_ERR;
|
||||
}
|
||||
//A # has been received. Get and check the received chsum.
|
||||
sentchs[0]=gdbRecvChar();
|
||||
sentchs[1]=gdbRecvChar();
|
||||
ptr=&sentchs[0];
|
||||
rchsum=gdbGetHexVal(&ptr, 8);
|
||||
// ets_printf("c %x r %x\n", chsum, rchsum);
|
||||
if (rchsum!=chsum) {
|
||||
gdbSendChar('-');
|
||||
return ST_ERR;
|
||||
} else {
|
||||
gdbSendChar('+');
|
||||
return gdbHandleCommand(cmd, p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void gdbstubPanicHandler(XtExcFrame *frame) {
|
||||
dumpHwToRegfile(frame);
|
||||
//Make sure txd/rxd are enabled
|
||||
PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD);
|
||||
|
||||
sendReason();
|
||||
while(gdbReadCommand()!=ST_CONT);
|
||||
while(1);
|
||||
}
|
||||
|
@@ -186,7 +186,11 @@ void heap_alloc_caps_init() {
|
||||
#endif
|
||||
|
||||
#if CONFIG_MEMMAP_TRACEMEM
|
||||
#if CONFIG_MEMMAP_TRACEMEM_TWOBANKS
|
||||
disable_mem_region((void*)0x3fff8000, (void*)0x40000000); //knock out trace mem region
|
||||
#else
|
||||
disable_mem_region((void*)0x3fff8000, (void*)0x3fffc000); //knock out trace mem region
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
|
21
components/esp32/include/esp_brownout.h
Normal file
21
components/esp32/include/esp_brownout.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2015-2016 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();
|
||||
|
||||
#endif
|
42
components/esp32/include/esp_crosscore_int.h
Normal file
42
components/esp32/include/esp_crosscore_int.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2015-2016 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_CROSSCORE_INT_H
|
||||
#define __ESP_CROSSCORE_INT_H
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the crosscore interrupt system for this CPU.
|
||||
* This needs to be called once on every CPU that is used
|
||||
* by FreeRTOS.
|
||||
*
|
||||
* If multicore FreeRTOS support is enabled, this will be
|
||||
* called automatically by the startup code and should not
|
||||
* be called manually.
|
||||
*/
|
||||
void esp_crosscore_int_init();
|
||||
|
||||
|
||||
/**
|
||||
* Send an interrupt to a CPU indicating it should yield its
|
||||
* currently running task in favour of a higher-priority task
|
||||
* that presumably just woke up.
|
||||
*
|
||||
* This is used internally by FreeRTOS in multicore mode
|
||||
* and should not be called by the user.
|
||||
*
|
||||
* @param coreID Core that should do the yielding
|
||||
*/
|
||||
void esp_crosscore_int_send_yield(int coreId);
|
||||
|
||||
#endif
|
@@ -31,6 +31,8 @@ typedef int32_t esp_err_t;
|
||||
#define ESP_ERR_NO_MEM 0x101
|
||||
#define ESP_ERR_INVALID_ARG 0x102
|
||||
#define ESP_ERR_INVALID_STATE 0x103
|
||||
#define ESP_ERR_INVALID_SIZE 0x104
|
||||
#define ESP_ERR_NOT_FOUND 0x105
|
||||
|
||||
/**
|
||||
* Macro which can be used to check the error code,
|
||||
|
106
components/esp32/include/esp_flash_data_types.h
Normal file
106
components/esp32/include/esp_flash_data_types.h
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2015-2016 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_BIN_TYPES_H__
|
||||
#define __ESP_BIN_TYPES_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define ESP_PARTITION_TABLE_ADDR 0x4000
|
||||
#define ESP_PARTITION_MAGIC 0x50AA
|
||||
|
||||
/* SPI flash mode, used in esp_image_header_t */
|
||||
typedef enum {
|
||||
ESP_IMAGE_SPI_MODE_QIO,
|
||||
ESP_IMAGE_SPI_MODE_QOUT,
|
||||
ESP_IMAGE_SPI_MODE_DIO,
|
||||
ESP_IMAGE_SPI_MODE_DOUT,
|
||||
ESP_IMAGE_SPI_MODE_FAST_READ,
|
||||
ESP_IMAGE_SPI_MODE_SLOW_READ
|
||||
} esp_image_spi_mode_t;
|
||||
|
||||
/* SPI flash clock frequency */
|
||||
enum {
|
||||
ESP_IMAGE_SPI_SPEED_40M,
|
||||
ESP_IMAGE_SPI_SPEED_26M,
|
||||
ESP_IMAGE_SPI_SPEED_20M,
|
||||
ESP_IMAGE_SPI_SPEED_80M = 0xF
|
||||
} esp_image_spi_freq_t;
|
||||
|
||||
/* Supported SPI flash sizes */
|
||||
typedef enum {
|
||||
ESP_IMAGE_FLASH_SIZE_1MB = 0,
|
||||
ESP_IMAGE_FLASH_SIZE_2MB,
|
||||
ESP_IMAGE_FLASH_SIZE_4MB,
|
||||
ESP_IMAGE_FLASH_SIZE_8MB,
|
||||
ESP_IMAGE_FLASH_SIZE_16MB,
|
||||
ESP_IMAGE_FLASH_SIZE_MAX
|
||||
} esp_image_flash_size_t;
|
||||
|
||||
/* Main header of binary image */
|
||||
typedef struct {
|
||||
uint8_t magic;
|
||||
uint8_t blocks;
|
||||
uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */
|
||||
uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */
|
||||
uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */
|
||||
uint32_t entry_addr;
|
||||
uint8_t encrypt_flag; /* encrypt flag */
|
||||
uint8_t secure_boot_flag; /* secure boot flag */
|
||||
uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */
|
||||
} esp_image_header_t;
|
||||
|
||||
/* Header of binary image segment */
|
||||
typedef struct {
|
||||
uint32_t load_addr;
|
||||
uint32_t data_len;
|
||||
} esp_image_section_header_t;
|
||||
|
||||
|
||||
/* OTA selection structure (two copies in the OTA data partition.)
|
||||
Size of 32 bytes is friendly to flash encryption */
|
||||
typedef struct {
|
||||
uint32_t ota_seq;
|
||||
uint8_t seq_label[24];
|
||||
uint32_t crc; /* CRC32 of ota_seq field only */
|
||||
} esp_ota_select_entry_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
} esp_partition_pos_t;
|
||||
|
||||
/* Structure which describes the layout of partition table entry.
|
||||
* See docs/partition_tables.rst for more information about individual fields.
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t magic;
|
||||
uint8_t type;
|
||||
uint8_t subtype;
|
||||
esp_partition_pos_t pos;
|
||||
uint8_t label[16];
|
||||
uint8_t reserved[4];
|
||||
} esp_partition_info_t;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__ESP_BIN_TYPES_H__
|
22
components/esp32/include/esp_gdbstub.h
Normal file
22
components/esp32/include/esp_gdbstub.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2015-2016 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 GDBSTUB_H
|
||||
#define GDBSTUB_H
|
||||
|
||||
#include <xtensa/config/core.h>
|
||||
#include "freertos/xtensa_api.h"
|
||||
|
||||
void esp_gdbstub_panic_handler(XtExcFrame *frame);
|
||||
|
||||
#endif
|
60
components/esp32/include/esp_int_wdt.h
Normal file
60
components/esp32/include/esp_int_wdt.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2015-2016 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_INT_WDT_H
|
||||
#define __ESP_INT_WDT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @addtogroup Watchdog_APIs
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*
|
||||
This routine enables a watchdog to catch instances of processes disabling
|
||||
interrupts for too long, or code within interrupt handlers taking too long.
|
||||
It does this by setting up a watchdog which gets fed from the FreeRTOS
|
||||
task switch interrupt. When this watchdog times out, initially it will call
|
||||
a high-level interrupt routine that will panic FreeRTOS in order to allow
|
||||
for forensic examination of the state of the CPU. When this interrupt
|
||||
handler is not called and the watchdog times out a second time, it will
|
||||
reset the SoC.
|
||||
|
||||
This uses the TIMERG1 WDT.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the interrupt watchdog. This is called in the init code if
|
||||
* the interrupt watchdog is enabled in menuconfig.
|
||||
*
|
||||
* @param null
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void esp_int_wdt_init();
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
21
components/esp32/include/esp_panic.h
Normal file
21
components/esp32/include/esp_panic.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef PANIC_H
|
||||
#define PANIC_H
|
||||
|
||||
|
||||
#define PANIC_RSN_NONE 0
|
||||
#define PANIC_RSN_DEBUGEXCEPTION 1
|
||||
#define PANIC_RSN_DOUBLEEXCEPTION 2
|
||||
#define PANIC_RSN_KERNELEXCEPTION 3
|
||||
#define PANIC_RSN_COPROCEXCEPTION 4
|
||||
#define PANIC_RSN_INTWDT_CPU0 5
|
||||
#define PANIC_RSN_INTWDT_CPU1 6
|
||||
#define PANIC_RSN_MAX 6
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
void esp_set_breakpoint_if_jtag(void *fn);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
83
components/esp32/include/esp_task_wdt.h
Normal file
83
components/esp32/include/esp_task_wdt.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2015-2016 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_TASK_WDT_H
|
||||
#define __ESP_TASK_WDT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/** \defgroup Watchdog_APIs Watchdog APIs
|
||||
* @brief Watchdog APIs
|
||||
*/
|
||||
|
||||
/** @addtogroup Watchdog_APIs
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*
|
||||
This routine enables a more general-purpose task watchdog: tasks can individually
|
||||
feed the watchdog and the watchdog will bark if one or more tasks haven't fed the
|
||||
watchdog within the specified time. Optionally, the idle tasks can also configured
|
||||
to feed the watchdog in a similar fashion, to detect CPU starvation.
|
||||
|
||||
This uses the TIMERG0 WDT.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the task watchdog. This is called in the init code, if the
|
||||
* task watchdog is enabled in menuconfig.
|
||||
*
|
||||
* @param null
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void esp_task_wdt_init();
|
||||
|
||||
/**
|
||||
* @brief Feed the watchdog. After the first feeding session, the watchdog will expect the calling
|
||||
* task to keep feeding the watchdog until task_wdt_delete() is called.
|
||||
*
|
||||
* @param null
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
|
||||
void esp_task_wdt_feed();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Delete the watchdog for the current task.
|
||||
*
|
||||
* @param null
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void esp_task_wdt_delete();
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif
|
@@ -191,14 +191,14 @@ esp_err_t esp_wifi_disconnect(void);
|
||||
esp_err_t esp_wifi_clear_fast_connect(void);
|
||||
|
||||
/**
|
||||
* @brief Kick the all station or associated id equals to aid
|
||||
* @brief deauthenticate all stations or associated id equals to aid
|
||||
*
|
||||
* @param uint16_t aid : when aid is 0, kick all stations, otherwise kick station whose associated id is aid
|
||||
* @param uint16_t aid : when aid is 0, deauthenticate all stations, otherwise deauthenticate station whose associated id is aid
|
||||
*
|
||||
* @return ESP_OK : succeed
|
||||
* @return others : fail
|
||||
*/
|
||||
esp_err_t esp_wifi_kick_station(uint16_t aid);
|
||||
esp_err_t esp_wifi_deauth_sta(uint16_t aid);
|
||||
|
||||
/**
|
||||
* @brief Scan all available APs.
|
||||
@@ -235,19 +235,30 @@ esp_err_t esp_wifi_scan_stop(void);
|
||||
* @return ESP_OK : succeed
|
||||
* @return others : fail
|
||||
*/
|
||||
esp_err_t esp_wifi_get_ap_num(uint16_t *number);
|
||||
esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number);
|
||||
|
||||
/**
|
||||
* @brief Get AP list found in last scan
|
||||
*
|
||||
* @param uint16_t *number : as input param, it stores max AP number ap_list can hold, as output param, it store
|
||||
* @param uint16_t *number : as input param, it stores max AP number ap_records can hold, as output param, it store
|
||||
the actual AP number this API returns
|
||||
* @param wifi_ap_list_t *ap_list : a list to hold the found APs
|
||||
* @param wifi_ap_record_t *ap_records: wifi_ap_record_t array to hold the found APs
|
||||
*
|
||||
* @return ESP_OK : succeed
|
||||
* @return others : fail
|
||||
*/
|
||||
esp_err_t esp_wifi_get_ap_list(uint16_t *number, wifi_ap_list_t *ap_list);
|
||||
esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get information of AP associated with ESP32 station
|
||||
*
|
||||
* @param wifi_ap_record_t *ap_info: the wifi_ap_record_t to hold station assocated AP
|
||||
*
|
||||
* @return ESP_OK : succeed
|
||||
* @return others : fail
|
||||
*/
|
||||
esp_err_t esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info);
|
||||
|
||||
/**
|
||||
* @brief Set current power save type
|
||||
@@ -471,14 +482,13 @@ esp_err_t esp_wifi_get_config(wifi_interface_t ifx, wifi_config_t *conf);
|
||||
*
|
||||
* @attention SSC only API
|
||||
*
|
||||
* @param struct station_info **station : station list
|
||||
* @param wifi_sta_list_t *sta: station list
|
||||
*
|
||||
* @return ESP_OK : succeed
|
||||
* @return others : fail
|
||||
*/
|
||||
esp_err_t esp_wifi_get_station_list(struct station_info **station);
|
||||
esp_err_t esp_wifi_ap_get_sta_list(wifi_sta_list_t *sta);
|
||||
|
||||
esp_err_t esp_wifi_free_station_list(void);
|
||||
|
||||
/**
|
||||
* @brief Set the WiFi API configuration storage type
|
||||
|
80
components/esp32/include/esp_wifi_internal.h
Normal file
80
components/esp32/include/esp_wifi_internal.h
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2015-2016 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.
|
||||
|
||||
/*
|
||||
* All the APIs declared here are internal only APIs, it can only be used by
|
||||
* espressif internal modules, such as SSC, LWIP, TCPIP adapter etc, espressif
|
||||
* customers are not recommended to use them.
|
||||
*
|
||||
* If someone really want to use specified APIs declared in here, please contact
|
||||
* espressif AE/developer to make sure you know the limitations or risk of
|
||||
* the API, otherwise you may get unexpected behavior!!!
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __ESP_WIFI_INTERNAL_H__
|
||||
#define __ESP_WIFI_INTERNAL_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "rom/queue.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_wifi_types.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief get whether the wifi driver is allowed to transmit data or not
|
||||
*
|
||||
* @param none
|
||||
*
|
||||
* @return true : upper layer should stop to transmit data to wifi driver
|
||||
* @return false : upper layer can transmit data to wifi driver
|
||||
*/
|
||||
bool esp_wifi_internal_tx_is_stop(void);
|
||||
|
||||
/**
|
||||
* @brief free the rx buffer which allocated by wifi driver
|
||||
*
|
||||
* @param void* buffer: rx buffer pointer
|
||||
*
|
||||
* @return nonoe
|
||||
*/
|
||||
void esp_wifi_internal_free_rx_buffer(void* buffer);
|
||||
|
||||
/**
|
||||
* @brief transmit the buffer via wifi driver
|
||||
*
|
||||
* @attention1 TODO should modify the return type from bool to int
|
||||
*
|
||||
* @param wifi_interface_t wifi_if : wifi interface id
|
||||
* @param void *buffer : the buffer to be tansmit
|
||||
* @param u16_t len : the length of buffer
|
||||
*
|
||||
* @return True : success transmit the buffer to wifi driver
|
||||
* False : failed to transmit the buffer to wifi driver
|
||||
*/
|
||||
bool esp_wifi_internal_tx(wifi_interface_t wifi_if, void *buffer, u16_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ESP_WIFI_H__ */
|
@@ -109,7 +109,7 @@ typedef struct {
|
||||
wifi_second_chan_t second; /**< second channel of AP */
|
||||
int8_t rssi; /**< signal strength of AP */
|
||||
wifi_auth_mode_t authmode; /**< authmode of AP */
|
||||
} wifi_ap_list_t;
|
||||
} wifi_ap_record_t;
|
||||
|
||||
typedef enum {
|
||||
WIFI_PS_NONE, /**< No power save */
|
||||
@@ -150,10 +150,15 @@ typedef union {
|
||||
wifi_sta_config_t sta; /**< configuration of STA */
|
||||
} wifi_config_t;
|
||||
|
||||
struct station_info {
|
||||
STAILQ_ENTRY(station_info) next;
|
||||
uint8_t bssid[6];
|
||||
};
|
||||
typedef struct {
|
||||
uint8_t mac[6]; /**< mac address of sta that associated with ESP32 soft-AP */
|
||||
}wifi_sta_info_t;
|
||||
|
||||
#define ESP_WIFI_MAX_CONN_NUM (10) /**< max number of stations which can connect to ESP32 soft-AP */
|
||||
typedef struct {
|
||||
wifi_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM]; /**< station list */
|
||||
int num; /**< number of station that associated with ESP32 soft-AP */
|
||||
}wifi_sta_list_t;
|
||||
|
||||
typedef enum {
|
||||
WIFI_STORAGE_FLASH, /**< all configuration will strore in both memory and flash */
|
||||
|
@@ -218,7 +218,7 @@ void SelectSpiFunction(uint32_t ishspi);
|
||||
void spi_flash_attach(uint32_t ishspi, bool legacy);
|
||||
|
||||
/**
|
||||
* @brief SPI Read Flash status register. We use CMD 0x05.
|
||||
* @brief SPI Read Flash status register. We use CMD 0x05 (RDSR).
|
||||
* Please do not call this function in SDK.
|
||||
*
|
||||
* @param SpiFlashChip *spi : The information for Flash, which is exported from ld file.
|
||||
@@ -232,7 +232,7 @@ void spi_flash_attach(uint32_t ishspi, bool legacy);
|
||||
SpiFlashOpResult SPI_read_status(SpiFlashChip *spi, uint32_t *status);
|
||||
|
||||
/**
|
||||
* @brief SPI Read Flash status register high 16 bit. We use CMD 0x35.
|
||||
* @brief SPI Read Flash status register bits 8-15. We use CMD 0x35 (RDSR2).
|
||||
* Please do not call this function in SDK.
|
||||
*
|
||||
* @param SpiFlashChip *spi : The information for Flash, which is exported from ld file.
|
||||
@@ -243,7 +243,7 @@ SpiFlashOpResult SPI_read_status(SpiFlashChip *spi, uint32_t *status);
|
||||
* SPI_FLASH_RESULT_ERR : read error.
|
||||
* SPI_FLASH_RESULT_TIMEOUT : read timeout.
|
||||
*/
|
||||
SpiFlashOpResult SPI_read_status_high(SpiFlashChip *spi, uint32_t *status);
|
||||
SpiFlashOpResult SPI_read_status_high(uint32_t *status);
|
||||
|
||||
/**
|
||||
* @brief Write status to Falsh status register.
|
||||
@@ -503,6 +503,12 @@ void SPI_Write_Encrypt_Disable(void);
|
||||
*/
|
||||
SpiFlashOpResult SPI_Encrypt_Write(uint32_t flash_addr, uint32_t *data, uint32_t len);
|
||||
|
||||
|
||||
/** @brief Global SpiFlashChip structure used by ROM functions
|
||||
*
|
||||
*/
|
||||
extern SpiFlashChip g_rom_flashchip;
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
@@ -15,6 +15,9 @@
|
||||
#ifndef _SOC_CPU_H
|
||||
#define _SOC_CPU_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "xtensa/corebits.h"
|
||||
|
||||
/* C macros for xtensa special register read/write/exchange */
|
||||
|
@@ -3830,6 +3830,11 @@
|
||||
#define DPORT_DATE_S 0
|
||||
#define DPORT_DPORT_DATE_VERSION 0x1605190
|
||||
|
||||
/* Flash MMU table for PRO CPU */
|
||||
#define DPORT_PRO_FLASH_MMU_TABLE ((volatile uint32_t*) 0x3FF10000)
|
||||
|
||||
/* Flash MMU table for APP CPU */
|
||||
#define DPORT_APP_FLASH_MMU_TABLE ((volatile uint32_t*) 0x3FF12000)
|
||||
|
||||
|
||||
|
||||
|
@@ -14,6 +14,9 @@
|
||||
#ifndef _SOC_RTC_CNTL_REG_H_
|
||||
#define _SOC_RTC_CNTL_REG_H_
|
||||
|
||||
/* The value that needs to be written to RTC_CNTL_WDT_WKEY to write-enable the wdt registers */
|
||||
#define RTC_CNTL_WDT_WKEY_VALUE 0x50D83AA1
|
||||
|
||||
|
||||
#include "soc.h"
|
||||
#define RTC_CNTL_OPTIONS0_REG (DR_REG_RTCCNTL_BASE + 0x0)
|
||||
|
@@ -210,10 +210,10 @@
|
||||
#define ETS_TG1_LACT_LEVEL_INTR_SOURCE 21/**< interrupt of TIMER_GROUP1, LACT, level*/
|
||||
#define ETS_GPIO_INTR_SOURCE 22/**< interrupt of GPIO, level*/
|
||||
#define ETS_GPIO_NMI_SOURCE 23/**< interrupt of GPIO, NMI*/
|
||||
#define ETS_FROM_CPU_INTR0_SOURCE 24/**< interrupt0 generated from a CPU, level*/
|
||||
#define ETS_FROM_CPU_INTR1_SOURCE 25/**< interrupt1 generated from a CPU, level*/
|
||||
#define ETS_FROM_CPU_INTR2_SOURCE 26/**< interrupt2 generated from a CPU, level*/
|
||||
#define ETS_FROM_CPU_INTR3_SOURCE 27/**< interrupt3 generated from a CPU, level*/
|
||||
#define ETS_FROM_CPU_INTR0_SOURCE 24/**< interrupt0 generated from a CPU, level*/ /* Used for FreeRTOS */
|
||||
#define ETS_FROM_CPU_INTR1_SOURCE 25/**< interrupt1 generated from a CPU, level*/ /* Used for FreeRTOS */
|
||||
#define ETS_FROM_CPU_INTR2_SOURCE 26/**< interrupt2 generated from a CPU, level*/ /* Used for VHCI */
|
||||
#define ETS_FROM_CPU_INTR3_SOURCE 27/**< interrupt3 generated from a CPU, level*/ /* Reserved */
|
||||
#define ETS_SPI0_INTR_SOURCE 28/**< interrupt of SPI0, level, SPI0 is for Cache Access, do not use this*/
|
||||
#define ETS_SPI1_INTR_SOURCE 29/**< interrupt of SPI1, level, SPI1 is for flash read/write, do not use this*/
|
||||
#define ETS_SPI2_INTR_SOURCE 30/**< interrupt of SPI2, level*/
|
||||
|
@@ -15,6 +15,16 @@
|
||||
#define __TIMG_REG_H__
|
||||
#include "soc.h"
|
||||
|
||||
/* The value that needs to be written to TIMG_WDT_WKEY to write-enable the wdt registers */
|
||||
#define TIMG_WDT_WKEY_VALUE 0x50D83AA1
|
||||
|
||||
/* Possible values for TIMG_WDT_STGx */
|
||||
#define TIMG_WDT_STG_SEL_OFF 0
|
||||
#define TIMG_WDT_STG_SEL_INT 1
|
||||
#define TIMG_WDT_STG_SEL_RESET_CPU 2
|
||||
#define TIMG_WDT_STG_SEL_RESET_SYSTEM 3
|
||||
|
||||
|
||||
#define REG_TIMG_BASE(i) (DR_REG_TIMERGROUP0_BASE + i*0x1000)
|
||||
#define TIMG_T0CONFIG_REG(i) (REG_TIMG_BASE(i) + 0x0000)
|
||||
/* TIMG_T0_EN : R/W ;bitpos:[31] ;default: 1'h0 ; */
|
||||
|
96
components/esp32/int_wdt.c
Normal file
96
components/esp32/int_wdt.c
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright 2015-2016 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 "sdkconfig.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <esp_types.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/timer_group_struct.h"
|
||||
#include "soc/timer_group_reg.h"
|
||||
|
||||
#include "esp_int_wdt.h"
|
||||
|
||||
#if CONFIG_INT_WDT
|
||||
|
||||
|
||||
#define WDT_INT_NUM 24
|
||||
|
||||
|
||||
void esp_int_wdt_init() {
|
||||
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS
|
||||
TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS
|
||||
TIMERG1.wdt_config0.level_int_en=1;
|
||||
TIMERG1.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt
|
||||
TIMERG1.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
|
||||
TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
|
||||
//The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets
|
||||
//it to their actual value.
|
||||
TIMERG1.wdt_config2=10000;
|
||||
TIMERG1.wdt_config3=10000;
|
||||
TIMERG1.wdt_config0.en=1;
|
||||
TIMERG1.wdt_feed=1;
|
||||
TIMERG1.wdt_wprotect=0;
|
||||
TIMERG1.int_clr_timers.wdt=1;
|
||||
TIMERG1.int_ena.wdt=1;
|
||||
ESP_INTR_DISABLE(WDT_INT_NUM);
|
||||
intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM);
|
||||
//We do not register a handler for the interrupt because it is interrupt level 4 which
|
||||
//is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for
|
||||
//this interrupt.
|
||||
ESP_INTR_ENABLE(WDT_INT_NUM);
|
||||
}
|
||||
|
||||
|
||||
//Take care: the tick hook can also be called before esp_int_wdt_init() is called.
|
||||
#if CONFIG_INT_WDT_CHECK_CPU1
|
||||
//Not static; the ISR assembly checks this.
|
||||
bool int_wdt_app_cpu_ticked=false;
|
||||
|
||||
void IRAM_ATTR vApplicationTickHook(void) {
|
||||
if (xPortGetCoreID()!=0) {
|
||||
int_wdt_app_cpu_ticked=true;
|
||||
} else {
|
||||
//Only feed wdt if app cpu also ticked.
|
||||
if (int_wdt_app_cpu_ticked) {
|
||||
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt
|
||||
TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset
|
||||
TIMERG1.wdt_feed=1;
|
||||
TIMERG1.wdt_wprotect=0;
|
||||
int_wdt_app_cpu_ticked=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void IRAM_ATTR vApplicationTickHook(void) {
|
||||
if (xPortGetCoreID()!=0) return;
|
||||
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt
|
||||
TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset
|
||||
TIMERG1.wdt_feed=1;
|
||||
TIMERG1.wdt_wprotect=0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@@ -47,6 +47,7 @@ SECTIONS
|
||||
_iram_text_start = ABSOLUTE(.);
|
||||
*(.iram1 .iram1.*)
|
||||
*libfreertos.a:(.literal .text .literal.* .text.*)
|
||||
*libesp32.a:panic.o(.literal .text .literal.* .text.*)
|
||||
*libphy.a:(.literal .text .literal.* .text.*)
|
||||
*librtc.a:(.literal .text .literal.* .text.*)
|
||||
*libpp.a:(.literal .text .literal.* .text.*)
|
||||
@@ -92,6 +93,7 @@ SECTIONS
|
||||
KEEP(*(.gnu.linkonce.s2.*))
|
||||
KEEP(*(.jcr))
|
||||
*(.dram1 .dram1.*)
|
||||
*libesp32.a:panic.o(.rodata .rodata.*)
|
||||
_data_end = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
_heap_start = ABSOLUTE(.);
|
||||
|
@@ -286,6 +286,7 @@ PROVIDE ( _global_impure_ptr = 0x3ffae0b0 );
|
||||
PROVIDE ( gmtime = 0x40059848 );
|
||||
PROVIDE ( gmtime_r = 0x40059868 );
|
||||
PROVIDE ( g_phyFuns_instance = 0x3ffae0c4 );
|
||||
PROVIDE ( g_rom_flashchip = 0x3ffae270 );
|
||||
PROVIDE ( gpio_init = 0x40009c20 );
|
||||
PROVIDE ( gpio_input_get = 0x40009b88 );
|
||||
PROVIDE ( gpio_input_get_high = 0x40009b9c );
|
||||
@@ -1584,6 +1585,8 @@ PROVIDE ( SPIEraseBlock = 0x40062c4c );
|
||||
PROVIDE ( SPIEraseChip = 0x40062c14 );
|
||||
PROVIDE ( SPIEraseSector = 0x40062ccc );
|
||||
PROVIDE ( spi_flash_attach = 0x40062a6c );
|
||||
/* NB: SPIUnlock @ 0x400628b0 has been replaced with an updated
|
||||
version in the "spi_flash" component */
|
||||
PROVIDE ( SPILock = 0x400628f0 );
|
||||
PROVIDE ( SPIMasterReadModeCnfig = 0x40062b64 );
|
||||
PROVIDE ( spi_modes = 0x3ff99270 );
|
||||
@@ -1595,9 +1598,8 @@ PROVIDE ( SPIReadModeCnfig = 0x40062944 );
|
||||
PROVIDE ( SPI_read_status = 0x4006226c );
|
||||
/* This is static function, but can be used, not generated by script*/
|
||||
PROVIDE ( SPI_read_status_high = 0x40062448 );
|
||||
PROVIDE ( SPIUnlock = 0x400628b0 );
|
||||
PROVIDE ( SPI_user_command_read = 0x400621b0 );
|
||||
PROVIDE ( spi_w25q16 = 0x3ffae270 );
|
||||
PROVIDE ( SPI_flashchip_data = 0x3ffae270 );
|
||||
PROVIDE ( SPIWrite = 0x40062d50 );
|
||||
/* This is static function, but can be used, not generated by script*/
|
||||
PROVIDE ( SPI_write_enable = 0x40062320 );
|
||||
|
Submodule components/esp32/lib updated: 0ee9babfd9...01d2e6e22e
287
components/esp32/panic.c
Normal file
287
components/esp32/panic.c
Normal file
@@ -0,0 +1,287 @@
|
||||
// Copyright 2015-2016 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 <xtensa/config/core.h>
|
||||
|
||||
#include "rom/rtc.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/xtensa_api.h"
|
||||
|
||||
#include "soc/uart_reg.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/timer_group_struct.h"
|
||||
#include "soc/timer_group_reg.h"
|
||||
|
||||
#include "esp_gdbstub.h"
|
||||
#include "esp_panic.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
/*
|
||||
Panic handlers; these get called when an unhandled exception occurs or the assembly-level
|
||||
task switching / interrupt code runs into an unrecoverable error. The default task stack
|
||||
overflow handler also is in here.
|
||||
*/
|
||||
|
||||
/*
|
||||
Note: The linker script will put everything in this file in IRAM/DRAM, so it also works with flash cache disabled.
|
||||
*/
|
||||
|
||||
#if !CONFIG_ESP32_PANIC_SILENT_REBOOT
|
||||
//printf may be broken, so we fix our own printing fns...
|
||||
inline static void panicPutchar(char c) {
|
||||
while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ;
|
||||
WRITE_PERI_REG(UART_FIFO_REG(0), c);
|
||||
}
|
||||
|
||||
inline static void panicPutStr(const char *c) {
|
||||
int x=0;
|
||||
while (c[x]!=0) {
|
||||
panicPutchar(c[x]);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
inline static void panicPutHex(int a) {
|
||||
int x;
|
||||
int c;
|
||||
panicPutchar(' ');
|
||||
for (x=0; x<8; x++) {
|
||||
c=(a>>28)&0xf;
|
||||
if (c<10) panicPutchar('0'+c); else panicPutchar('a'+c-10);
|
||||
a<<=4;
|
||||
}
|
||||
}
|
||||
|
||||
inline static void panicPutDec(int a) {
|
||||
int n1, n2;
|
||||
n1=a%10;
|
||||
n2=a/10;
|
||||
panicPutchar(' ');
|
||||
if (n2==0) panicPutchar(' '); else panicPutchar(n2+'0');
|
||||
panicPutchar(n1+'0');
|
||||
}
|
||||
#else
|
||||
//No printing wanted. Stub out these functions.
|
||||
inline static void panicPutchar(char c) { }
|
||||
inline static void panicPutStr(const char *c) { }
|
||||
inline static void panicPutHex(int a) { }
|
||||
inline static void panicPutDec(int a) { }
|
||||
#endif
|
||||
|
||||
|
||||
void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) {
|
||||
panicPutStr("***ERROR*** A stack overflow in task ");
|
||||
panicPutStr((char*)pcTaskName);
|
||||
panicPutStr(" has been detected.\r\n");
|
||||
}
|
||||
|
||||
static const char *edesc[]={
|
||||
"IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError",
|
||||
"Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue",
|
||||
"Privileged", "LoadStoreAlignment", "res", "res",
|
||||
"InstrPDAddrError", "LoadStorePIFDataError", "InstrPIFAddrError", "LoadStorePIFAddrError",
|
||||
"InstTLBMiss", "InstTLBMultiHit", "InstFetchPrivilege", "res",
|
||||
"InstrFetchProhibited", "res", "res", "res",
|
||||
"LoadStoreTLBMiss", "LoadStoreTLBMultihit", "LoadStorePrivilege", "res",
|
||||
"LoadProhibited", "StoreProhibited", "res", "res",
|
||||
"Cp0Dis", "Cp1Dis", "Cp2Dis", "Cp3Dis",
|
||||
"Cp4Dis", "Cp5Dis", "Cp6Dis", "Cp7Dis"
|
||||
};
|
||||
|
||||
|
||||
void commonErrorHandler(XtExcFrame *frame);
|
||||
|
||||
//The fact that we've panic'ed probably means the other CPU is now running wild, possibly
|
||||
//messing up the serial output, so we kill it here.
|
||||
static void haltOtherCore() {
|
||||
if (xPortGetCoreID()==0) {
|
||||
//Kill app cpu
|
||||
CLEAR_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, RTC_CNTL_SW_STALL_APPCPU_C1_M);
|
||||
SET_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, 0x21<<RTC_CNTL_SW_STALL_APPCPU_C1_S);
|
||||
CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_APPCPU_C0_M);
|
||||
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, 2<<RTC_CNTL_SW_STALL_APPCPU_C0_S);
|
||||
} else {
|
||||
//Kill pro cpu
|
||||
CLEAR_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, RTC_CNTL_SW_STALL_PROCPU_C1_M);
|
||||
SET_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, 0x21<<RTC_CNTL_SW_STALL_PROCPU_C1_S);
|
||||
CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_PROCPU_C0_M);
|
||||
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, 2<<RTC_CNTL_SW_STALL_PROCPU_C0_S);
|
||||
}
|
||||
}
|
||||
|
||||
//Returns true when a debugger is attached using JTAG.
|
||||
static int inOCDMode() {
|
||||
#if CONFIG_ESP32_DEBUG_OCDAWARE
|
||||
int dcr;
|
||||
int reg=0x10200C; //DSRSET register
|
||||
asm("rer %0,%1":"=r"(dcr):"r"(reg));
|
||||
return (dcr&0x1);
|
||||
#else
|
||||
return 0; //Always return no debugger is attached.
|
||||
#endif
|
||||
}
|
||||
|
||||
void panicHandler(XtExcFrame *frame) {
|
||||
int *regs=(int*)frame;
|
||||
//Please keep in sync with PANIC_RSN_* defines
|
||||
const char *reasons[]={
|
||||
"Unknown reason",
|
||||
"Unhandled debug exception",
|
||||
"Double exception",
|
||||
"Unhandled kernel exception",
|
||||
"Coprocessor exception",
|
||||
"Interrupt wdt timeout on CPU0",
|
||||
"Interrupt wdt timeout on CPU1",
|
||||
};
|
||||
const char *reason=reasons[0];
|
||||
//The panic reason is stored in the EXCCAUSE register.
|
||||
if (regs[20]<=PANIC_RSN_MAX) reason=reasons[regs[20]];
|
||||
haltOtherCore();
|
||||
panicPutStr("Guru Meditation Error: Core ");
|
||||
panicPutDec(xPortGetCoreID());
|
||||
panicPutStr(" panic'ed (");
|
||||
panicPutStr(reason);
|
||||
panicPutStr(")\r\n");
|
||||
|
||||
if (inOCDMode()) {
|
||||
asm("break.n 1");
|
||||
}
|
||||
commonErrorHandler(frame);
|
||||
}
|
||||
|
||||
static void setFirstBreakpoint(uint32_t pc) {
|
||||
asm(
|
||||
"wsr.ibreaka0 %0\n" \
|
||||
"rsr.ibreakenable a3\n" \
|
||||
"movi a4,1\n" \
|
||||
"or a4, a4, a3\n" \
|
||||
"wsr.ibreakenable a4\n" \
|
||||
::"r"(pc):"a3","a4");
|
||||
}
|
||||
|
||||
void xt_unhandled_exception(XtExcFrame *frame) {
|
||||
int *regs=(int*)frame;
|
||||
int x;
|
||||
|
||||
haltOtherCore();
|
||||
panicPutStr("Guru Meditation Error of type ");
|
||||
x=regs[20];
|
||||
if (x<40) panicPutStr(edesc[x]); else panicPutStr("Unknown");
|
||||
panicPutStr(" occurred on core ");
|
||||
panicPutDec(xPortGetCoreID());
|
||||
if (inOCDMode()) {
|
||||
panicPutStr(" at pc=");
|
||||
panicPutHex(regs[1]);
|
||||
panicPutStr(". Setting bp and returning..\r\n");
|
||||
//Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger
|
||||
//will kick in exactly at the context the error happened.
|
||||
setFirstBreakpoint(regs[1]);
|
||||
return;
|
||||
}
|
||||
panicPutStr(". Exception was unhandled.\r\n");
|
||||
commonErrorHandler(frame);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
If watchdogs are enabled, the panic handler runs the risk of getting aborted pre-emptively because
|
||||
an overzealous watchdog decides to reset it. On the other hand, if we disable all watchdogs, we run
|
||||
the risk of somehow halting in the panic handler and not resetting. That is why this routine kills
|
||||
all watchdogs except the timer group 0 watchdog, and it reconfigures that to reset the chip after
|
||||
one second.
|
||||
*/
|
||||
static void reconfigureAllWdts() {
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_feed=1;
|
||||
TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS
|
||||
TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS
|
||||
TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_RESET_SYSTEM; //1st stage timeout: reset system
|
||||
TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
|
||||
TIMERG0.wdt_config2=2000; //1 second before reset
|
||||
TIMERG0.wdt_config0.en=1;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
//Disable wdt 1
|
||||
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.en=0;
|
||||
TIMERG1.wdt_wprotect=0;
|
||||
}
|
||||
|
||||
#if CONFIG_ESP32_PANIC_GDBSTUB || CONFIG_ESP32_PANIC_PRINT_HALT
|
||||
/*
|
||||
This disables all the watchdogs for when we call the gdbstub.
|
||||
*/
|
||||
static void disableAllWdts() {
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_config0.en=0;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.en=0;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the
|
||||
serial port and either jump to the gdb stub, halt the CPU or reboot.
|
||||
*/
|
||||
void commonErrorHandler(XtExcFrame *frame) {
|
||||
int *regs=(int*)frame;
|
||||
int x, y;
|
||||
const char *sdesc[]={
|
||||
"PC ","PS ","A0 ","A1 ","A2 ","A3 ","A4 ","A5 ",
|
||||
"A6 ","A7 ","A8 ","A9 ","A10 ","A11 ","A12 ","A13 ",
|
||||
"A14 ","A15 ","SAR ","EXCCAUSE","EXCVADDR","LBEG ","LEND ","LCOUNT "};
|
||||
|
||||
//Feed the watchdogs, so they will give us time to print out debug info
|
||||
reconfigureAllWdts();
|
||||
|
||||
panicPutStr("Register dump:\r\n");
|
||||
|
||||
for (x=0; x<24; x+=4) {
|
||||
for (y=0; y<4; y++) {
|
||||
if (sdesc[x+y][0]!=0) {
|
||||
panicPutStr(sdesc[x+y]);
|
||||
panicPutStr(": ");
|
||||
panicPutHex(regs[x+y+1]);
|
||||
panicPutStr(" ");
|
||||
}
|
||||
}
|
||||
panicPutStr("\r\n");
|
||||
}
|
||||
#if CONFIG_ESP32_PANIC_GDBSTUB
|
||||
disableAllWdts();
|
||||
panicPutStr("Entering gdb stub now.\r\n");
|
||||
esp_gdbstub_panic_handler(frame);
|
||||
#elif CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT
|
||||
panicPutStr("Rebooting...\r\n");
|
||||
for (x=0; x<100; x++) ets_delay_us(1000);
|
||||
software_reset();
|
||||
#else
|
||||
disableAllWdts();
|
||||
panicPutStr("CPU halted.\r\n");
|
||||
while(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void esp_set_breakpoint_if_jtag(void *fn) {
|
||||
if (!inOCDMode()) return;
|
||||
setFirstBreakpoint((uint32_t)fn);
|
||||
}
|
@@ -1,457 +0,0 @@
|
||||
// Copyright 2015-2016 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 <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/reent.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_attr.h"
|
||||
#include "rom/libc_stubs.h"
|
||||
#include "rom/uart.h"
|
||||
#include "soc/cpu.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
void abort() {
|
||||
do
|
||||
{
|
||||
__asm__ ("break 0,0");
|
||||
*((int*) 0) = 0;
|
||||
} while(true);
|
||||
}
|
||||
|
||||
void* _malloc_r(struct _reent *r, size_t size) {
|
||||
return pvPortMalloc(size);
|
||||
}
|
||||
|
||||
void _free_r(struct _reent *r, void* ptr) {
|
||||
return vPortFree(ptr);
|
||||
}
|
||||
|
||||
void* _realloc_r(struct _reent *r, void* ptr, size_t size) {
|
||||
void* new_chunk;
|
||||
if (size == 0) {
|
||||
if (ptr) {
|
||||
vPortFree(ptr);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_chunk = pvPortMalloc(size);
|
||||
if (new_chunk && ptr) {
|
||||
memcpy(new_chunk, ptr, size);
|
||||
vPortFree(ptr);
|
||||
}
|
||||
// realloc behaviour: don't free original chunk if alloc failed
|
||||
return new_chunk;
|
||||
}
|
||||
|
||||
void* _calloc_r(struct _reent *r, size_t count, size_t size) {
|
||||
void* result = pvPortMalloc(count * size);
|
||||
if (result)
|
||||
{
|
||||
memset(result, 0, count * size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int _system_r(struct _reent *r, const char *str) {
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _rename_r(struct _reent *r, const char *src, const char *dst) {
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
clock_t _times_r(struct _reent *r, struct tms *ptms) {
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: read time from RTC
|
||||
int _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) {
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _raise_r(struct _reent *r) {
|
||||
abort();
|
||||
}
|
||||
|
||||
int _unlink_r(struct _reent *r, const char *path) {
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _link_r(struct _reent *r, const char* n1, const char* n2) {
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _stat_r(struct _reent *r, const char * path, struct stat * st) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _fstat_r(struct _reent *r, int fd, struct stat * st) {
|
||||
st->st_mode = S_IFCHR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* _sbrk_r(struct _reent *r, ptrdiff_t sz) {
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _getpid_r(struct _reent *r) {
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _kill_r(struct _reent *r, int pid, int sig) {
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _exit_r(struct _reent *r, int e) {
|
||||
abort();
|
||||
}
|
||||
|
||||
int _close_r(struct _reent *r, int fd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _open_r(struct _reent *r, const char * path, int flags, int mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _exit(int __status) {
|
||||
abort();
|
||||
}
|
||||
|
||||
ssize_t _write_r(struct _reent *r, int fd, const void * data, size_t size) {
|
||||
const char *data_c = (const char *)data;
|
||||
if (fd == STDOUT_FILENO) {
|
||||
static _lock_t stdout_lock; /* lazily initialised */
|
||||
/* Even though newlib does stream locking on stdout, we need
|
||||
a dedicated stdout UART lock...
|
||||
|
||||
This is because each task has its own _reent structure with
|
||||
unique FILEs for stdin/stdout/stderr, so these are
|
||||
per-thread (lazily initialised by __sinit the first time a
|
||||
stdio function is used, see findfp.c:235.
|
||||
|
||||
It seems like overkill to allocate a FILE-per-task and lock
|
||||
a thread-local stream, but I see no easy way to fix this
|
||||
(pre-__sinit_, tasks have "fake" FILEs ie __sf_fake_stdout
|
||||
which aren't fully valid.)
|
||||
*/
|
||||
_lock_acquire_recursive(&stdout_lock);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
#if CONFIG_NEWLIB_STDOUT_ADDCR
|
||||
if (data_c[i]=='\n') {
|
||||
uart_tx_one_char('\r');
|
||||
}
|
||||
#endif
|
||||
uart_tx_one_char(data_c[i]);
|
||||
}
|
||||
_lock_release_recursive(&stdout_lock);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
_off_t _lseek_r(struct _reent *r, int fd, _off_t size, int mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: implement reading from UART
|
||||
ssize_t _read_r(struct _reent *r, int fd, void * dst, size_t size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Notes on our newlib lock implementation:
|
||||
*
|
||||
* - Use FreeRTOS mutex semaphores as locks.
|
||||
* - lock_t is int, but we store an xSemaphoreHandle there.
|
||||
* - Locks are no-ops until the FreeRTOS scheduler is running.
|
||||
* - Due to this, locks need to be lazily initialised the first time
|
||||
* they are acquired. Initialisation/deinitialisation of locks is
|
||||
* protected by lock_init_spinlock.
|
||||
* - Race conditions around lazy initialisation (via lock_acquire) are
|
||||
* protected against.
|
||||
* - Anyone calling lock_close is reponsible for ensuring noone else
|
||||
* is holding the lock at this time.
|
||||
* - Race conditions between lock_close & lock_init (for the same lock)
|
||||
* are the responsibility of the caller.
|
||||
*/
|
||||
|
||||
static portMUX_TYPE lock_init_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
/* Initialise the given lock by allocating a new mutex semaphore
|
||||
as the _lock_t value.
|
||||
*/
|
||||
static void IRAM_ATTR lock_init_generic(_lock_t *lock, uint8_t mutex_type) {
|
||||
portENTER_CRITICAL(&lock_init_spinlock);
|
||||
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
|
||||
/* nothing to do until the scheduler is running */
|
||||
*lock = 0; /* ensure lock is zeroed out, in case it's an automatic variable */
|
||||
portEXIT_CRITICAL(&lock_init_spinlock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*lock) {
|
||||
/* Lock already initialised (either we didn't check earlier,
|
||||
or it got initialised while we were waiting for the
|
||||
spinlock.) */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create a new semaphore
|
||||
|
||||
this is a bit of an API violation, as we're calling the
|
||||
private function xQueueCreateMutex(x) directly instead of
|
||||
the xSemaphoreCreateMutex / xSemaphoreCreateRecursiveMutex
|
||||
wrapper functions...
|
||||
|
||||
The better alternative would be to pass pointers to one of
|
||||
the two xSemaphoreCreate___Mutex functions, but as FreeRTOS
|
||||
implements these as macros instead of inline functions
|
||||
(*party like it's 1998!*) it's not possible to do this
|
||||
without writing wrappers. Doing it this way seems much less
|
||||
spaghetti-like.
|
||||
*/
|
||||
xSemaphoreHandle new_sem = xQueueCreateMutex(mutex_type);
|
||||
if (!new_sem) {
|
||||
abort(); /* No more semaphores available or OOM */
|
||||
}
|
||||
*lock = (_lock_t)new_sem;
|
||||
}
|
||||
portEXIT_CRITICAL(&lock_init_spinlock);
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_init(_lock_t *lock) {
|
||||
lock_init_generic(lock, queueQUEUE_TYPE_MUTEX);
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_init_recursive(_lock_t *lock) {
|
||||
lock_init_generic(lock, queueQUEUE_TYPE_RECURSIVE_MUTEX);
|
||||
}
|
||||
|
||||
/* Free the mutex semaphore pointed to by *lock, and zero it out.
|
||||
|
||||
Note that FreeRTOS doesn't account for deleting mutexes while they
|
||||
are held, and neither do we... so take care not to delete newlib
|
||||
locks while they may be held by other tasks!
|
||||
*/
|
||||
void IRAM_ATTR _lock_close(_lock_t *lock) {
|
||||
portENTER_CRITICAL(&lock_init_spinlock);
|
||||
if (*lock) {
|
||||
xSemaphoreHandle h = (xSemaphoreHandle)(*lock);
|
||||
#if (INCLUDE_xSemaphoreGetMutexHolder == 1)
|
||||
configASSERT(xSemaphoreGetMutexHolder(h) == NULL); /* mutex should not be held */
|
||||
#endif
|
||||
vSemaphoreDelete(h);
|
||||
*lock = 0;
|
||||
}
|
||||
portEXIT_CRITICAL(&lock_init_spinlock);
|
||||
}
|
||||
|
||||
/* Acquire the mutex semaphore for lock. wait up to delay ticks.
|
||||
mutex_type is queueQUEUE_TYPE_RECURSIVE_MUTEX or queueQUEUE_TYPE_MUTEX
|
||||
*/
|
||||
static int IRAM_ATTR lock_acquire_generic(_lock_t *lock, uint32_t delay, uint8_t mutex_type) {
|
||||
xSemaphoreHandle h = (xSemaphoreHandle)(*lock);
|
||||
if (!h) {
|
||||
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
|
||||
return 0; /* locking is a no-op before scheduler is up, so this "succeeds" */
|
||||
}
|
||||
/* lazy initialise lock - might have had a static initializer in newlib (that we don't use),
|
||||
or _lock_init might have been called before the scheduler was running... */
|
||||
lock_init_generic(lock, mutex_type);
|
||||
h = (xSemaphoreHandle)(*lock);
|
||||
configASSERT(h != NULL);
|
||||
}
|
||||
|
||||
BaseType_t success;
|
||||
if (cpu_in_interrupt_context()) {
|
||||
/* In ISR Context */
|
||||
if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
|
||||
abort(); /* recursive mutexes make no sense in ISR context */
|
||||
}
|
||||
BaseType_t higher_task_woken = false;
|
||||
success = xSemaphoreTakeFromISR(h, &higher_task_woken);
|
||||
if (!success && delay > 0) {
|
||||
abort(); /* Tried to block on mutex from ISR, couldn't... rewrite your program to avoid libc interactions in ISRs! */
|
||||
}
|
||||
if (higher_task_woken) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* In task context */
|
||||
if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
|
||||
success = xSemaphoreTakeRecursive(h, delay);
|
||||
} else {
|
||||
success = xSemaphoreTake(h, delay);
|
||||
}
|
||||
}
|
||||
|
||||
return (success == pdTRUE) ? 0 : -1;
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_acquire(_lock_t *lock) {
|
||||
lock_acquire_generic(lock, portMAX_DELAY, queueQUEUE_TYPE_MUTEX);
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_acquire_recursive(_lock_t *lock) {
|
||||
lock_acquire_generic(lock, portMAX_DELAY, queueQUEUE_TYPE_RECURSIVE_MUTEX);
|
||||
}
|
||||
|
||||
int IRAM_ATTR _lock_try_acquire(_lock_t *lock) {
|
||||
return lock_acquire_generic(lock, 0, queueQUEUE_TYPE_MUTEX);
|
||||
}
|
||||
|
||||
int IRAM_ATTR _lock_try_acquire_recursive(_lock_t *lock) {
|
||||
return lock_acquire_generic(lock, 0, queueQUEUE_TYPE_RECURSIVE_MUTEX);
|
||||
}
|
||||
|
||||
/* Release the mutex semaphore for lock.
|
||||
mutex_type is queueQUEUE_TYPE_RECURSIVE_MUTEX or queueQUEUE_TYPE_MUTEX
|
||||
*/
|
||||
static void IRAM_ATTR lock_release_generic(_lock_t *lock, uint8_t mutex_type) {
|
||||
xSemaphoreHandle h = (xSemaphoreHandle)(*lock);
|
||||
if (h == NULL) {
|
||||
/* This is probably because the scheduler isn't running yet,
|
||||
or the scheduler just started running and some code was
|
||||
"holding" a not-yet-initialised lock... */
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu_in_interrupt_context()) {
|
||||
if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
|
||||
abort(); /* indicates logic bug, it shouldn't be possible to lock recursively in ISR */
|
||||
}
|
||||
BaseType_t higher_task_woken = false;
|
||||
xSemaphoreGiveFromISR(h, &higher_task_woken);
|
||||
if (higher_task_woken) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
} else {
|
||||
if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
|
||||
xSemaphoreGiveRecursive(h);
|
||||
} else {
|
||||
xSemaphoreGive(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_release(_lock_t *lock) {
|
||||
lock_release_generic(lock, queueQUEUE_TYPE_MUTEX);
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_release_recursive(_lock_t *lock) {
|
||||
lock_release_generic(lock, queueQUEUE_TYPE_RECURSIVE_MUTEX);
|
||||
}
|
||||
|
||||
// This function is not part on newlib API, it is defined in libc/stdio/local.h
|
||||
// It is called as part of _reclaim_reent via a pointer in __cleanup member
|
||||
// of struct _reent.
|
||||
// This function doesn't call _fclose_r for _stdin, _stdout, _stderr members
|
||||
// of struct reent. Not doing so causes a memory leak each time a task is
|
||||
// terminated. We replace __cleanup member with _extra_cleanup_r (below) to work
|
||||
// around this.
|
||||
extern void _cleanup_r(struct _reent* r);
|
||||
|
||||
void _extra_cleanup_r(struct _reent* r)
|
||||
{
|
||||
_cleanup_r(r);
|
||||
_fclose_r(r, r->_stdout);
|
||||
_fclose_r(r, r->_stderr);
|
||||
_fclose_r(r, r->_stdin);
|
||||
}
|
||||
|
||||
static struct _reent s_reent;
|
||||
|
||||
/*
|
||||
General ToDo that the Xtensa newlib support code did but we do not: Close every open fd a running task had when the task
|
||||
is killed. Do we want that too? - JD
|
||||
*/
|
||||
|
||||
extern int _printf_float(struct _reent *rptr,
|
||||
void *pdata,
|
||||
FILE * fp,
|
||||
int (*pfunc) (struct _reent *, FILE *, _CONST char *, size_t len),
|
||||
va_list * ap);
|
||||
|
||||
|
||||
extern int _scanf_float(struct _reent *rptr,
|
||||
void *pdata,
|
||||
FILE *fp,
|
||||
va_list *ap);
|
||||
|
||||
|
||||
static struct syscall_stub_table s_stub_table = {
|
||||
.__getreent = &__getreent,
|
||||
._malloc_r = &_malloc_r,
|
||||
._free_r = &_free_r,
|
||||
._realloc_r = &_realloc_r,
|
||||
._calloc_r = &_calloc_r,
|
||||
._abort = &abort,
|
||||
._system_r = &_system_r,
|
||||
._rename_r = &_rename_r,
|
||||
._times_r = &_times_r,
|
||||
._gettimeofday_r = &_gettimeofday_r,
|
||||
._raise_r = &_raise_r,
|
||||
._unlink_r = &_unlink_r,
|
||||
._link_r = &_link_r,
|
||||
._stat_r = &_stat_r,
|
||||
._fstat_r = &_fstat_r,
|
||||
._sbrk_r = &_sbrk_r,
|
||||
._getpid_r = &_getpid_r,
|
||||
._kill_r = &_kill_r,
|
||||
._exit_r = &_exit_r,
|
||||
._close_r = &_close_r,
|
||||
._open_r = &_open_r,
|
||||
._write_r = (int (*)(struct _reent *r, int, const void *, int)) &_write_r,
|
||||
._lseek_r = (int (*)(struct _reent *r, int, int, int)) &_lseek_r,
|
||||
._read_r = (int (*)(struct _reent *r, int, void *, int)) &_read_r,
|
||||
._lock_init = &_lock_init,
|
||||
._lock_init_recursive = &_lock_init_recursive,
|
||||
._lock_close = &_lock_close,
|
||||
._lock_close_recursive = &_lock_close,
|
||||
._lock_acquire = &_lock_acquire,
|
||||
._lock_acquire_recursive = &_lock_acquire_recursive,
|
||||
._lock_try_acquire = &_lock_try_acquire,
|
||||
._lock_try_acquire_recursive = &_lock_try_acquire_recursive,
|
||||
._lock_release = &_lock_release,
|
||||
._lock_release_recursive = &_lock_release_recursive,
|
||||
._printf_float = &_printf_float,
|
||||
._scanf_float = &_scanf_float,
|
||||
};
|
||||
|
||||
void ets_setup_syscalls() {
|
||||
syscall_table_ptr_pro = &s_stub_table;
|
||||
syscall_table_ptr_app = &s_stub_table;
|
||||
_GLOBAL_REENT = &s_reent;
|
||||
environ = malloc(sizeof(char*));
|
||||
environ[0] = NULL;
|
||||
}
|
||||
|
||||
|
173
components/esp32/task_wdt.c
Normal file
173
components/esp32/task_wdt.c
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright 2015-2016 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 <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <esp_types.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/timer_group_struct.h"
|
||||
#include "soc/timer_group_reg.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_task_wdt.h"
|
||||
|
||||
#if CONFIG_TASK_WDT
|
||||
|
||||
static const char* TAG = "task_wdt";
|
||||
|
||||
typedef struct wdt_task_t wdt_task_t;
|
||||
struct wdt_task_t {
|
||||
TaskHandle_t task_handle;
|
||||
bool fed_watchdog;
|
||||
wdt_task_t *next;
|
||||
};
|
||||
|
||||
static wdt_task_t *wdt_task_list=NULL;
|
||||
|
||||
static void IRAM_ATTR task_wdt_isr(void *arg) {
|
||||
wdt_task_t *wdttask;
|
||||
const char *cpu;
|
||||
//Feed the watchdog so we do not reset
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_feed=1;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
//Ack interrupt
|
||||
TIMERG0.int_clr_timers.wdt=1;
|
||||
//Watchdog got triggered because at least one task did not report in.
|
||||
ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n");
|
||||
for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) {
|
||||
if (!wdttask->fed_watchdog) {
|
||||
cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1";
|
||||
if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1";
|
||||
printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu);
|
||||
}
|
||||
}
|
||||
ets_printf("Tasks currently running:\n");
|
||||
for (int x=0; x<portNUM_PROCESSORS; x++) {
|
||||
ets_printf("CPU %d: %s\n", x, pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(x)));
|
||||
}
|
||||
|
||||
#if CONFIG_TASK_WDT_PANIC
|
||||
ets_printf("Aborting.\n");
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void esp_task_wdt_feed() {
|
||||
wdt_task_t *wdttask=wdt_task_list;
|
||||
bool found_task=false, do_feed_wdt=true;
|
||||
TaskHandle_t handle=xTaskGetCurrentTaskHandle();
|
||||
//Walk the linked list of wdt tasks to find this one, as well as see if we need to feed
|
||||
//the real watchdog timer.
|
||||
for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) {
|
||||
//See if we are at the current task.
|
||||
if (wdttask->task_handle == handle) {
|
||||
wdttask->fed_watchdog=true;
|
||||
found_task=true;
|
||||
}
|
||||
//If even one task in the list doesn't have the do_feed_wdt var set, we do not feed the watchdog.
|
||||
if (!wdttask->fed_watchdog) do_feed_wdt=false;
|
||||
}
|
||||
|
||||
if (!found_task) {
|
||||
//This is the first time the task calls the task_wdt_feed function. Create a new entry for it in
|
||||
//the linked list.
|
||||
wdt_task_t *newtask=malloc(sizeof(wdt_task_t));
|
||||
memset(newtask, 0, sizeof(wdt_task_t));
|
||||
newtask->task_handle=handle;
|
||||
newtask->fed_watchdog=true;
|
||||
if (wdt_task_list == NULL) {
|
||||
wdt_task_list=newtask;
|
||||
} else {
|
||||
for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) ;
|
||||
wdttask->next=newtask;
|
||||
}
|
||||
}
|
||||
if (do_feed_wdt) {
|
||||
//All tasks have checked in; time to feed the hw watchdog.
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_feed=1;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
//Reset fed_watchdog status
|
||||
for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_task_wdt_delete() {
|
||||
TaskHandle_t handle=xTaskGetCurrentTaskHandle();
|
||||
wdt_task_t *wdttask=wdt_task_list;
|
||||
//Wdt task list can't be empty
|
||||
if (!wdt_task_list) {
|
||||
ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?");
|
||||
return;
|
||||
}
|
||||
if (handle==wdt_task_list) {
|
||||
//Current task is first on list.
|
||||
wdt_task_list=wdt_task_list->next;
|
||||
free(wdttask);
|
||||
} else {
|
||||
//Find current task in list
|
||||
while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next;
|
||||
if (!wdttask->next) {
|
||||
ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!");
|
||||
return;
|
||||
}
|
||||
wdt_task_t *freeme=wdttask->next;
|
||||
wdttask->next=wdttask->next->next;
|
||||
free(freeme);
|
||||
}
|
||||
}
|
||||
|
||||
void esp_task_wdt_init() {
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS
|
||||
TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS
|
||||
TIMERG0.wdt_config0.level_int_en=1;
|
||||
TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt
|
||||
TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
|
||||
TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
|
||||
TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000; //Set timeout before interrupt
|
||||
TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000; //Set timeout before reset
|
||||
TIMERG0.wdt_config0.en=1;
|
||||
TIMERG0.wdt_feed=1;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
ESP_INTR_DISABLE(ETS_T0_WDT_INUM);
|
||||
intr_matrix_set(xPortGetCoreID(), ETS_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM);
|
||||
xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL);
|
||||
TIMERG0.int_clr_timers.wdt=1;
|
||||
TIMERG0.int_ena.wdt=1;
|
||||
ESP_INTR_ENABLE(ETS_T0_WDT_INUM);
|
||||
}
|
||||
|
||||
#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
|
||||
void vApplicationIdleHook(void) {
|
||||
#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
|
||||
if (xPortGetCoreID()!=0) return;
|
||||
#endif
|
||||
esp_task_wdt_feed();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user