micropython/ports/esp32/main.c
Amir Gonnen 995f9cfdfc esp32: Pin MicroPython tasks to a specific core.
On this port the GIL is enabled and everything works under the assumption
of the GIL, ie that a given task has exclusive access to the uPy state, and
any ISRs interrupt the current task and therefore the ISR inherits
exclusive access to the uPy state for the duration of its execution.

If the MicroPython tasks are not pinned to a specific core then an ISR may
be executed on a different core to the task, making it possible for the
main task and an ISR to execute in parallel, breaking the assumption of the
GIL.

The easiest and safest fix for this is to pin all MicroPython related code
to the same CPU core, as done by this patch.  Then any ISR that accesses
MicroPython state must be registered from a MicroPython task, to ensure it
is invoked on the same core.

See issue #4895.
2019-07-25 15:33:47 +10:00

166 lines
5.1 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* Development of the code in this file was sponsored by Microbric Pty Ltd
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_task.h"
#include "soc/cpu.h"
#include "esp_log.h"
#include "esp_spiram.h"
#include "py/stackctrl.h"
#include "py/nlr.h"
#include "py/compile.h"
#include "py/runtime.h"
#include "py/repl.h"
#include "py/gc.h"
#include "py/mphal.h"
#include "lib/mp-readline/readline.h"
#include "lib/utils/pyexec.h"
#include "uart.h"
#include "modmachine.h"
#include "modnetwork.h"
#include "mpthreadport.h"
// MicroPython runs as a task under FreeRTOS
#define MP_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1)
#define MP_TASK_STACK_SIZE (16 * 1024)
#define MP_TASK_STACK_LEN (MP_TASK_STACK_SIZE / sizeof(StackType_t))
int vprintf_null(const char *format, va_list ap) {
// do nothing: this is used as a log target during raw repl mode
return 0;
}
void mp_task(void *pvParameter) {
volatile uint32_t sp = (uint32_t)get_sp();
#if MICROPY_PY_THREAD
mp_thread_init(pxTaskGetStackStart(NULL), MP_TASK_STACK_LEN);
#endif
uart_init();
#if CONFIG_SPIRAM_SUPPORT
// Try to use the entire external SPIRAM directly for the heap
size_t mp_task_heap_size;
void *mp_task_heap = (void*)0x3f800000;
switch (esp_spiram_get_chip_size()) {
case ESP_SPIRAM_SIZE_16MBITS:
mp_task_heap_size = 2 * 1024 * 1024;
break;
case ESP_SPIRAM_SIZE_32MBITS:
case ESP_SPIRAM_SIZE_64MBITS:
mp_task_heap_size = 4 * 1024 * 1024;
break;
default:
// No SPIRAM, fallback to normal allocation
mp_task_heap_size = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
mp_task_heap = malloc(mp_task_heap_size);
break;
}
#else
// Allocate the uPy heap using malloc and get the largest available region
size_t mp_task_heap_size = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
void *mp_task_heap = malloc(mp_task_heap_size);
#endif
soft_reset:
// initialise the stack pointer for the main thread
mp_stack_set_top((void *)sp);
mp_stack_set_limit(MP_TASK_STACK_SIZE - 1024);
gc_init(mp_task_heap, mp_task_heap + mp_task_heap_size);
mp_init();
mp_obj_list_init(mp_sys_path, 0);
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_));
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib));
mp_obj_list_init(mp_sys_argv, 0);
readline_init0();
// initialise peripherals
machine_pins_init();
// run boot-up scripts
pyexec_frozen_module("_boot.py");
pyexec_file_if_exists("boot.py");
if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) {
pyexec_file_if_exists("main.py");
}
for (;;) {
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
vprintf_like_t vprintf_log = esp_log_set_vprintf(vprintf_null);
if (pyexec_raw_repl() != 0) {
break;
}
esp_log_set_vprintf(vprintf_log);
} else {
if (pyexec_friendly_repl() != 0) {
break;
}
}
}
machine_timer_deinit_all();
#if MICROPY_PY_THREAD
mp_thread_deinit();
#endif
gc_sweep_all();
mp_hal_stdout_tx_str("MPY: soft reboot\r\n");
// deinitialise peripherals
machine_pins_deinit();
usocket_events_deinit();
mp_deinit();
fflush(stdout);
goto soft_reset;
}
void app_main(void) {
nvs_flash_init();
xTaskCreatePinnedToCore(mp_task, "mp_task", MP_TASK_STACK_LEN, NULL, MP_TASK_PRIORITY, &mp_main_task_handle, MP_TASK_COREID);
}
void nlr_jump_fail(void *val) {
printf("NLR jump failed, val=%p\n", val);
esp_restart();
}
// modussl_mbedtls uses this function but it's not enabled in ESP IDF
void mbedtls_debug_set_threshold(int threshold) {
(void)threshold;
}