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

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
Show changes
Showing
with 474 additions and 71 deletions
......@@ -8,6 +8,8 @@
#include "py/obj.h"
#include "py/runtime.h"
#include "st3m_console.h"
#include "st3m_fs_sd.h"
#include "st3m_io.h"
#include "st3m_usb.h"
#include "st3m_version.h"
......@@ -29,6 +31,8 @@
/// - core_affinity: bitmask of where this task is allowed to be scheduled. Bit
/// 0 is core 0, bit 1 is core 1. The value 0b11 (3) means the
/// task is allowed to run on any core.
/// - percent: cpu load in percent since last update
typedef struct _task_obj_t {
mp_obj_base_t base;
......@@ -36,6 +40,8 @@ typedef struct _task_obj_t {
uint32_t number;
uint16_t stack_left;
uint32_t run_time;
uint32_t cpu_percent;
uint32_t current_priority;
eTaskState state;
uint32_t core_affinity;
} task_obj_t;
......@@ -72,8 +78,10 @@ STATIC void task_print(const mp_print_t *print, mp_obj_t self_in,
mp_print_str(print, "???");
break;
}
mp_printf(print, ",number=%d,stack_left=%d,run_time=%d,core_affinity=%d)",
self->number, self->stack_left, self->run_time,
mp_printf(print,
",number=%d,stack_left=%d,run_time=%d,,cpu_percent=%d,core_"
"affinity=%d)",
self->number, self->stack_left, self->run_time, self->cpu_percent,
self->core_affinity);
}
......@@ -98,6 +106,12 @@ STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
case MP_QSTR_run_time:
dest[0] = mp_obj_new_int_from_uint(self->run_time);
break;
case MP_QSTR_cpu_percent:
dest[0] = mp_obj_new_int_from_uint(self->cpu_percent);
break;
case MP_QSTR_current_priority:
dest[0] = mp_obj_new_int_from_uint(self->current_priority);
break;
case MP_QSTR_core_affinity:
dest[0] = mp_obj_new_int_from_uint(self->core_affinity);
break;
......@@ -152,6 +166,8 @@ MP_DEFINE_CONST_OBJ_TYPE(scheduler_snapshot_type, MP_QSTR_scheduler_snapshot,
attr, scheduler_snapshot_attr);
STATIC mp_obj_t mp_scheduler_snapshot(void) {
static uint32_t run_time_prev[50];
static uint32_t total_runtime_prev;
mp_obj_t tasks_out = mp_obj_new_list(0, NULL);
UBaseType_t ntasks = uxTaskGetNumberOfTasks();
......@@ -163,6 +179,13 @@ STATIC mp_obj_t mp_scheduler_snapshot(void) {
uint32_t total_runtime;
UBaseType_t ntasks_returned =
uxTaskGetSystemState(tasks, ntasks, &total_runtime);
scheduler_snapshot_obj_t *snap = m_new_obj(scheduler_snapshot_obj_t);
snap->base.type = &scheduler_snapshot_type;
snap->total_runtime = total_runtime;
uint32_t total_runtime_delta_percent =
(snap->total_runtime - total_runtime_prev) / 100;
for (UBaseType_t i = 0; i < ntasks_returned; i++) {
task_obj_t *task = m_new_obj(task_obj_t);
task->base.type = &task_type;
......@@ -170,6 +193,15 @@ STATIC mp_obj_t mp_scheduler_snapshot(void) {
task->number = tasks[i].xTaskNumber;
task->stack_left = tasks[i].usStackHighWaterMark;
task->run_time = tasks[i].ulRunTimeCounter;
task->current_priority = tasks[i].uxCurrentPriority;
if (total_runtime_delta_percent && (task->number < 50)) {
task->cpu_percent = (task->run_time - run_time_prev[task->number]) /
total_runtime_delta_percent;
run_time_prev[task->number] = task->run_time;
} else {
task->cpu_percent = 300; // indicates smth's wrong
}
task->state = tasks[i].eCurrentState;
task->core_affinity = 0b11;
switch (tasks[i].xCoreID) {
......@@ -182,11 +214,8 @@ STATIC mp_obj_t mp_scheduler_snapshot(void) {
}
mp_obj_list_append(tasks_out, MP_OBJ_FROM_PTR(task));
}
scheduler_snapshot_obj_t *snap = m_new_obj(scheduler_snapshot_obj_t);
snap->base.type = &scheduler_snapshot_type;
total_runtime_prev = snap->total_runtime;
snap->tasks = tasks_out;
snap->total_runtime = total_runtime;
return snap;
}
......@@ -372,6 +401,33 @@ STATIC mp_obj_t mp_i2c_scan(void) {
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_i2c_scan_obj, mp_i2c_scan);
STATIC mp_obj_t mp_battery_charging(void) {
bool res = st3m_io_charger_state_get();
return mp_obj_new_bool(!res);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_battery_charging_obj, mp_battery_charging);
STATIC mp_obj_t mp_sd_oem(void) {
int32_t res = st3m_fs_sd_oem_id();
if (res == -1) {
return mp_const_none;
}
return mp_obj_new_int(res);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_sd_oem_obj, mp_sd_oem);
STATIC mp_obj_t mp_sd_mfg(void) {
int32_t res = st3m_fs_sd_mfg_id();
if (res == -1) {
return mp_const_none;
}
return mp_obj_new_int(res);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_sd_mfg_obj, mp_sd_mfg);
STATIC const mp_rom_map_elem_t globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_scheduler_snapshot),
MP_ROM_PTR(&mp_scheduler_snapshot_obj) },
......@@ -385,6 +441,10 @@ STATIC const mp_rom_map_elem_t globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_usb_console_active),
MP_ROM_PTR(&mp_usb_console_active_obj) },
{ MP_ROM_QSTR(MP_QSTR_i2c_scan), MP_ROM_PTR(&mp_i2c_scan_obj) },
{ MP_ROM_QSTR(MP_QSTR_battery_charging),
MP_ROM_PTR(&mp_battery_charging_obj) },
{ MP_ROM_QSTR(MP_QSTR_sd_oem), MP_ROM_PTR(&mp_sd_oem_obj) },
{ MP_ROM_QSTR(MP_QSTR_sd_mfg), MP_ROM_PTR(&mp_sd_mfg_obj) },
{ MP_ROM_QSTR(MP_QSTR_RUNNING), MP_ROM_INT(eRunning) },
{ MP_ROM_QSTR(MP_QSTR_READY), MP_ROM_INT(eReady) },
......
// probably doesn't need all of these idk
#include <stdio.h>
#include <string.h>
#include "extmod/virtpin.h"
#include "machine_rtc.h"
#include "modmachine.h"
#include "mphalport.h"
#include "py/builtin.h"
#include "py/mphal.h"
#include "py/runtime.h"
#include "flow3r_bsp.h"
#include "st3m_mode.h"
STATIC mp_obj_t mp_mode_set(mp_obj_t kind) {
// this does not support message at the time
st3m_mode_set(mp_obj_get_int(kind), NULL);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_mode_set_obj, mp_mode_set);
STATIC const mp_rom_map_elem_t mp_module_sys_mode_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys_mode) },
{ MP_ROM_QSTR(MP_QSTR_mode_set), MP_ROM_PTR(&mp_mode_set_obj) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_sys_mode_globals,
mp_module_sys_mode_globals_table);
const mp_obj_module_t mp_module_sys_mode = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&mp_module_sys_mode_globals,
};
MP_REGISTER_MODULE(MP_QSTR_sys_mode, mp_module_sys_mode);
// probably doesn't need all of these idk
#include <stdio.h>
#include <string.h>
#include "extmod/virtpin.h"
#include "machine_rtc.h"
#include "modmachine.h"
#include "mphalport.h"
#include "py/builtin.h"
#include "py/mphal.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "flow3r_bsp.h"
#include "st3m_scope.h"
STATIC mp_obj_t mp_get_buffer_x(void) {
int16_t *buf;
size_t size = st3m_scope_get_buffer_x(&buf);
if (size) {
return mp_obj_new_memoryview('h', size, buf);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_get_buffer_x_obj, mp_get_buffer_x);
STATIC const mp_rom_map_elem_t mp_module_sys_scope_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys_scope) },
{ MP_ROM_QSTR(MP_QSTR_get_buffer_x), MP_ROM_PTR(&mp_get_buffer_x_obj) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_sys_scope_globals,
mp_module_sys_scope_globals_table);
const mp_obj_module_t mp_module_sys_scope = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&mp_module_sys_scope_globals,
};
MP_REGISTER_MODULE(MP_QSTR_sys_scope, mp_module_sys_scope);
......@@ -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)
......@@ -430,8 +436,10 @@ static mp_obj_t mp_ctx_get_font_name(mp_obj_t self_in, mp_obj_t no_in) {
mp_ctx_obj_t *self = MP_OBJ_TO_PTR(self_in);
int no = mp_obj_get_int(no_in);
const char *name = ctx_get_font_name(self->ctx, no);
if (name) return mp_obj_new_str(name, strlen(name));
return mp_const_none;
if (name)
return mp_obj_new_str(name, strlen(name));
else
mp_raise_ValueError("font with given index does not exist");
}
MP_DEFINE_CONST_FUN_OBJ_2(mp_ctx_get_font_name_obj, mp_ctx_get_font_name);
......@@ -543,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,
......@@ -800,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),
......@@ -819,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),
......@@ -826,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),
......@@ -835,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
......
......@@ -75,7 +75,7 @@ STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg);
#if MICROPY_SSL_MBEDTLS
#if MBEDTLS_VERSION_NUMBER < 0x02070000
#if MBEDTLS_VERSION_NUMBER < 0x02070000 || MBEDTLS_VERSION_NUMBER >= 0x03000000
#define mbedtls_sha256_starts_ret mbedtls_sha256_starts
#define mbedtls_sha256_update_ret mbedtls_sha256_update
#define mbedtls_sha256_finish_ret mbedtls_sha256_finish
......@@ -203,7 +203,7 @@ STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) {
#if MICROPY_SSL_MBEDTLS
#if MBEDTLS_VERSION_NUMBER < 0x02070000
#if MBEDTLS_VERSION_NUMBER < 0x02070000 || MBEDTLS_VERSION_NUMBER >= 0x03000000
#define mbedtls_sha1_starts_ret mbedtls_sha1_starts
#define mbedtls_sha1_update_ret mbedtls_sha1_update
#define mbedtls_sha1_finish_ret mbedtls_sha1_finish
......
......@@ -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 = ", ";
......
......@@ -225,7 +225,11 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
size_t key_len;
const byte *key = (const byte *)mp_obj_str_get_data(args->key.u_obj, &key_len);
// len should include terminating null
#if MBEDTLS_VERSION_NUMBER >= 0x03000000
ret = mbedtls_pk_parse_key(&o->pkey, key, key_len + 1, NULL, 0, mbedtls_ctr_drbg_random, &o->ctr_drbg);
#else
ret = mbedtls_pk_parse_key(&o->pkey, key, key_len + 1, NULL, 0);
#endif
if (ret != 0) {
ret = MBEDTLS_ERR_PK_BAD_INPUT_DATA; // use general error for all key errors
goto cleanup;
......
......@@ -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;
......
......@@ -56,6 +56,7 @@
#define MICROPY_USE_INTERNAL_PRINTF (0) // ESP32 SDK requires its own printf
#define MICROPY_SCHEDULER_DEPTH (8)
#define MICROPY_VFS (1)
#define MICROPY_READLINE_HISTORY_SIZE (32)
// control over Python builtins
#define MICROPY_PY_STR_BYTES_CMP_WARN (1)
......@@ -67,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)
......@@ -130,7 +134,7 @@
#define MICROPY_HW_SOFTSPI_MIN_DELAY (0)
#define MICROPY_HW_SOFTSPI_MAX_BAUDRATE (ets_get_cpu_frequency() * 1000000 / 200) // roughly
#define MICROPY_PY_USSL (1)
#define MICROPY_SSL_MBEDTLS (0)
#define MICROPY_SSL_MBEDTLS (1)
#define MICROPY_PY_USSL_FINALISER (1)
#define MICROPY_PY_UWEBSOCKET (1)
#define MICROPY_PY_WEBREPL (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);
}
......