Merge pull request #8553 from tannewt/switch_to_split_heap

Switch all ports to auto-growing split heap
This commit is contained in:
Dan Halbert 2023-11-08 12:48:12 -05:00 committed by GitHub
commit 01be5f402e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
91 changed files with 661 additions and 1438 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);
if (heap) {
heap_size = try_alloc;
} else {
ESP_LOGE(TAG, "CONFIG_SPIRAM_USE_CAPS_ALLOC but no spiram heap available");
}
}
#endif
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 heap_caps_malloc(size, caps);
}
return sm;
void port_free(void *ptr) {
heap_caps_free(ptr);
}
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

66
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,17 @@ bool gc_is_locked(void) {
return MP_STATE_THREAD(gc_lock_depth) != 0;
}
// CIRCUITPY-CHANGE
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 +391,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 +850,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 +980,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 +1262,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 +1405,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();
}

21
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,11 @@ 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);
// CIRCUITPY-CHANGE
// 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 (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);
if (next_code_configuration != NULL) {
port_free(next_code_configuration);
next_code_configuration = NULL;
}
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;
if (options != 0 || len != 0) {
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_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);
}
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

@ -3,7 +3,7 @@
*
* 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 +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.
*/
#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();
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);