From 7e8705fe2bdb083535e85c206b9fed9d2754e665 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 10 Aug 2025 08:42:11 +1000 Subject: [PATCH 01/60] all: Bump version to 1.27.0-preview. Signed-off-by: Damien George --- py/mpconfig.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 8775b74bb61bc..dd41ff6a70867 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -30,9 +30,9 @@ // as well as a fallback to generate MICROPY_GIT_TAG if the git repo or tags // are unavailable. #define MICROPY_VERSION_MAJOR 1 -#define MICROPY_VERSION_MINOR 26 +#define MICROPY_VERSION_MINOR 27 #define MICROPY_VERSION_MICRO 0 -#define MICROPY_VERSION_PRERELEASE 0 +#define MICROPY_VERSION_PRERELEASE 1 // Combined version as a 32-bit number for convenience to allow version // comparison. Doesn't include prerelease state. From c35427cbdbe105c8a3be099f8fd2946fa96c4a22 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 26 Jul 2025 17:31:24 +1000 Subject: [PATCH 02/60] tests/run-tests.py: Move tests to skip with native emitter to a list. This makes `run-tests.py` a little more organised, by putting all the tests-to-skip-when-using-the-native-emitter in a dedicated list. This should make it easier to maintain the list, and understand why a test is there. Signed-off-by: Damien George --- tests/run-tests.py | 85 ++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index 48dfc2f26e070..cb3a2c3cd1630 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -102,6 +102,48 @@ def open(self, path, mode): # Platforms associated with the unix port, values of `sys.platform`. PC_PLATFORMS = ("darwin", "linux", "win32") +# Tests to skip for specific emitters. +emitter_tests_to_skip = { + # Some tests are known to fail with native emitter. + # Remove them from the below when they work. + "native": ( + # These require raise_varargs. + "basics/gen_yield_from_close.py", + "basics/try_finally_return2.py", + "basics/try_reraise.py", + "basics/try_reraise2.py", + "misc/features.py", + # These require checking for unbound local. + "basics/annotate_var.py", + "basics/del_deref.py", + "basics/del_local.py", + "basics/scope_implicit.py", + "basics/unboundlocal.py", + # These require "raise from". + "basics/exception_chain.py", + # These require proper traceback info. + "basics/sys_tracebacklimit.py", + "misc/print_exception.py", + "micropython/emg_exc.py", + "micropython/heapalloc_traceback.py", + "micropython/opt_level_lineno.py", + # These require stack-allocated slice optimisation. + "micropython/heapalloc_slice.py", + # These require running the scheduler. + "micropython/schedule.py", + "extmod/asyncio_event_queue.py", + "extmod/asyncio_iterator_event.py", + # These require sys.exc_info(). + "misc/sys_exc_info.py", + # These require sys.settrace(). + "misc/sys_settrace_features.py", + "misc/sys_settrace_generator.py", + "misc/sys_settrace_loop.py", + # These are bytecode-specific tests. + "stress/bytecode_limit.py", + ), +} + # Tests to skip on specific targets. # These are tests that are difficult to detect that they should not be run on the given target. platform_tests_to_skip = { @@ -824,6 +866,9 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("basics/exception_chain.py") # warning is not printed skip_tests.add("micropython/meminfo.py") # output is very different to PC output + # Skip emitter-specific tests. + skip_tests.update(emitter_tests_to_skip.get(args.emit, ())) + # Skip platform-specific tests. skip_tests.update(platform_tests_to_skip.get(args.platform, ())) @@ -837,46 +882,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # Works but CPython uses '\' path separator skip_tests.add("import/import_file.py") - # Some tests are known to fail with native emitter - # Remove them from the below when they work - if args.emit == "native": - skip_tests.add("basics/gen_yield_from_close.py") # require raise_varargs - skip_tests.update( - {"basics/%s.py" % t for t in "try_reraise try_reraise2".split()} - ) # require raise_varargs - skip_tests.add("basics/annotate_var.py") # requires checking for unbound local - skip_tests.add("basics/del_deref.py") # requires checking for unbound local - skip_tests.add("basics/del_local.py") # requires checking for unbound local - skip_tests.add("basics/exception_chain.py") # raise from is not supported - skip_tests.add("basics/scope_implicit.py") # requires checking for unbound local - skip_tests.add("basics/sys_tracebacklimit.py") # requires traceback info - skip_tests.add("basics/try_finally_return2.py") # requires raise_varargs - skip_tests.add("basics/unboundlocal.py") # requires checking for unbound local - skip_tests.add("misc/features.py") # requires raise_varargs - skip_tests.add( - "misc/print_exception.py" - ) # because native doesn't have proper traceback info - skip_tests.add("misc/sys_exc_info.py") # sys.exc_info() is not supported for native - skip_tests.add("misc/sys_settrace_features.py") # sys.settrace() not supported - skip_tests.add("misc/sys_settrace_generator.py") # sys.settrace() not supported - skip_tests.add("misc/sys_settrace_loop.py") # sys.settrace() not supported - skip_tests.add( - "micropython/emg_exc.py" - ) # because native doesn't have proper traceback info - skip_tests.add( - "micropython/heapalloc_slice.py" - ) # because native doesn't do the stack-allocated slice optimisation - skip_tests.add( - "micropython/heapalloc_traceback.py" - ) # because native doesn't have proper traceback info - skip_tests.add( - "micropython/opt_level_lineno.py" - ) # native doesn't have proper traceback info - skip_tests.add("micropython/schedule.py") # native code doesn't check pending events - skip_tests.add("stress/bytecode_limit.py") # bytecode specific test - skip_tests.add("extmod/asyncio_event_queue.py") # native can't run schedule - skip_tests.add("extmod/asyncio_iterator_event.py") # native can't run schedule - def run_one_test(test_file): test_file = test_file.replace("\\", "/") test_file_abspath = os.path.abspath(test_file).replace("\\", "/") From 1401fdb8b39bf08e9ed9d4c1293207a6bdc93cf8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 26 Jul 2025 19:11:36 +1000 Subject: [PATCH 03/60] tests/run-tests.py: Run tests-with-regex-output as normal tests. Some tests (currently given by the `special_tests` list) have output which must be mached via a regex, because it can change from run to run (eg the address of an object is printed). These tests are currently classified as `is_special` in the test runner, which means they get special treatment. In particular they don't set the emitter as specified by `args.emit`. That means these tests do not run via .mpy or using the native emitter, even if those options are given on the command line. This commit fixes that by considering `is_special` as different to `tests_with_regex_output`. The former is used for things like target feature detection (which are not really tests) and when extra command line options need to be passed to the unix micropython executable. The latter (now called `tests_with_regex_output`) are specifically for tests that have output to be matched via regex. The `thread_exc2.py` test now needs to be excluded when running using the native emitter, because the native emitter doesn't print traceback info. And the `sys_settrace_cov.py` test needs to be excluded because set-trace output is different with the native emitter. Signed-off-by: Damien George --- tests/run-tests.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index cb3a2c3cd1630..859f16f484a85 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -127,6 +127,7 @@ def open(self, path, mode): "micropython/emg_exc.py", "micropython/heapalloc_traceback.py", "micropython/opt_level_lineno.py", + "thread/thread_exc2.py", # These require stack-allocated slice optimisation. "micropython/heapalloc_slice.py", # These require running the scheduler. @@ -136,6 +137,7 @@ def open(self, path, mode): # These require sys.exc_info(). "misc/sys_exc_info.py", # These require sys.settrace(). + "misc/sys_settrace_cov.py", "misc/sys_settrace_features.py", "misc/sys_settrace_generator.py", "misc/sys_settrace_loop.py", @@ -392,7 +394,7 @@ def run_script_on_remote_target(pyb, args, test_file, is_special): return had_crash, output_mupy -special_tests = [ +tests_with_regex_output = [ base_path(file) for file in ( "micropython/meminfo.py", @@ -409,10 +411,7 @@ def run_micropython(pyb, args, test_file, test_file_abspath, is_special=False): had_crash = False if pyb is None: # run on PC - if ( - test_file_abspath.startswith((base_path("cmdline/"), base_path("feature_check/"))) - or test_file_abspath in special_tests - ): + if test_file_abspath.startswith((base_path("cmdline/"), base_path("feature_check/"))): # special handling for tests of the unix cmdline program is_special = True @@ -544,7 +543,7 @@ def send_get(what): if is_special and not had_crash and b"\nSKIP\n" in output_mupy: return b"SKIP\n" - if is_special or test_file_abspath in special_tests: + if is_special or test_file_abspath in tests_with_regex_output: # convert parts of the output that are not stable across runs with open(test_file + ".exp", "rb") as f: lines_exp = [] From 1273751a3b6f8a14679378e2e082e03979cb2ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Tue, 22 Jul 2025 15:57:49 +0200 Subject: [PATCH 04/60] esp32: Support building against IDFv5.5. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniël van de Giessen --- ports/esp32/boards/sdkconfig.base | 1 + ports/esp32/esp32_common.cmake | 2 +- ports/esp32/machine_timer.c | 5 +++++ ports/esp32/modespnow.c | 11 ++++++++++- ports/esp32/network_wlan.c | 7 ++++++- 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index 4bbccf77d17d7..78b09ec6b24b0 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -119,6 +119,7 @@ CONFIG_UART_ISR_IN_IRAM=y # IDF 5 deprecated CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN=y CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y +CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN=y CONFIG_ETH_USE_SPI_ETHERNET=y CONFIG_ETH_SPI_ETHERNET_W5500=y diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 9e8acf8897c6a..79a60adac9f36 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -265,7 +265,7 @@ target_include_directories(${MICROPY_TARGET} PUBLIC # Add additional extmod and usermod components. if (MICROPY_PY_BTREE) - target_link_libraries(${MICROPY_TARGET} micropy_extmod_btree) + target_link_libraries(${MICROPY_TARGET} $) endif() target_link_libraries(${MICROPY_TARGET} usermod) diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index 3fb893aadf1ab..b0292a0379fea 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -181,8 +181,13 @@ void machine_timer_enable(machine_timer_obj_t *self) { timer_ll_enable_counter(self->hal_context.dev, self->index, false); esp_clk_tree_enable_src(TIMER_CLK_SRC, true); + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) timer_ll_set_clock_source(self->hal_context.dev, self->index, TIMER_CLK_SRC); timer_ll_enable_clock(self->hal_context.dev, self->index, true); + #else + timer_ll_set_clock_source(self->group, self->index, TIMER_CLK_SRC); + timer_ll_enable_clock(self->group, self->index, true); + #endif timer_ll_set_clock_prescale(self->hal_context.dev, self->index, TIMER_DIVIDER); timer_hal_set_counter_value(&self->hal_context, 0); timer_ll_set_count_direction(self->hal_context.dev, self->index, GPTIMER_COUNT_UP); diff --git a/ports/esp32/modespnow.c b/ports/esp32/modespnow.c index 7873ff8977876..ab50032ffeb51 100644 --- a/ports/esp32/modespnow.c +++ b/ports/esp32/modespnow.c @@ -179,7 +179,11 @@ static mp_obj_t espnow_make_new(const mp_obj_type_t *type, size_t n_args, } // Forward declare the send and recv ESPNow callbacks +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) static void send_cb(const uint8_t *mac_addr, esp_now_send_status_t status); +#else +static void send_cb(const esp_now_send_info_t *tx_info, esp_now_send_status_t status); +#endif static void recv_cb(const esp_now_recv_info_t *recv_info, const uint8_t *msg, int msg_len); @@ -539,7 +543,12 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_send_obj, 2, 4, espnow_send); // Callback triggered when a sent packet is acknowledged by the peer (or not). // Just count the number of responses and number of failures. // These are used in the send() logic. -static void send_cb(const uint8_t *mac_addr, esp_now_send_status_t status) { +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) +static void send_cb(const uint8_t *mac_addr, esp_now_send_status_t status) +#else +static void send_cb(const esp_now_send_info_t *tx_info, esp_now_send_status_t status) +#endif +{ esp_espnow_obj_t *self = _get_singleton(); self->tx_responses++; if (status != ESP_NOW_SEND_SUCCESS) { diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index e85d1328fdc2b..e20af4806c43d 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -767,6 +767,9 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_ENT), MP_ROM_INT(WIFI_AUTH_WPA3_ENTERPRISE) }, { MP_ROM_QSTR(MP_QSTR_SEC_WPA2_WPA3_ENT), MP_ROM_INT(WIFI_AUTH_WPA2_WPA3_ENTERPRISE) }, #endif + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) + { MP_ROM_QSTR(MP_QSTR_SEC_WPA_ENT), MP_ROM_INT(WIFI_AUTH_WPA_ENTERPRISE) }, + #endif { MP_ROM_QSTR(MP_QSTR_PM_NONE), MP_ROM_INT(WIFI_PS_NONE) }, { MP_ROM_QSTR(MP_QSTR_PM_PERFORMANCE), MP_ROM_INT(WIFI_PS_MIN_MODEM) }, @@ -774,7 +777,9 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { }; static MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) +_Static_assert(WIFI_AUTH_MAX == 17, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types_generic.h"); +#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) _Static_assert(WIFI_AUTH_MAX == 16, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types_generic.h"); #elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) _Static_assert(WIFI_AUTH_MAX == 14, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types_generic.h"); From 361c615f3e272df012fc18315e226f9131d25c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Thu, 10 Jul 2025 11:05:19 +0200 Subject: [PATCH 05/60] esp32/network_ppp: Use thread-safe API for PPPoS input. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A small follow-up to 3b1e22c66947271e8b60eddf4e8aa6dadc6d9a7d, in which the entire PPP implementation was reworked to more closely resemble the extmod version. One of the differences between extmod and the ESP32 port is that the ESP32 port uses the thread-safe API, but in that changeset the PPP input function was accidentally changed to use the non-safe API. Signed-off-by: Daniël van de Giessen --- ports/esp32/network_ppp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index 8b700c98ef386..77e4385ca1ee1 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -163,7 +163,7 @@ static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) { } mp_printf(&mp_plat_print, ")\n"); #endif - pppos_input(self->pcb, (u8_t *)buf, len); + pppos_input_tcpip(self->pcb, (u8_t *)buf, len); total_len += len; } From adcfdf625be4ce1a8506dccc33ea60ab5d84ab46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Thu, 10 Jul 2025 14:43:29 +0200 Subject: [PATCH 06/60] esp32/network_ppp: Use non-thread-safe API inside status callback. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The status callback runs on the lwIP tcpip_thread, and thus must use the non-thread-safe API because the thread-safe API would cause a deadlock. Signed-off-by: Daniël van de Giessen --- extmod/network_ppp_lwip.c | 8 +++++--- ports/esp32/network_ppp.c | 11 ++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/extmod/network_ppp_lwip.c b/extmod/network_ppp_lwip.c index 2c3dac92012a0..883275f48196b 100644 --- a/extmod/network_ppp_lwip.c +++ b/extmod/network_ppp_lwip.c @@ -62,8 +62,6 @@ typedef struct _network_ppp_obj_t { const mp_obj_type_t mp_network_ppp_lwip_type; -static mp_obj_t network_ppp___del__(mp_obj_t self_in); - static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) { if (self->stream == mp_const_none) { return; @@ -88,8 +86,12 @@ static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { // only need to free the PPP PCB, not close it. self->state = STATE_ACTIVE; } + network_ppp_stream_uart_irq_disable(self); // Clean up the PPP PCB. - network_ppp___del__(MP_OBJ_FROM_PTR(self)); + if (ppp_free(pcb) == ERR_OK) { + self->state = STATE_INACTIVE; + self->pcb = NULL; + } break; default: self->state = STATE_ERROR; diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index 77e4385ca1ee1..3a9cb239db893 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -68,8 +68,6 @@ typedef struct _network_ppp_obj_t { const mp_obj_type_t esp_network_ppp_lwip_type; -static mp_obj_t network_ppp___del__(mp_obj_t self_in); - static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) { if (self->stream == mp_const_none) { return; @@ -94,8 +92,15 @@ static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { // only need to free the PPP PCB, not close it. self->state = STATE_ACTIVE; } + network_ppp_stream_uart_irq_disable(self); // Clean up the PPP PCB. - network_ppp___del__(MP_OBJ_FROM_PTR(self)); + // Note: Because we use pppapi_close instead of ppp_close, this + // callback will run on the lwIP tcpip_thread, thus to prevent a + // deadlock we must use the non-threadsafe function here. + if (ppp_free(pcb) == ERR_OK) { + self->state = STATE_INACTIVE; + self->pcb = NULL; + } break; default: self->state = STATE_ERROR; From 8c47ff7153a7a54eced6c8177698339abe4973ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Tue, 8 Jul 2025 15:52:17 +0200 Subject: [PATCH 07/60] esp32/network_ppp: Correctly clean up PPP PCB after close. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If PPP is still connected, freeing the PCB will fail and thus instead we should trigger a disconnect and wait for the lwIP callback to actually free the PCB. When PPP is not connected we should check if the freeing failed, warn the user if so, and only mark the connection as inactive if not. When all this happens during garbage collection the best case is that the PPP connection is already dead, which means the callback will be called immediately and cleanup will happen correctly. The worst case is that the connection is still alive, thus we are unable to free the PCB (lwIP won't let us) and it remains referenced in the netif_list, meaning a use-after-free happens later when lwIP traverses that linked list. This change does not fully prevent that, but it *does* improve how the PPP.active(False) method on the ESP32 port behaves: It no longer immediately tries to free (and fails), but instead triggers a disconnect and lets the cleanup happen correctly through the status callback. Signed-off-by: Daniël van de Giessen --- extmod/network_ppp_lwip.c | 19 ++++++++++--------- ports/esp32/network_ppp.c | 18 +++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/extmod/network_ppp_lwip.c b/extmod/network_ppp_lwip.c index 883275f48196b..12205521f670f 100644 --- a/extmod/network_ppp_lwip.c +++ b/extmod/network_ppp_lwip.c @@ -119,17 +119,18 @@ static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, s static mp_obj_t network_ppp___del__(mp_obj_t self_in) { network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); - if (self->state >= STATE_ACTIVE) { - if (self->state >= STATE_ERROR) { - // Still connected over the stream. - // Force the connection to close, with nocarrier=1. - self->state = STATE_INACTIVE; - ppp_close(self->pcb, 1); - } - network_ppp_stream_uart_irq_disable(self); + + network_ppp_stream_uart_irq_disable(self); + if (self->state >= STATE_ERROR) { + // Still connected over the stream. + // Force the connection to close, with nocarrier=1. + ppp_close(self->pcb, 1); + } else if (self->state >= STATE_ACTIVE) { // Free PPP PCB and reset state. + if (ppp_free(self->pcb) != ERR_OK) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_free failed")); + } self->state = STATE_INACTIVE; - ppp_free(self->pcb); self->pcb = NULL; } return mp_const_none; diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index 3a9cb239db893..18e0c8816889a 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -128,17 +128,17 @@ static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, s static mp_obj_t network_ppp___del__(mp_obj_t self_in) { network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); - if (self->state >= STATE_ACTIVE) { - if (self->state >= STATE_ERROR) { - // Still connected over the stream. - // Force the connection to close, with nocarrier=1. - self->state = STATE_INACTIVE; - pppapi_close(self->pcb, 1); - } - network_ppp_stream_uart_irq_disable(self); + network_ppp_stream_uart_irq_disable(self); + if (self->state >= STATE_ERROR) { + // Still connected over the stream. + // Force the connection to close, with nocarrier=1. + pppapi_close(self->pcb, 1); + } else if (self->state >= STATE_ACTIVE) { // Free PPP PCB and reset state. + if (pppapi_free(self->pcb) != ERR_OK) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppapi_free failed")); + } self->state = STATE_INACTIVE; - pppapi_free(self->pcb); self->pcb = NULL; } return mp_const_none; From 20e1ae07336a89e7054562a1bef26fae99802210 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Sun, 3 Aug 2025 00:09:09 +0200 Subject: [PATCH 08/60] tools/mpremote: Fix encoding error in PyboardCommand. This is a fix for utf-8 decoding errors that are thrown when non-utf-8 content is received. For instance during a reboot of an ESP8266 module. The fix is to handle conversion errors by replacing illegal characters. Note that these illegal characters most often occur during an MCU reboot sequence when the MCU is using baudrates different from 115200. Signed-off-by: Jos Verlinde --- tools/mpremote/mpremote/transport_serial.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mpremote/mpremote/transport_serial.py b/tools/mpremote/mpremote/transport_serial.py index daeff02b594e2..e2490a7caf8be 100644 --- a/tools/mpremote/mpremote/transport_serial.py +++ b/tools/mpremote/mpremote/transport_serial.py @@ -795,7 +795,7 @@ def rd_str(self): if n == 0: return "" else: - return str(self.fin.read(n), "utf8") + return str(self.fin.read(n), "utf8", errors="backslashreplace") def wr_s8(self, i): self.fout.write(struct.pack(" {n}") From 6e450dba7e5042355462b807400ba8d389fc17e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Thu, 7 Aug 2025 13:30:13 +0200 Subject: [PATCH 09/60] tools/codeformat.py: Iterate lines instead of modifying list in-place. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Lechner Signed-off-by: Daniël van de Giessen --- tools/codeformat.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tools/codeformat.py b/tools/codeformat.py index c65174ddc1278..afba5c336d81d 100755 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -92,21 +92,20 @@ def fixup_c(filename): # Write out file with fixups. with open(filename, "w", newline="") as f: dedent_stack = [] - while lines: - # Get next line. - l = lines.pop(0) - + for line_number, line in enumerate(lines, 1): # Dedent #'s to match indent of following line (not previous line). - m = re.match(r"( +)#(if |ifdef |ifndef |elif |else|endif)", l) + m = re.match(r"( +)#(if |ifdef |ifndef |elif |else|endif)", line) if m: indent = len(m.group(1)) directive = m.group(2) if directive in ("if ", "ifdef ", "ifndef "): - l_next = lines[0] - indent_next = len(re.match(r"( *)", l_next).group(1)) - if indent - 4 == indent_next and re.match(r" +(} else |case )", l_next): + # Line numbers are 1-based, and lists are always 0-based, + # thus this retrieves the next line, not the current one + line_next = lines[line_number] + indent_next = len(re.match(r"( *)", line_next).group(1)) + if indent - 4 == indent_next and re.match(r" +(} else |case )", line_next): # This #-line (and all associated ones) needs dedenting by 4 spaces. - l = l[4:] + line = line[4:] dedent_stack.append(indent - 4) else: # This #-line does not need dedenting. @@ -116,12 +115,12 @@ def fixup_c(filename): # This associated #-line needs dedenting to match the #if. indent_diff = indent - dedent_stack[-1] assert indent_diff >= 0 - l = l[indent_diff:] + line = line[indent_diff:] if directive == "endif": dedent_stack.pop() # Write out line. - f.write(l) + f.write(line) assert not dedent_stack, filename From 485dac783b8ba7b88fdbf28fcdf54eb053cd8ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Tue, 5 Aug 2025 13:21:59 +0200 Subject: [PATCH 10/60] tools/codeformat.py: Print filename + linenumber when dedenting fails. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniël van de Giessen --- tools/codeformat.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/codeformat.py b/tools/codeformat.py index afba5c336d81d..7f13a059f45b4 100755 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -75,6 +75,10 @@ UNCRUSTIFY_CFG = os.path.join(TOP, "tools/uncrustify.cfg") +class IndentationError(ValueError): + pass + + def list_files(paths, exclusions=None, prefix=""): files = set() for pattern in paths: @@ -111,6 +115,10 @@ def fixup_c(filename): # This #-line does not need dedenting. dedent_stack.append(-1) else: + if len(dedent_stack) == 0: + raise IndentationError( + f'dedent stack is empty for "{directive}" at {filename}:{line_number}' + ) if dedent_stack[-1] >= 0: # This associated #-line needs dedenting to match the #if. indent_diff = indent - dedent_stack[-1] From 14ccdeb4d7b9b88ab012258e77d3070340fbc3da Mon Sep 17 00:00:00 2001 From: Jared Hancock Date: Mon, 25 Mar 2024 20:58:51 -0500 Subject: [PATCH 11/60] extmod/modre: Add support for start- and endpos. Pattern objects have two additional parameters for the ::search and ::match methods to define the starting and ending position of the subject within the string to be searched. This allows for searching a sub-string without creating a slice. However, one caveat of using the start-pos rather than a slice is that the start anchor (`^`) remains anchored to the beginning of the text. Signed-off-by: Jared Hancock --- docs/library/re.rst | 14 +++++- extmod/modre.c | 25 +++++++++- tests/extmod/re_start_end_pos.py | 78 ++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 tests/extmod/re_start_end_pos.py diff --git a/docs/library/re.rst b/docs/library/re.rst index 19b15d2d2c299..b8aeefd90cfa4 100644 --- a/docs/library/re.rst +++ b/docs/library/re.rst @@ -154,8 +154,8 @@ Regex objects Compiled regular expression. Instances of this class are created using `re.compile()`. -.. method:: regex.match(string) - regex.search(string) +.. method:: regex.match(string, [pos, [endpos]]) + regex.search(string, [pos, [endpos]]) regex.sub(replace, string, count=0, flags=0, /) Similar to the module-level functions :meth:`match`, :meth:`search` @@ -163,6 +163,16 @@ Compiled regular expression. Instances of this class are created using Using methods is (much) more efficient if the same regex is applied to multiple strings. + The optional second parameter *pos* gives an index in the string where the + search is to start; it defaults to ``0``. This is not completely equivalent + to slicing the string; the ``'^'`` pattern character matches at the real + beginning of the string and at positions just after a newline, but not + necessarily at the index where the search is to start. + + The optional parameter *endpos* limits how far the string will be searched; + it will be as if the string is *endpos* characters long, so only the + characters from *pos* to ``endpos - 1`` will be searched for a match. + .. method:: regex.split(string, max_split=-1, /) Split a *string* using regex. If *max_split* is given, it specifies diff --git a/extmod/modre.c b/extmod/modre.c index d17ec68d50eda..85e5d1b0f74f6 100644 --- a/extmod/modre.c +++ b/extmod/modre.c @@ -196,10 +196,11 @@ static void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t // Note: this function can't be named re_exec because it may clash with system headers, eg on FreeBSD static mp_obj_t re_exec_helper(bool is_anchored, uint n_args, const mp_obj_t *args) { - (void)n_args; mp_obj_re_t *self; + bool was_compiled = false; if (mp_obj_is_type(args[0], (mp_obj_type_t *)&re_type)) { self = MP_OBJ_TO_PTR(args[0]); + was_compiled = true; } else { self = MP_OBJ_TO_PTR(mod_re_compile(1, args)); } @@ -207,6 +208,28 @@ static mp_obj_t re_exec_helper(bool is_anchored, uint n_args, const mp_obj_t *ar size_t len; subj.begin_line = subj.begin = mp_obj_str_get_data(args[1], &len); subj.end = subj.begin + len; + + if (was_compiled && n_args > 2) { + // Arg #2 is starting-pos + mp_int_t startpos = mp_obj_get_int(args[2]); + if (startpos > (mp_int_t)len) { + startpos = len; + } else if (startpos < 0) { + startpos = 0; + } + subj.begin += startpos; + if (n_args > 3) { + // Arg #3 is ending-pos + mp_int_t endpos = mp_obj_get_int(args[3]); + if (endpos > (mp_int_t)len) { + endpos = len; + } else if (endpos < startpos) { + endpos = startpos; + } + subj.end = subj.begin_line + endpos; + } + } + int caps_num = (self->re.sub + 1) * 2; mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, caps, char *, caps_num); // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char diff --git a/tests/extmod/re_start_end_pos.py b/tests/extmod/re_start_end_pos.py new file mode 100644 index 0000000000000..bd16584374b89 --- /dev/null +++ b/tests/extmod/re_start_end_pos.py @@ -0,0 +1,78 @@ +# test start and end pos specification + +try: + import re +except ImportError: + print("SKIP") + raise SystemExit + + +def print_groups(match): + print("----") + try: + if match is not None: + i = 0 + while True: + print(match.group(i)) + i += 1 + except IndexError: + pass + + +p = re.compile(r"o") +m = p.match("dog") +print_groups(m) + +m = p.match("dog", 1) +print_groups(m) + +m = p.match("dog", 2) +print_groups(m) + +# No match past end of input +m = p.match("dog", 5) +print_groups(m) + +m = p.match("dog", 0, 1) +print_groups(m) + +# Caret only matches the actual beginning +p = re.compile(r"^o") +m = p.match("dog", 1) +print_groups(m) + +# End at beginning means searching empty string +p = re.compile(r"o") +m = p.match("dog", 1, 1) +print_groups(m) + +# End before the beginning doesn't match anything +m = p.match("dog", 2, 1) +print_groups(m) + +# Negative starting values don't crash +m = p.search("dog", -2) +print_groups(m) + +m = p.search("dog", -2, -5) +print_groups(m) + +# Search also works +print("--search") + +p = re.compile(r"o") +m = p.search("dog") +print_groups(m) + +m = p.search("dog", 1) +print_groups(m) + +m = p.search("dog", 2) +print_groups(m) + +# Negative starting values don't crash +m = p.search("dog", -2) +print_groups(m) + +m = p.search("dog", -2, -5) +print_groups(m) From f10707febb41e042e929efaf9ee4a66b847a68d4 Mon Sep 17 00:00:00 2001 From: Jared Hancock Date: Fri, 6 Jun 2025 18:12:29 -0500 Subject: [PATCH 12/60] extmod/modlwip: Support `family` specification in getaddrinfo. `socket.getaddrinfo()` supports the specification of an address family; however, the LwIP implementation does not use it. This change allows the application to specify the address family request in DNS resolution. If no family is specified, it falls back to the default preference configured with `network.ipconfig()`. Signed-off-by: Jared Hancock --- extmod/modlwip.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index b84b3b7626bfe..26aab6e81094a 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -1802,10 +1802,11 @@ static mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) { mp_obj_t host_in = args[0], port_in = args[1]; const char *host = mp_obj_str_get_str(host_in); mp_int_t port = mp_obj_get_int(port_in); + mp_int_t family = 0; // If constraints were passed then check they are compatible with the supported params if (n_args > 2) { - mp_int_t family = mp_obj_get_int(args[2]); + family = mp_obj_get_int(args[2]); mp_int_t type = 0; mp_int_t proto = 0; mp_int_t flags = 0; @@ -1818,7 +1819,7 @@ static mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) { } } } - if (!((family == 0 || family == MOD_NETWORK_AF_INET) + if (!((family == 0 || family == MOD_NETWORK_AF_INET || family == MOD_NETWORK_AF_INET6) && (type == 0 || type == MOD_NETWORK_SOCK_STREAM) && proto == 0 && flags == 0)) { @@ -1829,11 +1830,23 @@ static mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) { getaddrinfo_state_t state; state.status = 0; + #if LWIP_VERSION_MAJOR >= 2 + // If family was specified, then try and resolve the address type as + // requested. Otherwise, use the default from network configuration. + if (family == MOD_NETWORK_AF_INET) { + family = LWIP_DNS_ADDRTYPE_IPV4; + } else if (family == MOD_NETWORK_AF_INET6) { + family = LWIP_DNS_ADDRTYPE_IPV6; + } else { + family = mp_mod_network_prefer_dns_use_ip_version == 4 ? LWIP_DNS_ADDRTYPE_IPV4_IPV6 : LWIP_DNS_ADDRTYPE_IPV6_IPV4; + } + #endif + MICROPY_PY_LWIP_ENTER #if LWIP_VERSION_MAJOR < 2 err_t ret = dns_gethostbyname(host, (ip_addr_t *)&state.ipaddr, lwip_getaddrinfo_cb, &state); #else - err_t ret = dns_gethostbyname_addrtype(host, (ip_addr_t *)&state.ipaddr, lwip_getaddrinfo_cb, &state, mp_mod_network_prefer_dns_use_ip_version == 4 ? LWIP_DNS_ADDRTYPE_IPV4_IPV6 : LWIP_DNS_ADDRTYPE_IPV6_IPV4); + err_t ret = dns_gethostbyname_addrtype(host, (ip_addr_t *)&state.ipaddr, lwip_getaddrinfo_cb, &state, family); #endif MICROPY_PY_LWIP_EXIT From b1d5c656de3f11fc1729f165c8d9709594ccbfb6 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 24 Jul 2025 23:48:01 +0200 Subject: [PATCH 13/60] tests/micropython: Remove big ints dependence for viper boundary tests. This commit provides an implementation for viper boundary tests that can work even without big int support. Since it uses a fixed-size buffer to hold values to work with, this should work on any platform as long as its integers are at least 32 bits wide, regardless its configuration on how big integers can get. Signed-off-by: Alessandro Gatti --- ...ntbig.py => viper_ptr16_store_boundary.py} | 43 ++++++++++--------- ....exp => viper_ptr16_store_boundary.py.exp} | 0 ...ntbig.py => viper_ptr32_store_boundary.py} | 42 +++++++++--------- ....exp => viper_ptr32_store_boundary.py.exp} | 0 ...intbig.py => viper_ptr8_store_boundary.py} | 42 +++++++++--------- ...y.exp => viper_ptr8_store_boundary.py.exp} | 0 6 files changed, 67 insertions(+), 60 deletions(-) rename tests/micropython/{viper_ptr16_store_boundary_intbig.py => viper_ptr16_store_boundary.py} (57%) rename tests/micropython/{viper_ptr16_store_boundary_intbig.py.exp => viper_ptr16_store_boundary.py.exp} (100%) rename tests/micropython/{viper_ptr32_store_boundary_intbig.py => viper_ptr32_store_boundary.py} (60%) rename tests/micropython/{viper_ptr32_store_boundary_intbig.py.exp => viper_ptr32_store_boundary.py.exp} (100%) rename tests/micropython/{viper_ptr8_store_boundary_intbig.py => viper_ptr8_store_boundary.py} (55%) rename tests/micropython/{viper_ptr8_store_boundary_intbig.py.exp => viper_ptr8_store_boundary.py.exp} (100%) diff --git a/tests/micropython/viper_ptr16_store_boundary_intbig.py b/tests/micropython/viper_ptr16_store_boundary.py similarity index 57% rename from tests/micropython/viper_ptr16_store_boundary_intbig.py rename to tests/micropython/viper_ptr16_store_boundary.py index 2193eddae1385..3501a05685ec3 100644 --- a/tests/micropython/viper_ptr16_store_boundary_intbig.py +++ b/tests/micropython/viper_ptr16_store_boundary.py @@ -15,6 +15,23 @@ def set{off}(dest: ptr16): MASK = (1 << (8 * SIZE)) - 1 +next_int = 1 +test_buffer = bytearray(SIZE) + + +def next_value() -> uint: + global next_int + global test_buffer + for index in range(1, SIZE): + test_buffer[index - 1] = test_buffer[index] + test_buffer[SIZE - 1] = next_int + next_int += 1 + output = 0 + for byte in test_buffer: + output = (output << 8) | byte + return output & MASK + + @micropython.viper def set_index(dest: ptr16, i: int, val: uint): saved = dest @@ -27,8 +44,6 @@ def get_index(src, i): buffer = bytearray(((1 << max(BIT_THRESHOLDS) + 1) // 1024) * 1024) -next = 1 -val = 0 for bit in BIT_THRESHOLDS: print("---", bit) pre, idx, post = ( @@ -36,22 +51,10 @@ def get_index(src, i): (((1 << bit) - (1 * SIZE)) // SIZE), ((1 << bit) // SIZE), ) - val = (val << 8) + next - next += 1 - set_index(buffer, pre, val & MASK) - val = (val << 8) + next - next += 1 - set_index(buffer, idx, val & MASK) - val = (val << 8) + next - next += 1 - set_index(buffer, post, val & MASK) - val = (val << 8) + next - next += 1 + set_index(buffer, pre, next_value()) + set_index(buffer, idx, next_value()) + set_index(buffer, post, next_value()) print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post))) - exec(SET_TEMPLATE.format(off=pre, val=val & MASK)) - val = (val << 8) + next - next += 1 - exec(SET_TEMPLATE.format(off=idx, val=val & MASK)) - val = (val << 8) + next - next += 1 - exec(SET_TEMPLATE.format(off=post, val=val & MASK)) + exec(SET_TEMPLATE.format(off=pre, val=next_value())) + exec(SET_TEMPLATE.format(off=idx, val=next_value())) + exec(SET_TEMPLATE.format(off=post, val=next_value())) diff --git a/tests/micropython/viper_ptr16_store_boundary_intbig.py.exp b/tests/micropython/viper_ptr16_store_boundary.py.exp similarity index 100% rename from tests/micropython/viper_ptr16_store_boundary_intbig.py.exp rename to tests/micropython/viper_ptr16_store_boundary.py.exp diff --git a/tests/micropython/viper_ptr32_store_boundary_intbig.py b/tests/micropython/viper_ptr32_store_boundary.py similarity index 60% rename from tests/micropython/viper_ptr32_store_boundary_intbig.py rename to tests/micropython/viper_ptr32_store_boundary.py index b44f31b00af4d..8c207278ec538 100644 --- a/tests/micropython/viper_ptr32_store_boundary_intbig.py +++ b/tests/micropython/viper_ptr32_store_boundary.py @@ -14,6 +14,22 @@ def set{off}(dest: ptr32): SIZE = 4 MASK = (1 << (8 * SIZE)) - 1 +next_int = 1 +test_buffer = bytearray(SIZE) + + +def next_value() -> uint: + global next_int + global test_buffer + for index in range(1, SIZE): + test_buffer[index - 1] = test_buffer[index] + test_buffer[SIZE - 1] = next_int + next_int += 1 + output = 0 + for byte in test_buffer: + output = (output << 8) | byte + return output & MASK + @micropython.viper def set_index(dest: ptr32, i: int, val: uint): @@ -32,8 +48,6 @@ def get_index(src, i): buffer = bytearray(((1 << max(BIT_THRESHOLDS) + 1) // 1024) * 1024) -next = 1 -val = 0 for bit in BIT_THRESHOLDS: print("---", bit) pre, idx, post = ( @@ -41,22 +55,10 @@ def get_index(src, i): (((1 << bit) - (1 * SIZE)) // SIZE), ((1 << bit) // SIZE), ) - val = (val << 8) + next - next += 1 - set_index(buffer, pre, val & MASK) - val = (val << 8) + next - next += 1 - set_index(buffer, idx, val & MASK) - val = (val << 8) + next - next += 1 - set_index(buffer, post, val & MASK) - val = (val << 8) + next - next += 1 + set_index(buffer, pre, next_value()) + set_index(buffer, idx, next_value()) + set_index(buffer, post, next_value()) print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post))) - exec(SET_TEMPLATE.format(off=pre, val=val & MASK)) - val = (val << 8) + next - next += 1 - exec(SET_TEMPLATE.format(off=idx, val=val & MASK)) - val = (val << 8) + next - next += 1 - exec(SET_TEMPLATE.format(off=post, val=val & MASK)) + exec(SET_TEMPLATE.format(off=pre, val=next_value())) + exec(SET_TEMPLATE.format(off=idx, val=next_value())) + exec(SET_TEMPLATE.format(off=post, val=next_value())) diff --git a/tests/micropython/viper_ptr32_store_boundary_intbig.py.exp b/tests/micropython/viper_ptr32_store_boundary.py.exp similarity index 100% rename from tests/micropython/viper_ptr32_store_boundary_intbig.py.exp rename to tests/micropython/viper_ptr32_store_boundary.py.exp diff --git a/tests/micropython/viper_ptr8_store_boundary_intbig.py b/tests/micropython/viper_ptr8_store_boundary.py similarity index 55% rename from tests/micropython/viper_ptr8_store_boundary_intbig.py rename to tests/micropython/viper_ptr8_store_boundary.py index d22a062743321..d3cba17e16318 100644 --- a/tests/micropython/viper_ptr8_store_boundary_intbig.py +++ b/tests/micropython/viper_ptr8_store_boundary.py @@ -14,6 +14,22 @@ def set{off}(dest: ptr8): SIZE = 1 MASK = (1 << (8 * SIZE)) - 1 +next_int = 1 +test_buffer = bytearray(SIZE) + + +def next_value() -> uint: + global next_int + global test_buffer + for index in range(1, SIZE): + test_buffer[index - 1] = test_buffer[index] + test_buffer[SIZE - 1] = next_int + next_int += 1 + output = 0 + for byte in test_buffer: + output = (output << 8) | byte + return output & MASK + @micropython.viper def set_index(dest: ptr8, i: int, val: uint): @@ -27,27 +43,13 @@ def get_index(src: ptr8, i: int): buffer = bytearray(((1 << max(BIT_THRESHOLDS) + 1) // 1024) * 1024) -next = 1 -val = 0 for bit in BIT_THRESHOLDS: print("---", bit) pre, idx, post = (((1 << bit) - (2 * SIZE)), ((1 << bit) - (1 * SIZE)), (1 << bit)) - val = (val << 8) + next - next += 1 - set_index(buffer, pre, val & MASK) - val = (val << 8) + next - next += 1 - set_index(buffer, idx, val & MASK) - val = (val << 8) + next - next += 1 - set_index(buffer, post, val & MASK) - val = (val << 8) + next - next += 1 + set_index(buffer, pre, next_value()) + set_index(buffer, idx, next_value()) + set_index(buffer, post, next_value()) print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post))) - exec(SET_TEMPLATE.format(off=pre, val=val & MASK)) - val = (val << 8) + next - next += 1 - exec(SET_TEMPLATE.format(off=idx, val=val & MASK)) - val = (val << 8) + next - next += 1 - exec(SET_TEMPLATE.format(off=post, val=val & MASK)) + exec(SET_TEMPLATE.format(off=pre, val=next_value())) + exec(SET_TEMPLATE.format(off=idx, val=next_value())) + exec(SET_TEMPLATE.format(off=post, val=next_value())) diff --git a/tests/micropython/viper_ptr8_store_boundary_intbig.py.exp b/tests/micropython/viper_ptr8_store_boundary.py.exp similarity index 100% rename from tests/micropython/viper_ptr8_store_boundary_intbig.py.exp rename to tests/micropython/viper_ptr8_store_boundary.py.exp From 0ee3f99da24e678a415a1d0b4575c38ad51430fe Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 10 Jul 2025 00:50:15 +0200 Subject: [PATCH 14/60] py/asmrv32: Make lt/le comparisons emitter shorter. This commit simplifies the emitter code in charge of generating opcodes performing less-than and less-than-or-equal comparisons. By rewriting the SLT/SLTU opcode generator (handling less-than comparisons) and de-inlining the less-than comparison generator call in the less-than-or-equal generator, the output binary is ~80 bytes smaller (measurements taken from the QEMU port). Signed-off-by: Alessandro Gatti --- py/asmrv32.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/py/asmrv32.c b/py/asmrv32.c index 158b5521917a6..723d32cdd7c89 100644 --- a/py/asmrv32.c +++ b/py/asmrv32.c @@ -573,12 +573,8 @@ void asm_rv32_meta_comparison_ne(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2 } void asm_rv32_meta_comparison_lt(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd, bool unsigned_comparison) { - // slt(u) rd, rs1, rs2 - if (unsigned_comparison) { - asm_rv32_opcode_sltu(state, rd, rs1, rs2); - } else { - asm_rv32_opcode_slt(state, rd, rs1, rs2); - } + // slt|sltu rd, rs1, rs2 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, (0x02 | (unsigned_comparison ? 1 : 0)), 0x00, rd, rs1, rs2)); } void asm_rv32_meta_comparison_le(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd, bool unsigned_comparison) { @@ -588,11 +584,7 @@ void asm_rv32_meta_comparison_le(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2 // ... ; PC + 8 asm_rv32_opcode_cli(state, rd, 1); asm_rv32_opcode_beq(state, rs1, rs2, 8); - if (unsigned_comparison) { - asm_rv32_opcode_sltu(state, rd, rs1, rs2); - } else { - asm_rv32_opcode_slt(state, rd, rs1, rs2); - } + asm_rv32_meta_comparison_lt(state, rs1, rs2, rd, unsigned_comparison); } #endif // MICROPY_EMIT_RV32 From 4614ee9e682dfffa26a4226645baca0208ecfac6 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 21 Jul 2025 13:31:59 -0500 Subject: [PATCH 15/60] py/binary: Add MICROPY_PY_STRUCT_UNSAFE_TYPECODES. This adds a compile-time flag to disable some "unsafe" and non-standard typecodes in struct, array and related modules. This is useful to turn off when fuzzing, as improper use of these typecodes can crash MicroPython. Signed-off-by: Jeff Epler --- py/binary.c | 20 ++++++++++++++++---- py/mpconfig.h | 7 +++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/py/binary.c b/py/binary.c index 48d3421bca963..ef2857b4318dc 100644 --- a/py/binary.c +++ b/py/binary.c @@ -69,11 +69,13 @@ size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { case 'Q': size = 8; break; + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'P': case 'O': case 'S': size = sizeof(void *); break; + #endif case 'e': size = 2; break; @@ -119,12 +121,14 @@ size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { align = alignof(long long); size = sizeof(long long); break; + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'P': case 'O': case 'S': align = alignof(void *); size = sizeof(void *); break; + #endif case 'e': align = 2; size = 2; @@ -280,12 +284,14 @@ mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index) { case 'd': return mp_obj_new_float_from_d(((double *)p)[index]); #endif - // Extension to CPython: array of objects + // Extension to CPython: array of objects + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'O': return ((mp_obj_t *)p)[index]; // Extension to CPython: array of pointers case 'P': return mp_obj_new_int((mp_int_t)(uintptr_t)((void **)p)[index]); + #endif } return MP_OBJ_NEW_SMALL_INT(val); } @@ -334,9 +340,9 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * long long val = mp_binary_get_int(size, is_signed(val_type), (struct_type == '>'), p); - if (val_type == 'O') { + if (MICROPY_PY_STRUCT_UNSAFE_TYPECODES && val_type == 'O') { return (mp_obj_t)(mp_uint_t)val; - } else if (val_type == 'S') { + } else if (MICROPY_PY_STRUCT_UNSAFE_TYPECODES && val_type == 'S') { const char *s_val = (const char *)(uintptr_t)(mp_uint_t)val; return mp_obj_new_str_from_cstr(s_val); #if MICROPY_PY_BUILTINS_FLOAT @@ -407,9 +413,11 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p mp_uint_t val; switch (val_type) { + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'O': val = (mp_uint_t)val_in; break; + #endif #if MICROPY_PY_BUILTINS_FLOAT case 'e': val = mp_encode_half_float(mp_obj_get_float_to_f(val_in)); @@ -474,10 +482,12 @@ void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_ ((double *)p)[index] = mp_obj_get_float_to_d(val_in); break; #endif + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES // Extension to CPython: array of objects case 'O': ((mp_obj_t *)p)[index] = val_in; break; + #endif default: #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE if (mp_obj_is_exact_type(val_in, &mp_type_int)) { @@ -534,9 +544,11 @@ void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_i ((double *)p)[index] = (double)val; break; #endif - // Extension to CPython: array of pointers + // Extension to CPython: array of pointers + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'P': ((void **)p)[index] = (void *)(uintptr_t)val; break; + #endif } } diff --git a/py/mpconfig.h b/py/mpconfig.h index dd41ff6a70867..5fe0e822f775c 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1601,6 +1601,13 @@ typedef time_t mp_timestamp_t; #define MICROPY_PY_STRUCT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif +// Whether struct module provides unsafe and non-standard typecodes O, P, S. +// These typecodes are not in CPython and can cause crashes by accessing arbitrary +// memory. +#ifndef MICROPY_PY_STRUCT_UNSAFE_TYPECODES +#define MICROPY_PY_STRUCT_UNSAFE_TYPECODES (1) +#endif + // Whether to provide "sys" module #ifndef MICROPY_PY_SYS #define MICROPY_PY_SYS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) From 744270ac1b9ed3929cd41d1a6e1f6ea0e785745d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 6 Aug 2025 10:35:22 +1000 Subject: [PATCH 16/60] py/misc: Add explicit dependency on py/mpconfig.h. Macros in misc.h depend on values defined by including mpconfig.h. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- py/misc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/py/misc.h b/py/misc.h index 86ac2ec9a1061..5c1cc2f7a836b 100644 --- a/py/misc.h +++ b/py/misc.h @@ -26,6 +26,8 @@ #ifndef MICROPY_INCLUDED_PY_MISC_H #define MICROPY_INCLUDED_PY_MISC_H +#include "py/mpconfig.h" + // a mini library of useful types and functions /** types *******************************************************/ From f493075d88be9ce851c4d401d041b801701975c3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 10 Aug 2025 22:23:26 +1000 Subject: [PATCH 17/60] tests/run-tests.py: Automatically include float tests when possible. This simplifies the code by removing the explicit addition of the "float/" test directory for certain targets. It also means the tests won't be added incorrectly, eg on a unix build without float. Signed-off-by: Damien George --- tests/feature_check/float.py | 13 ----------- tests/feature_check/float.py.exp | 1 - tests/feature_check/target_info.py | 13 ++++++++++- tests/run-tests.py | 37 +++++++++++++----------------- 4 files changed, 28 insertions(+), 36 deletions(-) delete mode 100644 tests/feature_check/float.py delete mode 100644 tests/feature_check/float.py.exp diff --git a/tests/feature_check/float.py b/tests/feature_check/float.py deleted file mode 100644 index d6d2a99d2429d..0000000000000 --- a/tests/feature_check/float.py +++ /dev/null @@ -1,13 +0,0 @@ -# detect how many bits of precision the floating point implementation has - -try: - float -except NameError: - print(0) -else: - if float("1.0000001") == float("1.0"): - print(30) - elif float("1e300") == float("inf"): - print(32) - else: - print(64) diff --git a/tests/feature_check/float.py.exp b/tests/feature_check/float.py.exp deleted file mode 100644 index 900731ffd51ff..0000000000000 --- a/tests/feature_check/float.py.exp +++ /dev/null @@ -1 +0,0 @@ -64 diff --git a/tests/feature_check/target_info.py b/tests/feature_check/target_info.py index 9501d808ef214..6d95f8b163c09 100644 --- a/tests/feature_check/target_info.py +++ b/tests/feature_check/target_info.py @@ -22,4 +22,15 @@ ][sys_mpy >> 10] thread = getattr(sys.implementation, "_thread", None) -print(platform, arch, thread) +# Detect how many bits of precision the floating point implementation has. +try: + if float("1.0000001") == float("1.0"): + float_prec = 30 + elif float("1e300") == float("inf"): + float_prec = 32 + else: + float_prec = 64 +except NameError: + float_prec = 0 + +print(platform, arch, thread, float_prec) diff --git a/tests/run-tests.py b/tests/run-tests.py index 859f16f484a85..498370f70e90c 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -292,12 +292,13 @@ def detect_test_platform(pyb, args): output = run_feature_check(pyb, args, "target_info.py") if output.endswith(b"CRASH"): raise ValueError("cannot detect platform: {}".format(output)) - platform, arch, thread = str(output, "ascii").strip().split() + platform, arch, thread, float_prec = str(output, "ascii").strip().split() if arch == "None": arch = None inlineasm_arch = detect_inline_asm_arch(pyb, args) if thread == "None": thread = None + float_prec = int(float_prec) args.platform = platform args.arch = arch @@ -305,6 +306,7 @@ def detect_test_platform(pyb, args): args.mpy_cross_flags = "-march=" + arch args.inlineasm_arch = inlineasm_arch args.thread = thread + args.float_prec = float_prec print("platform={}".format(platform), end="") if arch: @@ -313,6 +315,8 @@ def detect_test_platform(pyb, args): print(" inlineasm={}".format(inlineasm_arch), end="") if thread: print(" thread={}".format(thread), end="") + if float_prec: + print(" float={}-bit".format(float_prec), end="") print() @@ -685,8 +689,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): has_complex = True has_coverage = False - upy_float_precision = 32 - if True: # Even if we run completely different tests in a different directory, # we need to access feature_checks from the same directory as the @@ -774,11 +776,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("cmdline/repl_words_move.py") upy_byteorder = run_feature_check(pyb, args, "byteorder.py") - upy_float_precision = run_feature_check(pyb, args, "float.py") - try: - upy_float_precision = int(upy_float_precision) - except ValueError: - upy_float_precision = 0 has_complex = run_feature_check(pyb, args, "complex.py") == b"complex\n" has_coverage = run_feature_check(pyb, args, "coverage.py") == b"coverage\n" cpy_byteorder = subprocess.check_output( @@ -814,7 +811,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # fails with stack overflow on Debug builds skip_tests.add("misc/sys_settrace_features.py") - if upy_float_precision == 0: + if args.float_prec == 0: skip_tests.add("extmod/uctypes_le_float.py") skip_tests.add("extmod/uctypes_native_float.py") skip_tests.add("extmod/uctypes_sizeof_float.py") @@ -822,7 +819,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("extmod/json_loads_float.py") skip_tests.add("extmod/random_extra_float.py") skip_tests.add("misc/rge_sm.py") - if upy_float_precision < 32: + if args.float_prec < 32: skip_tests.add( "float/float2int_intbig.py" ) # requires fp32, there's float2int_fp30_intbig.py instead @@ -832,7 +829,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("float/bytes_construct.py") # requires fp32 skip_tests.add("float/bytearray_construct.py") # requires fp32 skip_tests.add("float/float_format_ints_power10.py") # requires fp32 - if upy_float_precision < 64: + if args.float_prec < 64: skip_tests.add("float/float_divmod.py") # tested by float/float_divmod_relaxed.py instead skip_tests.add("float/float2int_doubleprec_intbig.py") skip_tests.add("float/float_struct_e_doubleprec.py") @@ -1348,26 +1345,25 @@ def main(): test_dirs += ("inlineasm/{}".format(args.inlineasm_arch),) if args.thread is not None: test_dirs += ("thread",) + if args.float_prec > 0: + test_dirs += ("float",) if args.platform == "pyboard": # run pyboard tests - test_dirs += ("float", "stress", "ports/stm32") + test_dirs += ("stress", "ports/stm32") elif args.platform == "mimxrt": - test_dirs += ("float", "stress") + test_dirs += ("stress",) elif args.platform == "renesas-ra": - test_dirs += ("float", "ports/renesas-ra") + test_dirs += ("ports/renesas-ra") elif args.platform == "rp2": - test_dirs += ("float", "stress", "ports/rp2") + test_dirs += ("stress", "ports/rp2") elif args.platform == "esp32": - test_dirs += ("float", "stress") - elif args.platform in ("esp8266", "minimal", "samd", "nrf"): - test_dirs += ("float",) + test_dirs += ("stress",) elif args.platform == "WiPy": # run WiPy tests test_dirs += ("ports/cc3200",) elif args.platform in PC_PLATFORMS: # run PC tests test_dirs += ( - "float", "import", "io", "stress", @@ -1377,11 +1373,10 @@ def main(): ) elif args.platform == "qemu": test_dirs += ( - "float", "ports/qemu", ) elif args.platform == "webassembly": - test_dirs += ("float", "ports/webassembly") + test_dirs += ("ports/webassembly",) else: # run tests from these directories test_dirs = args.test_dirs From 656582795504cb6c9b025e716f20a2468e5d71ac Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 10 Aug 2025 22:25:15 +1000 Subject: [PATCH 18/60] tests/run-tests.py: Always include stress/ tests directory in tests. Ports that now run the stress tests, that didn't prior to this commit are: cc3200, esp8266, minimal, nrf, renesas-ra, samd, qemu, webassembly. Signed-off-by: Damien George --- tests/run-tests.py | 10 +++------- tests/stress/dict_copy.py | 7 ++++++- tests/stress/dict_create.py | 6 +++++- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index 498370f70e90c..21e0ab8872abd 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -1340,6 +1340,7 @@ def main(): "micropython", "misc", "extmod", + "stress", ) if args.inlineasm_arch is not None: test_dirs += ("inlineasm/{}".format(args.inlineasm_arch),) @@ -1349,15 +1350,11 @@ def main(): test_dirs += ("float",) if args.platform == "pyboard": # run pyboard tests - test_dirs += ("stress", "ports/stm32") - elif args.platform == "mimxrt": - test_dirs += ("stress",) + test_dirs += ("ports/stm32",) elif args.platform == "renesas-ra": test_dirs += ("ports/renesas-ra") elif args.platform == "rp2": - test_dirs += ("stress", "ports/rp2") - elif args.platform == "esp32": - test_dirs += ("stress",) + test_dirs += ("ports/rp2",) elif args.platform == "WiPy": # run WiPy tests test_dirs += ("ports/cc3200",) @@ -1366,7 +1363,6 @@ def main(): test_dirs += ( "import", "io", - "stress", "unicode", "cmdline", "ports/unix", diff --git a/tests/stress/dict_copy.py b/tests/stress/dict_copy.py index 73d3a5b51d601..f9b742e20f716 100644 --- a/tests/stress/dict_copy.py +++ b/tests/stress/dict_copy.py @@ -1,6 +1,11 @@ # copying a large dictionary -a = {i: 2 * i for i in range(1000)} +try: + a = {i: 2 * i for i in range(1000)} +except MemoryError: + print("SKIP") + raise SystemExit + b = a.copy() for i in range(1000): print(i, b[i]) diff --git a/tests/stress/dict_create.py b/tests/stress/dict_create.py index e9db40a8e6c5b..91a83a12f9d85 100644 --- a/tests/stress/dict_create.py +++ b/tests/stress/dict_create.py @@ -3,6 +3,10 @@ d = {} x = 1 while x < 1000: - d[x] = x + try: + d[x] = x + except MemoryError: + print("SKIP") + raise SystemExit x += 1 print(d[500]) From 1db71f9e553f2775b2c0326a3de778319a647f90 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 11:13:56 +1000 Subject: [PATCH 19/60] tests/run-tests.py: Generalise addition of port specific test directory. Signed-off-by: Damien George --- tests/run-tests.py | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index 21e0ab8872abd..8735f93f2b3fe 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -102,6 +102,11 @@ def open(self, path, mode): # Platforms associated with the unix port, values of `sys.platform`. PC_PLATFORMS = ("darwin", "linux", "win32") +# Mapping from `sys.platform` to the port name, for special cases. +# See `platform_to_port()` function. +platform_to_port_map = {"pyboard": "stm32", "WiPy": "cc3200"} +platform_to_port_map.update({p: "unix" for p in PC_PLATFORMS}) + # Tests to skip for specific emitters. emitter_tests_to_skip = { # Some tests are known to fail with native emitter. @@ -252,6 +257,10 @@ def convert_regex_escapes(line): return bytes("".join(cs), "utf8") +def platform_to_port(platform): + return platform_to_port_map.get(platform, platform) + + def get_test_instance(test_instance, baudrate, user, password): if test_instance.startswith("port:"): _, port = test_instance.split(":", 1) @@ -1348,31 +1357,17 @@ def main(): test_dirs += ("thread",) if args.float_prec > 0: test_dirs += ("float",) - if args.platform == "pyboard": - # run pyboard tests - test_dirs += ("ports/stm32",) - elif args.platform == "renesas-ra": - test_dirs += ("ports/renesas-ra") - elif args.platform == "rp2": - test_dirs += ("ports/rp2",) - elif args.platform == "WiPy": - # run WiPy tests - test_dirs += ("ports/cc3200",) - elif args.platform in PC_PLATFORMS: + port_specific_test_dir = "ports/{}".format(platform_to_port(args.platform)) + if os.path.isdir(port_specific_test_dir): + test_dirs += (port_specific_test_dir,) + if args.platform in PC_PLATFORMS: # run PC tests test_dirs += ( "import", "io", "unicode", "cmdline", - "ports/unix", - ) - elif args.platform == "qemu": - test_dirs += ( - "ports/qemu", ) - elif args.platform == "webassembly": - test_dirs += ("ports/webassembly",) else: # run tests from these directories test_dirs = args.test_dirs From e2744ce679269692ceed2bed1e6f4f6a7840b49b Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 11:22:32 +1000 Subject: [PATCH 20/60] tests/run-tests.py: Autodetect if the target has unicode support. The unicode tests are now run on all targets that enable unicode. And other unicode tests (namely `extmod/json_loads.py`) are now properly skipped if the target doesn't have unicode support. Signed-off-by: Damien George --- tests/feature_check/target_info.py | 2 +- tests/run-tests.py | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/feature_check/target_info.py b/tests/feature_check/target_info.py index 6d95f8b163c09..962ebf4154b5c 100644 --- a/tests/feature_check/target_info.py +++ b/tests/feature_check/target_info.py @@ -33,4 +33,4 @@ except NameError: float_prec = 0 -print(platform, arch, thread, float_prec) +print(platform, arch, thread, float_prec, len("α") == 1) diff --git a/tests/run-tests.py b/tests/run-tests.py index 8735f93f2b3fe..e2bdf7c2c0286 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -301,13 +301,14 @@ def detect_test_platform(pyb, args): output = run_feature_check(pyb, args, "target_info.py") if output.endswith(b"CRASH"): raise ValueError("cannot detect platform: {}".format(output)) - platform, arch, thread, float_prec = str(output, "ascii").strip().split() + platform, arch, thread, float_prec, unicode = str(output, "ascii").strip().split() if arch == "None": arch = None inlineasm_arch = detect_inline_asm_arch(pyb, args) if thread == "None": thread = None float_prec = int(float_prec) + unicode = unicode == "True" args.platform = platform args.arch = arch @@ -316,6 +317,7 @@ def detect_test_platform(pyb, args): args.inlineasm_arch = inlineasm_arch args.thread = thread args.float_prec = float_prec + args.unicode = unicode print("platform={}".format(platform), end="") if arch: @@ -326,6 +328,8 @@ def detect_test_platform(pyb, args): print(" thread={}".format(thread), end="") if float_prec: print(" float={}-bit".format(float_prec), end="") + if unicode: + print(" unicode", end="") print() @@ -845,6 +849,9 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("float/float_format_ints_doubleprec.py") skip_tests.add("float/float_parse_doubleprec.py") + if not args.unicode: + skip_tests.add("extmod/json_loads.py") # tests loading a utf-8 character + if not has_complex: skip_tests.add("float/complex1.py") skip_tests.add("float/complex1_intbig.py") @@ -870,6 +877,9 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if args.platform not in PC_PLATFORMS: skip_tests.add("basics/exception_chain.py") # warning is not printed skip_tests.add("micropython/meminfo.py") # output is very different to PC output + skip_tests.add("unicode/file1.py") # requires local file access + skip_tests.add("unicode/file2.py") # requires local file access + skip_tests.add("unicode/file_invalid.py") # requires local file access # Skip emitter-specific tests. skip_tests.update(emitter_tests_to_skip.get(args.emit, ())) @@ -1357,6 +1367,8 @@ def main(): test_dirs += ("thread",) if args.float_prec > 0: test_dirs += ("float",) + if args.unicode: + test_dirs += ("unicode",) port_specific_test_dir = "ports/{}".format(platform_to_port(args.platform)) if os.path.isdir(port_specific_test_dir): test_dirs += (port_specific_test_dir,) @@ -1365,7 +1377,6 @@ def main(): test_dirs += ( "import", "io", - "unicode", "cmdline", ) else: From 54e6cfc6e3f3c2056bf2f06d32a58932d29f1ac3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 11:45:55 +1000 Subject: [PATCH 21/60] tests/basics: Skip tests of io module individually using SKIP. Instead of using a feature check. This is more consistent with how other optional modules are skipped. Signed-off-by: Damien George --- tests/basics/io_buffered_writer.py | 6 +++--- tests/basics/io_bytesio_cow.py | 8 +++++++- tests/basics/io_bytesio_ext.py | 8 +++++++- tests/basics/io_bytesio_ext2.py | 7 ++++++- tests/basics/io_iobase.py | 7 ++++--- tests/basics/io_stringio1.py | 7 ++++++- tests/basics/io_stringio_base.py | 6 +++++- tests/basics/io_stringio_with.py | 7 ++++++- tests/basics/io_write_ext.py | 5 +++-- tests/feature_check/io_module.py | 6 ------ tests/feature_check/io_module.py.exp | 0 tests/run-tests.py | 8 -------- 12 files changed, 47 insertions(+), 28 deletions(-) delete mode 100644 tests/feature_check/io_module.py delete mode 100644 tests/feature_check/io_module.py.exp diff --git a/tests/basics/io_buffered_writer.py b/tests/basics/io_buffered_writer.py index 60cf2c837d10f..3cfee0103f777 100644 --- a/tests/basics/io_buffered_writer.py +++ b/tests/basics/io_buffered_writer.py @@ -1,9 +1,9 @@ -import io - try: + import io + io.BytesIO io.BufferedWriter -except AttributeError: +except (AttributeError, ImportError): print('SKIP') raise SystemExit diff --git a/tests/basics/io_bytesio_cow.py b/tests/basics/io_bytesio_cow.py index 2edb7136a9691..543c12ad42ab2 100644 --- a/tests/basics/io_bytesio_cow.py +++ b/tests/basics/io_bytesio_cow.py @@ -1,6 +1,12 @@ # Make sure that write operations on io.BytesIO don't # change original object it was constructed from. -import io + +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + b = b"foobar" a = io.BytesIO(b) diff --git a/tests/basics/io_bytesio_ext.py b/tests/basics/io_bytesio_ext.py index 4d4c60c1363b7..92e715178116c 100644 --- a/tests/basics/io_bytesio_ext.py +++ b/tests/basics/io_bytesio_ext.py @@ -1,5 +1,11 @@ # Extended stream operations on io.BytesIO -import io + +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + a = io.BytesIO(b"foobar") a.seek(10) print(a.read(10)) diff --git a/tests/basics/io_bytesio_ext2.py b/tests/basics/io_bytesio_ext2.py index 414ac90a3b083..f60a6a9a6041e 100644 --- a/tests/basics/io_bytesio_ext2.py +++ b/tests/basics/io_bytesio_ext2.py @@ -1,4 +1,9 @@ -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + a = io.BytesIO(b"foobar") try: a.seek(-10) diff --git a/tests/basics/io_iobase.py b/tests/basics/io_iobase.py index d3824c177f3b3..c01ca6a5073d6 100644 --- a/tests/basics/io_iobase.py +++ b/tests/basics/io_iobase.py @@ -1,8 +1,9 @@ -import io try: + import io + io.IOBase -except AttributeError: - print('SKIP') +except (AttributeError, ImportError): + print("SKIP") raise SystemExit diff --git a/tests/basics/io_stringio1.py b/tests/basics/io_stringio1.py index 7d355930f5a29..889e3ce697377 100644 --- a/tests/basics/io_stringio1.py +++ b/tests/basics/io_stringio1.py @@ -1,4 +1,9 @@ -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + a = io.StringIO() print('io.StringIO' in repr(a)) print(a.getvalue()) diff --git a/tests/basics/io_stringio_base.py b/tests/basics/io_stringio_base.py index 0f65fb3fabc3b..c8890dab73ab7 100644 --- a/tests/basics/io_stringio_base.py +++ b/tests/basics/io_stringio_base.py @@ -1,7 +1,11 @@ # Checks that an instance type inheriting from a native base that uses # MP_TYPE_FLAG_ITER_IS_STREAM will still have a getiter. -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit a = io.StringIO() a.write("hello\nworld\nmicro\npython\n") diff --git a/tests/basics/io_stringio_with.py b/tests/basics/io_stringio_with.py index a3aa6ec84e066..0155ad5382dcd 100644 --- a/tests/basics/io_stringio_with.py +++ b/tests/basics/io_stringio_with.py @@ -1,4 +1,9 @@ -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + # test __enter__/__exit__ with io.StringIO() as b: b.write("foo") diff --git a/tests/basics/io_write_ext.py b/tests/basics/io_write_ext.py index 695abccef4421..5af1de7a6c3aa 100644 --- a/tests/basics/io_write_ext.py +++ b/tests/basics/io_write_ext.py @@ -1,10 +1,11 @@ # This tests extended (MicroPython-specific) form of write: # write(buf, len) and write(buf, offset, len) -import io try: + import io + io.BytesIO -except AttributeError: +except (AttributeError, ImportError): print('SKIP') raise SystemExit diff --git a/tests/feature_check/io_module.py b/tests/feature_check/io_module.py deleted file mode 100644 index 9094e605316ba..0000000000000 --- a/tests/feature_check/io_module.py +++ /dev/null @@ -1,6 +0,0 @@ -try: - import io - - print("io") -except ImportError: - print("no") diff --git a/tests/feature_check/io_module.py.exp b/tests/feature_check/io_module.py.exp deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/tests/run-tests.py b/tests/run-tests.py index e2bdf7c2c0286..958ddb1dc80e8 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -695,7 +695,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_async = False skip_const = False skip_revops = False - skip_io_module = False skip_fstring = False skip_endian = False skip_inlineasm = False @@ -752,11 +751,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if output == b"TypeError\n": skip_revops = True - # Check if io module exists, and skip such tests if it doesn't - output = run_feature_check(pyb, args, "io_module.py") - if output != b"io\n": - skip_io_module = True - # Check if fstring feature is enabled, and skip such tests if it doesn't output = run_feature_check(pyb, args, "fstring.py") if output != b"a=1\n": @@ -925,7 +919,6 @@ def run_one_test(test_file): is_slice = test_name.find("slice") != -1 or test_name in misc_slice_tests is_async = test_name.startswith(("async_", "asyncio_")) or test_name.endswith("_async") is_const = test_name.startswith("const") - is_io_module = test_name.startswith("io_") is_fstring = test_name.startswith("string_fstring") is_inlineasm = test_name.startswith("asm") @@ -940,7 +933,6 @@ def run_one_test(test_file): skip_it |= skip_async and is_async skip_it |= skip_const and is_const skip_it |= skip_revops and "reverse_op" in test_name - skip_it |= skip_io_module and is_io_module skip_it |= skip_fstring and is_fstring skip_it |= skip_inlineasm and is_inlineasm From f9b6d8e6088545f4d934ca4b4c2baa69ee62e89e Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 12:26:40 +1000 Subject: [PATCH 22/60] github/workflows: Run webassembly and zephyr workflows if tests/ change. Because these ports run tests as part of CI, and need to be run if any of the tests change, to check those changes. Signed-off-by: Damien George --- .github/workflows/ports_webassembly.yml | 1 + .github/workflows/ports_zephyr.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/ports_webassembly.yml b/.github/workflows/ports_webassembly.yml index 880f15ab34469..ceb35f83cd8f1 100644 --- a/.github/workflows/ports_webassembly.yml +++ b/.github/workflows/ports_webassembly.yml @@ -11,6 +11,7 @@ on: - 'shared/**' - 'lib/**' - 'ports/webassembly/**' + - 'tests/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/ports_zephyr.yml b/.github/workflows/ports_zephyr.yml index eb85af6a36154..ca3c36e004cca 100644 --- a/.github/workflows/ports_zephyr.yml +++ b/.github/workflows/ports_zephyr.yml @@ -11,6 +11,7 @@ on: - 'shared/**' - 'lib/**' - 'ports/zephyr/**' + - 'tests/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} From c16fe5b5ede3dde468bb65cc15e3e072101b0295 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 13:26:19 +1000 Subject: [PATCH 23/60] webassembly: Enable C-stack checking. This gets the recursive stress-tests working on this port. For relatively small Python functions the maximum recursive depth is about 150 nested calls. Signed-off-by: Damien George --- ports/webassembly/main.c | 5 +++++ ports/webassembly/mpconfigport.h | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/webassembly/main.c b/ports/webassembly/main.c index 770dfbe0ca5c7..e0e2d59ae6bd0 100644 --- a/ports/webassembly/main.c +++ b/ports/webassembly/main.c @@ -49,6 +49,9 @@ // the top-level call into C. static size_t external_call_depth = 0; +// Emscripten defaults to a 64k C-stack, so our limit should be less than that. +#define CSTACK_SIZE (32 * 1024) + #if MICROPY_GC_SPLIT_HEAP_AUTO static void gc_collect_top_level(void); #endif @@ -67,6 +70,8 @@ void external_call_depth_dec(void) { } void mp_js_init(int pystack_size, int heap_size) { + mp_cstack_init_with_sp_here(CSTACK_SIZE); + #if MICROPY_ENABLE_PYSTACK mp_obj_t *pystack = (mp_obj_t *)malloc(pystack_size * sizeof(mp_obj_t)); mp_pystack_init(pystack, pystack + pystack_size); diff --git a/ports/webassembly/mpconfigport.h b/ports/webassembly/mpconfigport.h index 544e76f80540d..0783aacbbcd24 100644 --- a/ports/webassembly/mpconfigport.h +++ b/ports/webassembly/mpconfigport.h @@ -44,7 +44,6 @@ #define MICROPY_READER_VFS (MICROPY_VFS) #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_PYSTACK (1) -#define MICROPY_STACK_CHECK (0) #define MICROPY_KBD_EXCEPTION (1) #define MICROPY_REPL_EVENT_DRIVEN (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) From 0cb2c69b3f25816416f7141ca32697a3135d8e53 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 13:51:58 +1000 Subject: [PATCH 24/60] tests/misc/rge_sm.py: Remove unused code from the test. This cleans up the test to remove all unused code, making it smaller, a bit faster to deploy to a target to run, and also use less RAM on the target (which may help it run on targets that are just slightly out of memory running it). Signed-off-by: Damien George --- tests/misc/rge_sm.py | 49 -------------------------------------------- 1 file changed, 49 deletions(-) diff --git a/tests/misc/rge_sm.py b/tests/misc/rge_sm.py index f5b0910dd3a3f..56dad5749776e 100644 --- a/tests/misc/rge_sm.py +++ b/tests/misc/rge_sm.py @@ -39,14 +39,6 @@ def solve(self, finishtime): if not self.iterate(): break - def solveNSteps(self, nSteps): - for i in range(nSteps): - if not self.iterate(): - break - - def series(self): - return zip(*self.Trajectory) - # 1-loop RGES for the main parameters of the SM # couplings are: g1, g2, g3 of U(1), SU(2), SU(3); yt (top Yukawa), lambda (Higgs quartic) @@ -79,45 +71,6 @@ def series(self): ) -def drange(start, stop, step): - r = start - while r < stop: - yield r - r += step - - -def phaseDiagram(system, trajStart, trajPlot, h=0.1, tend=1.0, range=1.0): - tstart = 0.0 - for i in drange(0, range, 0.1 * range): - for j in drange(0, range, 0.1 * range): - rk = RungeKutta(system, trajStart(i, j), tstart, h) - rk.solve(tend) - # draw the line - for tr in rk.Trajectory: - x, y = trajPlot(tr) - print(x, y) - print() - # draw the arrow - continue - l = (len(rk.Trajectory) - 1) / 3 - if l > 0 and 2 * l < len(rk.Trajectory): - p1 = rk.Trajectory[l] - p2 = rk.Trajectory[2 * l] - x1, y1 = trajPlot(p1) - x2, y2 = trajPlot(p2) - dx = -0.5 * (y2 - y1) # orthogonal to line - dy = 0.5 * (x2 - x1) # orthogonal to line - # l = math.sqrt(dx*dx + dy*dy) - # if abs(l) > 1e-3: - # l = 0.1 / l - # dx *= l - # dy *= l - print(x1 + dx, y1 + dy) - print(x2, y2) - print(x1 - dx, y1 - dy) - print() - - def singleTraj(system, trajStart, h=0.02, tend=1.0): is_REPR_C = float("1.0000001") == float("1.0") tstart = 0.0 @@ -141,7 +94,5 @@ def singleTraj(system, trajStart, h=0.02, tend=1.0): print(tr_str) -# phaseDiagram(sysSM, (lambda i, j: [0.354, 0.654, 1.278, 0.8 + 0.2 * i, 0.1 + 0.1 * j]), (lambda a: (a[4], a[5])), h=0.1, tend=math.log(10**17)) - # initial conditions at M_Z singleTraj(sysSM, [0.354, 0.654, 1.278, 0.983, 0.131], h=0.5, tend=math.log(10**17)) # true values From 0f5f6484a2e6f330d03ee4786011ccb8062ac325 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 10 Aug 2025 12:05:32 +1000 Subject: [PATCH 25/60] tests/run-tests.py: Add support for .native.exp expected output files. There are currently a few tests that are excluded when using the native emitter because they test printing of exception tracebacks, which includes line numbers. And the native emitter doesn't store line numbers, so gets these tests wrong. But we'd still like to run these tests using the native emitter, because they test useful things even if the line number info is not in the traceback (eg that threads which crash print out their exception). This commit adds support for native-specific .exp files, which are of the form `.py.native.exp`. If such an .exp file exists then it take precedence over any normal `.py.exp` file. (Actually, the implementation here is general enough that it also supports `.py.bytecode.exp` as well, if bytecode ever needs a specific exp file.) Signed-off-by: Damien George --- tests/run-tests.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index 958ddb1dc80e8..d0a9121b4a4ca 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -994,7 +994,11 @@ def run_one_test(test_file): # Expected output is result of running unittest. output_expected = None else: - test_file_expected = test_file + ".exp" + # Prefer emitter-specific expected output. + test_file_expected = test_file + "." + args.emit + ".exp" + if not os.path.isfile(test_file_expected): + # Fall back to generic expected output. + test_file_expected = test_file + ".exp" if os.path.isfile(test_file_expected): # Expected output given by a file, so read that in. with open(test_file_expected, "rb") as f: @@ -1202,8 +1206,8 @@ def main(): {test_directory_description} When running tests, run-tests.py compares the MicroPython output of the test with the output -produced by running the test through CPython unless a .exp file is found, in which -case it is used as comparison. +produced by running the test through CPython unless a .exp file is found (or a +.native.exp file when using the native emitter), in which case it is used as comparison. If a test fails, run-tests.py produces a pair of .out and .exp files in the result directory with the MicroPython output and the expectations, respectively. From 95d1794afdbef4d0b57af321c0a5d69320dbe9fd Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 10 Aug 2025 12:04:58 +1000 Subject: [PATCH 26/60] tests/misc/print_exception.py: Use "raise e" instead of no-arg "raise". This allows the test to run with the native emitter. The test semantics remain the same. Signed-off-by: Damien George --- tests/misc/print_exception.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/misc/print_exception.py b/tests/misc/print_exception.py index 92754733b58b1..d41478360faf3 100644 --- a/tests/misc/print_exception.py +++ b/tests/misc/print_exception.py @@ -71,7 +71,7 @@ def g(): except Exception as e: print("reraise") print_exc(e) - raise + raise e except Exception as e: print("caught") print_exc(e) From 3c72c3a1e60cecc6ab05d5a81a1b50f42999f10f Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 14:07:09 +1000 Subject: [PATCH 27/60] tests/micropython/opt_level_lineno.py: Force test func to use bytecode. So that the test can run the same on all targets when used with the native emitter. Signed-off-by: Damien George --- tests/micropython/opt_level_lineno.py | 13 ++++++++++++- tests/micropython/opt_level_lineno.py.exp | 2 +- tests/run-tests.py | 1 - 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/micropython/opt_level_lineno.py b/tests/micropython/opt_level_lineno.py index d8253e54b41f0..dda9092d868cf 100644 --- a/tests/micropython/opt_level_lineno.py +++ b/tests/micropython/opt_level_lineno.py @@ -3,4 +3,15 @@ # check that level 3 doesn't store line numbers # the expected output is that any line is printed as "line 1" micropython.opt_level(3) -exec("try:\n xyz\nexcept NameError as er:\n import sys\n sys.print_exception(er)") + +# force bytecode emitter, because native emitter doesn't store line numbers +exec(""" +@micropython.bytecode +def f(): + try: + xyz + except NameError as er: + import sys + sys.print_exception(er) +f() +""") diff --git a/tests/micropython/opt_level_lineno.py.exp b/tests/micropython/opt_level_lineno.py.exp index 469b90ba7938a..b50f0628c81fb 100644 --- a/tests/micropython/opt_level_lineno.py.exp +++ b/tests/micropython/opt_level_lineno.py.exp @@ -1,3 +1,3 @@ Traceback (most recent call last): - File "", line 1, in + File "", line 1, in f NameError: name 'xyz' isn't defined diff --git a/tests/run-tests.py b/tests/run-tests.py index d0a9121b4a4ca..f8174b2d3b0c2 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -131,7 +131,6 @@ def open(self, path, mode): "misc/print_exception.py", "micropython/emg_exc.py", "micropython/heapalloc_traceback.py", - "micropython/opt_level_lineno.py", "thread/thread_exc2.py", # These require stack-allocated slice optimisation. "micropython/heapalloc_slice.py", From a279c64046445c9dbde0a5d3d53d467cb7e43550 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 10 Aug 2025 12:06:16 +1000 Subject: [PATCH 28/60] tests: Add .native.exp output files for tests that differ with native. Signed-off-by: Damien George --- tests/basics/sys_tracebacklimit.py.native.exp | 22 +++++++++++++++++++ tests/micropython/emg_exc.py.native.exp | 2 ++ .../heapalloc_traceback.py.native.exp | 3 +++ tests/misc/print_exception.py.native.exp | 18 +++++++++++++++ tests/run-tests.py | 6 ----- tests/thread/thread_exc2.py.native.exp | 3 +++ 6 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 tests/basics/sys_tracebacklimit.py.native.exp create mode 100644 tests/micropython/emg_exc.py.native.exp create mode 100644 tests/micropython/heapalloc_traceback.py.native.exp create mode 100644 tests/misc/print_exception.py.native.exp create mode 100644 tests/thread/thread_exc2.py.native.exp diff --git a/tests/basics/sys_tracebacklimit.py.native.exp b/tests/basics/sys_tracebacklimit.py.native.exp new file mode 100644 index 0000000000000..f9d30c058564b --- /dev/null +++ b/tests/basics/sys_tracebacklimit.py.native.exp @@ -0,0 +1,22 @@ +ValueError: value + +limit 4 +ValueError: value + +limit 3 +ValueError: value + +limit 2 +ValueError: value + +limit 1 +ValueError: value + +limit 0 +ValueError: value + +limit -1 +ValueError: value + +True +False diff --git a/tests/micropython/emg_exc.py.native.exp b/tests/micropython/emg_exc.py.native.exp new file mode 100644 index 0000000000000..9677c526a9cc8 --- /dev/null +++ b/tests/micropython/emg_exc.py.native.exp @@ -0,0 +1,2 @@ +ValueError: 1 + diff --git a/tests/micropython/heapalloc_traceback.py.native.exp b/tests/micropython/heapalloc_traceback.py.native.exp new file mode 100644 index 0000000000000..d6ac26aa829e1 --- /dev/null +++ b/tests/micropython/heapalloc_traceback.py.native.exp @@ -0,0 +1,3 @@ +StopIteration +StopIteration: + diff --git a/tests/misc/print_exception.py.native.exp b/tests/misc/print_exception.py.native.exp new file mode 100644 index 0000000000000..59e856ae3c44a --- /dev/null +++ b/tests/misc/print_exception.py.native.exp @@ -0,0 +1,18 @@ +caught +Exception: msg + +caught +Exception: fail + +finally +caught +Exception: fail + +reraise +Exception: fail + +caught +Exception: fail + +AttributeError: 'function' object has no attribute 'X' + diff --git a/tests/run-tests.py b/tests/run-tests.py index f8174b2d3b0c2..aeea0bad5e779 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -126,12 +126,6 @@ def open(self, path, mode): "basics/unboundlocal.py", # These require "raise from". "basics/exception_chain.py", - # These require proper traceback info. - "basics/sys_tracebacklimit.py", - "misc/print_exception.py", - "micropython/emg_exc.py", - "micropython/heapalloc_traceback.py", - "thread/thread_exc2.py", # These require stack-allocated slice optimisation. "micropython/heapalloc_slice.py", # These require running the scheduler. diff --git a/tests/thread/thread_exc2.py.native.exp b/tests/thread/thread_exc2.py.native.exp new file mode 100644 index 0000000000000..9b2e715ef8dfc --- /dev/null +++ b/tests/thread/thread_exc2.py.native.exp @@ -0,0 +1,3 @@ +Unhandled exception in thread started by +ValueError: +done From 2bba507148e8a751123257fa2b075c70d67e1160 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Aug 2025 10:48:16 +1000 Subject: [PATCH 29/60] tests: Require SSL certificate file to be available for test to run. Previously, any test needing an SSL certificate file would automatically skip if the file could not be found. But that makes it too easy to accidentally skip tests. Instead, change it so that the test fails if the certificate file doesn't exist. That matches, for example, the fact that the test fails if networking (LAN, WiFi) is not active. Signed-off-by: Damien George --- tests/multi_net/asyncio_tls_server_client.py | 7 ------- .../asyncio_tls_server_client_cert_required_error.py | 7 ------- tests/multi_net/asyncio_tls_server_client_readline.py | 7 ------- tests/multi_net/asyncio_tls_server_client_verify_error.py | 7 ------- tests/multi_net/ssl_cert_ec.py | 7 ------- tests/multi_net/ssl_cert_rsa.py | 7 ------- tests/multi_net/sslcontext_check_hostname_error.py | 7 ------- tests/multi_net/sslcontext_getpeercert.py | 7 ------- tests/multi_net/sslcontext_server_client_files.py | 7 ------- tests/multi_net/sslcontext_verify_error.py | 7 ------- tests/multi_net/sslcontext_verify_time_error.py | 7 ------- tests/net_inet/asyncio_tls_open_connection_readline.py | 6 ------ tests/net_inet/test_sslcontext_client.py | 5 ----- 13 files changed, 88 deletions(-) diff --git a/tests/multi_net/asyncio_tls_server_client.py b/tests/multi_net/asyncio_tls_server_client.py index 98f15c6625f0e..016f57970c092 100644 --- a/tests/multi_net/asyncio_tls_server_client.py +++ b/tests/multi_net/asyncio_tls_server_client.py @@ -14,13 +14,6 @@ cert = cafile = "ec_cert.der" key = "ec_key.der" -try: - os.stat(cafile) - os.stat(key) -except OSError: - print("SKIP") - raise SystemExit - async def handle_connection(reader, writer): data = await reader.read(100) diff --git a/tests/multi_net/asyncio_tls_server_client_cert_required_error.py b/tests/multi_net/asyncio_tls_server_client_cert_required_error.py index 178ad39274032..eac9a98beae6c 100644 --- a/tests/multi_net/asyncio_tls_server_client_cert_required_error.py +++ b/tests/multi_net/asyncio_tls_server_client_cert_required_error.py @@ -14,13 +14,6 @@ cert = cafile = "ec_cert.der" key = "ec_key.der" -try: - os.stat(cafile) - os.stat(key) -except OSError: - print("SKIP") - raise SystemExit - async def handle_connection(reader, writer): print("handle connection") diff --git a/tests/multi_net/asyncio_tls_server_client_readline.py b/tests/multi_net/asyncio_tls_server_client_readline.py index da5f1afee2ad6..6093628ce5b7b 100644 --- a/tests/multi_net/asyncio_tls_server_client_readline.py +++ b/tests/multi_net/asyncio_tls_server_client_readline.py @@ -14,13 +14,6 @@ cert = cafile = "ec_cert.der" key = "ec_key.der" -try: - os.stat(cafile) - os.stat(key) -except OSError: - print("SKIP") - raise SystemExit - async def handle_connection(reader, writer): data = await reader.readline() diff --git a/tests/multi_net/asyncio_tls_server_client_verify_error.py b/tests/multi_net/asyncio_tls_server_client_verify_error.py index 362f0fc8ecf72..6dbff0482c8ae 100644 --- a/tests/multi_net/asyncio_tls_server_client_verify_error.py +++ b/tests/multi_net/asyncio_tls_server_client_verify_error.py @@ -14,13 +14,6 @@ cert = cafile = "ec_cert.der" key = "ec_key.der" -try: - os.stat(cafile) - os.stat(key) -except OSError: - print("SKIP") - raise SystemExit - async def handle_connection(reader, writer): print("handle connection") diff --git a/tests/multi_net/ssl_cert_ec.py b/tests/multi_net/ssl_cert_ec.py index 2c5734e052661..8ecbd4d34f6e8 100644 --- a/tests/multi_net/ssl_cert_ec.py +++ b/tests/multi_net/ssl_cert_ec.py @@ -13,13 +13,6 @@ certfile = "ec_cert.der" keyfile = "ec_key.der" -try: - os.stat(certfile) - os.stat(keyfile) -except OSError: - print("SKIP") - raise SystemExit - with open(certfile, "rb") as cf: cert = cadata = cf.read() diff --git a/tests/multi_net/ssl_cert_rsa.py b/tests/multi_net/ssl_cert_rsa.py index d148c8ebcfea8..b99493b0ae5d8 100644 --- a/tests/multi_net/ssl_cert_rsa.py +++ b/tests/multi_net/ssl_cert_rsa.py @@ -13,13 +13,6 @@ certfile = "rsa_cert.der" keyfile = "rsa_key.der" -try: - os.stat(certfile) - os.stat(keyfile) -except OSError: - print("SKIP") - raise SystemExit - with open(certfile, "rb") as cf: cert = cadata = cf.read() diff --git a/tests/multi_net/sslcontext_check_hostname_error.py b/tests/multi_net/sslcontext_check_hostname_error.py index d85363f00bd63..6bd911ddfd7cc 100644 --- a/tests/multi_net/sslcontext_check_hostname_error.py +++ b/tests/multi_net/sslcontext_check_hostname_error.py @@ -14,13 +14,6 @@ cert = cafile = "ec_cert.der" key = "ec_key.der" -try: - os.stat(cafile) - os.stat(key) -except OSError: - print("SKIP") - raise SystemExit - # Server def instance0(): diff --git a/tests/multi_net/sslcontext_getpeercert.py b/tests/multi_net/sslcontext_getpeercert.py index e9d96be248c70..e2d2a4251163b 100644 --- a/tests/multi_net/sslcontext_getpeercert.py +++ b/tests/multi_net/sslcontext_getpeercert.py @@ -15,13 +15,6 @@ cert = cafile = "ec_cert.der" key = "ec_key.der" -try: - os.stat(cafile) - os.stat(key) -except OSError: - print("SKIP") - raise SystemExit - # Server def instance0(): diff --git a/tests/multi_net/sslcontext_server_client_files.py b/tests/multi_net/sslcontext_server_client_files.py index 64a4215c75b44..48ff6376fad9b 100644 --- a/tests/multi_net/sslcontext_server_client_files.py +++ b/tests/multi_net/sslcontext_server_client_files.py @@ -14,13 +14,6 @@ cert = cafile = "ec_cert.der" key = "ec_key.der" -try: - os.stat(cafile) - os.stat(key) -except OSError: - print("SKIP") - raise SystemExit - # Server def instance0(): diff --git a/tests/multi_net/sslcontext_verify_error.py b/tests/multi_net/sslcontext_verify_error.py index 5dc461e7708a8..07dcc690b7ccc 100644 --- a/tests/multi_net/sslcontext_verify_error.py +++ b/tests/multi_net/sslcontext_verify_error.py @@ -14,13 +14,6 @@ cert = cafile = "ec_cert.der" key = "ec_key.der" -try: - os.stat(cafile) - os.stat(key) -except OSError: - print("SKIP") - raise SystemExit - # Server def instance0(): diff --git a/tests/multi_net/sslcontext_verify_time_error.py b/tests/multi_net/sslcontext_verify_time_error.py index fbefdecf9f430..7b986322d15bd 100644 --- a/tests/multi_net/sslcontext_verify_time_error.py +++ b/tests/multi_net/sslcontext_verify_time_error.py @@ -14,13 +14,6 @@ cert = cafile = "expired_cert.der" key = "ec_key.der" -try: - os.stat(cafile) - os.stat(key) -except OSError: - print("SKIP") - raise SystemExit - # Server def instance0(): diff --git a/tests/net_inet/asyncio_tls_open_connection_readline.py b/tests/net_inet/asyncio_tls_open_connection_readline.py index 70145d91a794b..a0df88be4af1b 100644 --- a/tests/net_inet/asyncio_tls_open_connection_readline.py +++ b/tests/net_inet/asyncio_tls_open_connection_readline.py @@ -20,12 +20,6 @@ ca_cert_chain = "isrg.der" -try: - os.stat(ca_cert_chain) -except OSError: - print("SKIP") - raise SystemExit - with open(ca_cert_chain, "rb") as ca: cadata = ca.read() diff --git a/tests/net_inet/test_sslcontext_client.py b/tests/net_inet/test_sslcontext_client.py index 0c83abb733378..77f68da496a77 100644 --- a/tests/net_inet/test_sslcontext_client.py +++ b/tests/net_inet/test_sslcontext_client.py @@ -13,11 +13,6 @@ # $ openssl x509 -in mpycert.pem -out mpycert.der -outform DER ca_cert_chain = "mpycert.der" -try: - os.stat(ca_cert_chain) -except OSError: - print("SKIP") - raise SystemExit def main(use_stream=True): From e15219800e4874a6c2653cfbdd0553c37610aeff Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 5 Aug 2025 17:19:02 -0500 Subject: [PATCH 30/60] extmod/modframebuf: Fix crash in scroll() for large inputs. If mp_int_t is wider than int, then the tests such as `xend < 0` can fail even when the amount of scrolling requested is out of range. This resulted in a segmentation fault when attempting an out-of-bounds access to the framebuffer. Signed-off-by: Jeff Epler --- extmod/modframebuf.c | 29 +++++++++++++++-------------- tests/extmod/framebuf_scroll.py | 5 +++++ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 5c4b9abf0c0b8..16606109ccdc4 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -787,39 +787,40 @@ static mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ys mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t xstep = mp_obj_get_int(xstep_in); mp_int_t ystep = mp_obj_get_int(ystep_in); - int sx, y, xend, yend, dx, dy; + unsigned int sx, y, xend, yend; + int dx, dy; if (xstep < 0) { - sx = 0; - xend = self->width + xstep; - if (xend <= 0) { + if (-xstep >= self->width) { return mp_const_none; } + sx = 0; + xend = self->width + (int)xstep; dx = 1; } else { - sx = self->width - 1; - xend = xstep - 1; - if (xend >= sx) { + if (xstep >= self->width) { return mp_const_none; } + sx = self->width - 1; + xend = (int)xstep - 1; dx = -1; } if (ystep < 0) { - y = 0; - yend = self->height + ystep; - if (yend <= 0) { + if (-ystep >= self->height) { return mp_const_none; } + y = 0; + yend = self->height + (int)ystep; dy = 1; } else { - y = self->height - 1; - yend = ystep - 1; - if (yend >= y) { + if (ystep >= self->height) { return mp_const_none; } + y = self->height - 1; + yend = (int)ystep - 1; dy = -1; } for (; y != yend; y += dy) { - for (int x = sx; x != xend; x += dx) { + for (unsigned x = sx; x != xend; x += dx) { setpixel(self, x, y, getpixel(self, x - xstep, y - ystep)); } } diff --git a/tests/extmod/framebuf_scroll.py b/tests/extmod/framebuf_scroll.py index db9b6ea1e9649..d7c07b1c65772 100644 --- a/tests/extmod/framebuf_scroll.py +++ b/tests/extmod/framebuf_scroll.py @@ -42,4 +42,9 @@ def prepare_buffer(): fbuf.scroll(15, 7) fbuf.scroll(10, -1) fbuf.scroll(1, -10) +try: + fbuf.scroll(1000000000000, -1) +except OverflowError: + # When mp_int_t is 32 bits, this throws OverflowError. + pass printbuf() From 803da9645f735edd3d1a80a3f920039e0f0298f2 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 11 Aug 2025 11:48:34 -0500 Subject: [PATCH 31/60] extmod/modframebuf: Save code size in setpixel. There's a slight code size increase paid for by using setpixel_checked for the last pixel of a line, instead of repeating the checks inline. Signed-off-by: Jeff Epler --- extmod/modframebuf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 16606109ccdc4..593125aa16f91 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -483,9 +483,7 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t e += 2 * dy; } - if (0 <= x2 && x2 < fb->width && 0 <= y2 && y2 < fb->height) { - setpixel(fb, x2, y2, col); - } + setpixel_checked(fb, x2, y2, col, 1); } static mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args_in) { From 0615d13963ba33650a5b2070368584c7aec9c9dd Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 6 Aug 2025 10:15:57 -0500 Subject: [PATCH 32/60] py/objringio: Detect incorrect constructor calls. ringbuffer.size must be at least 2, and is a 16-bit quantity. This fixes several cases including the one the fuzzer discovered, which would lead to a fatal signal when accessing the object. Fixes issue #17847. Signed-off-by: Jeff Epler --- py/objringio.c | 19 ++++++++----------- tests/micropython/ringio.py | 18 ++++++++++++++++++ tests/micropython/ringio.py.exp | 3 +++ tests/micropython/ringio_big.py | 29 +++++++++++++++++++++++++++++ tests/micropython/ringio_big.py.exp | 2 ++ 5 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 tests/micropython/ringio_big.py create mode 100644 tests/micropython/ringio_big.py.exp diff --git a/py/objringio.c b/py/objringio.c index ba1ec25307ea4..0025b26be3b07 100644 --- a/py/objringio.c +++ b/py/objringio.c @@ -39,22 +39,19 @@ typedef struct _micropython_ringio_obj_t { static mp_obj_t micropython_ringio_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); - mp_int_t buff_size = -1; mp_buffer_info_t bufinfo = {NULL, 0, 0}; if (!mp_get_buffer(args[0], &bufinfo, MP_BUFFER_RW)) { - buff_size = mp_obj_get_int(args[0]); + bufinfo.len = mp_obj_get_int(args[0]) + 1; + bufinfo.buf = m_new(uint8_t, bufinfo.len); } - micropython_ringio_obj_t *self = mp_obj_malloc(micropython_ringio_obj_t, type); - if (bufinfo.buf != NULL) { - // buffer passed in, use it directly for ringbuffer. - self->ringbuffer.buf = bufinfo.buf; - self->ringbuffer.size = bufinfo.len; - self->ringbuffer.iget = self->ringbuffer.iput = 0; - } else { - // Allocate new buffer, add one extra to buff_size as ringbuf consumes one byte for tracking. - ringbuf_alloc(&(self->ringbuffer), buff_size + 1); + if (bufinfo.len < 2 || bufinfo.len > UINT16_MAX) { + mp_raise_ValueError(NULL); } + micropython_ringio_obj_t *self = mp_obj_malloc(micropython_ringio_obj_t, type); + self->ringbuffer.buf = bufinfo.buf; + self->ringbuffer.size = bufinfo.len; + self->ringbuffer.iget = self->ringbuffer.iput = 0; return MP_OBJ_FROM_PTR(self); } diff --git a/tests/micropython/ringio.py b/tests/micropython/ringio.py index a0102ef63dafa..4109288798448 100644 --- a/tests/micropython/ringio.py +++ b/tests/micropython/ringio.py @@ -46,3 +46,21 @@ micropython.RingIO(None) except TypeError as ex: print(type(ex)) + +try: + # Buffer may not be empty + micropython.RingIO(bytearray(0)) +except ValueError as ex: + print(type(ex)) + +try: + # Buffer may not be too small + micropython.RingIO(bytearray(1)) +except ValueError as ex: + print(type(ex)) + +try: + # Size may not be too small + micropython.RingIO(0) +except ValueError as ex: + print(type(ex)) diff --git a/tests/micropython/ringio.py.exp b/tests/micropython/ringio.py.exp index 65bae06472f8d..c391529a41e54 100644 --- a/tests/micropython/ringio.py.exp +++ b/tests/micropython/ringio.py.exp @@ -14,3 +14,6 @@ b'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01' 0 b'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01' + + + diff --git a/tests/micropython/ringio_big.py b/tests/micropython/ringio_big.py new file mode 100644 index 0000000000000..d55c4c00b7c0d --- /dev/null +++ b/tests/micropython/ringio_big.py @@ -0,0 +1,29 @@ +# Check that micropython.RingIO works correctly. + +import micropython + +try: + micropython.RingIO +except AttributeError: + print("SKIP") + raise SystemExit + +try: + # The maximum possible size + micropython.RingIO(bytearray(65535)) + micropython.RingIO(65534) + + try: + # Buffer may not be too big + micropython.RingIO(bytearray(65536)) + except ValueError as ex: + print(type(ex)) + + try: + # Size may not be too big + micropython.RingIO(65535) + except ValueError as ex: + print(type(ex)) +except MemoryError: + print("SKIP") + raise SystemExit diff --git a/tests/micropython/ringio_big.py.exp b/tests/micropython/ringio_big.py.exp new file mode 100644 index 0000000000000..72af34b383872 --- /dev/null +++ b/tests/micropython/ringio_big.py.exp @@ -0,0 +1,2 @@ + + From 141f7d0c35516403c39ce6266be7c6fb4551ef6f Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 12 Aug 2025 11:51:53 +0100 Subject: [PATCH 33/60] py/mkrules.cmake: Clean genhdr and frozen_mpy dirs. CMake builds relied upon the parent Makefile removing the entire build directory to successfully clean build artifacts. py/mkrules.cmake: Add ADDITIONAL_CLEAN_FILES properties to ensure a "make clean" from within the build directory removes the genhdr and frozen_mpy directories. Signed-off-by: Phil Howard --- py/mkrules.cmake | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/py/mkrules.cmake b/py/mkrules.cmake index 27d8b24f67ac8..e3d769cc59b5c 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -100,6 +100,12 @@ if(MICROPY_ROM_TEXT_COMPRESSION) ) endif() +# Ensure genhdr directory is removed on clean + +set_property(TARGET ${MICROPY_TARGET} APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${MICROPY_GENHDR_DIR}" +) + # Command to force the build of another command # Generate mpversion.h @@ -244,6 +250,10 @@ add_custom_command( if(MICROPY_FROZEN_MANIFEST) set(MICROPY_FROZEN_CONTENT "${CMAKE_BINARY_DIR}/frozen_content.c") + set_property(TARGET ${MICROPY_TARGET} APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${CMAKE_BINARY_DIR}/frozen_mpy" + ) + target_sources(${MICROPY_TARGET} PRIVATE ${MICROPY_FROZEN_CONTENT} ) From 3efbd726eb89a1514c01a51857100b2718f12a50 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 14:44:48 +1000 Subject: [PATCH 34/60] py/parse: Remove explicit checks for invalid folding operations. They are instead checked by `binary_op_maybe()`, which catches exceptions for invalid int/float operations. This is a follow-up to 69ead7d98ef30df3b6bd4485633490e80fca1718 Signed-off-by: Damien George --- py/parse.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/py/parse.c b/py/parse.c index 6260987b28a35..1a50b13b5c790 100644 --- a/py/parse.c +++ b/py/parse.c @@ -771,12 +771,6 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { if (!mp_parse_node_get_number_maybe(pn, &arg1)) { return false; } - #if !MICROPY_COMP_CONST_FLOAT - if (op == MP_BINARY_OP_POWER && mp_obj_int_sign(arg1) < 0) { - // ** can't have negative rhs - return false; - } - #endif if (!binary_op_maybe(op, arg0, arg1, &arg0)) { return false; } @@ -796,16 +790,6 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { return false; } mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, i)); - if (tok == MP_TOKEN_OP_AT) { - // Can't fold @ - return false; - } - #if !MICROPY_COMP_CONST_FLOAT - if (tok == MP_TOKEN_OP_SLASH) { - // Can't fold / - return false; - } - #endif mp_binary_op_t op = MP_BINARY_OP_LSHIFT + (tok - MP_TOKEN_OP_DBL_LESS); if (!binary_op_maybe(op, arg0, arg1, &arg0)) { return false; From 40cc4e4f74b1f39076100136bc2cb8db5ff24ea5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 16:35:16 +1000 Subject: [PATCH 35/60] py/objtype: Make mp_obj_new_type a static function. It's only used once, in the same file it's defined, and making it static reduces code size. Along with this, the associated example code comment in `ports/unix/main.c` has been removed. Signed-off-by: Damien George --- ports/unix/main.c | 13 ------------- py/obj.h | 1 - py/objtype.c | 3 ++- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/ports/unix/main.c b/ports/unix/main.c index 530e20a3863b4..5f6b99f989a3a 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -616,19 +616,6 @@ MP_NOINLINE int main_(int argc, char **argv) { } #endif - // Here is some example code to create a class and instance of that class. - // First is the Python, then the C code. - // - // class TestClass: - // pass - // test_obj = TestClass() - // test_obj.attr = 42 - // - // mp_obj_t test_class_type, test_class_instance; - // test_class_type = mp_obj_new_type(qstr_from_str("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0)); - // mp_store_name(qstr_from_str("test_obj"), test_class_instance = mp_call_function_0(test_class_type)); - // mp_store_attr(test_class_instance, qstr_from_str("attr"), mp_obj_new_int(42)); - /* printf("bytes:\n"); printf(" total %d\n", m_get_total_bytes_allocated()); diff --git a/py/obj.h b/py/obj.h index 4ac0cc0c611e5..69d6e626db3c1 100644 --- a/py/obj.h +++ b/py/obj.h @@ -987,7 +987,6 @@ void *mp_obj_malloc_with_finaliser_helper(size_t num_bytes, const mp_obj_type_t bool mp_obj_is_dict_or_ordereddict(mp_obj_t o); #define mp_obj_is_fun(o) (mp_obj_is_obj(o) && (((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type->name == MP_QSTR_function)) -mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict); static inline mp_obj_t mp_obj_new_bool(mp_int_t x) { return x ? mp_const_true : mp_const_false; } diff --git a/py/objtype.c b/py/objtype.c index 818ceeb0589fb..25c2dee994d74 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -44,6 +44,7 @@ #define ENABLE_SPECIAL_ACCESSORS \ (MICROPY_PY_DESCRIPTORS || MICROPY_PY_DELATTR_SETATTR || MICROPY_PY_BUILTINS_PROPERTY) +static mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict); static mp_obj_t mp_obj_is_subclass(mp_obj_t object, mp_obj_t classinfo); static mp_obj_t static_class_method_make_new(const mp_obj_type_t *self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); @@ -1164,7 +1165,7 @@ MP_DEFINE_CONST_OBJ_TYPE( attr, type_attr ); -mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) { +static mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) { // Verify input objects have expected type if (!mp_obj_is_type(bases_tuple, &mp_type_tuple)) { mp_raise_TypeError(NULL); From b3cd1a355ebf50e06ec9f0c1594e47abd7ee339c Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Aug 2025 11:47:52 +1000 Subject: [PATCH 36/60] extmod/modtime: Move tuple creation to common localtime implementation. This factors code out of the ports and into the common `time.localtime` implementation in `extmod/modtime.c`. That helps to reduce code duplication, prevent errors in implementation, and reduce code size on some ports (mimxrt and stm32 at least). Signed-off-by: Damien George --- extmod/modtime.c | 26 +++++++++++++------------- ports/cc3200/mods/modtime.c | 19 +++---------------- ports/esp32/modtime.c | 18 +++--------------- ports/esp8266/modtime.c | 18 +++--------------- ports/mimxrt/modtime.c | 23 ++++++++++------------- ports/renesas-ra/modtime.c | 23 ++++++++++------------- ports/rp2/modtime.c | 18 +++--------------- ports/samd/modtime.c | 22 +++++----------------- ports/stm32/modtime.c | 23 ++++++++++------------- ports/webassembly/modtime.c | 18 +++--------------- 10 files changed, 63 insertions(+), 145 deletions(-) diff --git a/extmod/modtime.c b/extmod/modtime.c index 999b81230bcb9..ee898828a4ab1 100644 --- a/extmod/modtime.c +++ b/extmod/modtime.c @@ -53,26 +53,26 @@ // - weekday is 0-6 for Mon-Sun // - yearday is 1-366 static mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { + timeutils_struct_time_t tm; if (n_args == 0 || args[0] == mp_const_none) { // Get current date and time. - return mp_time_localtime_get(); + mp_time_localtime_get(&tm); } else { // Convert given seconds to tuple. mp_timestamp_t seconds = timeutils_obj_get_timestamp(args[0]); - timeutils_struct_time_t tm; timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); - mp_obj_t tuple[8] = { - tuple[0] = mp_obj_new_int(tm.tm_year), - tuple[1] = mp_obj_new_int(tm.tm_mon), - tuple[2] = mp_obj_new_int(tm.tm_mday), - tuple[3] = mp_obj_new_int(tm.tm_hour), - tuple[4] = mp_obj_new_int(tm.tm_min), - tuple[5] = mp_obj_new_int(tm.tm_sec), - tuple[6] = mp_obj_new_int(tm.tm_wday), - tuple[7] = mp_obj_new_int(tm.tm_yday), - }; - return mp_obj_new_tuple(8, tuple); } + mp_obj_t tuple[8] = { + mp_obj_new_int(tm.tm_year), + mp_obj_new_int(tm.tm_mon), + mp_obj_new_int(tm.tm_mday), + mp_obj_new_int(tm.tm_hour), + mp_obj_new_int(tm.tm_min), + mp_obj_new_int(tm.tm_sec), + mp_obj_new_int(tm.tm_wday), + mp_obj_new_int(tm.tm_yday), + }; + return mp_obj_new_tuple(8, tuple); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_time_localtime_obj, 0, 1, time_localtime); diff --git a/ports/cc3200/mods/modtime.c b/ports/cc3200/mods/modtime.c index 254678fb2ddc5..d111667001115 100644 --- a/ports/cc3200/mods/modtime.c +++ b/ports/cc3200/mods/modtime.c @@ -29,23 +29,10 @@ #include "shared/timeutils/timeutils.h" #include "pybrtc.h" -// Return the localtime as an 8-tuple. -static mp_obj_t mp_time_localtime_get(void) { - timeutils_struct_time_t tm; - +// Get the localtime. +static void mp_time_localtime_get(timeutils_struct_time_t *tm) { // get the seconds from the RTC - timeutils_seconds_since_2000_to_struct_time(pyb_rtc_get_seconds(), &tm); - mp_obj_t tuple[8] = { - mp_obj_new_int(tm.tm_year), - mp_obj_new_int(tm.tm_mon), - mp_obj_new_int(tm.tm_mday), - mp_obj_new_int(tm.tm_hour), - mp_obj_new_int(tm.tm_min), - mp_obj_new_int(tm.tm_sec), - mp_obj_new_int(tm.tm_wday), - mp_obj_new_int(tm.tm_yday) - }; - return mp_obj_new_tuple(8, tuple); + timeutils_seconds_since_2000_to_struct_time(pyb_rtc_get_seconds(), tm); } // Returns the number of seconds, as an integer, since the Epoch. diff --git a/ports/esp32/modtime.c b/ports/esp32/modtime.c index 991f2cf578771..64f9359db69af 100644 --- a/ports/esp32/modtime.c +++ b/ports/esp32/modtime.c @@ -31,23 +31,11 @@ #include "py/obj.h" #include "shared/timeutils/timeutils.h" -// Return the localtime as an 8-tuple. -static mp_obj_t mp_time_localtime_get(void) { +// Get the localtime. +static void mp_time_localtime_get(timeutils_struct_time_t *tm) { struct timeval tv; gettimeofday(&tv, NULL); - timeutils_struct_time_t tm; - timeutils_seconds_since_epoch_to_struct_time(tv.tv_sec, &tm); - mp_obj_t tuple[8] = { - tuple[0] = mp_obj_new_int(tm.tm_year), - tuple[1] = mp_obj_new_int(tm.tm_mon), - tuple[2] = mp_obj_new_int(tm.tm_mday), - tuple[3] = mp_obj_new_int(tm.tm_hour), - tuple[4] = mp_obj_new_int(tm.tm_min), - tuple[5] = mp_obj_new_int(tm.tm_sec), - tuple[6] = mp_obj_new_int(tm.tm_wday), - tuple[7] = mp_obj_new_int(tm.tm_yday), - }; - return mp_obj_new_tuple(8, tuple); + timeutils_seconds_since_epoch_to_struct_time(tv.tv_sec, tm); } // Return the number of seconds since the Epoch. diff --git a/ports/esp8266/modtime.c b/ports/esp8266/modtime.c index e99d920fde80b..c0c1dccfe474d 100644 --- a/ports/esp8266/modtime.c +++ b/ports/esp8266/modtime.c @@ -29,22 +29,10 @@ #include "shared/timeutils/timeutils.h" #include "modmachine.h" -// Return the localtime as an 8-tuple. -static mp_obj_t mp_time_localtime_get(void) { +// Get the localtime. +static void mp_time_localtime_get(timeutils_struct_time_t *tm) { mp_uint_t seconds = pyb_rtc_get_us_since_epoch() / 1000u / 1000u; - timeutils_struct_time_t tm; - timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); - mp_obj_t tuple[8] = { - tuple[0] = mp_obj_new_int(tm.tm_year), - tuple[1] = mp_obj_new_int(tm.tm_mon), - tuple[2] = mp_obj_new_int(tm.tm_mday), - tuple[3] = mp_obj_new_int(tm.tm_hour), - tuple[4] = mp_obj_new_int(tm.tm_min), - tuple[5] = mp_obj_new_int(tm.tm_sec), - tuple[6] = mp_obj_new_int(tm.tm_wday), - tuple[7] = mp_obj_new_int(tm.tm_yday), - }; - return mp_obj_new_tuple(8, tuple); + timeutils_seconds_since_epoch_to_struct_time(seconds, tm); } // Returns the number of seconds, as an integer, since the Epoch. diff --git a/ports/mimxrt/modtime.c b/ports/mimxrt/modtime.c index 3bcfac411c0b3..fe77b8a733be2 100644 --- a/ports/mimxrt/modtime.c +++ b/ports/mimxrt/modtime.c @@ -29,22 +29,19 @@ #include "shared/timeutils/timeutils.h" #include "fsl_snvs_lp.h" -// Return the localtime as an 8-tuple. -static mp_obj_t mp_time_localtime_get(void) { +// Get the localtime. +static void mp_time_localtime_get(timeutils_struct_time_t *tm) { // Get current date and time. snvs_lp_srtc_datetime_t t; SNVS_LP_SRTC_GetDatetime(SNVS, &t); - mp_obj_t tuple[8] = { - mp_obj_new_int(t.year), - mp_obj_new_int(t.month), - mp_obj_new_int(t.day), - mp_obj_new_int(t.hour), - mp_obj_new_int(t.minute), - mp_obj_new_int(t.second), - mp_obj_new_int(timeutils_calc_weekday(t.year, t.month, t.day)), - mp_obj_new_int(timeutils_year_day(t.year, t.month, t.day)), - }; - return mp_obj_new_tuple(8, tuple); + tm->tm_year = t.year; + tm->tm_mon = t.month; + tm->tm_mday = t.day; + tm->tm_hour = t.hour; + tm->tm_min = t.minute; + tm->tm_sec = t.second; + tm->tm_wday = timeutils_calc_weekday(t.year, t.month, t.day); + tm->tm_yday = timeutils_year_day(t.year, t.month, t.day); } // Return the number of seconds since the Epoch. diff --git a/ports/renesas-ra/modtime.c b/ports/renesas-ra/modtime.c index e1358f82bc5a4..b778ab2fe5e2a 100644 --- a/ports/renesas-ra/modtime.c +++ b/ports/renesas-ra/modtime.c @@ -28,23 +28,20 @@ #include "shared/timeutils/timeutils.h" #include "rtc.h" -// Return the localtime as an 8-tuple. -static mp_obj_t mp_time_localtime_get(void) { +// Get the localtime. +static void mp_time_localtime_get(timeutils_struct_time_t *tm) { // get current date and time rtc_init_finalise(); ra_rtc_t time; ra_rtc_get_time(&time); - mp_obj_t tuple[8] = { - mp_obj_new_int(time.year), - mp_obj_new_int(time.month), - mp_obj_new_int(time.date), - mp_obj_new_int(time.hour), - mp_obj_new_int(time.minute), - mp_obj_new_int(time.second), - mp_obj_new_int(time.weekday - 1), - mp_obj_new_int(timeutils_year_day(time.year, time.month, time.date)), - }; - return mp_obj_new_tuple(8, tuple); + tm->tm_year = time.year; + tm->tm_mon = time.month; + tm->tm_mday = time.date; + tm->tm_hour = time.hour; + tm->tm_min = time.minute; + tm->tm_sec = time.second; + tm->tm_wday = time.weekday - 1; + tm->tm_yday = timeutils_year_day(time.year, time.month, time.date); } // Returns the number of seconds, as an integer, since the Epoch. diff --git a/ports/rp2/modtime.c b/ports/rp2/modtime.c index 7d6dd8fd97112..a860903285e55 100644 --- a/ports/rp2/modtime.c +++ b/ports/rp2/modtime.c @@ -28,23 +28,11 @@ #include "shared/timeutils/timeutils.h" #include "pico/aon_timer.h" -// Return the localtime as an 8-tuple. -static mp_obj_t mp_time_localtime_get(void) { +// Get the localtime. +static void mp_time_localtime_get(timeutils_struct_time_t *tm) { struct timespec ts; aon_timer_get_time(&ts); - timeutils_struct_time_t tm; - timeutils_seconds_since_epoch_to_struct_time(ts.tv_sec, &tm); - mp_obj_t tuple[8] = { - mp_obj_new_int(tm.tm_year), - mp_obj_new_int(tm.tm_mon), - mp_obj_new_int(tm.tm_mday), - mp_obj_new_int(tm.tm_hour), - mp_obj_new_int(tm.tm_min), - mp_obj_new_int(tm.tm_sec), - mp_obj_new_int(tm.tm_wday), - mp_obj_new_int(tm.tm_yday), - }; - return mp_obj_new_tuple(8, tuple); + timeutils_seconds_since_epoch_to_struct_time(ts.tv_sec, tm); } // Return the number of seconds since the Epoch. diff --git a/ports/samd/modtime.c b/ports/samd/modtime.c index 0bed3cb83a868..6168c645d6313 100644 --- a/ports/samd/modtime.c +++ b/ports/samd/modtime.c @@ -30,23 +30,11 @@ static uint64_t time_us_64_offset_from_epoch; -// Return the localtime as an 8-tuple. -static mp_obj_t mp_time_localtime_get(void) { - timeutils_struct_time_t tm; - rtc_gettime(&tm); - tm.tm_wday = timeutils_calc_weekday(tm.tm_year, tm.tm_mon, tm.tm_mday); - tm.tm_yday = timeutils_year_day(tm.tm_year, tm.tm_mon, tm.tm_mday); - mp_obj_t tuple[8] = { - tuple[0] = mp_obj_new_int(tm.tm_year), - tuple[1] = mp_obj_new_int(tm.tm_mon), - tuple[2] = mp_obj_new_int(tm.tm_mday), - tuple[3] = mp_obj_new_int(tm.tm_hour), - tuple[4] = mp_obj_new_int(tm.tm_min), - tuple[5] = mp_obj_new_int(tm.tm_sec), - tuple[6] = mp_obj_new_int(tm.tm_wday), - tuple[7] = mp_obj_new_int(tm.tm_yday), - }; - return mp_obj_new_tuple(8, tuple); +// Get the localtime. +static void mp_time_localtime_get(timeutils_struct_time_t *tm) { + rtc_gettime(tm); + tm->tm_wday = timeutils_calc_weekday(tm->tm_year, tm->tm_mon, tm->tm_mday); + tm->tm_yday = timeutils_year_day(tm->tm_year, tm->tm_mon, tm->tm_mday); } // Returns the number of seconds, as an integer, since the Epoch. diff --git a/ports/stm32/modtime.c b/ports/stm32/modtime.c index 87a4536b04374..e7051065187bc 100644 --- a/ports/stm32/modtime.c +++ b/ports/stm32/modtime.c @@ -28,8 +28,8 @@ #include "shared/timeutils/timeutils.h" #include "rtc.h" -// Return the localtime as an 8-tuple. -static mp_obj_t mp_time_localtime_get(void) { +// Get the localtime. +static void mp_time_localtime_get(timeutils_struct_time_t *tm) { // get current date and time // note: need to call get time then get date to correctly access the registers rtc_init_finalise(); @@ -37,17 +37,14 @@ static mp_obj_t mp_time_localtime_get(void) { RTC_TimeTypeDef time; HAL_RTC_GetTime(&RTCHandle, &time, RTC_FORMAT_BIN); HAL_RTC_GetDate(&RTCHandle, &date, RTC_FORMAT_BIN); - mp_obj_t tuple[8] = { - mp_obj_new_int(2000 + date.Year), - mp_obj_new_int(date.Month), - mp_obj_new_int(date.Date), - mp_obj_new_int(time.Hours), - mp_obj_new_int(time.Minutes), - mp_obj_new_int(time.Seconds), - mp_obj_new_int(date.WeekDay - 1), - mp_obj_new_int(timeutils_year_day(2000 + date.Year, date.Month, date.Date)), - }; - return mp_obj_new_tuple(8, tuple); + tm->tm_year = 2000 + date.Year; + tm->tm_mon = date.Month; + tm->tm_mday = date.Date; + tm->tm_hour = time.Hours; + tm->tm_min = time.Minutes; + tm->tm_sec = time.Seconds; + tm->tm_wday = date.WeekDay - 1; + tm->tm_yday = timeutils_year_day(tm->tm_year, date.Month, date.Date); } // Returns the number of seconds, as an integer, since 1/1/2000. diff --git a/ports/webassembly/modtime.c b/ports/webassembly/modtime.c index 1b1e63d4ddf3a..b6c0cda96da12 100644 --- a/ports/webassembly/modtime.c +++ b/ports/webassembly/modtime.c @@ -28,21 +28,9 @@ #include "shared/timeutils/timeutils.h" #include "library.h" -// Return the localtime as an 8-tuple. -static mp_obj_t mp_time_localtime_get(void) { - timeutils_struct_time_t tm; - timeutils_seconds_since_epoch_to_struct_time(mp_hal_time_ms() / 1000, &tm); - mp_obj_t tuple[8] = { - mp_obj_new_int(tm.tm_year), - mp_obj_new_int(tm.tm_mon), - mp_obj_new_int(tm.tm_mday), - mp_obj_new_int(tm.tm_hour), - mp_obj_new_int(tm.tm_min), - mp_obj_new_int(tm.tm_sec), - mp_obj_new_int(tm.tm_wday), - mp_obj_new_int(tm.tm_yday), - }; - return mp_obj_new_tuple(8, tuple); +// Get the localtime. +static void mp_time_localtime_get(timeutils_struct_time_t *tm) { + timeutils_seconds_since_epoch_to_struct_time(mp_hal_time_ms() / 1000, tm); } // Returns the number of seconds, as a float, since the Epoch. From e58848a21eb6dafce53df991c52ac8db52354daa Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Aug 2025 13:00:11 +1000 Subject: [PATCH 37/60] alif/machine_rtc: Implement RTC.datetime to get and set the RTC. The LPRTC peripheral is a 32-bit counter with a 16-bit prescaler. It's configured here to count at 1Hz (to get maximum date range) and then the prescaler value is used to get 30 microsecond resolution. That's essentially a 32+15=47-bit counter. Signed-off-by: Damien George --- ports/alif/machine_rtc.c | 104 ++++++++++++++++++++++++++++++++++++--- ports/alif/mphalport.h | 2 + 2 files changed, 99 insertions(+), 7 deletions(-) diff --git a/ports/alif/machine_rtc.c b/ports/alif/machine_rtc.c index 6473d1d80fbff..739fcab4b6cb0 100644 --- a/ports/alif/machine_rtc.c +++ b/ports/alif/machine_rtc.c @@ -28,9 +28,20 @@ #include "py/mphal.h" #include "py/mperrno.h" #include "extmod/modmachine.h" +#include "shared/timeutils/timeutils.h" #include "rtc.h" #include "sys_ctrl_rtc.h" +// The LPRTC (low-power real-time counter) is a 32-bit counter with a 16-bit prescaler, +// and usually clocked by a 32768Hz clock source. To get a large date range of around +// 136 years, the prescaler is set to 32768 and so the counter clocks at 1Hz. Then the +// counter counts the number of seconds since the 1970 Epoch. The prescaler is used to +// get the subseconds which are then converted to microseconds. +// +// The combined counter+prescaler counts starting at 0 from the year 1970 up to the year +// 2106, with a resolution of 30.52 microseconds. +#define LPRTC_PRESCALER_SETTING (32768) + typedef struct _machine_rtc_obj_t { mp_obj_base_t base; LPRTC_Type *rtc; @@ -44,24 +55,97 @@ void LPRTC_IRQHandler(void) { lprtc_interrupt_disable(machine_rtc.rtc); } +// Returns the number of seconds and microseconds since the Epoch. +uint32_t mp_hal_time_get(uint32_t *microseconds) { + uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + uint32_t count = lprtc_get_count(machine_rtc.rtc); + if (microseconds == NULL) { + MICROPY_END_ATOMIC_SECTION(atomic_state); + return count; + } + uint32_t prescaler = machine_rtc.rtc->LPRTC_CPCVR; + uint32_t count2 = lprtc_get_count(machine_rtc.rtc); + if (count != count2) { + // The counter incremented during sampling of the prescaler, so resample the prescaler. + prescaler = machine_rtc.rtc->LPRTC_CPCVR; + } + MICROPY_END_ATOMIC_SECTION(atomic_state); + + // Compute the microseconds from the up-counting prescaler value. + MP_STATIC_ASSERT(LPRTC_PRESCALER_SETTING == 32768); + *microseconds = 15625UL * prescaler / 512UL; + + return count2; +} + static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { const machine_rtc_obj_t *self = &machine_rtc; // Check arguments. mp_arg_check_num(n_args, n_kw, 0, 0, false); - enable_lprtc_clk(); - lprtc_prescaler_disable(self->rtc); - lprtc_counter_wrap_disable(self->rtc); lprtc_interrupt_disable(self->rtc); lprtc_interrupt_unmask(self->rtc); + // Initialise the LPRTC if it's not already enabled. + if (!((VBAT->RTC_CLK_EN & RTC_CLK_ENABLE) + && (self->rtc->LPRTC_CCR & CCR_LPRTC_EN) + && (self->rtc->LPRTC_CPSR == LPRTC_PRESCALER_SETTING))) { + enable_lprtc_clk(); + self->rtc->LPRTC_CCR = 0; + lprtc_load_prescaler(self->rtc, LPRTC_PRESCALER_SETTING); + lprtc_load_count(self->rtc, 0); + self->rtc->LPRTC_CCR = CCR_LPRTC_PSCLR_EN | CCR_LPRTC_EN; + } + NVIC_SetPriority(LPRTC_IRQ_IRQn, IRQ_PRI_RTC); NVIC_ClearPendingIRQ(LPRTC_IRQ_IRQn); NVIC_EnableIRQ(LPRTC_IRQ_IRQn); + return MP_OBJ_FROM_PTR(self); } +static mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { + if (n_args == 1) { + // Get datetime. + uint32_t microseconds; + mp_timestamp_t s = mp_hal_time_get(µseconds); + timeutils_struct_time_t tm; + timeutils_seconds_since_epoch_to_struct_time(s, &tm); + mp_obj_t tuple[8] = { + mp_obj_new_int(tm.tm_year), + mp_obj_new_int(tm.tm_mon), + mp_obj_new_int(tm.tm_mday), + mp_obj_new_int(tm.tm_wday), + mp_obj_new_int(tm.tm_hour), + mp_obj_new_int(tm.tm_min), + mp_obj_new_int(tm.tm_sec), + mp_obj_new_int(microseconds), + }; + return mp_obj_new_tuple(8, tuple); + } else { + // Set datetime. + mp_obj_t *items; + mp_obj_get_array_fixed_n(args[1], 8, &items); + timeutils_struct_time_t tm = { + .tm_year = mp_obj_get_int(items[0]), + .tm_mon = mp_obj_get_int(items[1]), + .tm_mday = mp_obj_get_int(items[2]), + .tm_hour = mp_obj_get_int(items[4]), + .tm_min = mp_obj_get_int(items[5]), + .tm_sec = mp_obj_get_int(items[6]), + }; + mp_timestamp_t s = timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + + // Disable then re-enable the LPRTC so that the prescaler counter resets to 0. + machine_rtc.rtc->LPRTC_CCR = 0; + lprtc_load_count(machine_rtc.rtc, s); + machine_rtc.rtc->LPRTC_CCR = CCR_LPRTC_PSCLR_EN | CCR_LPRTC_EN; + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, machine_rtc_datetime); + static mp_obj_t machine_rtc_alarm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_id, ARG_time, ARG_repeat }; @@ -80,13 +164,18 @@ static mp_obj_t machine_rtc_alarm(size_t n_args, const mp_obj_t *pos_args, mp_ma if (mp_obj_is_int(args[ARG_time].u_obj)) { uint32_t seconds = mp_obj_get_int(args[1].u_obj) / 1000; - lprtc_counter_disable(self->rtc); - lprtc_load_count(self->rtc, 1); - lprtc_load_counter_match_register(self->rtc, seconds * 32768); + // Make sure we are guaranteed an interrupt: + // - if seconds = 0 it won't fire + // - if seconds = 1 it may miss if the counter rolls over just after it's read + // - if seconds >= 2 then it will always fire (when read/written close enough) + seconds = MAX(2, seconds); + // Configure the counter match as atomically as possible. + uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); lprtc_interrupt_ack(self->rtc); + lprtc_load_counter_match_register(self->rtc, lprtc_get_count(self->rtc) + seconds); lprtc_interrupt_enable(self->rtc); - lprtc_counter_enable(self->rtc); + MICROPY_END_ATOMIC_SECTION(atomic_state); } else { mp_raise_ValueError(MP_ERROR_TEXT("invalid argument(s)")); } @@ -96,6 +185,7 @@ static mp_obj_t machine_rtc_alarm(size_t n_args, const mp_obj_t *pos_args, mp_ma static MP_DEFINE_CONST_FUN_OBJ_KW(machine_rtc_alarm_obj, 1, machine_rtc_alarm); static const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_datetime), MP_ROM_PTR(&machine_rtc_datetime_obj) }, { MP_ROM_QSTR(MP_QSTR_alarm), MP_ROM_PTR(&machine_rtc_alarm_obj) }, }; static MP_DEFINE_CONST_DICT(machine_rtc_locals_dict, machine_rtc_locals_dict_table); diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index f03dc7c1c1f84..731ac14fc57e6 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -373,3 +373,5 @@ enum { void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]); void mp_hal_get_mac(int idx, uint8_t buf[6]); void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest); + +uint32_t mp_hal_time_get(uint32_t *microseconds); From 0feb4f5ea419c038c69a6fd0b295b788c09f06a2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Aug 2025 13:12:22 +1000 Subject: [PATCH 38/60] alif/mphalport: Implement mp_hal_time_ns. Signed-off-by: Damien George --- ports/alif/mphalport.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 39528a4b9f272..3bf8bb0aed7e3 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -167,7 +167,9 @@ void mp_hal_delay_ms(mp_uint_t ms) { } uint64_t mp_hal_time_ns(void) { - return 0; + uint32_t microseconds; + uint32_t s = mp_hal_time_get(µseconds); + return (uint64_t)s * 1000000000ULL + (uint64_t)microseconds * 1000ULL; } void mp_hal_pin_config(const machine_pin_obj_t *pin, uint32_t mode, From 46b366d7b245360e37443ca096f120c5d601f11d Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Aug 2025 13:04:20 +1000 Subject: [PATCH 39/60] alif/fatfs_port: Implement get_fattime. Signed-off-by: Damien George --- ports/alif/fatfs_port.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/ports/alif/fatfs_port.c b/ports/alif/fatfs_port.c index 5883c9f3b9447..20b7920ebf0f1 100644 --- a/ports/alif/fatfs_port.c +++ b/ports/alif/fatfs_port.c @@ -24,15 +24,12 @@ * THE SOFTWARE. */ +#include "py/mphal.h" +#include "shared/timeutils/timeutils.h" #include "lib/oofatfs/ff.h" DWORD get_fattime(void) { - // TODO - int year = 2024; - int month = 1; - int day = 1; - int hour = 0; - int min = 0; - int sec = 0; - return ((year - 1980) << 25) | (month << 21) | (day << 16) | (hour << 11) | (min << 5) | (sec / 2); + timeutils_struct_time_t tm; + timeutils_seconds_since_epoch_to_struct_time(mp_hal_time_get(NULL), &tm); + return ((tm.tm_year - 1980) << 25) | ((tm.tm_mon) << 21) | ((tm.tm_mday) << 16) | ((tm.tm_hour) << 11) | ((tm.tm_min) << 5) | (tm.tm_sec / 2); } From 326730d8b283245462f7c9dc7d191e7c01cb3756 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Aug 2025 13:05:09 +1000 Subject: [PATCH 40/60] alif/mbedtls: Implement the mbedTLS time function. Signed-off-by: Damien George --- ports/alif/mbedtls/mbedtls_port.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/ports/alif/mbedtls/mbedtls_port.c b/ports/alif/mbedtls/mbedtls_port.c index a8c155e31ae65..14e24f021f9e9 100644 --- a/ports/alif/mbedtls/mbedtls_port.c +++ b/ports/alif/mbedtls/mbedtls_port.c @@ -24,15 +24,10 @@ * THE SOFTWARE. */ -#include "py/obj.h" +#include "py/mphal.h" #include "se_services.h" #include "mbedtls_config_port.h" -#if defined(MBEDTLS_HAVE_TIME) -#include "shared/timeutils/timeutils.h" -#include "mbedtls/platform_time.h" -#endif - int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen) { uint32_t val = 0; int n = 0; @@ -52,14 +47,7 @@ int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t #if defined(MBEDTLS_HAVE_TIME) time_t alif_mbedtls_time(time_t *timer) { - // TODO implement proper RTC time - unsigned int year = 2025; - unsigned int month = 1; - unsigned int date = 1; - unsigned int hours = 12; - unsigned int minutes = 0; - unsigned int seconds = 0; - return timeutils_seconds_since_epoch(year, month, date, hours, minutes, seconds); + return mp_hal_time_get(NULL); } #endif From f1462448d023107e34bdc193d6947a2a78af7d54 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Aug 2025 13:05:39 +1000 Subject: [PATCH 41/60] alif/modtime: Implement the rest of the time module. Adds: `time.time()`, `time.time_ns()`, `time.localtime()`, `time.mktime()` and `time.gmtime()`. Signed-off-by: Damien George --- ports/alif/modtime.c | 39 +++++++++++++++++++++++++++++++++++++++ ports/alif/mpconfigport.h | 4 +++- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 ports/alif/modtime.c diff --git a/ports/alif/modtime.c b/ports/alif/modtime.c new file mode 100644 index 0000000000000..6d40ec2cc37cf --- /dev/null +++ b/ports/alif/modtime.c @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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 "py/mphal.h" +#include "shared/timeutils/timeutils.h" + +// Get the localtime. +static void mp_time_localtime_get(timeutils_struct_time_t *tm) { + mp_timestamp_t s = mp_hal_time_get(NULL); + timeutils_seconds_since_epoch_to_struct_time(s, tm); +} + +// Return the number of seconds since the Epoch. +static mp_obj_t mp_time_time_get(void) { + return mp_obj_new_int_from_uint(mp_hal_time_get(NULL)); +} diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 8a65721bb0ffc..6b30ea2e6245a 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -119,7 +119,9 @@ #define MICROPY_PY_OS_UNAME (1) #define MICROPY_PY_OS_URANDOM (1) #define MICROPY_PY_RANDOM_SEED_INIT_FUNC (se_services_rand64()) -#define MICROPY_PY_TIME (1) +#define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1) +#define MICROPY_PY_TIME_TIME_TIME_NS (1) +#define MICROPY_PY_TIME_INCLUDEFILE "ports/alif/modtime.c" #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/alif/modmachine.c" #define MICROPY_PY_MACHINE_RESET (1) From f0f5abb7a3dd37551736fbbfbb5792875b0a539a Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Aug 2025 13:30:09 +1000 Subject: [PATCH 42/60] alif/mpconfigport: Enable cryptolib and hashlib.md5/sha1. They are enabled when SSL/mbedTLS is included in the firmware. These new features cost around +1400 bytes of code size. Signed-off-by: Damien George --- ports/alif/mpconfigport.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 6b30ea2e6245a..08b27e2786e75 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -112,6 +112,9 @@ // Extended modules #define MICROPY_EPOCH_IS_1970 (1) +#define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL) +#define MICROPY_PY_HASHLIB_MD5 (MICROPY_PY_SSL) +#define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL) #define MICROPY_PY_OS_INCLUDEFILE "ports/alif/modos.c" #define MICROPY_PY_OS_DUPTERM (1) #define MICROPY_PY_OS_SEP (1) From b7cfafc1ee5c0bf8148cb27c6b82a597c3a1aae0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 13 Aug 2025 10:39:44 +1000 Subject: [PATCH 43/60] alif/alif.mk: Add MPY_CROSS_FLAGS setting. The HP and HE CPUs have double-precision hardware floating point, so can use the armv7emdp architecture. This allows frozen code to use native/viper/asm_thumb decorators. Fixes issue #17896. Signed-off-by: Damien George --- ports/alif/alif.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 265418aa07456..d9e7c32578ce8 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -22,6 +22,8 @@ include $(TOP)/extmod/extmod.mk ################################################################################ # Project specific settings and compiler/linker flags +MPY_CROSS_FLAGS += -march=armv7emdp + CROSS_COMPILE ?= arm-none-eabi- ALIF_DFP_REL_TOP ?= lib/alif_ensemble-cmsis-dfp ALIF_DFP_REL_HERE ?= $(TOP)/lib/alif_ensemble-cmsis-dfp From bba354201809dd715287fd32c85818a23f978d97 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Wed, 13 Aug 2025 12:37:23 -0500 Subject: [PATCH 44/60] extmod/modlwip: Remove unused include and functions. The lwIP includes are now port-specific. The `sys_arch_{,un}protect` functions are not used according to line 34 of `extmod/lwip-include/lwipopts_common.h`. Neither have been touched in nearly a decade. Signed-off-by: Thomas Watson --- extmod/lwip-include/arch/cc.h | 41 --------------------------------- extmod/lwip-include/arch/perf.h | 7 ------ extmod/lwip-include/lwipopts.h | 35 ---------------------------- extmod/modlwip.c | 12 ---------- 4 files changed, 95 deletions(-) delete mode 100644 extmod/lwip-include/arch/cc.h delete mode 100644 extmod/lwip-include/arch/perf.h delete mode 100644 extmod/lwip-include/lwipopts.h diff --git a/extmod/lwip-include/arch/cc.h b/extmod/lwip-include/arch/cc.h deleted file mode 100644 index 400dc6ec75de1..0000000000000 --- a/extmod/lwip-include/arch/cc.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H -#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H - -#include - -// Generate lwip's internal types from stdint - -typedef uint8_t u8_t; -typedef int8_t s8_t; -typedef uint16_t u16_t; -typedef int16_t s16_t; -typedef uint32_t u32_t; -typedef int32_t s32_t; - -typedef u32_t mem_ptr_t; - -#define U16_F "hu" -#define S16_F "hd" -#define X16_F "hx" -#define U32_F "u" -#define S32_F "d" -#define X32_F "x" - -#define X8_F "02x" -#define SZT_F "u" - -#define BYTE_ORDER LITTLE_ENDIAN - -#define LWIP_CHKSUM_ALGORITHM 2 - -#include -#define LWIP_PLATFORM_DIAG(x) -#define LWIP_PLATFORM_ASSERT(x) { assert(1); } - -//#define PACK_STRUCT_FIELD(x) x __attribute__((packed)) -#define PACK_STRUCT_FIELD(x) x -#define PACK_STRUCT_STRUCT __attribute__((packed)) -#define PACK_STRUCT_BEGIN -#define PACK_STRUCT_END - -#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H diff --git a/extmod/lwip-include/arch/perf.h b/extmod/lwip-include/arch/perf.h deleted file mode 100644 index d310fc339f162..0000000000000 --- a/extmod/lwip-include/arch/perf.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H -#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H - -#define PERF_START /* null definition */ -#define PERF_STOP(x) /* null definition */ - -#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H diff --git a/extmod/lwip-include/lwipopts.h b/extmod/lwip-include/lwipopts.h deleted file mode 100644 index 2122f30f044e3..0000000000000 --- a/extmod/lwip-include/lwipopts.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H -#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H - -#include -#include -#include - -// We're running without an OS for this port. We don't provide any services except light protection. -#define NO_SYS 1 - -#define SYS_LIGHTWEIGHT_PROT 1 -#include -typedef uint32_t sys_prot_t; - -#define TCP_LISTEN_BACKLOG 1 - -// We'll put these into a proper ifdef once somebody implements an ethernet driver -#define LWIP_ARP 0 -#define LWIP_ETHERNET 0 - -#define LWIP_DNS 1 - -#define LWIP_NETCONN 0 -#define LWIP_SOCKET 0 - -#ifdef MICROPY_PY_LWIP_SLIP -#define LWIP_HAVE_SLIPIF 1 -#endif - -// For now, we can simply define this as a macro for the timer code. But this function isn't -// universal and other ports will need to do something else. It may be necessary to move -// things like this into a port-provided header file. -#define sys_now mp_hal_ticks_ms - -#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 26aab6e81094a..4b1c1b8f3a5ec 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -1724,18 +1724,6 @@ static MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &lwip_socket_locals_dict ); -/******************************************************************************/ -// Support functions for memory protection. lwIP has its own memory management -// routines for its internal structures, and since they might be called in -// interrupt handlers, they need some protection. -sys_prot_t sys_arch_protect() { - return (sys_prot_t)MICROPY_BEGIN_ATOMIC_SECTION(); -} - -void sys_arch_unprotect(sys_prot_t state) { - MICROPY_END_ATOMIC_SECTION((mp_uint_t)state); -} - /******************************************************************************/ // Polling callbacks for the interfaces connected to lwIP. Right now it calls // itself a "list" but isn't; we only support a single interface. From afa7265ffa911917f381ac6790ffe032b2b7baf8 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Fri, 25 Jul 2025 16:35:27 -0500 Subject: [PATCH 45/60] zephyr: Upgrade to Zephyr v4.2.0. Updates the Zephyr port build instructions and CI to use the latest Zephyr release tag. Tested on max32690fthr and frdm_k64f. Signed-off-by: Maureen Helm --- docs/zephyr/tutorial/repl.rst | 4 ++-- ports/zephyr/README.md | 8 ++++---- tools/ci.sh | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/zephyr/tutorial/repl.rst b/docs/zephyr/tutorial/repl.rst index 199dda2b7aeee..1b16c5ad21d7b 100644 --- a/docs/zephyr/tutorial/repl.rst +++ b/docs/zephyr/tutorial/repl.rst @@ -31,8 +31,8 @@ With your serial program open (PuTTY, screen, picocom, etc) you may see a blank screen with a flashing cursor. Press Enter (or reset the board) and you should be presented with the following text:: - *** Booting Zephyr OS build v4.0.0 *** - MicroPython v1.24.0-preview.179.g5b85b24bd on 2024-08-05; zephyr-frdm_k64f with mk64f12 + *** Booting Zephyr OS build v4.2.0 *** + MicroPython v1.26.0-preview.451.gebc9525c9 on 2025-07-25; zephyr-frdm_k64f with mk64f12 Type "help()" for more information. >>> diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md index 17c1f613de4ed..8384fabcf0261 100644 --- a/ports/zephyr/README.md +++ b/ports/zephyr/README.md @@ -5,8 +5,8 @@ This is a work-in-progress port of MicroPython to Zephyr RTOS (http://zephyrproject.org). This port tries to support all Zephyr versions supported upstream, -i.e. currently v3.7 (LTS), v4.0 and the development branch. The CI is -setup to use the latest version, i.e. v4.0. +i.e. currently v3.7 (LTS), v4.2 and the development branch. The CI is +setup to use the latest version, i.e. v4.2. All boards supported by Zephyr (with standard level of features support, like UART console) should work with MicroPython (but not all @@ -43,13 +43,13 @@ setup is correct. If you already have Zephyr installed but are having issues building the MicroPython port then try installing the correct version of Zephyr via: - $ west init zephyrproject -m https://github.com/zephyrproject-rtos/zephyr --mr v4.0.0 + $ west init zephyrproject -m https://github.com/zephyrproject-rtos/zephyr --mr v4.2.0 Alternatively, you don't have to redo the Zephyr installation to just switch from master to a tagged release, you can instead do: $ cd zephyrproject/zephyr - $ git checkout v4.0.0 + $ git checkout v4.2.0 $ west update With Zephyr installed you may then need to configure your environment, diff --git a/tools/ci.sh b/tools/ci.sh index 8f045639b80c9..9152c2e2dd699 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -886,9 +886,9 @@ function ci_windows_build { ######################################################################################## # ports/zephyr -ZEPHYR_DOCKER_VERSION=v0.27.4 -ZEPHYR_SDK_VERSION=0.17.0 -ZEPHYR_VERSION=v4.0.0 +ZEPHYR_DOCKER_VERSION=v0.28.1 +ZEPHYR_SDK_VERSION=0.17.2 +ZEPHYR_VERSION=v4.2.0 function ci_zephyr_setup { IMAGE=ghcr.io/zephyrproject-rtos/ci:${ZEPHYR_DOCKER_VERSION} From 1588c455c41b445bfd1c65b5055847ab63114c96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 02:37:54 +0000 Subject: [PATCH 46/60] github/workflows: Bump actions/checkout from 4 to 5. Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/biome.yml | 2 +- .github/workflows/code_formatting.yml | 2 +- .github/workflows/code_size.yml | 2 +- .github/workflows/codespell.yml | 2 +- .github/workflows/commit_formatting.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/examples.yml | 2 +- .github/workflows/mpremote.yml | 2 +- .github/workflows/mpy_format.yml | 2 +- .github/workflows/ports.yml | 2 +- .github/workflows/ports_alif.yml | 2 +- .github/workflows/ports_cc3200.yml | 2 +- .github/workflows/ports_esp32.yml | 2 +- .github/workflows/ports_esp8266.yml | 2 +- .github/workflows/ports_mimxrt.yml | 2 +- .github/workflows/ports_nrf.yml | 2 +- .github/workflows/ports_powerpc.yml | 2 +- .github/workflows/ports_qemu.yml | 4 +-- .github/workflows/ports_renesas-ra.yml | 2 +- .github/workflows/ports_rp2.yml | 2 +- .github/workflows/ports_samd.yml | 2 +- .github/workflows/ports_stm32.yml | 2 +- .github/workflows/ports_unix.yml | 38 ++++++++++++------------- .github/workflows/ports_webassembly.yml | 2 +- .github/workflows/ports_windows.yml | 6 ++-- .github/workflows/ports_zephyr.yml | 2 +- .github/workflows/ruff.yml | 2 +- 27 files changed, 48 insertions(+), 48 deletions(-) diff --git a/.github/workflows/biome.yml b/.github/workflows/biome.yml index 88744f16ca7d6..fea9c9c8bd7bc 100644 --- a/.github/workflows/biome.yml +++ b/.github/workflows/biome.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Biome uses: biomejs/setup-biome@v2 with: diff --git a/.github/workflows/code_formatting.yml b/.github/workflows/code_formatting.yml index 9f30f048cfdbe..5669779946e06 100644 --- a/.github/workflows/code_formatting.yml +++ b/.github/workflows/code_formatting.yml @@ -10,7 +10,7 @@ jobs: code-formatting: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 - name: Install packages run: source tools/ci.sh && ci_c_code_formatting_setup diff --git a/.github/workflows/code_size.yml b/.github/workflows/code_size.yml index 67261933798cb..8587ef0179442 100644 --- a/.github/workflows/code_size.yml +++ b/.github/workflows/code_size.yml @@ -25,7 +25,7 @@ jobs: build: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 100 - name: Install packages diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 1d6b1dc9d2b27..688134b42515c 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -6,7 +6,7 @@ jobs: codespell: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 # codespell version should be kept in sync with .pre-commit-config.yml - run: pip install --user codespell==2.4.1 tomli - run: codespell diff --git a/.github/workflows/commit_formatting.yml b/.github/workflows/commit_formatting.yml index fcbcaa7092ee2..2e1def95c36e8 100644 --- a/.github/workflows/commit_formatting.yml +++ b/.github/workflows/commit_formatting.yml @@ -10,7 +10,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 100 - uses: actions/setup-python@v5 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 62a6f69fc3924..f9d61125b50c1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 - name: Install Python packages run: pip install -r docs/requirements.txt diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 6613f106625a2..d16122b720b6c 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -18,7 +18,7 @@ jobs: embedding: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build run: make -C examples/embedding -f micropython_embed.mk && make -C examples/embedding - name: Run diff --git a/.github/workflows/mpremote.yml b/.github/workflows/mpremote.yml index ee91b6360b9b4..359d888286400 100644 --- a/.github/workflows/mpremote.yml +++ b/.github/workflows/mpremote.yml @@ -11,7 +11,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: # Setting this to zero means fetch all history and tags, # which hatch-vcs can use to discover the version tag. diff --git a/.github/workflows/mpy_format.yml b/.github/workflows/mpy_format.yml index b6768a46c3cdc..4043b63288a0c 100644 --- a/.github/workflows/mpy_format.yml +++ b/.github/workflows/mpy_format.yml @@ -17,7 +17,7 @@ jobs: test: runs-on: ubuntu-22.04 # use 22.04 to get python2 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_mpy_format_setup - name: Test mpy-tool.py diff --git a/.github/workflows/ports.yml b/.github/workflows/ports.yml index 1f262b0ba4bee..d4e89bd1aa924 100644 --- a/.github/workflows/ports.yml +++ b/.github/workflows/ports.yml @@ -17,6 +17,6 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build ports download metadata run: mkdir boards && ./tools/autobuild/build-downloads.py . ./boards diff --git a/.github/workflows/ports_alif.yml b/.github/workflows/ports_alif.yml index 0e96e7d816e50..a06b3f96ffae4 100644 --- a/.github/workflows/ports_alif.yml +++ b/.github/workflows/ports_alif.yml @@ -26,7 +26,7 @@ jobs: - alif_ae3_build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_alif_setup - name: Build ci_${{matrix.ci_func }} diff --git a/.github/workflows/ports_cc3200.yml b/.github/workflows/ports_cc3200.yml index f178a140587db..b60ff370daf9d 100644 --- a/.github/workflows/ports_cc3200.yml +++ b/.github/workflows/ports_cc3200.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_cc3200_setup - name: Build diff --git a/.github/workflows/ports_esp32.yml b/.github/workflows/ports_esp32.yml index 36ab341cfcdd2..b86c6a76f82c7 100644 --- a/.github/workflows/ports_esp32.yml +++ b/.github/workflows/ports_esp32.yml @@ -28,7 +28,7 @@ jobs: - esp32_build_c2_c6 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - id: idf_ver name: Read the ESP-IDF version (including Python version) diff --git a/.github/workflows/ports_esp8266.yml b/.github/workflows/ports_esp8266.yml index 5236edf40b959..3293abed5980d 100644 --- a/.github/workflows/ports_esp8266.yml +++ b/.github/workflows/ports_esp8266.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_esp8266_setup && ci_esp8266_path >> $GITHUB_PATH - name: Build diff --git a/.github/workflows/ports_mimxrt.yml b/.github/workflows/ports_mimxrt.yml index 7743e036ab377..ae9a80ec5806c 100644 --- a/.github/workflows/ports_mimxrt.yml +++ b/.github/workflows/ports_mimxrt.yml @@ -24,7 +24,7 @@ jobs: run: working-directory: 'micropython repo' # test build with space in path steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: path: 'micropython repo' - name: Install packages diff --git a/.github/workflows/ports_nrf.yml b/.github/workflows/ports_nrf.yml index 76727c9d1f6bd..ce86617af05ce 100644 --- a/.github/workflows/ports_nrf.yml +++ b/.github/workflows/ports_nrf.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_nrf_setup - name: Build diff --git a/.github/workflows/ports_powerpc.yml b/.github/workflows/ports_powerpc.yml index c41b13e5ddffe..81f71ca8a96f4 100644 --- a/.github/workflows/ports_powerpc.yml +++ b/.github/workflows/ports_powerpc.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_powerpc_setup - name: Build diff --git a/.github/workflows/ports_qemu.yml b/.github/workflows/ports_qemu.yml index ac09dde86408b..857645776629a 100644 --- a/.github/workflows/ports_qemu.yml +++ b/.github/workflows/ports_qemu.yml @@ -29,7 +29,7 @@ jobs: - thumb runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_qemu_setup_arm - name: Build and run test suite ci_qemu_build_arm_${{ matrix.ci_func }} @@ -41,7 +41,7 @@ jobs: build_and_test_rv32: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_qemu_setup_rv32 - name: Build and run test suite diff --git a/.github/workflows/ports_renesas-ra.yml b/.github/workflows/ports_renesas-ra.yml index b9fa74331dc0b..bf99ed25fedae 100644 --- a/.github/workflows/ports_renesas-ra.yml +++ b/.github/workflows/ports_renesas-ra.yml @@ -21,7 +21,7 @@ jobs: build_renesas_ra_board: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_renesas_ra_setup - name: Build diff --git a/.github/workflows/ports_rp2.yml b/.github/workflows/ports_rp2.yml index 748f38e143893..22d2a9688015f 100644 --- a/.github/workflows/ports_rp2.yml +++ b/.github/workflows/ports_rp2.yml @@ -24,7 +24,7 @@ jobs: run: working-directory: 'micropython repo' # test build with space in path steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: path: 'micropython repo' - name: Install packages diff --git a/.github/workflows/ports_samd.yml b/.github/workflows/ports_samd.yml index 5bf1826cd17bb..dbea255c79ad4 100644 --- a/.github/workflows/ports_samd.yml +++ b/.github/workflows/ports_samd.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_samd_setup - name: Build diff --git a/.github/workflows/ports_stm32.yml b/.github/workflows/ports_stm32.yml index 8800f145189b8..43659a5d5dd5d 100644 --- a/.github/workflows/ports_stm32.yml +++ b/.github/workflows/ports_stm32.yml @@ -28,7 +28,7 @@ jobs: - stm32_misc_build runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_stm32_setup - name: Build ci_${{matrix.ci_func }} diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index f3f613a789af3..8fd8e1aec23d4 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -23,7 +23,7 @@ jobs: minimal: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build run: source tools/ci.sh && ci_unix_minimal_build - name: Run main test suite @@ -35,7 +35,7 @@ jobs: reproducible: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build with reproducible date run: source tools/ci.sh && ci_unix_minimal_build env: @@ -46,7 +46,7 @@ jobs: standard: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build run: source tools/ci.sh && ci_unix_standard_build - name: Run main test suite @@ -58,7 +58,7 @@ jobs: standard_v2: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build run: source tools/ci.sh && ci_unix_standard_v2_build - name: Run main test suite @@ -70,7 +70,7 @@ jobs: coverage: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests. # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default. @@ -105,7 +105,7 @@ jobs: coverage_32bit: runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_unix_32bit_setup - name: Build @@ -123,7 +123,7 @@ jobs: nanbox: runs-on: ubuntu-22.04 # use 22.04 to get python2, and libffi-dev:i386 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_unix_32bit_setup - name: Build @@ -137,7 +137,7 @@ jobs: longlong: runs-on: ubuntu-22.04 # use 22.04 to get python2, and libffi-dev:i386 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_unix_32bit_setup - name: Build @@ -151,7 +151,7 @@ jobs: float: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build run: source tools/ci.sh && ci_unix_float_build - name: Run main test suite @@ -163,7 +163,7 @@ jobs: gil_enabled: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build run: source tools/ci.sh && ci_unix_gil_enabled_build - name: Run main test suite @@ -175,7 +175,7 @@ jobs: stackless_clang: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_unix_clang_setup - name: Build @@ -189,7 +189,7 @@ jobs: float_clang: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_unix_clang_setup - name: Build @@ -203,7 +203,7 @@ jobs: settrace_stackless: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests. # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default. @@ -220,7 +220,7 @@ jobs: macos: runs-on: macos-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 with: python-version: '3.8' @@ -236,7 +236,7 @@ jobs: # ubuntu-22.04 is needed for older libffi. runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_unix_qemu_mips_setup - name: Build @@ -251,7 +251,7 @@ jobs: # ubuntu-22.04 is needed for older libffi. runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_unix_qemu_arm_setup - name: Build @@ -266,7 +266,7 @@ jobs: # ubuntu-22.04 is needed for older libffi. runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_unix_qemu_riscv64_setup - name: Build @@ -280,7 +280,7 @@ jobs: sanitize_address: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests. # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default. @@ -305,7 +305,7 @@ jobs: sanitize_undefined: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests. # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default. diff --git a/.github/workflows/ports_webassembly.yml b/.github/workflows/ports_webassembly.yml index ceb35f83cd8f1..14399950b569e 100644 --- a/.github/workflows/ports_webassembly.yml +++ b/.github/workflows/ports_webassembly.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_webassembly_setup - name: Build diff --git a/.github/workflows/ports_windows.yml b/.github/workflows/ports_windows.yml index f33277d471d3c..6b492640a1fa0 100644 --- a/.github/workflows/ports_windows.yml +++ b/.github/workflows/ports_windows.yml @@ -58,7 +58,7 @@ jobs: - uses: microsoft/setup-msbuild@v2 with: vs-version: ${{ matrix.vs_version }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build mpy-cross.exe run: msbuild mpy-cross\mpy-cross.vcxproj -maxcpucount -property:Configuration=${{ matrix.configuration }} -property:Platform=${{ matrix.platform }} - name: Update submodules @@ -125,7 +125,7 @@ jobs: git diffutils path-type: inherit # Remove when setup-python is removed - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build mpy-cross.exe run: make -C mpy-cross -j2 - name: Update submodules @@ -143,7 +143,7 @@ jobs: cross-build-on-linux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install packages run: source tools/ci.sh && ci_windows_setup - name: Build diff --git a/.github/workflows/ports_zephyr.yml b/.github/workflows/ports_zephyr.yml index ca3c36e004cca..9ce7034398669 100644 --- a/.github/workflows/ports_zephyr.yml +++ b/.github/workflows/ports_zephyr.yml @@ -30,7 +30,7 @@ jobs: large-packages: false docker-images: false swap-storage: false - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - id: versions name: Read Zephyr version run: source tools/ci.sh && echo "ZEPHYR=$ZEPHYR_VERSION" | tee "$GITHUB_OUTPUT" diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 4c4a2a3162ed6..633b0cdf82ef4 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -6,7 +6,7 @@ jobs: ruff: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 # ruff version should be kept in sync with .pre-commit-config.yaml & also micropython-lib - run: pipx install ruff==0.11.6 - run: ruff check --output-format=github . From 1b35116c920550ba7bfc0f06bd4bad7d20f33b21 Mon Sep 17 00:00:00 2001 From: Yuuki NAGAO Date: Tue, 22 Jul 2025 20:14:14 +0900 Subject: [PATCH 47/60] stm32/dac: Fix 12-bit DAC issue on STM32H5. For STM32H5, to use 12-bit DAC, the DMA parameter should set: - Actual DMA source datawidth to CTR1. - The length is the amount of data to be transferred from source to destination in bytes. Also, this commit modifies the (dummy) definition of DMA_CIRCULAR for STM32H5 to prevent conflict with data width specification. Signed-off-by: Yuuki NAGAO --- ports/stm32/dac.c | 7 +++++-- ports/stm32/dma.c | 4 ++-- ports/stm32/dma.h | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ports/stm32/dac.c b/ports/stm32/dac.c index 54d5acc6cea9d..0be0fd9300b12 100644 --- a/ports/stm32/dac.c +++ b/ports/stm32/dac.c @@ -174,7 +174,7 @@ static void dac_start_dma(uint32_t dac_channel, const dma_descr_t *dma_descr, ui // For STM32G4, DAC registers have to be accessed by words (32-bit). dma_align = DMA_MDATAALIGN_BYTE | DMA_PDATAALIGN_WORD; #elif defined(STM32H5) - dma_align = 0; + dma_align = DMA_SRC_DATAWIDTH_BYTE | DMA_DEST_DATAWIDTH_WORD; #else dma_align = DMA_MDATAALIGN_BYTE | DMA_PDATAALIGN_BYTE; #endif @@ -183,7 +183,7 @@ static void dac_start_dma(uint32_t dac_channel, const dma_descr_t *dma_descr, ui // For STM32G4, DAC registers have to be accessed by words (32-bit). dma_align = DMA_MDATAALIGN_HALFWORD | DMA_PDATAALIGN_WORD; #elif defined(STM32H5) - dma_align = 0; + dma_align = DMA_SRC_DATAWIDTH_HALFWORD | DMA_DEST_DATAWIDTH_WORD; #else dma_align = DMA_MDATAALIGN_HALFWORD | DMA_PDATAALIGN_HALFWORD; #endif @@ -490,7 +490,10 @@ mp_obj_t pyb_dac_write_timed(size_t n_args, const mp_obj_t *pos_args, mp_map_t * align = DAC_ALIGN_8B_R; } else { align = DAC_ALIGN_12B_R; + // For STM32H5, the length is the amount of data to be transferred from source to destination in bytes. + #if !defined(STM32H5) bufinfo.len /= 2; + #endif } dac_start_dma(self->dac_channel, tx_dma_descr, args[2].u_int, self->bits, align, bufinfo.len, bufinfo.buf); diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index c252770740d01..c2923f7ae732c 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -1737,10 +1737,10 @@ void dma_nohal_init(const dma_descr_t *descr, uint32_t config) { dma->CCR = init->Priority; uint32_t ctr1reg = 0; - ctr1reg |= init->SrcDataWidth; + ctr1reg |= config & DMA_CTR1_SDW_LOG2_Msk; ctr1reg |= init->SrcInc; ctr1reg |= (((init->SrcBurstLength - 1) << DMA_CTR1_SBL_1_Pos)) & DMA_CTR1_SBL_1_Msk; - ctr1reg |= init->DestDataWidth; + ctr1reg |= config & DMA_CTR1_DDW_LOG2_Msk; ctr1reg |= init->DestInc; ctr1reg |= (((init->DestBurstLength - 1) << DMA_CTR1_DBL_1_Pos)) & DMA_CTR1_DBL_1_Msk; diff --git a/ports/stm32/dma.h b/ports/stm32/dma.h index f05b22b5d05cd..75e007bc9fa98 100644 --- a/ports/stm32/dma.h +++ b/ports/stm32/dma.h @@ -33,7 +33,7 @@ typedef struct _dma_descr_t dma_descr_t; #if defined(STM32H5) // STM32H5 GPDMA doesn't feature circular mode directly, so define doesn't exist in // stm32 driver header. Define it here to make users like DAC driver happy. -#define DMA_CIRCULAR 0x00000001 +#define DMA_CIRCULAR 0x20000000 #endif #if defined(STM32F0) || defined(STM32F4) || defined(STM32F7) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) From 152a0782e6ef8fc510b3e0106df82e27595847c2 Mon Sep 17 00:00:00 2001 From: Yuuki NAGAO Date: Fri, 25 Jul 2025 20:14:03 +0900 Subject: [PATCH 48/60] stm32/dac: Add support for DAC feature on STM32G0. DAC.write() and DAC.write_timed() are now available on STM32G0. Tested on NUCLEO_G0B1RE. Signed-off-by: Yuuki NAGAO --- ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h | 2 +- ports/stm32/dac.c | 2 +- ports/stm32/dma.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h b/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h index 092ee177925eb..ead36ed6c763f 100644 --- a/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h @@ -5,7 +5,7 @@ #define MICROPY_HW_HAS_FLASH (1) #define MICROPY_HW_ENABLE_RNG (0) #define MICROPY_HW_ENABLE_RTC (1) -#define MICROPY_HW_ENABLE_DAC (0) +#define MICROPY_HW_ENABLE_DAC (1) #define MICROPY_HW_ENABLE_USB (0) // can be enabled if USB cable connected to PA11/PA12 #define MICROPY_PY_PYB_LEGACY (0) diff --git a/ports/stm32/dac.c b/ports/stm32/dac.c index 0be0fd9300b12..040892553d38c 100644 --- a/ports/stm32/dac.c +++ b/ports/stm32/dac.c @@ -97,7 +97,7 @@ static uint32_t TIMx_Config(mp_obj_t timer) { // work out the trigger channel (only certain ones are supported) if (tim->Instance == TIM2) { return DAC_TRIGGER_T2_TRGO; - #if defined(TIM4) + #if defined(TIM4) && defined(DAC_TRIGGER_T4_TRGO) // G0B1 doesn't have this } else if (tim->Instance == TIM4) { return DAC_TRIGGER_T4_TRGO; #endif diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index c2923f7ae732c..ad199b1fb91d9 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -1657,7 +1657,7 @@ static void dma_idle_handler(uint32_t tick) { } #endif -#if defined(STM32F0) || defined(STM32G4) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) +#if defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) void dma_nohal_init(const dma_descr_t *descr, uint32_t config) { DMA_Channel_TypeDef *dma = descr->instance; @@ -1680,7 +1680,7 @@ void dma_nohal_init(const dma_descr_t *descr, uint32_t config) { } else { __HAL_DMA2_REMAP(descr->sub_instance); } - #elif defined(STM32G4) + #elif defined(STM32G0) || defined(STM32G4) uint32_t *dmamux_ctrl = (void *)(DMAMUX1_Channel0_BASE + 0x04 * descr->id); *dmamux_ctrl = (*dmamux_ctrl & ~(0x7f)) | descr->sub_instance; #elif defined(STM32L1) @@ -1807,7 +1807,7 @@ void dma_nohal_start(const dma_descr_t *descr, uint32_t src_addr, uint32_t dst_a dma->CCR |= DMA_CCR_EN; } -#elif defined(STM32G0) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) +#elif defined(STM32N6) || defined(STM32WB) || defined(STM32WL) // These functions are currently not implemented or needed for this MCU. From 365329cd540f631f1cfe805b475f792cf00257e0 Mon Sep 17 00:00:00 2001 From: Yuuki NAGAO Date: Sun, 27 Jul 2025 22:03:42 +0900 Subject: [PATCH 49/60] stm32/dac: Fix DAC write for MCUs that have D-Cache. To prevent wrong DAC output, clean D-cache before starting DMA. For more details, please refer to the following document: https://www.st.com/resource/en/application_note/DM00272913.pdf Signed-off-by: Yuuki NAGAO --- ports/stm32/dac.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/stm32/dac.c b/ports/stm32/dac.c index 040892553d38c..8022fd274ce97 100644 --- a/ports/stm32/dac.c +++ b/ports/stm32/dac.c @@ -485,6 +485,9 @@ mp_obj_t pyb_dac_write_timed(size_t n_args, const mp_obj_t *pos_args, mp_map_t * #endif } + // To prevent invalid dac output, clean D-cache before starting dma. + MP_HAL_CLEAN_DCACHE(bufinfo.buf, bufinfo.len); + uint32_t align; if (self->bits == 8) { align = DAC_ALIGN_8B_R; From c7ddf0c54fcc9c6abdb38db27a558ab635ba9763 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 11 Aug 2025 14:20:36 +0200 Subject: [PATCH 50/60] stm32/Makefile: Add .gc.blocks.table section to generated binary. The generated binary file was missing this section, which caused a hard fault when loading bin or dfu firmware (eg on ARDUINO_GIGA). Signed-off-by: iabdalkader --- ports/stm32/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 37ce8fbd8383d..abca3a05f8c7a 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -611,7 +611,7 @@ TEXT0_ADDR ?= 0x08000000 ifeq ($(TEXT1_ADDR),) # No TEXT1_ADDR given so put all firmware at TEXT0_ADDR location -TEXT0_SECTIONS ?= .isr_vector .isr_extratext .text .data .ARM +TEXT0_SECTIONS ?= .isr_vector .isr_extratext .text .gc.blocks.table .data .ARM deploy-stlink: $(BUILD)/firmware.bin $(call RUN_STLINK,$^,$(TEXT0_ADDR)) @@ -629,7 +629,7 @@ else # TEXT0_ADDR and TEXT1_ADDR are specified so split firmware between these locations TEXT0_SECTIONS ?= .isr_vector .isr_extratext -TEXT1_SECTIONS ?= .text .data .ARM +TEXT1_SECTIONS ?= .text .gc.blocks.table .data .ARM deploy-stlink: $(BUILD)/firmware0.bin $(BUILD)/firmware1.bin $(call RUN_STLINK,$(word 1,$^),$(TEXT0_ADDR)) From 020eeba41246f6015fb56f830141077d43d2334b Mon Sep 17 00:00:00 2001 From: Tico06 Date: Mon, 11 Aug 2025 19:30:47 +0200 Subject: [PATCH 51/60] stm32/eth_phy: Fix typo in header guard macro. Typo on line 29 (PYH instead of PHY). Compilation failing, here is the output: eth_phy.h:28: error: header guard 'MICROPY_INCLUDED_STM32_PHY_H' followed by '#define' of a different macro [Werror=header-guard] 28 | #ifndef MICROPY_INCLUDED_STM32_PHY_H eth_phy.h:29: note: 'MICROPY_INCLUDED_STM32_PYH_H' is defined here; did you mean 'MICROPY_INCLUDED_STM32_PHY_H'? 29 | #define MICROPY_INCLUDED_STM32_PYH_H Signed-off-by: Tico06 --- ports/stm32/eth_phy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/eth_phy.h b/ports/stm32/eth_phy.h index 5036905c1f582..dccfb7951add8 100644 --- a/ports/stm32/eth_phy.h +++ b/ports/stm32/eth_phy.h @@ -26,7 +26,7 @@ */ #ifndef MICROPY_INCLUDED_STM32_PHY_H -#define MICROPY_INCLUDED_STM32_PYH_H +#define MICROPY_INCLUDED_STM32_PHY_H #if defined(MICROPY_HW_ETH_MDC) From d81d56cc4d7c44978761f967841bcb6e8e84b54d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 22 Jul 2025 10:56:22 +1000 Subject: [PATCH 52/60] tools/ci: Add UBSan to longlong CI build. Also rewrite the sanitizer argument variables to not assume a variant. longlong variant currently fails in this config, due to a bug fixed in follow-up commit. Signed-off-by: Angus Gratton --- tools/ci.sh | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tools/ci.sh b/tools/ci.sh index 9152c2e2dd699..095d96cf39beb 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -518,13 +518,11 @@ CI_UNIX_OPTS_QEMU_RISCV64=( ) CI_UNIX_OPTS_SANITIZE_ADDRESS=( - VARIANT=coverage CFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0" LDFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0" ) CI_UNIX_OPTS_SANITIZE_UNDEFINED=( - VARIANT=coverage CFLAGS_EXTRA="-fsanitize=undefined -fno-sanitize=nonnull-attribute" LDFLAGS_EXTRA="-fsanitize=undefined -fno-sanitize=nonnull-attribute" ) @@ -699,7 +697,7 @@ function ci_unix_nanbox_run_tests { } function ci_unix_longlong_build { - ci_unix_build_helper VARIANT=longlong + ci_unix_build_helper VARIANT=longlong "${CI_UNIX_OPTS_SANITIZE_UNDEFINED[@]}" } function ci_unix_longlong_run_tests { @@ -765,23 +763,23 @@ function ci_unix_settrace_stackless_run_tests { function ci_unix_sanitize_undefined_build { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix submodules - make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SANITIZE_UNDEFINED[@]}" + make ${MAKEOPTS} -C ports/unix VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_UNDEFINED[@]}" ci_unix_build_ffi_lib_helper gcc } function ci_unix_sanitize_undefined_run_tests { - MICROPY_TEST_TIMEOUT=60 ci_unix_run_tests_full_helper coverage "${CI_UNIX_OPTS_SANITIZE_UNDEFINED[@]}" + MICROPY_TEST_TIMEOUT=60 ci_unix_run_tests_full_helper coverage VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_UNDEFINED[@]}" } function ci_unix_sanitize_address_build { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix submodules - make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SANITIZE_ADDRESS[@]}" + make ${MAKEOPTS} -C ports/unix VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_ADDRESS[@]}" ci_unix_build_ffi_lib_helper gcc } function ci_unix_sanitize_address_run_tests { - MICROPY_TEST_TIMEOUT=60 ci_unix_run_tests_full_helper coverage "${CI_UNIX_OPTS_SANITIZE_ADDRESS[@]}" + MICROPY_TEST_TIMEOUT=60 ci_unix_run_tests_full_helper coverage VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_ADDRESS[@]}" } function ci_unix_macos_build { From 3faf2298537ee34648d27a662529ff2aa73cec1c Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 6 Aug 2025 10:19:43 +1000 Subject: [PATCH 53/60] py/misc: Add a way to detect sanitizer builds. Clang and gcc>=14 can use __has_feature() to detect if a sanitizer is enabled, but older GCC has no mechanism - need to set a macro explicitly for this to be recognised. Necessary for increasing some resource limits in sanitizer builds. Important not to use to avoid real issues! This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- py/misc.h | 24 ++++++++++++++++++++++++ tools/ci.sh | 6 ++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/py/misc.h b/py/misc.h index 5c1cc2f7a836b..081163cadf9fd 100644 --- a/py/misc.h +++ b/py/misc.h @@ -43,6 +43,11 @@ typedef unsigned int uint; #ifndef __has_builtin #define __has_builtin(x) (0) #endif +#ifndef __has_feature +// This macro is supported by Clang and gcc>=14 +#define __has_feature(x) (0) +#endif + /** generic ops *************************************************/ @@ -538,4 +543,23 @@ inline static bool mp_sub_ll_overflow(long long int lhs, long long int rhs, long } #endif + +// Helper macros for detecting if sanitizers are enabled +// +// Use sparingly, not for masking issues reported by sanitizers! +// +// Can be detected automatically in Clang and gcc>=14, need to be +// set manually otherwise. +#ifndef MP_UBSAN +#define MP_UBSAN __has_feature(undefined_behavior_sanitizer) +#endif + +#ifndef MP_ASAN +#define MP_ASAN __has_feature(address_sanitizer) +#endif + +#ifndef MP_SANITIZER_BUILD +#define MP_SANITIZER_BUILD (MP_UBSAN || MP_ASAN) +#endif + #endif // MICROPY_INCLUDED_PY_MISC_H diff --git a/tools/ci.sh b/tools/ci.sh index 095d96cf39beb..e165cb2cf3f48 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -518,12 +518,14 @@ CI_UNIX_OPTS_QEMU_RISCV64=( ) CI_UNIX_OPTS_SANITIZE_ADDRESS=( - CFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0" + # Macro MP_ASAN allows detecting ASan on gcc<=13 + CFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0 -DMP_ASAN=1" LDFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0" ) CI_UNIX_OPTS_SANITIZE_UNDEFINED=( - CFLAGS_EXTRA="-fsanitize=undefined -fno-sanitize=nonnull-attribute" + # Macro MP_UBSAN allows detecting UBSan on gcc<=13 + CFLAGS_EXTRA="-fsanitize=undefined -fno-sanitize=nonnull-attribute -DMP_UBSAN=1" LDFLAGS_EXTRA="-fsanitize=undefined -fno-sanitize=nonnull-attribute" ) From d8672f4cde83c0a5632f9e6ac5a55eb68fed64f9 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 6 Aug 2025 10:20:57 +1000 Subject: [PATCH 54/60] unix: Increase stack sizes if running with sanitizers. The specific problem seems to be that the runtime "Python stack frame" function call is several times more expensive in stack usage when running with UBSan on older GCC (observed on gcc 11.4 as used in CI, would get 'RuntimeError: maximum recursion depth exceeded' when running some tests with UBSan enabled.) Other stack usage (i.e. from pushing things on the stack in Python) stays the same. Whatever causes the usage seems to be mostly gone in later GCC versions. Includes a refactor to apply the same stack size multipliers for the default thread stack size same as the main stack size. This goes in a new port-specific header as it depends on macros in misc.h, so can't be in mpconfigport.h. A side effect of this is that the default thread stack size is now doubled on ARM, same as the main stack size. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/unix/main.c | 7 ++--- ports/unix/mpthreadport.c | 5 ++-- ports/unix/stack_size.h | 54 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 ports/unix/stack_size.h diff --git a/ports/unix/main.c b/ports/unix/main.c index 5f6b99f989a3a..dd0788aaad96b 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -54,6 +54,7 @@ #include "extmod/vfs_posix.h" #include "genhdr/mpversion.h" #include "input.h" +#include "stack_size.h" // Command line options, with their defaults static bool compile_only = false; @@ -479,11 +480,7 @@ int main(int argc, char **argv) { #endif // Define a reasonable stack limit to detect stack overflow. - mp_uint_t stack_size = 40000 * (sizeof(void *) / 4); - #if defined(__arm__) && !defined(__thumb2__) - // ARM (non-Thumb) architectures require more stack. - stack_size *= 2; - #endif + mp_uint_t stack_size = 40000 * UNIX_STACK_MULTIPLIER; // We should capture stack top ASAP after start, and it should be // captured guaranteedly before any other stack variables are allocated. diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index 141cd0218d93e..a41b3ec9f4701 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -31,6 +31,7 @@ #include "py/runtime.h" #include "py/mpthread.h" #include "py/gc.h" +#include "stack_size.h" #if MICROPY_PY_THREAD @@ -244,9 +245,9 @@ void mp_thread_start(void) { } mp_uint_t mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size) { - // default stack size is 8k machine-words + // default stack size if (*stack_size == 0) { - *stack_size = 8192 * sizeof(void *); + *stack_size = 32768 * UNIX_STACK_MULTIPLIER; } // minimum stack size is set by pthreads diff --git a/ports/unix/stack_size.h b/ports/unix/stack_size.h new file mode 100644 index 0000000000000..f6159bb69d529 --- /dev/null +++ b/ports/unix/stack_size.h @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Angus Gratton + * + * 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. + */ +#ifndef MICROPY_INCLUDED_UNIX_STACK_SIZE_H +#define MICROPY_INCLUDED_UNIX_STACK_SIZE_H + +#include "py/misc.h" + +// Define scaling factors for the stack size (also applies to main thread) +#ifndef UNIX_STACK_MULTIPLIER + +#if defined(__arm__) && !defined(__thumb2__) +// ARM (non-Thumb) architectures require more stack. +#define UNIX_STACK_MUL_ARM 2 +#else +#define UNIX_STACK_MUL_ARM 1 +#endif + +#if MP_SANITIZER_BUILD +// Sanitizer features consume significant stack in some cases +// This multiplier can probably be removed when using GCC 12 or newer. +#define UNIX_STACK_MUL_SANITIZERS 4 +#else +#define UNIX_STACK_MUL_SANITIZERS 1 +#endif + +// Double the stack size for 64-bit builds, plus additional scaling +#define UNIX_STACK_MULTIPLIER ((sizeof(void *) / 4) * UNIX_STACK_MUL_ARM * UNIX_STACK_MUL_SANITIZERS) + +#endif // UNIX_STACK_MULTIPLIER + +#endif // MICROPY_INCLUDED_UNIX_STACK_SIZE_H From 22deeeb8db1305b292e9146b7b00a58207755693 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 12 Aug 2025 11:03:50 +1000 Subject: [PATCH 55/60] tests/stress/recursive_iternext: Rewrite to find its own limit. Necessary on the unix port when running with sanitizers, as the newly increased stack size can run all tests at N=5000 without raising RuntimeError, and increasing N to fix this causes issues on other configurations. This way the test progressively builds a deeper data structure until it fails with RuntimeError. This is theoretically slower, but not noticeably so in reality. Signed-off-by: Angus Gratton --- tests/stress/recursive_iternext.py | 64 ++++++++++-------------------- 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/tests/stress/recursive_iternext.py b/tests/stress/recursive_iternext.py index bbc389e726237..c737f1e36d70a 100644 --- a/tests/stress/recursive_iternext.py +++ b/tests/stress/recursive_iternext.py @@ -1,4 +1,8 @@ # This tests that recursion with iternext doesn't lead to segfault. +# +# This test segfaults CPython, but that's not a bug as CPython doesn't enforce +# limits on C recursion - see +# https://github.com/python/cpython/issues/58218#issuecomment-1093570209 try: enumerate filter @@ -9,49 +13,25 @@ print("SKIP") raise SystemExit -# We need to pick an N that is large enough to hit the recursion -# limit, but not too large that we run out of heap memory. -try: - # large stack/heap, eg unix - [0] * 80000 - N = 5000 -except: - try: - # medium, eg pyboard - [0] * 10000 - N = 1000 - except: - # small, eg esp8266 - N = 100 - -try: - x = (1, 2) - for i in range(N): - x = enumerate(x) - tuple(x) -except RuntimeError: - print("RuntimeError") -try: +# Progressively build a bigger nested iterator structure (10 at a time for speed), +# and then try to evaluate it via tuple(x) which makes deep recursive function calls. +# +# Eventually this should raise a RuntimeError as MicroPython runs out of stack. +# It shouldn't ever raise a MemoryError, if it does then somehow MicroPython has +# run out of heap (for the nested structure) before running out of stack. +def recurse_iternext(nested_fn): x = (1, 2) - for i in range(N): - x = filter(None, x) - tuple(x) -except RuntimeError: - print("RuntimeError") + while True: + for _ in range(10): + x = nested_fn(x) + try: + tuple(x) + except RuntimeError: + print("RuntimeError") + break -try: - x = (1, 2) - for i in range(N): - x = map(max, x, ()) - tuple(x) -except RuntimeError: - print("RuntimeError") -try: - x = (1, 2) - for i in range(N): - x = zip(x) - tuple(x) -except RuntimeError: - print("RuntimeError") +# Test on various nested iterator structures +for nested_fn in [enumerate, lambda x: filter(None, x), lambda x: map(max, x, ()), zip]: + recurse_iternext(nested_fn) From bd413d3d853e2c1fc0382b328176ff912d79da70 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sun, 17 Aug 2025 07:28:35 +0200 Subject: [PATCH 56/60] py/asmthumb: Fix T3 encoding of conditional branches. This commit fixes the encoding of conditional branch opcodes emitted for ARMv7-M targets, when the emitter decides to use the T3 encoding for said operation. Fields J1 and J2 are now present in the generated opcode word, along with correcting some minor issues in bitmasks and shifts computation. This fixes #17940. Signed-off-by: Alessandro Gatti --- py/asmthumb.c | 5 ++--- tests/micropython/viper_large_jump.py | 20 ++++++++++++++++++++ tests/micropython/viper_large_jump.py.exp | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 tests/micropython/viper_large_jump.py create mode 100644 tests/micropython/viper_large_jump.py.exp diff --git a/py/asmthumb.c b/py/asmthumb.c index 93860d2fdc1a2..58cc7aea88085 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -267,9 +267,8 @@ bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) { #define OP_BCC_N(cond, byte_offset) (0xd000 | ((cond) << 8) | (((byte_offset) >> 1) & 0x00ff)) -// all these bit-arithmetic operations need coverage testing! -#define OP_BCC_W_HI(cond, byte_offset) (0xf000 | ((cond) << 6) | (((byte_offset) >> 10) & 0x0400) | (((byte_offset) >> 14) & 0x003f)) -#define OP_BCC_W_LO(byte_offset) (0x8000 | ((byte_offset) & 0x2000) | (((byte_offset) >> 1) & 0x0fff)) +#define OP_BCC_W_HI(cond, byte_offset) (0xf000 | ((cond) << 6) | (((byte_offset) >> 10) & 0x0400) | (((byte_offset) >> 12) & 0x003f)) +#define OP_BCC_W_LO(byte_offset) (0x8000 | (((byte_offset) >> 5) & 0x2000) | (((byte_offset) >> 8) & 0x0800) | (((byte_offset) >> 1) & 0x07ff)) bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) { mp_uint_t dest = get_label_dest(as, label); diff --git a/tests/micropython/viper_large_jump.py b/tests/micropython/viper_large_jump.py new file mode 100644 index 0000000000000..1c5913dec1ea2 --- /dev/null +++ b/tests/micropython/viper_large_jump.py @@ -0,0 +1,20 @@ +COUNT = 600 + + +try: + code = """ +@micropython.viper +def f() -> int: + x = 0 + while x < 10: +""" + for i in range(COUNT): + code += " x += 1\n" + code += " return x" + exec(code) +except MemoryError: + print("SKIP-TOO-LARGE") + raise SystemExit + + +print(f()) diff --git a/tests/micropython/viper_large_jump.py.exp b/tests/micropython/viper_large_jump.py.exp new file mode 100644 index 0000000000000..e9f960cf4ac4e --- /dev/null +++ b/tests/micropython/viper_large_jump.py.exp @@ -0,0 +1 @@ +600 From 36501966821db97feaf27a6b87086bee0eb27775 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 16:52:29 +1000 Subject: [PATCH 57/60] py/objtype: Use locals_ptr directly instead of getting it from the slot. This is a very minor code simplification, which reduces code size by about -8 bytes. It should have no functional change. Signed-off-by: Damien George --- py/objtype.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/py/objtype.c b/py/objtype.c index 25c2dee994d74..d40f619fae241 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -1292,8 +1292,7 @@ static mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals mp_raise_TypeError(MP_ERROR_TEXT("multiple bases have instance lay-out conflict")); } - mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(o, locals_dict)->map; - mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(MP_QSTR___new__), MP_MAP_LOOKUP); + mp_map_elem_t *elem = mp_map_lookup(&locals_ptr->map, MP_OBJ_NEW_QSTR(MP_QSTR___new__), MP_MAP_LOOKUP); if (elem != NULL) { // __new__ slot exists; check if it is a function if (mp_obj_is_fun(elem->value)) { From 169d382248fcec1b62891db6339c349477020bd2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 17:00:44 +1000 Subject: [PATCH 58/60] py/mpconfig: Rename MICROPY_PY___FILE__ to MICROPY_MODULE___FILE__. For consistency with other module-related configuration options. Signed-off-by: Damien George --- mpy-cross/main.c | 2 +- mpy-cross/mpconfigport.h | 2 +- ports/esp8266/mpconfigport.h | 2 +- ports/nrf/mpconfigport.h | 2 +- ports/pic16bit/mpconfigport.h | 2 +- ports/powerpc/mpconfigport.h | 2 +- ports/unix/main.c | 2 +- py/builtinimport.c | 6 +++--- py/mpconfig.h | 10 +++++----- py/objmodule.c | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 16f749ae4dc35..b7771ce6e798f 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -81,7 +81,7 @@ static int compile_and_save(const char *file, const char *output_file, const cha source_name = qstr_from_str(source_file); } - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); #endif diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index 81cbfc2eeb937..0a6478b4f3d24 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -85,7 +85,7 @@ #define MICROPY_GCREGS_SETJMP (1) #endif -#define MICROPY_PY___FILE__ (0) +#define MICROPY_MODULE___FILE__ (0) #define MICROPY_PY_ARRAY (0) #define MICROPY_PY_ATTRTUPLE (0) #define MICROPY_PY_COLLECTIONS (0) diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 323fba67f5b07..675751d2b4064 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -23,6 +23,7 @@ #define MICROPY_OPT_MATH_FACTORIAL (0) #define MICROPY_REPL_EMACS_KEYS (0) #define MICROPY_PY_BUILTINS_COMPLEX (0) +#define MICROPY_MODULE___FILE__ (0) #define MICROPY_PY_DELATTR_SETATTR (0) #define MICROPY_PY_BUILTINS_STR_CENTER (0) #define MICROPY_PY_BUILTINS_STR_PARTITION (0) @@ -32,7 +33,6 @@ #define MICROPY_PY_BUILTINS_EXECFILE (0) #define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (0) #define MICROPY_PY_BUILTINS_POW3 (0) -#define MICROPY_PY___FILE__ (0) #define MICROPY_PY_MATH_CONSTANTS (0) #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (0) #define MICROPY_PY_MATH_FACTORIAL (0) diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 963e1e8836db7..d944fc8a11e71 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -264,6 +264,7 @@ #define MICROPY_ERROR_REPORTING (2) #define MICROPY_FULL_CHECKS (1) #define MICROPY_GC_ALLOC_THRESHOLD (1) +#define MICROPY_MODULE___FILE__ (1) #define MICROPY_MODULE_GETATTR (1) #define MICROPY_MULTIPLE_INHERITANCE (1) #define MICROPY_PY_ARRAY (1) @@ -290,7 +291,6 @@ #define MICROPY_PY_STRUCT (1) #define MICROPY_PY_SYS (1) #define MICROPY_PY_SYS_PATH_ARGV_DEFAULTS (1) -#define MICROPY_PY___FILE__ (1) #endif #ifndef MICROPY_PY_UBLUEPY diff --git a/ports/pic16bit/mpconfigport.h b/ports/pic16bit/mpconfigport.h index 7e6e1c4e02b17..e95f25aa0b689 100644 --- a/ports/pic16bit/mpconfigport.h +++ b/ports/pic16bit/mpconfigport.h @@ -43,6 +43,7 @@ #define MICROPY_ENABLE_SOURCE_LINE (0) #define MICROPY_ENABLE_DOC_STRING (0) #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) +#define MICROPY_MODULE___FILE__ (0) #define MICROPY_PY_ASYNC_AWAIT (0) #define MICROPY_PY_BUILTINS_BYTEARRAY (0) #define MICROPY_PY_BUILTINS_MEMORYVIEW (0) @@ -51,7 +52,6 @@ #define MICROPY_PY_BUILTINS_SLICE (0) #define MICROPY_PY_BUILTINS_PROPERTY (0) #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) -#define MICROPY_PY___FILE__ (0) #define MICROPY_PY_GC (1) #define MICROPY_PY_ARRAY (0) #define MICROPY_PY_COLLECTIONS (0) diff --git a/ports/powerpc/mpconfigport.h b/ports/powerpc/mpconfigport.h index 25d85c9e61a72..091e94bdafdf2 100644 --- a/ports/powerpc/mpconfigport.h +++ b/ports/powerpc/mpconfigport.h @@ -57,6 +57,7 @@ #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) #define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) #define MICROPY_PY_ASYNC_AWAIT (0) +#define MICROPY_MODULE___FILE__ (0) #define MICROPY_MODULE_BUILTIN_INIT (1) #define MICROPY_PY_BUILTINS_BYTEARRAY (1) #define MICROPY_PY_BUILTINS_DICT_FROMKEYS (1) @@ -73,7 +74,6 @@ #define MICROPY_PY_BUILTINS_STR_OP_MODULO (1) #define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_MODULES (1) -#define MICROPY_PY___FILE__ (0) #define MICROPY_PY_GC (1) #define MICROPY_PY_ARRAY (1) #define MICROPY_PY_COLLECTIONS (1) diff --git a/ports/unix/main.c b/ports/unix/main.c index dd0788aaad96b..51d99ce5f1510 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -139,7 +139,7 @@ static int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu qstr source_name = lex->source_name; - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ if (input_kind == MP_PARSE_FILE_INPUT) { mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); } diff --git a/py/builtinimport.c b/py/builtinimport.c index 0611926fdd52a..a2737a03e8dc2 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -153,7 +153,7 @@ static mp_import_stat_t stat_top_level(qstr mod_name, vstr_t *dest) { #if MICROPY_MODULE_FROZEN_STR || MICROPY_ENABLE_COMPILER static void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) { - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ qstr source_name = lex->source_name; mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); #endif @@ -166,7 +166,7 @@ static void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) { #if (MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD) || MICROPY_MODULE_FROZEN_MPY static void do_execute_proto_fun(const mp_module_context_t *context, mp_proto_fun_t proto_fun, qstr source_name) { - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); #else (void)source_name; @@ -225,7 +225,7 @@ static void do_load(mp_module_context_t *module_obj, vstr_t *file) { if (frozen_type == MP_FROZEN_MPY) { const mp_frozen_module_t *frozen = modref; module_obj->constants = frozen->constants; - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ qstr frozen_file_qstr = qstr_from_str(file_str + frozen_path_prefix_len); #else qstr frozen_file_qstr = MP_QSTRnull; diff --git a/py/mpconfig.h b/py/mpconfig.h index 5fe0e822f775c..93d65f2b0e231 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -990,6 +990,11 @@ typedef time_t mp_timestamp_t; #define MICROPY_STREAMS_POSIX_API (0) #endif +// Whether to set __file__ on imported modules. +#ifndef MICROPY_MODULE___FILE__ +#define MICROPY_MODULE___FILE__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + // Whether modules can use MP_REGISTER_MODULE_DELEGATION() to delegate failed // attribute lookups to a custom handler function. #ifndef MICROPY_MODULE_ATTR_DELEGATION @@ -1422,11 +1427,6 @@ typedef time_t mp_timestamp_t; #define MICROPY_PY_BUILTINS_HELP_MODULES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif -// Whether to set __file__ for imported modules -#ifndef MICROPY_PY___FILE__ -#define MICROPY_PY___FILE__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) -#endif - // Whether to process __all__ when importing all public symbols from module #ifndef MICROPY_MODULE___ALL__ #define MICROPY_MODULE___ALL__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES) diff --git a/py/objmodule.c b/py/objmodule.c index 5ce373b83d3f1..5ee2f7dc86023 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -44,7 +44,7 @@ static void module_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kin module_name = mp_obj_str_get_str(elem->value); } - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ // If we store __file__ to imported modules then try to lookup this // symbol to give more information about the module. elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(MP_QSTR___file__), MP_MAP_LOOKUP); From 989abae12c9210a0acf966bbc71e90f285d8795c Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 17:02:01 +1000 Subject: [PATCH 59/60] py/mpconfig: Move MICROPY_MODULE___ALL__ option to other module options. Signed-off-by: Damien George --- py/mpconfig.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 93d65f2b0e231..b5483ee901199 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -990,6 +990,11 @@ typedef time_t mp_timestamp_t; #define MICROPY_STREAMS_POSIX_API (0) #endif +// Whether to process __all__ when importing all public symbols from a module. +#ifndef MICROPY_MODULE___ALL__ +#define MICROPY_MODULE___ALL__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES) +#endif + // Whether to set __file__ on imported modules. #ifndef MICROPY_MODULE___FILE__ #define MICROPY_MODULE___FILE__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) @@ -1427,11 +1432,6 @@ typedef time_t mp_timestamp_t; #define MICROPY_PY_BUILTINS_HELP_MODULES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif -// Whether to process __all__ when importing all public symbols from module -#ifndef MICROPY_MODULE___ALL__ -#define MICROPY_MODULE___ALL__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES) -#endif - // Whether to provide mem-info related functions in micropython module #ifndef MICROPY_PY_MICROPYTHON_MEM_INFO #define MICROPY_PY_MICROPYTHON_MEM_INFO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) From b5fcb33eaa682bb666c839cd4fb301175cc3564f Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 15 Aug 2025 12:56:16 +1000 Subject: [PATCH 60/60] py/mpconfig: Enable CRYPTOLIB, HASHLIB_MD5, HASHLIB_SHA1 if SSL enabled. This commit unifies the configuration of MICROPY_PY_CRYPTOLIB, MICROPY_PY_HASHLIB_MD5 and MICROPY_PY_HASHLIB_SHA1, so they are enabled by default if MICROPY_PY_SSL is enabled. This matches the existing configuration of most of the ports. With this change, all ports remain the same except: - reneses-ra now enables MICROPY_PY_CRYPTOLIB, MICROPY_PY_HASHLIB_MD5 and MICROPY_PY_HASHLIB_SHA1. - rp2 now enables MICROPY_PY_HASHLIB_MD5. Signed-off-by: Damien George --- ports/alif/mpconfigport.h | 3 --- ports/esp32/mpconfigport.h | 4 ---- ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h | 6 +----- ports/esp8266/mpconfigport.h | 1 + ports/mimxrt/mpconfigport.h | 3 --- .../rp2/boards/ARDUINO_NANO_RP2040_CONNECT/mpconfigboard.h | 3 --- ports/rp2/mpconfigport.h | 2 -- ports/stm32/mpconfigport.h | 3 --- ports/unix/variants/mpconfigvariant_common.h | 6 ------ py/mpconfig.h | 6 +++--- 10 files changed, 5 insertions(+), 32 deletions(-) diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 08b27e2786e75..6b30ea2e6245a 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -112,9 +112,6 @@ // Extended modules #define MICROPY_EPOCH_IS_1970 (1) -#define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL) -#define MICROPY_PY_HASHLIB_MD5 (MICROPY_PY_SSL) -#define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL) #define MICROPY_PY_OS_INCLUDEFILE "ports/alif/modos.c" #define MICROPY_PY_OS_DUPTERM (1) #define MICROPY_PY_OS_SEP (1) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index e47b333c73698..9b12dbd34a736 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -105,10 +105,6 @@ #define MICROPY_BLUETOOTH_NIMBLE (1) #define MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY (1) #endif -#define MICROPY_PY_HASHLIB_MD5 (1) -#define MICROPY_PY_HASHLIB_SHA1 (1) -#define MICROPY_PY_HASHLIB_SHA256 (1) -#define MICROPY_PY_CRYPTOLIB (1) #define MICROPY_PY_RANDOM_SEED_INIT_FUNC (esp_random()) #define MICROPY_PY_OS_INCLUDEFILE "ports/esp32/modos.c" #define MICROPY_PY_OS_DUPTERM (1) diff --git a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h index 1f679961e8786..cea2267c7ccd5 100644 --- a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h +++ b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h @@ -12,8 +12,6 @@ #define MICROPY_READER_VFS (MICROPY_VFS) #define MICROPY_VFS (1) -#define MICROPY_PY_CRYPTOLIB (1) - #elif defined(MICROPY_ESP8266_1M) #define MICROPY_HW_BOARD_NAME "ESP module (1M)" @@ -28,9 +26,6 @@ #define MICROPY_READER_VFS (MICROPY_VFS) #define MICROPY_VFS (1) - -#define MICROPY_PY_CRYPTOLIB (1) - #elif defined(MICROPY_ESP8266_512K) #define MICROPY_HW_BOARD_NAME "ESP module (512K)" @@ -45,6 +40,7 @@ #define MICROPY_PY_SYS_STDIO_BUFFER (0) #define MICROPY_PY_ASYNCIO (0) #define MICROPY_PY_RE_SUB (0) +#define MICROPY_PY_CRYPTOLIB (0) #define MICROPY_PY_FRAMEBUF (0) #endif diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 675751d2b4064..0321de45d7f22 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -56,6 +56,7 @@ #define MICROPY_REPL_EVENT_DRIVEN (0) #define MICROPY_USE_INTERNAL_ERRNO (1) #define MICROPY_PY_BUILTINS_HELP_TEXT esp_help_text +#define MICROPY_PY_HASHLIB_MD5 (0) #define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL && MICROPY_SSL_AXTLS) #define MICROPY_PY_RANDOM_SEED_INIT_FUNC (*WDEV_HWRNG) #define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1) diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 3cf6550d7b07b..d6694badbad95 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -145,9 +145,6 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_WEBSOCKET (MICROPY_PY_LWIP) #define MICROPY_PY_WEBREPL (MICROPY_PY_LWIP) #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) -#define MICROPY_PY_HASHLIB_MD5 (MICROPY_PY_SSL) -#define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL) -#define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL) #ifndef MICROPY_PY_NETWORK_PPP_LWIP #define MICROPY_PY_NETWORK_PPP_LWIP (MICROPY_PY_LWIP) #endif diff --git a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/mpconfigboard.h b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/mpconfigboard.h index 7783c0a17c64d..11aa663296f82 100644 --- a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/mpconfigboard.h +++ b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/mpconfigboard.h @@ -10,9 +10,6 @@ // Enable networking. #define MICROPY_PY_NETWORK (1) -// Enable MD5 hash. -#define MICROPY_PY_HASHLIB_MD5 (1) - // Disable internal error numbers. #define MICROPY_USE_INTERNAL_ERRNO (0) diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index c312293ace6e9..0c226538cda1a 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -150,8 +150,6 @@ #define MICROPY_PY_OS_URANDOM (1) #define MICROPY_PY_RE_MATCH_GROUPS (1) #define MICROPY_PY_RE_MATCH_SPAN_START_END (1) -#define MICROPY_PY_HASHLIB_SHA1 (1) -#define MICROPY_PY_CRYPTOLIB (1) #define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1) #define MICROPY_PY_TIME_TIME_TIME_NS (1) #define MICROPY_PY_TIME_INCLUDEFILE "ports/rp2/modtime.c" diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 191503cd4956f..fac261f7e2480 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -97,9 +97,6 @@ #endif // extended modules -#define MICROPY_PY_HASHLIB_MD5 (MICROPY_PY_SSL) -#define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL) -#define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL) #define MICROPY_PY_OS_INCLUDEFILE "ports/stm32/modos.c" #define MICROPY_PY_OS_DUPTERM (3) #define MICROPY_PY_OS_DUPTERM_BUILTIN_STREAM (1) diff --git a/ports/unix/variants/mpconfigvariant_common.h b/ports/unix/variants/mpconfigvariant_common.h index 65c874317666a..1ac59c95572dd 100644 --- a/ports/unix/variants/mpconfigvariant_common.h +++ b/ports/unix/variants/mpconfigvariant_common.h @@ -104,12 +104,6 @@ #define MICROPY_PY_TIME_CUSTOM_SLEEP (1) #define MICROPY_PY_TIME_INCLUDEFILE "ports/unix/modtime.c" -#if MICROPY_PY_SSL -#define MICROPY_PY_HASHLIB_MD5 (1) -#define MICROPY_PY_HASHLIB_SHA1 (1) -#define MICROPY_PY_CRYPTOLIB (1) -#endif - // The "select" module is enabled by default, but disable select.select(). #define MICROPY_PY_SELECT_POSIX_OPTIMISATIONS (1) #define MICROPY_PY_SELECT_SELECT (0) diff --git a/py/mpconfig.h b/py/mpconfig.h index b5483ee901199..877b262c8b7ae 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1860,11 +1860,11 @@ typedef time_t mp_timestamp_t; #endif #ifndef MICROPY_PY_HASHLIB_MD5 -#define MICROPY_PY_HASHLIB_MD5 (0) +#define MICROPY_PY_HASHLIB_MD5 (MICROPY_PY_SSL) #endif #ifndef MICROPY_PY_HASHLIB_SHA1 -#define MICROPY_PY_HASHLIB_SHA1 (0) +#define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL) #endif #ifndef MICROPY_PY_HASHLIB_SHA256 @@ -1872,7 +1872,7 @@ typedef time_t mp_timestamp_t; #endif #ifndef MICROPY_PY_CRYPTOLIB -#define MICROPY_PY_CRYPTOLIB (0) +#define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL) #endif // Depends on MICROPY_PY_CRYPTOLIB