Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 89-apps-should-be-able-to-specify-if-they-want-wifi-to-be-disabled-when-entering-them
  • 9Rmain
  • allow-reloading-sunmenu
  • always-have-a-wifi-instance
  • anon/gpndemo
  • anon/update-sim
  • anon/webflasher
  • app_text_viewer
  • audio_input
  • audio_io
  • blm_dev_chan
  • ch3/bl00mbox_docs
  • ci-1690580595
  • dev_p4
  • dev_p4-iggy
  • dev_p4-iggy-rebased
  • dx/dldldld
  • dx/fb-save-restore
  • dx/hint-hint
  • dx/jacksense-headset-mic-only
  • events
  • fil3s-limit-filesize
  • fil3s-media
  • fpletz/flake
  • gr33nhouse-improvements
  • history-rewrite
  • icon-flower
  • iggy/stemming
  • iggy/stemming_merge
  • led_fix_fix
  • main
  • main+schneider
  • media_has_video_has_audio
  • micropython_api
  • mixer2
  • moon2_demo_temp
  • moon2_migrate_apps
  • more-accurate-battery
  • pippin/ctx_sprite_sheet_support
  • pippin/display-python-errors-on-display
  • pippin/make_empty_drawlists_skip_render_and_blit
  • pippin/more-accurate-battery
  • pippin/tcp_redirect_hack
  • pippin/tune_ctx_config_update_from_upstream
  • pippin/uhm_flash_access_bust
  • pressable_bugfix
  • py_only_update_fps_overlay_when_changing
  • q3k/doom-poc
  • q3k/render-to-texture
  • rahix/flow3rseeds
  • raw_captouch_new
  • raw_captouch_old
  • release/1.0.0
  • release/1.1.0
  • release/1.1.1
  • release/1.2.0
  • release/1.3.0
  • release/1.4.0
  • restore_blit
  • return_of_melodic_demo
  • rev4_micropython
  • schneider/application-remove-name
  • schneider/bhi581
  • schneider/factory_test
  • schneider/recovery
  • scope_hack
  • sdkconfig-spiram-tinyusb
  • sec/auto-nick
  • sec/blinky
  • sector_size_512
  • shoegaze-fps
  • smaller_gradient_lut
  • store_delta_ms_and_ins_as_class_members
  • task_cleanup
  • uctx-wip
  • w1f1-in-sim
  • widgets_draw
  • wifi-json-error-handling
  • wip-docs
  • wip-tinyusb
  • v1.0.0
  • v1.0.0+rc1
  • v1.0.0+rc2
  • v1.0.0+rc3
  • v1.0.0+rc4
  • v1.0.0+rc5
  • v1.0.0+rc6
  • v1.1.0
  • v1.1.0+rc1
  • v1.1.1
  • v1.2.0
  • v1.2.0+rc1
  • v1.3.0
  • v1.4.0
94 results

Target

Select target project
  • flow3r/flow3r-firmware
  • Vespasian/flow3r-firmware
  • alxndr42/flow3r-firmware
  • pl/flow3r-firmware
  • Kari/flow3r-firmware
  • raimue/flow3r-firmware
  • grandchild/flow3r-firmware
  • mu5tach3/flow3r-firmware
  • Nervengift/flow3r-firmware
  • arachnist/flow3r-firmware
  • TheNewCivilian/flow3r-firmware
  • alibi/flow3r-firmware
  • manuel_v/flow3r-firmware
  • xeniter/flow3r-firmware
  • maxbachmann/flow3r-firmware
  • yGifoom/flow3r-firmware
  • istobic/flow3r-firmware
  • EiNSTeiN_/flow3r-firmware
  • gnudalf/flow3r-firmware
  • 999eagle/flow3r-firmware
  • toerb/flow3r-firmware
  • pandark/flow3r-firmware
  • teal/flow3r-firmware
  • x42/flow3r-firmware
  • alufers/flow3r-firmware
  • dos/flow3r-firmware
  • yrlf/flow3r-firmware
  • LuKaRo/flow3r-firmware
  • ThomasElRubio/flow3r-firmware
  • ai/flow3r-firmware
  • T_X/flow3r-firmware
  • highTower/flow3r-firmware
  • beanieboi/flow3r-firmware
  • Woazboat/flow3r-firmware
  • gooniesbro/flow3r-firmware
  • marvino/flow3r-firmware
  • kressnerd/flow3r-firmware
  • quazgar/flow3r-firmware
  • aoid/flow3r-firmware
  • jkj/flow3r-firmware
  • naomi/flow3r-firmware
41 results
Select Git revision
  • 9Rmain
  • anon/gpndemo
  • anon/update-sim
  • anon/webflasher
  • audio_input
  • audio_io
  • bl00mbox
  • bl00mbox_old
  • captouch-threshold
  • ch3/bl00mbox_docs
  • ci-1690580595
  • compressor
  • dev_p4
  • dev_p4-iggy
  • dev_p4-iggy-rebased
  • dos
  • dos-main-patch-50543
  • events
  • fm_fix
  • fm_fix2
  • fpletz/flake
  • history-rewrite
  • icon-flower
  • iggy/stemming
  • iggy/stemming_merge
  • json-error
  • main
  • main+schneider
  • media-buf
  • micropython_api
  • moon2_applications
  • moon2_demo_temp
  • moon2_gay_drums
  • passthrough
  • phhw
  • pippin/display-python-errors-on-display
  • pippin/make_empty_drawlists_skip_render_and_blit
  • pippin/media_framework
  • pippin/uhm_flash_access_bust
  • pressable_bugfix
  • q3k/doom-poc
  • rahix/big-flow3r
  • rahix/flow3rseeds
  • raw_captouch_new
  • raw_captouch_old
  • release/1.0.0
  • release/1.1.0
  • release/1.1.1
  • rev4_micropython
  • schneider/application-remove-name
  • schneider/bhi581
  • schneider/factory_test
  • schneider/recovery
  • scope
  • scope_hack
  • sdkconfig-spiram-tinyusb
  • sec/auto-nick
  • sec/blinky
  • simtest
  • slewtest
  • t
  • test
  • test2
  • uctx-wip
  • view-think
  • vm-pending
  • vsync
  • wave
  • wip-docs
  • wip-tinyusb
  • v1.0.0
  • v1.0.0+rc1
  • v1.0.0+rc2
  • v1.0.0+rc3
  • v1.0.0+rc4
  • v1.0.0+rc5
  • v1.0.0+rc6
  • v1.1.0
  • v1.1.0+rc1
  • v1.1.1
  • v1.2.0
  • v1.2.0+rc1
  • v1.3.0
83 results
Show changes
Showing
with 362 additions and 61 deletions
......@@ -202,8 +202,12 @@ MP_CTX_COMMON_FUN_0(restore);
MP_CTX_COMMON_FUN_0(start_frame);
MP_CTX_COMMON_FUN_0(end_frame);
#define UCTX_COMPOSITING_GROUPS 0
#if UCTX_COMPOSITING_GROUPS
MP_CTX_COMMON_FUN_0(start_group);
MP_CTX_COMMON_FUN_0(end_group);
#endif
MP_CTX_COMMON_FUN_0(clip);
MP_CTX_COMMON_FUN_1F(rotate);
MP_CTX_COMMON_FUN_2F(scale);
......@@ -383,13 +387,6 @@ static mp_obj_t mp_ctx_texture(size_t n_args, const mp_obj_t *args) {
int width = mp_obj_get_int(args[3]);
int height = mp_obj_get_int(args[4]);
int stride = mp_obj_get_int(args[5]);
// static int eid_no = 0;
// char ieid[10]; // this is a workaround for the rasterizer
// not keeping the cache validity expected
// as we drive it
// it also means we cannot properly use
// eid based APIs
// sprintf (ieid, "%i", eid_no++);
ctx_define_texture(self->ctx, NULL, width, height, stride, format,
buffer_info.buf, NULL);
return args[0];
......@@ -404,16 +401,25 @@ static mp_obj_t mp_ctx_image(size_t n_args, const mp_obj_t *args) {
float y0 = 0.0;
float width = -1.0;
float height = -1.0;
float clip_x = 0.0;
float clip_y = 0.0;
float clip_width = 0.0;
float clip_height = 0.0;
if (n_args > 2) x0 = mp_obj_get_float(args[2]);
if (n_args > 3) y0 = mp_obj_get_float(args[3]);
if (n_args > 4) width = mp_obj_get_float(args[4]);
if (n_args > 5) height = mp_obj_get_float(args[5]);
ctx_draw_image(self->ctx, path, x0, y0, width, height);
if (n_args > 6) clip_x = mp_obj_get_float(args[6]);
if (n_args > 7) clip_y = mp_obj_get_float(args[7]);
if (n_args > 8) clip_width = mp_obj_get_float(args[8]);
if (n_args > 9) clip_height = mp_obj_get_float(args[9]);
ctx_draw_image_clipped(self->ctx, path, x0, y0, width, height, clip_x,
clip_y, clip_width, clip_height);
return args[0];
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_ctx_image_obj, 2, 6, mp_ctx_image);
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_ctx_image_obj, 2, 10, mp_ctx_image);
#if 0
static mp_obj_t mp_ctx_font(mp_obj_t self_in, mp_obj_t font_in)
......@@ -545,8 +551,8 @@ MP_DEFINE_CONST_FUN_OBJ_2(mp_ctx_tinyvg_draw_obj, mp_ctx_tinyvg_draw);
/* CTX API functions }}} */
static void mp_ctx_set_pixels(Ctx *ctx, void *user_data, int x_in, int y_in,
int width_in, int height_in, void *buf_in,
int buf_size) {
int width_in, int height_in, void *buf_in) {
int buf_size = width_in * height_in * 2; // XXX : not valid for non-16bpp!
mp_obj_t args[5] = { mp_obj_new_int(x_in), mp_obj_new_int(y_in),
mp_obj_new_int(width_in), mp_obj_new_int(height_in),
mp_obj_new_memoryview(BYTEARRAY_TYPECODE, buf_size,
......@@ -802,6 +808,9 @@ STATIC void mp_ctx_attr(mp_obj_t obj, qstr attr, mp_obj_t *dest) {
/* CTX class/type */
static const mp_rom_map_elem_t mp_ctx_locals_dict_table[] = {
MP_CTX_METHOD(gray),
MP_CTX_METHOD(rgb),
MP_CTX_METHOD(rgba),
MP_CTX_METHOD(line_to),
MP_CTX_METHOD(move_to),
MP_CTX_METHOD(curve_to),
......@@ -821,6 +830,8 @@ static const mp_rom_map_elem_t mp_ctx_locals_dict_table[] = {
MP_CTX_METHOD(fill),
MP_CTX_METHOD(stroke),
MP_CTX_METHOD(paint),
MP_CTX_METHOD(save),
MP_CTX_METHOD(restore),
MP_CTX_METHOD(clip),
MP_CTX_METHOD(text),
MP_CTX_METHOD(text_width),
......@@ -828,8 +839,13 @@ static const mp_rom_map_elem_t mp_ctx_locals_dict_table[] = {
MP_CTX_METHOD(scale),
MP_CTX_METHOD(translate),
MP_CTX_METHOD(apply_transform),
#if UCTX_COMPOSITING_GROUPS
MP_CTX_METHOD(start_group),
MP_CTX_METHOD(end_group),
#else
{ MP_ROM_QSTR(MP_QSTR_start_group), MP_ROM_PTR(&mp_ctx_save_obj) },
{ MP_ROM_QSTR(MP_QSTR_end_group), MP_ROM_PTR(&mp_ctx_restore_obj) },
#endif
MP_CTX_METHOD(preserve),
MP_CTX_METHOD(linear_gradient),
MP_CTX_METHOD(radial_gradient),
......@@ -837,15 +853,10 @@ static const mp_rom_map_elem_t mp_ctx_locals_dict_table[] = {
MP_CTX_METHOD(line_dash),
MP_CTX_METHOD(texture),
MP_CTX_METHOD(image),
MP_CTX_METHOD(save),
MP_CTX_METHOD(restore),
MP_CTX_METHOD(start_frame),
MP_CTX_METHOD(end_frame),
MP_CTX_METHOD(get_font_name),
MP_CTX_METHOD(gray),
MP_CTX_METHOD(rgb),
MP_CTX_METHOD(rgba),
#if CTX_PARSER
MP_CTX_METHOD(parse),
#endif
......
......@@ -55,8 +55,6 @@ Functions
buffers and other data. This data is useful to get a sense of how much memory
is available to ESP-IDF and the networking stack in particular. It may shed
some light on situations where ESP-IDF operations fail due to allocation failures.
The information returned is *not* useful to troubleshoot Python allocation failures,
use `micropython.mem_info()` instead.
The capabilities parameter corresponds to ESP-IDF's ``MALLOC_CAP_XXX`` values but the
two most useful ones are predefined as `esp32.HEAP_DATA` for data heap regions and
......@@ -72,6 +70,21 @@ Functions
[(240, 0, 0, 0), (7288, 0, 0, 0), (16648, 4, 4, 4), (79912, 35712, 35512, 35108),
(15072, 15036, 15036, 15036), (113840, 0, 0, 0)]
.. note:: Free IDF heap memory in the `esp32.HEAP_DATA` region is available
to be automatically added to the MicroPython heap to prevent a
MicroPython allocation from failing. However, the information returned
here is otherwise *not* useful to troubleshoot Python allocation
failures. :func:`micropython.mem_info()` and :func:`gc.mem_free()` should
be used instead:
The "max new split" value in :func:`micropython.mem_info()` output
corresponds to the largest free block of ESP-IDF heap that could be
automatically added on demand to the MicroPython heap.
The result of :func:`gc.mem_free()` is the total of the current "free"
and "max new split" values printed by :func:`micropython.mem_info()`.
Flash partitions
----------------
......
......@@ -24,7 +24,7 @@ Functions
.. function:: mem_alloc()
Return the number of bytes of heap RAM that are allocated.
Return the number of bytes of heap RAM that are allocated by Python code.
.. admonition:: Difference to CPython
:class: attention
......@@ -33,8 +33,8 @@ Functions
.. function:: mem_free()
Return the number of bytes of available heap RAM, or -1 if this amount
is not known.
Return the number of bytes of heap RAM that is available for Python
code to allocate, or -1 if this amount is not known.
.. admonition:: Difference to CPython
:class: attention
......
......@@ -25,6 +25,7 @@
*/
#include <stdio.h>
#include <string.h>
#include "py/objlist.h"
#include "py/objstringio.h"
......@@ -41,16 +42,33 @@ enum {
DUMP_MODE_TO_STREAM = 2,
};
STATIC mp_obj_t mod_ujson_dump_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, unsigned int mode) {
enum { ARG_separators };
enum { ARG_separators, ARG_indent};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_separators, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_indent, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - mode, pos_args + mode, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_print_ext_t print_ext;
mp_obj_t indent = args[ARG_indent].u_obj;
print_ext.indent = NULL;
print_ext.indent_depth = 0;
if (indent != mp_const_none) {
if (mp_obj_is_int(indent)) {
mp_int_t len_whitespace = mp_obj_get_int(indent);
len_whitespace = len_whitespace > 0 ? len_whitespace : 0;
char * whitespace = m_malloc(len_whitespace + 1);
memset(whitespace, ' ', len_whitespace);
whitespace[len_whitespace] = '\0';
print_ext.indent = whitespace;
} else if (mp_obj_is_str(indent)) {
print_ext.indent = mp_obj_str_get_str(indent);
}
}
if (args[ARG_separators].u_obj == mp_const_none) {
print_ext.item_separator = ", ";
......
......@@ -47,6 +47,8 @@
#include <unistd.h>
#include <dirent.h>
void get_statvfs(char* drive, uint32_t *out);
typedef struct _mp_obj_vfs_posix_t {
mp_obj_base_t base;
vstr_t root;
......@@ -325,6 +327,39 @@ STATIC mp_obj_t vfs_posix_stat(mp_obj_t self_in, mp_obj_t path_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_stat_obj, vfs_posix_stat);
STATIC mp_obj_t vfs_posix_statvfs(mp_obj_t self_in, mp_obj_t path_in) {
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
const char *path = vfs_posix_get_path_str(self, path_in);
char device_path[3];
if (strncmp(path, "/sd", 3) == 0) {
strcpy(device_path, "1:");
} else if (strncmp(path, "/flash", 6) == 0) {
strcpy(device_path, "0:");
} else {
mp_raise_OSError(2);
return mp_const_none;
}
uint32_t statvfs_array[10] = {0};
get_statvfs(device_path, statvfs_array);
// https://man7.org/linux/man-pages/man3/statvfs.3.htm
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
t->items[0] = MP_OBJ_NEW_SMALL_INT(0); // bsize
t->items[1] = MP_OBJ_NEW_SMALL_INT(statvfs_array[1]); // frsize
t->items[2] = MP_OBJ_NEW_SMALL_INT(statvfs_array[2]); // blocks
t->items[3] = MP_OBJ_NEW_SMALL_INT(statvfs_array[3]); // bfree
t->items[4] = MP_OBJ_NEW_SMALL_INT(statvfs_array[4]); // bavail
t->items[5] = MP_OBJ_NEW_SMALL_INT(0);
t->items[6] = MP_OBJ_NEW_SMALL_INT(0);
t->items[7] = MP_OBJ_NEW_SMALL_INT(0);
t->items[8] = MP_OBJ_NEW_SMALL_INT(0);
t->items[9] = MP_OBJ_NEW_SMALL_INT(255); // namemax
return MP_OBJ_FROM_PTR(t);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_statvfs_obj, vfs_posix_statvfs);
STATIC const mp_rom_map_elem_t vfs_posix_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_posix_mount_obj) },
{ MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&vfs_posix_umount_obj) },
......@@ -338,6 +373,7 @@ STATIC const mp_rom_map_elem_t vfs_posix_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&vfs_posix_rename_obj) },
{ MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&vfs_posix_rmdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&vfs_posix_stat_obj) },
{ MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&vfs_posix_statvfs_obj) },
};
STATIC MP_DEFINE_CONST_DICT(vfs_posix_locals_dict, vfs_posix_locals_dict_table);
......
......@@ -49,6 +49,15 @@ CONFIG_LWIP_PPP_CHAP_SUPPORT=y
# Use 4kiB output buffer instead of default 16kiB (because IDF heap is fragmented in 4.0)
CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y
# Allow mbedTLS to allocate from PSRAM or internal memory
#
# (The ESP-IDF default is internal-only, partly for physical security to prevent
# possible information leakage from unencrypted PSRAM contents on the original
# ESP32 - no PSRAM encryption on that chip. MicroPython doesn't support flash
# encryption and is already storing the Python heap in PSRAM so this isn't a
# significant factor in overall physical security.)
CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC=y
# ULP coprocessor support
CONFIG_ESP32_ULP_COPROC_ENABLED=y
CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=2040
......
......@@ -3,7 +3,11 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_CACHE_WORKAROUND=y
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
CONFIG_SPIRAM_USE_CAPS_ALLOC=y
CONFIG_SPIRAM_USE_MALLOC=y
# This is the threshold for preferring small allocations from internal memory
# first, before failing over to PSRAM.
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=8192
# SPIRAM increases the size of the firmware and overflows iram0_0_seg, due
# to PSRAM bug workarounds. Apply some options to reduce the firmware size.
......
......@@ -7,4 +7,8 @@ CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_BOOT_INIT=y
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
CONFIG_SPIRAM_USE_CAPS_ALLOC=y
CONFIG_SPIRAM_USE_MALLOC=y
# This is the threshold for preferring small allocations from internal memory
# first, before failing over to PSRAM.
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=8192
#include "esp_vfs.h"
#include "esp_vfs_fat.h"
void get_statvfs(char* drive, uint32_t *out) {
FATFS *fs;
uint32_t fre_clust;
if (f_getfree(drive, &fre_clust, &fs) != FR_OK) {
return;
}
uint32_t fre_sect = (fre_clust * fs->csize);
uint32_t tot_sect = (fs->n_fatent - 2) * fs->csize;
out[1] = fs->ssize; // frsize, mapped to sector size
out[2] = tot_sect; // blocks, mapped to total sectors
out[3] = fre_sect; // bfree, mapped to free sectors
out[4] = fre_sect; // bavail, mapped to free sectors
}
......@@ -39,30 +39,25 @@
#include "xtensa/hal.h"
static void gc_collect_inner(int level) {
// The level argument must be volatile to force the compiler to emit code that
// will call this function recursively, to nest the C stack.
static void gc_collect_inner(volatile unsigned int level) {
if (level < XCHAL_NUM_AREGS / 8) {
// Go deeper on the stack to spill more registers from the register window.
gc_collect_inner(level + 1);
if (level != 0) {
return;
}
}
if (level == XCHAL_NUM_AREGS / 8) {
// get the sp
} else {
// Deep enough so that all registers are on the C stack, now trace the stack.
volatile uint32_t sp = (uint32_t)esp_cpu_get_sp();
gc_collect_root((void **)sp, ((mp_uint_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t));
return;
}
// trace root pointers from any threads
#if MICROPY_PY_THREAD
mp_thread_gc_others();
#endif
}
void gc_collect(void) {
gc_collect_start();
gc_collect_inner(0);
#if MICROPY_PY_THREAD
mp_thread_gc_others();
#endif
gc_collect_end();
}
......@@ -80,3 +75,14 @@ void gc_collect(void) {
}
#endif
#if MICROPY_GC_SPLIT_HEAP_AUTO
// The largest new region that is available to become Python heap is the largest
// free block in the ESP-IDF system heap.
size_t gc_get_max_new_split(void) {
return heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
}
#endif
......@@ -63,7 +63,7 @@
#endif
// MicroPython runs as a task under FreeRTOS
#define MP_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 2)
#define MP_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1)
#define MP_TASK_STACK_SIZE (16 * 1024)
// Set the margin for detecting stack overflow, depending on the CPU architecture.
......@@ -73,6 +73,10 @@
#define MP_TASK_STACK_LIMIT_MARGIN (1024)
#endif
// Initial Python heap size. This starts small but adds new heap areas on
// demand due to settings MICROPY_GC_SPLIT_HEAP & MICROPY_GC_SPLIT_HEAP_AUTO
#define MP_TASK_HEAP_SIZE (64 * 1024)
int vprintf_null(const char *format, va_list ap) {
// do nothing: this is used as a log target during raw repl mode
return 0;
......@@ -109,19 +113,13 @@ void mp_task(void *pvParameter) {
ESP_LOGE("esp_init", "can't create event loop: 0x%x\n", err);
}
// Allocate the uPy heap using malloc and get the largest available region,
// limiting to 1/2 total available memory to leave memory for the OS.
// When SPIRAM is enabled, this will allocate from SPIRAM.
uint32_t caps = MALLOC_CAP_8BIT;
size_t heap_total = heap_caps_get_total_size(caps);
size_t mp_task_heap_size = MIN(heap_caps_get_largest_free_block(caps), heap_total / 2);
void *mp_task_heap = heap_caps_malloc(mp_task_heap_size, caps);
void *mp_task_heap = MP_PLAT_ALLOC_HEAP(MP_TASK_HEAP_SIZE);
soft_reset:
// initialise the stack pointer for the main thread
mp_stack_set_top((void *)sp);
mp_stack_set_limit(MP_TASK_STACK_SIZE - MP_TASK_STACK_LIMIT_MARGIN);
gc_init(mp_task_heap, mp_task_heap + mp_task_heap_size);
gc_init(mp_task_heap, mp_task_heap + MP_TASK_HEAP_SIZE);
mp_init();
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash_slash_sys));
readline_init0();
......@@ -134,8 +132,6 @@ soft_reset:
machine_i2s_init0();
#endif
st3m_mode_set(st3m_mode_kind_app, NULL);
// run boot-up scripts
pyexec_frozen_module("_boot.py");
pyexec_file_if_exists("boot.py");
......@@ -158,6 +154,7 @@ soft_reset:
}
esp_log_set_vprintf(vprintf_log);
} else {
st3m_mode_set(st3m_mode_kind_repl, NULL);
if ((ret = pyexec_friendly_repl() != 0)) {
_diskmode_maybe(pyexec_system_exit);
break;
......
......@@ -68,6 +68,9 @@
#define MICROPY_PY_THREAD_GIL (1)
#define MICROPY_PY_THREAD_GIL_VM_DIVISOR (32)
#define MICROPY_GC_SPLIT_HEAP (1)
#define MICROPY_GC_SPLIT_HEAP_AUTO (1)
// extended modules
#ifndef MICROPY_PY_BLUETOOTH
#define MICROPY_PY_BLUETOOTH (1)
......
......@@ -86,7 +86,6 @@ static bool stdin_chr_buf = false;
static char stdin_chr_val = 0;
uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
printf("mp_hal_stdio_poll\n");
uintptr_t ret = 0;
if ((poll_flags & MP_STREAM_POLL_RD)) {
if (!stdin_chr_buf) {
......
......@@ -79,7 +79,7 @@
#define ATB_3_IS_FREE(a) (((a) & ATB_MASK_3) == 0)
#if MICROPY_GC_SPLIT_HEAP
#define NEXT_AREA(area) (area->next)
#define NEXT_AREA(area) ((area)->next)
#else
#define NEXT_AREA(area) (NULL)
#endif
......@@ -129,7 +129,13 @@ STATIC void gc_setup_area(mp_state_mem_area_t *area, void *start, void *end) {
// => T = A * (1 + BLOCKS_PER_ATB / BLOCKS_PER_FTB + BLOCKS_PER_ATB * BYTES_PER_BLOCK)
size_t total_byte_len = (byte *)end - (byte *)start;
#if MICROPY_ENABLE_FINALISER
area->gc_alloc_table_byte_len = (total_byte_len - ALLOC_TABLE_GAP_BYTE) * MP_BITS_PER_BYTE / (MP_BITS_PER_BYTE + MP_BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + MP_BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK);
area->gc_alloc_table_byte_len = (total_byte_len - ALLOC_TABLE_GAP_BYTE)
* MP_BITS_PER_BYTE
/ (
MP_BITS_PER_BYTE
+ MP_BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB
+ MP_BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK
);
#else
area->gc_alloc_table_byte_len = (total_byte_len - ALLOC_TABLE_GAP_BYTE) / (1 + MP_BITS_PER_BYTE / 2 * BYTES_PER_BLOCK);
#endif
......@@ -158,17 +164,26 @@ STATIC void gc_setup_area(mp_state_mem_area_t *area, void *start, void *end) {
#endif
area->gc_last_free_atb_index = 0;
area->gc_last_used_block = 0;
#if MICROPY_GC_SPLIT_HEAP
area->next = NULL;
#endif
DEBUG_printf("GC layout:\n");
DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(area).gc_alloc_table_start, MP_STATE_MEM(area).gc_alloc_table_byte_len, MP_STATE_MEM(area).gc_alloc_table_byte_len * BLOCKS_PER_ATB);
DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, "
UINT_FMT " blocks\n",
area->gc_alloc_table_start, area->gc_alloc_table_byte_len,
area->gc_alloc_table_byte_len * BLOCKS_PER_ATB);
#if MICROPY_ENABLE_FINALISER
DEBUG_printf(" finaliser table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(area).gc_finaliser_table_start, gc_finaliser_table_byte_len, gc_finaliser_table_byte_len * BLOCKS_PER_FTB);
DEBUG_printf(" finaliser table at %p, length " UINT_FMT " bytes, "
UINT_FMT " blocks\n", area->gc_finaliser_table_start,
gc_finaliser_table_byte_len,
gc_finaliser_table_byte_len * BLOCKS_PER_FTB);
#endif
DEBUG_printf(" pool at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(area).gc_pool_start, gc_pool_block_len * BYTES_PER_BLOCK, gc_pool_block_len);
DEBUG_printf(" pool at %p, length " UINT_FMT " bytes, "
UINT_FMT " blocks\n", area->gc_pool_start,
gc_pool_block_len * BYTES_PER_BLOCK, gc_pool_block_len);
}
void gc_init(void *start, void *end) {
......@@ -221,6 +236,83 @@ void gc_add(void *start, void *end) {
// Add this area to the linked list
prev_area->next = area;
}
#if MICROPY_GC_SPLIT_HEAP_AUTO
// Try to automatically add a heap area large enough to fulfill 'failed_alloc'.
STATIC bool gc_try_add_heap(size_t failed_alloc) {
// 'needed' is the size of a heap large enough to hold failed_alloc, with
// the additional metadata overheads as calculated in gc_setup_area().
//
// Rather than reproduce all of that logic here, we approximate that adding
// (13/512) is enough overhead for sufficiently large heap areas (the
// overhead converges to 3/128, but there's some fixed overhead and some
// rounding up of partial block sizes).
size_t needed = failed_alloc + MAX(2048, failed_alloc * 13 / 512);
size_t avail = gc_get_max_new_split();
DEBUG_printf("gc_try_add_heap failed_alloc " UINT_FMT ", "
"needed " UINT_FMT ", avail " UINT_FMT " bytes \n",
failed_alloc,
needed,
avail);
if (avail < needed) {
// Can't fit this allocation, or system heap has nearly run out anyway
return false;
}
// Deciding how much to grow the total heap by each time is tricky:
//
// - Grow by too small amounts, leads to heap fragmentation issues.
//
// - Grow by too large amounts, may lead to system heap running out of
// space.
//
// Currently, this implementation is:
//
// - At minimum, aim to double the total heap size each time we add a new
// heap. i.e. without any large single allocations, total size will be
// 64KB -> 128KB -> 256KB -> 512KB -> 1MB, etc
//
// - If the failed allocation is too large to fit in that size, the new
// heap is made exactly large enough for that allocation. Future growth
// will double the total heap size again.
//
// - If the new heap won't fit in the available free space, add the largest
// new heap that will fit (this may lead to failed system heap allocations
// elsewhere, but some allocation will likely fail in this circumstance!)
size_t total_heap = 0;
for (mp_state_mem_area_t *area = &MP_STATE_MEM(area);
area != NULL;
area = NEXT_AREA(area)) {
total_heap += area->gc_pool_end - area->gc_alloc_table_start;
total_heap += ALLOC_TABLE_GAP_BYTE + sizeof(mp_state_mem_area_t);
}
DEBUG_printf("total_heap " UINT_FMT " bytes\n", total_heap);
size_t to_alloc = MIN(avail, MAX(total_heap, needed));
mp_state_mem_area_t *new_heap = MP_PLAT_ALLOC_HEAP(to_alloc);
DEBUG_printf("MP_PLAT_ALLOC_HEAP " UINT_FMT " = %p\n",
to_alloc, new_heap);
if (new_heap == NULL) {
// This should only fail:
// - In a threaded environment if another thread has
// allocated while this function ran.
// - If there is a bug in gc_get_max_new_split().
return false;
}
gc_add(new_heap, (void *)new_heap + to_alloc);
return true;
}
#endif
#endif
void gc_lock(void) {
......@@ -379,8 +471,18 @@ STATIC void gc_sweep(void) {
#endif
// free unmarked heads and their tails
int free_tail = 0;
#if MICROPY_GC_SPLIT_HEAP_AUTO
mp_state_mem_area_t *prev_area = NULL;
#endif
for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) {
for (size_t block = 0; block < area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) {
size_t end_block = area->gc_alloc_table_byte_len * BLOCKS_PER_ATB;
if (area->gc_last_used_block < end_block) {
end_block = area->gc_last_used_block + 1;
}
size_t last_used_block = 0;
for (size_t block = 0; block < end_block; block++) {
MICROPY_GC_HOOK_LOOP
switch (ATB_GET_KIND(area, block)) {
case AT_HEAD:
......@@ -420,15 +522,31 @@ STATIC void gc_sweep(void) {
#if CLEAR_ON_SWEEP
memset((void *)PTR_FROM_BLOCK(area, block), 0, BYTES_PER_BLOCK);
#endif
} else {
last_used_block = block;
}
break;
case AT_MARK:
ATB_MARK_TO_HEAD(area, block);
free_tail = 0;
last_used_block = block;
break;
}
}
area->gc_last_used_block = last_used_block;
#if MICROPY_GC_SPLIT_HEAP_AUTO
// Free any empty area, aside from the first one
if (last_used_block == 0 && prev_area != NULL) {
DEBUG_printf("gc_sweep free empty area %p\n", area);
NEXT_AREA(prev_area) = NEXT_AREA(area);
MP_PLAT_FREE_HEAP(area);
area = prev_area;
}
prev_area = area;
#endif
}
}
......@@ -584,6 +702,11 @@ void gc_info(gc_info_t *info) {
info->used *= BYTES_PER_BLOCK;
info->free *= BYTES_PER_BLOCK;
#if MICROPY_GC_SPLIT_HEAP_AUTO
info->max_new_split = gc_get_max_new_split();
#endif
GC_EXIT();
}
......@@ -610,6 +733,9 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) {
size_t start_block;
size_t n_free;
int collected = !MP_STATE_MEM(gc_auto_collect_enabled);
#if MICROPY_GC_SPLIT_HEAP_AUTO
bool added = false;
#endif
#if MICROPY_GC_ALLOC_THRESHOLD
if (!collected && MP_STATE_MEM(gc_alloc_amount) >= MP_STATE_MEM(gc_alloc_threshold)) {
......@@ -654,6 +780,12 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) {
GC_EXIT();
// nothing found!
if (collected) {
#if MICROPY_GC_SPLIT_HEAP_AUTO
if (!added && gc_try_add_heap(n_bytes)) {
added = true;
continue;
}
#endif
return NULL;
}
DEBUG_printf("gc_alloc(" UINT_FMT "): no free mem, triggering GC\n", n_bytes);
......@@ -680,6 +812,8 @@ found:
area->gc_last_free_atb_index = (i + 1) / BLOCKS_PER_ATB;
}
area->gc_last_used_block = MAX(area->gc_last_used_block, end_block);
// mark first block as used head
ATB_FREE_TO_HEAD(area, start_block);
......@@ -969,11 +1103,14 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
// check if we can expand in place
if (new_blocks <= n_blocks + n_free) {
// mark few more blocks as used tail
for (size_t bl = block + n_blocks; bl < block + new_blocks; bl++) {
size_t end_block = block + new_blocks;
for (size_t bl = block + n_blocks; bl < end_block; bl++) {
assert(ATB_GET_KIND(area, bl) == AT_FREE);
ATB_FREE_TO_TAIL(area, bl);
}
area->gc_last_used_block = MAX(area->gc_last_used_block, end_block);
GC_EXIT();
#if MICROPY_GC_CONSERVATIVE_CLEAR
......@@ -1022,9 +1159,12 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
void gc_dump_info(const mp_print_t *print) {
gc_info_t info;
gc_info(&info);
mp_printf(print, "GC: total: %u, used: %u, free: %u\n",
mp_printf(print, "GC: total: %u, used: %u, free: %u",
(uint)info.total, (uint)info.used, (uint)info.free);
mp_printf(print, " No. of 1-blocks: %u, 2-blocks: %u, max blk sz: %u, max free sz: %u\n",
#if MICROPY_GC_SPLIT_HEAP_AUTO
mp_printf(print, ", max new split: %u", (uint)info.max_new_split);
#endif
mp_printf(print, "\n No. of 1-blocks: %u, 2-blocks: %u, max blk sz: %u, max free sz: %u\n",
(uint)info.num_1block, (uint)info.num_2block, (uint)info.max_block, (uint)info.max_free);
}
......
......@@ -35,7 +35,13 @@ void gc_init(void *start, void *end);
#if MICROPY_GC_SPLIT_HEAP
// Used to add additional memory areas to the heap.
void gc_add(void *start, void *end);
#endif
#if MICROPY_GC_SPLIT_HEAP_AUTO
// Port must implement this function to return the maximum available block of
// RAM to allocate a new heap area into using MP_PLAT_ALLOC_HEAP.
size_t gc_get_max_new_split(void);
#endif // MICROPY_GC_SPLIT_HEAP_AUTO
#endif // MICROPY_GC_SPLIT_HEAP
// These lock/unlock functions can be nested.
// They can be used to prevent the GC from allocating/freeing.
......@@ -69,6 +75,9 @@ typedef struct _gc_info_t {
size_t num_1block;
size_t num_2block;
size_t max_block;
#if MICROPY_GC_SPLIT_HEAP_AUTO
size_t max_new_split;
#endif
} gc_info_t;
void gc_info(gc_info_t *info);
......
......@@ -64,7 +64,12 @@ MP_DEFINE_CONST_FUN_OBJ_0(gc_isenabled_obj, gc_isenabled);
STATIC mp_obj_t gc_mem_free(void) {
gc_info_t info;
gc_info(&info);
#if MICROPY_GC_SPLIT_HEAP_AUTO
// Include max_new_split value here as a more useful heuristic
return MP_OBJ_NEW_SMALL_INT(info.free + info.max_new_split);
#else
return MP_OBJ_NEW_SMALL_INT(info.free);
#endif
}
MP_DEFINE_CONST_FUN_OBJ_0(gc_mem_free_obj, gc_mem_free);
......
......@@ -616,6 +616,11 @@
#define MICROPY_GC_SPLIT_HEAP (0)
#endif
// Whether regions should be added/removed from the split heap as needed.
#ifndef MICROPY_GC_SPLIT_HEAP_AUTO
#define MICROPY_GC_SPLIT_HEAP_AUTO (0)
#endif
// Hook to run code during time consuming garbage collector operations
#ifndef MICROPY_GC_HOOK_LOOP
#define MICROPY_GC_HOOK_LOOP
......@@ -1847,6 +1852,16 @@ typedef double mp_float_t;
#define MP_PLAT_FREE_EXEC(ptr, size) m_del(byte, ptr, size)
#endif
// Allocating new heap area at runtime requires port to be able to allocate from system heap
#if MICROPY_GC_SPLIT_HEAP_AUTO
#ifndef MP_PLAT_ALLOC_HEAP
#define MP_PLAT_ALLOC_HEAP(size) malloc(size)
#endif
#ifndef MP_PLAT_FREE_HEAP
#define MP_PLAT_FREE_HEAP(ptr) free(ptr)
#endif
#endif
// This macro is used to do all output (except when MICROPY_PY_IO is defined)
#ifndef MP_PLAT_PRINT_STRN
#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len)
......
......@@ -50,6 +50,17 @@ STATIC void plat_print_strn(void *env, const char *str, size_t len) {
const mp_print_t mp_plat_print = {NULL, plat_print_strn};
int mp_print_indent(const mp_print_t * print, const char * indent_str){
if(!indent_str) return 0;
int len = 0;
len += mp_print_str(print, "\n");
size_t num_indents = MP_PRINT_GET_EXT(print)->indent_depth;
for(size_t i = 0; i < num_indents; i++){
len += mp_print_str(print, indent_str);
}
return len;
}
int mp_print_str(const mp_print_t *print, const char *str) {
size_t len = strlen(str);
if (len) {
......
......@@ -56,6 +56,8 @@ typedef struct _mp_print_ext_t {
mp_print_t base;
const char *item_separator;
const char *key_separator;
const char *indent;
size_t indent_depth;
} mp_print_ext_t;
#define MP_PRINT_GET_EXT(print) ((mp_print_ext_t *)print)
......@@ -68,6 +70,7 @@ extern const mp_print_t mp_plat_print;
extern const mp_print_t mp_sys_stdout_print;
#endif
int mp_print_indent(const mp_print_t * print, const char * indent_str);
int mp_print_str(const mp_print_t *print, const char *str);
int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width);
#if MICROPY_PY_BUILTINS_FLOAT
......