summaryrefslogtreecommitdiff
path: root/supervisor/shared
diff options
context:
space:
mode:
authorBenjamin Shockley <benjaminshockley@hotmail.com>2021-01-03 10:41:16 -0600
committerGitHub <noreply@github.com>2021-01-03 10:41:16 -0600
commit8c8961ad0142a20793910363c8182c847b47bc58 (patch)
tree986358982921ccb6e887b0f0f5af39ec67709990 /supervisor/shared
parent0084f0af24e4e219bc040c52ee723959423de6e2 (diff)
parent80fa60d4ef6a054cc481318caba96ef8d77fc026 (diff)
Merge pull request #3 from adafruit/main
Rebase
Diffstat (limited to 'supervisor/shared')
-rw-r--r--supervisor/shared/background_callback.c139
-rw-r--r--supervisor/shared/bluetooth.c81
-rw-r--r--supervisor/shared/bluetooth.h5
-rw-r--r--supervisor/shared/board.c19
-rw-r--r--supervisor/shared/board.h14
-rw-r--r--supervisor/shared/display.c119
-rw-r--r--supervisor/shared/display.h6
-rw-r--r--supervisor/shared/external_flash/common_commands.h2
-rw-r--r--supervisor/shared/external_flash/devices.h97
-rw-r--r--supervisor/shared/external_flash/external_flash.c2
-rw-r--r--supervisor/shared/filesystem.c17
-rw-r--r--supervisor/shared/flash.c49
-rwxr-xr-xsupervisor/shared/memory.c307
-rw-r--r--supervisor/shared/micropython.c10
-rw-r--r--supervisor/shared/rgb_led_status.c63
-rw-r--r--supervisor/shared/rgb_led_status.h2
-rw-r--r--supervisor/shared/safe_mode.c152
-rw-r--r--supervisor/shared/safe_mode.h5
-rw-r--r--supervisor/shared/serial.c4
-rwxr-xr-xsupervisor/shared/stack.c38
-rwxr-xr-xsupervisor/shared/stack.h6
-rw-r--r--supervisor/shared/tick.c80
-rw-r--r--supervisor/shared/tick.h16
-rw-r--r--supervisor/shared/translate.c23
-rw-r--r--supervisor/shared/translate.h13
-rw-r--r--supervisor/shared/usb/tusb_config.h29
-rw-r--r--supervisor/shared/usb/usb.c21
-rw-r--r--supervisor/shared/workflow.c47
-rw-r--r--supervisor/shared/workflow.h32
29 files changed, 1023 insertions, 375 deletions
diff --git a/supervisor/shared/background_callback.c b/supervisor/shared/background_callback.c
new file mode 100644
index 000000000..ef686cbab
--- /dev/null
+++ b/supervisor/shared/background_callback.c
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 <string.h>
+
+#include "py/gc.h"
+#include "py/mpconfig.h"
+#include "supervisor/background_callback.h"
+#include "supervisor/linker.h"
+#include "supervisor/shared/tick.h"
+#include "shared-bindings/microcontroller/__init__.h"
+
+STATIC volatile background_callback_t *callback_head, *callback_tail;
+
+#define CALLBACK_CRITICAL_BEGIN (common_hal_mcu_disable_interrupts())
+#define CALLBACK_CRITICAL_END (common_hal_mcu_enable_interrupts())
+
+void background_callback_add_core(background_callback_t *cb) {
+ CALLBACK_CRITICAL_BEGIN;
+ if (cb->prev || callback_head == cb) {
+ CALLBACK_CRITICAL_END;
+ return;
+ }
+ cb->next = 0;
+ cb->prev = (background_callback_t*)callback_tail;
+ if (callback_tail) {
+ callback_tail->next = cb;
+ cb->prev = (background_callback_t*)callback_tail;
+ }
+ if (!callback_head) {
+ callback_head = cb;
+ }
+ callback_tail = cb;
+ CALLBACK_CRITICAL_END;
+}
+
+void background_callback_add(background_callback_t *cb, background_callback_fun fun, void *data) {
+ cb->fun = fun;
+ cb->data = data;
+ background_callback_add_core(cb);
+}
+
+static bool in_background_callback;
+void PLACE_IN_ITCM(background_callback_run_all)() {
+ if (!callback_head) {
+ return;
+ }
+ CALLBACK_CRITICAL_BEGIN;
+ if (in_background_callback) {
+ CALLBACK_CRITICAL_END;
+ return;
+ }
+ in_background_callback = true;
+ background_callback_t *cb = (background_callback_t*)callback_head;
+ callback_head = NULL;
+ callback_tail = NULL;
+ while (cb) {
+ background_callback_t *next = cb->next;
+ cb->next = cb->prev = NULL;
+ background_callback_fun fun = cb->fun;
+ void *data = cb->data;
+ CALLBACK_CRITICAL_END;
+ // Leave the critical section in order to run the callback function
+ if (fun) {
+ fun(data);
+ }
+ CALLBACK_CRITICAL_BEGIN;
+ cb = next;
+ }
+ in_background_callback = false;
+ CALLBACK_CRITICAL_END;
+}
+
+void background_callback_begin_critical_section() {
+ CALLBACK_CRITICAL_BEGIN;
+}
+
+void background_callback_end_critical_section() {
+ CALLBACK_CRITICAL_END;
+}
+
+void background_callback_reset() {
+ CALLBACK_CRITICAL_BEGIN;
+ background_callback_t *cb = (background_callback_t*)callback_head;
+ while(cb) {
+ background_callback_t *next = cb->next;
+ memset(cb, 0, sizeof(*cb));
+ cb = next;
+ }
+ callback_head = NULL;
+ callback_tail = NULL;
+ in_background_callback = false;
+ CALLBACK_CRITICAL_END;
+}
+
+void background_callback_gc_collect(void) {
+ // We don't enter the callback critical section here. We rely on
+ // gc_collect_ptr _NOT_ entering background callbacks, so it is not
+ // possible for the list to be cleared.
+ //
+ // However, it is possible for the list to be extended. We make the
+ // minor assumption that no newly added callback is for a
+ // collectable object. That is, we only plug the hole where an
+ // object becomes collectable AFTER it is added but before the
+ // callback is run, not the hole where an object was ALREADY
+ // collectable but adds a background task for itself.
+ //
+ // It's necessary to traverse the whole list here, as the callbacks
+ // themselves can be in non-gc memory, and some of the cb->data
+ // objects themselves might be in non-gc memory.
+ background_callback_t *cb = (background_callback_t*)callback_head;
+ while(cb) {
+ gc_collect_ptr(cb->data);
+ cb = cb->next;
+ }
+}
diff --git a/supervisor/shared/bluetooth.c b/supervisor/shared/bluetooth.c
index 98d0fab38..56bf32117 100644
--- a/supervisor/shared/bluetooth.c
+++ b/supervisor/shared/bluetooth.c
@@ -24,6 +24,15 @@
* THE SOFTWARE.
*/
+#if !CIRCUITPY_BLE_FILE_SERVICE
+void supervisor_start_bluetooth(void) {
+}
+
+void supervisor_bluetooth_background(void) {
+}
+
+#else
+
#include <string.h>
#include "extmod/vfs.h"
@@ -41,6 +50,8 @@
#include "py/mpstate.h"
+
+
bleio_service_obj_t supervisor_ble_service;
bleio_uuid_obj_t supervisor_ble_service_uuid;
bleio_characteristic_obj_t supervisor_ble_version_characteristic;
@@ -63,10 +74,7 @@ mp_obj_t service_list_items[1];
mp_obj_list_t characteristic_list;
mp_obj_t characteristic_list_items[4];
-void supervisor_bluetooth_start_advertising(void) {
- #if !CIRCUITPY_BLE_FILE_SERVICE
- return;
- #endif
+STATIC void supervisor_bluetooth_start_advertising(void) {
bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj);
if (is_connected) {
return;
@@ -83,10 +91,6 @@ void supervisor_bluetooth_start_advertising(void) {
}
void supervisor_start_bluetooth(void) {
- #if !CIRCUITPY_BLE_FILE_SERVICE
- return;
- #endif
-
common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true);
supervisor_ble_service_uuid.base.type = &bleio_uuid_type;
@@ -177,7 +181,7 @@ volatile bool new_filename;
volatile bool run_ble_background;
bool was_connected;
-void update_file_length(void) {
+STATIC void update_file_length(void) {
int32_t file_length = -1;
mp_buffer_info_t bufinfo;
bufinfo.buf = &file_length;
@@ -188,7 +192,7 @@ void update_file_length(void) {
common_hal_bleio_characteristic_set_value(&supervisor_ble_length_characteristic, &bufinfo);
}
-void open_current_file(void) {
+STATIC void open_current_file(void) {
if (active_file.obj.fs != 0) {
return;
}
@@ -203,7 +207,7 @@ void open_current_file(void) {
update_file_length();
}
-void close_current_file(void) {
+STATIC void close_current_file(void) {
f_close(&active_file);
}
@@ -211,9 +215,6 @@ uint32_t current_command[1024 / sizeof(uint32_t)];
volatile size_t current_offset;
void supervisor_bluetooth_background(void) {
- #if !CIRCUITPY_BLE_FILE_SERVICE
- return;
- #endif
if (!run_ble_background) {
return;
}
@@ -305,54 +306,4 @@ void supervisor_bluetooth_background(void) {
}
}
-// This happens in an interrupt so we need to be quick.
-bool supervisor_bluetooth_hook(ble_evt_t *ble_evt) {
- #if !CIRCUITPY_BLE_FILE_SERVICE
- return false;
- #endif
- // Catch writes to filename or contents. Length is read-only.
-
- bool done = false;
- switch (ble_evt->header.evt_id) {
- case BLE_GAP_EVT_CONNECTED:
- // We run our background task even if it wasn't us connected to because we may want to
- // advertise if the user code stopped advertising.
- run_ble_background = true;
- break;
- case BLE_GAP_EVT_DISCONNECTED:
- run_ble_background = true;
- break;
- case BLE_GATTS_EVT_WRITE: {
- // A client wrote to a characteristic.
-
- ble_gatts_evt_write_t *evt_write = &ble_evt->evt.gatts_evt.params.write;
- // Event handle must match the handle for my characteristic.
- if (evt_write->handle == supervisor_ble_contents_characteristic.handle) {
- // Handle events
- //write_to_ringbuf(self, evt_write->data, evt_write->len);
- // First packet includes a uint16_t le for length at the start.
- uint16_t current_length = ((uint16_t*) current_command)[0];
- memcpy(((uint8_t*) current_command) + current_offset, evt_write->data, evt_write->len);
- current_offset += evt_write->len;
- current_length = ((uint16_t*) current_command)[0];
- if (current_offset == current_length) {
- run_ble_background = true;
- done = true;
- }
- } else if (evt_write->handle == supervisor_ble_filename_characteristic.handle) {
- new_filename = true;
- run_ble_background = true;
- done = true;
- } else {
- return done;
- }
- break;
- }
-
- default:
- // For debugging.
- // mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id);
- break;
- }
- return done;
-}
+#endif // #else
diff --git a/supervisor/shared/bluetooth.h b/supervisor/shared/bluetooth.h
index 1fb6a879a..55f9c86fa 100644
--- a/supervisor/shared/bluetooth.h
+++ b/supervisor/shared/bluetooth.h
@@ -27,8 +27,7 @@
#ifndef MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H
#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H
-void supervisor_start_bluetooth(void);
-bool supervisor_bluetooth_hook(ble_evt_t *ble_evt);
void supervisor_bluetooth_background(void);
+void supervisor_start_bluetooth(void);
-#endif
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H
diff --git a/supervisor/shared/board.c b/supervisor/shared/board.c
index e3eb8fd0d..30603aa66 100644
--- a/supervisor/shared/board.c
+++ b/supervisor/shared/board.c
@@ -26,23 +26,22 @@
#include "supervisor/shared/board.h"
-#include "shared-bindings/digitalio/DigitalInOut.h"
-#include "shared-bindings/neopixel_write/__init__.h"
+#if CIRCUITPY_DIGITALIO && CIRCUITPY_NEOPIXEL_WRITE
-#ifdef USER_NEOPIXELS_PIN
+#include <string.h>
-// The maximum number of user neopixels right now is 10, on Circuit Playgrounds.
-// PyBadge and PyGamer have max 5
-#define USER_NEOPIXELS_MAX_COUNT 10
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/neopixel_write/__init__.h"
-void board_reset_user_neopixels(void) {
+void board_reset_user_neopixels(const mcu_pin_obj_t* pin, size_t count) {
// Turn off on-board NeoPixel string
- uint8_t empty[USER_NEOPIXELS_MAX_COUNT * 3] = { 0 };
+ uint8_t empty[count * 3];
+ memset(empty, 0, count);
digitalio_digitalinout_obj_t neopixel_pin;
- common_hal_digitalio_digitalinout_construct(&neopixel_pin, USER_NEOPIXELS_PIN);
+ common_hal_digitalio_digitalinout_construct(&neopixel_pin, pin);
common_hal_digitalio_digitalinout_switch_to_output(&neopixel_pin, false,
DRIVE_MODE_PUSH_PULL);
- common_hal_neopixel_write(&neopixel_pin, empty, USER_NEOPIXELS_MAX_COUNT * 3);
+ common_hal_neopixel_write(&neopixel_pin, empty, count * 3);
common_hal_digitalio_digitalinout_deinit(&neopixel_pin);
}
diff --git a/supervisor/shared/board.h b/supervisor/shared/board.h
index 0e4d73455..fe887a933 100644
--- a/supervisor/shared/board.h
+++ b/supervisor/shared/board.h
@@ -24,15 +24,13 @@
* THE SOFTWARE.
*/
-#ifndef MICROPY_INCLUDED_SUPERVISOR_BOARD_H
-#define MICROPY_INCLUDED_SUPERVISOR_BOARD_H
+#ifndef MICROPY_INCLUDED_SUPERVISOR_SHARED_BOARD_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BOARD_H
-#include "py/mpconfig.h"
+#include <stddef.h>
-#ifdef USER_NEOPIXELS_PIN
+#include "shared-bindings/microcontroller/Pin.h"
-void board_reset_user_neopixels(void);
+void board_reset_user_neopixels(const mcu_pin_obj_t* pin, size_t count);
-#endif
-
-#endif // MICROPY_INCLUDED_SUPERVISOR_BOARD_H
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BOARD_H
diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c
index 9c074209b..9c9c66cd7 100644
--- a/supervisor/shared/display.c
+++ b/supervisor/shared/display.c
@@ -29,6 +29,7 @@
#include <string.h>
#include "py/mpstate.h"
+#include "shared-bindings/displayio/Bitmap.h"
#include "shared-bindings/displayio/Group.h"
#include "shared-bindings/displayio/Palette.h"
#include "shared-bindings/displayio/TileGrid.h"
@@ -38,93 +39,119 @@
#include "shared-module/displayio/__init__.h"
#endif
+#if CIRCUITPY_SHARPDISPLAY
+#include "shared-module/displayio/__init__.h"
+#include "shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h"
+#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h"
+#endif
+
extern size_t blinka_bitmap_data[];
extern displayio_bitmap_t blinka_bitmap;
extern displayio_group_t circuitpython_splash;
+#if CIRCUITPY_TERMINALIO
static supervisor_allocation* tilegrid_tiles = NULL;
+#endif
void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
+ // Default the scale to 2 because we may show blinka without the terminal for
+ // languages that don't have font support.
+ uint8_t scale = 2;
+
+ #if CIRCUITPY_TERMINALIO
displayio_tilegrid_t* grid = &supervisor_terminal_text_grid;
- uint16_t width_in_tiles = (width_px - blinka_bitmap.width) / grid->tile_width;
+ bool tall = height_px > width_px;
+ uint16_t terminal_width_px = tall ? width_px : width_px - blinka_bitmap.width;
+ uint16_t terminal_height_px = tall ? height_px - blinka_bitmap.height : height_px ;
+ uint16_t width_in_tiles = terminal_width_px / grid->tile_width;
// determine scale based on h
- uint8_t scale = 1;
- if (width_in_tiles > 80) {
- scale = 2;
+ if (width_in_tiles < 80) {
+ scale = 1;
}
- width_in_tiles = (width_px - blinka_bitmap.width * scale) / (grid->tile_width * scale);
- uint16_t height_in_tiles = height_px / (grid->tile_height * scale);
- uint16_t remaining_pixels = height_px % (grid->tile_height * scale);
- if (remaining_pixels > 0) {
+
+ width_in_tiles = terminal_width_px / (grid->tile_width * scale);
+ if (width_in_tiles < 1) {
+ width_in_tiles = 1;
+ }
+ uint16_t height_in_tiles = terminal_height_px / (grid->tile_height * scale);
+ uint16_t remaining_pixels = tall ? 0 : terminal_height_px % (grid->tile_height * scale);
+ if (height_in_tiles < 1 || remaining_pixels > 0) {
height_in_tiles += 1;
}
- circuitpython_splash.scale = scale;
uint16_t total_tiles = width_in_tiles * height_in_tiles;
- // First try to allocate outside the heap. This will fail when the VM is running.
- tilegrid_tiles = allocate_memory(align32_size(total_tiles), false);
- uint8_t* tiles;
- if (tilegrid_tiles == NULL) {
- tiles = m_malloc(total_tiles, true);
- MP_STATE_VM(terminal_tilegrid_tiles) = tiles;
- } else {
- tiles = (uint8_t*) tilegrid_tiles->ptr;
+ // Reuse the previous allocation if possible
+ if (tilegrid_tiles) {
+ if (get_allocation_length(tilegrid_tiles) != align32_size(total_tiles)) {
+ free_memory(tilegrid_tiles);
+ tilegrid_tiles = NULL;
+ }
}
-
- if (tiles == NULL) {
- return;
+ if (!tilegrid_tiles) {
+ tilegrid_tiles = allocate_memory(align32_size(total_tiles), false, true);
+ if (!tilegrid_tiles) {
+ return;
+ }
}
- grid->y = 0;
+ uint8_t* tiles = (uint8_t*) tilegrid_tiles->ptr;
+
+ grid->y = tall ? blinka_bitmap.height : 0;
+ grid->x = tall ? 0 : blinka_bitmap.width;
grid->top_left_y = 0;
if (remaining_pixels > 0) {
grid->y -= (grid->tile_height - remaining_pixels);
}
grid->width_in_tiles = width_in_tiles;
grid->height_in_tiles = height_in_tiles;
+ assert(width_in_tiles > 0);
+ assert(height_in_tiles > 0);
grid->pixel_width = width_in_tiles * grid->tile_width;
grid->pixel_height = height_in_tiles * grid->tile_height;
grid->tiles = tiles;
grid->full_change = true;
common_hal_terminalio_terminal_construct(&supervisor_terminal, grid, &supervisor_terminal_font);
+ #endif
+
+ circuitpython_splash.scale = scale;
}
void supervisor_stop_terminal(void) {
+ #if CIRCUITPY_TERMINALIO
if (tilegrid_tiles != NULL) {
free_memory(tilegrid_tiles);
tilegrid_tiles = NULL;
- supervisor_terminal_text_grid.inline_tiles = false;
supervisor_terminal_text_grid.tiles = NULL;
}
+ #endif
}
void supervisor_display_move_memory(void) {
- #if CIRCUITPY_DISPLAYIO
- displayio_tilegrid_t* grid = &supervisor_terminal_text_grid;
- if (MP_STATE_VM(terminal_tilegrid_tiles) == NULL || grid->tiles != MP_STATE_VM(terminal_tilegrid_tiles)) {
- return;
- }
- uint16_t total_tiles = grid->width_in_tiles * grid->height_in_tiles;
-
- tilegrid_tiles = allocate_memory(align32_size(total_tiles), false);
+ #if CIRCUITPY_TERMINALIO
if (tilegrid_tiles != NULL) {
- memcpy(tilegrid_tiles->ptr, grid->tiles, total_tiles);
- grid->tiles = (uint8_t*) tilegrid_tiles->ptr;
+ supervisor_terminal_text_grid.tiles = (uint8_t*) tilegrid_tiles->ptr;
} else {
- grid->tiles = NULL;
- grid->inline_tiles = false;
+ supervisor_terminal_text_grid.tiles = NULL;
}
- MP_STATE_VM(terminal_tilegrid_tiles) = NULL;
- #if CIRCUITPY_RGBMATRIX
+ #endif
+
+ #if CIRCUITPY_DISPLAYIO
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
- if (displays[i].rgbmatrix.base.type == &rgbmatrix_RGBMatrix_type) {
- rgbmatrix_rgbmatrix_obj_t * pm = &displays[i].rgbmatrix;
+ #if CIRCUITPY_RGBMATRIX
+ if (displays[i].rgbmatrix.base.type == &rgbmatrix_RGBMatrix_type) {
+ rgbmatrix_rgbmatrix_obj_t * pm = &displays[i].rgbmatrix;
common_hal_rgbmatrix_rgbmatrix_reconstruct(pm, NULL);
- }
+ }
+ #endif
+ #if CIRCUITPY_SHARPDISPLAY
+ if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) {
+ sharpdisplay_framebuffer_obj_t * sharp = &displays[i].sharpdisplay;
+ common_hal_sharpdisplay_framebuffer_reconstruct(sharp);
+ }
+ #endif
}
#endif
- #endif
}
size_t blinka_bitmap_data[32] = {
@@ -242,18 +269,26 @@ displayio_tilegrid_t blinka_sprite = {
.in_group = true
};
+#if CIRCUITPY_TERMINALIO
+#define CHILD_COUNT 2
displayio_group_child_t splash_children[2] = {
{&blinka_sprite, &blinka_sprite},
{&supervisor_terminal_text_grid, &supervisor_terminal_text_grid}
};
+#else
+#define CHILD_COUNT 1
+displayio_group_child_t splash_children[1] = {
+ {&blinka_sprite, &blinka_sprite},
+};
+#endif
displayio_group_t circuitpython_splash = {
.base = {.type = &displayio_group_type },
.x = 0,
.y = 0,
.scale = 2,
- .size = 2,
- .max_size = 2,
+ .size = CHILD_COUNT,
+ .max_size = CHILD_COUNT,
.children = splash_children,
.item_removed = false,
.in_group = false,
diff --git a/supervisor/shared/display.h b/supervisor/shared/display.h
index 2a2ccf46d..4110cfe8e 100644
--- a/supervisor/shared/display.h
+++ b/supervisor/shared/display.h
@@ -27,6 +27,10 @@
#ifndef MICROPY_INCLUDED_SUPERVISOR_SHARED_DISPLAY_H
#define MICROPY_INCLUDED_SUPERVISOR_SHARED_DISPLAY_H
+#include <stdint.h>
+
+#if CIRCUITPY_TERMINALIO
+
#include "shared-bindings/displayio/Bitmap.h"
#include "shared-bindings/displayio/TileGrid.h"
#include "shared-bindings/fontio/BuiltinFont.h"
@@ -42,6 +46,8 @@ extern displayio_bitmap_t supervisor_terminal_font_bitmap;
extern displayio_tilegrid_t supervisor_terminal_text_grid;
extern terminalio_terminal_obj_t supervisor_terminal;
+#endif
+
void supervisor_start_terminal(uint16_t width_px, uint16_t height_px);
void supervisor_stop_terminal(void);
diff --git a/supervisor/shared/external_flash/common_commands.h b/supervisor/shared/external_flash/common_commands.h
index 2eaa84833..37efd8ceb 100644
--- a/supervisor/shared/external_flash/common_commands.h
+++ b/supervisor/shared/external_flash/common_commands.h
@@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
- * Copyright (c) 2013, 2014 Damien P. George
+ * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 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
diff --git a/supervisor/shared/external_flash/devices.h b/supervisor/shared/external_flash/devices.h
index 466ab49eb..c8c941adf 100644
--- a/supervisor/shared/external_flash/devices.h
+++ b/supervisor/shared/external_flash/devices.h
@@ -94,6 +94,26 @@ typedef struct {
.single_status_byte = false, \
}
+// Settings for the Adesto Tech AT25DF641-SSHD-T 8MiB SPI flash
+// for the Oak Dev Tech Icy Tree M0 (SAMD21) feather board.
+// Source: https://www.digikey.com/product-detail/en/adesto-technologies/AT25SF641-SDHD-T/1265-1180-1-ND/
+// Datasheet: https://www.adestotech.com/wp-content/uploads/doc8693.pdf
+#define AT25DF641A {\
+ .total_size = (1 << 23), /* 8 MiB */ \
+ .start_up_time_us = 10000, \
+ .manufacturer_id = 0x1f, \
+ .memory_type = 0x48, \
+ .capacity = 0x00, \
+ .max_clock_speed_mhz = 85, \
+ .quad_enable_bit_mask = 0x00, \
+ .has_sector_protection = true, \
+ .supports_fast_read = true, \
+ .supports_qspi = false, \
+ .supports_qspi_writes = false, \
+ .write_status_register_split = false, \
+ .single_status_byte = false, \
+}
+
// Settings for the Adesto Tech AT25SF161-SSHD-T 2MiB SPI flash
// for the StringCar M0 (SAMD21) Express board.
// Source: https://www.digikey.com/product-detail/en/adesto-technologies/AT25SF161-SDHD-T/1265-1230-1-ND/
@@ -114,6 +134,7 @@ typedef struct {
.single_status_byte = false, \
}
+
// Settings for the Adesto Tech AT25SF041 1MiB SPI flash. It's on the SparkFun
// SAMD51 Thing Plus board
// Datasheet: https://www.adestotech.com/wp-content/uploads/DS-AT25SF041_044.pdf
@@ -363,6 +384,24 @@ typedef struct {
.write_status_register_split = false, \
}
+// Settings for the Winbond W25Q64FV 8MiB SPI flash.
+// Datasheet: https://www.winbond.com/resource-files/w25q64fv%20revs%2007182017.pdf
+#define W25Q64FV {\
+ .total_size = (1 << 23), /* 8 MiB */ \
+ .start_up_time_us = 5000, \
+ .manufacturer_id = 0xef, \
+ .memory_type = 0x40, \
+ .capacity = 0x17, \
+ .max_clock_speed_mhz = 104, \
+ .quad_enable_bit_mask = 0x02, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = false, \
+ .single_status_byte = false, \
+}
+
// Settings for the Winbond W25Q64JV-IM 8MiB SPI flash. Note that JV-IQ has a different .memory_type (0x40)
// Datasheet: http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf
#define W25Q64JV_IM {\
@@ -537,7 +576,7 @@ typedef struct {
.start_up_time_us = 800, \
.manufacturer_id = 0xc2, \
.memory_type = 0x28, \
- .capacity = 0x18, \
+ .capacity = 0x15, \
.max_clock_speed_mhz = 33, /* 8 mhz for dual/quad */ \
.quad_enable_bit_mask = 0x80, \
.has_sector_protection = false, \
@@ -566,6 +605,43 @@ typedef struct {
.single_status_byte = true, \
}
+// Settings for the Macronix MX25L51245G 64MiB SPI flash.
+// Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/7437/MX25L51245G,%203V,%20512Mb,%20v1.6.pdf
+#define MX25L25645G {\
+ .total_size = (1 << 25), /* 32 MiB */ \
+ .start_up_time_us = 5000, \
+ .manufacturer_id = 0x9f, \
+ .memory_type = 0xab, \
+ .capacity = 0x90, \
+ .max_clock_speed_mhz = 133, \
+ .quad_enable_bit_mask = 0xaf, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = false, \
+ .single_status_byte = true, \
+}
+
+// Settings for the Macronix MX25L12833F 16MiB SPI flash
+// Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/7447/MX25L12833F,%203V,%20128Mb,%20v1.0.pdf
+
+#define MX25L12833F {\
+ .total_size = (1UL << 24), /* 16 MiB */ \
+ .start_up_time_us = 5000, \
+ .manufacturer_id = 0xc2, \
+ .memory_type = 0x20, \
+ .capacity = 0x18, \
+ .max_clock_speed_mhz = 133, \
+ .quad_enable_bit_mask = 0x40, \
+ .has_sector_protection = true, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = false, \
+ .single_status_byte = true, \
+ }
+
// Settings for the Winbond W25Q128JV-PM 16MiB SPI flash. Note that JV-IM has a different .memory_type (0x70)
// Datasheet: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf
#define W25Q128JV_PM {\
@@ -618,4 +694,23 @@ typedef struct {
.write_status_register_split = false, \
.single_status_byte = true, \
}
+
+// Settings for the Micron N25Q256A 256Mb (32MiB) QSPI flash.
+// Datasheet: https://www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_256mb_3v.pdf
+#define N25Q256A {\
+ /* .total_size = (1 << 25), 32 MiB does not work at this time, as assumptions about 3-byte addresses abound */ \
+ .total_size = (1 << 24), /* 16 MiB */ \
+ .start_up_time_us = 10000, \
+ .manufacturer_id = 0x20, \
+ .memory_type = 0xBA, \
+ .capacity = 0x19, \
+ .max_clock_speed_mhz = 108, \
+ .quad_enable_bit_mask = 0x02, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = false, \
+ .single_status_byte = true, \
+}
#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H
diff --git a/supervisor/shared/external_flash/external_flash.c b/supervisor/shared/external_flash/external_flash.c
index 5bde7fd48..e2d767235 100644
--- a/supervisor/shared/external_flash/external_flash.c
+++ b/supervisor/shared/external_flash/external_flash.c
@@ -338,7 +338,7 @@ static bool allocate_ram_cache(void) {
uint32_t table_size = blocks_per_sector * pages_per_block * sizeof(uint32_t);
// Attempt to allocate outside the heap first.
- supervisor_cache = allocate_memory(table_size + SPI_FLASH_ERASE_SIZE, false);
+ supervisor_cache = allocate_memory(table_size + SPI_FLASH_ERASE_SIZE, false, false);
if (supervisor_cache != NULL) {
MP_STATE_VM(flash_ram_cache) = (uint8_t **) supervisor_cache->ptr;
uint8_t* page_start = (uint8_t *) supervisor_cache->ptr + table_size;
diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c
index f6b94e38b..88603be0c 100644
--- a/supervisor/shared/filesystem.c
+++ b/supervisor/shared/filesystem.c
@@ -105,13 +105,19 @@ void filesystem_init(bool create_allowed, bool force_create) {
// set label
#ifdef CIRCUITPY_DRIVE_LABEL
- f_setlabel(&vfs_fat->fatfs, CIRCUITPY_DRIVE_LABEL);
+ res = f_setlabel(&vfs_fat->fatfs, CIRCUITPY_DRIVE_LABEL);
#else
- f_setlabel(&vfs_fat->fatfs, "CIRCUITPY");
+ res = f_setlabel(&vfs_fat->fatfs, "CIRCUITPY");
#endif
+ if (res != FR_OK) {
+ return;
+ }
// inhibit file indexing on MacOS
- f_mkdir(&vfs_fat->fatfs, "/.fseventsd");
+ res = f_mkdir(&vfs_fat->fatfs, "/.fseventsd");
+ if (res != FR_OK) {
+ return;
+ }
make_empty_file(&vfs_fat->fatfs, "/.metadata_never_index");
make_empty_file(&vfs_fat->fatfs, "/.Trashes");
make_empty_file(&vfs_fat->fatfs, "/.fseventsd/no_log");
@@ -119,7 +125,10 @@ void filesystem_init(bool create_allowed, bool force_create) {
make_sample_code_file(&vfs_fat->fatfs);
// create empty lib directory
- f_mkdir(&vfs_fat->fatfs, "/lib");
+ res = f_mkdir(&vfs_fat->fatfs, "/lib");
+ if (res != FR_OK) {
+ return;
+ }
// and ensure everything is flushed
supervisor_flash_flush();
diff --git a/supervisor/shared/flash.c b/supervisor/shared/flash.c
index 298e7d83e..1e09fe14b 100644
--- a/supervisor/shared/flash.c
+++ b/supervisor/shared/flash.c
@@ -88,9 +88,6 @@ static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_blo
mp_uint_t flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
if (block_num == 0) {
- if (block_num > 1) {
- return 1; // error
- }
// fake the MBR so we can decide on our own partition table
for (int i = 0; i < 446; i++) {
@@ -104,9 +101,13 @@ mp_uint_t flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_bloc
dest[510] = 0x55;
dest[511] = 0xaa;
-
- return 0; // ok
-
+ if (num_blocks > 1) {
+ dest += 512;
+ num_blocks -= 1;
+ // Fall through and do a read from flash.
+ } else {
+ return 0; // Done and ok.
+ }
}
return supervisor_flash_read_blocks(dest, block_num - PART1_START_BLOCK, num_blocks);
}
@@ -159,16 +160,37 @@ STATIC mp_obj_t supervisor_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_n
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_writeblocks_obj, supervisor_flash_obj_writeblocks);
+bool flash_ioctl(size_t cmd, mp_int_t* out_value) {
+ *out_value = 0;
+ switch (cmd) {
+ case BP_IOCTL_INIT:
+ supervisor_flash_init();
+ break;
+ case BP_IOCTL_DEINIT:
+ supervisor_flash_flush();
+ break; // TODO properly
+ case BP_IOCTL_SYNC:
+ supervisor_flash_flush();
+ break;
+ case BP_IOCTL_SEC_COUNT:
+ *out_value = flash_get_block_count();
+ break;
+ case BP_IOCTL_SEC_SIZE:
+ *out_value = supervisor_flash_get_block_size();
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
STATIC mp_obj_t supervisor_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) {
mp_int_t cmd = mp_obj_get_int(cmd_in);
- switch (cmd) {
- case BP_IOCTL_INIT: supervisor_flash_init(); return MP_OBJ_NEW_SMALL_INT(0);
- case BP_IOCTL_DEINIT: supervisor_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly
- case BP_IOCTL_SYNC: supervisor_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0);
- case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(flash_get_block_count());
- case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(supervisor_flash_get_block_size());
- default: return mp_const_none;
+ mp_int_t out_value;
+ if (flash_ioctl(cmd, &out_value)) {
+ return MP_OBJ_NEW_SMALL_INT(out_value);
}
+ return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_ioctl_obj, supervisor_flash_obj_ioctl);
@@ -200,4 +222,5 @@ void supervisor_flash_init_vfs(fs_user_mount_t *vfs) {
vfs->writeblocks[2] = (mp_obj_t)flash_write_blocks; // native version
vfs->u.ioctl[0] = (mp_obj_t)&supervisor_flash_obj_ioctl_obj;
vfs->u.ioctl[1] = (mp_obj_t)&supervisor_flash_obj;
+ vfs->u.ioctl[2] = (mp_obj_t)flash_ioctl; // native version
}
diff --git a/supervisor/shared/memory.c b/supervisor/shared/memory.c
index 8ae8a1699..480c322b0 100755
--- a/supervisor/shared/memory.c
+++ b/supervisor/shared/memory.c
@@ -27,64 +27,111 @@
#include "supervisor/memory.h"
#include "supervisor/port.h"
-#include <stddef.h>
+#include <string.h>
+#include "py/gc.h"
#include "supervisor/shared/display.h"
-#define CIRCUITPY_SUPERVISOR_ALLOC_COUNT (12)
+enum {
+ CIRCUITPY_SUPERVISOR_IMMOVABLE_ALLOC_COUNT =
+ // stack + heap
+ 2
+#ifdef EXTERNAL_FLASH_DEVICES
+ + 1
+#endif
+#if CIRCUITPY_USB_MIDI
+ + 1
+#endif
+ ,
+ CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT =
+ 0
+#if CIRCUITPY_DISPLAYIO
+ #if CIRCUITPY_TERMINALIO
+ + 1
+ #endif
+ + CIRCUITPY_DISPLAY_LIMIT * (
+ // Maximum needs of one display: max(4 if RGBMATRIX, 1 if SHARPDISPLAY, 0)
+ #if CIRCUITPY_RGBMATRIX
+ 4
+ #elif CIRCUITPY_SHARPDISPLAY
+ 1
+ #else
+ 0
+ #endif
+ )
+#endif
+ ,
+ CIRCUITPY_SUPERVISOR_ALLOC_COUNT = CIRCUITPY_SUPERVISOR_IMMOVABLE_ALLOC_COUNT + CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT
+};
+
+// The lowest two bits of a valid length are always zero, so we can use them to mark an allocation
+// as a hole (freed by the client but not yet reclaimed into the free middle) and as movable.
+#define FLAGS 3
+#define HOLE 1
+#define MOVABLE 2
static supervisor_allocation allocations[CIRCUITPY_SUPERVISOR_ALLOC_COUNT];
-// We use uint32_t* to ensure word (4 byte) alignment.
-uint32_t* low_address;
-uint32_t* high_address;
+supervisor_allocation* old_allocations;
-void memory_init(void) {
- low_address = port_heap_get_bottom();
- high_address = port_heap_get_top();
-}
+typedef struct _supervisor_allocation_node {
+ struct _supervisor_allocation_node* next;
+ size_t length;
+ // We use uint32_t to ensure word (4 byte) alignment.
+ uint32_t data[];
+} supervisor_allocation_node;
+
+supervisor_allocation_node* low_head;
+supervisor_allocation_node* high_head;
+
+// Intermediate (void*) is to suppress -Wcast-align warning. Alignment will always be correct
+// because this only reverses how (alloc)->ptr was obtained as &(node->data[0]).
+#define ALLOCATION_NODE(alloc) ((supervisor_allocation_node*)(void*)((char*)((alloc)->ptr) - sizeof(supervisor_allocation_node)))
void free_memory(supervisor_allocation* allocation) {
- if (allocation == NULL) {
+ if (allocation == NULL || allocation->ptr == NULL) {
return;
}
- int32_t index = 0;
- bool found = false;
- for (index = 0; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
- found = allocation == &allocations[index];
- if (found) {
- break;
- }
+ supervisor_allocation_node* node = ALLOCATION_NODE(allocation);
+ if (node == low_head) {
+ do {
+ low_head = low_head->next;
+ } while (low_head != NULL && (low_head->length & HOLE));
}
- if (!found) {
- // Bad!
- // TODO(tannewt): Add a way to escape into safe mode on error.
+ else if (node == high_head) {
+ do {
+ high_head = high_head->next;
+ } while (high_head != NULL && (high_head->length & HOLE));
}
- if (allocation->ptr == high_address) {
- high_address += allocation->length / 4;
- for (index++; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
- if (allocations[index].ptr != NULL) {
- break;
- }
- high_address += allocations[index].length / 4;
+ else {
+ // Check if it's in the list of embedded allocations.
+ supervisor_allocation_node** emb = &MP_STATE_VM(first_embedded_allocation);
+ while (*emb != NULL && *emb != node) {
+ emb = &((*emb)->next);
}
- } else if (allocation->ptr + allocation->length / 4 == low_address) {
- low_address = allocation->ptr;
- for (index--; index >= 0; index--) {
- if (allocations[index].ptr != NULL) {
- break;
- }
- low_address -= allocations[index].length / 4;
+ if (*emb != NULL) {
+ // Found, remove it from the list.
+ *emb = node->next;
+ m_free(node
+#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
+ , sizeof(supervisor_allocation_node) + (node->length & ~FLAGS)
+#endif
+ );
+ }
+ else {
+ // Else it must be within the low or high ranges and becomes a hole.
+ node->length = ((node->length & ~FLAGS) | HOLE);
}
- } else {
- // Freed memory isn't in the middle so skip updating bounds. The memory will be added to the
- // middle when the memory to the inside is freed.
}
allocation->ptr = NULL;
}
supervisor_allocation* allocation_from_ptr(void *ptr) {
+ // When called from the context of supervisor_move_memory() (old_allocations != NULL), search
+ // by old pointer to give clients a way of mapping from old to new pointer. But not if
+ // ptr == NULL, then the caller wants an allocation whose current ptr is NULL.
+ supervisor_allocation* list = (old_allocations && ptr) ? old_allocations : &allocations[0];
for (size_t index = 0; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
- if (allocations[index].ptr == ptr) {
+ if (list[index].ptr == ptr) {
return &allocations[index];
}
}
@@ -92,44 +139,182 @@ supervisor_allocation* allocation_from_ptr(void *ptr) {
}
supervisor_allocation* allocate_remaining_memory(void) {
- if (low_address == high_address) {
- return NULL;
+ return allocate_memory((uint32_t)-1, false, false);
+}
+
+static supervisor_allocation_node* find_hole(supervisor_allocation_node* node, size_t length) {
+ for (; node != NULL; node = node->next) {
+ if (node->length == (length | HOLE)) {
+ break;
+ }
}
- return allocate_memory((high_address - low_address) * 4, false);
+ return node;
}
-supervisor_allocation* allocate_memory(uint32_t length, bool high) {
- if ((high_address - low_address) * 4 < (int32_t) length || length % 4 != 0) {
- return NULL;
+static supervisor_allocation_node* allocate_memory_node(uint32_t length, bool high, bool movable) {
+ if (CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT == 0) {
+ assert(!movable);
}
- uint8_t index = 0;
- int8_t direction = 1;
- if (high) {
- index = CIRCUITPY_SUPERVISOR_ALLOC_COUNT - 1;
- direction = -1;
+ // supervisor_move_memory() currently does not support movable allocations on the high side, it
+ // must be extended first if this is ever needed.
+ assert(!(high && movable));
+ uint32_t* low_address = low_head ? low_head->data + low_head->length / 4 : port_heap_get_bottom();
+ uint32_t* high_address = high_head ? (uint32_t*)high_head : port_heap_get_top();
+ // Special case for allocate_remaining_memory(), avoids computing low/high_address twice.
+ if (length == (uint32_t)-1) {
+ length = (high_address - low_address) * 4 - sizeof(supervisor_allocation_node);
}
- for (; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index += direction) {
- if (allocations[index].ptr == NULL) {
- break;
+ if (length == 0 || length % 4 != 0) {
+ return NULL;
+ }
+ // 1. Matching hole on the requested side?
+ supervisor_allocation_node* node = find_hole(high ? high_head : low_head, length);
+ if (!node) {
+ // 2. Enough free space in the middle?
+ if ((high_address - low_address) * 4 >= (int32_t)(sizeof(supervisor_allocation_node) + length)) {
+ if (high) {
+ high_address -= (sizeof(supervisor_allocation_node) + length) / 4;
+ node = (supervisor_allocation_node*)high_address;
+ node->next = high_head;
+ high_head = node;
+ }
+ else {
+ node = (supervisor_allocation_node*)low_address;
+ node->next = low_head;
+ low_head = node;
+ }
}
+ else {
+ // 3. Matching hole on the other side?
+ node = find_hole(high ? low_head : high_head, length);
+ if (!node) {
+ // 4. GC allocation?
+ if (movable && gc_alloc_possible()) {
+ node = m_malloc_maybe(sizeof(supervisor_allocation_node) + length, true);
+ if (node) {
+ node->next = MP_STATE_VM(first_embedded_allocation);
+ MP_STATE_VM(first_embedded_allocation) = node;
+ }
+ }
+ if (!node) {
+ // 5. Give up.
+ return NULL;
+ }
+ }
+ }
+ }
+ node->length = length;
+ if (movable) {
+ node->length |= MOVABLE;
}
- if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT) {
+ return node;
+}
+
+supervisor_allocation* allocate_memory(uint32_t length, bool high, bool movable) {
+ supervisor_allocation_node* node = allocate_memory_node(length, high, movable);
+ if (!node) {
return NULL;
}
- supervisor_allocation* alloc = &allocations[index];
- if (high) {
- high_address -= length / 4;
- alloc->ptr = high_address;
- } else {
- alloc->ptr = low_address;
- low_address += length / 4;
+ // Find the first free allocation.
+ supervisor_allocation* alloc = allocation_from_ptr(NULL);
+ if (!alloc) {
+ // We should free node again to avoid leaking, but something is wrong anyway if clients try
+ // to make more allocations than available, so don't bother.
+ return NULL;
}
- alloc->length = length;
+ alloc->ptr = &(node->data[0]);
return alloc;
}
+size_t get_allocation_length(supervisor_allocation* allocation) {
+ return ALLOCATION_NODE(allocation)->length & ~FLAGS;
+}
+
void supervisor_move_memory(void) {
+ // This whole function is not needed when there are no movable allocations, let it be optimized
+ // out.
+ if (CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT == 0) {
+ return;
+ }
+ // This must be called exactly after freeing the heap, so that the embedded allocations, if any,
+ // are now in the free region.
+ assert(MP_STATE_VM(first_embedded_allocation) == NULL || (low_head < MP_STATE_VM(first_embedded_allocation) && MP_STATE_VM(first_embedded_allocation) < high_head));
+
+ // Save the old pointers for allocation_from_ptr().
+ supervisor_allocation old_allocations_array[CIRCUITPY_SUPERVISOR_ALLOC_COUNT];
+ memcpy(old_allocations_array, allocations, sizeof(allocations));
+
+ // Compact the low side. Traverse the list repeatedly, finding movable allocations preceded by a
+ // hole and swapping them, until no more are found. This is not the most runtime-efficient way,
+ // but probably the shortest and simplest code.
+ bool acted;
+ do {
+ acted = false;
+ supervisor_allocation_node** nodep = &low_head;
+ while (*nodep != NULL && (*nodep)->next != NULL) {
+ if (((*nodep)->length & MOVABLE) && ((*nodep)->next->length & HOLE)) {
+ supervisor_allocation_node* oldnode = *nodep;
+ supervisor_allocation_node* start = oldnode->next;
+ supervisor_allocation* alloc = allocation_from_ptr(&(oldnode->data[0]));
+ assert(alloc != NULL);
+ alloc->ptr = &(start->data[0]);
+ oldnode->next = start->next;
+ size_t holelength = start->length;
+ size_t size = sizeof(supervisor_allocation_node) + (oldnode->length & ~FLAGS);
+ memmove(start, oldnode, size);
+ supervisor_allocation_node* newhole = (supervisor_allocation_node*)(void*)((char*)start + size);
+ newhole->next = start;
+ newhole->length = holelength;
+ *nodep = newhole;
+ acted = true;
+ }
+ nodep = &((*nodep)->next);
+ }
+ } while (acted);
+ // Any holes bubbled to the top can be absorbed into the free middle.
+ while (low_head != NULL && (low_head->length & HOLE)) {
+ low_head = low_head->next;
+ };
+
+ // Don't bother compacting the high side, there are no movable allocations and no holes there in
+ // current usage.
+
+ // Promote the embedded allocations to top-level ones, compacting them at the beginning of the
+ // now free region (or possibly in matching holes).
+ // The linked list is unordered, but allocations must be processed in order to avoid risking
+ // overwriting each other. To that end, repeatedly find the lowest element of the list, remove
+ // it from the list, and process it. This ad-hoc selection sort results in substantially shorter
+ // code than using the qsort() function from the C library.
+ while (MP_STATE_VM(first_embedded_allocation)) {
+ // First element is first candidate.
+ supervisor_allocation_node** pminnode = &MP_STATE_VM(first_embedded_allocation);
+ // Iterate from second element (if any) on.
+ for (supervisor_allocation_node** pnode = &(MP_STATE_VM(first_embedded_allocation)->next); *pnode != NULL; pnode = &(*pnode)->next) {
+ if (*pnode < *pminnode) {
+ pminnode = pnode;
+ }
+ }
+ // Remove from list.
+ supervisor_allocation_node* node = *pminnode;
+ *pminnode = node->next;
+ // Process.
+ size_t length = (node->length & ~FLAGS);
+ supervisor_allocation* alloc = allocation_from_ptr(&(node->data[0]));
+ assert(alloc != NULL);
+ // This may overwrite the header of node if it happened to be there already, but not the
+ // data.
+ supervisor_allocation_node* new_node = allocate_memory_node(length, false, true);
+ // There must be enough free space.
+ assert(new_node != NULL);
+ memmove(&(new_node->data[0]), &(node->data[0]), length);
+ alloc->ptr = &(new_node->data[0]);
+ }
+
+ // Notify clients that their movable allocations may have moved.
+ old_allocations = &old_allocations_array[0];
#if CIRCUITPY_DISPLAYIO
supervisor_display_move_memory();
#endif
+ // Add calls to further clients here.
+ old_allocations = NULL;
}
diff --git a/supervisor/shared/micropython.c b/supervisor/shared/micropython.c
index 245db11d4..bbc4807f9 100644
--- a/supervisor/shared/micropython.c
+++ b/supervisor/shared/micropython.c
@@ -29,14 +29,24 @@
#include "supervisor/serial.h"
#include "lib/oofatfs/ff.h"
#include "py/mpconfig.h"
+#include "py/mpstate.h"
+#include "py/runtime.h"
#include "supervisor/shared/status_leds.h"
+#if CIRCUITPY_WATCHDOG
+#include "shared-bindings/watchdog/__init__.h"
+#define WATCHDOG_EXCEPTION_CHECK() (MP_STATE_VM(mp_pending_exception) == &mp_watchdog_timeout_exception)
+#else
+#define WATCHDOG_EXCEPTION_CHECK() 0
+#endif
+
int mp_hal_stdin_rx_chr(void) {
for (;;) {
#ifdef MICROPY_VM_HOOK_LOOP
MICROPY_VM_HOOK_LOOP
#endif
+ mp_handle_pending();
if (serial_bytes_available()) {
toggle_rx_led();
return serial_read();
diff --git a/supervisor/shared/rgb_led_status.c b/supervisor/shared/rgb_led_status.c
index f3c210647..006bb1b34 100644
--- a/supervisor/shared/rgb_led_status.c
+++ b/supervisor/shared/rgb_led_status.c
@@ -28,6 +28,7 @@
#include "shared-bindings/microcontroller/Pin.h"
#include "rgb_led_status.h"
#include "supervisor/shared/tick.h"
+#include "py/obj.h"
#ifdef MICROPY_HW_NEOPIXEL
uint8_t rgb_status_brightness = 63;
@@ -65,22 +66,22 @@ busio_spi_obj_t status_apa102 = {
#if defined(CP_RGB_STATUS_R) || defined(CP_RGB_STATUS_G) || defined(CP_RGB_STATUS_B)
#define CP_RGB_STATUS_LED
-#include "shared-bindings/pulseio/PWMOut.h"
+#include "shared-bindings/pwmio/PWMOut.h"
#include "shared-bindings/microcontroller/Pin.h"
-pulseio_pwmout_obj_t rgb_status_r = {
+pwmio_pwmout_obj_t rgb_status_r = {
.base = {
- .type = &pulseio_pwmout_type,
+ .type = &pwmio_pwmout_type,
},
};
-pulseio_pwmout_obj_t rgb_status_g = {
+pwmio_pwmout_obj_t rgb_status_g = {
.base = {
- .type = &pulseio_pwmout_type,
+ .type = &pwmio_pwmout_type,
},
};
-pulseio_pwmout_obj_t rgb_status_b = {
+pwmio_pwmout_obj_t rgb_status_b = {
.base = {
- .type = &pulseio_pwmout_type,
+ .type = &pwmio_pwmout_type,
},
};
@@ -146,26 +147,26 @@ void rgb_led_status_init() {
#if defined(CP_RGB_STATUS_LED)
if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_R)) {
- pwmout_result_t red_result = common_hal_pulseio_pwmout_construct(&rgb_status_r, CP_RGB_STATUS_R, 0, 50000, false);
+ pwmout_result_t red_result = common_hal_pwmio_pwmout_construct(&rgb_status_r, CP_RGB_STATUS_R, 0, 50000, false);
if (PWMOUT_OK == red_result) {
- common_hal_pulseio_pwmout_never_reset(&rgb_status_r);
+ common_hal_pwmio_pwmout_never_reset(&rgb_status_r);
}
}
if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_G)) {
- pwmout_result_t green_result = common_hal_pulseio_pwmout_construct(&rgb_status_g, CP_RGB_STATUS_G, 0, 50000, false);
+ pwmout_result_t green_result = common_hal_pwmio_pwmout_construct(&rgb_status_g, CP_RGB_STATUS_G, 0, 50000, false);
if (PWMOUT_OK == green_result) {
- common_hal_pulseio_pwmout_never_reset(&rgb_status_g);
+ common_hal_pwmio_pwmout_never_reset(&rgb_status_g);
}
}
if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_B)) {
- pwmout_result_t blue_result = common_hal_pulseio_pwmout_construct(&rgb_status_b, CP_RGB_STATUS_B, 0, 50000, false);
+ pwmout_result_t blue_result = common_hal_pwmio_pwmout_construct(&rgb_status_b, CP_RGB_STATUS_B, 0, 50000, false);
if (PWMOUT_OK == blue_result) {
- common_hal_pulseio_pwmout_never_reset(&rgb_status_b);
+ common_hal_pwmio_pwmout_never_reset(&rgb_status_b);
}
}
#endif
@@ -241,9 +242,9 @@ void new_status_color(uint32_t rgb) {
status_rgb_color[2] = (uint16_t) (blue_u8 << 8) + blue_u8;
#endif
- common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_r, status_rgb_color[0]);
- common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, status_rgb_color[1]);
- common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, status_rgb_color[2]);
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_r, status_rgb_color[0]);
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_g, status_rgb_color[1]);
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_b, status_rgb_color[2]);
#endif
}
@@ -287,9 +288,9 @@ void temp_status_color(uint32_t rgb) {
temp_status_color_rgb[2] = (uint16_t) (blue_u8 << 8) + blue_u8;
#endif
- common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_r, temp_status_color_rgb[0]);
- common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, temp_status_color_rgb[1]);
- common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, temp_status_color_rgb[2]);
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_r, temp_status_color_rgb[0]);
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_g, temp_status_color_rgb[1]);
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_b, temp_status_color_rgb[2]);
#endif
}
@@ -326,9 +327,9 @@ void clear_temp_status() {
blue = status_rgb_color[2];
#endif
- common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_r, red);
- common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, green);
- common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, blue);
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_r, red);
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_g, green);
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_b, blue);
#endif
}
@@ -365,6 +366,11 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
status->safe_mode = safe_mode;
status->found_main = found_main;
status->total_exception_cycle = 0;
+ status->ok = result->return_code != PYEXEC_EXCEPTION;
+ if (status->ok) {
+ // If this isn't an exception, skip exception sorting and handling
+ return;
+ }
status->ones = result->exception_line % 10;
status->ones += status->ones > 0 ? 1 : 0;
status->tens = (result->exception_line / 10) % 10;
@@ -382,11 +388,12 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
}
line /= 10;
}
- status->ok = result->return_code != PYEXEC_EXCEPTION;
if (!status->ok) {
status->total_exception_cycle = EXCEPTION_TYPE_LENGTH_MS * 3 + LINE_NUMBER_TOGGLE_LENGTH * status->digit_sum + LINE_NUMBER_TOGGLE_LENGTH * num_places;
}
- if (mp_obj_is_subclass_fast(result->exception_type, &mp_type_IndentationError)) {
+ if (!result->exception_type) {
+ status->exception_color = OTHER_ERROR;
+ } else if (mp_obj_is_subclass_fast(result->exception_type, &mp_type_IndentationError)) {
status->exception_color = INDENTATION_ERROR;
} else if (mp_obj_is_subclass_fast(result->exception_type, &mp_type_SyntaxError)) {
status->exception_color = SYNTAX_ERROR;
@@ -404,14 +411,15 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
#endif
}
-void tick_rgb_status_animation(rgb_status_animation_t* status) {
+bool tick_rgb_status_animation(rgb_status_animation_t* status) {
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
uint32_t tick_diff = supervisor_ticks_ms32() - status->pattern_start;
if (status->ok) {
// All is good. Ramp ALL_DONE up and down.
if (tick_diff > ALL_GOOD_CYCLE_MS) {
status->pattern_start = supervisor_ticks_ms32();
- tick_diff = 0;
+ new_status_color(BLACK);
+ return true;
}
uint16_t brightness = tick_diff * 255 / (ALL_GOOD_CYCLE_MS / 2);
@@ -426,7 +434,7 @@ void tick_rgb_status_animation(rgb_status_animation_t* status) {
} else {
if (tick_diff > status->total_exception_cycle) {
status->pattern_start = supervisor_ticks_ms32();
- tick_diff = 0;
+ return true;
}
// First flash the file color.
if (tick_diff < EXCEPTION_TYPE_LENGTH_MS) {
@@ -475,4 +483,5 @@ void tick_rgb_status_animation(rgb_status_animation_t* status) {
}
}
#endif
+ return false; // Animation is not finished.
}
diff --git a/supervisor/shared/rgb_led_status.h b/supervisor/shared/rgb_led_status.h
index e4e1981a2..84c97796a 100644
--- a/supervisor/shared/rgb_led_status.h
+++ b/supervisor/shared/rgb_led_status.h
@@ -76,6 +76,6 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
bool found_main,
safe_mode_t safe_mode,
rgb_status_animation_t* status);
-void tick_rgb_status_animation(rgb_status_animation_t* status);
+bool tick_rgb_status_animation(rgb_status_animation_t* status);
#endif // MICROPY_INCLUDED_SUPERVISOR_RGB_LED_STATUS_H
diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c
index a167ab392..9032e4045 100644
--- a/supervisor/shared/safe_mode.c
+++ b/supervisor/shared/safe_mode.c
@@ -29,6 +29,8 @@
#include "mphalport.h"
#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/microcontroller/Processor.h"
+#include "shared-bindings/microcontroller/ResetReason.h"
#include "supervisor/serial.h"
#include "supervisor/shared/rgb_led_colors.h"
@@ -52,6 +54,12 @@ safe_mode_t wait_for_safe_mode_reset(void) {
current_safe_mode = safe_mode;
return safe_mode;
}
+
+ const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason();
+ if (reset_reason != RESET_REASON_POWER_ON &&
+ reset_reason != RESET_REASON_RESET_PIN) {
+ return NO_SAFE_MODE;
+ }
port_set_saved_word(SAFE_MODE_DATA_GUARD | (MANUAL_SAFE_MODE << 8));
// Wait for a while to allow for reset.
temp_status_color(SAFE_MODE);
@@ -60,6 +68,11 @@ safe_mode_t wait_for_safe_mode_reset(void) {
common_hal_digitalio_digitalinout_construct(&status_led, MICROPY_HW_LED_STATUS);
common_hal_digitalio_digitalinout_switch_to_output(&status_led, true, DRIVE_MODE_PUSH_PULL);
#endif
+ #ifdef CIRCUITPY_BOOT_BUTTON
+ digitalio_digitalinout_obj_t boot_button;
+ common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON);
+ common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP);
+ #endif
uint64_t start_ticks = supervisor_ticks_ms64();
uint64_t diff = 0;
while (diff < 700) {
@@ -67,6 +80,11 @@ safe_mode_t wait_for_safe_mode_reset(void) {
// Blink on for 100, off for 100, on for 100, off for 100 and on for 200
common_hal_digitalio_digitalinout_set_value(&status_led, diff > 100 && diff / 100 != 2 && diff / 100 != 4);
#endif
+ #ifdef CIRCUITPY_BOOT_BUTTON
+ if (!common_hal_digitalio_digitalinout_get_value(&boot_button)) {
+ return USER_SAFE_MODE;
+ }
+ #endif
diff = supervisor_ticks_ms64() - start_ticks;
}
#ifdef MICROPY_HW_LED_STATUS
@@ -103,69 +121,79 @@ void print_safe_mode_message(safe_mode_t reason) {
return;
}
serial_write("\n");
- // Output a user safe mode string if it's set.
- #ifdef BOARD_USER_SAFE_MODE
- if (reason == USER_SAFE_MODE) {
- serial_write_compressed(translate("You requested starting safe mode by "));
- serial_write(BOARD_USER_SAFE_MODE_ACTION);
- serial_write_compressed(translate("\nTo exit, please reset the board without "));
- serial_write(BOARD_USER_SAFE_MODE_ACTION);
- serial_write("\n");
- } else
- #endif
- switch (reason) {
- case MANUAL_SAFE_MODE:
- serial_write_compressed(translate("CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode.\n"));
- return;
- case PROGRAMMATIC_SAFE_MODE:
- serial_write_compressed(translate("The `microcontroller` module was used to boot into safe mode. Press reset to exit safe mode.\n"));
- return;
- default:
- break;
- }
- serial_write_compressed(translate("You are in safe mode: something unanticipated happened.\n"));
- switch (reason) {
- case BROWNOUT:
- serial_write_compressed(translate("The microcontroller's power dipped. Make sure your power supply provides\nenough power for the whole circuit and press reset (after ejecting CIRCUITPY).\n"));
- return;
- case HEAP_OVERWRITTEN:
- serial_write_compressed(translate("The CircuitPython heap was corrupted because the stack was too small.\nPlease increase the stack size if you know how, or if not:"));
- serial_write_compressed(FILE_AN_ISSUE);
- return;
- default:
+ switch (reason) {
+ case USER_SAFE_MODE:
+ #ifdef BOARD_USER_SAFE_MODE_ACTION
+ // Output a user safe mode string if it's set.
+ serial_write_compressed(translate("You requested starting safe mode by "));
+ serial_write_compressed(BOARD_USER_SAFE_MODE_ACTION);
+ serial_write_compressed(translate("To exit, please reset the board without "));
+ serial_write_compressed(BOARD_USER_SAFE_MODE_ACTION);
+ #else
break;
- }
+ #endif
+ return;
+ case MANUAL_SAFE_MODE:
+ serial_write_compressed(translate("CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode.\n"));
+ return;
+ case PROGRAMMATIC_SAFE_MODE:
+ serial_write_compressed(translate("The `microcontroller` module was used to boot into safe mode. Press reset to exit safe mode.\n"));
+ return;
+ default:
+ break;
+ }
- serial_write_compressed(translate("CircuitPython core code crashed hard. Whoops!\n"));
- switch (reason) {
- case HARD_CRASH:
- serial_write_compressed(translate("Crash into the HardFault_Handler."));
- return;
- case MICROPY_NLR_JUMP_FAIL:
- serial_write_compressed(translate("MicroPython NLR jump failed. Likely memory corruption."));
- return;
- case MICROPY_FATAL_ERROR:
- serial_write_compressed(translate("MicroPython fatal error."));
- break;
- case GC_ALLOC_OUTSIDE_VM:
- serial_write_compressed(translate("Attempted heap allocation when MicroPython VM not running."));
- break;
- case NORDIC_SOFT_DEVICE_ASSERT:
- serial_write_compressed(translate("Nordic Soft Device failure assertion."));
- break;
- case FLASH_WRITE_FAIL:
- serial_write_compressed(translate("Failed to write internal flash."));
- break;
- case MEM_MANAGE:
- serial_write_compressed(translate("Invalid memory access."));
- break;
- case WATCHDOG_RESET:
- serial_write_compressed(translate("Watchdog timer expired."));
- break;
- default:
- serial_write_compressed(translate("Unknown reason."));
- break;
- }
- serial_write_compressed(FILE_AN_ISSUE);
+ serial_write_compressed(translate("You are in safe mode: something unanticipated happened.\n"));
+ switch (reason) {
+ case BROWNOUT:
+ serial_write_compressed(translate("The microcontroller's power dipped. Make sure your power supply provides\nenough power for the whole circuit and press reset (after ejecting CIRCUITPY).\n"));
+ return;
+ case HEAP_OVERWRITTEN:
+ serial_write_compressed(translate("The CircuitPython heap was corrupted because the stack was too small.\nPlease increase the stack size if you know how, or if not:"));
+ serial_write_compressed(FILE_AN_ISSUE);
+ return;
+ case NO_HEAP:
+ serial_write_compressed(translate("CircuitPython was unable to allocate the heap.\n"));
+ serial_write_compressed(FILE_AN_ISSUE);
+ return;
+ default:
+ break;
+ }
+
+ serial_write_compressed(translate("CircuitPython core code crashed hard. Whoops!\n"));
+ switch (reason) {
+ case HARD_CRASH:
+ serial_write_compressed(translate("Crash into the HardFault_Handler."));
+ return;
+ case MICROPY_NLR_JUMP_FAIL:
+ serial_write_compressed(translate("MicroPython NLR jump failed. Likely memory corruption."));
+ return;
+ case MICROPY_FATAL_ERROR:
+ serial_write_compressed(translate("MicroPython fatal error."));
+ break;
+ case GC_ALLOC_OUTSIDE_VM:
+ serial_write_compressed(translate("Attempted heap allocation when MicroPython VM not running."));
+ break;
+ #ifdef SOFTDEVICE_PRESENT
+ // defined in ports/nrf/bluetooth/bluetooth_common.mk
+ // will print "Unknown reason" if somehow encountered on other ports
+ case NORDIC_SOFT_DEVICE_ASSERT:
+ serial_write_compressed(translate("Nordic Soft Device failure assertion."));
+ break;
+ #endif
+ case FLASH_WRITE_FAIL:
+ serial_write_compressed(translate("Failed to write internal flash."));
+ break;
+ case MEM_MANAGE:
+ serial_write_compressed(translate("Invalid memory access."));
+ break;
+ case WATCHDOG_RESET:
+ serial_write_compressed(translate("Watchdog timer expired."));
+ break;
+ default:
+ serial_write_compressed(translate("Unknown reason."));
+ break;
+ }
+ serial_write_compressed(FILE_AN_ISSUE);
}
diff --git a/supervisor/shared/safe_mode.h b/supervisor/shared/safe_mode.h
index c160739ae..34fc3c8ae 100644
--- a/supervisor/shared/safe_mode.h
+++ b/supervisor/shared/safe_mode.h
@@ -27,6 +27,8 @@
#ifndef MICROPY_INCLUDED_SUPERVISOR_SAFE_MODE_H
#define MICROPY_INCLUDED_SUPERVISOR_SAFE_MODE_H
+#include "py/mpconfig.h"
+
typedef enum {
NO_SAFE_MODE = 0,
BROWNOUT,
@@ -42,12 +44,13 @@ typedef enum {
FLASH_WRITE_FAIL,
MEM_MANAGE,
WATCHDOG_RESET,
+ NO_HEAP,
} safe_mode_t;
safe_mode_t wait_for_safe_mode_reset(void);
void safe_mode_on_next_reset(safe_mode_t reason);
-void reset_into_safe_mode(safe_mode_t reason);
+void reset_into_safe_mode(safe_mode_t reason) NORETURN;
void print_safe_mode_message(safe_mode_t reason);
diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c
index 513022667..303f89e75 100644
--- a/supervisor/shared/serial.c
+++ b/supervisor/shared/serial.c
@@ -55,7 +55,7 @@ void serial_early_init(void) {
const mcu_pin_obj_t* tx = MP_OBJ_TO_PTR(DEBUG_UART_TX);
common_hal_busio_uart_construct(&debug_uart, tx, rx, NULL, NULL, NULL,
- false, 115200, 8, PARITY_NONE, 1, 1.0f, 64,
+ false, 115200, 8, UART_PARITY_NONE, 1, 1.0f, 64,
buf_array, true);
common_hal_busio_uart_never_reset(&debug_uart);
#endif
@@ -99,7 +99,7 @@ void serial_write_substring(const char* text, uint32_t length) {
if (length == 0) {
return;
}
-#if CIRCUITPY_DISPLAYIO
+#if CIRCUITPY_TERMINALIO
int errcode;
common_hal_terminalio_terminal_write(&supervisor_terminal, (const uint8_t*) text, length, &errcode);
#endif
diff --git a/supervisor/shared/stack.c b/supervisor/shared/stack.c
index e7aa956b0..afea20401 100755
--- a/supervisor/shared/stack.c
+++ b/supervisor/shared/stack.c
@@ -34,36 +34,42 @@
extern uint32_t _estack;
+// Requested size.
static uint32_t next_stack_size = CIRCUITPY_DEFAULT_STACK_SIZE;
static uint32_t current_stack_size = 0;
-supervisor_allocation* stack_alloc = NULL;
+// Actual location and size, may be larger than requested.
+static uint32_t* stack_limit = NULL;
+static size_t stack_length = 0;
#define EXCEPTION_STACK_SIZE 1024
void allocate_stack(void) {
- if (port_fixed_stack() != NULL) {
- stack_alloc = port_fixed_stack();
- current_stack_size = stack_alloc->length;
+ if (port_has_fixed_stack()) {
+ stack_limit = port_stack_get_limit();
+ stack_length = (port_stack_get_top() - stack_limit)*sizeof(uint32_t);
+ current_stack_size = stack_length;
} else {
mp_uint_t regs[10];
mp_uint_t sp = cpu_get_regs_and_sp(regs);
mp_uint_t c_size = (uint32_t) port_stack_get_top() - sp;
- stack_alloc = allocate_memory(c_size + next_stack_size + EXCEPTION_STACK_SIZE, true);
+ supervisor_allocation* stack_alloc = allocate_memory(c_size + next_stack_size + EXCEPTION_STACK_SIZE, true, false);
if (stack_alloc == NULL) {
- stack_alloc = allocate_memory(c_size + CIRCUITPY_DEFAULT_STACK_SIZE + EXCEPTION_STACK_SIZE, true);
+ stack_alloc = allocate_memory(c_size + CIRCUITPY_DEFAULT_STACK_SIZE + EXCEPTION_STACK_SIZE, true, false);
current_stack_size = CIRCUITPY_DEFAULT_STACK_SIZE;
} else {
current_stack_size = next_stack_size;
}
+ stack_limit = stack_alloc->ptr;
+ stack_length = get_allocation_length(stack_alloc);
}
- *stack_alloc->ptr = STACK_CANARY_VALUE;
+ *stack_limit = STACK_CANARY_VALUE;
}
inline bool stack_ok(void) {
- return stack_alloc == NULL || *stack_alloc->ptr == STACK_CANARY_VALUE;
+ return stack_limit == NULL || *stack_limit == STACK_CANARY_VALUE;
}
inline void assert_heap_ok(void) {
@@ -77,18 +83,26 @@ void stack_init(void) {
}
void stack_resize(void) {
- if (stack_alloc == NULL) {
+ if (stack_limit == NULL) {
return;
}
if (next_stack_size == current_stack_size) {
- *stack_alloc->ptr = STACK_CANARY_VALUE;
+ *stack_limit = STACK_CANARY_VALUE;
return;
}
- free_memory(stack_alloc);
- stack_alloc = NULL;
+ free_memory(allocation_from_ptr(stack_limit));
+ stack_limit = NULL;
allocate_stack();
}
+uint32_t* stack_get_bottom(void) {
+ return stack_limit;
+}
+
+size_t stack_get_length(void) {
+ return stack_length;
+}
+
void set_next_stack_size(uint32_t size) {
next_stack_size = size;
}
diff --git a/supervisor/shared/stack.h b/supervisor/shared/stack.h
index 7096f0b3e..1c75de5f7 100755
--- a/supervisor/shared/stack.h
+++ b/supervisor/shared/stack.h
@@ -31,10 +31,12 @@
#include "supervisor/memory.h"
-extern supervisor_allocation* stack_alloc;
-
void stack_init(void);
void stack_resize(void);
+// Actual stack location and size, may be larger than requested.
+uint32_t* stack_get_bottom(void);
+size_t stack_get_length(void);
+// Next/current requested stack size.
void set_next_stack_size(uint32_t size);
uint32_t get_current_stack_size(void);
bool stack_ok(void);
diff --git a/supervisor/shared/tick.c b/supervisor/shared/tick.c
index 47395bd60..e51c48c73 100644
--- a/supervisor/shared/tick.c
+++ b/supervisor/shared/tick.c
@@ -26,13 +26,24 @@
#include "supervisor/shared/tick.h"
+#include "lib/utils/interrupt_char.h"
#include "py/mpstate.h"
+#include "py/runtime.h"
#include "supervisor/linker.h"
#include "supervisor/filesystem.h"
+#include "supervisor/background_callback.h"
#include "supervisor/port.h"
#include "supervisor/shared/autoreload.h"
+#include "supervisor/shared/stack.h"
-static volatile uint64_t PLACE_IN_DTCM_BSS(background_ticks);
+#if CIRCUITPY_BLEIO
+#include "supervisor/shared/bluetooth.h"
+#include "common-hal/_bleio/__init__.h"
+#endif
+
+#if CIRCUITPY_DISPLAYIO
+#include "shared-module/displayio/__init__.h"
+#endif
#if CIRCUITPY_GAMEPAD
#include "shared-module/gamepad/__init__.h"
@@ -42,6 +53,10 @@ static volatile uint64_t PLACE_IN_DTCM_BSS(background_ticks);
#include "shared-module/gamepadshift/__init__.h"
#endif
+#if CIRCUITPY_NETWORK
+#include "shared-module/network/__init__.h"
+#endif
+
#include "shared-bindings/microcontroller/__init__.h"
#if CIRCUITPY_WATCHDOG
@@ -51,6 +66,44 @@ static volatile uint64_t PLACE_IN_DTCM_BSS(background_ticks);
#define WATCHDOG_EXCEPTION_CHECK() 0
#endif
+static volatile uint64_t PLACE_IN_DTCM_BSS(background_ticks);
+
+static background_callback_t tick_callback;
+
+volatile uint64_t last_finished_tick = 0;
+
+void supervisor_background_tasks(void *unused) {
+ port_start_background_task();
+
+ assert_heap_ok();
+
+ #if CIRCUITPY_DISPLAYIO
+ displayio_background();
+ #endif
+
+ #if CIRCUITPY_NETWORK
+ network_module_background();
+ #endif
+ filesystem_background();
+
+ #if CIRCUITPY_BLEIO
+ supervisor_bluetooth_background();
+ bleio_background();
+ #endif
+
+ port_background_task();
+
+ assert_heap_ok();
+
+ last_finished_tick = port_get_raw_ticks(NULL);
+
+ port_finish_background_task();
+}
+
+bool supervisor_background_tasks_ok(void) {
+ return port_get_raw_ticks(NULL) - last_finished_tick < 1024;
+}
+
void supervisor_tick(void) {
#if CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS > 0
filesystem_tick();
@@ -68,13 +121,12 @@ void supervisor_tick(void) {
#endif
}
#endif
+ background_callback_add(&tick_callback, supervisor_background_tasks, NULL);
}
uint64_t supervisor_ticks_ms64() {
uint64_t result;
- common_hal_mcu_disable_interrupts();
result = port_get_raw_ticks(NULL);
- common_hal_mcu_enable_interrupts();
result = result * 1000 / 1024;
return result;
}
@@ -83,14 +135,9 @@ uint32_t supervisor_ticks_ms32() {
return supervisor_ticks_ms64();
}
-extern void run_background_tasks(void);
void PLACE_IN_ITCM(supervisor_run_background_tasks_if_tick)() {
- // TODO: Add a global that can be set by anyone to indicate we should run background tasks. That
- // way we can short circuit the background tasks early. We used to do it based on time but it
- // breaks cases where we wake up for a short period and then sleep. If we skipped the last
- // background task or more before sleeping we may end up starving a task like USB.
- run_background_tasks();
+ background_callback_run_all();
}
void mp_hal_delay_ms(mp_uint_t delay) {
@@ -99,14 +146,11 @@ void mp_hal_delay_ms(mp_uint_t delay) {
delay = delay * 1024 / 1000;
uint64_t end_tick = start_tick + delay;
int64_t remaining = delay;
- while (remaining > 0) {
+
+ // Loop until we've waited long enough or we've been CTRL-Ced by autoreload
+ // or the user.
+ while (remaining > 0 && !mp_hal_is_interrupted()) {
RUN_BACKGROUND_TASKS;
- // Check to see if we've been CTRL-Ced by autoreload or the user.
- if(MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)) ||
- MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception)) ||
- WATCHDOG_EXCEPTION_CHECK()) {
- break;
- }
remaining = end_tick - port_get_raw_ticks(NULL);
// We break a bit early so we don't risk setting the alarm before the time when we call
// sleep.
@@ -114,8 +158,8 @@ void mp_hal_delay_ms(mp_uint_t delay) {
break;
}
port_interrupt_after_ticks(remaining);
- // Sleep until an interrupt happens.
- port_sleep_until_interrupt();
+ // Idle until an interrupt happens.
+ port_idle_until_interrupt();
remaining = end_tick - port_get_raw_ticks(NULL);
}
}
diff --git a/supervisor/shared/tick.h b/supervisor/shared/tick.h
index e7e808058..3a01bd622 100644
--- a/supervisor/shared/tick.h
+++ b/supervisor/shared/tick.h
@@ -28,6 +28,7 @@
#define __INCLUDED_SUPERVISOR_TICK_H
#include <stdint.h>
+#include <stdbool.h>
/** @brief To be called once every ms
*
@@ -36,13 +37,6 @@
* interrupt context.
*/
extern void supervisor_tick(void);
-/** @brief Cause background tasks to be called soon
- *
- * Normally, background tasks are only run once per tick. For other cases where
- * an event noticed from an interrupt context needs to be completed by a background
- * task activity, the interrupt can call supervisor_fake_tick.
- */
-extern void supervisor_fake_tick(void);
/** @brief Get the lower 32 bits of the time in milliseconds
*
* This can be more efficient than supervisor_ticks_ms64, for sites where a wraparound
@@ -67,4 +61,12 @@ extern void supervisor_run_background_if_tick(void);
extern void supervisor_enable_tick(void);
extern void supervisor_disable_tick(void);
+/**
+ * @brief Return true if tick-based background tasks ran within the last 1s
+ *
+ * Note that when ticks are not enabled, this function can return false; this is
+ * intended.
+ */
+extern bool supervisor_background_tasks_ok(void);
+
#endif
diff --git a/supervisor/shared/translate.c b/supervisor/shared/translate.c
index 606f8fa91..44544c98d 100644
--- a/supervisor/shared/translate.c
+++ b/supervisor/shared/translate.c
@@ -34,6 +34,7 @@
#include "genhdr/compression.generated.h"
#endif
+#include "py/misc.h"
#include "supervisor/serial.h"
void serial_write_compressed(const compressed_string_t* compressed) {
@@ -46,13 +47,29 @@ STATIC int put_utf8(char *buf, int u) {
if(u <= 0x7f) {
*buf = u;
return 1;
+ } else if(word_start <= u && u <= word_end) {
+ uint n = (u - word_start);
+ size_t pos = 0;
+ if (n > 0) {
+ pos = wends[n - 1] + (n * 2);
+ }
+ int ret = 0;
+ // note that at present, entries in the words table are
+ // guaranteed not to represent words themselves, so this adds
+ // at most 1 level of recursive call
+ for(; pos < wends[n] + (n + 1) * 2; pos++) {
+ int len = put_utf8(buf, words[pos]);
+ buf += len;
+ ret += len;
+ }
+ return ret;
} else if(u <= 0x07ff) {
*buf++ = 0b11000000 | (u >> 6);
*buf = 0b10000000 | (u & 0b00111111);
return 2;
- } else { // u <= 0xffff)
- *buf++ = 0b11000000 | (u >> 12);
- *buf = 0b10000000 | ((u >> 6) & 0b00111111);
+ } else { // u <= 0xffff
+ *buf++ = 0b11100000 | (u >> 12);
+ *buf++ = 0b10000000 | ((u >> 6) & 0b00111111);
*buf = 0b10000000 | (u & 0b00111111);
return 3;
}
diff --git a/supervisor/shared/translate.h b/supervisor/shared/translate.h
index 731b26d12..16296a416 100644
--- a/supervisor/shared/translate.h
+++ b/supervisor/shared/translate.h
@@ -43,6 +43,19 @@
// (building the huffman encoding on UTF-16 code points gave better
// compression than building it on UTF-8 bytes)
//
+// - code points starting at 128 (word_start) and potentially extending
+// to 255 (word_end) (but never interfering with the target
+// language's used code points) stand for dictionary entries in a
+// dictionary with size up to 256 code points. The dictionary entries
+// are computed with a heuristic based on frequent substrings of 2 to
+// 9 code points. These are called "words" but are not, grammatically
+// speaking, words. They're just spans of code points that frequently
+// occur together.
+//
+// - dictionary entries are non-overlapping, and the _ending_ index of each
+// entry is stored in an array. Since the index given is the ending
+// index, the array is called "wends".
+//
// The "data" / "tail" construct is so that the struct's last member is a
// "flexible array". However, the _only_ member is not permitted to be
// a flexible member, so we have to declare the first byte as a separte
diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h
index 5b7230983..15d9fabaf 100644
--- a/supervisor/shared/usb/tusb_config.h
+++ b/supervisor/shared/usb/tusb_config.h
@@ -47,8 +47,6 @@
//--------------------------------------------------------------------+
// COMMON CONFIGURATION
//--------------------------------------------------------------------+
-#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
-
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
@@ -58,8 +56,6 @@
#define CFG_TUSB_OS OPT_OS_NONE
#endif
//#define CFG_TUD_TASK_QUEUE_SZ 16
-//#define CFG_TUD_TASK_PRIO 0
-//#define CFG_TUD_TASK_STACK_SZ 150
//--------------------------------------------------------------------+
// DEVICE CONFIGURATION
@@ -67,14 +63,6 @@
#define CFG_TUD_ENDOINT0_SIZE 64
-/*------------- Descriptors -------------*/
-/* Enable auto generated descriptor, tinyusb will try its best to create
- * descriptor ( device, configuration, hid ) that matches enabled CFG_* in this file
- *
- * Note: All CFG_TUD_DESC_* are relevant only if CFG_TUD_DESC_AUTO is enabled
- */
-#define CFG_TUD_DESC_AUTO 0
-
//------------- CLASS -------------//
#define CFG_TUD_CDC 1
#define CFG_TUD_MSC 1
@@ -86,23 +74,6 @@
/* CLASS DRIVER
*------------------------------------------------------------------*/
-/* TX is sent automatically on every Start of Frame event ~ 1ms.
- * If not enabled, application must call tud_cdc_flush() periodically
- * Note: Enabled this could overflow device task, if it does, define
- * CFG_TUD_TASK_QUEUE_SZ with large value
- */
-#define CFG_TUD_CDC_FLUSH_ON_SOF 0
-
-
-/*------------- MSC -------------*/
-// Number of supported Logical Unit Number (At least 1)
-#define CFG_TUD_MSC_MAXLUN 1
-
-// Number of Blocks
-#define CFG_TUD_MSC_BLOCK_NUM (256*1024)/512
-
-
-
// Product revision string included in Inquiry response, max 4 bytes
#define CFG_TUD_MSC_PRODUCT_REV "1.0"
diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c
index edf810118..ff08ade18 100644
--- a/supervisor/shared/usb/usb.c
+++ b/supervisor/shared/usb/usb.c
@@ -27,8 +27,11 @@
#include "py/objstr.h"
#include "shared-bindings/microcontroller/Processor.h"
#include "shared-module/usb_midi/__init__.h"
+#include "supervisor/background_callback.h"
#include "supervisor/port.h"
+#include "supervisor/serial.h"
#include "supervisor/usb.h"
+#include "supervisor/shared/workflow.h"
#include "lib/utils/interrupt_char.h"
#include "lib/mp-readline/readline.h"
@@ -63,8 +66,8 @@ void usb_init(void) {
tusb_init();
#if MICROPY_KBD_EXCEPTION
- // Set Ctrl+C as wanted char, tud_cdc_rx_wanted_cb() callback will be invoked when Ctrl+C is received
- // This callback always got invoked regardless of mp_interrupt_char value since we only set it once here
+ // Set Ctrl+C as wanted char, tud_cdc_rx_wanted_cb() usb_callback will be invoked when Ctrl+C is received
+ // This usb_callback always got invoked regardless of mp_interrupt_char value since we only set it once here
tud_cdc_set_wanted_char(CHAR_CTRL_C);
#endif
@@ -73,6 +76,10 @@ void usb_init(void) {
#endif
}
+void usb_disconnect(void) {
+ tud_disconnect();
+}
+
void usb_background(void) {
if (usb_enabled()) {
#if CFG_TUSB_OS == OPT_OS_NONE
@@ -82,6 +89,16 @@ void usb_background(void) {
}
}
+static background_callback_t usb_callback;
+static void usb_background_do(void* unused) {
+ usb_background();
+}
+
+void usb_irq_handler(void) {
+ tud_int_handler(0);
+ background_callback_add(&usb_callback, usb_background_do, NULL);
+}
+
//--------------------------------------------------------------------+
// tinyusb callbacks
//--------------------------------------------------------------------+
diff --git a/supervisor/shared/workflow.c b/supervisor/shared/workflow.c
new file mode 100644
index 000000000..4986c0957
--- /dev/null
+++ b/supervisor/shared/workflow.c
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 <stdbool.h>
+#include "py/mpconfig.h"
+#include "tusb.h"
+
+void supervisor_workflow_reset(void) {
+}
+
+// Return true as soon as USB communication with host has started,
+// even before enumeration is done.
+// Not that some chips don't notice when USB is unplugged after first being plugged in,
+// so this is not perfect, but tud_suspended() check helps.
+bool supervisor_workflow_connecting(void) {
+ return tud_connected() && !tud_suspended();
+}
+
+// Return true if host has completed connection to us (such as USB enumeration).
+bool supervisor_workflow_active(void) {
+ // Eventually there might be other non-USB workflows, such as BLE.
+ // tud_ready() checks for usb mounted and not suspended.
+ return tud_ready();
+}
diff --git a/supervisor/shared/workflow.h b/supervisor/shared/workflow.h
new file mode 100644
index 000000000..22a9fa468
--- /dev/null
+++ b/supervisor/shared/workflow.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+extern void supervisor_workflow_reset(void);
+
+extern bool supervisor_workflow_connecting(void);
+extern bool supervisor_workflow_active(void);