Further refine autoreload

This unifies the delay into the post-run delay that also waits
for user input and fake sleep. This ensures we always delay.
Previous code would only delay if the code.py was running when
autoreload was triggered. Now it will always delay.

We also now suspend autoreload when a USB write starts and then
resume on completion. This should prevent reloading in between
sectors of a single write.
This commit is contained in:
Scott Shawcroft 2022-03-14 16:49:30 -07:00
parent 7cb66dd316
commit 32ac396a41
No known key found for this signature in database
GPG Key ID: 0DFD512649C052DA
8 changed files with 82 additions and 57 deletions

View File

@ -25,7 +25,7 @@ msgstr ""
#: main.c #: main.c
msgid "" msgid ""
"\n" "\n"
"Code stopped by auto-reload.\n" "Code stopped by auto-reload. Reloading soon.\n"
msgstr "" msgstr ""
#: supervisor/shared/safe_mode.c #: supervisor/shared/safe_mode.c
@ -584,10 +584,6 @@ msgstr ""
msgid "Brightness must be 0-1.0" msgid "Brightness must be 0-1.0"
msgstr "" msgstr ""
#: shared-bindings/supervisor/__init__.c
msgid "Brightness must be between 0 and 255"
msgstr ""
#: shared-bindings/displayio/Display.c #: shared-bindings/displayio/Display.c
#: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c
msgid "Brightness not adjustable" msgid "Brightness not adjustable"
@ -688,6 +684,7 @@ msgstr ""
msgid "Can only alarm on two low pins from deep sleep." msgid "Can only alarm on two low pins from deep sleep."
msgstr "" msgstr ""
#: ports/espressif/common-hal/_bleio/Characteristic.c
#: ports/nrf/common-hal/_bleio/Characteristic.c #: ports/nrf/common-hal/_bleio/Characteristic.c
msgid "Can't set CCCD on local Characteristic" msgid "Can't set CCCD on local Characteristic"
msgstr "" msgstr ""
@ -1605,6 +1602,7 @@ msgstr ""
msgid "Nimble out of memory" msgid "Nimble out of memory"
msgstr "" msgstr ""
#: ports/espressif/common-hal/_bleio/Characteristic.c
#: ports/nrf/common-hal/_bleio/Characteristic.c #: ports/nrf/common-hal/_bleio/Characteristic.c
msgid "No CCCD for this Characteristic" msgid "No CCCD for this Characteristic"
msgstr "" msgstr ""
@ -3642,7 +3640,7 @@ msgstr ""
msgid "matrix is not positive definite" msgid "matrix is not positive definite"
msgstr "" msgstr ""
#: shared-bindings/wifi/Radio.c #: ports/espressif/common-hal/wifi/Radio.c
msgid "max_connections must be between 0 and 10" msgid "max_connections must be between 0 and 10"
msgstr "" msgstr ""
@ -4061,6 +4059,7 @@ msgstr ""
#: ports/espressif/boards/adafruit_metro_esp32s2/mpconfigboard.h #: ports/espressif/boards/adafruit_metro_esp32s2/mpconfigboard.h
#: ports/espressif/boards/adafruit_qtpy_esp32s2/mpconfigboard.h #: ports/espressif/boards/adafruit_qtpy_esp32s2/mpconfigboard.h
#: ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h #: ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h
#: ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.h
#: ports/espressif/boards/ai_thinker_esp32-c3s/mpconfigboard.h #: ports/espressif/boards/ai_thinker_esp32-c3s/mpconfigboard.h
#: ports/espressif/boards/ai_thinker_esp_12k_nodemcu/mpconfigboard.h #: ports/espressif/boards/ai_thinker_esp_12k_nodemcu/mpconfigboard.h
#: ports/espressif/boards/artisense_rd00/mpconfigboard.h #: ports/espressif/boards/artisense_rd00/mpconfigboard.h
@ -4068,6 +4067,7 @@ msgstr ""
#: ports/espressif/boards/crumpspace_crumps2/mpconfigboard.h #: ports/espressif/boards/crumpspace_crumps2/mpconfigboard.h
#: ports/espressif/boards/electroniccats_bastwifi/mpconfigboard.h #: ports/espressif/boards/electroniccats_bastwifi/mpconfigboard.h
#: ports/espressif/boards/espressif_esp32c3_devkitm_1_n4/mpconfigboard.h #: ports/espressif/boards/espressif_esp32c3_devkitm_1_n4/mpconfigboard.h
#: ports/espressif/boards/espressif_esp32s2_devkitc_1_n4r2/mpconfigboard.h
#: ports/espressif/boards/espressif_esp32s3_box/mpconfigboard.h #: ports/espressif/boards/espressif_esp32s3_box/mpconfigboard.h
#: ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.h #: ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.h
#: ports/espressif/boards/espressif_esp32s3_devkitc_1_n8r2/mpconfigboard.h #: ports/espressif/boards/espressif_esp32s3_devkitc_1_n8r2/mpconfigboard.h
@ -4083,6 +4083,7 @@ msgstr ""
#: ports/espressif/boards/gravitech_cucumber_ms/mpconfigboard.h #: ports/espressif/boards/gravitech_cucumber_ms/mpconfigboard.h
#: ports/espressif/boards/gravitech_cucumber_r/mpconfigboard.h #: ports/espressif/boards/gravitech_cucumber_r/mpconfigboard.h
#: ports/espressif/boards/gravitech_cucumber_rs/mpconfigboard.h #: ports/espressif/boards/gravitech_cucumber_rs/mpconfigboard.h
#: ports/espressif/boards/hiibot_iots2/mpconfigboard.h
#: ports/espressif/boards/lilygo_ttgo_t8_s2_st7789/mpconfigboard.h #: ports/espressif/boards/lilygo_ttgo_t8_s2_st7789/mpconfigboard.h
#: ports/espressif/boards/lolin_s2_mini/mpconfigboard.h #: ports/espressif/boards/lolin_s2_mini/mpconfigboard.h
#: ports/espressif/boards/lolin_s2_pico/mpconfigboard.h #: ports/espressif/boards/lolin_s2_pico/mpconfigboard.h

39
main.c
View File

@ -124,7 +124,6 @@ static void reset_devices(void) {
} }
STATIC void start_mp(supervisor_allocation *heap, bool first_run) { STATIC void start_mp(supervisor_allocation *heap, bool first_run) {
autoreload_reset();
supervisor_workflow_reset(); supervisor_workflow_reset();
// Stack limit should be less than real stack size, so we have a chance // Stack limit should be less than real stack size, so we have a chance
@ -336,7 +335,13 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re
// Collects stickiness bits that apply in the current situation. // Collects stickiness bits that apply in the current situation.
uint8_t next_code_stickiness_situation = SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET; uint8_t next_code_stickiness_situation = SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
// Do the filesystem flush check before reload in case another write comes
// in while we're doing the flush.
if (safe_mode == NO_SAFE_MODE) { if (safe_mode == NO_SAFE_MODE) {
stack_resize();
filesystem_flush();
}
if (safe_mode == NO_SAFE_MODE && !autoreload_pending()) {
static const char *const supported_filenames[] = STRING_LIST( static const char *const supported_filenames[] = STRING_LIST(
"code.txt", "code.py", "main.py", "main.txt"); "code.txt", "code.py", "main.py", "main.txt");
#if CIRCUITPY_FULL_BUILD #if CIRCUITPY_FULL_BUILD
@ -345,8 +350,6 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re
"main.txt.py", "main.py.txt", "main.txt.txt","main.py.py"); "main.txt.py", "main.py.txt", "main.txt.txt","main.py.py");
#endif #endif
stack_resize();
filesystem_flush();
supervisor_allocation *heap = allocate_remaining_memory(); supervisor_allocation *heap = allocate_remaining_memory();
// Prepare the VM state. Includes an alarm check/reset for sleep. // Prepare the VM state. Includes an alarm check/reset for sleep.
@ -390,22 +393,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re
// Print done before resetting everything so that we get the message over // Print done before resetting everything so that we get the message over
// BLE before it is reset and we have a delay before reconnect. // BLE before it is reset and we have a delay before reconnect.
if ((result.return_code & PYEXEC_RELOAD) && supervisor_get_run_reason() == RUN_REASON_AUTO_RELOAD) { if ((result.return_code & PYEXEC_RELOAD) && supervisor_get_run_reason() == RUN_REASON_AUTO_RELOAD) {
serial_write_compressed(translate("\nCode stopped by auto-reload.\n")); serial_write_compressed(translate("\nCode stopped by auto-reload. Reloading soon.\n"));
// Wait for autoreload interval before reloading
uint64_t start_ticks = 0;
do {
// Start waiting, or restart interval if another reload request was initiated
// while we were waiting.
if (reload_requested) {
reload_requested = false;
start_ticks = supervisor_ticks_ms64();
}
RUN_BACKGROUND_TASKS;
} while (supervisor_ticks_ms64() - start_ticks < CIRCUITPY_AUTORELOAD_DELAY_MS);
// Restore request for use below.
reload_requested = true;
} else { } else {
serial_write_compressed(translate("\nCode done running.\n")); serial_write_compressed(translate("\nCode done running.\n"));
} }
@ -425,8 +413,6 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re
if (result.return_code & PYEXEC_RELOAD) { if (result.return_code & PYEXEC_RELOAD) {
next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD; next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD;
skip_repl = true;
skip_wait = true;
} else if (result.return_code == 0) { } else if (result.return_code == 0) {
next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_SUCCESS; next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_SUCCESS;
if (next_code_options & SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_SUCCESS) { if (next_code_options & SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_SUCCESS) {
@ -484,6 +470,8 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re
size_t total_time = blink_time + LED_SLEEP_TIME_MS; size_t total_time = blink_time + LED_SLEEP_TIME_MS;
#endif #endif
// This loop is waits after code completes. It waits for fake sleeps to
// finish, user input or autoreloads.
#if CIRCUITPY_ALARM #if CIRCUITPY_ALARM
bool fake_sleeping = false; bool fake_sleeping = false;
#endif #endif
@ -491,15 +479,18 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re
RUN_BACKGROUND_TASKS; RUN_BACKGROUND_TASKS;
// If a reload was requested by the supervisor or autoreload, return. // If a reload was requested by the supervisor or autoreload, return.
if (reload_requested) { if (autoreload_ready()) {
next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD; next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD;
// Should the STICKY_ON_SUCCESS and STICKY_ON_ERROR bits be cleared in // Should the STICKY_ON_SUCCESS and STICKY_ON_ERROR bits be cleared in
// next_code_stickiness_situation? I can see arguments either way, but I'm deciding // next_code_stickiness_situation? I can see arguments either way, but I'm deciding
// "no" for now, mainly because it's a bit less code. At this point, we have both a // "no" for now, mainly because it's a bit less code. At this point, we have both a
// success or error and a reload, so let's have both of the respective options take // success or error and a reload, so let's have both of the respective options take
// effect (in OR combination). // effect (in OR combination).
reload_requested = false;
skip_repl = true; skip_repl = true;
// We're kicking off the autoreload process so reset now. If any
// other reloads trigger after this, then we'll want another wait
// period.
autoreload_reset();
break; break;
} }
@ -526,7 +517,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re
#endif #endif
// If messages haven't been printed yet, print them // If messages haven't been printed yet, print them
if (!printed_press_any_key && serial_connected()) { if (!printed_press_any_key && serial_connected() && !autoreload_pending()) {
if (!serial_connected_at_start) { if (!serial_connected_at_start) {
print_code_py_status_message(safe_mode); print_code_py_status_message(safe_mode);
} }

View File

@ -346,8 +346,6 @@ void port_enable_tick(void) {
stm32_peripherals_rtc_assign_wkup_callback(supervisor_tick); stm32_peripherals_rtc_assign_wkup_callback(supervisor_tick);
stm32_peripherals_rtc_enable_wakeup_timer(); stm32_peripherals_rtc_enable_wakeup_timer();
} }
// TODO: what is this? can I get rid of it?
extern volatile uint32_t autoreload_delay_ms;
// Disable 1/1024 second tick. // Disable 1/1024 second tick.
void port_disable_tick(void) { void port_disable_tick(void) {

View File

@ -82,7 +82,7 @@ void displayio_background(void) {
if (mp_hal_is_interrupted()) { if (mp_hal_is_interrupted()) {
return; return;
} }
if (reload_requested) { if (autoreload_ready()) {
// Reload is about to happen, so don't redisplay. // Reload is about to happen, so don't redisplay.
return; return;
} }

View File

@ -325,8 +325,7 @@ STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) {
if (chunk_size == 0) { if (chunk_size == 0) {
// Don't reload until everything is written out of the packet buffer. // Don't reload until everything is written out of the packet buffer.
common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer); common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer);
// Trigger an autoreload autoreload_trigger();
autoreload_start();
return ANY_COMMAND; return ANY_COMMAND;
} }
@ -383,8 +382,7 @@ STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) {
#endif #endif
// Don't reload until everything is written out of the packet buffer. // Don't reload until everything is written out of the packet buffer.
common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer); common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer);
// Trigger an autoreload autoreload_trigger();
autoreload_start();
return ANY_COMMAND; return ANY_COMMAND;
} }
return WRITE_DATA; return WRITE_DATA;
@ -465,8 +463,7 @@ STATIC uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) {
if (result == FR_OK) { if (result == FR_OK) {
// Don't reload until everything is written out of the packet buffer. // Don't reload until everything is written out of the packet buffer.
common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer); common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer);
// Trigger an autoreload autoreload_trigger();
autoreload_start();
} }
return ANY_COMMAND; return ANY_COMMAND;
} }
@ -520,8 +517,7 @@ STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) {
if (result == FR_OK) { if (result == FR_OK) {
// Don't reload until everything is written out of the packet buffer. // Don't reload until everything is written out of the packet buffer.
common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer); common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer);
// Trigger an autoreload autoreload_trigger();
autoreload_start();
} }
return ANY_COMMAND; return ANY_COMMAND;
} }
@ -668,8 +664,7 @@ STATIC uint8_t _process_move(const uint8_t *raw_buf, size_t command_len) {
if (result == FR_OK) { if (result == FR_OK) {
// Don't reload until everything is written out of the packet buffer. // Don't reload until everything is written out of the packet buffer.
common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer); common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer);
// Trigger an autoreload autoreload_trigger();
autoreload_start();
} }
return ANY_COMMAND; return ANY_COMMAND;
} }

View File

@ -40,11 +40,9 @@ static bool autoreload_enabled = false;
// Non-zero if autoreload is temporarily off, due to an AUTORELOAD_SUSPEND_... reason. // Non-zero if autoreload is temporarily off, due to an AUTORELOAD_SUSPEND_... reason.
static uint32_t autoreload_suspended = 0; static uint32_t autoreload_suspended = 0;
// True if something has requested a reload/restart. volatile uint32_t last_autoreload_trigger = 0;
volatile bool reload_requested = false;
void reload_initiate(supervisor_run_reason_t run_reason) { void reload_initiate(supervisor_run_reason_t run_reason) {
reload_requested = true;
supervisor_set_run_reason(run_reason); supervisor_set_run_reason(run_reason);
// Raise reload exception, in case code is running. // Raise reload exception, in case code is running.
@ -57,12 +55,12 @@ void reload_initiate(supervisor_run_reason_t run_reason) {
} }
void autoreload_reset() { void autoreload_reset() {
reload_requested = false; last_autoreload_trigger = 0;
} }
void autoreload_enable() { void autoreload_enable() {
autoreload_enabled = true; autoreload_enabled = true;
reload_requested = false; last_autoreload_trigger = 0;
} }
void autoreload_disable() { void autoreload_disable() {
@ -81,8 +79,35 @@ inline bool autoreload_is_enabled() {
return autoreload_enabled; return autoreload_enabled;
} }
void autoreload_start() { void autoreload_trigger() {
if (autoreload_enabled && autoreload_suspended == 0) { if (autoreload_enabled) {
last_autoreload_trigger = supervisor_ticks_ms32();
// Guard against the rare time that ticks is 0;
if (last_autoreload_trigger == 0) {
last_autoreload_trigger += 1;
}
// Initiate a reload of the VM immediately. Later code will pause to
// wait for the autoreload to become ready. Doing the VM exit
// immediately is clearer for the user.
reload_initiate(RUN_REASON_AUTO_RELOAD); reload_initiate(RUN_REASON_AUTO_RELOAD);
} }
} }
bool autoreload_ready() {
if (last_autoreload_trigger == 0 || autoreload_suspended != 0) {
return false;
}
// Wait for autoreload interval before reloading
uint32_t now = supervisor_ticks_ms32();
uint32_t diff;
if (now >= last_autoreload_trigger) {
diff = now - last_autoreload_trigger;
} else {
diff = now + (0xffffffff - last_autoreload_trigger);
}
return diff > CIRCUITPY_AUTORELOAD_DELAY_MS;
}
bool autoreload_pending(void) {
return last_autoreload_trigger != 0;
}

View File

@ -42,7 +42,8 @@ enum {
enum { enum {
AUTORELOAD_SUSPEND_REPL = 0x1, AUTORELOAD_SUSPEND_REPL = 0x1,
AUTORELOAD_SUSPEND_BLE = 0x2 AUTORELOAD_SUSPEND_BLE = 0x2,
AUTORELOAD_SUSPEND_USB = 0x4
}; };
typedef struct { typedef struct {
@ -52,17 +53,29 @@ typedef struct {
extern supervisor_allocation *next_code_allocation; extern supervisor_allocation *next_code_allocation;
extern volatile bool reload_requested; // Helper for exiting the VM and reloading immediately.
void reload_initiate(supervisor_run_reason_t run_reason); void reload_initiate(supervisor_run_reason_t run_reason);
void autoreload_start(void); // Enabled state is user controllable and very sticky. We don't reset it.
void autoreload_reset(void);
void autoreload_enable(void); void autoreload_enable(void);
void autoreload_disable(void); void autoreload_disable(void);
bool autoreload_is_enabled(void); bool autoreload_is_enabled(void);
// Temporarily turn autoreload off, for the given reason(s). // Start the autoreload process.
void autoreload_trigger(void);
// True when the autoreload should occur. (A trigger happened and the delay has
// passed.)
bool autoreload_ready(void);
// Reset the autoreload timer in preparation for another trigger. Call when the
// last trigger starts being executed.
void autoreload_reset(void);
// True when a trigger has occurred but we're still delaying in case another
// trigger occurs.
bool autoreload_pending(void);
// Temporarily turn autoreload off, for the given reason(s). Autoreload triggers
// will still be tracked so resuming with autoreload ready with cause an
// immediate reload.
// Used during the REPL or during parts of BLE workflow. // Used during the REPL or during parts of BLE workflow.
void autoreload_suspend(uint32_t suspend_reason_mask); void autoreload_suspend(uint32_t suspend_reason_mask);
// Allow autoreloads again, for the given reason(s). // Allow autoreloads again, for the given reason(s).

View File

@ -185,6 +185,7 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buff
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) { int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
(void)lun; (void)lun;
(void)offset; (void)offset;
autoreload_suspend(AUTORELOAD_SUSPEND_USB);
const uint32_t block_count = bufsize / MSC_FLASH_BLOCK_SIZE; const uint32_t block_count = bufsize / MSC_FLASH_BLOCK_SIZE;
@ -215,7 +216,8 @@ void tud_msc_write10_complete_cb(uint8_t lun) {
(void)lun; (void)lun;
// This write is complete; initiate an autoreload. // This write is complete; initiate an autoreload.
autoreload_start(); autoreload_trigger();
autoreload_resume(AUTORELOAD_SUSPEND_USB);
} }
// Invoked when received SCSI_CMD_INQUIRY // Invoked when received SCSI_CMD_INQUIRY