From f486ead84a44846996c66d117de391fab048b01b Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 3 May 2018 23:43:02 -0400 Subject: [PATCH] Handle bad power on reset. --- main.c | 86 +++++++++++++++++++++++------- ports/atmel-samd/mphalport.c | 7 +++ ports/atmel-samd/supervisor/port.c | 18 +++++++ 3 files changed, 91 insertions(+), 20 deletions(-) diff --git a/main.c b/main.c index 62938acf11..680a74dead 100644 --- a/main.c +++ b/main.c @@ -30,6 +30,7 @@ #include "extmod/vfs.h" #include "extmod/vfs_fat.h" +#include "genhdr/mpversion.h" #include "py/nlr.h" #include "py/compile.h" #include "py/frozenmod.h" @@ -99,19 +100,27 @@ void reset_mp(void) { } #define STRING_LIST(...) {__VA_ARGS__, ""} -bool maybe_run_list(const char ** filenames, pyexec_result_t* exec_result) { - +// Look for the first file that exists in the list of filenames, using mp_import_stat(). +// Return its index. If no file found, return -1. +int first_existing_file_in_list(const char ** filenames) { for (int i = 0; filenames[i] != (char*)""; i++) { mp_import_stat_t stat = mp_import_stat(filenames[i]); - if (stat != MP_IMPORT_STAT_FILE) { - continue; + if (stat == MP_IMPORT_STAT_FILE) { + return i; } - serial_write(filenames[i]); - serial_write(MSG_OUTPUT_SUFFIX); - pyexec_file(filenames[i], exec_result); - return true; } - return false; + return -1; +} + +bool maybe_run_list(const char ** filenames, pyexec_result_t* exec_result) { + int i = first_existing_file_in_list(filenames); + if (i == -1) { + return false; + } + mp_hal_stdout_tx_str(filenames[i]); + mp_hal_stdout_tx_str(MSG_OUTPUT_SUFFIX); + pyexec_file(filenames[i], exec_result); + return true; } bool start_mp(safe_mode_t safe_mode) { @@ -261,27 +270,64 @@ int __attribute__((used)) main(void) { // If not in safe mode, run boot before initing USB and capture output in a // file. if (filesystem_present() && safe_mode == NO_SAFE_MODE && MP_STATE_VM(vfs_mount_table) != NULL) { + static const char *boot_py_filenames[] = STRING_LIST("settings.txt", "settings.py", "boot.py", "boot.txt"); + new_status_color(BOOT_RUNNING); + #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - // Since USB isn't up yet we can cheat and let ourselves write the boot - // output file. - filesystem_writable_by_python(true); FIL file_pointer; boot_output_file = &file_pointer; - f_open(&((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs, - boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); - filesystem_writable_by_python(false); + + // Get the base filesystem. + FATFS *fs = &((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + + bool have_boot_py = first_existing_file_in_list(boot_py_filenames) != -1; + + bool skip_boot_output = false; + + // If there's no boot.py file that might write some changing output, + // read the existing copy of CIRCUITPY_BOOT_OUTPUT_FILE and see if its contents + // match the version info we would print anyway. If so, skip writing CIRCUITPY_BOOT_OUTPUT_FILE. + // This saves wear and tear on the flash and also prevents filesystem damage if power is lost + // during the write, which may happen due to bobbling the power connector or weak power. + + if (!have_boot_py && f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) { + char file_contents[512]; + UINT chars_read = 0; + f_read(boot_output_file, file_contents, 512, &chars_read); + f_close(boot_output_file); + skip_boot_output = + // + 2 accounts for \r\n. + chars_read == strlen(MICROPY_FULL_VERSION_INFO) + 2 && + strncmp(file_contents, MICROPY_FULL_VERSION_INFO, strlen(MICROPY_FULL_VERSION_INFO)) == 0; + } + + if (!skip_boot_output) { + // Wait 1.5 seconds before opening CIRCUITPY_BOOT_OUTPUT_FILE for write, + // in case power is momentary or will fail shortly due to, say a low, battery. + mp_hal_delay_ms(1500); + + // USB isn't up, so we can write the file. + filesystem_writable_by_python(true); + f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); + + // Write version info to boot_out.txt. + mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); + mp_hal_stdout_tx_str("\r\n"); + } #endif // TODO(tannewt): Re-add support for flashing boot error output. - static const char *filenames[] = STRING_LIST("settings.txt", "settings.py", "boot.py", "boot.txt"); - bool found_boot = maybe_run_list(filenames, NULL); + bool found_boot = maybe_run_list(boot_py_filenames, NULL); (void) found_boot; #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - f_close(boot_output_file); - filesystem_flush(); - boot_output_file = NULL; + if (!skip_boot_output) { + f_close(boot_output_file); + filesystem_flush(); + boot_output_file = NULL; + } + filesystem_writable_by_python(false); #endif // Reset to remove any state that boot.py setup. It should only be used to diff --git a/ports/atmel-samd/mphalport.c b/ports/atmel-samd/mphalport.c index 6ac66c9d14..cdf27fe028 100644 --- a/ports/atmel-samd/mphalport.c +++ b/ports/atmel-samd/mphalport.c @@ -45,6 +45,13 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { gpio_toggle_pin_level(MICROPY_HW_LED_TX); #endif + #ifdef CIRCUITPY_BOOT_OUTPUT_FILE + if (boot_output_file != NULL) { + UINT bytes_written = 0; + f_write(boot_output_file, str, len, &bytes_written); + } + #endif + usb_write(str, len); } diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 3ac9cbfc96..0e2d937634 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -79,6 +79,14 @@ __attribute__((__aligned__(TRACE_BUFFER_SIZE_BYTES))) uint32_t mtb[TRACE_BUFFER_ safe_mode_t port_init(void) { #if defined(SAMD21) + + // Set brownout detection to ~2.7V. Default from factory is 1.7V, + // which is too low for proper operation of external SPI flash chips (they are 2.7-3.6V). + // Disable while changing level. + SYSCTRL->BOD33.bit.ENABLE = 0; + SYSCTRL->BOD33.bit.LEVEL = 39; // 2.77V with hysteresis off. Table 37.20 in datasheet. + SYSCTRL->BOD33.bit.ENABLE = 1; + #ifdef ENABLE_MICRO_TRACE_BUFFER REG_MTB_POSITION = ((uint32_t) (mtb - REG_MTB_BASE)) & 0xFFFFFFF8; REG_MTB_FLOW = (((uint32_t) mtb - REG_MTB_BASE) + TRACE_BUFFER_SIZE_BYTES) & 0xFFFFFFF8; @@ -90,6 +98,16 @@ safe_mode_t port_init(void) { #endif #endif +#if defined(SAMD51) + // Set brownout detection to ~2.7V. Default from factory is 1.7V, + // which is too low for proper operation of external SPI flash chips (they are 2.7-3.6V). + // Disable while changing level. + SUPC->BOD33.bit.ENABLE = 0; + SUPC->BOD33.bit.LEVEL = 200; // 2.7V: 1.5V + LEVEL * 6mV. + SUPC->BOD33.bit.ENABLE = 1; +#endif + + // On power on start or external reset, set _ezero to the canary word. If it // gets killed, we boot in safe mode. _ezero is the boundary between statically