Switch all ports to auto-growing split heap

This simplifies allocating outside of the VM because the VM doesn't
take up all remaining memory by default.

On ESP we delegate to the IDF for allocations. For all other ports,
we use TLSF to manage an outer "port" heap. The IDF uses TLSF
internally and we use their fork for the other ports.

This also removes the dynamic C stack sizing. It wasn't often used
and is not possible with a fixed outer heap.

Fixes #8512. Fixes #7334.
This commit is contained in:
Scott Shawcroft 2023-10-02 16:49:31 -07:00
parent 648c141aa4
commit 8137e2d6d2
No known key found for this signature in database
GPG Key ID: 0DFD512649C052DA
91 changed files with 659 additions and 1437 deletions

3
.gitmodules vendored
View File

@ -351,3 +351,6 @@
[submodule "lib/certificates"]
path = lib/certificates
url = https://github.com/adafruit/certificates
[submodule "lib/tlsf"]
path = lib/tlsf
url = https://github.com/espressif/tlsf.git

14
lib/libm/fabsf.c Normal file
View File

@ -0,0 +1,14 @@
/*****************************************************************************/
/*****************************************************************************/
// fabsf from musl-0.9.15
/*****************************************************************************/
/*****************************************************************************/
#include "libm.h"
float fabsf(float x)
{
union {float f; uint32_t i;} u = {x};
u.i &= 0x7fffffff;
return u.f;
}

1
lib/tlsf Submodule

@ -0,0 +1 @@
Subproject commit 8c9cd0517adf99e363812e9a295dfe3898fdd345

View File

@ -1271,10 +1271,6 @@ msgstr ""
msgid "Invalid BSSID"
msgstr ""
#: main.c
msgid "Invalid CIRCUITPY_PYSTACK_SIZE\n"
msgstr ""
#: shared-bindings/wifi/Radio.c
msgid "Invalid MAC address"
msgstr ""
@ -2164,7 +2160,7 @@ msgid "Unable to allocate buffers for signed conversion"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Unable to allocate the heap."
msgid "Unable to allocate to the heap."
msgstr ""
#: ports/espressif/common-hal/busio/I2C.c
@ -2735,7 +2731,7 @@ msgstr ""
msgid "can't set attribute"
msgstr ""
#: py/runtime.c shared-bindings/supervisor/Runtime.c
#: py/runtime.c
msgid "can't set attribute '%q'"
msgstr ""
@ -3004,12 +3000,6 @@ msgstr ""
msgid "error = 0x%08lX"
msgstr ""
#: ports/espressif/common-hal/espcamera/Camera.c
msgid ""
"espcamera.Camera requires reserved PSRAM to be configured. See the "
"documentation for instructions."
msgstr ""
#: py/runtime.c
msgid "exceptions must derive from BaseException"
msgstr ""

177
main.c
View File

@ -49,7 +49,6 @@
#include "supervisor/board.h"
#include "supervisor/cpu.h"
#include "supervisor/filesystem.h"
#include "supervisor/memory.h"
#include "supervisor/port.h"
#include "supervisor/serial.h"
#include "supervisor/shared/reload.h"
@ -65,6 +64,7 @@
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/microcontroller/Processor.h"
#include "shared-bindings/supervisor/__init__.h"
#include "shared-bindings/supervisor/Runtime.h"
#include "shared-bindings/os/__init__.h"
@ -137,24 +137,36 @@ static void reset_devices(void) {
#endif
}
#if MICROPY_ENABLE_PYSTACK
STATIC supervisor_allocation *allocate_pystack(safe_mode_t safe_mode) {
#if CIRCUITPY_OS_GETENV && CIRCUITPY_SETTABLE_PYSTACK
STATIC uint8_t *_heap;
STATIC uint8_t *_pystack;
#if MICROPY_ENABLE_PYSTACK || MICROPY_ENABLE_GC
STATIC uint8_t *_allocate_memory(safe_mode_t safe_mode, const char *env_key, size_t default_size, size_t *final_size) {
*final_size = default_size;
#if CIRCUITPY_OS_GETENV
if (safe_mode == SAFE_MODE_NONE) {
mp_int_t pystack_size = CIRCUITPY_PYSTACK_SIZE;
(void)common_hal_os_getenv_int("CIRCUITPY_PYSTACK_SIZE", &pystack_size);
supervisor_allocation *pystack = allocate_memory(pystack_size >= 384 ? pystack_size : 0, false, false);
if (pystack) {
return pystack;
(void)common_hal_os_getenv_int(env_key, (mp_int_t *)final_size);
if (*final_size < 0) {
*final_size = default_size;
}
serial_write_compressed(MP_ERROR_TEXT("Invalid CIRCUITPY_PYSTACK_SIZE\n"));
}
#endif
return allocate_memory(CIRCUITPY_PYSTACK_SIZE, false, false);
uint8_t *ptr = port_malloc(*final_size, false);
#if CIRCUITPY_OS_GETENV
if (ptr == NULL) {
// Fallback to the build size.
ptr = port_malloc(default_size, false);
}
#endif
if (ptr == NULL) {
reset_into_safe_mode(SAFE_MODE_NO_HEAP);
}
return ptr;
}
#endif
STATIC void start_mp(supervisor_allocation *heap, supervisor_allocation *pystack) {
STATIC void start_mp(safe_mode_t safe_mode) {
supervisor_workflow_reset();
// Stack limit should be less than real stack size, so we have a chance
@ -162,13 +174,12 @@ STATIC void start_mp(supervisor_allocation *heap, supervisor_allocation *pystack
// stack is set to our current state. Not the actual top.
mp_stack_ctrl_init();
uint32_t *stack_bottom = stack_get_bottom();
if (stack_bottom != NULL) {
size_t stack_length = stack_get_length();
mp_stack_set_top(stack_bottom + (stack_length / sizeof(uint32_t)));
mp_stack_set_limit(stack_length - 1024);
}
uint32_t *stack_bottom = port_stack_get_limit();
uint32_t *stack_top = port_stack_get_top();
size_t stack_length = (stack_top - stack_bottom) * sizeof(uint32_t);
mp_stack_set_top(stack_top);
mp_stack_set_limit(stack_length - CIRCUITPY_EXCEPTION_STACK_SIZE);
#if MICROPY_MAX_STACK_USAGE
// _ezero (same as _ebss) is an int, so start 4 bytes above it.
@ -186,11 +197,15 @@ STATIC void start_mp(supervisor_allocation *heap, supervisor_allocation *pystack
readline_init0();
#if MICROPY_ENABLE_PYSTACK
mp_pystack_init(pystack->ptr, pystack->ptr + get_allocation_length(pystack) / sizeof(size_t));
size_t pystack_size = 0;
_pystack = _allocate_memory(safe_mode, "CIRCUITPY_PYSTACK_SIZE", CIRCUITPY_PYSTACK_SIZE, &pystack_size);
mp_pystack_init(_pystack, _pystack + pystack_size / sizeof(size_t));
#endif
#if MICROPY_ENABLE_GC
gc_init(heap->ptr, heap->ptr + get_allocation_length(heap) / 4);
size_t heap_size = 0;
_heap = _allocate_memory(safe_mode, "CIRCUITPY_HEAP_START_SIZE", CIRCUITPY_HEAP_START_SIZE, &heap_size);
gc_init(_heap, _heap + heap_size / 4);
#endif
mp_init();
mp_obj_list_init((mp_obj_list_t *)mp_sys_path, 0);
@ -227,6 +242,13 @@ STATIC void stop_mp(void) {
qstr_reset();
gc_deinit();
port_free(_heap);
_heap = NULL;
#if MICROPY_ENABLE_PYSTACK
port_free(_pystack);
_pystack = NULL;
#endif
}
STATIC const char *_current_executing_filename = NULL;
@ -300,33 +322,34 @@ STATIC void count_strn(void *data, const char *str, size_t len) {
*(size_t *)data += len;
}
STATIC void cleanup_after_vm(supervisor_allocation *heap, supervisor_allocation *pystack, mp_obj_t exception) {
STATIC void cleanup_after_vm(mp_obj_t exception) {
// Get the traceback of any exception from this run off the heap.
// MP_OBJ_SENTINEL means "this run does not contribute to traceback storage, don't touch it"
// MP_OBJ_NULL (=0) means "this run completed successfully, clear any stored traceback"
if (exception != MP_OBJ_SENTINEL) {
free_memory(prev_traceback_allocation);
if (prev_traceback_string != NULL) {
port_free(prev_traceback_string);
prev_traceback_string = NULL;
}
// ReloadException is exempt from traceback printing in pyexec_file(), so treat it as "no
// traceback" here too.
if (exception && exception != MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception))) {
size_t traceback_len = 0;
mp_print_t print_count = {&traceback_len, count_strn};
mp_obj_print_exception(&print_count, exception);
prev_traceback_allocation = allocate_memory(align32_size(traceback_len + 1), false, true);
prev_traceback_string = (char *)port_malloc(traceback_len + 1, false);
// Empirically, this never fails in practice - even when the heap is totally filled up
// with single-block-sized objects referenced by a root pointer, exiting the VM frees
// up several hundred bytes, sufficient for the traceback (which tends to be shortened
// because there wasn't memory for the full one). There may be convoluted ways of
// making it fail, but at this point I believe they are not worth spending code on.
if (prev_traceback_allocation != NULL) {
if (prev_traceback_string != NULL) {
vstr_t vstr;
vstr_init_fixed_buf(&vstr, traceback_len, (char *)prev_traceback_allocation->ptr);
vstr_init_fixed_buf(&vstr, traceback_len, prev_traceback_string);
mp_print_t print = {&vstr, (mp_print_strn_t)vstr_add_strn};
mp_obj_print_exception(&print, exception);
((char *)prev_traceback_allocation->ptr)[traceback_len] = '\0';
prev_traceback_string[traceback_len] = '\0';
}
} else {
prev_traceback_allocation = NULL;
}
}
@ -380,11 +403,6 @@ STATIC void cleanup_after_vm(supervisor_allocation *heap, supervisor_allocation
// Free the heap last because other modules may reference heap memory and need to shut down.
filesystem_flush();
stop_mp();
free_memory(heap);
#if MICROPY_ENABLE_PYSTACK
free_memory(pystack);
#endif
supervisor_move_memory();
// Let the workflows know we've reset in case they want to restart.
supervisor_workflow_reset();
@ -424,7 +442,6 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool *simulate_reset) {
// Do the filesystem flush check before reload in case another write comes
// in while we're doing the flush.
if (safe_mode == SAFE_MODE_NONE) {
stack_resize();
filesystem_flush();
}
if (safe_mode == SAFE_MODE_NONE && !autoreload_pending()) {
@ -438,12 +455,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool *simulate_reset) {
};
#endif
supervisor_allocation *pystack = NULL;
#if MICROPY_ENABLE_PYSTACK
pystack = allocate_pystack(safe_mode);
#endif
supervisor_allocation *heap = allocate_remaining_memory();
start_mp(heap, pystack);
start_mp(safe_mode);
#if CIRCUITPY_USB
usb_setup_with_vm();
@ -453,16 +465,15 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool *simulate_reset) {
common_hal_os_chdir("/");
// Check if a different run file has been allocated
if (next_code_allocation) {
next_code_info_t *info = ((next_code_info_t *)next_code_allocation->ptr);
info->options &= ~SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
next_code_options = info->options;
if (info->filename[0] != '\0') {
if (next_code_configuration != NULL) {
next_code_configuration->options &= ~SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
next_code_options = next_code_configuration->options;
if (next_code_configuration->filename[0] != '\0') {
// This is where the user's python code is actually executed:
const char *const filenames[] = { info->filename };
const char *const filenames[] = { next_code_configuration->filename };
found_main = maybe_run_list(filenames, MP_ARRAY_SIZE(filenames));
if (!found_main) {
serial_write(info->filename);
serial_write(next_code_configuration->filename);
serial_write_compressed(MP_ERROR_TEXT(" not found.\n"));
}
}
@ -494,16 +505,16 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool *simulate_reset) {
// Finished executing python code. Cleanup includes filesystem flush and a board reset.
cleanup_after_vm(heap, pystack, _exec_result.exception);
cleanup_after_vm(_exec_result.exception);
_exec_result.exception = NULL;
// If a new next code file was set, that is a reason to keep it (obviously). Stuff this into
// the options because it can be treated like any other reason-for-stickiness bit. The
// source is different though: it comes from the options that will apply to the next run,
// while the rest of next_code_options is what applied to this run.
if (next_code_allocation != NULL &&
(((next_code_info_t *)next_code_allocation->ptr)->options & SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET)) {
next_code_options |= SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
if (next_code_configuration != NULL &&
next_code_configuration->options & SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET) {
next_code_configuration->options |= SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
}
if (_exec_result.return_code & PYEXEC_RELOAD) {
@ -757,9 +768,9 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool *simulate_reset) {
#endif
// free code allocation if unused
if ((next_code_options & next_code_stickiness_situation) == 0) {
free_memory(next_code_allocation);
next_code_allocation = NULL;
if (next_code_configuration != NULL && (next_code_configuration->options & next_code_stickiness_situation) == 0) {
port_free(next_code_configuration);
next_code_configuration = NULL;
}
#if CIRCUITPY_STATUS_LED
@ -791,12 +802,7 @@ STATIC void __attribute__ ((noinline)) run_safemode_py(safe_mode_t safe_mode) {
return;
}
supervisor_allocation *pystack = NULL;
#if MICROPY_ENABLE_PYSTACK
pystack = allocate_pystack(safe_mode);
#endif
supervisor_allocation *heap = allocate_remaining_memory();
start_mp(heap, pystack);
start_mp(safe_mode);
static const char *const safemode_py_filenames[] = {"safemode.py", "safemode.txt"};
maybe_run_list(safemode_py_filenames, MP_ARRAY_SIZE(safemode_py_filenames));
@ -807,7 +813,7 @@ STATIC void __attribute__ ((noinline)) run_safemode_py(safe_mode_t safe_mode) {
set_safe_mode(SAFE_MODE_SAFEMODE_PY_ERROR);
}
cleanup_after_vm(heap, pystack, _exec_result.exception);
cleanup_after_vm(_exec_result.exception);
_exec_result.exception = NULL;
}
#endif
@ -828,12 +834,7 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
// Do USB setup even if boot.py is not run.
supervisor_allocation *pystack = NULL;
#if MICROPY_ENABLE_PYSTACK
pystack = allocate_pystack(safe_mode);
#endif
supervisor_allocation *heap = allocate_remaining_memory();
start_mp(heap, pystack);
start_mp(safe_mode);
#if CIRCUITPY_USB
// Set up default USB values after boot.py VM starts but before running boot.py.
@ -906,40 +907,19 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
#endif
}
#if CIRCUITPY_USB
// Some data needs to be carried over from the USB settings in boot.py
// to the next VM, while the heap is still available.
// Its size can vary, so save it temporarily on the stack,
// and then when the heap goes away, copy it in into a
// storage_allocation.
size_t size = usb_boot_py_data_size();
uint8_t usb_boot_py_data[size];
usb_get_boot_py_data(usb_boot_py_data, size);
#endif
port_post_boot_py(true);
cleanup_after_vm(heap, pystack, _exec_result.exception);
cleanup_after_vm(_exec_result.exception);
_exec_result.exception = NULL;
port_post_boot_py(false);
#if CIRCUITPY_USB
// Now give back the data we saved from the heap going away.
usb_return_boot_py_data(usb_boot_py_data, size);
#endif
}
STATIC int run_repl(safe_mode_t safe_mode) {
int exit_code = PYEXEC_FORCED_EXIT;
stack_resize();
filesystem_flush();
supervisor_allocation *pystack = NULL;
#if MICROPY_ENABLE_PYSTACK
pystack = allocate_pystack(safe_mode);
#endif
supervisor_allocation *heap = allocate_remaining_memory();
start_mp(heap, pystack);
start_mp(safe_mode);
#if CIRCUITPY_USB
usb_setup_with_vm();
@ -987,7 +967,7 @@ STATIC int run_repl(safe_mode_t safe_mode) {
exit_code = PYEXEC_DEEP_SLEEP;
}
#endif
cleanup_after_vm(heap, pystack, MP_OBJ_SENTINEL);
cleanup_after_vm(MP_OBJ_SENTINEL);
// Also reset bleio. The above call omits it in case workflows should continue. In this case,
// we're switching straight to another VM so we want to reset.
@ -1010,6 +990,8 @@ int __attribute__((used)) main(void) {
// initialise the cpu and peripherals
set_safe_mode(port_init());
port_heap_init();
// Turn on RX and TX LEDs if we have them.
init_rxtx_leds();
@ -1055,10 +1037,6 @@ int __attribute__((used)) main(void) {
set_safe_mode(SAFE_MODE_NO_CIRCUITPY);
}
// We maybe can't initialize the heap until here, because on espressif port we need to be able to check for reserved psram in settings.toml
// (but it's OK if this is a no-op due to the heap being initialized in port_init())
set_safe_mode(port_heap_init(get_safe_mode()));
#if CIRCUITPY_ALARM
// Record which alarm woke us up, if any.
// common_hal_alarm_record_wake_alarm() should return a static, non-heap object
@ -1188,11 +1166,8 @@ void gc_collect(void) {
MP_WEAK void port_gc_collect() {
}
// A port may initialize the heap in port_init but if it cannot (for instance
// in espressif it must be done after CIRCUITPY is mounted) then it must provde
// an implementation of this function.
MP_WEAK safe_mode_t port_heap_init(safe_mode_t safe_mode_in) {
return safe_mode_in;
size_t gc_get_max_new_split(void) {
return port_heap_get_largest_free_size();
}
void NORETURN nlr_jump_fail(void *val) {

View File

@ -176,7 +176,10 @@ ifeq ($(GCC_VERSION_GTEQ_11),1)
CFLAGS += -Wno-stringop-overread -Wno-stringop-overflow
endif
LDFLAGS = $(CFLAGS) -nostartfiles -Wl,-nostdlib -Wl,-T,$(GENERATED_LD_FILE) -Wl,-Map=$@.map -Wl,-cref -Wl,-gc-sections -specs=nano.specs
# Somehow the lto doesn't know it needs __ffssi2 until it is too late. (Maybe
# because the code uses __builtin_ffs().) So, explicitly say we'll need it up
# front. -u is to say a symbol is undefined from the start.
LDFLAGS = $(CFLAGS) -u __ffssi2 -nostartfiles -Wl,-nostdlib -Wl,-T,$(GENERATED_LD_FILE) -Wl,-Map=$@.map -Wl,-cref -Wl,-gc-sections -specs=nano.specs
# Use toolchain libm if we're not using our own.
ifndef INTERNAL_LIBM
@ -302,6 +305,8 @@ SRC_C += \
timer_handler.c \
$(SRC_PERIPHERALS) \
$(BUILD)/lib/tlsf/tlsf.o: CFLAGS += -Wno-cast-align
$(BUILD)/lib/tinyusb/src/portable/microchip/samd/dcd_samd.o: CFLAGS += -Wno-missing-prototypes
# This is an OR because it filters to any 1s and then checks to see if it is not

View File

@ -95,13 +95,5 @@ SECTIONS
_ebss = .;
} >RAM
/* this just checks there is enough RAM for the requested stack. */
.stack (NOLOAD) :
{
. = ALIGN(4);
. = . + ${CIRCUITPY_DEFAULT_STACK_SIZE};
. = ALIGN(4);
} >RAM
.ARM.attributes 0 : { *(.ARM.attributes) }
}

View File

@ -8,7 +8,7 @@
#define MICROPY_HW_NEOPIXEL (&pin_PA30)
// Clock rates are off: Salae reads 12MHz which is the limit even though we set it to the safer 8MHz.
// Clock rates are off: Saleae reads 12MHz which is the limit even though we set it to the safer 8MHz.
#define SPI_FLASH_BAUDRATE (8000000)
#define SPI_FLASH_MOSI_PIN &pin_PB22

View File

@ -477,24 +477,21 @@ void reset_cpu(void) {
reset();
}
bool port_has_fixed_stack(void) {
return false;
}
uint32_t *port_stack_get_limit(void) {
return &_ebss;
return port_stack_get_top() - (CIRCUITPY_DEFAULT_STACK_SIZE + CIRCUITPY_EXCEPTION_STACK_SIZE) / sizeof(uint32_t);
}
uint32_t *port_stack_get_top(void) {
return &_estack;
}
// Used for the shared heap allocator.
uint32_t *port_heap_get_bottom(void) {
return port_stack_get_limit();
return &_ebss;
}
uint32_t *port_heap_get_top(void) {
return port_stack_get_top();
return port_stack_get_limit();
}
// Place the word to save 8k from the end of RAM so we and the bootloader don't clobber it.

View File

@ -127,6 +127,7 @@ endif
CFLAGS += $(INC) -Wall -Werror -std=gnu11 $(BASE_CFLAGS) $(CFLAGS_MOD) $(COPT) $(DISABLE_WARNINGS)
$(BUILD)/lib/tlsf/tlsf.o: CFLAGS += -Wno-cast-align
SRC_QSTR += $(SRC_C) $(SRC_SUPERVISOR) $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_MODULE_EXPANDED) $(SRC_CIRCUITPY_COMMON)

View File

@ -123,10 +123,6 @@ void reset_cpu(void) {
}
}
bool port_has_fixed_stack(void) {
return true;
}
// From the linker script
extern uint32_t __bss_end;
extern uint32_t _ld_ram_end;

View File

@ -104,10 +104,6 @@ void reset_to_bootloader(void) {
}
}
bool port_has_fixed_stack(void) {
return true;
}
uint32_t *port_stack_get_limit(void) {
struct tcb_s *rtcb = this_task();

View File

@ -189,6 +189,8 @@ else ifeq ($(IDF_TARGET_ARCH),riscv)
CFLAGS += -march=rv32imac_zicsr_zifencei
endif
$(BUILD)/lib/tlsf/tlsf.o: CFLAGS += -Wno-cast-align
LDFLAGS = $(CFLAGS) -Wl,-nostdlib -Wl,-Map=$@.map -Wl,-cref -Wl,--undefined=uxTopUsedPriority
LDFLAGS += \

View File

@ -141,14 +141,6 @@ STATIC mp_obj_t espidf_get_total_psram(void) {
}
MP_DEFINE_CONST_FUN_OBJ_0(espidf_get_total_psram_obj, espidf_get_total_psram);
//| def get_reserved_psram() -> int:
//| """Returns number of bytes of psram reserved for use by esp-idf, either a board-specific default value or the value defined in ``settings.toml``."""
//|
STATIC mp_obj_t espidf_get_reserved_psram(void) {
return MP_OBJ_NEW_SMALL_INT(common_hal_espidf_get_reserved_psram());
}
MP_DEFINE_CONST_FUN_OBJ_0(espidf_get_reserved_psram_obj, espidf_get_reserved_psram);
STATIC const mp_rom_map_elem_t espidf_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_espidf) },
@ -159,7 +151,6 @@ STATIC const mp_rom_map_elem_t espidf_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_erase_nvs), MP_ROM_PTR(&espidf_erase_nvs_obj)},
{ MP_ROM_QSTR(MP_QSTR_get_total_psram), MP_ROM_PTR(&espidf_get_total_psram_obj)},
{ MP_ROM_QSTR(MP_QSTR_get_reserved_psram), MP_ROM_PTR(&espidf_get_reserved_psram_obj)},
{ MP_ROM_QSTR(MP_QSTR_IDFError), MP_ROM_PTR(&mp_type_espidf_IDFError) },
{ MP_ROM_QSTR(MP_QSTR_MemoryError), MP_ROM_PTR(&mp_type_espidf_MemoryError) },
};

View File

@ -49,7 +49,7 @@ void ble_event_remove_heap_handlers(void) {
ble_event_handler_entry_t *it = MP_STATE_VM(ble_event_handler_entries);
while (it != NULL) {
// If the param is on the heap, then delete the handler.
if (HEAP_PTR(it->param)) {
if (gc_ptr_on_heap(it->param)) {
ble_event_remove_handler(it->func, it->param);
}
it = it->next;

View File

@ -77,10 +77,6 @@ void common_hal_espcamera_camera_construct(
mp_int_t framebuffer_count,
camera_grab_mode_t grab_mode) {
if (common_hal_espidf_get_reserved_psram() == 0) {
mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT(
"espcamera.Camera requires reserved PSRAM to be configured. See the documentation for instructions."));
}
for (int i = 0; i < 8; i++) {
claim_pin_number(data_pins[i]);
}

View File

@ -25,7 +25,6 @@
*/
#include "bindings/espidf/__init__.h"
#include "supervisor/memory.h"
#include "py/runtime.h"
#include "esp_now.h"
@ -71,36 +70,6 @@ bool common_hal_espidf_set_reserved_psram(size_t amount) {
#endif
}
supervisor_allocation *psram_for_idf;
void common_hal_espidf_reserve_psram(void) {
#ifdef CONFIG_SPIRAM_USE_MEMMAP
if (!psram_for_idf) {
ESP_LOGI(TAG, "Reserving %d bytes of psram", reserved_psram);
if (reserved_psram == 0) {
return;
}
psram_for_idf = allocate_memory(reserved_psram, true, false);
if (psram_for_idf) {
intptr_t psram_for_idf_start = (intptr_t)psram_for_idf->ptr;
intptr_t psram_for_idf_end = psram_for_idf_start + reserved_psram;
ESP_LOGI(TAG, "Reserved %x..%x", psram_for_idf_start, psram_for_idf_end);
heap_caps_add_region(psram_for_idf_start, psram_for_idf_end);
} else {
ESP_LOGE(TAG, "supervisor allocation failed");
}
}
#endif
}
size_t common_hal_espidf_get_reserved_psram(void) {
#ifdef CONFIG_SPIRAM
return reserved_psram;
#else
return 0;
#endif
}
size_t common_hal_espidf_get_total_psram(void) {
return psram_size_usable();
}

View File

@ -8,12 +8,6 @@
# ESP PSRAM
#
CONFIG_SPIRAM=y
#
# SPI RAM config
#
CONFIG_SPIRAM_USE_MEMMAP=y
# end of SPI RAM config
# end of ESP PSRAM
#

View File

@ -55,7 +55,6 @@
#include "common-hal/socketpool/Socket.h"
#include "common-hal/wifi/__init__.h"
#include "supervisor/background_callback.h"
#include "supervisor/memory.h"
#include "supervisor/shared/tick.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/microcontroller/RunMode.h"
@ -321,55 +320,29 @@ safe_mode_t port_init(void) {
return SAFE_MODE_NONE;
}
safe_mode_t port_heap_init(safe_mode_t sm) {
mp_int_t reserved = 0;
if (filesystem_present() && common_hal_os_getenv_int("CIRCUITPY_RESERVED_PSRAM", &reserved) == GETENV_OK) {
common_hal_espidf_set_reserved_psram(reserved);
}
void port_heap_init(void) {
// The IDF sets up the heap, so we don't need to.
}
#if defined(CONFIG_SPIRAM_USE_MEMMAP)
{
intptr_t heap_start = common_hal_espidf_get_psram_start();
intptr_t heap_end = common_hal_espidf_get_psram_end();
size_t spiram_size = heap_end - heap_start;
if (spiram_size > 0) {
heap = (uint32_t *)heap_start;
heap_size = (heap_end - heap_start) / sizeof(uint32_t);
common_hal_espidf_reserve_psram();
} else {
ESP_LOGE(TAG, "CONFIG_SPIRAM_USE_MMAP enabled but no spiram heap available");
}
void *port_malloc(size_t size, bool dma_capable) {
size_t caps = MALLOC_CAP_8BIT;
if (dma_capable) {
caps |= MALLOC_CAP_DMA;
}
#elif defined(CONFIG_SPIRAM_USE_CAPS_ALLOC)
{
intptr_t psram_start = common_hal_espidf_get_psram_start();
intptr_t psram_end = common_hal_espidf_get_psram_end();
size_t psram_amount = psram_end - psram_start;
size_t biggest_block = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
size_t try_alloc = MIN(biggest_block, psram_amount - common_hal_espidf_get_reserved_psram());
heap = heap_caps_malloc(try_alloc, MALLOC_CAP_SPIRAM);
return heap_caps_malloc(size, caps);
}
if (heap) {
heap_size = try_alloc;
} else {
ESP_LOGE(TAG, "CONFIG_SPIRAM_USE_CAPS_ALLOC but no spiram heap available");
}
}
#endif
void port_free(void *ptr) {
heap_caps_free(ptr);
}
if (heap == NULL) {
size_t heap_total = heap_caps_get_total_size(MALLOC_CAP_8BIT);
heap_size = MIN(heap_caps_get_largest_free_block(MALLOC_CAP_8BIT), heap_total / 2);
heap = malloc(heap_size);
heap_size = heap_size / sizeof(uint32_t);
}
if (heap == NULL) {
heap_size = 0;
return SAFE_MODE_NO_HEAP;
}
return sm;
void port_realloc(void *ptr, size_t size) {
heap_caps_realloc(ptr, size, MALLOC_CAP_8BIT);
}
size_t port_heap_get_largest_free_size(void) {
size_t free_size = heap_caps_get_largest_free_block(0);
return free_size;
}
void reset_port(void) {
@ -455,14 +428,6 @@ void reset_cpu(void) {
esp_restart();
}
uint32_t *port_heap_get_bottom(void) {
return heap;
}
uint32_t *port_heap_get_top(void) {
return heap + heap_size;
}
uint32_t *port_stack_get_limit(void) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
@ -485,10 +450,6 @@ uint32_t *port_stack_get_top(void) {
return port_stack_get_limit() + ESP_TASK_MAIN_STACK / (sizeof(uint32_t) / sizeof(StackType_t));
}
bool port_has_fixed_stack(void) {
return true;
}
void port_set_saved_word(uint32_t value) {
REG_WRITE(CP_SAVED_WORD_REGISTER, value);
}

View File

@ -106,6 +106,7 @@ SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE))
$(addprefix shared-module/, $(SRC_SHARED_MODULE)) \
$(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL))
$(BUILD)/lib/tlsf/tlsf.o: CFLAGS += -Wno-cast-align
ifneq ($(FROZEN_MPY_DIR),)
FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py')

View File

@ -101,20 +101,19 @@ void reset_cpu(void) {
for (;;) {}
}
bool port_has_fixed_stack(void) {
return false;
}
uint32_t *port_heap_get_bottom(void) {
return port_stack_get_limit();
return &_ebss;
}
uint32_t *port_heap_get_top(void) {
return port_stack_get_top();
return port_stack_get_limit();
}
uint32_t *port_stack_get_limit(void) {
return &_ebss;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
return port_stack_get_top() - (CIRCUITPY_DEFAULT_STACK_SIZE + CIRCUITPY_EXCEPTION_STACK_SIZE) / sizeof(uint32_t);
#pragma GCC diagnostic pop
}
uint32_t *port_stack_get_top(void) {

View File

@ -89,6 +89,8 @@ ifeq ($(CIRCUITPY_SWO_TRACE), 1)
CFLAGS += -finstrument-functions -finstrument-functions-exclude-file-list=tinyusb -finstrument-functions-exclude-function-list='USB_OTG1_IRQHandler,usb_irq_handler,nlr_push,CLOCK_EnableClock,CLOCK_SetDiv,CLOCK_SetMux,__DMB,__ISB,__DSB,SCB_EnableICache,SCB_EnableDCache,ARM_MPU_Disable,ARM_MPU_Enable,SCB_DisableDCache,SCB_DisableICache,__enable_irq,__disable_irq,__set_MSP,port_get_raw_ticks,supervisor_ticks_ms64'
endif
$(BUILD)/lib/tlsf/tlsf.o: CFLAGS += -Wno-cast-align
LD_FILES = $(wildcard boards/$(BOARD)/*.ld) $(addprefix linking/, flash/$(FLASH).ld chip_family/$(CHIP_FAMILY).ld common.ld)
LD_SCRIPT_FLAG := -Wl,-T,

View File

@ -499,10 +499,6 @@ uint32_t *port_stack_get_top(void) {
return &_ld_stack_top;
}
bool port_has_fixed_stack(void) {
return true;
}
uint32_t *port_heap_get_bottom(void) {
return &_ld_heap_start;
}

View File

@ -61,7 +61,7 @@ void ble_drv_remove_heap_handlers(void) {
ble_drv_evt_handler_entry_t *it = MP_STATE_VM(ble_drv_evt_handler_entries);
while (it != NULL) {
// If the param is on the heap, then delete the handler.
if (HEAP_PTR(it->param)) {
if (gc_ptr_on_heap(it->param)) {
ble_drv_remove_event_handler(it->func, it->param);
}
it = it->next;

View File

@ -16,3 +16,7 @@ CIRCUITPY_ONEWIREIO = 0
CIRCUITPY_PIXELBUF = 1
CIRCUITPY_PIXELMAP = 0
CIRCUITPY_TOUCHIO = 0
# Features to disable
CIRCUITPY_SAFEMODE_PY = 0
CIRCUITPY_OPT_LOAD_ATTR_FAST_PATH = 0

View File

@ -286,15 +286,14 @@ uint32_t *port_heap_get_bottom(void) {
}
uint32_t *port_heap_get_top(void) {
return port_stack_get_top();
}
bool port_has_fixed_stack(void) {
return false;
return port_stack_get_limit();
}
uint32_t *port_stack_get_limit(void) {
return &_euninitialized;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
return port_stack_get_top() - (CIRCUITPY_DEFAULT_STACK_SIZE + CIRCUITPY_EXCEPTION_STACK_SIZE) / sizeof(uint32_t);
#pragma GCC diagnostic pop
}
uint32_t *port_stack_get_top(void) {

View File

@ -173,7 +173,7 @@ endif
# Remove -Wno-stringop-overflow after we can test with CI's GCC 10. Mac's looks weird.
DISABLE_WARNINGS = -Wno-stringop-overflow -Wno-cast-align
CFLAGS += $(INC) -Wall -Werror -std=gnu11 -nostdlib -fshort-enums $(BASE_CFLAGS) $(CFLAGS_MOD) $(COPT) $(DISABLE_WARNINGS) -Werror=missing-prototypes
CFLAGS += $(INC) -Wall -Werror -std=gnu11 -fshort-enums $(BASE_CFLAGS) $(CFLAGS_MOD) $(COPT) $(DISABLE_WARNINGS) -Werror=missing-prototypes
CFLAGS += \
-march=armv6-m \

View File

@ -31,6 +31,7 @@
#include "shared-bindings/time/__init__.h"
#include "common-hal/pwmio/PWMOut.h"
#include "common-hal/rp2pio/StateMachine.h"
#include "supervisor/port.h"
#include "src/common/pico_stdlib/include/pico/stdlib.h"
#include "src/rp2040/hardware_structs/include/hardware/structs/mpu.h"
@ -123,8 +124,6 @@ static void __not_in_flash_func(core1_scanline_callback)(void) {
self->next_scanline += 1;
if (self->next_scanline >= self->height) {
self->next_scanline = 0;
// Update the framebuffer pointer in case it moved.
self->framebuffer = self->allocation->ptr;
}
}
@ -243,8 +242,8 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
size_t framebuffer_size = self->pitch * self->height;
self->tmdsbuf_size = tmds_bufs_per_scanline * scanline_width / DVI_SYMBOLS_PER_WORD + 1;
size_t total_allocation_size = sizeof(uint32_t) * (framebuffer_size + DVI_N_TMDS_BUFFERS * self->tmdsbuf_size);
self->allocation = allocate_memory(total_allocation_size, false, true);
if (self->allocation == NULL) {
self->framebuffer = (uint32_t *)port_malloc(total_allocation_size, true);
if (self->framebuffer == NULL) {
m_malloc_fail(total_allocation_size);
return;
}
@ -270,7 +269,6 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
// For the output.
user_irq_claim(DMA_IRQ_1);
self->framebuffer_len = framebuffer_size;
self->framebuffer = self->allocation->ptr;
self->color_depth = color_depth;
self->dvi.timing = timing;
@ -383,7 +381,7 @@ void common_hal_picodvi_framebuffer_deinit(picodvi_framebuffer_obj_t *self) {
active_picodvi = NULL;
free_memory(self->allocation);
port_free(self->framebuffer);
self->framebuffer = NULL;
self->base.type = &mp_type_NoneType;

View File

@ -28,13 +28,10 @@
#include "py/obj.h"
#include "supervisor/memory.h"
#include "lib/PicoDVI/software/libdvi/dvi.h"
typedef struct {
mp_obj_base_t base;
supervisor_allocation *allocation;
uint32_t *framebuffer;
size_t framebuffer_len; // in words
size_t tmdsbuf_size; // in words

View File

@ -233,15 +233,15 @@ void reset_cpu(void) {
}
}
bool port_has_fixed_stack(void) {
return false;
}
// From the linker script
extern uint32_t _ld_cp_dynamic_mem_start;
extern uint32_t _ld_cp_dynamic_mem_end;
uint32_t *port_stack_get_limit(void) {
return &_ld_cp_dynamic_mem_start;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
return port_stack_get_top() - (CIRCUITPY_DEFAULT_STACK_SIZE + CIRCUITPY_EXCEPTION_STACK_SIZE) / sizeof(uint32_t);
#pragma GCC diagnostic pop
}
uint32_t *port_stack_get_top(void) {
@ -249,11 +249,11 @@ uint32_t *port_stack_get_top(void) {
}
uint32_t *port_heap_get_bottom(void) {
return port_stack_get_limit();
return &_ld_cp_dynamic_mem_start;
}
uint32_t *port_heap_get_top(void) {
return port_stack_get_top();
return port_stack_get_limit();
}
uint32_t __uninitialized_ram(saved_word);

View File

@ -218,10 +218,6 @@ uint32_t *port_heap_get_top(void) {
return heap + heap_size;
}
bool port_has_fixed_stack(void) {
return true;
}
uint32_t *port_stack_get_limit(void) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"

View File

@ -29,7 +29,6 @@
#include "shared-bindings/audiobusio/PDMIn.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "py/runtime.h"
#include "supervisor/memory.h"
#include "MEMS_Audio_ll_stm32l4.h"

View File

@ -30,7 +30,6 @@
#include <stdint.h>
#include "py/obj.h"
#include "peripherals/pins.h"
#include "supervisor/memory.h"
typedef struct MemsAudio_t MemsAudio;
typedef struct MemsAudio_STM32L4SAIPDM_t MemsAudio_STM32L4SAIPDM;
@ -43,7 +42,6 @@ typedef struct {
uint8_t bit_depth;
bool mono;
uint8_t oversample;
supervisor_allocation *audio_allocation;
MemsAudio *audio;
MemsAudio_STM32L4SAIPDM *audio_impl;
/**

View File

@ -330,15 +330,15 @@ uint32_t *port_heap_get_bottom(void) {
}
uint32_t *port_heap_get_top(void) {
return &_ld_heap_end;
}
bool port_has_fixed_stack(void) {
return false;
return port_stack_get_limit();
}
uint32_t *port_stack_get_limit(void) {
return &_ld_stack_bottom;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
return port_stack_get_top() - (CIRCUITPY_DEFAULT_STACK_SIZE + CIRCUITPY_EXCEPTION_STACK_SIZE) / sizeof(uint32_t);
#pragma GCC diagnostic pop
}
uint32_t *port_stack_get_top(void) {

View File

@ -779,6 +779,7 @@ SRC_LIBM = \
$(addprefix lib/,\
libm/math.c \
libm/roundf.c \
libm/fabsf.c \
libm/fmodf.c \
libm/nearbyintf.c \
libm/ef_sqrt.c \

View File

@ -91,6 +91,11 @@ extern void common_hal_mcu_enable_interrupts(void);
#define MICROPY_FLOAT_HIGH_QUALITY_HASH (0)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_GC_ALLOC_THRESHOLD (0)
#define MICROPY_GC_SPLIT_HEAP (1)
#define MICROPY_GC_SPLIT_HEAP_AUTO (1)
#define MP_PLAT_ALLOC_HEAP(size) port_malloc(size, true)
#define MP_PLAT_FREE_HEAP(ptr) port_free(ptr)
#include "supervisor/port_heap.h"
#define MICROPY_HELPER_LEXER_UNIX (0)
#define MICROPY_HELPER_REPL (1)
#define MICROPY_KBD_EXCEPTION (1)
@ -155,7 +160,6 @@ extern void common_hal_mcu_enable_interrupts(void);
#define MICROPY_QSTR_BYTES_IN_HASH (1)
#define MICROPY_REPL_AUTO_INDENT (1)
#define MICROPY_REPL_EVENT_DRIVEN (0)
#define CIRCUITPY_SETTABLE_PYSTACK (1)
#define MICROPY_STACK_CHECK (1)
#define MICROPY_STREAMS_NON_BLOCK (1)
#ifndef MICROPY_USE_INTERNAL_PRINTF
@ -431,6 +435,18 @@ void background_callback_run_all(void);
#define CIRCUITPY_PYSTACK_SIZE 1536
#endif
// The VM heap starts at this size and doubles in size as needed until it runs
// out of memory in the outer heap. Once it can't double, it'll then grow into
// the largest contiguous free area.
#ifndef CIRCUITPY_HEAP_START_SIZE
#define CIRCUITPY_HEAP_START_SIZE (8 * 1024)
#endif
// How much of the c stack we leave to ensure we can process exceptions.
#ifndef CIRCUITPY_EXCEPTION_STACK_SIZE
#define CIRCUITPY_EXCEPTION_STACK_SIZE 1024
#endif
// Wait this long before sleeping immediately after startup, to see if we are connected via USB or BLE.
#ifndef CIRCUITPY_WORKFLOW_CONNECTION_SLEEP_DELAY
#define CIRCUITPY_WORKFLOW_CONNECTION_SLEEP_DELAY 5

65
py/gc.c
View File

@ -338,16 +338,13 @@ STATIC bool gc_try_add_heap(size_t failed_alloc) {
#endif
#if !MICROPY_GC_SPLIT_HEAP
// CIRCUITPY-CHANGE
// TODO FOR MERGE: fix this for split heap
void gc_deinit(void) {
// Run any finalisers before we stop using the heap.
// Run any finalisers before we stop using the heap. This will also free
// any additional heap areas (but not the first.)
gc_sweep_all();
/// MP_STATIC_ASSERT(!MICROPY_GC_SPLIT_HEAP);
memset(&MP_STATE_MEM(area), 0, sizeof(MP_STATE_MEM(area)));
}
#endif
void gc_lock(void) {
// This does not need to be atomic or have the GC mutex because:
@ -366,6 +363,16 @@ bool gc_is_locked(void) {
return MP_STATE_THREAD(gc_lock_depth) != 0;
}
bool gc_ptr_on_heap(void *ptr) {
for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) {
if (ptr >= (void *)area->gc_pool_start // must be above start of pool
&& ptr < (void *)area->gc_pool_end) { // must be below end of pool
return true;
}
}
return false;
}
#if MICROPY_GC_SPLIT_HEAP
// Returns the area to which this pointer belongs, or NULL if it isn't
// allocated on the GC-managed heap.
@ -383,7 +390,12 @@ STATIC inline mp_state_mem_area_t *gc_get_ptr_area(const void *ptr) {
}
#endif
// CIRCUITPY-CHANGE: VERIFY_PTR moved to gc.h to make it available elsewhere.
// ptr should be of type void*
#define VERIFY_PTR(ptr) ( \
((uintptr_t)(ptr) & (BYTES_PER_BLOCK - 1)) == 0 /* must be aligned on a block */ \
&& ptr >= (void *)MP_STATE_MEM(area).gc_pool_start /* must be above start of pool */ \
&& ptr < (void *)MP_STATE_MEM(area).gc_pool_end /* must be below end of pool */ \
)
#ifndef TRACE_MARK
#if DEBUG_PRINT
@ -837,6 +849,10 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) {
continue;
}
#endif
#if CIRCUITPY_DEBUG
gc_dump_alloc_table(&mp_plat_print);
#endif
return NULL;
}
DEBUG_printf("gc_alloc(" UINT_FMT "): no free mem, triggering GC\n", n_bytes);
@ -963,7 +979,7 @@ void gc_free(void *ptr) {
// assert(area);
#else
// CIRCUITPY-CHANGE: extra checking
if (MP_STATE_MEM(area.gc_pool_start) == 0) {
if (MP_STATE_MEM(area).gc_pool_start == 0) {
reset_into_safe_mode(SAFE_MODE_GC_ALLOC_OUTSIDE_VM);
}
assert(VERIFY_PTR(ptr));
@ -1245,39 +1261,6 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
}
#endif // Alternative gc_realloc impl
// CIRCUITPY-CHANGE
bool gc_never_free(void *ptr) {
// Check to make sure the pointer is on the heap in the first place.
if (gc_nbytes(ptr) == 0) {
return false;
}
// Pointers are stored in a linked list where each block is BYTES_PER_BLOCK long and the first
// pointer is the next block of pointers.
void **current_reference_block = MP_STATE_MEM(permanent_pointers);
void **last_reference_block = NULL;
while (current_reference_block != NULL) {
for (size_t i = 1; i < BYTES_PER_BLOCK / sizeof(void *); i++) {
if (current_reference_block[i] == NULL) {
current_reference_block[i] = ptr;
return true;
}
}
last_reference_block = current_reference_block; // keep a record of last "proper" reference block
current_reference_block = current_reference_block[0];
}
void **next_block = gc_alloc(BYTES_PER_BLOCK, false);
if (next_block == NULL) {
return false;
}
if (MP_STATE_MEM(permanent_pointers) == NULL) {
MP_STATE_MEM(permanent_pointers) = next_block;
} else {
last_reference_block[0] = next_block;
}
next_block[1] = ptr;
return true;
}
void gc_dump_info(const mp_print_t *print) {
gc_info_t info;
gc_info(&info);
@ -1421,8 +1404,6 @@ void gc_dump_alloc_table(const mp_print_t *print) {
}
mp_print_str(print, "\n");
}
// CIRCUITPY-CHANGE
mp_print_str(&mp_plat_print, "\n");
GC_EXIT();
}

20
py/gc.h
View File

@ -34,22 +34,6 @@
#include "py/mpstate.h"
#include "py/misc.h"
// CIRCUITPY-CHANGE: - this may change when use split heap
#if !MICROPY_GC_SPLIT_HEAP
#define HEAP_PTR(ptr) ( \
MP_STATE_MEM(area).gc_pool_start != 0 /* Not on the heap if it isn't inited */ \
&& ptr >= (void *)MP_STATE_MEM(area).gc_pool_start /* must be above start of pool */ \
&& ptr < (void *)MP_STATE_MEM(area).gc_pool_end /* must be below end of pool */ \
)
// CIRCUITPY-CHANGE: defined here so available outside of gc.c
// ptr should be of type void*
#define VERIFY_PTR(ptr) ( \
((uintptr_t)(ptr) & (MICROPY_BYTES_PER_GC_BLOCK - 1)) == 0 /* must be aligned on a block */ \
&& HEAP_PTR(ptr) \
)
#endif
void gc_init(void *start, void *end);
// CIRCUITPY-CHANGE
void gc_deinit(void);
@ -101,6 +85,10 @@ void *gc_realloc(void *ptr, size_t n_bytes, bool allow_move);
// very sparingly because it can leak memory.
bool gc_never_free(void *ptr);
// True if the pointer is on the MP heap. Doesn't require that it is the start
// of a block.
bool gc_ptr_on_heap(void *ptr);
typedef struct _gc_info_t {
size_t total;
size_t used;

View File

@ -38,7 +38,7 @@
//| """
//|
//| annotations: Any
//| """In CPython, ``from __future import annotations``
//| """In CPython, ``from __future__ import annotations``
//| indicates that evaluation of annotations is postponed, as described in PEP 563.
//| CircuitPython (and MicroPython) ignore annotations entirely, whether or not this feature is imported.
//| This is a limitation of CircuitPython and MicroPython for efficiency reasons.

View File

@ -66,3 +66,4 @@ typedef bool (*display_bus_begin_transaction)(mp_obj_t bus);
typedef void (*display_bus_send)(mp_obj_t bus, display_byte_type_t byte_type,
display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length);
typedef void (*display_bus_end_transaction)(mp_obj_t bus);
typedef void (*display_bus_collect_ptrs)(mp_obj_t bus);

View File

@ -51,3 +51,7 @@ void common_hal_fourwire_fourwire_send(mp_obj_t self, display_byte_type_t byte_t
display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length);
void common_hal_fourwire_fourwire_end_transaction(mp_obj_t self);
// The FourWire object always lives off the MP heap. So, code must collect any pointers
// back to the MP heap manually. Otherwise they'll get freed.
void common_hal_fourwire_fourwire_collect_ptrs(mp_obj_t obj);

View File

@ -47,3 +47,7 @@ void common_hal_i2cdisplaybus_i2cdisplaybus_send(mp_obj_t self, display_byte_typ
display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length);
void common_hal_i2cdisplaybus_i2cdisplaybus_end_transaction(mp_obj_t self);
// The I2CDisplayBus object always lives off the MP heap. So, code must collect any pointers
// back to the MP heap manually. Otherwise they'll get freed.
void common_hal_i2cdisplaybus_i2cdisplaybus_collect_ptrs(mp_obj_t obj);

View File

@ -71,7 +71,7 @@
//| :param bool scale: if True display is scaled down by 3 when displayed
//| :param bool gamma: if True apply gamma correction to all LEDs"""
//| ...
STATIC mp_obj_t is31fl3741_FrameBuffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
STATIC mp_obj_t is31fl3741_framebuffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_is31, ARG_width, ARG_height, ARG_mapping, ARG_framebuffer, ARG_scale, ARG_gamma };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_is31, MP_ARG_OBJ | MP_ARG_REQUIRED },
@ -85,8 +85,8 @@ STATIC mp_obj_t is31fl3741_FrameBuffer_make_new(const mp_obj_type_t *type, size_
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
is31fl3741_FrameBuffer_obj_t *self = &allocate_display_bus_or_raise()->is31fl3741;
self->base.type = &is31fl3741_FrameBuffer_type;
is31fl3741_framebuffer_obj_t *self = &allocate_display_bus_or_raise()->is31fl3741;
self->base.type = &is31fl3741_framebuffer_type;
if (args[ARG_width].u_int <= 0) {
mp_raise_ValueError(MP_ERROR_TEXT("width must be greater than zero"));
@ -115,7 +115,7 @@ STATIC mp_obj_t is31fl3741_FrameBuffer_make_new(const mp_obj_type_t *type, size_
framebuffer = mp_obj_new_bytearray_of_zeros(bufsize);
}
common_hal_is31fl3741_FrameBuffer_construct(self,
common_hal_is31fl3741_framebuffer_construct(self,
args[ARG_width].u_int,
args[ARG_height].u_int,
framebuffer,
@ -131,14 +131,14 @@ STATIC mp_obj_t is31fl3741_FrameBuffer_make_new(const mp_obj_type_t *type, size_
//| IS31FL3741 instance. After deinitialization, no further operations
//| may be performed."""
//| ...
STATIC mp_obj_t is31fl3741_FrameBuffer_deinit(mp_obj_t self_in) {
is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in;
common_hal_is31fl3741_FrameBuffer_deinit(self);
STATIC mp_obj_t is31fl3741_framebuffer_deinit(mp_obj_t self_in) {
is31fl3741_framebuffer_obj_t *self = (is31fl3741_framebuffer_obj_t *)self_in;
common_hal_is31fl3741_framebuffer_deinit(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_FrameBuffer_deinit_obj, is31fl3741_FrameBuffer_deinit);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_framebuffer_deinit_obj, is31fl3741_framebuffer_deinit);
static void check_for_deinit(is31fl3741_FrameBuffer_obj_t *self) {
static void check_for_deinit(is31fl3741_framebuffer_obj_t *self) {
if (self->framebuffer == NULL) {
raise_deinited_error();
}
@ -147,18 +147,18 @@ static void check_for_deinit(is31fl3741_FrameBuffer_obj_t *self) {
//| brightness: float
//| """In the current implementation, 0.0 turns the display off entirely
//| and any other value up to 1.0 turns the display on fully."""
STATIC mp_obj_t is31fl3741_FrameBuffer_get_brightness(mp_obj_t self_in) {
is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in;
STATIC mp_obj_t is31fl3741_framebuffer_get_brightness(mp_obj_t self_in) {
is31fl3741_framebuffer_obj_t *self = (is31fl3741_framebuffer_obj_t *)self_in;
check_for_deinit(self);
uint8_t current = common_hal_is31fl3741_get_current(self->is31fl3741);
float brightness = (float)current / (float)0xFF;
return mp_obj_new_float(brightness);
}
MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_FrameBuffer_get_brightness_obj, is31fl3741_FrameBuffer_get_brightness);
MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_framebuffer_get_brightness_obj, is31fl3741_framebuffer_get_brightness);
STATIC mp_obj_t is31fl3741_FrameBuffer_set_brightness(mp_obj_t self_in, mp_obj_t value_in) {
is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in;
STATIC mp_obj_t is31fl3741_framebuffer_set_brightness(mp_obj_t self_in, mp_obj_t value_in) {
is31fl3741_framebuffer_obj_t *self = (is31fl3741_framebuffer_obj_t *)self_in;
check_for_deinit(self);
mp_float_t brightness = mp_obj_get_float(value_in);
if (brightness < 0.0f || brightness > 1.0f) {
@ -170,119 +170,119 @@ STATIC mp_obj_t is31fl3741_FrameBuffer_set_brightness(mp_obj_t self_in, mp_obj_t
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(is31fl3741_FrameBuffer_set_brightness_obj, is31fl3741_FrameBuffer_set_brightness);
MP_DEFINE_CONST_FUN_OBJ_2(is31fl3741_framebuffer_set_brightness_obj, is31fl3741_framebuffer_set_brightness);
MP_PROPERTY_GETSET(is31fl3741_FrameBuffer_brightness_obj,
(mp_obj_t)&is31fl3741_FrameBuffer_get_brightness_obj,
(mp_obj_t)&is31fl3741_FrameBuffer_set_brightness_obj);
MP_PROPERTY_GETSET(is31fl3741_framebuffer_brightness_obj,
(mp_obj_t)&is31fl3741_framebuffer_get_brightness_obj,
(mp_obj_t)&is31fl3741_framebuffer_set_brightness_obj);
//| def refresh(self) -> None:
//| """Transmits the color data in the buffer to the pixels so that
//| they are shown."""
//| ...
STATIC mp_obj_t is31fl3741_FrameBuffer_refresh(mp_obj_t self_in) {
is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in;
STATIC mp_obj_t is31fl3741_framebuffer_refresh(mp_obj_t self_in) {
is31fl3741_framebuffer_obj_t *self = (is31fl3741_framebuffer_obj_t *)self_in;
check_for_deinit(self);
common_hal_is31fl3741_FrameBuffer_refresh(self, 0);
common_hal_is31fl3741_framebuffer_refresh(self, 0);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_FrameBuffer_refresh_obj, is31fl3741_FrameBuffer_refresh);
MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_framebuffer_refresh_obj, is31fl3741_framebuffer_refresh);
//| width: int
//| """The width of the display, in pixels"""
STATIC mp_obj_t is31fl3741_FrameBuffer_get_width(mp_obj_t self_in) {
is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in;
STATIC mp_obj_t is31fl3741_framebuffer_get_width(mp_obj_t self_in) {
is31fl3741_framebuffer_obj_t *self = (is31fl3741_framebuffer_obj_t *)self_in;
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(common_hal_is31fl3741_FrameBuffer_get_width(self));
return MP_OBJ_NEW_SMALL_INT(common_hal_is31fl3741_framebuffer_get_width(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_FrameBuffer_get_width_obj, is31fl3741_FrameBuffer_get_width);
MP_PROPERTY_GETTER(is31fl3741_FrameBuffer_width_obj,
(mp_obj_t)&is31fl3741_FrameBuffer_get_width_obj);
MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_framebuffer_get_width_obj, is31fl3741_framebuffer_get_width);
MP_PROPERTY_GETTER(is31fl3741_framebuffer_width_obj,
(mp_obj_t)&is31fl3741_framebuffer_get_width_obj);
//| height: int
//| """The height of the display, in pixels"""
//|
STATIC mp_obj_t is31fl3741_FrameBuffer_get_height(mp_obj_t self_in) {
is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in;
STATIC mp_obj_t is31fl3741_framebuffer_get_height(mp_obj_t self_in) {
is31fl3741_framebuffer_obj_t *self = (is31fl3741_framebuffer_obj_t *)self_in;
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(common_hal_is31fl3741_FrameBuffer_get_height(self));
return MP_OBJ_NEW_SMALL_INT(common_hal_is31fl3741_framebuffer_get_height(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_FrameBuffer_get_height_obj, is31fl3741_FrameBuffer_get_height);
MP_PROPERTY_GETTER(is31fl3741_FrameBuffer_height_obj,
(mp_obj_t)&is31fl3741_FrameBuffer_get_height_obj);
MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_framebuffer_get_height_obj, is31fl3741_framebuffer_get_height);
MP_PROPERTY_GETTER(is31fl3741_framebuffer_height_obj,
(mp_obj_t)&is31fl3741_framebuffer_get_height_obj);
STATIC const mp_rom_map_elem_t is31fl3741_FrameBuffer_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&is31fl3741_FrameBuffer_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&is31fl3741_FrameBuffer_brightness_obj) },
{ MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&is31fl3741_FrameBuffer_refresh_obj) },
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&is31fl3741_FrameBuffer_width_obj) },
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&is31fl3741_FrameBuffer_height_obj) },
STATIC const mp_rom_map_elem_t is31fl3741_framebuffer_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&is31fl3741_framebuffer_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&is31fl3741_framebuffer_brightness_obj) },
{ MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&is31fl3741_framebuffer_refresh_obj) },
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&is31fl3741_framebuffer_width_obj) },
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&is31fl3741_framebuffer_height_obj) },
};
STATIC MP_DEFINE_CONST_DICT(is31fl3741_FrameBuffer_locals_dict, is31fl3741_FrameBuffer_locals_dict_table);
STATIC MP_DEFINE_CONST_DICT(is31fl3741_framebuffer_locals_dict, is31fl3741_framebuffer_locals_dict_table);
STATIC void is31fl3741_FrameBuffer_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) {
is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in;
STATIC void is31fl3741_framebuffer_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) {
is31fl3741_framebuffer_obj_t *self = (is31fl3741_framebuffer_obj_t *)self_in;
check_for_deinit(self);
*bufinfo = self->bufinfo;
}
STATIC void is31fl3741_FrameBuffer_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmap) {
common_hal_is31fl3741_FrameBuffer_refresh(self_in, dirty_row_bitmap);
STATIC void is31fl3741_framebuffer_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmap) {
common_hal_is31fl3741_framebuffer_refresh(self_in, dirty_row_bitmap);
}
STATIC void is31fl3741_FrameBuffer_deinit_proto(mp_obj_t self_in) {
common_hal_is31fl3741_FrameBuffer_deinit(self_in);
STATIC void is31fl3741_framebuffer_deinit_proto(mp_obj_t self_in) {
common_hal_is31fl3741_framebuffer_deinit(self_in);
}
STATIC float is31fl3741_FrameBuffer_get_brightness_proto(mp_obj_t self_in) {
return common_hal_is31fl3741_FrameBuffer_get_paused(self_in) ? 0.0f : 1.0f;
STATIC float is31fl3741_framebuffer_get_brightness_proto(mp_obj_t self_in) {
return common_hal_is31fl3741_framebuffer_get_paused(self_in) ? 0.0f : 1.0f;
}
STATIC bool is31fl3741_FrameBuffer_set_brightness_proto(mp_obj_t self_in, mp_float_t value) {
common_hal_is31fl3741_FrameBuffer_set_paused(self_in, value <= 0);
STATIC bool is31fl3741_framebuffer_set_brightness_proto(mp_obj_t self_in, mp_float_t value) {
common_hal_is31fl3741_framebuffer_set_paused(self_in, value <= 0);
return true;
}
STATIC int is31fl3741_FrameBuffer_get_width_proto(mp_obj_t self_in) {
return common_hal_is31fl3741_FrameBuffer_get_width(self_in);
STATIC int is31fl3741_framebuffer_get_width_proto(mp_obj_t self_in) {
return common_hal_is31fl3741_framebuffer_get_width(self_in);
}
STATIC int is31fl3741_FrameBuffer_get_height_proto(mp_obj_t self_in) {
return common_hal_is31fl3741_FrameBuffer_get_height(self_in);
STATIC int is31fl3741_framebuffer_get_height_proto(mp_obj_t self_in) {
return common_hal_is31fl3741_framebuffer_get_height(self_in);
}
STATIC int is31fl3741_FrameBuffer_get_color_depth_proto(mp_obj_t self_in) {
STATIC int is31fl3741_framebuffer_get_color_depth_proto(mp_obj_t self_in) {
// The way displayio works depth is used to calculate bytes
// We use an uint32_t for color already so setting to 24 causes
// more changes required
return 32;
}
STATIC int is31fl3741_FrameBuffer_get_bytes_per_cell_proto(mp_obj_t self_in) {
STATIC int is31fl3741_framebuffer_get_bytes_per_cell_proto(mp_obj_t self_in) {
return 1;
}
STATIC int is31fl3741_FrameBuffer_get_native_frames_per_second_proto(mp_obj_t self_in) {
STATIC int is31fl3741_framebuffer_get_native_frames_per_second_proto(mp_obj_t self_in) {
return 60; // This was just chosen may vary based on LEDs used?
}
STATIC const framebuffer_p_t is31fl3741_FrameBuffer_proto = {
STATIC const framebuffer_p_t is31fl3741_framebuffer_proto = {
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer)
.get_bufinfo = is31fl3741_FrameBuffer_get_bufinfo,
.set_brightness = is31fl3741_FrameBuffer_set_brightness_proto,
.get_brightness = is31fl3741_FrameBuffer_get_brightness_proto,
.get_width = is31fl3741_FrameBuffer_get_width_proto,
.get_height = is31fl3741_FrameBuffer_get_height_proto,
.get_color_depth = is31fl3741_FrameBuffer_get_color_depth_proto,
.get_bytes_per_cell = is31fl3741_FrameBuffer_get_bytes_per_cell_proto,
.get_native_frames_per_second = is31fl3741_FrameBuffer_get_native_frames_per_second_proto,
.swapbuffers = is31fl3741_FrameBuffer_swapbuffers,
.deinit = is31fl3741_FrameBuffer_deinit_proto,
.get_bufinfo = is31fl3741_framebuffer_get_bufinfo,
.set_brightness = is31fl3741_framebuffer_set_brightness_proto,
.get_brightness = is31fl3741_framebuffer_get_brightness_proto,
.get_width = is31fl3741_framebuffer_get_width_proto,
.get_height = is31fl3741_framebuffer_get_height_proto,
.get_color_depth = is31fl3741_framebuffer_get_color_depth_proto,
.get_bytes_per_cell = is31fl3741_framebuffer_get_bytes_per_cell_proto,
.get_native_frames_per_second = is31fl3741_framebuffer_get_native_frames_per_second_proto,
.swapbuffers = is31fl3741_framebuffer_swapbuffers,
.deinit = is31fl3741_framebuffer_deinit_proto,
};
STATIC mp_int_t is31fl3741_FrameBuffer_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in;
STATIC mp_int_t is31fl3741_framebuffer_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
is31fl3741_framebuffer_obj_t *self = (is31fl3741_framebuffer_obj_t *)self_in;
// a readonly framebuffer would be unusual but not impossible
if ((flags & MP_BUFFER_WRITE) && !(self->bufinfo.typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
return 1;
@ -293,11 +293,11 @@ STATIC mp_int_t is31fl3741_FrameBuffer_get_buffer(mp_obj_t self_in, mp_buffer_in
}
MP_DEFINE_CONST_OBJ_TYPE(
is31fl3741_FrameBuffer_type,
is31fl3741_framebuffer_type,
MP_QSTR_is31fl3741,
MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS,
locals_dict, &is31fl3741_FrameBuffer_locals_dict,
make_new, is31fl3741_FrameBuffer_make_new,
buffer, is31fl3741_FrameBuffer_get_buffer,
protocol, &is31fl3741_FrameBuffer_proto
locals_dict, &is31fl3741_framebuffer_locals_dict,
make_new, is31fl3741_framebuffer_make_new,
buffer, is31fl3741_framebuffer_get_buffer,
protocol, &is31fl3741_framebuffer_proto
);

View File

@ -29,22 +29,22 @@
#include "shared-module/is31fl3741/FrameBuffer.h"
#include "shared-module/is31fl3741/IS31FL3741.h"
extern const mp_obj_type_t is31fl3741_FrameBuffer_type;
extern const mp_obj_type_t is31fl3741_framebuffer_type;
void common_hal_is31fl3741_FrameBuffer_construct(is31fl3741_FrameBuffer_obj_t *self, int width, int height, mp_obj_t framebuffer, is31fl3741_IS31FL3741_obj_t *is31, mp_obj_t mapping);
void common_hal_is31fl3741_framebuffer_construct(is31fl3741_framebuffer_obj_t *self, int width, int height, mp_obj_t framebuffer, is31fl3741_IS31FL3741_obj_t *is31, mp_obj_t mapping);
void common_hal_is31fl3741_FrameBuffer_deinit(is31fl3741_FrameBuffer_obj_t *);
void common_hal_is31fl3741_framebuffer_deinit(is31fl3741_framebuffer_obj_t *);
int common_hal_is31fl3741_FrameBuffer_get_width(is31fl3741_FrameBuffer_obj_t *self);
int common_hal_is31fl3741_FrameBuffer_get_height(is31fl3741_FrameBuffer_obj_t *self);
int common_hal_is31fl3741_framebuffer_get_width(is31fl3741_framebuffer_obj_t *self);
int common_hal_is31fl3741_framebuffer_get_height(is31fl3741_framebuffer_obj_t *self);
void common_hal_is31fl3741_FrameBuffer_set_global_current(is31fl3741_FrameBuffer_obj_t *self, uint8_t current);
uint8_t common_hal_is31fl3741_FrameBuffer_get_global_current(is31fl3741_FrameBuffer_obj_t *self);
void common_hal_is31fl3741_framebuffer_set_global_current(is31fl3741_framebuffer_obj_t *self, uint8_t current);
uint8_t common_hal_is31fl3741_framebuffer_get_global_current(is31fl3741_framebuffer_obj_t *self);
void common_hal_is31fl3741_FrameBuffer_set_paused(is31fl3741_FrameBuffer_obj_t *self, bool paused);
bool common_hal_is31fl3741_FrameBuffer_get_paused(is31fl3741_FrameBuffer_obj_t *self);
void common_hal_is31fl3741_FrameBuffer_refresh(is31fl3741_FrameBuffer_obj_t *self, uint8_t *dirtyrows);
void common_hal_is31fl3741_framebuffer_set_paused(is31fl3741_framebuffer_obj_t *self, bool paused);
bool common_hal_is31fl3741_framebuffer_get_paused(is31fl3741_framebuffer_obj_t *self);
void common_hal_is31fl3741_framebuffer_refresh(is31fl3741_framebuffer_obj_t *self, uint8_t *dirtyrows);
void common_hal_is31fl3741_FrameBuffer_reconstruct(is31fl3741_FrameBuffer_obj_t *self, mp_obj_t framebuffer);
void common_hal_is31fl3741_framebuffer_reconstruct(is31fl3741_framebuffer_obj_t *self, mp_obj_t framebuffer);
void is31fl3741_FrameBuffer_collect_ptrs(is31fl3741_FrameBuffer_obj_t *self);
void is31fl3741_framebuffer_collect_ptrs(is31fl3741_framebuffer_obj_t *self);

View File

@ -37,7 +37,7 @@
STATIC const mp_rom_map_elem_t is31fl3741_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_is31fl3741) },
{ MP_ROM_QSTR(MP_QSTR_IS31FL3741), MP_ROM_PTR(&is31fl3741_IS31FL3741_type) },
{ MP_ROM_QSTR(MP_QSTR_IS31FL3741_FrameBuffer), MP_ROM_PTR(&is31fl3741_FrameBuffer_type) },
{ MP_ROM_QSTR(MP_QSTR_IS31FL3741_FrameBuffer), MP_ROM_PTR(&is31fl3741_framebuffer_type) },
};
STATIC MP_DEFINE_CONST_DICT(is31fl3741_module_globals, is31fl3741_module_globals_table);

View File

@ -53,3 +53,7 @@ void common_hal_paralleldisplaybus_parallelbus_send(mp_obj_t self, display_byte_
display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length);
void common_hal_paralleldisplaybus_parallelbus_end_transaction(mp_obj_t self);
// The ParallelBus object always lives off the MP heap. So, code must collect any pointers
// back to the MP heap manually. Otherwise they'll get freed.
void common_hal_paralleldisplaybus_parallelbus_collect_ptrs(mp_obj_t self);

View File

@ -186,32 +186,6 @@ MP_PROPERTY_GETSET(supervisor_runtime_ble_workflow_obj,
(mp_obj_t)&supervisor_runtime_get_ble_workflow_obj,
(mp_obj_t)&supervisor_runtime_set_ble_workflow_obj);
//| next_stack_limit: int
//| """The size of the stack for the next vm run. If its too large, the default will be used.
//|
//| **Limitations**: Stack size is fixed at startup on the ``espressif`` port; setting this will have no effect.
//| """
STATIC mp_obj_t supervisor_runtime_get_next_stack_limit(mp_obj_t self) {
return mp_obj_new_int(get_next_stack_size());
}
MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_next_stack_limit_obj, supervisor_runtime_get_next_stack_limit);
STATIC mp_obj_t supervisor_runtime_set_next_stack_limit(mp_obj_t self, mp_obj_t size_obj) {
mp_int_t size = mp_obj_get_int(size_obj);
mp_arg_validate_int_min(size, 256, MP_QSTR_size);
if (!set_next_stack_size(size)) {
mp_raise_msg_varg(&mp_type_AttributeError,
MP_ERROR_TEXT("can't set attribute '%q'"),
MP_QSTR_next_stack_limit);
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(supervisor_runtime_set_next_stack_limit_obj, supervisor_runtime_set_next_stack_limit);
MP_PROPERTY_GETSET(supervisor_runtime_next_stack_limit_obj,
(mp_obj_t)&supervisor_runtime_get_next_stack_limit_obj,
(mp_obj_t)&supervisor_runtime_set_next_stack_limit_obj);
//| rgb_status_brightness: int
//| """Set brightness of status RGB LED from 0-255. This will take effect
//| after the current code finishes and the status LED is used to show
@ -245,7 +219,6 @@ STATIC const mp_rom_map_elem_t supervisor_runtime_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_safe_mode_reason), MP_ROM_PTR(&supervisor_runtime_safe_mode_reason_obj) },
{ MP_ROM_QSTR(MP_QSTR_autoreload), MP_ROM_PTR(&supervisor_runtime_autoreload_obj) },
{ MP_ROM_QSTR(MP_QSTR_ble_workflow), MP_ROM_PTR(&supervisor_runtime_ble_workflow_obj) },
{ MP_ROM_QSTR(MP_QSTR_next_stack_limit), MP_ROM_PTR(&supervisor_runtime_next_stack_limit_obj) },
{ MP_ROM_QSTR(MP_QSTR_rgb_status_brightness), MP_ROM_PTR(&supervisor_runtime_rgb_status_brightness_obj) },
};

View File

@ -30,6 +30,7 @@
#include "py/objstr.h"
#include "shared/runtime/interrupt_char.h"
#include "supervisor/port.h"
#include "supervisor/shared/display.h"
#include "supervisor/shared/reload.h"
#include "supervisor/shared/traceback.h"
@ -153,18 +154,18 @@ STATIC mp_obj_t supervisor_set_next_code_file(size_t n_args, const mp_obj_t *pos
}
size_t len;
const char *filename = mp_obj_str_get_data(args.filename.u_obj, &len);
free_memory(next_code_allocation);
if (next_code_configuration != NULL) {
port_free(next_code_configuration);
next_code_configuration = NULL;
}
if (options != 0 || len != 0) {
next_code_allocation = allocate_memory(align32_size(sizeof(next_code_info_t) + len + 1), false, true);
if (next_code_allocation == NULL) {
m_malloc_fail(sizeof(next_code_info_t) + len + 1);
next_code_configuration = port_malloc(sizeof(supervisor_next_code_info_t) + len + 1, false);
if (next_code_configuration == NULL) {
m_malloc_fail(sizeof(supervisor_next_code_info_t) + len + 1);
}
next_code_info_t *next_code = (next_code_info_t *)next_code_allocation->ptr;
next_code->options = options | SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
memcpy(&next_code->filename, filename, len);
next_code->filename[len] = '\0';
} else {
next_code_allocation = NULL;
next_code_configuration->options = options | SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
memcpy(&next_code_configuration->filename, filename, len);
next_code_configuration->filename[len] = '\0';
}
return mp_const_none;
}
@ -229,14 +230,14 @@ MP_DEFINE_CONST_FUN_OBJ_0(supervisor_ticks_ms_obj, supervisor_ticks_ms);
//| ...
//|
STATIC mp_obj_t supervisor_get_previous_traceback(void) {
if (prev_traceback_allocation) {
size_t len = strlen((const char *)prev_traceback_allocation->ptr);
if (prev_traceback_string) {
size_t len = strlen(prev_traceback_string);
if (len > 0) {
mp_obj_str_t *o = mp_obj_malloc(mp_obj_str_t, &mp_type_str);
o->len = len;
// callers probably aren't going to compare this string, so skip computing the hash
o->hash = 0;
o->data = (const byte *)prev_traceback_allocation->ptr;
o->data = (const byte *)prev_traceback_string;
return MP_OBJ_FROM_PTR(o);
}
}
@ -292,34 +293,33 @@ STATIC mp_obj_t supervisor_set_usb_identification(size_t n_args, const mp_obj_t
} args;
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args);
if (!usb_identification_allocation) {
usb_identification_allocation = allocate_memory(sizeof(usb_identification_t), false, true);
if (custom_usb_identification == NULL) {
custom_usb_identification = port_malloc(sizeof(usb_identification_t), false);
}
usb_identification_t *identification = (usb_identification_t *)usb_identification_allocation->ptr;
mp_arg_validate_int_range(args.vid.u_int, -1, (1 << 16) - 1, MP_QSTR_vid);
mp_arg_validate_int_range(args.pid.u_int, -1, (1 << 16) - 1, MP_QSTR_pid);
identification->vid = args.vid.u_int > -1 ? args.vid.u_int : USB_VID;
identification->pid = args.pid.u_int > -1 ? args.pid.u_int : USB_PID;
custom_usb_identification->vid = args.vid.u_int > -1 ? args.vid.u_int : USB_VID;
custom_usb_identification->pid = args.pid.u_int > -1 ? args.pid.u_int : USB_PID;
mp_buffer_info_t info;
if (args.manufacturer.u_obj != mp_const_none) {
mp_get_buffer_raise(args.manufacturer.u_obj, &info, MP_BUFFER_READ);
mp_arg_validate_length_range(info.len, 0, 126, MP_QSTR_manufacturer);
memcpy(identification->manufacturer_name, info.buf, info.len);
identification->manufacturer_name[info.len] = 0;
memcpy(custom_usb_identification->manufacturer_name, info.buf, info.len);
custom_usb_identification->manufacturer_name[info.len] = 0;
} else {
strcpy(identification->manufacturer_name, USB_MANUFACTURER);
strcpy(custom_usb_identification->manufacturer_name, USB_MANUFACTURER);
}
if (args.product.u_obj != mp_const_none) {
mp_get_buffer_raise(args.product.u_obj, &info, MP_BUFFER_READ);
mp_arg_validate_length_range(info.len, 0, 126, MP_QSTR_product);
memcpy(identification->product_name, info.buf, info.len);
identification->product_name[info.len] = 0;
memcpy(custom_usb_identification->product_name, info.buf, info.len);
custom_usb_identification->product_name[info.len] = 0;
} else {
strcpy(identification->product_name, USB_PRODUCT);
strcpy(custom_usb_identification->product_name, USB_PRODUCT);
}
return mp_const_none;

View File

@ -32,10 +32,20 @@
#include "common-hal/supervisor/Runtime.h"
#include "shared-module/supervisor/StatusBar.h"
#include "supervisor/usb.h"
typedef struct {
uint8_t options;
char filename[];
} supervisor_next_code_info_t;
extern const super_runtime_obj_t common_hal_supervisor_runtime_obj;
extern supervisor_status_bar_obj_t shared_module_supervisor_status_bar_obj;
extern mp_obj_t supervisor_ticks_ms(void);
extern char *prev_traceback_string;
extern supervisor_next_code_info_t *next_code_configuration;
extern usb_identification_t *custom_usb_identification;
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SUPERVISOR___INIT___H

View File

@ -441,4 +441,5 @@ void reset_busdisplay(busdisplay_busdisplay_obj_t *self) {
void busdisplay_busdisplay_collect_ptrs(busdisplay_busdisplay_obj_t *self) {
displayio_display_core_collect_ptrs(&self->core);
displayio_display_bus_collect_ptrs(&self->bus);
}

View File

@ -37,7 +37,6 @@
#include "shared-module/displayio/area.h"
#include "supervisor/shared/display.h"
#include "supervisor/shared/reload.h"
#include "supervisor/memory.h"
#include "supervisor/spi_flash_api.h"
#include "py/mpconfig.h"
@ -166,8 +165,8 @@ void common_hal_displayio_release_displays(void) {
common_hal_rgbmatrix_rgbmatrix_deinit(&display_buses[i].rgbmatrix);
#endif
#if CIRCUITPY_IS31FL3741
} else if (bus_type == &is31fl3741_FrameBuffer_type) {
common_hal_is31fl3741_FrameBuffer_deinit(&display_buses[i].is31fl3741);
} else if (bus_type == &is31fl3741_framebuffer_type) {
common_hal_is31fl3741_framebuffer_deinit(&display_buses[i].is31fl3741);
#endif
#if CIRCUITPY_SHARPDISPLAY
} else if (bus_type == &sharpdisplay_framebuffer_type) {
@ -260,8 +259,8 @@ void reset_displays(void) {
}
#endif
#if CIRCUITPY_IS31FL3741
} else if (display_bus_type == &is31fl3741_FrameBuffer_type) {
is31fl3741_FrameBuffer_obj_t *is31fb = &display_buses[i].is31fl3741;
} else if (display_bus_type == &is31fl3741_framebuffer_type) {
is31fl3741_framebuffer_obj_t *is31fb = &display_buses[i].is31fl3741;
if (((uint32_t)is31fb->is31fl3741->i2c) < ((uint32_t)&display_buses) ||
((uint32_t)is31fb->is31fl3741->i2c) > ((uint32_t)&display_buses + CIRCUITPY_DISPLAY_LIMIT)) {
@ -284,9 +283,9 @@ void reset_displays(void) {
}
if (!any_display_uses_this_framebuffer(&is31fb->base)) {
common_hal_is31fl3741_FrameBuffer_deinit(is31fb);
common_hal_is31fl3741_framebuffer_deinit(is31fb);
} else {
common_hal_is31fl3741_FrameBuffer_set_paused(is31fb, true);
common_hal_is31fl3741_framebuffer_set_paused(is31fb, true);
}
#endif
#if CIRCUITPY_SHARPDISPLAY
@ -351,8 +350,8 @@ void displayio_gc_collect(void) {
}
#endif
#if CIRCUITPY_IS31FL3741
if (display_bus_type == &is31fl3741_FrameBuffer_type) {
is31fl3741_FrameBuffer_collect_ptrs(&display_buses[i].is31fl3741);
if (display_bus_type == &is31fl3741_framebuffer_type) {
is31fl3741_framebuffer_collect_ptrs(&display_buses[i].is31fl3741);
}
#endif
#if CIRCUITPY_SHARPDISPLAY

View File

@ -81,7 +81,7 @@ typedef struct {
rgbmatrix_rgbmatrix_obj_t rgbmatrix;
#endif
#if CIRCUITPY_IS31FL3741
is31fl3741_FrameBuffer_obj_t is31fl3741;
is31fl3741_framebuffer_obj_t is31fl3741;
#endif
#if CIRCUITPY_SHARPDISPLAY
sharpdisplay_framebuffer_obj_t sharpdisplay;

View File

@ -76,6 +76,7 @@ void displayio_display_bus_construct(displayio_display_bus_t *self,
self->begin_transaction = common_hal_paralleldisplaybus_parallelbus_begin_transaction;
self->send = common_hal_paralleldisplaybus_parallelbus_send;
self->end_transaction = common_hal_paralleldisplaybus_parallelbus_end_transaction;
self->collect_ptrs = common_hal_paralleldisplaybus_parallelbus_collect_ptrs;
} else
#endif
#if CIRCUITPY_FOURWIRE
@ -85,6 +86,7 @@ void displayio_display_bus_construct(displayio_display_bus_t *self,
self->begin_transaction = common_hal_fourwire_fourwire_begin_transaction;
self->send = common_hal_fourwire_fourwire_send;
self->end_transaction = common_hal_fourwire_fourwire_end_transaction;
self->collect_ptrs = common_hal_fourwire_fourwire_collect_ptrs;
} else
#endif
#if CIRCUITPY_I2CDISPLAYBUS
@ -94,6 +96,7 @@ void displayio_display_bus_construct(displayio_display_bus_t *self,
self->begin_transaction = common_hal_i2cdisplaybus_i2cdisplaybus_begin_transaction;
self->send = common_hal_i2cdisplaybus_i2cdisplaybus_send;
self->end_transaction = common_hal_i2cdisplaybus_i2cdisplaybus_end_transaction;
self->collect_ptrs = common_hal_i2cdisplaybus_i2cdisplaybus_collect_ptrs;
} else
#endif
{
@ -231,3 +234,7 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d
displayio_display_bus_end_transaction(self);
}
}
void displayio_display_bus_collect_ptrs(displayio_display_bus_t *self) {
self->collect_ptrs(self->bus);
}

View File

@ -41,6 +41,7 @@ typedef struct {
display_bus_begin_transaction begin_transaction;
display_bus_send send;
display_bus_end_transaction end_transaction;
display_bus_collect_ptrs collect_ptrs;
uint16_t ram_width;
uint16_t ram_height;
int16_t colstart;

View File

@ -504,6 +504,7 @@ void epaperdisplay_epaperdisplay_reset(epaperdisplay_epaperdisplay_obj_t *self)
void epaperdisplay_epaperdisplay_collect_ptrs(epaperdisplay_epaperdisplay_obj_t *self) {
displayio_display_core_collect_ptrs(&self->core);
displayio_display_bus_collect_ptrs(&self->bus);
gc_collect_ptr((void *)self->start_sequence);
gc_collect_ptr((void *)self->stop_sequence);
}

View File

@ -42,9 +42,6 @@ void common_hal_fourwire_fourwire_construct(fourwire_fourwire_obj_t *self,
self->bus = spi;
common_hal_busio_spi_never_reset(self->bus);
// Our object is statically allocated off the heap so make sure the bus object lives to the end
// of the heap as well.
gc_never_free(self->bus);
self->frequency = baudrate;
self->polarity = polarity;
@ -176,3 +173,8 @@ void common_hal_fourwire_fourwire_end_transaction(mp_obj_t obj) {
common_hal_digitalio_digitalinout_set_value(&self->chip_select, true);
common_hal_busio_spi_unlock(self->bus);
}
void common_hal_fourwire_fourwire_collect_ptrs(mp_obj_t obj) {
fourwire_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj);
gc_collect_ptr((void *)self->bus);
}

View File

@ -61,9 +61,6 @@ void common_hal_i2cdisplaybus_i2cdisplaybus_construct(i2cdisplaybus_i2cdisplaybu
// Write to the device and return 0 on success or an appropriate error code from mperrno.h
self->bus = i2c;
common_hal_busio_i2c_never_reset(self->bus);
// Our object is statically allocated off the heap so make sure the bus object lives to the end
// of the heap as well.
gc_never_free(self->bus);
self->address = device_address;
}
@ -72,6 +69,10 @@ void common_hal_i2cdisplaybus_i2cdisplaybus_deinit(i2cdisplaybus_i2cdisplaybus_o
if (self->bus == &self->inline_bus) {
common_hal_busio_i2c_deinit(self->bus);
}
// TODO figure out how to undo never_reset. maybe only mark never_reset when
// we subsume objects off the mp heap.
self->bus = NULL;
if (self->reset.base.type == &digitalio_digitalinout_type) {
common_hal_digitalio_digitalinout_deinit(&self->reset);
@ -126,3 +127,8 @@ void common_hal_i2cdisplaybus_i2cdisplaybus_end_transaction(mp_obj_t obj) {
i2cdisplaybus_i2cdisplaybus_obj_t *self = MP_OBJ_TO_PTR(obj);
common_hal_busio_i2c_unlock(self->bus);
}
void common_hal_i2cdisplaybus_i2cdisplaybus_collect_ptrs(mp_obj_t obj) {
i2cdisplaybus_i2cdisplaybus_obj_t *self = MP_OBJ_TO_PTR(obj);
gc_collect_ptr((void *)self->bus);
}

View File

@ -32,14 +32,14 @@
#include "py/objproperty.h"
#include "py/runtime.h"
#include "shared-module/is31fl3741/allocator.h"
#include "shared-bindings/is31fl3741/IS31FL3741.h"
#include "shared-bindings/is31fl3741/FrameBuffer.h"
#include "shared-bindings/util.h"
#include "shared-module/framebufferio/FramebufferDisplay.h"
#include "shared-bindings/busio/I2C.h"
#include "supervisor/port.h"
void common_hal_is31fl3741_FrameBuffer_construct(is31fl3741_FrameBuffer_obj_t *self, int width, int height, mp_obj_t framebuffer, is31fl3741_IS31FL3741_obj_t *is31, mp_obj_t mapping) {
void common_hal_is31fl3741_framebuffer_construct(is31fl3741_framebuffer_obj_t *self, int width, int height, mp_obj_t framebuffer, is31fl3741_IS31FL3741_obj_t *is31, mp_obj_t mapping) {
self->width = width;
self->height = height;
@ -48,10 +48,6 @@ void common_hal_is31fl3741_FrameBuffer_construct(is31fl3741_FrameBuffer_obj_t *s
self->is31fl3741 = is31;
common_hal_busio_i2c_never_reset(self->is31fl3741->i2c);
// Our object is statically allocated off the heap so make sure the bus object lives to the end
// of the heap as well.
gc_never_free(self->is31fl3741->i2c);
gc_never_free(self->is31fl3741);
mp_obj_t *items;
size_t len;
@ -61,7 +57,10 @@ void common_hal_is31fl3741_FrameBuffer_construct(is31fl3741_FrameBuffer_obj_t *s
mp_raise_ValueError(MP_ERROR_TEXT("LED mappings must match display size"));
}
self->mapping = common_hal_is31fl3741_allocator_impl(sizeof(uint16_t) * len);
self->mapping = port_malloc(sizeof(uint16_t) * len, false);
if (self->mapping == NULL) {
m_malloc_fail(sizeof(uint16_t) * len);
}
for (size_t i = 0; i < len; i++) {
mp_int_t value = mp_obj_get_int(items[i]);
// We only store up to 16 bits
@ -71,10 +70,10 @@ void common_hal_is31fl3741_FrameBuffer_construct(is31fl3741_FrameBuffer_obj_t *s
self->mapping[i] = (uint16_t)value;
}
common_hal_is31fl3741_FrameBuffer_reconstruct(self, framebuffer);
common_hal_is31fl3741_framebuffer_reconstruct(self, framebuffer);
}
void common_hal_is31fl3741_FrameBuffer_reconstruct(is31fl3741_FrameBuffer_obj_t *self, mp_obj_t framebuffer) {
void common_hal_is31fl3741_framebuffer_reconstruct(is31fl3741_framebuffer_obj_t *self, mp_obj_t framebuffer) {
self->paused = 1;
if (framebuffer) {
@ -89,10 +88,15 @@ void common_hal_is31fl3741_FrameBuffer_reconstruct(is31fl3741_FrameBuffer_obj_t
// verify that the matrix is big enough
mp_get_index(mp_obj_get_type(self->framebuffer), self->bufinfo.len, MP_OBJ_NEW_SMALL_INT(self->bufsize - 1), false);
} else {
common_hal_is31fl3741_free_impl(self->bufinfo.buf);
if (self->framebuffer == NULL && self->bufinfo.buf != NULL) {
port_free(self->bufinfo.buf);
}
self->framebuffer = NULL;
self->bufinfo.buf = common_hal_is31fl3741_allocator_impl(self->bufsize);
self->bufinfo.buf = port_malloc(self->bufsize, false);
if (self->bufinfo.buf == NULL) {
return;
}
self->bufinfo.len = self->bufsize;
self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
}
@ -112,14 +116,18 @@ void common_hal_is31fl3741_FrameBuffer_reconstruct(is31fl3741_FrameBuffer_obj_t
self->paused = 0;
}
void common_hal_is31fl3741_FrameBuffer_deinit(is31fl3741_FrameBuffer_obj_t *self) {
void common_hal_is31fl3741_framebuffer_deinit(is31fl3741_framebuffer_obj_t *self) {
common_hal_is31fl3741_end_transaction(self->is31fl3741); // in case we still had a lock
common_hal_is31fl3741_IS31FL3741_deinit(self->is31fl3741);
if (self->mapping != 0) {
common_hal_is31fl3741_free_impl(self->mapping);
self->mapping = 0;
if (self->mapping != NULL) {
port_free(self->mapping);
self->mapping = NULL;
}
if (self->framebuffer == NULL && self->bufinfo.buf != NULL) {
port_free(self->bufinfo.buf);
}
self->base.type = NULL;
@ -129,15 +137,15 @@ void common_hal_is31fl3741_FrameBuffer_deinit(is31fl3741_FrameBuffer_obj_t *self
self->framebuffer = NULL;
}
void common_hal_is31fl3741_FrameBuffer_set_paused(is31fl3741_FrameBuffer_obj_t *self, bool paused) {
void common_hal_is31fl3741_framebuffer_set_paused(is31fl3741_framebuffer_obj_t *self, bool paused) {
self->paused = paused;
}
bool common_hal_is31fl3741_FrameBuffer_get_paused(is31fl3741_FrameBuffer_obj_t *self) {
bool common_hal_is31fl3741_framebuffer_get_paused(is31fl3741_framebuffer_obj_t *self) {
return self->paused;
}
void common_hal_is31fl3741_FrameBuffer_refresh(is31fl3741_FrameBuffer_obj_t *self, uint8_t *dirtyrows) {
void common_hal_is31fl3741_framebuffer_refresh(is31fl3741_framebuffer_obj_t *self, uint8_t *dirtyrows) {
if (!self->paused) {
common_hal_is31fl3741_begin_transaction(self->is31fl3741);
@ -205,24 +213,16 @@ void common_hal_is31fl3741_FrameBuffer_refresh(is31fl3741_FrameBuffer_obj_t *sel
}
}
int common_hal_is31fl3741_FrameBuffer_get_width(is31fl3741_FrameBuffer_obj_t *self) {
int common_hal_is31fl3741_framebuffer_get_width(is31fl3741_framebuffer_obj_t *self) {
return self->width;
}
int common_hal_is31fl3741_FrameBuffer_get_height(is31fl3741_FrameBuffer_obj_t *self) {
int common_hal_is31fl3741_framebuffer_get_height(is31fl3741_framebuffer_obj_t *self) {
return self->height;
}
void *common_hal_is31fl3741_allocator_impl(size_t sz) {
supervisor_allocation *allocation = allocate_memory(align32_size(sz), false, true);
return allocation ? allocation->ptr : NULL;
}
void common_hal_is31fl3741_free_impl(void *ptr_in) {
free_memory(allocation_from_ptr(ptr_in));
}
void is31fl3741_FrameBuffer_collect_ptrs(is31fl3741_FrameBuffer_obj_t *self) {
void is31fl3741_framebuffer_collect_ptrs(is31fl3741_framebuffer_obj_t *self) {
gc_collect_ptr(self->framebuffer);
gc_collect_ptr(self->mapping);
gc_collect_ptr(self->is31fl3741->i2c);
gc_collect_ptr(self->is31fl3741);
}

View File

@ -30,7 +30,7 @@
#include "lib/protomatter/src/core.h"
#include "shared-module/is31fl3741/IS31FL3741.h"
extern const mp_obj_type_t is31fl3741_FrameBuffer_type;
extern const mp_obj_type_t is31fl3741_framebuffer_type;
typedef struct {
mp_obj_base_t base;
is31fl3741_IS31FL3741_obj_t *is31fl3741;
@ -43,4 +43,4 @@ typedef struct {
bool paused;
bool scale;
bool auto_gamma;
} is31fl3741_FrameBuffer_obj_t;
} is31fl3741_framebuffer_obj_t;

View File

@ -42,7 +42,6 @@
#include "py/parsenum.h"
#include "py/runtime.h"
#include "supervisor/filesystem.h"
#include "supervisor/memory.h"
#define GETENV_PATH "/settings.toml"

View File

@ -29,9 +29,12 @@
// If non-sequential pins aren't supported, then this default (weak)
// implementation will raise an exception for you.
__attribute__((weak))
void common_hal_paralleldisplaybus_parallelbus_construct_nonsequential(paralleldisplaybus_parallelbus_obj_t *self,
MP_WEAK void common_hal_paralleldisplaybus_parallelbus_construct_nonsequential(paralleldisplaybus_parallelbus_obj_t *self,
uint8_t n_pins, const mcu_pin_obj_t **data_pins, const mcu_pin_obj_t *command, const mcu_pin_obj_t *chip_select,
const mcu_pin_obj_t *write, const mcu_pin_obj_t *read, const mcu_pin_obj_t *reset, uint32_t frequency) {
mp_raise_NotImplementedError(MP_ERROR_TEXT("This microcontroller only supports data0=, not data_pins=, because it requires contiguous pins."));
}
MP_WEAK void common_hal_paralleldisplaybus_parallelbus_collect_ptrs(mp_obj_t self) {
}

View File

@ -79,12 +79,11 @@ STATIC void common_hal_rgbmatrix_rgbmatrix_construct1(rgbmatrix_rgbmatrix_obj_t
}
// verify that the matrix is big enough
mp_get_index(mp_obj_get_type(self->framebuffer), self->bufinfo.len, MP_OBJ_NEW_SMALL_INT(self->bufsize - 1), false);
self->allocation = NULL;
} else {
// The supervisor allocation can move memory by changing self->allocation->ptr.
// So we hold onto it and update bufinfo every time we use it.
self->allocation = allocate_memory(align32_size(self->bufsize), false, true);
self->bufinfo.buf = self->allocation->ptr;
self->bufinfo.buf = port_malloc(self->bufsize, false);
if (self->bufinfo.buf == NULL) {
m_malloc_fail(self->bufsize);
}
self->bufinfo.len = self->bufsize;
self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
}
@ -150,9 +149,7 @@ STATIC void free_pin_seq(uint8_t *seq, int count) {
extern int pm_row_count;
STATIC void common_hal_rgbmatrix_rgbmatrix_deinit1(rgbmatrix_rgbmatrix_obj_t *self) {
if (self->timer != NULL) {
common_hal_rgbmatrix_timer_disable(self->timer);
}
common_hal_rgbmatrix_timer_disable(self->timer);
if (_PM_protoPtr == &self->protomatter) {
_PM_protoPtr = NULL;
@ -166,15 +163,14 @@ STATIC void common_hal_rgbmatrix_rgbmatrix_deinit1(rgbmatrix_rgbmatrix_obj_t *se
// If it was supervisor-allocated, it is supervisor-freed and the pointer
// is zeroed, otherwise the pointer is just zeroed
if (self->allocation != NULL) {
free_memory(self->allocation);
if (self->framebuffer == mp_const_none) {
port_free(self->bufinfo.buf);
}
self->bufinfo.buf = NULL;
// If a framebuffer was passed in to the constructor, clear the reference
// here so that it will become GC'able
self->framebuffer = mp_const_none;
self->bufinfo.buf = NULL;
}
void common_hal_rgbmatrix_rgbmatrix_deinit(rgbmatrix_rgbmatrix_obj_t *self) {
@ -194,21 +190,22 @@ void common_hal_rgbmatrix_rgbmatrix_deinit(rgbmatrix_rgbmatrix_obj_t *self) {
}
void common_hal_rgbmatrix_rgbmatrix_get_bufinfo(rgbmatrix_rgbmatrix_obj_t *self, mp_buffer_info_t *bufinfo) {
if (self->allocation != NULL) {
self->bufinfo.buf = self->allocation->ptr;
}
*bufinfo = self->bufinfo;
}
void common_hal_rgbmatrix_rgbmatrix_reconstruct(rgbmatrix_rgbmatrix_obj_t *self) {
common_hal_rgbmatrix_rgbmatrix_set_paused(self, true);
// Stop using any Python provided framebuffer.
if (self->framebuffer != mp_const_none) {
memset(&self->bufinfo, 0, sizeof(self->bufinfo));
self->bufinfo.buf = port_malloc(self->bufsize, false);
if (self->bufinfo.buf == NULL) {
common_hal_rgbmatrix_rgbmatrix_deinit(self);
return;
}
self->bufinfo.len = self->bufsize;
self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
}
#if CIRCUITPY_RGBMATRIX_USES_SUPERVISOR_ALLOCATION
common_hal_rgbmatrix_rgbmatrix_set_paused(self, true);
common_hal_rgbmatrix_rgbmatrix_deinit1(self);
common_hal_rgbmatrix_rgbmatrix_construct1(self, mp_const_none);
#endif
memset(self->bufinfo.buf, 0, self->bufinfo.len);
common_hal_rgbmatrix_rgbmatrix_set_paused(self, false);
}
@ -222,9 +219,6 @@ void common_hal_rgbmatrix_rgbmatrix_set_paused(rgbmatrix_rgbmatrix_obj_t *self,
_PM_stop(&self->protomatter);
} else if (!paused && self->paused) {
_PM_resume(&self->protomatter);
if (self->allocation) {
self->bufinfo.buf = self->allocation->ptr;
}
_PM_convert_565(&self->protomatter, self->bufinfo.buf, self->width);
_PM_swapbuffer_maybe(&self->protomatter);
}
@ -237,9 +231,6 @@ bool common_hal_rgbmatrix_rgbmatrix_get_paused(rgbmatrix_rgbmatrix_obj_t *self)
void common_hal_rgbmatrix_rgbmatrix_refresh(rgbmatrix_rgbmatrix_obj_t *self) {
if (!self->paused) {
if (self->allocation != NULL) {
self->bufinfo.buf = self->allocation->ptr;
}
_PM_convert_565(&self->protomatter, self->bufinfo.buf, self->width);
_PM_swapbuffer_maybe(&self->protomatter);
}
@ -253,44 +244,3 @@ int common_hal_rgbmatrix_rgbmatrix_get_height(rgbmatrix_rgbmatrix_obj_t *self) {
int computed_height = (self->rgb_count / 3) * (1 << (self->addr_count)) * self->tile;
return computed_height;
}
// Track the returned pointers and their matching allocation so that we can free
// them even when the memory was moved by the supervisor. This prevents leaks
// but doesn't protect against the memory being used after its been freed! The
// long term fix is to utilize a permanent heap that can be shared with MP's
// split heap.
typedef struct matrix_allocation {
void *original_pointer;
supervisor_allocation *allocation;
} matrix_allocation_t;
// Four should be more than we ever need. ProtoMatter does 3 allocations currently.
static matrix_allocation_t allocations[4];
void *common_hal_rgbmatrix_allocator_impl(size_t sz) {
supervisor_allocation *allocation = allocate_memory(align32_size(sz), false, true);
if (allocation == NULL) {
return NULL;
}
for (size_t i = 0; i < sizeof(allocations); i++) {
matrix_allocation_t *matrix_allocation = &allocations[i];
if (matrix_allocation->original_pointer == NULL) {
matrix_allocation->original_pointer = allocation->ptr;
matrix_allocation->allocation = allocation;
return allocation->ptr;
}
}
return NULL;
}
void common_hal_rgbmatrix_free_impl(void *ptr_in) {
for (size_t i = 0; i < sizeof(allocations); i++) {
matrix_allocation_t *matrix_allocation = &allocations[i];
if (matrix_allocation->original_pointer == ptr_in) {
matrix_allocation->original_pointer = NULL;
free_memory(matrix_allocation->allocation);
matrix_allocation->allocation = NULL;
return;
}
}
}

View File

@ -28,14 +28,12 @@
#include "py/obj.h"
#include "lib/protomatter/src/core.h"
#include "supervisor/memory.h"
extern const mp_obj_type_t rgbmatrix_RGBMatrix_type;
typedef struct {
mp_obj_base_t base;
mp_obj_t framebuffer;
mp_buffer_info_t bufinfo;
supervisor_allocation *allocation;
Protomatter_core protomatter;
void *timer;
uint16_t bufsize, width;

View File

@ -29,17 +29,13 @@
#include <stdbool.h>
#include "py/gc.h"
#include "py/misc.h"
#include "supervisor/memory.h"
#include "supervisor/port.h"
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S2)
// Use DMA-capable RAM (not PSRAM) for framebuffer:
#include "components/heap/include/esp_heap_caps.h"
#define _PM_allocate(x) heap_caps_malloc(x, MALLOC_CAP_DMA | MALLOC_CAP_8BIT)
#define _PM_free(x) heap_caps_free(x)
#else
#define _PM_allocate common_hal_rgbmatrix_allocator_impl
#define _PM_free(x) (common_hal_rgbmatrix_free_impl((x)), (x) = NULL, (void)0)
#define _PM_allocate(x) port_malloc(x, true)
#define _PM_free(x) port_free(x)
#endif
extern void *common_hal_rgbmatrix_allocator_impl(size_t sz);
extern void common_hal_rgbmatrix_free_impl(void *);

View File

@ -32,8 +32,7 @@
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h"
#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h"
#include "supervisor/memory.h"
#include "supervisor/port.h"
#define SHARPMEM_BIT_WRITECMD_LSB (0x80)
#define JDI_BIT_WRITECMD_LSB (0x90)
@ -86,9 +85,6 @@ void common_hal_sharpdisplay_framebuffer_reset(sharpdisplay_framebuffer_obj_t *s
}
void common_hal_sharpdisplay_framebuffer_reconstruct(sharpdisplay_framebuffer_obj_t *self) {
// Look up the allocation by the old pointer and get the new pointer from it.
supervisor_allocation *alloc = allocation_from_ptr(self->bufinfo.buf);
self->bufinfo.buf = alloc ? alloc->ptr : NULL;
}
void common_hal_sharpdisplay_framebuffer_get_bufinfo(sharpdisplay_framebuffer_obj_t *self, mp_buffer_info_t *bufinfo) {
@ -96,12 +92,11 @@ void common_hal_sharpdisplay_framebuffer_get_bufinfo(sharpdisplay_framebuffer_ob
int row_stride = common_hal_sharpdisplay_framebuffer_get_row_stride(self);
int height = common_hal_sharpdisplay_framebuffer_get_height(self);
self->bufinfo.len = row_stride * height + 2;
supervisor_allocation *alloc = allocate_memory(align32_size(self->bufinfo.len), false, true);
if (alloc == NULL) {
self->bufinfo.buf = port_malloc(self->bufinfo.len, false);
if (self->bufinfo.buf == NULL) {
m_malloc_fail(self->bufinfo.len);
}
self->bufinfo.buf = alloc->ptr;
memset(alloc->ptr, 0, self->bufinfo.len);
memset(self->bufinfo.buf, 0, self->bufinfo.len);
uint8_t *data = self->bufinfo.buf;
if (self->jdi_display) {
@ -136,7 +131,7 @@ void common_hal_sharpdisplay_framebuffer_deinit(sharpdisplay_framebuffer_obj_t *
common_hal_reset_pin(self->chip_select.pin);
free_memory(allocation_from_ptr(self->bufinfo.buf));
port_free(self->bufinfo.buf);
memset(self, 0, sizeof(*self));
}

View File

@ -35,3 +35,12 @@ supervisor_status_bar_obj_t shared_module_supervisor_status_bar_obj = {
.type = &supervisor_status_bar_type,
},
};
// String of the last traceback.
char *prev_traceback_string = NULL;
// Custom settings for next code run.
supervisor_next_code_info_t *next_code_configuration = NULL;
// Custom USB settings.
usb_identification_t *custom_usb_identification = NULL;

View File

@ -36,8 +36,7 @@
#include "tusb.h"
#if CIRCUITPY_USB_VENDOR
// todo - this doesn't feel like it should be here.
#include "supervisor/memory.h"
#include "supervisor/port.h"
#endif
#if CFG_TUD_CDC != 2
@ -370,27 +369,25 @@ size_t usb_vendor_descriptor_length(void) {
return sizeof(usb_vendor_descriptor_template);
}
static supervisor_allocation *ms_os_20_descriptor_allocation;
static uint8_t *ms_os_20_descriptor = NULL;
size_t vendor_ms_os_20_descriptor_length() {
return sizeof(ms_os_20_descriptor_template);
return ms_os_20_descriptor != NULL ? sizeof(ms_os_20_descriptor_template) : 0;
}
uint8_t const *vendor_ms_os_20_descriptor() {
return (uint8_t *)ms_os_20_descriptor_allocation->ptr;
return ms_os_20_descriptor;
}
size_t usb_vendor_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string) {
if (ms_os_20_descriptor_template[MS_OS_20_ITF_NUM_OFFSET] == MS_OS_20_ITF_NUM_MAGIC) {
ms_os_20_descriptor_allocation =
allocate_memory(align32_size(sizeof(ms_os_20_descriptor_template)),
/*high_address*/ false, /*movable*/ false);
uint8_t *ms_os_20_descriptor_buf = (uint8_t *)ms_os_20_descriptor_allocation->ptr;
memcpy(ms_os_20_descriptor_buf, ms_os_20_descriptor_template, sizeof(ms_os_20_descriptor_template));
ms_os_20_descriptor_buf[MS_OS_20_ITF_NUM_OFFSET] = descriptor_counts->current_interface;
ms_os_20_descriptor_buf[VENDOR_IN_ENDPOINT_INDEX] = 0x80 | descriptor_counts->current_endpoint;
ms_os_20_descriptor_buf[VENDOR_OUT_ENDPOINT_INDEX] = descriptor_counts->current_endpoint;
if (ms_os_20_descriptor == NULL) {
ms_os_20_descriptor = port_malloc(sizeof(ms_os_20_descriptor_template), false);
if (ms_os_20_descriptor == NULL) {
return 0;
}
memcpy(ms_os_20_descriptor, ms_os_20_descriptor_template, sizeof(ms_os_20_descriptor_template));
ms_os_20_descriptor[MS_OS_20_ITF_NUM_OFFSET] = descriptor_counts->current_interface;
ms_os_20_descriptor[VENDOR_IN_ENDPOINT_INDEX] = 0x80 | descriptor_counts->current_endpoint;
ms_os_20_descriptor[VENDOR_OUT_ENDPOINT_INDEX] = descriptor_counts->current_endpoint;
}
memcpy(descriptor_buf, usb_vendor_descriptor_template, sizeof(usb_vendor_descriptor_template));
@ -411,7 +408,4 @@ size_t usb_vendor_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *d
return sizeof(usb_vendor_descriptor_template);
}
#endif

View File

@ -32,7 +32,7 @@
#include "py/runtime.h"
#include "shared-bindings/usb_hid/__init__.h"
#include "shared-bindings/usb_hid/Device.h"
#include "supervisor/memory.h"
#include "supervisor/port.h"
#include "supervisor/usb.h"
static const uint8_t usb_hid_descriptor_template[] = {
@ -78,7 +78,7 @@ static const uint8_t usb_hid_descriptor_template[] = {
#define MAX_HID_DEVICES 8
static supervisor_allocation *hid_report_descriptor_allocation;
static uint8_t *hid_report_descriptor = NULL;
static usb_hid_device_obj_t hid_devices[MAX_HID_DEVICES];
// If 0, USB HID is disabled.
static mp_int_t num_hid_devices;
@ -270,12 +270,17 @@ size_t usb_hid_report_descriptor_length(void) {
}
// Build the combined HID report descriptor in the given space.
void usb_hid_build_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length) {
void usb_hid_build_report_descriptor(void) {
if (!usb_hid_enabled()) {
return;
}
size_t report_length = usb_hid_report_descriptor_length();
hid_report_descriptor = port_malloc(report_length, false);
if (hid_report_descriptor == NULL) {
return;
}
uint8_t *report_descriptor_start = report_descriptor_space;
uint8_t *report_descriptor_start = hid_report_descriptor;
for (mp_int_t i = 0; i < num_hid_devices; i++) {
usb_hid_device_obj_t *device = &hid_devices[i];
@ -291,23 +296,6 @@ void usb_hid_build_report_descriptor(uint8_t *report_descriptor_space, size_t re
}
}
// Call this after the heap and VM are finished.
void usb_hid_save_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length) {
if (!usb_hid_enabled()) {
return;
}
// Allocate storage that persists across VMs to hold the combined report descriptor.
// and to remember the device details.
// Copy the descriptor from the temporary area to a supervisor storage allocation that
// will leave between VM instantiations.
hid_report_descriptor_allocation =
allocate_memory(align32_size(report_descriptor_length),
/*high_address*/ false, /*movable*/ false);
memcpy((uint8_t *)hid_report_descriptor_allocation->ptr, report_descriptor_space, report_descriptor_length);
}
void usb_hid_gc_collect(void) {
gc_collect_ptr(hid_devices_tuple);
@ -345,7 +333,7 @@ bool usb_hid_get_device_with_report_id(uint8_t report_id, usb_hid_device_obj_t *
// Application returns pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf) {
return (uint8_t *)hid_report_descriptor_allocation->ptr;
return (uint8_t *)hid_report_descriptor;
}
// Callback invoked when we receive a SET_PROTOCOL request.

View File

@ -42,8 +42,7 @@ size_t usb_hid_report_descriptor_length(void);
void usb_hid_setup_devices(void);
size_t usb_hid_report_descriptor_length(void);
void usb_hid_build_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length);
void usb_hid_save_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length);
void usb_hid_build_report_descriptor(void);
bool usb_hid_get_device_with_report_id(uint8_t report_id, usb_hid_device_obj_t **device_out, size_t *id_idx_out);

View File

@ -33,7 +33,6 @@
#include "py/objtuple.h"
#include "shared-bindings/usb_midi/PortIn.h"
#include "shared-bindings/usb_midi/PortOut.h"
#include "supervisor/memory.h"
#include "supervisor/usb.h"
#include "tusb.h"

View File

@ -1,77 +0,0 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 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.
*/
// Basic allocations outside them for areas such as the VM heap and stack.
// supervisor/shared/memory.c has a basic implementation for a continuous chunk of memory. Add it
// to a SRC_ in a Makefile to use it.
#ifndef MICROPY_INCLUDED_SUPERVISOR_MEMORY_H
#define MICROPY_INCLUDED_SUPERVISOR_MEMORY_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
typedef struct {
uint32_t *ptr;
} supervisor_allocation;
void free_memory(supervisor_allocation *allocation);
// Find the allocation with the given ptr, NULL if not found. When called from the context of a
// supervisor_move_memory() callback, finds the allocation that had that ptr *before* the move, but
// the returned allocation already contains the ptr after the move.
// When called with NULL, may return either NULL or an unused allocation whose ptr is NULL (this is
// a feature used internally in allocate_memory to save code size). Passing the return value to
// free_memory() is a permissible no-op in either case.
supervisor_allocation *allocation_from_ptr(void *ptr);
supervisor_allocation *allocate_remaining_memory(void);
// Allocate a piece of a given length in bytes. If high_address is true then it should be allocated
// at a lower address from the top of the stack. Otherwise, addresses will increase starting after
// statically allocated memory. If movable is false, memory will be taken from outside the GC heap
// and will stay stationary until freed. While the VM is running, this will fail unless a previous
// allocation of exactly matching length has recently been freed. If movable is true, memory will be
// taken from either outside or inside the GC heap, and when the VM exits, will be moved outside.
// The ptr of the returned supervisor_allocation will change at that point. If you need to be
// notified of that, add your own callback function at the designated place near the end of
// supervisor_move_memory().
supervisor_allocation *allocate_memory(uint32_t length, bool high_address, bool movable);
static inline size_t align32_size(size_t size) {
return (size + 3) & ~3;
}
size_t get_allocation_length(supervisor_allocation *allocation);
// Called after the GC heap is freed, transfers movable allocations from the GC heap to the
// supervisor heap and compacts the supervisor heap.
void supervisor_move_memory(void);
#endif // MICROPY_INCLUDED_SUPERVISOR_MEMORY_H

View File

@ -24,12 +24,11 @@
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SUPERVISOR_PORT_H
#define MICROPY_INCLUDED_SUPERVISOR_PORT_H
#pragma once
#include "py/mpconfig.h"
#include <stdbool.h>
#include <stddef.h>
#include "supervisor/memory.h"
#include "supervisor/shared/safe_mode.h"
// Provided by the linker;
@ -43,11 +42,6 @@ extern uint32_t _ebss;
safe_mode_t port_init(void);
// If the port does not initialize the heap during port_init(), it must provide
// this function which is called after CIRCUITPY is mounted.
// If not required, a default (weak) implementation that does nothing is used.
safe_mode_t port_heap_init(safe_mode_t);
// Reset the microcontroller completely.
void reset_cpu(void) NORETURN;
@ -63,9 +57,6 @@ uint32_t *port_stack_get_limit(void);
// Get stack top address
uint32_t *port_stack_get_top(void);
// True if stack is not located inside heap (at the top)
bool port_has_fixed_stack(void);
// Get heap bottom address
uint32_t *port_heap_get_bottom(void);
@ -137,5 +128,3 @@ void port_boot_info(void);
// Some ports want to mark additional pointers as gc roots.
// A default weak implementation is provided that does nothing.
void port_gc_collect(void);
#endif // MICROPY_INCLUDED_SUPERVISOR_PORT_H

View File

@ -1,9 +1,9 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Mark Komus
* Copyright (c) 2023 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
@ -27,9 +27,23 @@
#pragma once
#include <stdbool.h>
#include "py/gc.h"
#include "py/misc.h"
#include "supervisor/memory.h"
#include <stddef.h>
extern void *common_hal_is31fl3741_allocator_impl(size_t sz);
extern void common_hal_is31fl3741_free_impl(void *);
// Ports provide a heap for allocations that live outside the VM. The VM heap
// is allocated into it in split chunks. The supervisor provides a default heap
// implementation for ports that don't have one of their own. Allocations done
// to the outer heap *must* be explicitly managed. Only VM allocations are garbage
// collected.
// Called after port_init(). Ports can init the heap earlier in `port_init()` if
// they need and leave this empty. Splitting this out allows us to provide a weak
// implementation to use by default.
void port_heap_init(void);
void *port_malloc(size_t size, bool dma_capable);
void port_free(void *ptr);
void port_realloc(void *ptr, size_t size);
size_t port_heap_get_largest_free_size(void);

View File

@ -126,7 +126,7 @@ void background_callback_reset() {
background_callback_t *cb = (background_callback_t *)callback_head;
while (cb) {
background_callback_t *next = cb->next;
if (!HEAP_PTR((void *)cb)) {
if (gc_ptr_on_heap((void *)cb)) {
*previous_next = cb;
previous_next = &cb->next;
cb->next = NULL;

View File

@ -33,7 +33,6 @@
#include "shared-bindings/displayio/Group.h"
#include "shared-bindings/displayio/Palette.h"
#include "shared-bindings/displayio/TileGrid.h"
#include "supervisor/memory.h"
#if CIRCUITPY_RGBMATRIX
#include "shared-module/displayio/__init__.h"
@ -49,6 +48,10 @@
#include "supervisor/shared/status_bar.h"
#endif
#if CIRCUITPY_TERMINALIO
#include "supervisor/port.h"
#endif
#if CIRCUITPY_REPL_LOGO
extern uint32_t blinka_bitmap_data[];
extern displayio_bitmap_t blinka_bitmap;
@ -56,7 +59,8 @@ extern displayio_bitmap_t blinka_bitmap;
extern displayio_group_t circuitpython_splash;
#if CIRCUITPY_TERMINALIO
static supervisor_allocation *tilegrid_tiles = NULL;
static uint8_t *tilegrid_tiles = NULL;
static size_t tilegrid_tiles_size = 0;
#endif
void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
@ -89,14 +93,15 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
}
// Reuse the previous allocation if possible
if (tilegrid_tiles) {
if (get_allocation_length(tilegrid_tiles) != align32_size(total_tiles)) {
free_memory(tilegrid_tiles);
if (tilegrid_tiles_size != total_tiles) {
port_free(tilegrid_tiles);
tilegrid_tiles = NULL;
tilegrid_tiles_size = 0;
reset_tiles = true;
}
}
if (!tilegrid_tiles) {
tilegrid_tiles = allocate_memory(align32_size(total_tiles), false, true);
tilegrid_tiles = port_malloc(total_tiles, false);
reset_tiles = true;
if (!tilegrid_tiles) {
return;
@ -104,8 +109,6 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
}
if (reset_tiles) {
uint8_t *tiles = (uint8_t *)tilegrid_tiles->ptr;
// Adjust the display dimensions to account for scale of the outer group.
width_px /= scale;
height_px /= scale;
@ -127,7 +130,7 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
// Right align the status bar.
status_bar->x = width_px - status_bar->pixel_width;
status_bar->top_left_y = 0;
status_bar->tiles = tiles;
status_bar->tiles = tilegrid_tiles;
status_bar->full_change = true;
scroll_area->width_in_tiles = width_in_tiles;
@ -141,7 +144,7 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
// Align the scroll area to the bottom so that the newest line isn't cutoff. The top line
// may be clipped by the status bar and that's ok.
scroll_area->y = height_px - scroll_area->pixel_height;
scroll_area->tiles = tiles + width_in_tiles;
scroll_area->tiles = tilegrid_tiles + width_in_tiles;
scroll_area->full_change = true;
common_hal_terminalio_terminal_construct(&supervisor_terminal, scroll_area, &supervisor_terminal_font, status_bar);
@ -156,8 +159,9 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
void supervisor_stop_terminal(void) {
#if CIRCUITPY_TERMINALIO
if (tilegrid_tiles != NULL) {
free_memory(tilegrid_tiles);
port_free(tilegrid_tiles);
tilegrid_tiles = NULL;
tilegrid_tiles_size = 0;
supervisor_terminal_scroll_area_text_grid.tiles = NULL;
supervisor_terminal_status_bar_text_grid.tiles = NULL;
supervisor_terminal.scroll_area = NULL;
@ -174,37 +178,6 @@ bool supervisor_terminal_started(void) {
#endif
}
void supervisor_display_move_memory(void) {
#if CIRCUITPY_TERMINALIO
displayio_tilegrid_t *scroll_area = &supervisor_terminal_scroll_area_text_grid;
displayio_tilegrid_t *status_bar = &supervisor_terminal_status_bar_text_grid;
if (tilegrid_tiles != NULL) {
status_bar->tiles = (uint8_t *)tilegrid_tiles->ptr;
scroll_area->tiles = (uint8_t *)tilegrid_tiles->ptr + scroll_area->width_in_tiles;
} else {
scroll_area->tiles = NULL;
status_bar->tiles = NULL;
}
#endif
#if CIRCUITPY_DISPLAYIO
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
#if CIRCUITPY_RGBMATRIX
if (display_buses[i].rgbmatrix.base.type == &rgbmatrix_RGBMatrix_type) {
rgbmatrix_rgbmatrix_obj_t *pm = &display_buses[i].rgbmatrix;
common_hal_rgbmatrix_rgbmatrix_reconstruct(pm);
}
#endif
#if CIRCUITPY_SHARPDISPLAY
if (display_buses[i].bus_base.type == &sharpdisplay_framebuffer_type) {
sharpdisplay_framebuffer_obj_t *sharp = &display_buses[i].sharpdisplay;
common_hal_sharpdisplay_framebuffer_reconstruct(sharp);
}
#endif
}
#endif
}
#if CIRCUITPY_TERMINALIO
#if CIRCUITPY_REPL_LOGO
mp_obj_t members[] = { &supervisor_terminal_scroll_area_text_grid, &supervisor_blinka_sprite, &supervisor_terminal_status_bar_text_grid, };

View File

@ -56,6 +56,4 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px);
void supervisor_stop_terminal(void);
bool supervisor_terminal_started(void);
void supervisor_display_move_memory(void);
#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_DISPLAY_H

View File

@ -29,17 +29,16 @@
#include <string.h>
#include "genhdr/devices.h"
#include "supervisor/flash.h"
#include "supervisor/port.h"
#include "supervisor/spi_flash_api.h"
#include "supervisor/shared/external_flash/common_commands.h"
#include "extmod/vfs.h"
#include "extmod/vfs_fat.h"
#include "py/gc.h"
#include "py/misc.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "lib/oofatfs/ff.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "supervisor/memory.h"
#define NO_SECTOR_LOADED 0xFFFFFFFF
@ -55,7 +54,8 @@ static const external_flash_device *flash_device = NULL;
// cache.
static uint32_t dirty_mask;
static supervisor_allocation *supervisor_cache = NULL;
// Table of pointers to each cached block
static uint8_t **flash_cache_table = NULL;
// Wait until both the write enable and write in progress bits have cleared.
static bool wait_for_flash_ready(void) {
@ -295,7 +295,7 @@ void supervisor_flash_init(void) {
current_sector = NO_SECTOR_LOADED;
dirty_mask = 0;
MP_STATE_VM(flash_ram_cache) = NULL;
flash_cache_table = NULL;
}
// The size of each individual block.
@ -352,47 +352,23 @@ static bool allocate_ram_cache(void) {
uint8_t blocks_per_sector = SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE;
uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE;
uint32_t table_size = blocks_per_sector * pages_per_block * sizeof(uint32_t);
uint32_t table_size = blocks_per_sector * pages_per_block * sizeof(size_t);
// Attempt to allocate outside the heap first.
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;
flash_cache_table = port_malloc(table_size, false);
for (uint8_t i = 0; i < blocks_per_sector; i++) {
for (uint8_t j = 0; j < pages_per_block; j++) {
uint32_t offset = i * pages_per_block + j;
MP_STATE_VM(flash_ram_cache)[offset] = page_start + offset * SPI_FLASH_PAGE_SIZE;
}
}
return true;
}
// Couldn't allocate outside the heap. Is the heap available?
if (!gc_alloc_possible()) {
return false;
}
MP_STATE_VM(flash_ram_cache) = m_malloc_maybe(blocks_per_sector * pages_per_block * sizeof(uint32_t));
if (MP_STATE_VM(flash_ram_cache) == NULL) {
return false;
}
// Declare i and j outside the loops in case we fail to allocate everything
// we need. In that case we'll give it back.
uint8_t i = 0;
uint8_t j = 0;
bool success = true;
for (i = 0; i < blocks_per_sector; i++) {
for (j = 0; j < pages_per_block; j++) {
uint8_t *page_cache = m_malloc_maybe(SPI_FLASH_PAGE_SIZE);
bool success = flash_cache_table != NULL;
for (i = 0; i < blocks_per_sector && success; i++) {
for (j = 0; j < pages_per_block && success; j++) {
uint8_t *page_cache = port_malloc(SPI_FLASH_PAGE_SIZE, false);
if (page_cache == NULL) {
success = false;
break;
}
MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j] = page_cache;
}
if (!success) {
break;
flash_cache_table[i * pages_per_block + j] = page_cache;
}
}
// We couldn't allocate enough so give back what we got.
@ -402,24 +378,27 @@ static bool allocate_ram_cache(void) {
i++;
for (; i > 0; i--) {
for (; j > 0; j--) {
m_free(MP_STATE_VM(flash_ram_cache)[(i - 1) * pages_per_block + (j - 1)]);
port_free(flash_cache_table[(i - 1) * pages_per_block + (j - 1)]);
}
j = pages_per_block;
}
m_free(MP_STATE_VM(flash_ram_cache));
MP_STATE_VM(flash_ram_cache) = NULL;
port_free(flash_cache_table);
flash_cache_table = NULL;
}
return success;
}
static void release_ram_cache(void) {
if (supervisor_cache != NULL) {
free_memory(supervisor_cache);
supervisor_cache = NULL;
} else if (gc_alloc_possible()) {
m_free(MP_STATE_VM(flash_ram_cache));
uint8_t blocks_per_sector = SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE;
uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE;
for (uint8_t i = 0; i < blocks_per_sector; i++) {
for (uint8_t j = 0; j < pages_per_block; j++) {
uint32_t offset = i * pages_per_block + j;
port_free(flash_cache_table[offset]);
}
}
MP_STATE_VM(flash_ram_cache) = NULL;
port_free(flash_cache_table);
flash_cache_table = NULL;
}
// Flush the cached sector from ram onto the flash. We'll free the cache unless
@ -441,7 +420,7 @@ static bool flush_ram_cache(bool keep_cache) {
for (uint8_t j = 0; j < pages_per_block; j++) {
copy_to_ram_ok = read_flash(
current_sector + (i * pages_per_block + j) * SPI_FLASH_PAGE_SIZE,
MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j],
flash_cache_table[i * pages_per_block + j],
SPI_FLASH_PAGE_SIZE);
if (!copy_to_ram_ok) {
break;
@ -462,11 +441,8 @@ static bool flush_ram_cache(bool keep_cache) {
for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) {
for (uint8_t j = 0; j < pages_per_block; j++) {
write_flash(current_sector + (i * pages_per_block + j) * SPI_FLASH_PAGE_SIZE,
MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j],
flash_cache_table[i * pages_per_block + j],
SPI_FLASH_PAGE_SIZE);
if (!keep_cache && supervisor_cache == NULL && gc_alloc_possible()) {
m_free(MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j]);
}
}
}
// We're done with the cache for now so give it back.
@ -483,7 +459,7 @@ static void spi_flash_flush_keep_cache(bool keep_cache) {
port_pin_set_output_level(MICROPY_HW_LED_MSC, true);
#endif
// If we've cached to the flash itself flush from there.
if (MP_STATE_VM(flash_ram_cache) == NULL) {
if (flash_cache_table == NULL) {
flush_scratch_flash();
} else {
flush_ram_cache(keep_cache);
@ -524,11 +500,11 @@ static bool external_flash_read_block(uint8_t *dest, uint32_t block) {
uint8_t mask = 1 << (block_index);
// We're reading from the currently cached sector.
if (current_sector == this_sector && (mask & dirty_mask) > 0) {
if (MP_STATE_VM(flash_ram_cache) != NULL) {
if (flash_cache_table != NULL) {
uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE;
for (int i = 0; i < pages_per_block; i++) {
memcpy(dest + i * SPI_FLASH_PAGE_SIZE,
MP_STATE_VM(flash_ram_cache)[block_index * pages_per_block + i],
flash_cache_table[block_index * pages_per_block + i],
SPI_FLASH_PAGE_SIZE);
}
return true;
@ -564,7 +540,7 @@ static bool external_flash_write_block(const uint8_t *data, uint32_t block) {
if (current_sector != NO_SECTOR_LOADED) {
supervisor_flash_flush();
}
if (MP_STATE_VM(flash_ram_cache) == NULL && !allocate_ram_cache()) {
if (flash_cache_table == NULL && !allocate_ram_cache()) {
erase_sector(flash_device->total_size - SPI_FLASH_ERASE_SIZE);
wait_for_flash_ready();
}
@ -573,10 +549,10 @@ static bool external_flash_write_block(const uint8_t *data, uint32_t block) {
}
dirty_mask |= mask;
// Copy the block to the appropriate cache.
if (MP_STATE_VM(flash_ram_cache) != NULL) {
if (flash_cache_table != NULL) {
uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE;
for (int i = 0; i < pages_per_block; i++) {
memcpy(MP_STATE_VM(flash_ram_cache)[block_index * pages_per_block + i],
memcpy(flash_cache_table[block_index * pages_per_block + i],
data + i * SPI_FLASH_PAGE_SIZE,
SPI_FLASH_PAGE_SIZE);
}
@ -607,5 +583,3 @@ mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block_num,
void MP_WEAK external_flash_setup(void) {
}
MP_REGISTER_ROOT_POINTER(uint8_t * *flash_ram_cache);

View File

@ -1,350 +1 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 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 "supervisor/memory.h"
#include "supervisor/port.h"
#include <string.h>
#include "py/gc.h"
#include "supervisor/shared/display.h"
enum {
CIRCUITPY_SUPERVISOR_IMMOVABLE_ALLOC_COUNT =
0
// stack + heap
+ 2
#if INTERNAL_FLASH_FILESYSTEM == 0
+ 1
#endif
#if CIRCUITPY_USB
+ 1 // device_descriptor_allocation
+ 1 // configuration_descriptor_allocation
+ 1 // string_descriptors_allocation
#endif
#if CIRCUITPY_USB_HID
+ 1 // hid_report_descriptor_allocation
+ 1 // hid_devices_allocation
#endif
#if CIRCUITPY_USB_VENDOR
+ 1 // usb_vendor_add_descriptor
#endif
+ CIRCUITPY_PORT_NUM_SUPERVISOR_ALLOCATIONS
#if CIRCUITPY_AUDIOBUSIO_PDMIN
+ 1
#endif
,
CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT =
0
// next_code_allocation
+ 1
// prev_traceback_allocation
+ 1
#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_PICODVI
2
#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];
supervisor_allocation *old_allocations;
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 || allocation->ptr == NULL) {
return;
}
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));
} else if (node == high_head) {
do {
high_head = high_head->next;
} while (high_head != NULL && (high_head->length & HOLE));
} 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);
}
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);
}
}
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 (list[index].ptr == ptr) {
return &allocations[index];
}
}
return NULL;
}
supervisor_allocation *allocate_remaining_memory(void) {
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 node;
}
static supervisor_allocation_node *allocate_memory_node(uint32_t length, bool high, bool movable) {
if (CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT == 0) {
assert(!movable);
}
// 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);
}
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);
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;
}
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;
}
// 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->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 == NULL || low_head < MP_STATE_VM(first_embedded_allocation)) &&
(high_head == NULL || 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;
}
MP_REGISTER_ROOT_POINTER(struct _supervisor_allocation_node *first_embedded_allocation);

View File

@ -26,6 +26,14 @@
#include "supervisor/port.h"
#include <string.h>
#include "py/runtime.h"
#include "lib/tlsf/tlsf.h"
static tlsf_t heap;
MP_WEAK void port_wake_main_task(void) {
}
@ -37,3 +45,37 @@ MP_WEAK void port_yield(void) {
MP_WEAK void port_boot_info(void) {
}
MP_WEAK void port_heap_init(void) {
uint32_t *heap_bottom = port_heap_get_bottom();
uint32_t *heap_top = port_heap_get_top();
size_t size = (heap_top - heap_bottom) * sizeof(uint32_t);
heap = tlsf_create_with_pool(heap_bottom, size, size);
}
MP_WEAK void *port_malloc(size_t size, bool dma_capable) {
void *block = tlsf_malloc(heap, size);
return block;
}
MP_WEAK void port_free(void *ptr) {
tlsf_free(heap, ptr);
}
MP_WEAK void port_realloc(void *ptr, size_t size) {
tlsf_realloc(heap, ptr, size);
}
static void max_size_walker(void *ptr, size_t size, int used, void *user) {
size_t *max_size = (size_t *)user;
if (!used && *max_size < size) {
*max_size = size;
}
}
MP_WEAK size_t port_heap_get_largest_free_size(void) {
size_t max_size = 0;
tlsf_walk_pool(tlsf_get_pool(heap), max_size_walker, &max_size);
// IDF does this. Not sure why.
return tlsf_fit_size(heap, max_size);
}

View File

@ -32,7 +32,6 @@
#include "supervisor/shared/reload.h"
#include "supervisor/shared/tick.h"
supervisor_allocation *next_code_allocation;
#include "shared-bindings/supervisor/Runtime.h"
// True if user has disabled autoreload.

View File

@ -27,7 +27,6 @@
#ifndef MICROPY_INCLUDED_SUPERVISOR_AUTORELOAD_H
#define MICROPY_INCLUDED_SUPERVISOR_AUTORELOAD_H
#include "supervisor/memory.h"
#include "py/obj.h"
#include "shared-bindings/supervisor/RunReason.h"
@ -47,13 +46,6 @@ enum {
AUTORELOAD_SUSPEND_WEB = 0x8
};
typedef struct {
uint8_t options;
char filename[];
} next_code_info_t;
extern supervisor_allocation *next_code_allocation;
// Helper for exiting the VM and reloading immediately.
void reload_initiate(supervisor_run_reason_t run_reason);

View File

@ -218,7 +218,7 @@ void print_safe_mode_message(safe_mode_t reason) {
message = MP_ERROR_TEXT("NLR jump failed. Likely memory corruption.");
break;
case SAFE_MODE_NO_HEAP:
message = MP_ERROR_TEXT("Unable to allocate the heap.");
message = MP_ERROR_TEXT("Unable to allocate to the heap.");
break;
case SAFE_MODE_SDK_FATAL_ERROR:
message = MP_ERROR_TEXT("Third-party firmware fatal error.");

View File

@ -32,48 +32,14 @@
#include "supervisor/port.h"
#include "supervisor/shared/safe_mode.h"
extern uint32_t _estack;
// Requested size.
static uint32_t next_stack_size = 0;
static uint32_t current_stack_size = 0;
// 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
static void allocate_stack(void) {
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;
next_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 = (mp_uint_t)port_stack_get_top() - sp;
if (next_stack_size == 0) {
next_stack_size = CIRCUITPY_DEFAULT_STACK_SIZE;
}
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, 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);
}
void stack_init(void) {
uint32_t *stack_limit = port_stack_get_limit();
*stack_limit = STACK_CANARY_VALUE;
}
inline bool stack_ok(void) {
return stack_limit == NULL || *stack_limit == STACK_CANARY_VALUE;
uint32_t *stack_limit = port_stack_get_limit();
return *stack_limit == STACK_CANARY_VALUE;
}
inline void assert_heap_ok(void) {
@ -81,44 +47,3 @@ inline void assert_heap_ok(void) {
reset_into_safe_mode(SAFE_MODE_STACK_OVERFLOW);
}
}
void stack_init(void) {
allocate_stack();
}
void stack_resize(void) {
if (stack_limit == NULL) {
return;
}
if (next_stack_size == current_stack_size) {
*stack_limit = STACK_CANARY_VALUE;
return;
}
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;
}
bool set_next_stack_size(uint32_t size) {
if (port_has_fixed_stack()) {
return false;
}
next_stack_size = size;
return true;
}
uint32_t get_next_stack_size(void) {
return next_stack_size;
}
uint32_t get_current_stack_size(void) {
return current_stack_size;
}

View File

@ -24,22 +24,16 @@
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SUPERVISOR_STACK_H
#define MICROPY_INCLUDED_SUPERVISOR_STACK_H
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include "supervisor/memory.h"
#include <stdint.h>
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.
bool set_next_stack_size(uint32_t size);
uint32_t get_next_stack_size(void);
uint32_t get_current_stack_size(void);
bool stack_ok(void);
// Use this after any calls into a library which may use a lot of stack. This will raise a Python
@ -49,5 +43,3 @@ void assert_heap_ok(void);
#ifndef STACK_CANARY_VALUE
#define STACK_CANARY_VALUE 0x017829ef
#endif
#endif // MICROPY_INCLUDED_SUPERVISOR_STACK_H

View File

@ -25,5 +25,3 @@
*/
#include "traceback.h"
supervisor_allocation *prev_traceback_allocation;

View File

@ -27,8 +27,4 @@
#ifndef MICROPY_INCLUDED_SUPERVISOR_TRACEBACK_H
#define MICROPY_INCLUDED_SUPERVISOR_TRACEBACK_H
#include "supervisor/memory.h"
extern supervisor_allocation *prev_traceback_allocation;
#endif // MICROPY_INCLUDED_SUPERVISOR_TRACEBACK_H

View File

@ -27,6 +27,7 @@
#include "py/objstr.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/Processor.h"
#include "shared-bindings/supervisor/__init__.h"
#include "supervisor/background_callback.h"
#include "supervisor/port.h"
#include "supervisor/serial.h"
@ -85,7 +86,27 @@ MP_WEAK void post_usb_init(void) {
}
void usb_init(void) {
usb_identification_t defaults;
usb_identification_t *identification;
if (custom_usb_identification != NULL) {
identification = custom_usb_identification;
} else {
// This compiles to less code than using a struct initializer.
defaults.vid = USB_VID;
defaults.pid = USB_PID;
strcpy(defaults.manufacturer_name, USB_MANUFACTURER);
strcpy(defaults.product_name, USB_PRODUCT);
identification = &defaults;
// This memory only needs to be live through the end of usb_build_descriptors.
}
if (!usb_build_descriptors(identification)) {
return;
}
init_usb_hardware();
#if CIRCUITPY_USB_HID
usb_hid_build_report_descriptor();
#endif
// Only init device. Host gets inited by the `usb_host` module common-hal.
tud_init(TUD_OPT_RHPORT);
@ -123,66 +144,6 @@ void usb_set_defaults(void) {
#endif
};
#if CIRCUITPY_USB_IDENTIFICATION
supervisor_allocation *usb_identification_allocation = NULL;
#endif
// Some dynamic USB data must be saved after boot.py. How much is needed?
size_t usb_boot_py_data_size(void) {
size_t size = sizeof(usb_identification_t);
#if CIRCUITPY_USB_HID
size += usb_hid_report_descriptor_length();
#endif
return size;
}
// Fill in the data to save.
void usb_get_boot_py_data(uint8_t *temp_storage, size_t temp_storage_size) {
#if CIRCUITPY_USB_IDENTIFICATION
if (usb_identification_allocation) {
memcpy(temp_storage, usb_identification_allocation->ptr, sizeof(usb_identification_t));
free_memory(usb_identification_allocation);
}
#else
if (false) {
}
#endif
else {
usb_identification_t defaults;
// This compiles to less code than using a struct initializer.
defaults.vid = USB_VID;
defaults.pid = USB_PID;
strcpy(defaults.manufacturer_name, USB_MANUFACTURER);
strcpy(defaults.product_name, USB_PRODUCT);
memcpy(temp_storage, &defaults, sizeof(defaults));
}
temp_storage += sizeof(usb_identification_t);
temp_storage_size -= sizeof(usb_identification_t);
#if CIRCUITPY_USB_HID
usb_hid_build_report_descriptor(temp_storage, temp_storage_size);
#endif
}
// After VM is gone, save data into non-heap storage (storage_allocations).
void usb_return_boot_py_data(uint8_t *temp_storage, size_t temp_storage_size) {
usb_identification_t identification;
memcpy(&identification, temp_storage, sizeof(usb_identification_t));
temp_storage += sizeof(usb_identification_t);
temp_storage_size -= sizeof(usb_identification_t);
#if CIRCUITPY_USB_HID
usb_hid_save_report_descriptor(temp_storage, temp_storage_size);
#endif
// Now we can also build the rest of the descriptors and place them in storage_allocations.
usb_build_descriptors(&identification);
}
// Call this when ready to run code.py or a REPL, and a VM has been started.
void usb_setup_with_vm(void) {
#if CIRCUITPY_USB_HID

View File

@ -28,7 +28,7 @@
#include "py/objstr.h"
#include "py/runtime.h"
#include "supervisor/memory.h"
#include "supervisor/port.h"
#include "supervisor/shared/safe_mode.h"
#include "supervisor/usb.h"
@ -64,14 +64,13 @@ static interface_string_t collected_interface_strings[MAX_INTERFACE_STRINGS];
static size_t collected_interface_strings_length;
static uint8_t current_interface_string;
static supervisor_allocation *device_descriptor_allocation;
static supervisor_allocation *configuration_descriptor_allocation;
static supervisor_allocation *string_descriptors_allocation;
static uint8_t *device_descriptor;
static uint8_t *configuration_descriptor;
static uint16_t *string_descriptors;
// Serial number string is UID length * 2 (2 nibbles per byte) + 1 byte for null termination.
static char serial_number_hex_string[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH * 2 + 1];
static const uint8_t device_descriptor_template[] = {
0x12, // 0 bLength
0x01, // 1 bDescriptorType (Device)
@ -110,11 +109,13 @@ static const uint8_t configuration_descriptor_template[] = {
0x32, // 8 bMaxPower 100mA
};
static void usb_build_device_descriptor(const usb_identification_t *identification) {
device_descriptor_allocation =
allocate_memory(align32_size(sizeof(device_descriptor_template)),
/*high_address*/ false, /*movable*/ false);
uint8_t *device_descriptor = (uint8_t *)device_descriptor_allocation->ptr;
static bool usb_build_device_descriptor(const usb_identification_t *identification) {
device_descriptor =
(uint8_t *)port_malloc(sizeof(device_descriptor_template),
/*dma_capable*/ false);
if (device_descriptor == NULL) {
return false;
}
memcpy(device_descriptor, device_descriptor_template, sizeof(device_descriptor_template));
device_descriptor[DEVICE_VID_LO_INDEX] = identification->vid & 0xFF;
@ -133,9 +134,11 @@ static void usb_build_device_descriptor(const usb_identification_t *identificati
usb_add_interface_string(current_interface_string, serial_number_hex_string);
device_descriptor[DEVICE_SERIAL_NUMBER_STRING_INDEX] = current_interface_string;
current_interface_string++;
return true;
}
static void usb_build_configuration_descriptor(void) {
static bool usb_build_configuration_descriptor(void) {
size_t total_descriptor_length = sizeof(configuration_descriptor_template);
// CDC should be first, for compatibility with Adafruit Windows 7 drivers.
@ -174,11 +177,13 @@ static void usb_build_configuration_descriptor(void) {
#endif
// Now we now how big the configuration descriptor will be, so we can allocate space for it.
configuration_descriptor_allocation =
allocate_memory(align32_size(total_descriptor_length),
/*high_address*/ false, /*movable*/ false);
uint8_t *configuration_descriptor = (uint8_t *)configuration_descriptor_allocation->ptr;
// Now we know how big the configuration descriptor will be, so we can allocate space for it.
configuration_descriptor =
(uint8_t *)port_malloc(total_descriptor_length,
/*dma_capable*/ false);
if (configuration_descriptor == NULL) {
return false;
}
// Copy the template, which is the first part of the descriptor, and fix up its length.
@ -260,6 +265,7 @@ static void usb_build_configuration_descriptor(void) {
descriptor_counts.num_out_endpoints > USB_NUM_OUT_ENDPOINTS) {
reset_into_safe_mode(SAFE_MODE_USB_TOO_MANY_ENDPOINTS);
}
return true;
}
// str must not be on the heap.
@ -277,14 +283,15 @@ static const uint16_t language_id[] = {
0x0409,
};
static void usb_build_interface_string_table(void) {
static bool usb_build_interface_string_table(void) {
// Allocate space for the le16 String descriptors.
// Space needed is 2 bytes for String Descriptor header, then 2 bytes for each character
string_descriptors_allocation =
allocate_memory(align32_size(current_interface_string * 2 + collected_interface_strings_length * 2),
/*high_address*/ false, /*movable*/ false);
uint16_t *string_descriptors = (uint16_t *)string_descriptors_allocation->ptr;
string_descriptors =
port_malloc(current_interface_string * 2 + collected_interface_strings_length * 2,
/*dma_capable*/ false);
if (string_descriptors == NULL) {
return false;
}
uint16_t *string_descriptor = string_descriptors;
@ -312,11 +319,11 @@ static void usb_build_interface_string_table(void) {
// Move to next descriptor slot.
string_descriptor += descriptor_size_words;
}
return true;
}
// After boot.py runs, the USB devices to be used have been chosen, and the descriptors can be set up.
// This is called after the VM is finished, because it uses storage_allocations.
void usb_build_descriptors(const usb_identification_t *identification) {
bool usb_build_descriptors(const usb_identification_t *identification) {
uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH];
common_hal_mcu_processor_get_uid(raw_id);
@ -333,15 +340,15 @@ void usb_build_descriptors(const usb_identification_t *identification) {
current_interface_string = 1;
collected_interface_strings_length = 0;
usb_build_device_descriptor(identification);
usb_build_configuration_descriptor();
usb_build_interface_string_table();
return usb_build_device_descriptor(identification) &&
usb_build_configuration_descriptor() &&
usb_build_interface_string_table();
}
// Invoked when GET DEVICE DESCRIPTOR is received.
// Application return pointer to descriptor
uint8_t const *tud_descriptor_device_cb(void) {
return (uint8_t *)device_descriptor_allocation->ptr;
return device_descriptor;
}
// Invoked when GET CONFIGURATION DESCRIPTOR is received.
@ -349,7 +356,7 @@ uint8_t const *tud_descriptor_device_cb(void) {
// Descriptor contents must exist long enough for transfer to complete
uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
return (uint8_t *)configuration_descriptor_allocation->ptr;
return configuration_descriptor;
}
// Invoked when GET STRING DESCRIPTOR request is received.

View File

@ -35,15 +35,3 @@ void assert_heap_ok(void) {
void stack_init(void) {
}
void stack_resize(void) {
}
bool set_next_stack_size(uint32_t size) {
(void)size;
return true;
}
uint32_t get_current_stack_size(void) {
return 0;
}

View File

@ -1,5 +1,6 @@
SRC_SUPERVISOR = \
main.c \
lib/tlsf/tlsf.c \
supervisor/port.c \
supervisor/shared/background_callback.c \
supervisor/shared/board.c \
@ -7,7 +8,6 @@ SRC_SUPERVISOR = \
supervisor/shared/fatfs.c \
supervisor/shared/flash.c \
supervisor/shared/lock.c \
supervisor/shared/memory.c \
supervisor/shared/micropython.c \
supervisor/shared/port.c \
supervisor/shared/reload.c \
@ -21,6 +21,9 @@ SRC_SUPERVISOR = \
supervisor/shared/workflow.c \
supervisor/stub/misc.c \
# For tlsf
CFLAGS += -D_DEBUG=0
NO_USB ?= $(wildcard supervisor/usb.c)

View File

@ -31,8 +31,6 @@
#include <stddef.h>
#include <stdint.h>
#include "supervisor/memory.h"
// Ports must call this as frequently as they can in order to keep the USB
// connection alive and responsive. Normally this is called from background
// tasks after the USB IRQ handler is executed, but in specific circumstances
@ -67,12 +65,10 @@ typedef struct {
char product_name[128];
} usb_identification_t;
extern supervisor_allocation *usb_identification_allocation;
// Shared implementation.
bool usb_enabled(void);
void usb_add_interface_string(uint8_t interface_string_index, const char str[]);
void usb_build_descriptors(const usb_identification_t *identification);
bool usb_build_descriptors(const usb_identification_t *identification);
void usb_disconnect(void);
void usb_init(void);
void usb_set_defaults(void);