From 2926001b60fb5060727f3d8034c84cfb014a5dca Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 31 May 2024 16:35:27 +1000 Subject: [PATCH 0001/1300] all: Bump version to 1.24.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 0ea52b1d8bbad..61cfe96da8027 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 23 +#define MICROPY_VERSION_MINOR 24 #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 74fb42aa82d537b6fefb463907274bd660c6ce8d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 2 Jan 2024 12:19:33 +1100 Subject: [PATCH 0002/1300] rp2: Refactor soft timer to use hardware timer alarm. Progress towards removing pico-sdk alarm pool, due to a known issue. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/main.c | 2 ++ ports/rp2/mpconfigport.h | 4 ++++ ports/rp2/mphalport.c | 28 ++++++++++++++++------------ ports/rp2/mpnetworkport.c | 2 +- ports/rp2/pendsv.h | 3 +++ shared/runtime/softtimer.h | 3 +++ 6 files changed, 29 insertions(+), 13 deletions(-) diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 40374faff92e3..668017668a28b 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -76,6 +76,8 @@ int main(int argc, char **argv) { // This is a tickless port, interrupts should always trigger SEV. SCB->SCR |= SCB_SCR_SEVONPEND_Msk; + soft_timer_init(); + #if MICROPY_HW_ENABLE_UART_REPL bi_decl(bi_program_feature("UART REPL")) setup_default_uart(); diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index a29692d0be071..6ef994bb83272 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -154,6 +154,10 @@ #define MICROPY_SSL_MBEDTLS (1) #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) +// Hardware timer alarm index. Available range 0-3. +// Number 3 is currently used by pico-sdk (PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM) +#define MICROPY_HW_SOFT_TIMER_ALARM_NUM (2) + // fatfs configuration #define MICROPY_FATFS_ENABLE_LFN (1) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index 1641eadb7b696..ac3a70822a2c6 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -35,6 +35,7 @@ #include "pendsv.h" #include "tusb.h" #include "uart.h" +#include "hardware/irq.h" #include "hardware/rtc.h" #include "pico/unique_id.h" @@ -46,8 +47,6 @@ // microseconds since the Epoch. static uint64_t time_us_64_offset_from_epoch; -static alarm_id_t soft_timer_alarm_id = 0; - #if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC #ifndef MICROPY_HW_STDIN_BUFFER_LEN @@ -273,21 +272,26 @@ uint32_t storage_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blo panic_unsupported(); } -static int64_t soft_timer_callback(alarm_id_t id, void *user_data) { - soft_timer_alarm_id = 0; - pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); - return 0; // don't reschedule this alarm -} - uint32_t soft_timer_get_ms(void) { return mp_hal_ticks_ms(); } void soft_timer_schedule_at_ms(uint32_t ticks_ms) { - if (soft_timer_alarm_id != 0) { - cancel_alarm(soft_timer_alarm_id); - } int32_t ms = soft_timer_ticks_diff(ticks_ms, mp_hal_ticks_ms()); ms = MAX(0, ms); - soft_timer_alarm_id = add_alarm_in_ms(ms, soft_timer_callback, NULL, true); + if (hardware_alarm_set_target(MICROPY_HW_SOFT_TIMER_ALARM_NUM, delayed_by_ms(get_absolute_time(), ms))) { + // "missed" hardware alarm target + hardware_alarm_force_irq(MICROPY_HW_SOFT_TIMER_ALARM_NUM); + } +} + +static void soft_timer_hardware_callback(unsigned int alarm_num) { + // The timer alarm ISR needs to call here and trigger PendSV dispatch via + // a second ISR, as PendSV may be currently suspended by the other CPU. + pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); +} + +void soft_timer_init(void) { + hardware_alarm_claim(MICROPY_HW_SOFT_TIMER_ALARM_NUM); + hardware_alarm_set_callback(MICROPY_HW_SOFT_TIMER_ALARM_NUM, soft_timer_hardware_callback); } diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c index 09a543c2db34f..4690a4e511024 100644 --- a/ports/rp2/mpnetworkport.c +++ b/ports/rp2/mpnetworkport.c @@ -66,7 +66,7 @@ static void gpio_irq_handler(void) { void cyw43_irq_init(void) { gpio_add_raw_irq_handler_with_order_priority(CYW43_PIN_WL_HOST_WAKE, gpio_irq_handler, CYW43_SHARED_IRQ_HANDLER_PRIORITY); irq_set_enabled(IO_IRQ_BANK0, true); - NVIC_SetPriority(PendSV_IRQn, PICO_LOWEST_IRQ_PRIORITY); + NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_PENDSV); } void cyw43_post_poll_hook(void) { diff --git a/ports/rp2/pendsv.h b/ports/rp2/pendsv.h index bc8e8d61c8c0f..c9bdb27637507 100644 --- a/ports/rp2/pendsv.h +++ b/ports/rp2/pendsv.h @@ -42,6 +42,9 @@ enum { #define PENDSV_DISPATCH_NUM_SLOTS PENDSV_DISPATCH_MAX +// PendSV IRQ priority, to run system-level tasks that preempt the main thread. +#define IRQ_PRI_PENDSV PICO_LOWEST_IRQ_PRIORITY + typedef void (*pendsv_dispatch_t)(void); void pendsv_suspend(void); diff --git a/shared/runtime/softtimer.h b/shared/runtime/softtimer.h index fe5d02b90787b..6921c9f47d66a 100644 --- a/shared/runtime/softtimer.h +++ b/shared/runtime/softtimer.h @@ -81,6 +81,9 @@ static inline void soft_timer_reinsert(soft_timer_entry_t *entry, uint32_t initi // pend-SV IRQ level, or equivalent. uint32_t soft_timer_get_ms(void); void soft_timer_schedule_at_ms(uint32_t ticks_ms); + +// Optional port-specific initialisation function (provided and called by the port if needed). +void soft_timer_init(void); #endif #endif // MICROPY_INCLUDED_SHARED_RUNTIME_SOFTTIMER_H From 83e82c5ad3fb9e8796d786e01509d264cfb205d3 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 2 Jan 2024 16:35:27 +1100 Subject: [PATCH 0003/1300] rp2: Refactor to not use pico-sdk alarm pool functions for sleeping. The best_effort_wfe_or_timeout() and sleep_us() pico-sdk functions use the pico-sdk alarm pool internally, and that has a bug. Some usages inside pico-sdk (notably multicore_lockout_start_blocking()) will still end up calling best_effort_wfe_or_timeout(), although usually with "end_of_time" as the timeout value so it should avoid any alarm pool race conditions. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/mphalport.c | 40 ++++++++++++++++++++++++++++++++------ ports/rp2/mphalport.h | 9 ++++----- shared/runtime/softtimer.c | 2 +- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index ac3a70822a2c6..a13a0edc30093 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -198,17 +198,31 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { return did_write ? ret : 0; } +void mp_hal_delay_us(mp_uint_t us) { + // Avoid calling sleep_us() and invoking the alarm pool by splitting long + // sleeps into an optional longer sleep and a shorter busy-wait + uint64_t end = time_us_64() + us; + if (us > 1000) { + mp_hal_delay_ms(us / 1000); + } + while (time_us_64() < end) { + // Tight loop busy-wait for accurate timing + } +} + void mp_hal_delay_ms(mp_uint_t ms) { - absolute_time_t t = make_timeout_time_ms(ms); + mp_uint_t start = mp_hal_ticks_ms(); + mp_uint_t elapsed = 0; do { - mp_event_handle_nowait(); - } while (!best_effort_wfe_or_timeout(t)); + mp_event_wait_ms(ms - elapsed); + elapsed = mp_hal_ticks_ms() - start; + } while (elapsed < ms); } void mp_hal_time_ns_set_from_rtc(void) { - // Delay at least one RTC clock cycle so it's registers have updated with the most - // recent time settings. - sleep_us(23); + // Outstanding RTC register writes need at least two RTC clock cycles to + // update. (See RP2040 datasheet section 4.8.4 "Reference clock"). + mp_hal_delay_us(44); // Sample RTC and time_us_64() as close together as possible, so the offset // calculated for the latter can be as accurate as possible. @@ -295,3 +309,17 @@ void soft_timer_init(void) { hardware_alarm_claim(MICROPY_HW_SOFT_TIMER_ALARM_NUM); hardware_alarm_set_callback(MICROPY_HW_SOFT_TIMER_ALARM_NUM, soft_timer_hardware_callback); } + +void mp_wfe_or_timeout(uint32_t timeout_ms) { + soft_timer_entry_t timer; + + // Note the timer doesn't have an associated callback, it just exists to create a + // hardware interrupt to wake the CPU + soft_timer_static_init(&timer, SOFT_TIMER_MODE_ONE_SHOT, 0, NULL); + soft_timer_insert(&timer, timeout_ms); + + __wfe(); + + // Clean up the timer node if it's not already + soft_timer_remove(&timer); +} diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h index d2d74d783a628..bc09f8c4a572f 100644 --- a/ports/rp2/mphalport.h +++ b/ports/rp2/mphalport.h @@ -62,23 +62,22 @@ if ((TIMEOUT_MS) < 0) { \ __wfe(); \ } else { \ - best_effort_wfe_or_timeout(make_timeout_time_ms(TIMEOUT_MS)); \ + mp_wfe_or_timeout(TIMEOUT_MS); \ } \ } while (0) extern int mp_interrupt_char; extern ringbuf_t stdin_ringbuf; +// Port-specific function to create a wakeup interrupt after timeout_ms and enter WFE +void mp_wfe_or_timeout(uint32_t timeout_ms); + uint32_t mp_thread_begin_atomic_section(void); void mp_thread_end_atomic_section(uint32_t); void mp_hal_set_interrupt_char(int c); void mp_hal_time_ns_set_from_rtc(void); -static inline void mp_hal_delay_us(mp_uint_t us) { - sleep_us(us); -} - static inline void mp_hal_delay_us_fast(mp_uint_t us) { busy_wait_us(us); } diff --git a/shared/runtime/softtimer.c b/shared/runtime/softtimer.c index 13a6e78777ef4..92a0d6d511100 100644 --- a/shared/runtime/softtimer.c +++ b/shared/runtime/softtimer.c @@ -89,7 +89,7 @@ void soft_timer_handler(void) { heap = (soft_timer_entry_t *)mp_pairheap_pop(soft_timer_lt, &heap->pairheap); if (entry->flags & SOFT_TIMER_FLAG_PY_CALLBACK) { mp_sched_schedule(entry->py_callback, MP_OBJ_FROM_PTR(entry)); - } else { + } else if (entry->c_callback) { entry->c_callback(entry); } if (entry->mode == SOFT_TIMER_MODE_PERIODIC) { From 3af006efb39ad0b7aa7c0401c93329b654bca617 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 10 Jan 2024 11:36:17 +1100 Subject: [PATCH 0004/1300] rp2: Support calling pendsv_suspend/resume from core 1. Previously, this was subject to races incrementing/decrementing the counter variable pendsv_lock. Technically, all that's needed here would be to make pendsv_lock an atomic counter. This implementation fulfils a stronger guarantee: it also provides mutual exclusion for the core which calls pendsv_suspend(). This is because the current use of pendsv_suspend/resume in MicroPython is to ensure exclusive access to softtimer data structures, and this does require mutual exclusion. The conceptually cleaner implementation would split the mutual exclusion part out into a softtimer-specific spinlock, but this increases the complexity and doesn't seem like it makes for a better implementation in the long run. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/main.c | 1 + ports/rp2/pendsv.c | 32 +++++++++++++++++++++++++------- ports/rp2/pendsv.h | 1 + 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 668017668a28b..5fb47cc4028ef 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -76,6 +76,7 @@ int main(int argc, char **argv) { // This is a tickless port, interrupts should always trigger SEV. SCB->SCR |= SCB_SCR_SEVONPEND_Msk; + pendsv_init(); soft_timer_init(); #if MICROPY_HW_ENABLE_UART_REPL diff --git a/ports/rp2/pendsv.c b/ports/rp2/pendsv.c index e4f662715f1c1..d24b4cb0122b3 100644 --- a/ports/rp2/pendsv.c +++ b/ports/rp2/pendsv.c @@ -25,6 +25,7 @@ */ #include +#include "pico/mutex.h" #include "py/mpconfig.h" #include "pendsv.h" #include "RP2040.h" @@ -34,15 +35,21 @@ #endif static pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS]; -static int pendsv_lock; +static recursive_mutex_t pendsv_mutex; + +void pendsv_init(void) { + recursive_mutex_init(&pendsv_mutex); +} void pendsv_suspend(void) { - pendsv_lock++; + // Recursive Mutex here as either core may call pendsv_suspend() and expect + // both mutual exclusion (other core can't enter pendsv_suspend() at the + // same time), and that no PendSV handler will run. + recursive_mutex_enter_blocking(&pendsv_mutex); } void pendsv_resume(void) { - pendsv_lock--; - assert(pendsv_lock >= 0); + recursive_mutex_exit(&pendsv_mutex); // Run pendsv if needed. Find an entry with a dispatch and call pendsv dispatch // with it. If pendsv runs it will service all slots. int count = PENDSV_DISPATCH_NUM_SLOTS; @@ -55,9 +62,11 @@ void pendsv_resume(void) { } void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { - assert(pendsv_lock >= 0); pendsv_dispatch_table[slot] = f; - if (pendsv_lock == 0) { + if (pendsv_mutex.enter_count == 0) { + // There is a race here where other core calls pendsv_suspend() before + // ISR can execute, but dispatch will happen later when other core + // calls pendsv_resume(). SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; } else { #if MICROPY_PY_NETWORK_CYW43 @@ -68,7 +77,14 @@ void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { // PendSV interrupt handler to perform background processing. void PendSV_Handler(void) { - assert(pendsv_lock == 0); + + if (!recursive_mutex_try_enter(&pendsv_mutex, NULL)) { + // Failure here means core 1 holds pendsv_mutex. ISR will + // run again after core 1 calls pendsv_resume(). + return; + } + // Core 0 should not already have locked pendsv_mutex + assert(pensv_mutex.enter_count == 1); #if MICROPY_PY_NETWORK_CYW43 CYW43_STAT_INC(PENDSV_RUN_COUNT); @@ -81,4 +97,6 @@ void PendSV_Handler(void) { f(); } } + + recursive_mutex_exit(&pendsv_mutex); } diff --git a/ports/rp2/pendsv.h b/ports/rp2/pendsv.h index c9bdb27637507..a5d9440211a10 100644 --- a/ports/rp2/pendsv.h +++ b/ports/rp2/pendsv.h @@ -47,6 +47,7 @@ enum { typedef void (*pendsv_dispatch_t)(void); +void pendsv_init(void); void pendsv_suspend(void); void pendsv_resume(void); void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f); From 3613ad96244ecaa67e8d45b637dc6b24fd2942be Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 9 May 2024 06:33:57 +0200 Subject: [PATCH 0005/1300] lib/libm: Do not force floating point type size evaluation. Since C99, `FLT_EVAL_METHOD` should be left for the compiler/libc to define. Its redefinition breaks compilation with picolibc as the target's libc, since it defines said symbol in math.h before the libm define is evaluated by the compiler. In its place, there is a check to make sure floating point type sizes are what are expected to be, triggering a compilation error if those assumptions are no longer valid. Co-authored-by: Angus Gratton Signed-off-by: Alessandro Gatti --- lib/libm/libm.h | 5 ++++- lib/libm_dbl/libm.h | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/libm/libm.h b/lib/libm/libm.h index f782249e53465..78de4c3ee4b0f 100644 --- a/lib/libm/libm.h +++ b/lib/libm/libm.h @@ -19,7 +19,10 @@ #include #include -#define FLT_EVAL_METHOD 0 +// These lines verify that FLT_EVAL_METHOD==0, MicroPython's libm requires this. +// If compilation fails here then check the host compiler's FLT_EVAL_METHOD. +typedef float float_t; +typedef double double_t; #define FORCE_EVAL(x) do { \ if (sizeof(x) == sizeof(float)) { \ diff --git a/lib/libm_dbl/libm.h b/lib/libm_dbl/libm.h index dc0b431a44857..cbae6916625e2 100644 --- a/lib/libm_dbl/libm.h +++ b/lib/libm_dbl/libm.h @@ -15,7 +15,10 @@ #include #include -#define FLT_EVAL_METHOD 0 +// These lines verify that FLT_EVAL_METHOD==0, MicroPython's libm requires this. +// If compilation fails here then check the host compiler's FLT_EVAL_METHOD. +typedef float float_t; +typedef double double_t; #define FORCE_EVAL(x) do { \ if (sizeof(x) == sizeof(float)) { \ From c98789a6d8e05acb608afe4b30cf3ca563419b2d Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Fri, 10 May 2024 22:20:11 +1000 Subject: [PATCH 0006/1300] shared/tinyusb: Add common CDC TX/RX functions. There are a few TinyUSB CDC functions used for stdio that are currently replicated across a number of ports. Not surprisingly in a couple of cases these have started to diverge slightly, with additional features added to one of them. This commit consolidates a couple of key shared functions used directly by TinyUSB based ports, and makes those functions available to all. Signed-off-by: Andrew Leech --- .../ARDUINO_NANO_ESP32/mpconfigboard.cmake | 2 +- ports/nrf/Makefile | 2 +- ports/renesas-ra/Makefile | 2 +- ports/rp2/CMakeLists.txt | 2 +- ports/samd/Makefile | 2 +- shared/tinyusb/mp_usbd_cdc.c | 153 ++++++++++++++++++ .../{mp_cdc_common.c => mp_usbd_cdc.h} | 41 ++--- 7 files changed, 168 insertions(+), 36 deletions(-) create mode 100644 shared/tinyusb/mp_usbd_cdc.c rename shared/tinyusb/{mp_cdc_common.c => mp_usbd_cdc.h} (55%) diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake index 40456f7c0b8a9..d7db192ca43f5 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake @@ -18,7 +18,7 @@ set(SDKCONFIG_DEFAULTS set(MICROPY_SOURCE_BOARD ${MICROPY_BOARD_DIR}/board_init.c ${MICROPY_BOARD_DIR}/double_tap.c - ${MICROPY_DIR}/shared/tinyusb/mp_cdc_common.c + ${MICROPY_DIR}/shared/tinyusb/mp_usbd_cdc.c ) set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 4fe921f0fad88..1691719dbb1b1 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -185,7 +185,7 @@ SRC_SHARED_C += $(addprefix shared/,\ runtime/pyexec.c \ runtime/sys_stdio_mphal.c \ runtime/interrupt_char.c \ - tinyusb/mp_cdc_common.c \ + tinyusb/mp_usbd_cdc.c \ timeutils/timeutils.c \ ) diff --git a/ports/renesas-ra/Makefile b/ports/renesas-ra/Makefile index b4b9733182bc0..3a9abf7246516 100644 --- a/ports/renesas-ra/Makefile +++ b/ports/renesas-ra/Makefile @@ -174,8 +174,8 @@ SHARED_SRC_C += $(addprefix shared/,\ runtime/stdout_helpers.c \ runtime/sys_stdio_mphal.c \ timeutils/timeutils.c \ - tinyusb/mp_cdc_common.c \ tinyusb/mp_usbd.c \ + tinyusb/mp_usbd_cdc.c \ tinyusb/mp_usbd_descriptor.c \ ) diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index fcc435b7b9f34..f86224a5c0672 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -105,8 +105,8 @@ set(MICROPY_SOURCE_LIB ${MICROPY_DIR}/shared/runtime/softtimer.c ${MICROPY_DIR}/shared/runtime/sys_stdio_mphal.c ${MICROPY_DIR}/shared/timeutils/timeutils.c - ${MICROPY_DIR}/shared/tinyusb/mp_cdc_common.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd.c + ${MICROPY_DIR}/shared/tinyusb/mp_usbd_cdc.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd_descriptor.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd_runtime.c ) diff --git a/ports/samd/Makefile b/ports/samd/Makefile index b678cd9828c6b..487a9cb38bbce 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -133,8 +133,8 @@ SHARED_SRC_C += \ shared/runtime/stdout_helpers.c \ shared/runtime/sys_stdio_mphal.c \ shared/timeutils/timeutils.c \ - shared/tinyusb/mp_cdc_common.c \ shared/tinyusb/mp_usbd.c \ + shared/tinyusb/mp_usbd_cdc.c \ shared/tinyusb/mp_usbd_descriptor.c \ shared/tinyusb/mp_usbd_runtime.c \ diff --git a/shared/tinyusb/mp_usbd_cdc.c b/shared/tinyusb/mp_usbd_cdc.c new file mode 100644 index 0000000000000..4135cd720fef5 --- /dev/null +++ b/shared/tinyusb/mp_usbd_cdc.c @@ -0,0 +1,153 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Ibrahim Abdelkader + * + * 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/runtime.h" +#include "py/mphal.h" +#include "py/mpconfig.h" +#include "py/stream.h" +#include "extmod/modmachine.h" + +#if MICROPY_HW_USB_CDC && MICROPY_HW_ENABLE_USBDEV +#include "tusb.h" +#include "device/usbd.h" + +#include "mp_usbd_cdc.h" +#include "mp_usbd.h" + +static uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll + +uintptr_t mp_usbd_cdc_poll_interfaces(uintptr_t poll_flags) { + uintptr_t ret = 0; + if (!cdc_itf_pending) { + // Explicitly run the USB stack as the scheduler may be locked (eg we are in + // an interrupt handler) while there is data pending. + mp_usbd_task(); + } + + // any CDC interfaces left to poll? + if (cdc_itf_pending && ringbuf_free(&stdin_ringbuf)) { + for (uint8_t itf = 0; itf < 8; ++itf) { + if (cdc_itf_pending & (1 << itf)) { + tud_cdc_rx_cb(itf); + if (!cdc_itf_pending) { + break; + } + } + } + } + if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { + ret |= MP_STREAM_POLL_RD; + } + if ((poll_flags & MP_STREAM_POLL_WR) && tud_cdc_connected() && tud_cdc_write_available() > 0) { + // When connected operate as blocking, only allow if space is available. + ret |= MP_STREAM_POLL_WR; + } + return ret; +} + +void tud_cdc_rx_cb(uint8_t itf) { + // consume pending USB data immediately to free usb buffer and keep the endpoint from stalling. + // in case the ringbuffer is full, mark the CDC interface that need attention later on for polling + cdc_itf_pending &= ~(1 << itf); + for (uint32_t bytes_avail = tud_cdc_n_available(itf); bytes_avail > 0; --bytes_avail) { + if (ringbuf_free(&stdin_ringbuf)) { + int data_char = tud_cdc_read_char(); + #if MICROPY_KBD_EXCEPTION + if (data_char == mp_interrupt_char) { + // Clear the ring buffer + stdin_ringbuf.iget = stdin_ringbuf.iput = 0; + // and stop + mp_sched_keyboard_interrupt(); + } else { + ringbuf_put(&stdin_ringbuf, data_char); + } + #else + ringbuf_put(&stdin_ringbuf, data_char); + #endif + } else { + cdc_itf_pending |= (1 << itf); + return; + } + } +} + +mp_uint_t mp_usbd_cdc_tx_strn(const char *str, mp_uint_t len) { + size_t i = 0; + if (tud_cdc_connected()) { + while (i < len) { + uint32_t n = len - i; + if (n > CFG_TUD_CDC_EP_BUFSIZE) { + n = CFG_TUD_CDC_EP_BUFSIZE; + } + int timeout = 0; + // Wait with a max of USC_CDC_TIMEOUT ms + while (n > tud_cdc_write_available() && timeout++ < MICROPY_HW_USB_CDC_TX_TIMEOUT) { + mp_event_wait_ms(1); + + // Explicitly run the USB stack as the scheduler may be locked (eg we + // are in an interrupt handler), while there is data pending. + mp_usbd_task(); + } + if (timeout >= MICROPY_HW_USB_CDC_TX_TIMEOUT) { + break; + } + uint32_t n2 = tud_cdc_write(str + i, n); + tud_cdc_write_flush(); + i += n2; + } + } + return i; +} + +#if MICROPY_HW_USB_CDC_1200BPS_TOUCH && MICROPY_HW_ENABLE_USBDEV + +static mp_sched_node_t mp_bootloader_sched_node; + +static void usbd_cdc_run_bootloader_task(mp_sched_node_t *node) { + mp_hal_delay_ms(250); + machine_bootloader(0, NULL); +} + +void +#if MICROPY_HW_USB_EXTERNAL_TINYUSB +mp_usbd_line_state_cb +#else +tud_cdc_line_state_cb +#endif + (uint8_t itf, bool dtr, bool rts) { + if (dtr == false && rts == false) { + // Device is disconnected. + cdc_line_coding_t line_coding; + tud_cdc_n_get_line_coding(itf, &line_coding); + if (line_coding.bit_rate == 1200) { + // Delay bootloader jump to allow the USB stack to service endpoints. + mp_sched_schedule_node(&mp_bootloader_sched_node, usbd_cdc_run_bootloader_task); + } + } +} + +#endif +#endif diff --git a/shared/tinyusb/mp_cdc_common.c b/shared/tinyusb/mp_usbd_cdc.h similarity index 55% rename from shared/tinyusb/mp_cdc_common.c rename to shared/tinyusb/mp_usbd_cdc.h index e790646f4f86d..f84cf8d03e5a2 100644 --- a/shared/tinyusb/mp_cdc_common.c +++ b/shared/tinyusb/mp_usbd_cdc.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2022 Ibrahim Abdelkader + * Copyright (c) 2022 Blake W. Felt & 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 @@ -24,37 +24,16 @@ * THE SOFTWARE. */ -#include "py/runtime.h" -#include "py/mphal.h" -#include "extmod/modmachine.h" +#ifndef MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_CDC_H +#define MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_CDC_H -#if MICROPY_HW_USB_CDC_1200BPS_TOUCH && MICROPY_HW_ENABLE_USBDEV - -#include "tusb.h" +#ifndef MICROPY_HW_USB_CDC_TX_TIMEOUT +#define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) +#endif -static mp_sched_node_t mp_bootloader_sched_node; +uintptr_t mp_usbd_cdc_poll_interfaces(uintptr_t poll_flags); +void tud_cdc_rx_cb(uint8_t itf); +mp_uint_t mp_usbd_cdc_tx_strn(const char *str, mp_uint_t len); -static void usbd_cdc_run_bootloader_task(mp_sched_node_t *node) { - mp_hal_delay_ms(250); - machine_bootloader(0, NULL); -} -void -#if MICROPY_HW_USB_EXTERNAL_TINYUSB -mp_usbd_line_state_cb -#else -tud_cdc_line_state_cb -#endif - (uint8_t itf, bool dtr, bool rts) { - if (dtr == false && rts == false) { - // Device is disconnected. - cdc_line_coding_t line_coding; - tud_cdc_n_get_line_coding(itf, &line_coding); - if (line_coding.bit_rate == 1200) { - // Delay bootloader jump to allow the USB stack to service endpoints. - mp_sched_schedule_node(&mp_bootloader_sched_node, usbd_cdc_run_bootloader_task); - } - } -} - -#endif +#endif // MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_CDC_H From 1eaa562fdf2a7472991bbfe2d2bc0e8d0b11bb28 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Fri, 10 May 2024 22:23:05 +1000 Subject: [PATCH 0007/1300] rp2/mphalport: Refactor to use shared TinyUSB CDC functions. Signed-off-by: Andrew Leech --- ports/rp2/mphalport.c | 89 ++++--------------------------------------- 1 file changed, 7 insertions(+), 82 deletions(-) diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index a13a0edc30093..3fe5357c89664 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -32,6 +32,7 @@ #include "shared/runtime/softtimer.h" #include "shared/timeutils/timeutils.h" #include "shared/tinyusb/mp_usbd.h" +#include "shared/tinyusb/mp_usbd_cdc.h" #include "pendsv.h" #include "tusb.h" #include "uart.h" @@ -58,68 +59,14 @@ ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array) }; #endif -#if MICROPY_HW_USB_CDC - -uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll - -void poll_cdc_interfaces(void) { - if (!cdc_itf_pending) { - // Explicitly run the USB stack as the scheduler may be locked (eg we are in - // an interrupt handler) while there is data pending. - mp_usbd_task(); - } - - // any CDC interfaces left to poll? - if (cdc_itf_pending && ringbuf_free(&stdin_ringbuf)) { - for (uint8_t itf = 0; itf < 8; ++itf) { - if (cdc_itf_pending & (1 << itf)) { - tud_cdc_rx_cb(itf); - if (!cdc_itf_pending) { - break; - } - } - } - } -} - -void tud_cdc_rx_cb(uint8_t itf) { - // consume pending USB data immediately to free usb buffer and keep the endpoint from stalling. - // in case the ringbuffer is full, mark the CDC interface that need attention later on for polling - cdc_itf_pending &= ~(1 << itf); - for (uint32_t bytes_avail = tud_cdc_n_available(itf); bytes_avail > 0; --bytes_avail) { - if (ringbuf_free(&stdin_ringbuf)) { - int data_char = tud_cdc_read_char(); - if (data_char == mp_interrupt_char) { - mp_sched_keyboard_interrupt(); - } else { - ringbuf_put(&stdin_ringbuf, data_char); - } - } else { - cdc_itf_pending |= (1 << itf); - return; - } - } -} - -#endif - uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { uintptr_t ret = 0; #if MICROPY_HW_USB_CDC - poll_cdc_interfaces(); + ret |= mp_usbd_cdc_poll_interfaces(poll_flags); #endif - #if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC - if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { - ret |= MP_STREAM_POLL_RD; - } + #if MICROPY_HW_ENABLE_UART_REPL if (poll_flags & MP_STREAM_POLL_WR) { - #if MICROPY_HW_ENABLE_UART_REPL ret |= MP_STREAM_POLL_WR; - #else - if (tud_cdc_connected() && tud_cdc_write_available() > 0) { - ret |= MP_STREAM_POLL_WR; - } - #endif } #endif #if MICROPY_PY_OS_DUPTERM @@ -132,7 +79,7 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { int mp_hal_stdin_rx_chr(void) { for (;;) { #if MICROPY_HW_USB_CDC - poll_cdc_interfaces(); + mp_usbd_cdc_poll_interfaces(0); #endif int c = ringbuf_get(&stdin_ringbuf); @@ -159,32 +106,10 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { #endif #if MICROPY_HW_USB_CDC - if (tud_cdc_connected()) { - size_t i = 0; - while (i < len) { - uint32_t n = len - i; - if (n > CFG_TUD_CDC_EP_BUFSIZE) { - n = CFG_TUD_CDC_EP_BUFSIZE; - } - int timeout = 0; - // Wait with a max of USC_CDC_TIMEOUT ms - while (n > tud_cdc_write_available() && timeout++ < MICROPY_HW_USB_CDC_TX_TIMEOUT) { - mp_event_wait_ms(1); - - // Explicitly run the USB stack as the scheduler may be locked (eg we - // are in an interrupt handler), while there is data pending. - mp_usbd_task(); - } - if (timeout >= MICROPY_HW_USB_CDC_TX_TIMEOUT) { - ret = i; - break; - } - uint32_t n2 = tud_cdc_write(str + i, n); - tud_cdc_write_flush(); - i += n2; - } - ret = MIN(i, ret); + mp_uint_t cdc_res = mp_usbd_cdc_tx_strn(str, len); + if (cdc_res > 0) { did_write = true; + ret = MIN(cdc_res, ret); } #endif From 2475a52f5cb8dfdf62a5ca5fe0eb9eeb14fc61b9 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Sat, 11 May 2024 08:50:13 +1000 Subject: [PATCH 0008/1300] mimxrt/mphalport: Refactor to use shared TinyUSB CDC functions. Signed-off-by: Andrew Leech --- ports/mimxrt/Makefile | 2 ++ ports/mimxrt/mpconfigport.h | 1 + ports/mimxrt/mphalport.c | 72 ++++--------------------------------- ports/mimxrt/mphalport.h | 1 + 4 files changed, 10 insertions(+), 66 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 96abb11bff433..75cf9caec962c 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -232,6 +232,8 @@ SHARED_SRC_C += \ shared/runtime/stdout_helpers.c \ shared/runtime/sys_stdio_mphal.c \ shared/timeutils/timeutils.c \ + shared/tinyusb/mp_usbd.c \ + shared/tinyusb/mp_usbd_cdc.c \ # Set flash driver name, base address and internal flash flag, based on the flash type. ifeq ($(MICROPY_HW_FLASH_TYPE),$(filter $(MICROPY_HW_FLASH_TYPE),qspi_nor_flash)) diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index a4c6d6e2060b9..ac918ba4da8d5 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -148,6 +148,7 @@ uint32_t trng_random_u32(void); #endif #define MICROPY_HW_ENABLE_USBDEV (1) +#define MICROPY_HW_USB_CDC (1) // Hooks to add builtins diff --git a/ports/mimxrt/mphalport.c b/ports/mimxrt/mphalport.c index 6e982b8c8e245..be5abd95bec54 100644 --- a/ports/mimxrt/mphalport.c +++ b/ports/mimxrt/mphalport.c @@ -30,6 +30,7 @@ #include "py/mphal.h" #include "shared/timeutils/timeutils.h" #include "shared/runtime/interrupt_char.h" +#include "shared/tinyusb/mp_usbd_cdc.h" #include "extmod/misc.h" #include "ticks.h" #include "tusb.h" @@ -44,51 +45,9 @@ static uint8_t stdin_ringbuf_array[MICROPY_HW_STDIN_BUFFER_LEN]; ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, 0}; -uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll - -void poll_cdc_interfaces(void) { - // any CDC interfaces left to poll? - if (cdc_itf_pending && ringbuf_free(&stdin_ringbuf)) { - for (uint8_t itf = 0; itf < 8; ++itf) { - if (cdc_itf_pending & (1 << itf)) { - tud_cdc_rx_cb(itf); - if (!cdc_itf_pending) { - break; - } - } - } - } -} - - -void tud_cdc_rx_cb(uint8_t itf) { - // consume pending USB data immediately to free usb buffer and keep the endpoint from stalling. - // in case the ringbuffer is full, mark the CDC interface that need attention later on for polling - cdc_itf_pending &= ~(1 << itf); - for (uint32_t bytes_avail = tud_cdc_n_available(itf); bytes_avail > 0; --bytes_avail) { - if (ringbuf_free(&stdin_ringbuf)) { - int data_char = tud_cdc_read_char(); - if (data_char == mp_interrupt_char) { - mp_sched_keyboard_interrupt(); - } else { - ringbuf_put(&stdin_ringbuf, data_char); - } - } else { - cdc_itf_pending |= (1 << itf); - return; - } - } -} - uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { uintptr_t ret = 0; - poll_cdc_interfaces(); - if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { - ret |= MP_STREAM_POLL_RD; - } - if ((poll_flags & MP_STREAM_POLL_WR) && tud_cdc_connected() && tud_cdc_write_available() > 0) { - ret |= MP_STREAM_POLL_WR; - } + ret |= mp_usbd_cdc_poll_interfaces(poll_flags); #if MICROPY_PY_OS_DUPTERM ret |= mp_os_dupterm_poll(poll_flags); #endif @@ -97,7 +56,7 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { int mp_hal_stdin_rx_chr(void) { for (;;) { - poll_cdc_interfaces(); + mp_usbd_cdc_poll_interfaces(0); int c = ringbuf_get(&stdin_ringbuf); if (c != -1) { return c; @@ -115,29 +74,10 @@ int mp_hal_stdin_rx_chr(void) { mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { mp_uint_t ret = len; bool did_write = false; - if (tud_cdc_connected()) { - size_t i = 0; - while (i < len) { - uint32_t n = len - i; - if (n > CFG_TUD_CDC_EP_BUFSIZE) { - n = CFG_TUD_CDC_EP_BUFSIZE; - } - uint64_t timeout = ticks_us64() + (uint64_t)(MICROPY_HW_USB_CDC_TX_TIMEOUT * 1000); - // Wait with a max of USC_CDC_TIMEOUT ms - while (n > tud_cdc_write_available() && ticks_us64() < timeout) { - MICROPY_EVENT_POLL_HOOK - } - if (ticks_us64() >= timeout) { - ret = i; - break; - } - - uint32_t n2 = tud_cdc_write(str + i, n); - tud_cdc_write_flush(); - i += n2; - } + mp_uint_t cdc_res = mp_usbd_cdc_tx_strn(str, len); + if (cdc_res > 0) { did_write = true; - ret = MIN(i, ret); + ret = MIN(cdc_res, ret); } #if MICROPY_PY_OS_DUPTERM int dupterm_res = mp_os_dupterm_tx_strn(str, len); diff --git a/ports/mimxrt/mphalport.h b/ports/mimxrt/mphalport.h index 7f5a228fada9f..c69ebf58106b2 100644 --- a/ports/mimxrt/mphalport.h +++ b/ports/mimxrt/mphalport.h @@ -72,6 +72,7 @@ #define MP_HAL_PIN_TRIGGER_RISE kGPIO_IntRisingEdge #define MP_HAL_PIN_TRIGGER_RISE_FALL kGPIO_IntRisingOrFallingEdge +extern int mp_interrupt_char; extern ringbuf_t stdin_ringbuf; // Define an alias for systick_ms, because the shared softtimer.c uses From 2d33071b171e2de4a3e04f48d1f19b5bbe87c663 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Sat, 11 May 2024 08:53:21 +1000 Subject: [PATCH 0009/1300] renesas-ra/mphalport: Refactor to use shared TinyUSB CDC functions. Signed-off-by: Andrew Leech --- ports/renesas-ra/mphalport.c | 76 ++++-------------------------------- ports/renesas-ra/mphalport.h | 1 + 2 files changed, 8 insertions(+), 69 deletions(-) diff --git a/ports/renesas-ra/mphalport.c b/ports/renesas-ra/mphalport.c index 7a5bcba2c9128..1c62c23dd70be 100644 --- a/ports/renesas-ra/mphalport.c +++ b/ports/renesas-ra/mphalport.c @@ -34,6 +34,7 @@ #include "py/ringbuf.h" #include "extmod/misc.h" #include "shared/runtime/interrupt_char.h" +#include "shared/tinyusb/mp_usbd_cdc.h" #include "tusb.h" #include "uart.h" @@ -64,62 +65,16 @@ NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { mp_raise_OSError(mp_hal_status_to_errno_table[status]); } -#if MICROPY_HW_USB_CDC - uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll -void poll_cdc_interfaces(void) { - // any CDC interfaces left to poll? - if (cdc_itf_pending && ringbuf_free(&stdin_ringbuf)) { - for (uint8_t itf = 0; itf < 8; ++itf) { - if (cdc_itf_pending & (1 << itf)) { - tud_cdc_rx_cb(itf); - if (!cdc_itf_pending) { - break; - } - } - } - } -} - -void tud_cdc_rx_cb(uint8_t itf) { - // consume pending USB data immediately to free usb buffer and keep the endpoint from stalling. - // in case the ringbuffer is full, mark the CDC interface that need attention later on for polling - cdc_itf_pending &= ~(1 << itf); - for (uint32_t bytes_avail = tud_cdc_n_available(itf); bytes_avail > 0; --bytes_avail) { - if (ringbuf_free(&stdin_ringbuf)) { - int data_char = tud_cdc_read_char(); - if (data_char == mp_interrupt_char) { - mp_sched_keyboard_interrupt(); - } else { - ringbuf_put(&stdin_ringbuf, data_char); - } - } else { - cdc_itf_pending |= (1 << itf); - return; - } - } -} - -#endif - uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { uintptr_t ret = 0; #if MICROPY_HW_USB_CDC - poll_cdc_interfaces(); + ret |= mp_usbd_cdc_poll_interfaces(poll_flags); #endif - #if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC - if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { - ret |= MP_STREAM_POLL_RD; - } + #if MICROPY_HW_ENABLE_UART_REPL if (poll_flags & MP_STREAM_POLL_WR) { - #if MICROPY_HW_ENABLE_UART_REPL ret |= MP_STREAM_POLL_WR; - #else - if (tud_cdc_connected() && tud_cdc_write_available() > 0) { - ret |= MP_STREAM_POLL_WR; - } - #endif } #endif #if MICROPY_PY_OS_DUPTERM @@ -132,7 +87,7 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { int mp_hal_stdin_rx_chr(void) { for (;;) { #if MICROPY_HW_USB_CDC - poll_cdc_interfaces(); + mp_usbd_cdc_poll_interfaces(0); #endif #if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE @@ -165,27 +120,10 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { #endif #if MICROPY_HW_USB_CDC - if (tud_cdc_connected()) { - size_t i = 0; - while (i < len) { - uint32_t n = len - i; - if (n > CFG_TUD_CDC_EP_BUFSIZE) { - n = CFG_TUD_CDC_EP_BUFSIZE; - } - int timeout = 0; - // Wait with a max of USC_CDC_TIMEOUT ms - while (n > tud_cdc_write_available() && timeout++ < MICROPY_HW_USB_CDC_TX_TIMEOUT) { - MICROPY_EVENT_POLL_HOOK - } - if (timeout >= MICROPY_HW_USB_CDC_TX_TIMEOUT) { - break; - } - uint32_t n2 = tud_cdc_write(str + i, n); - tud_cdc_write_flush(); - i += n2; - } - ret = MIN(i, ret); + mp_uint_t cdc_res = mp_usbd_cdc_tx_strn(str, len); + if (cdc_res > 0) { did_write = true; + ret = MIN(cdc_res, ret); } #endif diff --git a/ports/renesas-ra/mphalport.h b/ports/renesas-ra/mphalport.h index c5d27f2e43e9d..14e6ee0c3eea6 100644 --- a/ports/renesas-ra/mphalport.h +++ b/ports/renesas-ra/mphalport.h @@ -42,6 +42,7 @@ #define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) extern const unsigned char mp_hal_status_to_errno_table[4]; +extern int mp_interrupt_char; extern ringbuf_t stdin_ringbuf; static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) { From c11efc74ee72021987a75f1b17d1375d592db1a5 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Sat, 11 May 2024 08:53:44 +1000 Subject: [PATCH 0010/1300] samd/mphalport: Refactor to use shared TinyUSB CDC functions. Signed-off-by: Andrew Leech --- ports/samd/mphalport.c | 80 ++++-------------------------------------- ports/samd/mphalport.h | 1 + 2 files changed, 7 insertions(+), 74 deletions(-) diff --git a/ports/samd/mphalport.c b/ports/samd/mphalport.c index 3fc6875908975..80904804c9980 100644 --- a/ports/samd/mphalport.c +++ b/ports/samd/mphalport.c @@ -30,6 +30,7 @@ #include "py/stream.h" #include "shared/runtime/interrupt_char.h" #include "shared/tinyusb/mp_usbd.h" +#include "shared/tinyusb/mp_usbd_cdc.h" #include "extmod/misc.h" #include "samd_soc.h" #include "tusb.h" @@ -51,48 +52,6 @@ ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, mp_usbd_task(); \ } while (0) -uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll - -void poll_cdc_interfaces(void) { - // any CDC interfaces left to poll? - if (cdc_itf_pending && ringbuf_free(&stdin_ringbuf)) { - for (uint8_t itf = 0; itf < 8; ++itf) { - if (cdc_itf_pending & (1 << itf)) { - tud_cdc_rx_cb(itf); - if (!cdc_itf_pending) { - break; - } - } - } - } -} - -void tud_cdc_rx_cb(uint8_t itf) { - // consume pending USB data immediately to free usb buffer and keep the endpoint from stalling. - // in case the ringbuffer is full, mark the CDC interface that need attention later on for polling - cdc_itf_pending &= ~(1 << itf); - for (uint32_t bytes_avail = tud_cdc_n_available(itf); bytes_avail > 0; --bytes_avail) { - if (ringbuf_free(&stdin_ringbuf)) { - int data_char = tud_cdc_read_char(); - #if MICROPY_KBD_EXCEPTION - if (data_char == mp_interrupt_char) { - // Clear the ring buffer - stdin_ringbuf.iget = stdin_ringbuf.iput = 0; - // and stop - mp_sched_keyboard_interrupt(); - } else { - ringbuf_put(&stdin_ringbuf, data_char); - } - #else - ringbuf_put(&stdin_ringbuf, data_char); - #endif - } else { - cdc_itf_pending |= (1 << itf); - return; - } - } -} - void mp_hal_set_pin_mux(mp_hal_pin_obj_t pin, uint8_t mux) { int pin_grp = pin / 32; int port_grp = (pin % 32) / 2; @@ -165,16 +124,7 @@ uint64_t mp_hal_ticks_us_64(void) { uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { uintptr_t ret = 0; - - poll_cdc_interfaces(); - if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { - ret |= MP_STREAM_POLL_RD; - } - - if ((poll_flags & MP_STREAM_POLL_WR) && tud_cdc_connected() && tud_cdc_write_available() > 0) { - ret |= MP_STREAM_POLL_WR; - } - + ret |= mp_usbd_cdc_poll_interfaces(poll_flags); #if MICROPY_PY_OS_DUPTERM ret |= mp_os_dupterm_poll(poll_flags); #endif @@ -184,7 +134,7 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { int mp_hal_stdin_rx_chr(void) { for (;;) { - poll_cdc_interfaces(); + mp_usbd_cdc_poll_interfaces(0); int c = ringbuf_get(&stdin_ringbuf); if (c != -1) { return c; @@ -203,28 +153,10 @@ int mp_hal_stdin_rx_chr(void) { mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { mp_uint_t ret = len; bool did_write = false; - if (tud_cdc_connected()) { - size_t i = 0; - while (i < len) { - uint32_t n = len - i; - if (n > CFG_TUD_CDC_EP_BUFSIZE) { - n = CFG_TUD_CDC_EP_BUFSIZE; - } - int timeout = 0; - // Wait with a max of USC_CDC_TIMEOUT ms - while (n > tud_cdc_write_available() && timeout++ < MICROPY_HW_USB_CDC_TX_TIMEOUT) { - MICROPY_EVENT_POLL_HOOK_WITH_USB; - } - if (timeout >= MICROPY_HW_USB_CDC_TX_TIMEOUT) { - ret = i; - break; - } - uint32_t n2 = tud_cdc_write(str + i, n); - tud_cdc_write_flush(); - i += n2; - } - ret = MIN(i, ret); + mp_uint_t cdc_res = mp_usbd_cdc_tx_strn(str, len); + if (cdc_res > 0) { did_write = true; + ret = MIN(cdc_res, ret); } #if MICROPY_PY_OS_DUPTERM int dupterm_res = mp_os_dupterm_tx_strn(str, len); diff --git a/ports/samd/mphalport.h b/ports/samd/mphalport.h index 48f047066d43e..ee1ebf7aea2b0 100644 --- a/ports/samd/mphalport.h +++ b/ports/samd/mphalport.h @@ -44,6 +44,7 @@ #define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) extern int mp_interrupt_char; +extern ringbuf_t stdin_ringbuf; extern volatile uint32_t systick_ms; uint64_t mp_hal_ticks_us_64(void); From c1a6b95bf2653b5bf4a9ad9fed84610e749e40f9 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 24 May 2024 14:28:00 +1000 Subject: [PATCH 0011/1300] github/ISSUE_TEMPLATE: Update issue form to remove checklist generation. Update to the issue forms added earlier this year, that seem to generally be working well. Improvements in this commit: - No longer generates TODO checklists in new issues. - Issue bodies (and therefore email previews) no longer start with the same fixed checklist text for each new issue. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- .github/ISSUE_TEMPLATE/bug_report.yml | 30 ++++++++------- .github/ISSUE_TEMPLATE/documentation.yml | 28 +++++++------- .github/ISSUE_TEMPLATE/feature_request.yml | 43 +++++++++++++--------- .github/ISSUE_TEMPLATE/security.yml | 31 ++++++++-------- 4 files changed, 73 insertions(+), 59 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index c44a140faf0ff..1ec6c7067f9cd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -11,19 +11,10 @@ body: * If you have a question \"How Do I ...?\", please post it on [GitHub Discussions](https://github.com/orgs/micropython/discussions/) or [Discord](https://discord.gg/RB8HZSAExQ) instead of here. * For missing or incorrect documentation, or feature requests, then please [choose a different issue type](https://github.com/micropython/micropython/issues/new/choose). - - type: checkboxes - id: terms - attributes: - label: Checks - description: | - Before submitting your bug report, please go over these check points: - options: - - label: | - I agree to follow the MicroPython [Code of Conduct](https://github.com/micropython/micropython/blob/master/CODEOFCONDUCT.md) to ensure a safe and respectful space for everyone. - required: true - - label: | - I've searched for [existing issues](https://github.com/micropython/micropython/issues) matching this bug, and didn't find any. - required: true + + #### Existing issue? + + * Please search for [existing issues](https://github.com/micropython/micropython/issues) matching this bug before reporting. - type: input id: port-board-hw attributes: @@ -33,7 +24,7 @@ body: placeholder: | esp32 port, ESP32-Fantastic board. validations: - required: true + required: true - type: textarea id: version attributes: @@ -101,6 +92,17 @@ body: description: | Is there anything else that might help to resolve this issue? value: No, I've provided everything above. + - type: dropdown + id: code-of-conduct + attributes: + label: Code of Conduct + description: | + Do you agree to follow the MicroPython [Code of Conduct](https://github.com/micropython/micropython/blob/master/CODEOFCONDUCT.md) to ensure a safe and respectful space for everyone? + options: + - "Yes, I agree" + multiple: true + validations: + required: true - type: markdown attributes: value: | diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml index 5be0241701747..93051e51c80c7 100644 --- a/.github/ISSUE_TEMPLATE/documentation.yml +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -9,19 +9,10 @@ body: This form is for reporting issues with the documentation or examples provided with MicroPython. If you have a general question \"How Do I ...?\", please post it on [GitHub Discussions](https://github.com/orgs/micropython/discussions/) or [Discord](https://discord.gg/RB8HZSAExQ) instead of here. - - type: checkboxes - id: terms - attributes: - label: Checks - description: | - Before submitting your bug report, please go over these check points: - options: - - label: | - I agree to follow the MicroPython [Code of Conduct](https://github.com/micropython/micropython/blob/master/CODEOFCONDUCT.md) to ensure a safe and respectful space for everyone. - required: true - - label: | - I've searched for [existing issues](https://github.com/micropython/micropython/issues) and didn't find any that matched. - required: true + + #### Existing issue? + + * Please search for [existing issues](https://github.com/micropython/micropython/issues) before reporting a new one. - type: input id: page attributes: @@ -38,6 +29,17 @@ body: Please describe what was missing from the documentation and/or what was incorrect/incomplete. validations: required: true + - type: dropdown + id: code-of-conduct + attributes: + label: Code of Conduct + description: | + Do you agree to follow the MicroPython [Code of Conduct](https://github.com/micropython/micropython/blob/master/CODEOFCONDUCT.md) to ensure a safe and respectful space for everyone? + options: + - "Yes, I agree" + multiple: true + validations: + required: true - type: markdown attributes: value: | diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 845fbed810ef9..7d5162a32a4af 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -15,19 +15,10 @@ body: * If you have a question \"How Do I ...?\", please post it on GitHub Discussions or Discord instead of here. * Could this feature be implemented as a pure Python library? If so, please open the request on the [micropython-lib repository](https://github.com/micropython/micropython-lib/issues) instead. - - type: checkboxes - id: terms - attributes: - label: Checks - description: | - Before submitting your feature request, please go over these check points: - options: - - label: | - I agree to follow the MicroPython [Code of Conduct](https://github.com/micropython/micropython/blob/master/CODEOFCONDUCT.md) to ensure a safe and respectful space for everyone. - required: true - - label: | - I've searched for [existing issues](https://github.com/micropython/micropython/issues) regarding this feature, and didn't find any. - required: true + + #### Existing issue? + + * Please search for [existing issues](https://github.com/micropython/micropython/issues) before opening a new one. - type: textarea id: feature attributes: @@ -51,14 +42,32 @@ body: MicroPython aims to strike a balance between functionality and code size. Can this feature be optionally enabled? If you believe the usefulness of this feature would outweigh the additional code size, please explain. (It's OK to say you're unsure here, we're happy to discuss this with you.) - - type: checkboxes + - type: dropdown id: implementation attributes: label: Implementation + description: | + What is your suggestion for implementing this feature? + + (See also: [How to sponsor](https://github.com/sponsors/micropython#sponsors), [How to submit a Pull Request](https://github.com/micropython/micropython/wiki/ContributorGuidelines).) options: - - label: I intend to implement this feature and would submit a Pull Request if desirable. - - label: I hope the MicroPython maintainers or community will implement this feature. - - label: I would like to [Sponsor](https://github.com/sponsors/micropython#sponsors) development of this feature. + - I hope the MicroPython maintainers or community will implement this feature + - I intend to implement this feature and would submit a Pull Request if desirable + - I would like to sponsor development of this feature + multiple: true + validations: + required: true + - type: dropdown + id: code-of-conduct + attributes: + label: Code of Conduct + description: | + Do you agree to follow the MicroPython [Code of Conduct](https://github.com/micropython/micropython/blob/master/CODEOFCONDUCT.md) to ensure a safe and respectful space for everyone? + options: + - "Yes, I agree" + multiple: true + validations: + required: true - type: markdown attributes: value: | diff --git a/.github/ISSUE_TEMPLATE/security.yml b/.github/ISSUE_TEMPLATE/security.yml index 7d66a72f64120..57c2a5885ed85 100644 --- a/.github/ISSUE_TEMPLATE/security.yml +++ b/.github/ISSUE_TEMPLATE/security.yml @@ -9,21 +9,11 @@ body: 1. For issues that are readily exploitable or have high impact, please email contact@micropython.org instead. 1. If this is a question about security, please ask it in [Discussions](https://github.com/orgs/micropython/discussions/) or [Discord](https://discord.gg/RB8HZSAExQ) instead. - - type: checkboxes - id: terms - attributes: - label: Checks - description: | - Before submitting your bug report, please go over these check points: - options: - - label: | - I agree to follow the MicroPython [Code of Conduct](https://github.com/micropython/micropython/blob/master/CODEOFCONDUCT.md) to ensure a safe and respectful space for everyone. - required: true - - label: I wish to report a specific security issue that is **not readily exploitable and does not have high impact** for MicroPython developers or users. - required: true - - label: | - I've searched for [existing issues](https://github.com/micropython/micropython/issues) and didn't find any that matched. - required: true + + #### Existing issue? + + * Please search for [existing issues](https://github.com/micropython/micropython/issues) before reporting a new one. + - type: input id: port-board-hw attributes: @@ -57,3 +47,14 @@ body: * How does the attacker exploit this issue? validations: required: true + - type: dropdown + id: code-of-conduct + attributes: + label: Code of Conduct + description: | + Do you agree to follow the MicroPython [Code of Conduct](https://github.com/micropython/micropython/blob/master/CODEOFCONDUCT.md) to ensure a safe and respectful space for everyone? + options: + - "Yes, I agree" + multiple: true + validations: + required: true From 84a8f7ea6dfa3529b024d31fa25bb91153bbfc35 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 2 Jun 2024 22:16:29 +1000 Subject: [PATCH 0012/1300] shared/tinyusb: Allow ports to use 1200bps-touch without other CDC code. This fixes the build for some esp32 and nrf boards (for example `ARDUINO_NANO_33_BLE_SENSE` and `ARDUINO_NANO_ESP32`) due to commit c98789a6d8e05acb608afe4b30cf3ca563419b2d. Changes are: - Allow the CDC TX/RX functions in `mp_usbd_cdc.c` to be enabled separately to those needed for `MICROPY_HW_USB_CDC_1200BPS_TOUCH`. - Add `MICROPY_EXCLUDE_SHARED_TINYUSB_USBD_CDC` option as a temporary workaround for the nrf port to use. - Declare `mp_usbd_line_state_cb()` in a header as a public function. - Fix warning with type cast of `.callback_line_state_changed`. Signed-off-by: Damien George --- ports/esp32/boards/ARDUINO_NANO_ESP32/board_init.c | 2 +- ports/esp32/usb.c | 2 +- ports/nrf/mpconfigport.h | 1 + shared/tinyusb/mp_usbd_cdc.c | 12 +++++------- shared/tinyusb/mp_usbd_cdc.h | 3 +++ 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/board_init.c b/ports/esp32/boards/ARDUINO_NANO_ESP32/board_init.c index ab03139d9055f..69a414cd5d9e3 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/board_init.c +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/board_init.c @@ -26,6 +26,7 @@ #include #include "py/mphal.h" +#include "shared/tinyusb/mp_usbd_cdc.h" #include #include @@ -87,7 +88,6 @@ void NANO_ESP32_enter_bootloader(void) { } void NANO_ESP32_usb_callback_line_state_changed(int itf, void *event_in) { - extern void mp_usbd_line_state_cb(uint8_t itf, bool dtr, bool rts); cdcacm_event_t *event = event_in; mp_usbd_line_state_cb(itf, event->line_state_changed_data.dtr, event->line_state_changed_data.rts); } diff --git a/ports/esp32/usb.c b/ports/esp32/usb.c index 316482e1898a0..2a3a2595dea0d 100644 --- a/ports/esp32/usb.c +++ b/ports/esp32/usb.c @@ -79,7 +79,7 @@ void usb_init(void) { .callback_rx_wanted_char = &MICROPY_HW_USB_CUSTOM_RX_WANTED_CHAR_CB, #endif #ifdef MICROPY_HW_USB_CUSTOM_LINE_STATE_CB - .callback_line_state_changed = &MICROPY_HW_USB_CUSTOM_LINE_STATE_CB, + .callback_line_state_changed = (tusb_cdcacm_callback_t)&MICROPY_HW_USB_CUSTOM_LINE_STATE_CB, #endif #ifdef MICROPY_HW_USB_CUSTOM_LINE_CODING_CB .callback_line_coding_changed = &MICROPY_HW_USB_CUSTOM_LINE_CODING_CB, diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 6e92fa876ef4b..0b9b667eeaf31 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -335,6 +335,7 @@ long unsigned int rng_generate_random_word(void); #if MICROPY_HW_USB_CDC #include "device/usbd.h" #define MICROPY_HW_USBDEV_TASK_HOOK extern void tud_task(void); tud_task(); +#define MICROPY_EXCLUDE_SHARED_TINYUSB_USBD_CDC (1) #else #define MICROPY_HW_USBDEV_TASK_HOOK ; #endif diff --git a/shared/tinyusb/mp_usbd_cdc.c b/shared/tinyusb/mp_usbd_cdc.c index 4135cd720fef5..6d789ff5e81ca 100644 --- a/shared/tinyusb/mp_usbd_cdc.c +++ b/shared/tinyusb/mp_usbd_cdc.c @@ -26,16 +26,13 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "py/mpconfig.h" #include "py/stream.h" #include "extmod/modmachine.h" -#if MICROPY_HW_USB_CDC && MICROPY_HW_ENABLE_USBDEV -#include "tusb.h" -#include "device/usbd.h" - -#include "mp_usbd_cdc.h" #include "mp_usbd.h" +#include "mp_usbd_cdc.h" + +#if MICROPY_HW_USB_CDC && MICROPY_HW_ENABLE_USBDEV && !MICROPY_EXCLUDE_SHARED_TINYUSB_USBD_CDC static uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll @@ -122,6 +119,8 @@ mp_uint_t mp_usbd_cdc_tx_strn(const char *str, mp_uint_t len) { return i; } +#endif + #if MICROPY_HW_USB_CDC_1200BPS_TOUCH && MICROPY_HW_ENABLE_USBDEV static mp_sched_node_t mp_bootloader_sched_node; @@ -150,4 +149,3 @@ tud_cdc_line_state_cb } #endif -#endif diff --git a/shared/tinyusb/mp_usbd_cdc.h b/shared/tinyusb/mp_usbd_cdc.h index f84cf8d03e5a2..1abe0fa0a41b3 100644 --- a/shared/tinyusb/mp_usbd_cdc.h +++ b/shared/tinyusb/mp_usbd_cdc.h @@ -35,5 +35,8 @@ uintptr_t mp_usbd_cdc_poll_interfaces(uintptr_t poll_flags); void tud_cdc_rx_cb(uint8_t itf); mp_uint_t mp_usbd_cdc_tx_strn(const char *str, mp_uint_t len); +#if MICROPY_HW_USB_EXTERNAL_TINYUSB +void mp_usbd_line_state_cb(uint8_t itf, bool dtr, bool rts); +#endif #endif // MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_CDC_H From 93394da69c46324b53465abaee05919d543a754a Mon Sep 17 00:00:00 2001 From: Matthias Blankertz Date: Fri, 31 May 2024 18:49:59 +0200 Subject: [PATCH 0013/1300] rp2/modmachine: Use atomic section macros in lightsleep code. To avoid undefined references to `mp_thread_begin_atomic_section()` / `mp_thread_end_atomic_section()`, replace them with the `MICROPY_BEGIN_ATOMIC_SECTION` / `MICROPY_END_ATOMIC_SECTION` macros. That way, it's possible to build again with `MICROPY_PY_THREAD` disabled (made possible by efa54c27b9eab3b61319e7f16d05db0ac3b6bc14). Fixes commit 19844b4983066c5771cb60a341c3fce52bca2a87. Signed-off-by: Matthias Blankertz --- ports/rp2/modmachine.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 5551bf723a797..e6a8542d77307 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -127,10 +127,10 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { const uint32_t xosc_hz = XOSC_MHZ * 1000000; - uint32_t my_interrupts = mp_thread_begin_atomic_section(); + uint32_t my_interrupts = MICROPY_BEGIN_ATOMIC_SECTION(); #if MICROPY_PY_NETWORK_CYW43 if (cyw43_has_pending && cyw43_poll != NULL) { - mp_thread_end_atomic_section(my_interrupts); + MICROPY_END_ATOMIC_SECTION(my_interrupts); return; } #endif @@ -196,7 +196,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { // Bring back all clocks. clocks_init(); - mp_thread_end_atomic_section(my_interrupts); + MICROPY_END_ATOMIC_SECTION(my_interrupts); } NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { From a84c7a0ed931667cda44a8e27888f066e085d5eb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 24 May 2024 15:57:01 +1000 Subject: [PATCH 0014/1300] rp2/modmachine: Selectively leave the USB clocks enabled in lightsleep. Without this change going to lightsleep stops the USB peripheral clock, and can lead to either the device going into a weird state or the host deciding to issue a bus reset. This change only keeps the USB peripheral clocks enabled if the USB device is currently active and a host has configured the device. This means the USB device continues to respond to host transfers and (presumably) will even complete pending endpoint transfers. All other requests are NAKed while still asleep, but the interaction with the host seems to resume correctly on wake Otherwise, if USB is not active or configured by a host, USB clocks are disabled, the same as before. With the change, one can issue a `machine.lightsleep(...)` with USB CDC connected and the USB CDC remains connected during the sleep and resumes when the lightsleep finishes. Tested on a RPi Pico, the power consumption is: - During normal idle at the REPL, about 15.3mA. - During lightsleep, prior to this change, about 1.35mA. - During lightsleep, with this change and USB CDC connected, about 3.7mA. If power consumption should be as low as possible when USB is connected, one can use `machine.USBDevice` to disable the USB before entering lightsleep. As discussed at https://github.com/orgs/micropython/discussions/14401 This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/modmachine.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index e6a8542d77307..4eebb3d16c64e 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -28,6 +28,7 @@ // extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE. #include "py/mphal.h" +#include "mp_usbd.h" #include "modmachine.h" #include "uart.h" #include "hardware/clocks.h" @@ -134,8 +135,17 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { return; } #endif - // Disable USB and ADC clocks. - clock_stop(clk_usb); + + #if MICROPY_HW_ENABLE_USBDEV + // Only disable the USB clock if a USB host has not configured the device + bool disable_usb = !tud_mounted(); + #else + bool disable_usb = true; + #endif + if (disable_usb) { + clock_stop(clk_usb); + } + clock_stop(clk_adc); // CLK_REF = XOSC @@ -152,7 +162,9 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { // Disable PLLs. pll_deinit(pll_sys); - pll_deinit(pll_usb); + if (disable_usb) { + pll_deinit(pll_usb); + } // Disable ROSC. rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB; @@ -181,6 +193,12 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { // TODO: Use RTC alarm to wake. clocks_hw->sleep_en1 = 0; } + + if (!disable_usb) { + clocks_hw->sleep_en0 |= CLOCKS_SLEEP_EN0_CLK_SYS_PLL_USB_BITS; + clocks_hw->sleep_en1 |= CLOCKS_SLEEP_EN1_CLK_USB_USBCTRL_BITS; + } + scb_hw->scr |= M0PLUS_SCR_SLEEPDEEP_BITS; __wfi(); scb_hw->scr &= ~M0PLUS_SCR_SLEEPDEEP_BITS; From 932f76c6ba64c5a3e68de3324556d9979f09303b Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 27 Feb 2024 10:19:35 +0000 Subject: [PATCH 0015/1300] rp2/CMakeLists: Use MICROPY_BOARD_DIR to find pins.csv. Assuming that ${MICROPY_PORT_DIR}/boards/${MICROPY_BOARD} is equal to ${MICROPY_BOARD_DIR} is not valid, because the latter could point to a path outside the main MicroPython repository. Replace this path with the canonical ${MICROPY_BOARD_DIR} so that pins.csv is correctly located when building against out-of-tree board definitions. Additionally remove MICROPY_BOARDS_DIR to discourage similar mistakes. Signed-off-by: Phil Howard --- ports/rp2/CMakeLists.txt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index f86224a5c0672..d3ecee586054c 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -530,15 +530,14 @@ endforeach() # Include the main MicroPython cmake rules. include(${MICROPY_DIR}/py/mkrules.cmake) -set(MICROPY_BOARDS_DIR "${MICROPY_PORT_DIR}/boards") -set(GEN_PINS_AF_CSV "${MICROPY_BOARDS_DIR}/rp2_af.csv") -set(GEN_PINS_PREFIX "${MICROPY_BOARDS_DIR}/rp2_prefix.c") -set(GEN_PINS_MKPINS "${MICROPY_BOARDS_DIR}/make-pins.py") +set(GEN_PINS_AF_CSV "${MICROPY_PORT_DIR}/boards/rp2_af.csv") +set(GEN_PINS_PREFIX "${MICROPY_PORT_DIR}/boards/rp2_prefix.c") +set(GEN_PINS_MKPINS "${MICROPY_PORT_DIR}/boards/make-pins.py") set(GEN_PINS_SRC "${CMAKE_BINARY_DIR}/pins_${MICROPY_BOARD}.c") set(GEN_PINS_HDR "${MICROPY_GENHDR_DIR}/pins.h") -if(EXISTS "${MICROPY_BOARDS_DIR}/${MICROPY_BOARD}/pins.csv") - set(GEN_PINS_BOARD_CSV "${MICROPY_BOARDS_DIR}/${MICROPY_BOARD}/pins.csv") +if(EXISTS "${MICROPY_BOARD_DIR}/pins.csv") + set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_DIR}/pins.csv") set(GEN_PINS_CSV_ARG --board-csv "${GEN_PINS_BOARD_CSV}") endif() From 0e19286c944d12a0eb7b15f004e368a09affbccd Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 4 Jun 2024 09:52:31 +1000 Subject: [PATCH 0016/1300] tools/metrics.py: Change rp2 board selection to RPI_PICO_W. This tool is used to compute size differences in the firmware (eg as part of CI), but it doesn't currently check any firmware that has bare-metal lwIP/networking, making it hard to see how firmware size changes when networking related changes are made. So, change the board selection for the rp2 port to RPI_PICO_W. Changes in size to standard RPI_PICO firmware will be very similar to other bare-metal boards like PYBV10. Signed-off-by: Damien George --- tools/ci.sh | 2 +- tools/metrics.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/ci.sh b/tools/ci.sh index bac225caa4df1..b0746178b6689 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -55,7 +55,7 @@ function ci_code_size_setup { function ci_code_size_build { # check the following ports for the change in their code size PORTS_TO_CHECK=bmusxpd - SUBMODULES="lib/asf4 lib/berkeley-db-1.xx lib/mbedtls lib/micropython-lib lib/nxp_driver lib/pico-sdk lib/stm32lib lib/tinyusb" + SUBMODULES="lib/asf4 lib/berkeley-db-1.xx lib/btstack lib/cyw43-driver lib/lwip lib/mbedtls lib/micropython-lib lib/nxp_driver lib/pico-sdk lib/stm32lib lib/tinyusb" # starts off at either the ref/pull/N/merge FETCH_HEAD, or the current branch HEAD git checkout -b pull_request # save the current location diff --git a/tools/metrics.py b/tools/metrics.py index 9c5ed1d7d5a60..2064675be9f5c 100755 --- a/tools/metrics.py +++ b/tools/metrics.py @@ -69,7 +69,7 @@ def __init__(self, name, dir, output, make_flags=None): "x": PortData("mimxrt", "mimxrt", "build-TEENSY40/firmware.elf"), "e": PortData("renesas-ra", "renesas-ra", "build-EK_RA6M2/firmware.elf"), "r": PortData("nrf", "nrf", "build-PCA10040/firmware.elf"), - "p": PortData("rp2", "rp2", "build-RPI_PICO/firmware.elf"), + "p": PortData("rp2", "rp2", "build-RPI_PICO_W/firmware.elf", "BOARD=RPI_PICO_W"), "d": PortData("samd", "samd", "build-ADAFRUIT_ITSYBITSY_M4_EXPRESS/firmware.elf"), } From 1f23ab1e3dabbbe1812f60dfeb5301d887954ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20D=C3=B6rre?= Date: Wed, 27 Mar 2024 21:53:34 +0000 Subject: [PATCH 0017/1300] esp32,mimxrt,stm32: Implement ipconfig() for more network interfaces. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements: - esp32: network.ipconfig() - esp32: network.LAN.ipconfig() - esp32: network.WLAN.ipconfig() - mimxrt: network.LAN.ipconfig() - stm32: network.LAN.ipconfig() Signed-off-by: Felix Dörre --- ports/esp32/modnetwork.h | 2 + ports/esp32/modnetwork_globals.h | 1 + ports/esp32/network_common.c | 172 ++++++++++++++++++++++++++++++- ports/esp32/network_lan.c | 1 + ports/esp32/network_wlan.c | 1 + ports/mimxrt/network_lan.c | 7 ++ ports/stm32/network_lan.c | 7 ++ 7 files changed, 190 insertions(+), 1 deletion(-) diff --git a/ports/esp32/modnetwork.h b/ports/esp32/modnetwork.h index e57b80657f58f..387f961976d60 100644 --- a/ports/esp32/modnetwork.h +++ b/ports/esp32/modnetwork.h @@ -54,6 +54,8 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_get_wlan_obj); MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_get_lan_obj); MP_DECLARE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_ifconfig_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(esp_nic_ipconfig_obj); MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_config_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj); diff --git a/ports/esp32/modnetwork_globals.h b/ports/esp32/modnetwork_globals.h index 7dd2ee9e66e7a..c0cf229421daf 100644 --- a/ports/esp32/modnetwork_globals.h +++ b/ports/esp32/modnetwork_globals.h @@ -11,6 +11,7 @@ { MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_make_new_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_network_phy_mode_obj) }, +{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_network_ipconfig_obj) }, #if MICROPY_PY_NETWORK_WLAN { MP_ROM_QSTR(MP_QSTR_STA_IF), MP_ROM_INT(WIFI_IF_STA)}, diff --git a/ports/esp32/network_common.c b/ports/esp32/network_common.c index 223803a5d14ad..6e957909eafe7 100644 --- a/ports/esp32/network_common.c +++ b/ports/esp32/network_common.c @@ -34,6 +34,7 @@ #include #include "py/runtime.h" +#include "py/parsenum.h" #include "py/mperrno.h" #include "shared/netutils/netutils.h" #include "modnetwork.h" @@ -42,7 +43,7 @@ #include "esp_netif.h" #include "esp_wifi.h" #include "lwip/sockets.h" -// #include "lwip/dns.h" +#include "lwip/dns.h" NORETURN void esp_exceptions_helper(esp_err_t e) { switch (e) { @@ -154,6 +155,175 @@ static mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_ifconfig_obj, 1, 2, esp_ifconfig); +static mp_obj_t esp_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + if (kwargs->used == 0) { + // Get config value + if (n_args != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("must query one param")); + } + + switch (mp_obj_str_get_qstr(args[0])) { + case MP_QSTR_dns: { + char addr_str[IPADDR_STRLEN_MAX]; + ipaddr_ntoa_r(dns_getserver(0), addr_str, sizeof(addr_str)); + return mp_obj_new_str(addr_str, strlen(addr_str)); + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } else { + // Set config value(s) + if (n_args != 0) { + mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); + } + + for (size_t i = 0; i < kwargs->alloc; ++i) { + if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { + mp_map_elem_t *e = &kwargs->table[i]; + switch (mp_obj_str_get_qstr(e->key)) { + case MP_QSTR_dns: { + ip_addr_t dns; + size_t addr_len; + const char *addr_str = mp_obj_str_get_data(e->value, &addr_len); + if (!ipaddr_aton(addr_str, &dns)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments as dns server")); + } + dns_setserver(0, &dns); + break; + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } + } + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj, 0, esp_network_ipconfig); + +static mp_obj_t esp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + base_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + esp_netif_ip_info_t info; + esp_netif_get_ip_info(self->netif, &info); + + if (kwargs->used == 0) { + // Get config value + if (n_args != 2) { + mp_raise_TypeError(MP_ERROR_TEXT("must query one param")); + } + + switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_dhcp4: { + if (self->if_id == ESP_IF_WIFI_STA || self->if_id == ESP_IF_ETH) { + esp_netif_dhcp_status_t status; + esp_exceptions(esp_netif_dhcpc_get_status(self->netif, &status)); + return mp_obj_new_bool(status == ESP_NETIF_DHCP_STARTED); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + case MP_QSTR_addr4: { + mp_obj_t tuple[2] = { + netutils_format_ipv4_addr((uint8_t *)&info.ip, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t *)&info.netmask, NETUTILS_BIG), + }; + return mp_obj_new_tuple(2, tuple); + } + case MP_QSTR_gw4: { + return netutils_format_ipv4_addr((uint8_t *)&info.gw, NETUTILS_BIG); + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + return mp_const_none; + } else { + // Set config value(s) + if (n_args != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); + } + int touched_ip_info = 0; + for (size_t i = 0; i < kwargs->alloc; ++i) { + if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { + mp_map_elem_t *e = &kwargs->table[i]; + switch (mp_obj_str_get_qstr(e->key)) { + case MP_QSTR_dhcp4: { + esp_netif_dhcp_status_t status; + if (self->if_id == ESP_IF_WIFI_STA || self->if_id == ESP_IF_ETH) { + esp_exceptions(esp_netif_dhcpc_get_status(self->netif, &status)); + if (mp_obj_is_true(e->value) && status != ESP_NETIF_DHCP_STARTED) { + esp_exceptions(esp_netif_dhcpc_start(self->netif)); + } else if (!mp_obj_is_true(e->value) && status == ESP_NETIF_DHCP_STARTED) { + esp_exceptions(esp_netif_dhcpc_stop(self->netif)); + } + } else { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + break; + } + case MP_QSTR_addr4: { + if (e->value != mp_const_none && mp_obj_is_str(e->value)) { + size_t addr_len; + const char *input_str = mp_obj_str_get_data(e->value, &addr_len); + char *split = strchr(input_str, '/'); + if (split) { + mp_obj_t prefix_obj = mp_parse_num_integer(split + 1, strlen(split + 1), 10, NULL); + int prefix_bits = mp_obj_get_int(prefix_obj); + uint32_t mask = -(1u << (32 - prefix_bits)); + uint32_t *m = (uint32_t *)&info.netmask; + *m = esp_netif_htonl(mask); + } + netutils_parse_ipv4_addr(e->value, (void *)&info.ip, NETUTILS_BIG); + } else if (e->value != mp_const_none) { + mp_obj_t *items; + mp_obj_get_array_fixed_n(e->value, 2, &items); + netutils_parse_ipv4_addr(items[0], (void *)&info.ip, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[1], (void *)&info.netmask, NETUTILS_BIG); + } + touched_ip_info = 1; + break; + } + case MP_QSTR_gw4: { + netutils_parse_ipv4_addr(e->value, (void *)&info.gw, NETUTILS_BIG); + touched_ip_info = 1; + break; + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } + } + if (self->if_id == ESP_IF_WIFI_STA || self->if_id == ESP_IF_ETH) { + if (touched_ip_info) { + esp_err_t e = esp_netif_dhcpc_stop(self->netif); + if (e != ESP_OK && e != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) { + esp_exceptions_helper(e); + } + esp_exceptions(esp_netif_set_ip_info(self->netif, &info)); + } + } else if (self->if_id == ESP_IF_WIFI_AP) { + esp_err_t e = esp_netif_dhcps_stop(self->netif); + if (e != ESP_OK && e != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) { + esp_exceptions_helper(e); + } + esp_exceptions(esp_netif_set_ip_info(self->netif, &info)); + esp_exceptions(esp_netif_dhcps_start(self->netif)); + } + + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(esp_nic_ipconfig_obj, 1, esp_ipconfig); + mp_obj_t esp_ifname(esp_netif_t *netif) { char ifname[NETIF_NAMESIZE + 1] = {0}; mp_obj_t ret = mp_const_none; diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c index 570c9a4afdefb..7e7ebcc929185 100644 --- a/ports/esp32/network_lan.c +++ b/ports/esp32/network_lan.c @@ -404,6 +404,7 @@ static const mp_rom_map_elem_t lan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&lan_status_obj) }, { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&lan_config_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&esp_network_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_nic_ipconfig_obj) }, }; static MP_DEFINE_CONST_DICT(lan_if_locals_dict, lan_if_locals_dict_table); diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index eb2bcda03e1b8..6d051f0256291 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -728,6 +728,7 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_wlan_isconnected_obj) }, { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_wlan_config_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&esp_network_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_nic_ipconfig_obj) }, // Constants { MP_ROM_QSTR(MP_QSTR_IF_STA), MP_ROM_INT(WIFI_IF_STA)}, diff --git a/ports/mimxrt/network_lan.c b/ports/mimxrt/network_lan.c index e01079cbf3144..7d6ae2d7277fb 100644 --- a/ports/mimxrt/network_lan.c +++ b/ports/mimxrt/network_lan.c @@ -179,6 +179,12 @@ static mp_obj_t network_lan_ifconfig(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_lan_ifconfig_obj, 1, 2, network_lan_ifconfig); +static mp_obj_t network_lan_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); + return mod_network_nic_ipconfig(eth_netif(self->eth), n_args - 1, args + 1, kwargs); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(network_lan_ipconfig_obj, 1, network_lan_ipconfig); + static mp_obj_t network_lan_status(size_t n_args, const mp_obj_t *args) { network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); (void)self; @@ -241,6 +247,7 @@ static const mp_rom_map_elem_t network_lan_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&network_lan_active_obj) }, { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_lan_isconnected_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_lan_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_lan_ipconfig_obj) }, { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_lan_status_obj) }, { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_lan_config_obj) }, diff --git a/ports/stm32/network_lan.c b/ports/stm32/network_lan.c index 27f3e1337e693..0ef33e29777f9 100644 --- a/ports/stm32/network_lan.c +++ b/ports/stm32/network_lan.c @@ -101,6 +101,12 @@ static mp_obj_t network_lan_ifconfig(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_lan_ifconfig_obj, 1, 2, network_lan_ifconfig); +static mp_obj_t network_lan_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); + return mod_network_nic_ipconfig(eth_netif(self->eth), n_args - 1, args + 1, kwargs); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(network_lan_ipconfig_obj, 1, network_lan_ipconfig); + static mp_obj_t network_lan_status(size_t n_args, const mp_obj_t *args) { network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); (void)self; @@ -163,6 +169,7 @@ static const mp_rom_map_elem_t network_lan_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&network_lan_active_obj) }, { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_lan_isconnected_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_lan_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_lan_ipconfig_obj) }, { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_lan_status_obj) }, { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_lan_config_obj) }, From 7e7cc2b427f588559d50bae35d49c305201c2365 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 28 Mar 2024 08:03:41 +0100 Subject: [PATCH 0018/1300] extmod/network_ninaw10: Implement the ipconfig methods for ninaw10. This implements network.ipconfig() and network.WLAN.ipconfig() when the ninaw10 driver is used for WLAN. Due to a omission in the ninaw10 driver stack, setting the DNS address has no effect. But the interface is kept here just in case it's fixed eventually. dhcp4 and has_dhcp4 are dummy arguments. Ninaw10 seems to always use DHCP. Signed-off-by: robert-hh --- extmod/modnetwork.c | 5 +- extmod/modnetwork.h | 4 + extmod/network_ninaw10.c | 151 +++++++++++++++++++++++++++++++++++++ shared/netutils/netutils.c | 8 +- 4 files changed, 166 insertions(+), 2 deletions(-) diff --git a/extmod/modnetwork.c b/extmod/modnetwork.c index aa237bd93c15f..88f9d37180bcd 100644 --- a/extmod/modnetwork.c +++ b/extmod/modnetwork.c @@ -144,12 +144,15 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_network_hostname_obj, 0, 1, mod_n #if LWIP_VERSION_MAJOR >= 2 MP_DEFINE_CONST_FUN_OBJ_KW(mod_network_ipconfig_obj, 0, mod_network_ipconfig); #endif +#if MICROPY_PY_NETWORK_NINAW10 +MP_DEFINE_CONST_FUN_OBJ_KW(mod_network_ipconfig_obj, 0, network_ninaw10_ipconfig); +#endif static const mp_rom_map_elem_t mp_module_network_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_network) }, { MP_ROM_QSTR(MP_QSTR_country), MP_ROM_PTR(&mod_network_country_obj) }, { MP_ROM_QSTR(MP_QSTR_hostname), MP_ROM_PTR(&mod_network_hostname_obj) }, - #if LWIP_VERSION_MAJOR >= 2 + #if LWIP_VERSION_MAJOR >= 2 || MICROPY_PY_NETWORK_NINAW10 { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&mod_network_ipconfig_obj) }, #endif diff --git a/extmod/modnetwork.h b/extmod/modnetwork.h index 2ff9ce09dea18..1a4aa7797e455 100644 --- a/extmod/modnetwork.h +++ b/extmod/modnetwork.h @@ -83,6 +83,10 @@ extern int mp_mod_network_prefer_dns_use_ip_version; #endif #elif defined(MICROPY_PORT_NETWORK_INTERFACES) +#if MICROPY_PY_NETWORK_NINAW10 +mp_obj_t network_ninaw10_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); +#endif + struct _mod_network_socket_obj_t; typedef struct _mod_network_nic_protocol_t { diff --git a/extmod/network_ninaw10.c b/extmod/network_ninaw10.c index 926e228a35b88..702adcd9762e5 100644 --- a/extmod/network_ninaw10.c +++ b/extmod/network_ninaw10.c @@ -41,6 +41,7 @@ #include "py/runtime.h" #include "py/misc.h" #include "py/mperrno.h" +#include "py/parsenum.h" #include "shared/netutils/netutils.h" #include "shared/runtime/softtimer.h" #include "extmod/modnetwork.h" @@ -75,6 +76,8 @@ typedef struct _nina_obj_t { #define SO_NO_CHECK (0x100a) #define NINAW10_POLL_INTERVAL (100) +#define IPADDR_STRLEN_MAX 46 + #define is_nonblocking_error(errno) ((errno) == MP_EAGAIN || (errno) == MP_EWOULDBLOCK || (errno) == MP_EINPROGRESS) #define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__) @@ -87,6 +90,8 @@ static mp_sched_node_t mp_wifi_poll_node; static soft_timer_entry_t mp_wifi_poll_timer; static void network_ninaw10_deinit(void); +static bool network_ninaw10_dhcp_active = false; + static bool network_ninaw10_poll_list_is_empty(void) { return MP_STATE_PORT(mp_wifi_poll_list) == NULL || MP_STATE_PORT(mp_wifi_poll_list)->len == 0; @@ -199,6 +204,7 @@ static mp_obj_t network_ninaw10_active(size_t n_args, const mp_obj_t *args) { mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("failed to initialize Nina-W10 module, error: %d"), error); } + network_ninaw10_dhcp_active = true; // check firmware version uint8_t semver[NINA_FW_VER_LEN]; if (nina_fw_version(semver) != 0) { @@ -367,11 +373,155 @@ static mp_obj_t network_ninaw10_ifconfig(size_t n_args, const mp_obj_t *args) { netutils_parse_ipv4_addr(items[2], ifconfig.gateway_addr, NETUTILS_BIG); netutils_parse_ipv4_addr(items[3], ifconfig.dns_addr, NETUTILS_BIG); nina_ifconfig(&ifconfig, true); + network_ninaw10_dhcp_active = false; return mp_const_none; } } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ninaw10_ifconfig_obj, 1, 2, network_ninaw10_ifconfig); +mp_obj_t network_ninaw10_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + nina_ifconfig_t ifconfig; + // get ifconfig info + nina_ifconfig(&ifconfig, false); + + if (kwargs->used == 0) { + // Get config value + if (n_args != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("must query one param")); + } + switch (mp_obj_str_get_qstr(args[0])) { + case MP_QSTR_dns: { + return netutils_format_ipv4_addr(ifconfig.dns_addr, NETUTILS_BIG); + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } else { + // Set config value(s) + if (n_args != 0) { + mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); + } + + for (size_t i = 0; i < kwargs->alloc; ++i) { + if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { + mp_map_elem_t *e = &kwargs->table[i]; + switch (mp_obj_str_get_qstr(e->key)) { + case MP_QSTR_dns: { + netutils_parse_ipv4_addr(e->value, ifconfig.dns_addr, NETUTILS_BIG); + nina_ifconfig(&ifconfig, true); + break; + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } + } + } + return mp_const_none; +} + +static mp_obj_t network_ninaw10_nic_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + nina_ifconfig_t ifconfig; + // get ifconfig info + nina_ifconfig(&ifconfig, false); + + if (kwargs->used == 0) { + // Get config value + if (n_args != 2) { + mp_raise_TypeError(MP_ERROR_TEXT("must query one param")); + } + + switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_dhcp4: { + return mp_obj_new_bool(network_ninaw10_dhcp_active); + } + case MP_QSTR_has_dhcp4: { + uint16_t ip_sum = + ifconfig.ip_addr[0] + ifconfig.ip_addr[1] + ifconfig.ip_addr[2] + ifconfig.ip_addr[3]; + if (network_ninaw10_dhcp_active) { + return mp_obj_new_bool(ip_sum != 0); + } else { + return mp_const_false; + } + } + case MP_QSTR_addr4: { + mp_obj_t tuple[2] = { + netutils_format_ipv4_addr(ifconfig.ip_addr, NETUTILS_BIG), + netutils_format_ipv4_addr(ifconfig.subnet_addr, NETUTILS_BIG), + }; + return mp_obj_new_tuple(2, tuple); + } + case MP_QSTR_gw4: { + return netutils_format_ipv4_addr(ifconfig.gateway_addr, NETUTILS_BIG); + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + return mp_const_none; + } else { + // Set config value(s) + if (n_args != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); + } + + for (size_t i = 0; i < kwargs->alloc; ++i) { + if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { + mp_map_elem_t *e = &kwargs->table[i]; + switch (mp_obj_str_get_qstr(e->key)) { + case MP_QSTR_dhcp4: { + mp_raise_ValueError(MP_ERROR_TEXT("DHCP control unsupported")); + break; + } + case MP_QSTR_addr4: { + int prefix_bits = 32; + if (e->value != mp_const_none && mp_obj_is_str(e->value)) { + size_t addr_len; + const char *input_str = mp_obj_str_get_data(e->value, &addr_len); + char *split = strchr(input_str, '/'); + if (split) { + mp_obj_t prefix_obj = mp_parse_num_integer(split + 1, strlen(split + 1), 10, NULL); + prefix_bits = mp_obj_get_int(prefix_obj); + uint32_t mask = -(1u << (32 - prefix_bits)); + ifconfig.subnet_addr[0] = (mask >> 24) & 0xFF; + ifconfig.subnet_addr[1] = (mask >> 16) & 0xFF; + ifconfig.subnet_addr[2] = (mask >> 8) & 0xFF; + ifconfig.subnet_addr[3] = mask & 0xFF; + } + netutils_parse_ipv4_addr(e->value, ifconfig.ip_addr, NETUTILS_BIG); + } else if (e->value != mp_const_none) { + mp_obj_t *items; + mp_obj_get_array_fixed_n(e->value, 2, &items); + netutils_parse_ipv4_addr(items[0], ifconfig.ip_addr, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[1], ifconfig.subnet_addr, NETUTILS_BIG); + } + nina_ifconfig(&ifconfig, true); + network_ninaw10_dhcp_active = false; + break; + } + case MP_QSTR_gw4: { + netutils_parse_ipv4_addr(e->value, ifconfig.gateway_addr, NETUTILS_BIG); + nina_ifconfig(&ifconfig, true); + network_ninaw10_dhcp_active = false; + break; + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } + } + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ninaw10_nic_ipconfig_obj, 1, network_ninaw10_nic_ipconfig); + static mp_obj_t network_ninaw10_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { nina_obj_t *self = MP_OBJ_TO_PTR(args[0]); (void)self; @@ -856,6 +1006,7 @@ static const mp_rom_map_elem_t nina_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&network_ninaw10_disconnect_obj) }, { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_ninaw10_isconnected_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_ninaw10_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_ninaw10_nic_ipconfig_obj) }, { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_ninaw10_config_obj) }, { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_ninaw10_status_obj) }, { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&network_ninaw10_ioctl_obj) }, diff --git a/shared/netutils/netutils.c b/shared/netutils/netutils.c index 84b4405c41d38..cd1422f7c8058 100644 --- a/shared/netutils/netutils.c +++ b/shared/netutils/netutils.c @@ -63,7 +63,13 @@ void netutils_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian return; } const char *s = addr_str; - const char *s_top = addr_str + addr_len; + const char *s_top; + // Scan for the end of valid address characters + for (s_top = addr_str; s_top < addr_str + addr_len; s_top++) { + if (!(*s_top == '.' || (*s_top >= '0' && *s_top <= '9'))) { + break; + } + } for (mp_uint_t i = 3; ; i--) { mp_uint_t val = 0; for (; s < s_top && *s != '.'; s++) { From a71471be66300eb9415f05e030cd92649f810b34 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 28 Mar 2024 13:19:51 +0100 Subject: [PATCH 0019/1300] extmod/network_lwip: Allow using the CIDR notation for addr4. There was a little omisssion in the code. Signed-off-by: robert-hh --- extmod/network_lwip.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/extmod/network_lwip.c b/extmod/network_lwip.c index 97cb43902df63..329a45c46fb99 100644 --- a/extmod/network_lwip.c +++ b/extmod/network_lwip.c @@ -297,20 +297,23 @@ mp_obj_t mod_network_nic_ipconfig(struct netif *netif, size_t n_args, const mp_o char plain_ip[IPADDR_STRLEN_MAX]; char *split = strchr(input_str, '/'); const char *addr_str = input_str; + int to_copy = MIN(sizeof(plain_ip) - 1, addr_len); + memcpy(plain_ip, addr_str, to_copy); if (split) { - int to_copy = sizeof(plain_ip) - 1; if (split - addr_str < to_copy) { to_copy = split - addr_str; } - memcpy(plain_ip, addr_str, to_copy); mp_obj_t prefix_obj = mp_parse_num_integer(split + 1, strlen(split + 1), 10, NULL); prefix_bits = mp_obj_get_int(prefix_obj); + if (mp_obj_str_get_qstr(args[0]) == MP_QSTR_addr4) { + uint32_t mask = -(1u << (32 - prefix_bits)); + ip_addr_set_ip4_u32_val(netmask, ((mask & 0xFF) << 24) | ((mask & 0xFF00) << 8) | ((mask >> 8) & 0xFF00) | ((mask >> 24) & 0xFF)); + } + } else { + netmask = netif->netmask; } - if (mp_obj_str_get_qstr(args[0]) == MP_QSTR_addr4) { - uint32_t mask = -(1u << (32 - prefix_bits)); - ip_addr_set_ip4_u32_val(netmask, ((mask & 0xFF) << 24) | ((mask & 0xFF00) << 8) | ((mask >> 8) & 0xFF00) | ((mask >> 24) & 0xFF)); - } - if (!ipaddr_aton(addr_str, &ip_addr)) { + plain_ip[to_copy] = '\0'; + if (!ipaddr_aton(plain_ip, &ip_addr)) { mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments")); } if ((mp_obj_str_get_qstr(args[0]) == MP_QSTR_addr6) != IP_IS_V6(&ip_addr) From b555d6ccafe2ff93bc6372aa322e31a69c56ee40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20D=C3=B6rre?= Date: Thu, 28 Mar 2024 17:06:10 +0000 Subject: [PATCH 0020/1300] esp32/network_ppp: Implement network.PPP.ipconfig method. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Felix Dörre --- ports/esp32/network_ppp.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index 4e9b2a32ca0b6..47a9b72dc15cf 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -266,6 +266,42 @@ static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_ifconfig_obj, 1, 2, ppp_ifconfig); +static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + if (kwargs->used == 0) { + ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (self->pcb == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("PPP not active")); + } + struct netif *netif = ppp_netif(self->pcb); + // Get config value + if (n_args != 2) { + mp_raise_TypeError(MP_ERROR_TEXT("must query one param")); + } + + switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_addr4: { + mp_obj_t tuple[2] = { + netutils_format_ipv4_addr((uint8_t *)&netif->ip_addr, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t *)&netif->netmask, NETUTILS_BIG), + }; + return mp_obj_new_tuple(2, tuple); + } + case MP_QSTR_gw4: { + return netutils_format_ipv4_addr((uint8_t *)&netif->gw, NETUTILS_BIG); + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + return mp_const_none; + } else { + mp_raise_TypeError(MP_ERROR_TEXT("setting properties not supported")); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(ppp_ipconfig_obj, 1, ppp_ipconfig); + static mp_obj_t ppp_status(mp_obj_t self_in) { return mp_const_none; } @@ -328,6 +364,7 @@ static const mp_rom_map_elem_t ppp_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&ppp_status_obj) }, { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&ppp_config_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&ppp_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&ppp_ipconfig_obj) }, { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ppp_delete_obj) }, { MP_ROM_QSTR(MP_QSTR_AUTH_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) }, { MP_ROM_QSTR(MP_QSTR_AUTH_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) }, From 9ece9f9b52ccaab7bbc5bee369b9f5ebac2e5745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20D=C3=B6rre?= Date: Fri, 29 Mar 2024 09:45:04 +0000 Subject: [PATCH 0021/1300] esp8266/network_wlan: Implement network.ipconfig and WLAN.ipconfig. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: robert-hh Signed-off-by: Felix Dörre --- ports/esp8266/modnetwork.h | 1 + ports/esp8266/modnetwork_globals.h | 1 + ports/esp8266/network_wlan.c | 171 +++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) diff --git a/ports/esp8266/modnetwork.h b/ports/esp8266/modnetwork.h index 5fd142e71eefb..5f451c866acda 100644 --- a/ports/esp8266/modnetwork.h +++ b/ports/esp8266/modnetwork.h @@ -1,3 +1,4 @@ extern const mp_obj_type_t esp_network_wlan_type; MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj); diff --git a/ports/esp8266/modnetwork_globals.h b/ports/esp8266/modnetwork_globals.h index 1a04568024b56..972b0e9806690 100644 --- a/ports/esp8266/modnetwork_globals.h +++ b/ports/esp8266/modnetwork_globals.h @@ -1,5 +1,6 @@ { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&esp_network_wlan_type) }, { MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_network_phy_mode_obj) }, +{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_network_ipconfig_obj) }, { MP_ROM_QSTR(MP_QSTR_STA_IF), MP_ROM_INT(STATION_IF)}, { MP_ROM_QSTR(MP_QSTR_AP_IF), MP_ROM_INT(SOFTAP_IF)}, diff --git a/ports/esp8266/network_wlan.c b/ports/esp8266/network_wlan.c index af1611a2496ed..57ad5d8305c1d 100644 --- a/ports/esp8266/network_wlan.c +++ b/ports/esp8266/network_wlan.c @@ -31,6 +31,7 @@ #include "py/objlist.h" #include "py/runtime.h" #include "py/mphal.h" +#include "py/parsenum.h" #include "extmod/modnetwork.h" #include "shared/netutils/netutils.h" #include "queue.h" @@ -41,6 +42,8 @@ #include "lwip/dns.h" #include "modnetwork.h" +#define IPADDR_STRLEN_MAX (20) + typedef struct _wlan_if_obj_t { mp_obj_base_t base; int if_id; @@ -330,6 +333,173 @@ static mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj, 1, 2, esp_ifconfig); +static mp_obj_t esp_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + if (kwargs->used == 0) { + // Get config value + if (n_args != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("must query one param")); + } + + switch (mp_obj_str_get_qstr(args[0])) { + case MP_QSTR_dns: { + char addr_str[IPADDR_STRLEN_MAX]; + ip_addr_t dns_addr = dns_getserver(0); + ipaddr_ntoa_r(&dns_addr, addr_str, sizeof(addr_str)); + return mp_obj_new_str(addr_str, strlen(addr_str)); + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } else { + // Set config value(s) + if (n_args != 0) { + mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); + } + + for (size_t i = 0; i < kwargs->alloc; ++i) { + if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { + mp_map_elem_t *e = &kwargs->table[i]; + switch (mp_obj_str_get_qstr(e->key)) { + case MP_QSTR_dns: { + ip_addr_t dns; + size_t addr_len; + const char *addr_str = mp_obj_str_get_data(e->value, &addr_len); + if (!ipaddr_aton(addr_str, &dns)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments as dns server")); + } + dns_setserver(0, &dns); + break; + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } + } + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj, 0, esp_network_ipconfig); + +static mp_obj_t esp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + struct ip_info info; + wifi_get_ip_info(self->if_id, &info); + + if (kwargs->used == 0) { + // Get config value + if (n_args != 2) { + mp_raise_TypeError(MP_ERROR_TEXT("must query one param")); + } + + switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_dhcp4: { + if (self->if_id == STATION_IF) { + return mp_obj_new_bool(wifi_station_dhcpc_status() == DHCP_STARTED); + } else if (self->if_id == SOFTAP_IF) { + return mp_obj_new_bool(wifi_softap_dhcps_status() == DHCP_STARTED); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + case MP_QSTR_addr4: { + mp_obj_t tuple[2] = { + netutils_format_ipv4_addr((uint8_t *)&info.ip, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t *)&info.netmask, NETUTILS_BIG), + }; + return mp_obj_new_tuple(2, tuple); + } + case MP_QSTR_gw4: { + return netutils_format_ipv4_addr((uint8_t *)&info.gw, NETUTILS_BIG); + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + return mp_const_none; + } else { + // Set config value(s) + if (n_args != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); + } + int touched_ip_info = 0; + for (size_t i = 0; i < kwargs->alloc; ++i) { + if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { + mp_map_elem_t *e = &kwargs->table[i]; + switch (mp_obj_str_get_qstr(e->key)) { + case MP_QSTR_dhcp4: { + if (self->if_id == STATION_IF) { + enum dhcp_status status = wifi_station_dhcpc_status(); + if (mp_obj_is_true(e->value) && status != DHCP_STARTED) { + wifi_station_dhcpc_start(); + } else if (!mp_obj_is_true(e->value) && status == DHCP_STARTED) { + wifi_station_dhcpc_stop(); + } + } else { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + break; + } + case MP_QSTR_addr4: { + if (e->value != mp_const_none && mp_obj_is_str(e->value)) { + size_t addr_len; + const char *input_str = mp_obj_str_get_data(e->value, &addr_len); + char *split = strchr(input_str, '/'); + if (split) { + mp_obj_t prefix_obj = mp_parse_num_integer(split + 1, strlen(split + 1), 10, NULL); + int prefix_bits = mp_obj_get_int(prefix_obj); + uint32_t mask = -(1u << (32 - prefix_bits)); + uint32_t *m = (uint32_t *)&info.netmask; + *m = htonl(mask); + } + netutils_parse_ipv4_addr(e->value, (void *)&info.ip, NETUTILS_BIG); + } else if (e->value != mp_const_none) { + mp_obj_t *items; + mp_obj_get_array_fixed_n(e->value, 2, &items); + netutils_parse_ipv4_addr(items[0], (void *)&info.ip, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[1], (void *)&info.netmask, NETUTILS_BIG); + } + touched_ip_info = 1; + break; + } + case MP_QSTR_gw4: { + netutils_parse_ipv4_addr(e->value, (void *)&info.gw, NETUTILS_BIG); + touched_ip_info = 1; + break; + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } + } + bool restart_dhcp_server = false; + if (self->if_id == STATION_IF) { + if (touched_ip_info) { + wifi_station_dhcpc_stop(); + wifi_set_ip_info(self->if_id, &info); + } + } else { + restart_dhcp_server = wifi_softap_dhcps_status(); + wifi_softap_dhcps_stop(); + wifi_set_ip_info(self->if_id, &info); + } + if (restart_dhcp_server) { + wifi_softap_dhcps_start(); + } + + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(esp_nic_ipconfig_obj, 1, esp_ipconfig); + static mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { if (n_args != 1 && kwargs->used != 0) { mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed")); @@ -514,6 +684,7 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&esp_isconnected_obj) }, { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&esp_config_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&esp_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_nic_ipconfig_obj) }, // Constants { MP_ROM_QSTR(MP_QSTR_IF_STA), MP_ROM_INT(STATION_IF)}, From e138bafbc7888108a22dd1c9c5c7105d5facfde1 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 29 Mar 2024 15:25:16 +0100 Subject: [PATCH 0022/1300] cc3200/mods: Implement network.ipconfig and network.WLAN.ipconfig. Signed-off-by: robert-hh --- ports/cc3200/mods/modnetwork.c | 49 +++++++++++++++ ports/cc3200/mods/modwlan.c | 112 +++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/ports/cc3200/mods/modnetwork.c b/ports/cc3200/mods/modnetwork.c index 3a949136dcfdd..e16668f8adb0d 100644 --- a/ports/cc3200/mods/modnetwork.c +++ b/ports/cc3200/mods/modnetwork.c @@ -28,6 +28,7 @@ #include "py/runtime.h" #include "py/mperrno.h" #include "py/mphal.h" +#include "shared/netutils/netutils.h" #include "modnetwork.h" #include "serverstask.h" #include "simplelink.h" @@ -53,6 +54,53 @@ static const mp_obj_type_t network_server_type; void mod_network_init0(void) { } +static mp_obj_t mod_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + unsigned char len = sizeof(SlNetCfgIpV4Args_t); + unsigned char dhcpIsOn; + SlNetCfgIpV4Args_t ipV4; + sl_NetCfgGet(SL_IPV4_STA_P2P_CL_GET_INFO, &dhcpIsOn, &len, (uint8_t *)&ipV4); + + if (kwargs->used == 0) { + // Get config value + if (n_args != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("must query one param")); + } + switch (mp_obj_str_get_qstr(args[0])) { + case MP_QSTR_dns: { + return netutils_format_ipv4_addr((uint8_t *)&ipV4.ipV4DnsServer, NETUTILS_LITTLE); + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } else { + // Set config value(s) + if (n_args != 0) { + mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); + } + + for (size_t i = 0; i < kwargs->alloc; ++i) { + if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { + mp_map_elem_t *e = &kwargs->table[i]; + switch (mp_obj_str_get_qstr(e->key)) { + case MP_QSTR_dns: { + netutils_parse_ipv4_addr(e->value, (uint8_t *)&ipV4.ipV4DnsServer, NETUTILS_LITTLE); + sl_NetCfgSet(SL_IPV4_STA_P2P_CL_STATIC_ENABLE, IPCONFIG_MODE_ENABLE_IPV4, sizeof(SlNetCfgIpV4Args_t), (_u8 *)&ipV4); + break; + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } + } + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(mod_network_ipconfig_obj, 0, mod_network_ipconfig); + #if (MICROPY_PORT_HAS_TELNET || MICROPY_PORT_HAS_FTP) static mp_obj_t network_server_init_helper(mp_obj_t self, const mp_arg_val_t *args) { const char *user = SERVERS_DEF_USER; @@ -145,6 +193,7 @@ static MP_DEFINE_CONST_FUN_OBJ_1(network_server_deinit_obj, network_server_deini static const mp_rom_map_elem_t mp_module_network_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_network) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&mod_network_ipconfig_obj) }, { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_nic_type_wlan) }, #if (MICROPY_PORT_HAS_TELNET || MICROPY_PORT_HAS_FTP) diff --git a/ports/cc3200/mods/modwlan.c b/ports/cc3200/mods/modwlan.c index fc55201eaa3c0..34b5adab28b8c 100644 --- a/ports/cc3200/mods/modwlan.c +++ b/ports/cc3200/mods/modwlan.c @@ -32,6 +32,7 @@ #include "py/mpconfig.h" #include "py/obj.h" #include "py/objstr.h" +#include "py/parsenum.h" #include "py/runtime.h" #include "py/stream.h" #include "py/mphal.h" @@ -1057,6 +1058,116 @@ static mp_obj_t wlan_ifconfig(size_t n_args, const mp_obj_t *pos_args, mp_map_t } static MP_DEFINE_CONST_FUN_OBJ_KW(wlan_ifconfig_obj, 1, wlan_ifconfig); +static mp_obj_t wlan_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + unsigned char len = sizeof(SlNetCfgIpV4Args_t); + unsigned char dhcpIsOn; + SlNetCfgIpV4Args_t ipV4; + sl_NetCfgGet(SL_IPV4_STA_P2P_CL_GET_INFO, &dhcpIsOn, &len, (uint8_t *)&ipV4); + + if (kwargs->used == 0) { + // Get config value + if (n_args != 2) { + mp_raise_TypeError(MP_ERROR_TEXT("must query one param")); + } + + switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_dhcp4: { + return mp_obj_new_bool(dhcpIsOn); + } + case MP_QSTR_has_dhcp4: { + return mp_obj_new_bool(dhcpIsOn && ipV4.ipV4 != 0); + } + case MP_QSTR_addr4: { + mp_obj_t tuple[2] = { + netutils_format_ipv4_addr((uint8_t *)&ipV4.ipV4, NETUTILS_LITTLE), + netutils_format_ipv4_addr((uint8_t *)&ipV4.ipV4Mask, NETUTILS_LITTLE), + }; + return mp_obj_new_tuple(2, tuple); + } + case MP_QSTR_gw4: { + return netutils_format_ipv4_addr((uint8_t *)&ipV4.ipV4Gateway, NETUTILS_LITTLE); + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + return mp_const_none; + } else { + // Set config value(s) + if (n_args != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); + } + + for (size_t i = 0; i < kwargs->alloc; ++i) { + if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { + mp_map_elem_t *e = &kwargs->table[i]; + switch (mp_obj_str_get_qstr(e->key)) { + case MP_QSTR_dhcp4: { + if (wlan_obj.mode == ROLE_AP) { + if (mp_obj_is_true(e->value)) { + sl_NetCfgSet(SL_IPV4_AP_P2P_GO_STATIC_ENABLE, IPCONFIG_MODE_ENABLE_IPV4, + sizeof(SlNetCfgIpV4Args_t), (_u8 *)&ipV4); + SlNetAppDhcpServerBasicOpt_t dhcpParams; + dhcpParams.lease_time = 4096; // lease time (in seconds) of the IP Address + dhcpParams.ipv4_addr_start = ipV4.ipV4 + 1; // first IP Address for allocation. + dhcpParams.ipv4_addr_last = (ipV4.ipV4 & 0xFFFFFF00) + 254; // last IP Address for allocation. + sl_NetAppStop(SL_NET_APP_DHCP_SERVER_ID); // stop DHCP server before settings + sl_NetAppSet(SL_NET_APP_DHCP_SERVER_ID, NETAPP_SET_DHCP_SRV_BASIC_OPT, + sizeof(SlNetAppDhcpServerBasicOpt_t), (_u8* )&dhcpParams); // set parameters + sl_NetAppStart(SL_NET_APP_DHCP_SERVER_ID); // start DHCP server with new settings + } else { + sl_NetAppStop(SL_NET_APP_DHCP_SERVER_ID); // stop DHCP server before settings + } + } else { + _u8 val = 1; + if (mp_obj_is_true(e->value)) { + sl_NetCfgSet(SL_IPV4_STA_P2P_CL_DHCP_ENABLE, IPCONFIG_MODE_ENABLE_IPV4, 1, &val); + } else { + sl_NetCfgSet(SL_IPV4_STA_P2P_CL_STATIC_ENABLE, IPCONFIG_MODE_ENABLE_IPV4, + sizeof(SlNetCfgIpV4Args_t), (_u8 *)&ipV4); + } + } + break; + } + case MP_QSTR_addr4: { + int prefix_bits = 32; + if (e->value != mp_const_none && mp_obj_is_str(e->value)) { + size_t addr_len; + const char *input_str = mp_obj_str_get_data(e->value, &addr_len); + char *split = strchr(input_str, '/'); + if (split) { + mp_obj_t prefix_obj = mp_parse_num_integer(split + 1, strlen(split + 1), 10, NULL); + prefix_bits = mp_obj_get_int(prefix_obj); + ipV4.ipV4Mask = -(1u << (32 - prefix_bits)); + } + netutils_parse_ipv4_addr(e->value, (uint8_t *)&ipV4.ipV4, NETUTILS_LITTLE); + } else if (e->value != mp_const_none) { + mp_obj_t *items; + mp_obj_get_array_fixed_n(e->value, 2, &items); + netutils_parse_ipv4_addr(items[0], (uint8_t *)&ipV4.ipV4, NETUTILS_LITTLE); + netutils_parse_ipv4_addr(items[1], (uint8_t *)&ipV4.ipV4Mask, NETUTILS_LITTLE); + } + ASSERT_ON_ERROR(sl_NetCfgSet(SL_IPV4_STA_P2P_CL_STATIC_ENABLE, IPCONFIG_MODE_ENABLE_IPV4, sizeof(SlNetCfgIpV4Args_t), (_u8 *)&ipV4)); + break; + } + case MP_QSTR_gw4: { + netutils_parse_ipv4_addr(e->value, (uint8_t *)&ipV4.ipV4Gateway, NETUTILS_LITTLE); + sl_NetCfgSet(SL_IPV4_STA_P2P_CL_STATIC_ENABLE, IPCONFIG_MODE_ENABLE_IPV4, sizeof(SlNetCfgIpV4Args_t), (_u8 *)&ipV4); + break; + } + default: { + mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); + break; + } + } + } + } + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(wlan_ipconfig_obj, 1, wlan_ipconfig); + static mp_obj_t wlan_mode(size_t n_args, const mp_obj_t *args) { wlan_obj_t *self = args[0]; if (n_args == 1) { @@ -1260,6 +1371,7 @@ static const mp_rom_map_elem_t wlan_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&wlan_disconnect_obj) }, { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&wlan_isconnected_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&wlan_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&wlan_ipconfig_obj) }, { MP_ROM_QSTR(MP_QSTR_mode), MP_ROM_PTR(&wlan_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_ssid), MP_ROM_PTR(&wlan_ssid_obj) }, { MP_ROM_QSTR(MP_QSTR_auth), MP_ROM_PTR(&wlan_auth_obj) }, From d144f0699de10b10ce42f3013f46d46b8fe97c13 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 15 May 2024 14:40:23 +1000 Subject: [PATCH 0023/1300] lib/tinyusb: Update to the most recent master. Signed-off-by: Andrew Leech --- lib/tinyusb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tinyusb b/lib/tinyusb index 1fdf29075d4e6..d10b65ada4be7 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit 1fdf29075d4e613eacfa881166015263797db0f6 +Subproject commit d10b65ada4be7d5754b3128e80a9b4db72bdb23f From 8809ae71610c2b3af4128c498d711535195d5221 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 15 May 2024 14:40:23 +1000 Subject: [PATCH 0024/1300] shared/tinyusb: Buffer startup CDC data to send to host on connection. At startup, buffer initial stdout / MicroyPthon banner so that it can be sent to the host on initial connection of the USB serial port. This buffering also works for when the CDC becomes disconnected and the device is still printing to stdout, and when CDC is reconnected the most recent part of stdout (depending on how big the internal USB FIFO is) is flushed to the host. This change is most obvious when you've first plugged in a MicroPython device (or hit reset), when it's a board that uses USB (CDC) serial in the chip itself for the REPL interface. This doesn't apply to UART going via a separate USB-serial chip. The stm32 port already has this buffering behaviour (it doesn't use TinyUSB) and this commit extends such behaviour to rp2, mimxrt, samd and renesas-ra ports, which do use TinyUSB. Signed-off-by: Andrew Leech --- ports/mimxrt/tusb_config.h | 1 + shared/tinyusb/mp_usbd_cdc.c | 53 +++++++++++++++++++++++++++--------- shared/tinyusb/tusb_config.h | 1 + 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/ports/mimxrt/tusb_config.h b/ports/mimxrt/tusb_config.h index 862fb6c52d2e5..607f36446f858 100644 --- a/ports/mimxrt/tusb_config.h +++ b/ports/mimxrt/tusb_config.h @@ -32,5 +32,6 @@ #define CFG_TUD_CDC (1) #define CFG_TUD_CDC_RX_BUFSIZE (512) #define CFG_TUD_CDC_TX_BUFSIZE (512) +#define CFG_TUD_CDC_PERSISTENT_TX_BUFF (1) #endif // MICROPY_INCLUDED_MIMXRT_TUSB_CONFIG_H diff --git a/shared/tinyusb/mp_usbd_cdc.c b/shared/tinyusb/mp_usbd_cdc.c index 6d789ff5e81ca..674d6b089947e 100644 --- a/shared/tinyusb/mp_usbd_cdc.c +++ b/shared/tinyusb/mp_usbd_cdc.c @@ -35,6 +35,7 @@ #if MICROPY_HW_USB_CDC && MICROPY_HW_ENABLE_USBDEV && !MICROPY_EXCLUDE_SHARED_TINYUSB_USBD_CDC static uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll +static int8_t cdc_connected_flush_delay = 0; uintptr_t mp_usbd_cdc_poll_interfaces(uintptr_t poll_flags) { uintptr_t ret = 0; @@ -58,7 +59,9 @@ uintptr_t mp_usbd_cdc_poll_interfaces(uintptr_t poll_flags) { if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { ret |= MP_STREAM_POLL_RD; } - if ((poll_flags & MP_STREAM_POLL_WR) && tud_cdc_connected() && tud_cdc_write_available() > 0) { + if ((poll_flags & MP_STREAM_POLL_WR) && + (!tud_cdc_connected() || (tud_cdc_connected() && tud_cdc_write_available() > 0))) { + // Always allow write when not connected, fifo will retain latest. // When connected operate as blocking, only allow if space is available. ret |= MP_STREAM_POLL_WR; } @@ -93,14 +96,14 @@ void tud_cdc_rx_cb(uint8_t itf) { mp_uint_t mp_usbd_cdc_tx_strn(const char *str, mp_uint_t len) { size_t i = 0; - if (tud_cdc_connected()) { - while (i < len) { - uint32_t n = len - i; - if (n > CFG_TUD_CDC_EP_BUFSIZE) { - n = CFG_TUD_CDC_EP_BUFSIZE; - } + while (i < len) { + uint32_t n = len - i; + if (n > CFG_TUD_CDC_EP_BUFSIZE) { + n = CFG_TUD_CDC_EP_BUFSIZE; + } + if (tud_cdc_connected()) { int timeout = 0; - // Wait with a max of USC_CDC_TIMEOUT ms + // If CDC port is connected but the buffer is full, wait for up to USC_CDC_TIMEOUT ms. while (n > tud_cdc_write_available() && timeout++ < MICROPY_HW_USB_CDC_TX_TIMEOUT) { mp_event_wait_ms(1); @@ -108,27 +111,40 @@ mp_uint_t mp_usbd_cdc_tx_strn(const char *str, mp_uint_t len) { // are in an interrupt handler), while there is data pending. mp_usbd_task(); } - if (timeout >= MICROPY_HW_USB_CDC_TX_TIMEOUT) { + // Limit write to available space in tx buffer when connected. + n = MIN(n, tud_cdc_write_available()); + if (n == 0) { break; } - uint32_t n2 = tud_cdc_write(str + i, n); - tud_cdc_write_flush(); - i += n2; } + // When not connected we always write to usb fifo, ensuring it has latest data. + uint32_t n2 = tud_cdc_write(str + i, n); + tud_cdc_write_flush(); + i += n2; } return i; } +void tud_sof_cb(uint32_t frame_count) { + if (--cdc_connected_flush_delay < 0) { + // Finished on-connection delay, disable SOF interrupt again. + tud_sof_cb_enable(false); + tud_cdc_write_flush(); + } +} + #endif -#if MICROPY_HW_USB_CDC_1200BPS_TOUCH && MICROPY_HW_ENABLE_USBDEV +#if MICROPY_HW_ENABLE_USBDEV && (MICROPY_HW_USB_CDC_1200BPS_TOUCH || MICROPY_HW_USB_CDC) +#if MICROPY_HW_USB_CDC_1200BPS_TOUCH static mp_sched_node_t mp_bootloader_sched_node; static void usbd_cdc_run_bootloader_task(mp_sched_node_t *node) { mp_hal_delay_ms(250); machine_bootloader(0, NULL); } +#endif void #if MICROPY_HW_USB_EXTERNAL_TINYUSB @@ -137,6 +153,16 @@ mp_usbd_line_state_cb tud_cdc_line_state_cb #endif (uint8_t itf, bool dtr, bool rts) { + #if MICROPY_HW_USB_CDC && !MICROPY_EXCLUDE_SHARED_TINYUSB_USBD_CDC + if (dtr) { + // A host application has started to open the cdc serial port. + // Wait a few ms for host to be ready then send tx buffer. + // High speed connection SOF fires at 125us, full speed at 1ms. + cdc_connected_flush_delay = (tud_speed_get() == TUSB_SPEED_HIGH) ? 128 : 16; + tud_sof_cb_enable(true); + } + #endif + #if MICROPY_HW_USB_CDC_1200BPS_TOUCH if (dtr == false && rts == false) { // Device is disconnected. cdc_line_coding_t line_coding; @@ -146,6 +172,7 @@ tud_cdc_line_state_cb mp_sched_schedule_node(&mp_bootloader_sched_node, usbd_cdc_run_bootloader_task); } } + #endif } #endif diff --git a/shared/tinyusb/tusb_config.h b/shared/tinyusb/tusb_config.h index ab47321afdc04..1f8f5e5f6dc56 100644 --- a/shared/tinyusb/tusb_config.h +++ b/shared/tinyusb/tusb_config.h @@ -79,6 +79,7 @@ #if CFG_TUD_CDC #define CFG_TUD_CDC_RX_BUFSIZE ((CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED) ? 512 : 256) #define CFG_TUD_CDC_TX_BUFSIZE ((CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED) ? 512 : 256) +#define CFG_TUD_CDC_PERSISTENT_TX_BUFF (1) #endif // MSC Configuration From 596f92bf77a44cefe5a3ec6e0c3fee8cfb5a3906 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 13 Oct 2023 18:52:15 +0200 Subject: [PATCH 0025/1300] tools/makemanifest.py: Generate working code for empty manifests. When no usable manifest directives are found (as opposed to no manifest being set in the makefile), non-compiling code was generated for the empty frozen constants pool block. Signed-off-by: Alessandro Gatti --- tools/makemanifest.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/makemanifest.py b/tools/makemanifest.py index a74a6934aeabe..e076a03e0be3c 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -245,7 +245,11 @@ def main(): b'#include "py/emitglue.h"\n' b"extern const qstr_pool_t mp_qstr_const_pool;\n" b"const qstr_pool_t mp_qstr_frozen_const_pool = {\n" - b" (qstr_pool_t*)&mp_qstr_const_pool, MP_QSTRnumber_of, 0, 0\n" + b" #if MICROPY_QSTR_BYTES_IN_HASH\n" + b" (qstr_pool_t*)&mp_qstr_const_pool, MP_QSTRnumber_of, 0, 0, 0, NULL, NULL, {},\n" + b" #else\n" + b" (qstr_pool_t*)&mp_qstr_const_pool, MP_QSTRnumber_of, 0, 0, 0, NULL, {},\n" + b" #endif\n" b"};\n" b'const char mp_frozen_names[] = { MP_FROZEN_STR_NAMES "\\0"};\n' b"const mp_raw_code_t *const mp_frozen_mpy_content[] = {NULL};\n" From e6ae69999851c143e97f1214678e8f966ee69803 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 22 May 2024 08:07:26 +0200 Subject: [PATCH 0026/1300] py/nlrrv32: Add RISC-V RV32I NLR implementation. Add custom NLR support for 32 bits RISC-V RV32I targets. Signed-off-by: Alessandro Gatti --- py/nlr.h | 4 +++ py/nlrrv32.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++ py/py.cmake | 1 + py/py.mk | 1 + 4 files changed, 87 insertions(+) create mode 100644 py/nlrrv32.c diff --git a/py/nlr.h b/py/nlr.h index 62972dba62713..80f6d7cc11493 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -44,6 +44,7 @@ #define MICROPY_NLR_NUM_REGS_MIPS (13) #define MICROPY_NLR_NUM_REGS_XTENSA (10) #define MICROPY_NLR_NUM_REGS_XTENSAWIN (17) +#define MICROPY_NLR_NUM_REGS_RV32I (14) // *FORMAT-OFF* @@ -88,6 +89,9 @@ #elif defined(__mips__) #define MICROPY_NLR_MIPS (1) #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_MIPS) +#elif defined(__riscv) && defined(__riscv_xlen) && (__riscv_xlen == 32) + #define MICROPY_NLR_RV32I (1) + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_RV32I) #else #define MICROPY_NLR_SETJMP (1) //#warning "No native NLR support for this arch, using setjmp implementation" diff --git a/py/nlrrv32.c b/py/nlrrv32.c new file mode 100644 index 0000000000000..9a12ede400daa --- /dev/null +++ b/py/nlrrv32.c @@ -0,0 +1,81 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Alessandro Gatti + * + * 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/mpstate.h" + +#if MICROPY_NLR_RV32I + +#undef nlr_push + +__attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { + __asm volatile ( + "sw x1, 8(x10) \n" // Store RA. + "sw x8, 12(x10) \n" // Store S0. + "sw x9, 16(x10) \n" // Store S1. + "sw x18, 20(x10) \n" // Store S2. + "sw x19, 24(x10) \n" // Store S3. + "sw x20, 28(x10) \n" // Store S4. + "sw x21, 32(x10) \n" // Store S5. + "sw x22, 36(x10) \n" // Store S6. + "sw x23, 40(x10) \n" // Store S7. + "sw x24, 44(x10) \n" // Store S8. + "sw x25, 48(x10) \n" // Store S9. + "sw x26, 52(x10) \n" // Store S10. + "sw x27, 56(x10) \n" // Store S11. + "sw x2, 60(x10) \n" // Store SP. + "jal x0, nlr_push_tail \n" // Jump to the C part. + ); +} + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + __asm volatile ( + "add x10, x0, %0 \n" // Load nlr_buf address. + "lw x1, 8(x10) \n" // Retrieve RA. + "lw x8, 12(x10) \n" // Retrieve S0. + "lw x9, 16(x10) \n" // Retrieve S1. + "lw x18, 20(x10) \n" // Retrieve S2. + "lw x19, 24(x10) \n" // Retrieve S3. + "lw x20, 28(x10) \n" // Retrieve S4. + "lw x21, 32(x10) \n" // Retrieve S5. + "lw x22, 36(x10) \n" // Retrieve S6. + "lw x23, 40(x10) \n" // Retrieve S7. + "lw x24, 44(x10) \n" // Retrieve S8. + "lw x25, 48(x10) \n" // Retrieve S9. + "lw x26, 52(x10) \n" // Retrieve S10. + "lw x27, 56(x10) \n" // Retrieve S11. + "lw x2, 60(x10) \n" // Retrieve SP. + "addi x10, x0, 1 \n" // Return 1 for a non-local return. + "jalr x0, x1, 0 \n" // Return. + : // Outputs. + : "r" (top) // Inputs. + : "memory" // Clobbered. + ); + + MP_UNREACHABLE +} + +#endif // MICROPY_NLR_RV32I diff --git a/py/py.cmake b/py/py.cmake index 95a841b5f4a02..74a433c97a3f1 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -55,6 +55,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/nlr.c ${MICROPY_PY_DIR}/nlrmips.c ${MICROPY_PY_DIR}/nlrpowerpc.c + ${MICROPY_PY_DIR}/nlrrv32.c ${MICROPY_PY_DIR}/nlrsetjmp.c ${MICROPY_PY_DIR}/nlrthumb.c ${MICROPY_PY_DIR}/nlrx64.c diff --git a/py/py.mk b/py/py.mk index e81df52fb7f99..1378968c9646b 100644 --- a/py/py.mk +++ b/py/py.mk @@ -83,6 +83,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ nlrmips.o \ nlrpowerpc.o \ nlrxtensa.o \ + nlrrv32.o \ nlrsetjmp.o \ malloc.o \ gc.o \ From de0e13a9a82fb27a50aa632b6c22663d9d58a4a0 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 22 May 2024 09:03:10 +0200 Subject: [PATCH 0027/1300] shared/runtime/gchelper: Add RISC-V RV32I native gchelper. Add native gchelper support for 32 bits RISC-V RV32I targets. Signed-off-by: Alessandro Gatti --- shared/runtime/gchelper.h | 2 ++ shared/runtime/gchelper_generic.c | 31 ++++++++++++++++++ shared/runtime/gchelper_rv32i.s | 52 +++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 shared/runtime/gchelper_rv32i.s diff --git a/shared/runtime/gchelper.h b/shared/runtime/gchelper.h index 645ee837f5146..a863fb9aa880a 100644 --- a/shared/runtime/gchelper.h +++ b/shared/runtime/gchelper.h @@ -41,6 +41,8 @@ typedef uintptr_t gc_helper_regs_t[4]; typedef uintptr_t gc_helper_regs_t[10]; #elif defined(__aarch64__) typedef uintptr_t gc_helper_regs_t[11]; // x19-x29 +#elif defined(__riscv) && defined(__riscv_xlen) && (__riscv_xlen == 32) +typedef uintptr_t gc_helper_regs_t[12]; // S0-S11 #endif #endif diff --git a/shared/runtime/gchelper_generic.c b/shared/runtime/gchelper_generic.c index 4ef2e73f7a2ee..f1087e1990f60 100644 --- a/shared/runtime/gchelper_generic.c +++ b/shared/runtime/gchelper_generic.c @@ -150,6 +150,37 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { arr[10] = x29; } +#elif defined(__riscv) && defined(__riscv_xlen) && (__riscv_xlen == 32) + +// Fallback implementation for RV32I, prefer gchelper_rv32i.s + +static void gc_helper_get_regs(gc_helper_regs_t arr) { + register long s0 asm ("x8"); + register long s1 asm ("x9"); + register long s2 asm ("x18"); + register long s3 asm ("x19"); + register long s4 asm ("x20"); + register long s5 asm ("x21"); + register long s6 asm ("x22"); + register long s7 asm ("x23"); + register long s8 asm ("x24"); + register long s9 asm ("x25"); + register long s10 asm ("x26"); + register long s11 asm ("x27"); + arr[0] = s0; + arr[1] = s1; + arr[2] = s2; + arr[3] = s3; + arr[4] = s4; + arr[5] = s5; + arr[6] = s6; + arr[7] = s7; + arr[8] = s8; + arr[9] = s9; + arr[10] = s10; + arr[11] = s11; +} + #else #error "Architecture not supported for gc_helper_get_regs. Set MICROPY_GCREGS_SETJMP to use the fallback implementation." diff --git a/shared/runtime/gchelper_rv32i.s b/shared/runtime/gchelper_rv32i.s new file mode 100644 index 0000000000000..64248e771a861 --- /dev/null +++ b/shared/runtime/gchelper_rv32i.s @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Alessandro Gatti + * + * 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. + */ + + .global gc_helper_get_regs_and_sp + .type gc_helper_get_regs_and_sp, @function + +gc_helper_get_regs_and_sp: + + /* Store registers into the given array. */ + + sw x8, 0(x10) /* Save S0. */ + sw x9, 4(x10) /* Save S1. */ + sw x18, 8(x10) /* Save S2. */ + sw x19, 12(x10) /* Save S3. */ + sw x20, 16(x10) /* Save S4. */ + sw x21, 20(x10) /* Save S5. */ + sw x22, 24(x10) /* Save S6. */ + sw x23, 28(x10) /* Save S7. */ + sw x24, 32(x10) /* Save S8. */ + sw x25, 36(x10) /* Save S9. */ + sw x26, 40(x10) /* Save S10. */ + sw x27, 44(x10) /* Save S11. */ + + /* Return the stack pointer. */ + + add x10, x0, x2 + jalr x0, x1, 0 + + .size gc_helper_get_regs_and_sp, .-gc_helper_get_regs_and_sp From 90d50ce9189feffb63ebf9b92d4302bd5c12730b Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 22 May 2024 10:17:13 +0200 Subject: [PATCH 0028/1300] shared/runtime/semihosting: Rename ARM semihosting files. Make room for RISC-V semihosting code, by renaming the existing `semihosting.[ch]` files into `semihosting_arm.[ch]`. Signed-off-by: Alessandro Gatti --- shared/runtime/{semihosting.c => semihosting_arm.c} | 2 +- shared/runtime/{semihosting.h => semihosting_arm.h} | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) rename shared/runtime/{semihosting.c => semihosting_arm.c} (99%) rename shared/runtime/{semihosting.h => semihosting_arm.h} (86%) diff --git a/shared/runtime/semihosting.c b/shared/runtime/semihosting_arm.c similarity index 99% rename from shared/runtime/semihosting.c rename to shared/runtime/semihosting_arm.c index 18c7f5d57a3f3..3ca29d5d752c4 100644 --- a/shared/runtime/semihosting.c +++ b/shared/runtime/semihosting_arm.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include "semihosting.h" +#include "semihosting_arm.h" // Resources: // http://embed.rs/articles/2016/semi-hosting-rust/ diff --git a/shared/runtime/semihosting.h b/shared/runtime/semihosting_arm.h similarity index 86% rename from shared/runtime/semihosting.h rename to shared/runtime/semihosting_arm.h index d053a03edaae9..7e90f25ac916c 100644 --- a/shared/runtime/semihosting.h +++ b/shared/runtime/semihosting_arm.h @@ -23,16 +23,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_LIB_UTILS_SEMIHOSTING_H -#define MICROPY_INCLUDED_LIB_UTILS_SEMIHOSTING_H +#ifndef MICROPY_INCLUDED_SHARED_RUNTIME_SEMIHOSTING_ARM_H +#define MICROPY_INCLUDED_SHARED_RUNTIME_SEMIHOSTING_ARM_H /* To use semi-hosting for a replacement UART: -- Add lib/semihosting/semihosting.c to the Makefile sources. +- Add shared/runtime/semihosting_arm.c to the Makefile sources. - Call mp_semihosting_init() in main(), around the time UART is initialized. - Replace mp_hal_stdin_rx_chr and similar in mphalport.c with the semihosting equivalent. -- Include lib/semihosting/semihosting.h in the relevant files. +- Include shared/runtime/semihosting_arm.h in the relevant files. Then make sure the debugger is attached and enables semihosting. In OpenOCD this is done with ARM semihosting enable followed by reset. The terminal will need further @@ -48,4 +48,4 @@ int mp_semihosting_rx_char(); uint32_t mp_semihosting_tx_strn(const char *str, size_t len); uint32_t mp_semihosting_tx_strn_cooked(const char *str, size_t len); -#endif // MICROPY_INCLUDED_LIB_UTILS_SEMIHOSTING_H +#endif // MICROPY_INCLUDED_SHARED_RUNTIME_SEMIHOSTING_ARM_H From ace08c39786871f4674c2d36a4b59343d64c7603 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 22 May 2024 10:50:20 +0200 Subject: [PATCH 0029/1300] shared/runtime/semihosting: Add RISC-V semihosting support. This adds a RISC-V RV32 semihosting implementation, with all defined system calls exposed to the user. Signed-off-by: Alessandro Gatti --- shared/runtime/semihosting_rv32.c | 481 ++++++++++++++++++++++++++++++ shared/runtime/semihosting_rv32.h | 247 +++++++++++++++ 2 files changed, 728 insertions(+) create mode 100644 shared/runtime/semihosting_rv32.c create mode 100644 shared/runtime/semihosting_rv32.h diff --git a/shared/runtime/semihosting_rv32.c b/shared/runtime/semihosting_rv32.c new file mode 100644 index 0000000000000..1d02b69b23ea8 --- /dev/null +++ b/shared/runtime/semihosting_rv32.c @@ -0,0 +1,481 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Alessandro Gatti + * + * 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 + +#include "semihosting_rv32.h" + +#if !defined(__riscv) || !defined(__riscv_xlen) || (__riscv_xlen != 32) +#error "This semihosting support code is only available for RV32 targets." +#endif + +// Features file magic header. +#define MAGIC_SIZE 4 +#define MAGIC_0 0x53 // 'S' +#define MAGIC_1 0x48 // 'H' +#define MAGIC_2 0x46 // 'B' +#define MAGIC_3 0x42 // 'F' + +#define CMDLINE_MIN_BUFFER_SIZE 80 + +#define SYS_OPEN 0x01 +#define SYS_CLOSE 0x02 +#define SYS_WRITEC 0x03 +#define SYS_WRITE0 0x04 +#define SYS_WRITE 0x05 +#define SYS_READ 0x06 +#define SYS_READC 0x07 +#define SYS_ISERROR 0x08 +#define SYS_ISTTY 0x09 +#define SYS_SEEK 0x0A +#define SYS_FLEN 0x0C +#define SYS_TMPNAM 0x0D +#define SYS_REMOVE 0x0E +#define SYS_RENAME 0x0F +#define SYS_CLOCK 0x10 +#define SYS_TIME 0x11 +#define SYS_SYSTEM 0x12 +#define SYS_ERRNO 0x13 +#define SYS_GET_CMDLINE 0x15 +#define SYS_HEAPINFO 0x16 +#define SYS_EXIT 0x18 +#define SYS_EXIT_EXTENDED 0x20 +#define SYS_ELAPSED 0x30 +#define SYS_TICKFREQ 0x31 + +// Extended features availability flags. +static bool exit_extended_available = false; +static bool split_stdout_stderr = false; + +// Perform a semihosting call with the given call number and using the given +// parameters block. +int mp_semihosting_call(uint32_t num, void *arg); + +// Convert the given fopen(3) open mode string into the appropriate integer +// value required by SYS_OPEN. If the mode is invalid, it will return -1. +static int mp_lookup_open_mode(const char *mode); + +// Computes the length of the given string. If it gets passed a NULL pointer +// it will return -1. +static int mp_strlen(const char *string); + +// Check which extended features are advertised by the host system. +static void mp_check_extended_features_availability(void); + +// Write the given string to the host system's debug console. +static int mp_write_to_debug_console(const char *string, size_t length); + +// The host system's STDOUT file handle. +int mp_semihosting_stdout = -1; + +// The host system's STDERR file handle. +int mp_semihosting_stderr = -1; + +int mp_semihosting_open(const char *file_name, const char *file_mode) { + if (file_name == NULL || file_mode == NULL) { + return -1; + } + + int file_name_length = mp_strlen(file_name); + if (file_name_length <= 0) { + return -1; + } + int file_open_mode = mp_lookup_open_mode(file_mode); + if (file_open_mode < 0) { + return -1; + } + + uint32_t arguments[3] = { (uintptr_t)file_name, file_open_mode, file_name_length }; + return mp_semihosting_call(SYS_OPEN, arguments); +} + +int mp_semihosting_close(int handle) { + uint32_t arguments[] = { handle }; + return mp_semihosting_call(SYS_CLOSE, arguments); +} + +void mp_semihosting_writec(char character) { + uint32_t arguments[] = { character }; + mp_semihosting_call(SYS_WRITEC, arguments); +} + +void mp_semihosting_write0(const char *string) { + if (string == NULL) { + return; + } + uint32_t arguments[] = { (uintptr_t)string }; + mp_semihosting_call(SYS_WRITE0, arguments); +} + +int mp_semihosting_write(int handle, const void *data, size_t length) { + if (data == NULL) { + return length; + } + if (length == 0) { + return 0; + } + + uint32_t arguments[] = { handle, (uintptr_t)data, length }; + return mp_semihosting_call(SYS_WRITE, arguments); +} + +int mp_semihosting_read(int handle, void *data, size_t length) { + if (data == NULL) { + return -1; + } + if (length == 0) { + return 0; + } + + uint32_t arguments[] = { handle, (uintptr_t)data, length }; + return mp_semihosting_call(SYS_READ, arguments); +} + +inline int mp_semihosting_readc(void) { + return mp_semihosting_call(SYS_READC, NULL); +} + +int mp_semihosting_iserror(int code) { + uint32_t arguments[] = { code }; + return mp_semihosting_call(SYS_ISERROR, arguments); +} + +int mp_semihosting_istty(int handle) { + uint32_t arguments[] = { handle }; + return mp_semihosting_call(SYS_ISTTY, arguments); +} + +int mp_semihosting_seek(int handle, uint32_t offset) { + uint32_t arguments[] = { handle, offset }; + return mp_semihosting_call(SYS_SEEK, arguments); +} + +int mp_semihosting_flen(int handle) { + uint32_t arguments[] = { handle }; + return mp_semihosting_call(SYS_FLEN, arguments); +} + +int mp_semihosting_tmpnam(uint8_t identifier, void *buffer, size_t buffer_length) { + if (buffer == NULL || buffer_length == 0) { + return -1; + } + + uint32_t arguments[] = { (uintptr_t)buffer, identifier, buffer_length }; + return mp_semihosting_call(SYS_TMPNAM, arguments); +} + +int mp_semihosting_remove(const char *file_name) { + if (file_name == NULL) { + return -1; + } + + int file_name_length = mp_strlen(file_name); + if (file_name_length <= 0) { + return -1; + } + + uint32_t arguments[] = { (uintptr_t)file_name, file_name_length }; + return mp_semihosting_call(SYS_REMOVE, arguments); +} + +int mp_semihosting_rename(const char *old_name, const char *new_name) { + if (old_name == NULL || new_name == NULL) { + return -1; + } + + int old_name_length = mp_strlen(old_name); + if (old_name_length <= 0) { + return -1; + } + + int new_name_length = mp_strlen(new_name); + if (new_name_length <= 0) { + return -1; + } + + uint32_t arguments[] = { + (uintptr_t)old_name, old_name_length, (uintptr_t)new_name, new_name_length + }; + return mp_semihosting_call(SYS_RENAME, arguments); +} + +inline int mp_semihosting_clock(void) { + return mp_semihosting_call(SYS_CLOCK, NULL); +} + +inline int mp_semihosting_time(void) { + return mp_semihosting_call(SYS_TIME, NULL); +} + +int mp_semihosting_system(const char *command) { + if (command == NULL) { + return -1; + } + + int command_length = mp_strlen(command); + if (command_length <= 0) { + return -1; + } + + uint32_t arguments[] = { (uintptr_t)command, command_length }; + return mp_semihosting_call(SYS_SYSTEM, arguments); +} + +inline int mp_semihosting_errno(void) { + return mp_semihosting_call(SYS_ERRNO, NULL); +} + +int mp_semihosting_get_cmdline(void *buffer, size_t buffer_length) { + if (buffer == NULL || buffer_length < CMDLINE_MIN_BUFFER_SIZE) { + return -1; + } + + uint32_t arguments[] = { (uintptr_t)buffer, buffer_length }; + return mp_semihosting_call(SYS_GET_CMDLINE, arguments); +} + +void mp_semihosting_heapinfo(mp_semihosting_heap_info_t *block) { + if (block == NULL) { + return; + } + + uint32_t arguments[] = { (uintptr_t)block }; + mp_semihosting_call(SYS_HEAPINFO, arguments); +} + +void mp_semihosting_exit(uint32_t code, uint32_t subcode) { + uint32_t arguments[] = { code, subcode }; + mp_semihosting_call(SYS_EXIT, arguments); + for (;;) {} +} + +void mp_semihosting_exit_extended(uint32_t code, uint32_t subcode) { + uint32_t arguments[] = { code, subcode }; + mp_semihosting_call(SYS_EXIT_EXTENDED, arguments); + for (;;) {} +} + +int mp_semihosting_elapsed(mp_semihosting_elapsed_ticks_t *ticks) { + if (ticks == NULL) { + return -1; + } + + uint32_t arguments[] = { (uintptr_t)ticks }; + return mp_semihosting_call(SYS_ELAPSED, arguments); +} + +inline int mp_semihosting_tickfreq(void) { + return mp_semihosting_call(SYS_TICKFREQ, NULL); +} + +void mp_semihosting_init() { + mp_check_extended_features_availability(); + mp_semihosting_stdout = mp_semihosting_open(":tt", "w"); + if (split_stdout_stderr) { + mp_semihosting_stderr = mp_semihosting_open(":tt", "a"); + } else { + mp_semihosting_stderr = mp_semihosting_stdout; + } +} + +void mp_check_extended_features_availability(void) { + int features_handle = mp_semihosting_open(":semihosting-features", "r"); + if (features_handle < 0) { + return; + } + + uint8_t magic_buffer[MAGIC_SIZE]; + if (mp_semihosting_flen(features_handle) < sizeof(magic_buffer)) { + mp_semihosting_close(features_handle); + return; + } + + if (mp_semihosting_read(features_handle, magic_buffer, sizeof(magic_buffer)) != 0) { + mp_semihosting_close(features_handle); + return; + } + + if (magic_buffer[0] != MAGIC_0 || + magic_buffer[1] != MAGIC_1 || + magic_buffer[2] != MAGIC_2 || + magic_buffer[3] != MAGIC_3) { + mp_semihosting_close(features_handle); + return; + } + + uint8_t features_byte = 0; + if (mp_semihosting_read(features_handle, &features_byte, sizeof(features_byte)) != 0) { + mp_semihosting_close(features_handle); + return; + } + + mp_semihosting_close(features_handle); + + exit_extended_available = (features_byte & 0x01) != 0; + split_stdout_stderr = (features_byte & 0x02) != 0; +} + +int mp_strlen(const char *string) { + int length = 0; + while (*string++ != 0) { + length += 1; + } + return length; +} + +int mp_lookup_open_mode(const char *mode) { + if (mode == NULL) { + return -1; + } + + int mode_found; + + switch (mode[0]) { + case 'r': + mode_found = 0x00; + break; + case 'w': + mode_found = 0x04; + break; + case 'a': + mode_found = 0x08; + break; + default: + return -1; + } + + switch (mode[1]) { + case 'b': + mode_found |= 0x01; + break; + case '+': + mode_found |= 0x02; + break; + case '\0': + return mode_found; + default: + return -1; + } + + switch (mode[2]) { + case 'b': + if (mode_found & 0x01) { + // 'b' was already seen. + return -1; + } + mode_found |= 1; + break; + case '+': + if (mode_found & 0x02) { + // '+' was already seen. + return -1; + } + mode_found |= 2; + break; + case '\0': + return mode_found; + default: + return -1; + } + + return mode[3] == '\0' ? mode_found : -1; +} + +int mp_semihosting_call(uint32_t num, void *arg) { + register uint32_t call_number_register __asm__ ("x10") = num; + register void *arguments_register __asm__ ("x11") = arg; + + __asm volatile ( + ".option push \n" // Transient options + ".option norvc \n" // Do not emit compressed instructions + ".align 4 \n" // 16 bytes alignment + "slli zero, zero, 0x1F \n" // Entry NOP + "ebreak \n" // Give control to the debugger + "srai zero, zero, 7 \n" // Semihosting call + ".option pop \n" // Restore previous options set + : "+r" (call_number_register) + : "r" (arguments_register) + : "memory" + ); + + return call_number_register; +} + +inline int mp_semihosting_rx_char() { + return mp_semihosting_call(SYS_READC, NULL); +} + +int mp_write_to_debug_console(const char *string, size_t length) { + if (length == 0) { + return 0; + } + + if (length == 1) { + mp_semihosting_writec(*string); + return 0; + } + + return mp_semihosting_write(mp_semihosting_stdout, string, length); +} + +void mp_semihosting_terminate(uint32_t code, uint32_t subcode) { + if (exit_extended_available) { + mp_semihosting_exit_extended(code, subcode); + } else { + mp_semihosting_exit(code, subcode); + } +} + +int mp_semihosting_tx_strn(const char *string, size_t length) { + if (string == NULL) { + return -1; + } + + return mp_write_to_debug_console(string, length); +} + +int mp_semihosting_tx_strn_cooked(const char *string, size_t length) { + if (string == NULL) { + return -1; + } + + if (length == 0) { + return 0; + } + + size_t current_offset = 0; + for (size_t index = 0; index < length; index++) { + if (string[index] != '\n') { + continue; + } + + mp_write_to_debug_console(string + current_offset, index - current_offset); + mp_semihosting_writec('\r'); + current_offset = index; + } + + return mp_write_to_debug_console(string + current_offset, length - current_offset); +} diff --git a/shared/runtime/semihosting_rv32.h b/shared/runtime/semihosting_rv32.h new file mode 100644 index 0000000000000..7cabd1692c1cf --- /dev/null +++ b/shared/runtime/semihosting_rv32.h @@ -0,0 +1,247 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Alessandro Gatti + * + * 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_SHARED_RUNTIME_SEMIHOSTING_RV32_H +#define MICROPY_INCLUDED_SHARED_RUNTIME_SEMIHOSTING_RV32_H + +/* + * To integrate semihosting, make sure to call mp_semihosting_init() first. + * Then, if the host system's STDOUT should be used instead of a UART, replace + * mp_hal_stdin_rx_chr and similar calls in mphalport.c with the semihosting + * equivalents. + * + * At runtime, make sure that the debugger is attached and semihosting is + * enabled on its end. The terminal must be configured in raw mode with local + * echo disabled, on Linux this can be done with "stty raw -echo" on the + * command line. + */ + +/* + * This follows the RISC-V Semihosting specification version 0.3. + * + * That document can be downloaded from + * https://github.com/riscv-non-isa/riscv-semihosting/releases/ + * + * Version 0.3 claims that the current RISC-V Semihosting implementation + * should follow Arm's, and more precisely the "Semihosting for AArch32 + * and AArch64" document, revision 2023Q3. + */ + +#include +#include +#include + +// A container for heap and stack pointers as returned by SYS_HEAPINFO. +typedef struct { + void *heap_base; + void *heap_limit; + void *stack_base; + void *stack_limit; +} mp_semihosting_heap_info_t; + +// A 64-bits value indicating how many ticks were counted since the target +// image's execution started. +typedef struct { + uint32_t low; + uint32_t high; +} mp_semihosting_elapsed_ticks_t; + +// The host system's STDOUT file handle. +extern int mp_semihosting_stdout; + +// The host system's STDERR file handle. If the host system does not support +// explicit STDOUT and STDERR handles, this handle will be aliased to STDOUT +// instead. +extern int mp_semihosting_stderr; + +/* + * Even though exit codes from 0x20000 to 0x20007 are part of the original Arm + * specification document, they are omitted due to them being tied to hardware + * events. Whilst some of them may still have a meaning on the RISC-V + * platform, it is not yet clear which ones are available and which ones are + * not. Thus, only "soft" error codes are provided here although the SYS_EXIT + * and SYS_EXIT_EXTENDED semihosting calls accept any 32-bits integer as an + * exit code. + */ + +enum { + MP_SEMIHOSTING_EXIT_BREAKPOINT = 0x20020, + MP_SEMIHOSTING_EXIT_WATCHPOINT, + MP_SEMIHOSTING_EXIT_STEP_COMPLETE, + MP_SEMIHOSTING_EXIT_RUNTIME_ERROR_UNKNOWN, + MP_SEMIHOSTING_EXIT_INTERNAL_ERROR, + MP_SEMIHOSTING_EXIT_USER_INTERRUPTION, + MP_SEMIHOSTING_EXIT_APPLICATION_EXIT, + MP_SEMIHOSTING_EXIT_STACK_OVERFLOW, + MP_SEMIHOSTING_EXIT_DIVISION_BY_ZERO, + MP_SEMIHOSTING_EXIT_OS_SPECIFIC +}; + +// Initialises semihosting support. +void mp_semihosting_init(); + +// Read a character from the host system's STDIN stream. +int mp_semihosting_rx_char(); + +// Write the given string to the host system's STDOUT stream. +int mp_semihosting_tx_strn(const char *string, size_t length); + +// Write the given string to the host system's STDOUT stream, writing a CR byte +// before each LF byte to be written. +int mp_semihosting_tx_strn_cooked(const char *string, size_t length); + +// Terminates execution with the given code and an optional subcode. This +// will choose the appropriate semihosting call (either SYS_EXIT or +// SYS_EXIT_EXTENDED) depending on the host system's reported capabilities. +noreturn void mp_semihosting_terminate(uint32_t code, uint32_t subcode); + +// Direct semihosting calls access. + +// Open a file on the host system with the given name and file mode. +// The file mode follows fopen(3)'s syntax. The function will return -1 if it +// failed to open the required file, or a file handle number if the operation +// succeeded. To see why the operation failed, call mp_semihosting_errno. +int mp_semihosting_open(const char *file_name, const char *file_mode); + +// Close a file previously opened with mp_semihosting_open. If the file cannot +// be closed, the function will return -1, otherwise it will return 0. To see +// why the operation failed, call mp_semihosting_errno. +int mp_semihosting_close(int handle); + +// Write the given character to the host system's STDOUT file handle. +void mp_semihosting_writec(char character); + +// Write the given NULL-terminated string to the host system's STDOUT file +// handle. +void mp_semihosting_write0(const char *string); + +// Write the given buffer to the given host system file handle. The function +// will return how many characters were left to be written (0 if the operation +// wrote the whole buffer), or -1 if the input buffer pointer is NULL. +int mp_semihosting_write(int handle, const void *data, size_t length); + +// Read from the given host system file handle into the given buffer. The +// function will return how many characters were left to be read (0 if the +// operation read whole buffer), or -1 if the input buffer pointer is NULL. +int mp_semihosting_read(int handle, void *data, size_t length); + +// Read a single character from the host system's STDIN file handle. +int mp_semihosting_readc(void); + +// Check whether the given result code represents an error. The function will +// return a non-zero value if the code is indeed an error, or zero otherwise. +int mp_semihosting_iserror(int code); + +// Check whether the given host system file handle is mapped to an interactive +// device. The function will return 1 if the handle is mapped to an +// interactive device, 0 if it is mapped to a regular file, and anything else +// if an error occurred. +int mp_semihosting_istty(int handle); + +// Move the file pointer on the given host system's file handle to the given +// absolute offset (in bytes). The function will return 0 if the file pointer +// was moved to the requested position, or a negative value if it was not +// possible to do so. To see why the operation failed, call +// mp_semihosting_errno. +int mp_semihosting_seek(int handle, uint32_t offset); + +// Get the length (in bytes) of the host system's file mapped to the given +// handle. The function will return a negative value if an error occurred, or +// the requested file's length (in bytes). +int mp_semihosting_flen(int handle); + +// Create a temporary file on the host system. The function requires a file +// identifier between 0 and 255 (inclusive) that will be bound to the requested +// temporary file. Subsequent calls to mp_semihosting_tmpnam with the same +// identifier will always return the same host system file name. On success, +// the function will fill the given buffer with the host system's file name, +// and will return 0. If the buffer pointer is NULL, the buffer name area is +// too small, or the operation failed on the host system's end, the function +// will return -1 instead. Make sure that the buffer is big enough to contain +// a host system's full path name. +int mp_semihosting_tmpnam(uint8_t identifier, void *buffer, size_t buffer_length); + +// Delete a file on the host system's matching the given file name. The +// function will return 0 if the deletion operation succeeded, or a host system +// dependent error code instead. +int mp_semihosting_remove(const char *file_name); + +// Rename a file on the host system's name matching the given file name to the +// new chosen name. The function will return 0 if the rename operation +// succeeded, or a host system dependent error code instead. +int mp_semihosting_rename(const char *old_name, const char *new_name); + +// Get how many hundredths of a second passed since execution started. If an +// error occurred whilst retrieving clock value, the function will return -1. +int mp_semihosting_clock(void); + +// Get the host system's clock in seconds since midnight of January 1st, 1970 +// at UTC. +int mp_semihosting_time(void); + +// Execute the given command on the host system. The function will return the +// command's result code retrieved on the host system. +int mp_semihosting_system(const char *command); + +// Get the last operation's status code. The function will return the host +// system's errno variable contents, and can be used to see the exact result +// code for failed I/O operations. +int mp_semihosting_errno(void); + +// Get the host system's command line that started execution of the target +// image. The function will fill the given buffer with the command line +// arguments passed to the target executable. The function will return 0 on +// success, or -1 if it failed. Make sure that the buffer can contain at +// least 80 bytes, as it is the minimum supported size defined by the +// specifications document. +int mp_semihosting_get_cmdline(void *buffer, size_t buffer_length); + +// Fill the given heap info structure with the system's stack and heap +// start/end addresses. +void mp_semihosting_heapinfo(mp_semihosting_heap_info_t *block); + +// Terminate the execution with the given reason code and optional subcode. +// This should be preferred over mp_semihosting_exit_extended if the host +// system does not support the SYS_EXIT_EXTENDED semihosting call. In doubt +// use mp_semihosting_terminate instead. +noreturn void mp_semihosting_exit(uint32_t code, uint32_t subcode); + +// Terminate the execution with the given reason code and optional subcode. +// This should be preferred over mp_semihosting_exit if the host system +// supports this semihosting call. In doubt use mp_semihosting_terminate +// instead. +noreturn void mp_semihosting_exit_extended(uint32_t code, uint32_t subcode); + +// Fill the given structure with how many ticks were counted since execution +// started. On success, the function will return 0, or -1 if it was not +// possible to compute the ticks count. +int mp_semihosting_elapsed(mp_semihosting_elapsed_ticks_t *ticks); + +// Get the system's tick frequency. If this value is not known, the function +// will return -1 instead. +int mp_semihosting_tickfreq(void); + +#endif // MICROPY_INCLUDED_SHARED_SEMIHOSTING_RUNTIME_RV32_H From d7aa2fe9d7efc30591e22ddcd276e85f1d13e9d2 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 1 Jun 2024 11:49:38 +0200 Subject: [PATCH 0030/1300] lib/libm: Define _IEEE_LIBM only if not set. fdilibm was originally meant to see _IEEE_LIBM defined from outside the libm code, not it being hardcoded in. Picolibc assumes this assumption holds true and attempts to define itself, conflicting with the existing definition. Signed-off-by: Alessandro Gatti --- lib/libm/wf_lgamma.c | 2 ++ lib/libm/wf_tgamma.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/libm/wf_lgamma.c b/lib/libm/wf_lgamma.c index 834a4de482223..4b07a7289288d 100644 --- a/lib/libm/wf_lgamma.c +++ b/lib/libm/wf_lgamma.c @@ -24,7 +24,9 @@ */ #include "fdlibm.h" +#ifndef _IEEE_LIBM #define _IEEE_LIBM 1 +#endif #ifdef __STDC__ float lgammaf(float x) diff --git a/lib/libm/wf_tgamma.c b/lib/libm/wf_tgamma.c index 3ff05f331d099..1a6c74ca11d86 100644 --- a/lib/libm/wf_tgamma.c +++ b/lib/libm/wf_tgamma.c @@ -24,7 +24,9 @@ #include "math.h" #include "fdlibm.h" +#ifndef _IEEE_LIBM #define _IEEE_LIBM 1 +#endif #ifdef __STDC__ float tgammaf(float x) From a066f2308f7b0d872352073cec0a945dca3a7a9c Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Jun 2024 11:14:28 +1000 Subject: [PATCH 0031/1300] py/lexer: Support concatenation of adjacent f-strings. This is quite a simple and small change to support concatenation of adjacent f-strings, and improve compatibility with CPython. Signed-off-by: Damien George --- py/lexer.c | 8 ++++++-- tests/basics/string_fstring.py | 10 ++++++++++ tests/cpydiff/core_fstring_concat.py | 5 ++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/py/lexer.c b/py/lexer.c index bff8e637656d6..2774759a15d47 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -336,8 +336,12 @@ static void parse_string_literal(mp_lexer_t *lex, bool is_raw, bool is_fstring) // assume there's going to be interpolation, so prep the injection data // fstring_args_idx==0 && len(fstring_args)>0 means we're extracting the args. // only when fstring_args_idx>0 will we consume the arg data - // note: lex->fstring_args will be empty already (it's reset when finished) - vstr_add_str(&lex->fstring_args, ".format("); + // lex->fstring_args is reset when finished, so at this point there are two cases: + // - lex->fstring_args is empty: start of a new f-string + // - lex->fstring_args is non-empty: concatenation of adjacent f-strings + if (vstr_len(&lex->fstring_args) == 0) { + vstr_add_str(&lex->fstring_args, ".format("); + } } #endif diff --git a/tests/basics/string_fstring.py b/tests/basics/string_fstring.py index 42d093b37b508..3a8969272d6dd 100644 --- a/tests/basics/string_fstring.py +++ b/tests/basics/string_fstring.py @@ -65,3 +65,13 @@ def foo(a, b): # Still allow ! in expressions. print(f"{'1' if a != '456' else '0'!r:8s}") print(f"{'1' if a != '456' else '0'!s:8s}") + +# Concatenation of adjacent f-strings. +print(f"" f"") +print(f"a" f"b") +print(f"{x}" f"{y}") +print( + f"a{x}b---------------------------------" + f"cd---------------------------------" + f"e{y}f---------------------------------" +) diff --git a/tests/cpydiff/core_fstring_concat.py b/tests/cpydiff/core_fstring_concat.py index c2bdb4e666a2d..83bf18820ece1 100644 --- a/tests/cpydiff/core_fstring_concat.py +++ b/tests/cpydiff/core_fstring_concat.py @@ -1,8 +1,8 @@ """ categories: Core -description: f-strings don't support concatenation with adjacent literals if the adjacent literals contain braces or are f-strings +description: f-strings don't support concatenation with adjacent literals if the adjacent literals contain braces cause: MicroPython is optimised for code space. -workaround: Use the + operator between literal strings when either or both are f-strings +workaround: Use the + operator between literal strings when they are not both f-strings """ x, y = 1, 2 @@ -10,4 +10,3 @@ print(f"{x}" "ab") # works print("a{}a" f"{x}") # fails print(f"{x}" "a{}b") # fails -print(f"{x}" f"{y}") # fails From 3c8089d1b10683ee31ddbaeebd0b18c47bf6d09d Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Jun 2024 17:31:19 +1000 Subject: [PATCH 0032/1300] py/lexer: Support raw f-strings. Support for raw str/bytes already exists, and extending that to raw f-strings is easy. It also reduces code size because it eliminates an error message. Signed-off-by: Damien George --- py/lexer.c | 14 ++++++-------- py/lexer.h | 1 - py/parse.c | 3 --- tests/basics/string_fstring.py | 4 ++++ tests/cmdline/cmd_parsetree.py.exp | 4 ++-- tests/cpydiff/core_fstring_raw.py | 8 -------- 6 files changed, 12 insertions(+), 22 deletions(-) delete mode 100644 tests/cpydiff/core_fstring_raw.py diff --git a/py/lexer.c b/py/lexer.c index 2774759a15d47..48497663c58d1 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -661,21 +661,19 @@ void mp_lexer_to_next(mp_lexer_t *lex) { } #if MICROPY_PY_FSTRINGS if (is_char_following(lex, 'f')) { - // raw-f-strings unsupported, immediately return (invalid) token. - lex->tok_kind = MP_TOKEN_FSTRING_RAW; - break; + is_fstring = true; + n_char = 2; } #endif } #if MICROPY_PY_FSTRINGS else if (is_char(lex, 'f')) { + is_fstring = true; + n_char = 1; if (is_char_following(lex, 'r')) { - // raw-f-strings unsupported, immediately return (invalid) token. - lex->tok_kind = MP_TOKEN_FSTRING_RAW; - break; + is_raw = true; + n_char = 2; } - n_char = 1; - is_fstring = true; } #endif diff --git a/py/lexer.h b/py/lexer.h index 2d9d0447b8ba3..e0b506b20b66e 100644 --- a/py/lexer.h +++ b/py/lexer.h @@ -46,7 +46,6 @@ typedef enum _mp_token_kind_t { MP_TOKEN_LONELY_STRING_OPEN, #if MICROPY_PY_FSTRINGS MP_TOKEN_MALFORMED_FSTRING, - MP_TOKEN_FSTRING_RAW, #endif MP_TOKEN_NEWLINE, diff --git a/py/parse.c b/py/parse.c index 1392303e6ff97..4ba9560734f04 100644 --- a/py/parse.c +++ b/py/parse.c @@ -1351,9 +1351,6 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { } else if (lex->tok_kind == MP_TOKEN_MALFORMED_FSTRING) { exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, MP_ERROR_TEXT("malformed f-string")); - } else if (lex->tok_kind == MP_TOKEN_FSTRING_RAW) { - exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, - MP_ERROR_TEXT("raw f-strings are not supported")); #endif } else { exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, diff --git a/tests/basics/string_fstring.py b/tests/basics/string_fstring.py index 3a8969272d6dd..d94cc0cd3e630 100644 --- a/tests/basics/string_fstring.py +++ b/tests/basics/string_fstring.py @@ -75,3 +75,7 @@ def foo(a, b): f"cd---------------------------------" f"e{y}f---------------------------------" ) + +# Raw f-strings. +print(rf"\r\a\w {'f'} \s\t\r\i\n\g") +print(fr"\r{x}") diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index 6ec553b8a9ae2..672c212a965b9 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -1,6 +1,6 @@ ---------------- [ 1] file_input_2(1) (n=10) - tok(6) + tok(5) [ 4] \(rule\|for_stmt\)(22) (n=4) id(i) [ 4] \(rule\|atom_paren\)(45) (n=1) @@ -9,7 +9,7 @@ NULL [ 6] \(rule\|expr_stmt\)(5) (n=2) id(a) - tok(16) + tok(15) [ 7] \(rule\|expr_stmt\)(5) (n=2) id(b) str(str) diff --git a/tests/cpydiff/core_fstring_raw.py b/tests/cpydiff/core_fstring_raw.py deleted file mode 100644 index 84e265f70fc52..0000000000000 --- a/tests/cpydiff/core_fstring_raw.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -categories: Core -description: Raw f-strings are not supported -cause: MicroPython is optimised for code space. -workaround: Unknown -""" - -rf"hello" From 80a4f632ee812de9f2b22e88abb36ca36d230ec0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 7 Jun 2024 11:43:06 +1000 Subject: [PATCH 0033/1300] rp2/cyw43_configport: Make cyw43_delay_ms() a busy loop. Currently, `cyw43_delay_ms()` calls `mp_hal_delay_ms()` which uses PendSV to set up a timer and wait for an interrupt, using wfe. But in the cyw43 initialisation stage PendSV is disabled and so this delay suspends on the wfe instruction for an indefinite amount of time. Work around this by changing the implementation of `cyw43_delay_ms()` to a busy loop. Fixes issue #15220. Signed-off-by: Damien George --- ports/rp2/cyw43_configport.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ports/rp2/cyw43_configport.h b/ports/rp2/cyw43_configport.h index b69cfbc263a28..7b1a1ae3d4f02 100644 --- a/ports/rp2/cyw43_configport.h +++ b/ports/rp2/cyw43_configport.h @@ -117,7 +117,12 @@ static inline void cyw43_delay_us(uint32_t us) { } static inline void cyw43_delay_ms(uint32_t ms) { - mp_hal_delay_ms(ms); + // PendSV may be disabled via CYW43_THREAD_ENTER, so this delay is a busy loop. + uint32_t us = ms * 1000; + uint32_t start = mp_hal_ticks_us(); + while (mp_hal_ticks_us() - start < us) { + mp_event_handle_nowait(); + } } #define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait() From df0d7e942986b65ed42fbbf75712639f4a83dc0e Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 7 Jun 2024 13:33:47 +1000 Subject: [PATCH 0034/1300] extmod/modlwip: Make socket.connect raise ETIMEDOUT on non-zero timeout. If the socket timeout is 0 then a failed socket.connect() raises EINPROGRESS (which is what the lwIP bindings already did), but if the socket timeout is non-zero then a failed socket.connect() should raise ETIMEDOUT. The latter is fixed in this commit. A test is added for these timeout cases. Signed-off-by: Damien George --- extmod/modlwip.c | 28 ++++++++++++++++------------ tests/net_hosted/connect_timeout.py | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 tests/net_hosted/connect_timeout.py diff --git a/extmod/modlwip.c b/extmod/modlwip.c index afac512e834de..1ae8c114c3dfb 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -326,6 +326,10 @@ typedef struct _lwip_socket_obj_t { int8_t state; } lwip_socket_obj_t; +static inline bool socket_is_timedout(lwip_socket_obj_t *socket, mp_uint_t ticks_start) { + return socket->timeout != -1 && (mp_uint_t)(mp_hal_ticks_ms() - ticks_start) >= socket->timeout; +} + static inline void poll_sockets(void) { mp_event_wait_ms(1); } @@ -1130,21 +1134,21 @@ static mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) { MICROPY_PY_LWIP_EXIT // And now we wait... - if (socket->timeout != -1) { - for (mp_uint_t retries = socket->timeout / 100; retries--;) { - mp_hal_delay_ms(100); - if (socket->state != STATE_CONNECTING) { - break; - } - } - if (socket->state == STATE_CONNECTING) { - mp_raise_OSError(MP_EINPROGRESS); + mp_uint_t ticks_start = mp_hal_ticks_ms(); + for (;;) { + poll_sockets(); + if (socket->state != STATE_CONNECTING) { + break; } - } else { - while (socket->state == STATE_CONNECTING) { - poll_sockets(); + if (socket_is_timedout(socket, ticks_start)) { + if (socket->timeout == 0) { + mp_raise_OSError(MP_EINPROGRESS); + } else { + mp_raise_OSError(MP_ETIMEDOUT); + } } } + if (socket->state == STATE_CONNECTED) { err = ERR_OK; } else { diff --git a/tests/net_hosted/connect_timeout.py b/tests/net_hosted/connect_timeout.py new file mode 100644 index 0000000000000..5f35047c8c892 --- /dev/null +++ b/tests/net_hosted/connect_timeout.py @@ -0,0 +1,24 @@ +# Test that socket.connect() on a socket with timeout raises EINPROGRESS or ETIMEDOUT appropriately. + +import errno +import socket + + +def test(peer_addr, timeout, expected_exc): + s = socket.socket() + s.settimeout(timeout) + try: + s.connect(peer_addr) + print("OK") + except OSError as er: + print(er.args[0] in expected_exc) + s.close() + + +if __name__ == "__main__": + # This test needs an address that doesn't respond to TCP connections. + # 1.1.1.1:8000 seem to reliably timeout, so use that. + addr = socket.getaddrinfo("1.1.1.1", 8000)[0][-1] + + test(addr, 0, (errno.EINPROGRESS,)) + test(addr, 1, (errno.ETIMEDOUT, "timed out")) # CPython uses a string instead of errno From 5903ee561c8e2fec7ecc8f08f221c27574090649 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 7 Jun 2024 13:36:30 +1000 Subject: [PATCH 0035/1300] extmod/modlwip: Consolidate socket.accept timeout logic. This makes the code a bit simpler to understand for the three cases of timeout behaviour (-1, 0, non-zero), and eliminates a dependency on the (slow) `mp_hal_delay_ms(100)` call. Signed-off-by: Damien George --- extmod/modlwip.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 1ae8c114c3dfb..bcee3f0ff1a93 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -1040,26 +1040,22 @@ static mp_obj_t lwip_socket_accept(mp_obj_t self_in) { // accept incoming connection struct tcp_pcb *volatile *incoming_connection = &lwip_socket_incoming_array(socket)[socket->incoming.connection.iget]; if (*incoming_connection == NULL) { - if (socket->timeout == 0) { + mp_uint_t ticks_start = mp_hal_ticks_ms(); + for (;;) { MICROPY_PY_LWIP_EXIT - m_del_obj(lwip_socket_obj_t, socket2); - mp_raise_OSError(MP_EAGAIN); - } else if (socket->timeout != -1) { - mp_uint_t retries = socket->timeout / 100; - while (*incoming_connection == NULL) { + poll_sockets(); + MICROPY_PY_LWIP_REENTER + if (*incoming_connection != NULL) { + break; + } + if (socket_is_timedout(socket, ticks_start)) { MICROPY_PY_LWIP_EXIT - if (retries-- == 0) { - m_del_obj(lwip_socket_obj_t, socket2); + m_del_obj(lwip_socket_obj_t, socket2); + if (socket->timeout == 0) { + mp_raise_OSError(MP_EAGAIN); + } else { mp_raise_OSError(MP_ETIMEDOUT); } - mp_hal_delay_ms(100); - MICROPY_PY_LWIP_REENTER - } - } else { - while (*incoming_connection == NULL) { - MICROPY_PY_LWIP_EXIT - poll_sockets(); - MICROPY_PY_LWIP_REENTER } } } From 9bfb4cec6e832c38eb04ad680a2b5e377830d3af Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Jun 2024 12:13:37 +1000 Subject: [PATCH 0036/1300] qemu-arm: Add license and copyright to files missing them. All of these files are first-party code written from scratch as part of this repository, and were added when the top-level MIT license was active, so they have an MIT license by default. Tracing back the git history show the original authors/source/copyright as follows: - main.c, mpconfigport.h: copied from the bare-arm port [1]. - test_main.c: added in [2]. - mphalport.h: added in [3] then updated in [4]. - mps2.ld, nrf51.ld, stm32.ld, uart.h: added in [4]. - imx6.ld, uart.c, startup.c: added in [4] and updated in [5]. [1] Commit c55721582282f282d33e35e458a2944890210ebc in 2014, the initial bare-arm port; see related ee857853d60a135685c5088e5492dfe6e8a5acb0. [2] Commit c1c32d65af038ba1b2a2a8dd69e3f7e63eac5f3e in 2014, initial qemu-arm CI tests. [3] Commit b0a15aa73540f82ef47c9492af7ce196d1b1a6e9 in 2016, enabling extmods and their tests. [4] Commit e7332b05841549a41614e522285639dcaa7bd526 in 2018, big refactor. [5] Commit b84406f3133a36a703a1506d754fc046dd955922 in 2021, adding Cortex-A9 support. Signed-off-by: Damien George --- ports/qemu-arm/imx6.ld | 5 +++++ ports/qemu-arm/main.c | 26 ++++++++++++++++++++++++++ ports/qemu-arm/mpconfigport.h | 26 ++++++++++++++++++++++++++ ports/qemu-arm/mphalport.h | 26 ++++++++++++++++++++++++++ ports/qemu-arm/mps2.ld | 5 +++++ ports/qemu-arm/nrf51.ld | 5 +++++ ports/qemu-arm/startup.c | 26 ++++++++++++++++++++++++++ ports/qemu-arm/stm32.ld | 5 +++++ ports/qemu-arm/test_main.c | 26 ++++++++++++++++++++++++++ ports/qemu-arm/uart.c | 26 ++++++++++++++++++++++++++ ports/qemu-arm/uart.h | 26 ++++++++++++++++++++++++++ 11 files changed, 202 insertions(+) diff --git a/ports/qemu-arm/imx6.ld b/ports/qemu-arm/imx6.ld index 7155956f15ad5..c6d8e0818111b 100644 --- a/ports/qemu-arm/imx6.ld +++ b/ports/qemu-arm/imx6.ld @@ -1,3 +1,8 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2021 Damien P. George + */ + /* Vector table is at 0x00000000, entry point is 0x10000000. */ MEMORY diff --git a/ports/qemu-arm/main.c b/ports/qemu-arm/main.c index f57c03fb9ec84..212fd60215178 100644 --- a/ports/qemu-arm/main.c +++ b/ports/qemu-arm/main.c @@ -1,3 +1,29 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2023 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 #include #include diff --git a/ports/qemu-arm/mpconfigport.h b/ports/qemu-arm/mpconfigport.h index ccee777752bdf..fce379e47ecf4 100644 --- a/ports/qemu-arm/mpconfigport.h +++ b/ports/qemu-arm/mpconfigport.h @@ -1,3 +1,29 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2024 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 // options to control how MicroPython is built diff --git a/ports/qemu-arm/mphalport.h b/ports/qemu-arm/mphalport.h index 29f080805f53a..d854cd82d9928 100644 --- a/ports/qemu-arm/mphalport.h +++ b/ports/qemu-arm/mphalport.h @@ -1,3 +1,29 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016-2018 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 #include "uart.h" diff --git a/ports/qemu-arm/mps2.ld b/ports/qemu-arm/mps2.ld index 5c1aa21ca249b..dffefa3b00254 100644 --- a/ports/qemu-arm/mps2.ld +++ b/ports/qemu-arm/mps2.ld @@ -1,3 +1,8 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2018 Damien P. George + */ + MEMORY { RAM : ORIGIN = 0x00000000, LENGTH = 4M diff --git a/ports/qemu-arm/nrf51.ld b/ports/qemu-arm/nrf51.ld index 70269773cb9f7..46d7201b32aed 100644 --- a/ports/qemu-arm/nrf51.ld +++ b/ports/qemu-arm/nrf51.ld @@ -1,3 +1,8 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2018 Damien P. George + */ + MEMORY { ROM : ORIGIN = 0x00000000, LENGTH = 1M diff --git a/ports/qemu-arm/startup.c b/ports/qemu-arm/startup.c index a52b068c332ae..a1e89d111c007 100644 --- a/ports/qemu-arm/startup.c +++ b/ports/qemu-arm/startup.c @@ -1,3 +1,29 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2021 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 #include #include diff --git a/ports/qemu-arm/stm32.ld b/ports/qemu-arm/stm32.ld index 4e541526b70b9..835f9bbe970fd 100644 --- a/ports/qemu-arm/stm32.ld +++ b/ports/qemu-arm/stm32.ld @@ -1,3 +1,8 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2018 Damien P. George + */ + MEMORY { ROM : ORIGIN = 0x00000000, LENGTH = 1M diff --git a/ports/qemu-arm/test_main.c b/ports/qemu-arm/test_main.c index bd0a9918da565..13d18bb0f094b 100644 --- a/ports/qemu-arm/test_main.c +++ b/ports/qemu-arm/test_main.c @@ -1,3 +1,29 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Ilya Dmitrichenko + * + * 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 #include #include diff --git a/ports/qemu-arm/uart.c b/ports/qemu-arm/uart.c index 828398cd175a8..d7338f9a24230 100644 --- a/ports/qemu-arm/uart.c +++ b/ports/qemu-arm/uart.c @@ -1,3 +1,29 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2021 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 #include diff --git a/ports/qemu-arm/uart.h b/ports/qemu-arm/uart.h index a89e3f26e209f..b2b9e523cd60f 100644 --- a/ports/qemu-arm/uart.h +++ b/ports/qemu-arm/uart.h @@ -1,2 +1,28 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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. + */ + void uart_init(void); void uart_tx_strn(const char *buf, size_t len); From d7d77d91becd0716ac1672c92652d9dd72ec613f Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Jun 2024 16:31:49 +1000 Subject: [PATCH 0037/1300] qemu-arm: Clean up header file includes. Signed-off-by: Damien George --- ports/qemu-arm/main.c | 5 ----- ports/qemu-arm/mphalport.h | 1 - ports/qemu-arm/test_main.c | 4 ---- ports/qemu-arm/uart.h | 6 ++++++ 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/ports/qemu-arm/main.c b/ports/qemu-arm/main.c index 212fd60215178..025c1f17da04a 100644 --- a/ports/qemu-arm/main.c +++ b/ports/qemu-arm/main.c @@ -24,18 +24,13 @@ * THE SOFTWARE. */ -#include #include #include -#include -#include -#include "py/obj.h" #include "py/compile.h" #include "py/runtime.h" #include "py/stackctrl.h" #include "py/gc.h" -#include "py/repl.h" #include "py/mperrno.h" void do_str(const char *src, mp_parse_input_kind_t input_kind) { diff --git a/ports/qemu-arm/mphalport.h b/ports/qemu-arm/mphalport.h index d854cd82d9928..8a40505ba6b3a 100644 --- a/ports/qemu-arm/mphalport.h +++ b/ports/qemu-arm/mphalport.h @@ -24,7 +24,6 @@ * THE SOFTWARE. */ -#include #include "uart.h" #define mp_hal_stdin_rx_chr() (0) diff --git a/ports/qemu-arm/test_main.c b/ports/qemu-arm/test_main.c index 13d18bb0f094b..96984f7cd16ed 100644 --- a/ports/qemu-arm/test_main.c +++ b/ports/qemu-arm/test_main.c @@ -24,12 +24,8 @@ * THE SOFTWARE. */ -#include #include #include -#include -#include -#include #include "py/compile.h" #include "py/runtime.h" diff --git a/ports/qemu-arm/uart.h b/ports/qemu-arm/uart.h index b2b9e523cd60f..33eae05bd76dd 100644 --- a/ports/qemu-arm/uart.h +++ b/ports/qemu-arm/uart.h @@ -23,6 +23,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#ifndef MICROPY_INCLUDED_QEMU_ARM_UART_H +#define MICROPY_INCLUDED_QEMU_ARM_UART_H + +#include void uart_init(void); void uart_tx_strn(const char *buf, size_t len); + +#endif // MICROPY_INCLUDED_QEMU_ARM_UART_H From 2d69aab7b350810322a4cba2667626949ee3d974 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 31 Oct 2023 21:55:07 +0100 Subject: [PATCH 0038/1300] qemu-riscv: Add new QEMU RV32 port. This adds a QEMU-based bare metal RISC-V 32 bits port. For the time being only QEMU's "virt" 32 bits board is supported, using the ilp32 ABI and the RV32IMC architecture. The top-level README and the run-tests.py files are updated for this new port. Signed-off-by: Alessandro Gatti --- README.md | 3 +- ports/qemu-riscv/Makefile | 128 +++++++++++++ ports/qemu-riscv/Makefile.test | 31 +++ ports/qemu-riscv/README.md | 29 +++ ports/qemu-riscv/entrypoint.s | 70 +++++++ ports/qemu-riscv/interrupts.c | 292 +++++++++++++++++++++++++++++ ports/qemu-riscv/mpconfigport.h | 76 ++++++++ ports/qemu-riscv/mphalport.h | 32 ++++ ports/qemu-riscv/qstrdefsport.h | 2 + ports/qemu-riscv/startup.c | 86 +++++++++ ports/qemu-riscv/test_main.c | 66 +++++++ ports/qemu-riscv/tests_profile.txt | 53 ++++++ ports/qemu-riscv/uart.c | 45 +++++ ports/qemu-riscv/uart.h | 33 ++++ ports/qemu-riscv/virt.ld | 98 ++++++++++ tests/run-tests.py | 9 + 16 files changed, 1052 insertions(+), 1 deletion(-) create mode 100644 ports/qemu-riscv/Makefile create mode 100644 ports/qemu-riscv/Makefile.test create mode 100644 ports/qemu-riscv/README.md create mode 100644 ports/qemu-riscv/entrypoint.s create mode 100644 ports/qemu-riscv/interrupts.c create mode 100644 ports/qemu-riscv/mpconfigport.h create mode 100644 ports/qemu-riscv/mphalport.h create mode 100644 ports/qemu-riscv/qstrdefsport.h create mode 100644 ports/qemu-riscv/startup.c create mode 100644 ports/qemu-riscv/test_main.c create mode 100644 ports/qemu-riscv/tests_profile.txt create mode 100644 ports/qemu-riscv/uart.c create mode 100644 ports/qemu-riscv/uart.h create mode 100644 ports/qemu-riscv/virt.ld diff --git a/README.md b/README.md index f5bc6d78f0a40..a20e0aaf13a28 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,8 @@ In addition, the following ports are provided in this repository: - [nrf](ports/nrf) -- Nordic Semiconductor nRF51 and nRF52. - [pic16bit](ports/pic16bit) -- Microchip PIC 16-bit. - [powerpc](ports/powerpc) -- IBM PowerPC (including Microwatt) - - [qemu-arm](ports/qemu-arm) -- QEMU-based emulated target, for testing) + - [qemu-arm](ports/qemu-arm) -- QEMU-based Arm emulated target (for testing) + - [qemu-riscv](ports/qemu-riscv) -- QEMU-based RISC-V emulated target (for testing) - [renesas-ra](ports/renesas-ra) -- Renesas RA family. - [rp2](ports/rp2) -- Raspberry Pi RP2040 (including Pico and Pico W). - [samd](ports/samd) -- Microchip (formerly Atmel) SAMD21 and SAMD51. diff --git a/ports/qemu-riscv/Makefile b/ports/qemu-riscv/Makefile new file mode 100644 index 0000000000000..6f15ce52e73c7 --- /dev/null +++ b/ports/qemu-riscv/Makefile @@ -0,0 +1,128 @@ +include ../../py/mkenv.mk +-include mpconfigport.mk + +# qstr definitions (must come before including py.mk) +QSTR_DEFS = qstrdefsport.h + +# MicroPython feature configurations +MICROPY_ROM_TEXT_COMPRESSION ?= 1 + +# include py core make definitions +include $(TOP)/py/py.mk +include $(TOP)/extmod/extmod.mk + +BOARD ?= virt + +CROSS_COMPILE ?= riscv64-unknown-elf- + +GCC_VERSION = $(word 1, $(subst ., , $(shell $(CC) -dumpversion))) + +# If Picolibc is available then select it explicitly. Ubuntu 22.04 ships its +# bare metal RISC-V toolchain with Picolibc rather than Newlib, and the default +# is "nosys" so a value must be provided. To avoid having per-distro +# workarounds, always select Picolibc if available. +PICOLIBC_SPECS = $(shell $(CC) --print-file-name=picolibc.specs) +ifeq ($(PICOLIBC_SPECS),picolibc.specs) +# Picolibc was not found. +SPECS_FRAGMENT = +else +SPECS_FRAGMENT = --specs=$(PICOLIBC_SPECS) +CFLAGS += $(SPECS_FRAGMENT) +endif + +ifeq ($(BOARD),virt) +ABI = ilp32 +# GCC 10 and lower do not recognise the Zicsr extension in the +# architecture name. "Make" unfortunately does not provide any simple way +# to perform numeric comparisons, so to keep things simple we assume that +# GCC is at least version 10 for the time being. +ifeq ($(GCC_VERSION),10) +ARCH ?= rv32imac +else +# Recent GCC versions explicitly require to declare extensions. +ARCH ?= rv32imac_zicsr +ARCH_LD ?= rv32imac_zicsr +endif +AFLAGS = -mabi=$(ABI) -march=$(ARCH) +CFLAGS += $(AFLAGS) +CFLAGS += -DQEMU_SOC_VIRT +ARCH_LD ?= $(ARCH) +LDSCRIPT = virt.ld +LDFLAGS += -mabi=$(ABI) -march=$(ARCH_LD) -T $(LDSCRIPT) -Wl,-EL +SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_rv32i.o +SRC_BOARD_O += entrypoint.o +endif + +INC += -I. +INC += -I$(TOP) +INC += -I$(BUILD) + +CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Werror -std=gnu99 $(COPT) \ + -ffunction-sections -fdata-sections +CFLAGS += $(CFLAGS_EXTRA) + +LD = $(CC) + +# Debugging/Optimization +ifeq ($(DEBUG), 1) +CFLAGS += -g +COPT = -O0 +else +COPT += -Os -DNDEBUG +endif + +LDFLAGS += $(SPECS_FRAGMENT) -Wl,--gc-sections -Wl,-Map=$(@:.elf=.map) + +SRC_COMMON_C = \ + interrupts.c \ + startup.c \ + uart.c \ + shared/libc/string0.c \ + shared/runtime/sys_stdio_mphal.c \ + +SRC_RUN_C = \ + ports/qemu-arm/main.c \ + +SRC_TEST_C = \ + test_main.c \ + lib/tinytest/tinytest.c \ + +LIB_SRC_C += $(SRC_LIB_LIBM_C) +LIB_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C) + +OBJ_COMMON = +OBJ_COMMON += $(PY_O) +OBJ_COMMON += $(addprefix $(BUILD)/, $(SRC_COMMON_C:.c=.o)) +OBJ_COMMON += $(addprefix $(BUILD)/, $(SRC_BOARD_O)) +OBJ_COMMON += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) + +OBJ_RUN = +OBJ_RUN += $(addprefix $(BUILD)/, $(SRC_RUN_C:.c=.o)) + +ALL_OBJ_RUN = $(OBJ_COMMON) $(OBJ_RUN) + +OBJ_TEST = +OBJ_TEST += $(addprefix $(BUILD)/, $(SRC_TEST_C:.c=.o)) + +ALL_OBJ_TEST = $(OBJ_COMMON) $(OBJ_TEST) + +# All object files, needed to get dependencies correct +OBJ = $(OBJ_COMMON) $(OBJ_RUN) $(OBJ_TEST) + +# List of sources for qstr extraction +SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C) + +all: run + +# `make debug` will block QEMU until a debugger is connected to port 1234. +debug: $(BUILD)/firmware.elf + qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -serial mon:stdio -S -s -kernel $< + +run: $(BUILD)/firmware.elf + qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< + +$(BUILD)/firmware.elf: $(LDSCRIPT) $(ALL_OBJ_RUN) + $(Q)$(LD) $(LDFLAGS) -o $@ $(ALL_OBJ_RUN) $(LIBS) + $(Q)$(SIZE) $@ + +include $(TOP)/py/mkrules.mk diff --git a/ports/qemu-riscv/Makefile.test b/ports/qemu-riscv/Makefile.test new file mode 100644 index 0000000000000..df64a8d7cc920 --- /dev/null +++ b/ports/qemu-riscv/Makefile.test @@ -0,0 +1,31 @@ +LIB_SRC_C = shared/upytesthelper/upytesthelper.c + +include Makefile + +CFLAGS += -DTEST + +.PHONY: $(BUILD)/genhdr/tests.h + +TESTS_PROFILE = $(dir $(abspath $(firstword $(MAKEFILE_LIST))))/tests_profile.txt + +$(BUILD)/test_main.o: $(BUILD)/genhdr/tests.h +$(BUILD)/genhdr/tests.h: + (cd $(TOP)/tests; ./run-tests.py --target=qemu-riscv --write-exp) + $(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py --profile $(TESTS_PROFILE) $(addprefix --exclude ,$(TESTS_EXCLUDE))) > $@ + +$(BUILD)/lib/tinytest/tinytest.o: CFLAGS += -DNO_FORKING + +$(BUILD)/firmware-test.elf: $(LDSCRIPT) $(ALL_OBJ_TEST) + $(Q)$(LD) $(LDFLAGS) -o $@ $(ALL_OBJ_TEST) $(LIBS) + $(Q)$(SIZE) $@ + +# Note: Using timeout(1) to handle cases where qemu hangs (e.g. this can happen with alignment errors). +test: $(BUILD)/firmware-test.elf + timeout --foreground -k 5s 60s qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< > $(BUILD)/console.out + $(Q)tail -n2 $(BUILD)/console.out + $(Q)tail -n1 $(BUILD)/console.out | grep -q "status: 0" + +# `make debugtest` will block QEMU until a debugger is connected to port 1234. + +debugtest: $(BUILD)/firmware-test.elf + qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -serial mon:stdio -S -s -kernel $< diff --git a/ports/qemu-riscv/README.md b/ports/qemu-riscv/README.md new file mode 100644 index 0000000000000..50b1a65374d2b --- /dev/null +++ b/ports/qemu-riscv/README.md @@ -0,0 +1,29 @@ +This is an experimental, community-supported port for RISC-V RV32IMC emulation +as provided by QEMU (http://qemu.org). + +The purposes of this port are to enable: + +1. Continuous integration + - run tests against architecture-specific parts of code base +2. Experimentation + - simulation & prototyping of anything that has architecture-specific + code + - exploring instruction set in terms of optimising some part of + MicroPython or a module +3. Streamlined debugging + - no need for JTAG or even an MCU chip itself + - no need to use OpenOCD or anything else that might slow down the + process in terms of plugging things together, pressing buttons, etc. + +This port requires a bare metal RISC-V toolchain with GCC 10 or later, either +with multilib support or 32 bits specific (M, C, and Zicsr extensions must be +supported, along with ilp32 ABI). Both newlib and picolibc are supported, +with the latter having precedence if found. + +Most pre-built toolchains should work out of the box, either coming from your +Linux distribution's package manager, or independently packaged ones like +[xPack](https://xpack.github.io/dev-tools/riscv-none-elf-gcc/). + +To build and run the image with builtin testsuite: + + make -f Makefile.test test diff --git a/ports/qemu-riscv/entrypoint.s b/ports/qemu-riscv/entrypoint.s new file mode 100644 index 0000000000000..9144c0c485421 --- /dev/null +++ b/ports/qemu-riscv/entrypoint.s @@ -0,0 +1,70 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Alessandro Gatti + * + * 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. + */ + + .section .start + .option norvc /* Do not emit compressed instructions. */ + .type start, @function + .global start + +start: + .cfi_startproc + + .option push + .option norelax + la gp, _global_pointer /* Load global pointer register. */ + .option pop + + csrw satp, zero /* Disable supervisor mode. */ + + /* Fill stack with a canary value. */ + + li t0, 0xBBBBBBBB /* Load canary value. */ + la t1, _sstack /* Load stack area start address. */ + la t2, _estack /* Load stack area end address. */ +1: + sw t0, (t1) /* Write canary. */ + addi t1, t1, 4 /* Next word. */ + bltu t1, t2, 1b /* Loop until stack is filled. */ + + la sp, _estack /* Load stack pointer. */ + + /* Clear BSS area. */ + + la t1, _sbss /* Load BSS area start address. */ + la t2, _ebss /* Load BSS area end address. */ +1: + sw zero, (t1) /* Clear word. */ + addi t1, t1, 4 /* Next word. */ + bltu t1, t2, 1b /* Loop until BSS is cleared. */ + + /* Set program counter. */ + + la t0, _entry_point /* Load program counter address. */ + csrw mepc, t0 /* Store it into machine exception PC. */ + tail _entry_point /* Jump to entry point. */ + + .cfi_endproc + .end diff --git a/ports/qemu-riscv/interrupts.c b/ports/qemu-riscv/interrupts.c new file mode 100644 index 0000000000000..ccb4644f9d5b9 --- /dev/null +++ b/ports/qemu-riscv/interrupts.c @@ -0,0 +1,292 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Alessandro Gatti + * + * 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 +#include +#include + +// Vector table + +void mtvec_table(void) __attribute__((naked, section(".text.mtvec"), aligned(256))); + +// Default interrupts + +#define ASSIGN_EMPTY_MACHINE_INTERRUPT(interrupt_name) \ + void interrupt_name(void) __attribute__((alias("mtvec_nop"))) + +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_ssi); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_msi); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_sti); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_mti); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_sei); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_mei); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq0); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq1); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq2); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq3); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq4); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq5); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq6); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq7); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq8); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq9); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq10); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq11); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq12); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq13); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq14); +ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq15); + +void mtvec_table(void) { + __asm volatile ( + ".org mtvec_table + 0 \n" + "jal zero, mtvec_exception \n" // Exception Handler + ".org mtvec_table + 4 \n" + "jal zero, mtvec_ssi \n" // Supervisor Software Interrupt + ".org mtvec_table + 12 \n" + "jal zero, mtvec_msi \n" // Machine Software Interrupt + ".org mtvec_table + 20 \n" + "jal zero, mtvec_sti \n" // Supervisor Timer Interrupt + ".org mtvec_table + 28 \n" + "jal zero, mtvec_mti \n" // Machine Timer Interrupt + ".org mtvec_table + 36 \n" + "jal zero, mtvec_sei \n" // Supervisor External Interrupt + ".org mtvec_table + 44 \n" + "jal zero, mtvec_mei \n" // Machine External Interrupt + // Not sure how many platform interrupts QEMU handles... + ".org mtvec_table + 48 \n" + "jal mtvec_plat_irq0 \n" // Platform Interrupt #0 + "jal mtvec_plat_irq1 \n" // Platform Interrupt #1 + "jal mtvec_plat_irq2 \n" // Platform Interrupt #2 + "jal mtvec_plat_irq3 \n" // Platform Interrupt #3 + "jal mtvec_plat_irq4 \n" // Platform Interrupt #4 + "jal mtvec_plat_irq5 \n" // Platform Interrupt #5 + "jal mtvec_plat_irq6 \n" // Platform Interrupt #6 + "jal mtvec_plat_irq7 \n" // Platform Interrupt #7 + "jal mtvec_plat_irq8 \n" // Platform Interrupt #8 + "jal mtvec_plat_irq9 \n" // Platform Interrupt #9 + "jal mtvec_plat_irq10 \n" // Platform Interrupt #10 + "jal mtvec_plat_irq11 \n" // Platform Interrupt #11 + "jal mtvec_plat_irq12 \n" // Platform Interrupt #12 + "jal mtvec_plat_irq13 \n" // Platform Interrupt #13 + "jal mtvec_plat_irq14 \n" // Platform Interrupt #14 + "jal mtvec_plat_irq15 \n" // Platform Interrupt #15 + : + : + : "memory" + ); +} + +volatile uint32_t registers_copy[35] = { 0 }; + +const char *exception_causes[] = { + "Reserved", // 0 + "Supervisor software interrupt", // 1 + "Machine software interrupt", // 2 + "Supervisor timer interrupt", // 3 + "Machine timer interrupt", // 4 + "Supervisor external interrupt", // 5 + "Machine external interrupt", // 6 + "Designated for platform use", // 7 + "Instruction address misaligned", // 8 + "Instruction address fault", // 9 + "Illegal instruction", // 10 + "Breakpoint", // 11 + "Load address misaligned", // 12 + "Load address fault", // 13 + "Store/AMO address misaligned", // 14 + "Store/AMO access fault", // 15 + "Environment call from U-mode", // 16 + "Environment call from S-mode", // 17 + "Environment call from M-mode", // 18 + "Instruction page fault", // 19 + "Load page fault", // 20 + "Store/AMO page fault", // 21 + "Designated for custom use" // 22 +}; + +const char *lookup_cause(uint32_t mcause) { + if (mcause & 0x80000000) { + switch (mcause & 0x7FFFFFFF) { + case 1: + return exception_causes[1]; + case 3: + return exception_causes[2]; + case 5: + return exception_causes[3]; + case 7: + return exception_causes[4]; + case 9: + return exception_causes[5]; + case 11: + return exception_causes[6]; + default: + return (mcause >= 16) ? + exception_causes[7] : + exception_causes[0]; + } + } + + switch (mcause) { + case 0: + return exception_causes[8]; + case 1: + return exception_causes[9]; + case 2: + return exception_causes[10]; + case 3: + return exception_causes[11]; + case 4: + return exception_causes[12]; + case 5: + return exception_causes[13]; + case 6: + return exception_causes[14]; + case 7: + return exception_causes[15]; + case 8: + return exception_causes[16]; + case 9: + return exception_causes[17]; + case 11: + return exception_causes[18]; + case 12: + return exception_causes[19]; + case 13: + return exception_causes[20]; + case 15: + return exception_causes[21]; + default: { + if ((mcause >= 24 && mcause <= 31) || + (mcause >= 48 && mcause <= 63)) { + return exception_causes[22]; + } + + return exception_causes[0]; + } + } +} + +#pragma GCC push_options +#pragma GCC optimize ("align-functions=4") + +__attribute__((interrupt("machine"), weak)) void mtvec_nop(void) { +} + +__attribute__((interrupt("machine"), weak)) void mtvec_exception(void) { + __asm volatile ( + "csrrw x31, mscratch, x31 \n" // Save X31 + "la x31, registers_copy \n" // Load target address + "sw x1, 0(x31) \n" // Save X1 + "sw x2, 4(x31) \n" // Save X2 + "sw x3, 8(x31) \n" // Save X3 + "sw x4, 12(x31) \n" // Save X4 + "sw x5, 16(x31) \n" // Save X5 + "sw x6, 20(x31) \n" // Save X6 + "sw x7, 24(x31) \n" // Save X7 + "sw x8, 28(x31) \n" // Save X8 + "sw x9, 32(x31) \n" // Save X9 + "sw x10, 36(x31) \n" // Save X10 + "sw x11, 40(x31) \n" // Save X11 + "sw x12, 44(x31) \n" // Save X12 + "sw x13, 48(x31) \n" // Save X13 + "sw x14, 52(x31) \n" // Save X14 + "sw x15, 56(x31) \n" // Save X15 + "sw x16, 60(x31) \n" // Save X16 + "sw x17, 64(x31) \n" // Save X17 + "sw x18, 68(x31) \n" // Save X18 + "sw x19, 72(x31) \n" // Save X19 + "sw x20, 76(x31) \n" // Save X20 + "sw x21, 80(x31) \n" // Save X21 + "sw x22, 84(x31) \n" // Save X22 + "sw x23, 88(x31) \n" // Save X23 + "sw x24, 92(x31) \n" // Save X24 + "sw x25, 98(x31) \n" // Save X25 + "sw x26, 100(x31) \n" // Save X26 + "sw x27, 104(x31) \n" // Save X27 + "sw x28, 108(x31) \n" // Save X28 + "sw x29, 112(x31) \n" // Save X29 + "sw x30, 116(x31) \n" // Save X30 + "csrr x30, mscratch \n" // Restore X31 + "sw x30, 120(x31) \n" // Save X31 + "csrr x30, mepc \n" // Load MEPC + "sw x30, 124(x31) \n" // Save MEPC + "csrr x30, mcause \n" // Load MCAUSE + "sw x30, 128(x31) \n" // Save MCAUSE + "csrr x30, mtval \n" // Load MTVAL + "sw x30, 132(x31) \n" // Save MTVAL + "csrr x30, mstatus \n" // Load MSTATUS + "sw x30, 138(x31) \n" // Save MSTATUS + "lw x30, 116(x31) \n" // Restore X30 + "lw x31, 120(x31) \n" // Restore X31 + : + : + : "memory" + ); + + printf("\nMACHINE EXCEPTION CAUGHT:\n\n"); + + printf(" RA=%08lX SP=%08lX GP=%08lX TP=%08lX T0=%08lX T1=%08lX\n", + registers_copy[0], registers_copy[1], registers_copy[2], + registers_copy[3], registers_copy[4], registers_copy[5]); + printf(" T2=%08lX S0=%08lX S1=%08lX A0=%08lX A1=%08lX A2=%08lX\n", + registers_copy[6], registers_copy[7], registers_copy[8], + registers_copy[9], registers_copy[10], registers_copy[11]); + printf(" A3=%08lX A4=%08lX A5=%08lX A6=%08lX A7=%08lX S2=%08lX\n", + registers_copy[12], registers_copy[13], registers_copy[14], + registers_copy[15], registers_copy[16], registers_copy[17]); + printf(" S3=%08lX S4=%08lX S5=%08lX S6=%08lX S7=%08lX S8=%08lX\n", + registers_copy[18], registers_copy[19], registers_copy[20], + registers_copy[21], registers_copy[22], registers_copy[23]); + printf(" S9=%08lX S10=%08lX S11=%08lX T3=%08lX T4=%08lX T5=%08lX\n", + registers_copy[24], registers_copy[25], registers_copy[26], + registers_copy[27], registers_copy[28], registers_copy[29]); + printf(" T6=%08lX\n\n", registers_copy[30]); + + printf(" MEPC=%08lX MTVAL=%08lX MSTATUS=%08lx MCAUSE=%08lx (%s)\n", + registers_copy[31], registers_copy[33], registers_copy[34], + registers_copy[32], lookup_cause(registers_copy[32])); + + exit(-1); +} + +#pragma GCC pop_options + +void set_interrupt_table(void) { + __asm volatile ( + "csrrci s0, mstatus, 8 \n" // S0 = MSTATUS & ~MIE + "csrw mstatus, s0 \n" // Global machine interrupts are disabled + "csrw mie, zero \n" // Disable machine interrupts + "csrw mip, zero \n" // Clear pending machine interrupts + "addi s0, %0, 1 \n" // Vectored machine interrupts enabled + "csrw mtvec, s0 \n" // Set new machine vector table + "csrrsi s0, mstatus, 8 \n" // S0 = MSTATUS | MIE + "csrw mstatus, s0 \n" // Global machine interrupts are enabled + : + : "r" (mtvec_table) + : "memory" + ); +} diff --git a/ports/qemu-riscv/mpconfigport.h b/ports/qemu-riscv/mpconfigport.h new file mode 100644 index 0000000000000..8f50828e0f1a4 --- /dev/null +++ b/ports/qemu-riscv/mpconfigport.h @@ -0,0 +1,76 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2024 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 + +// options to control how MicroPython is built + +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) + +#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) +#define MICROPY_MEM_STATS (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_KBD_EXCEPTION (0) +#define MICROPY_HELPER_REPL (0) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#define MICROPY_WARNINGS (1) +#define MICROPY_PY_BUILTINS_INPUT (0) +#define MICROPY_PY_BUILTINS_HELP (0) +#define MICROPY_PY_IO_IOBASE (0) +#define MICROPY_PY_SYS_PLATFORM "qemu-riscv32" +#define MICROPY_PY_SYS_STDFILES (0) +#define MICROPY_PY_SYS_STDIO_BUFFER (0) +#define MICROPY_PY_SELECT (0) +#define MICROPY_PY_TIME (0) +#define MICROPY_PY_ASYNCIO (0) +#define MICROPY_PY_MACHINE (1) +#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/qemu-arm/modmachine.c" +#define MICROPY_PY_MACHINE_PIN_BASE (1) +#define MICROPY_VFS (1) + +// type definitions for the specific machine + +#define MP_SSIZE_MAX (0x7fffffff) + +#define UINT_FMT "%lu" +#define INT_FMT "%ld" + +typedef int32_t mp_int_t; // must be pointer size +typedef uint32_t mp_uint_t; // must be pointer size +typedef long mp_off_t; + +// We need an implementation of the log2 function which is not a macro. +#define MP_NEED_LOG2 (1) + +// We need to provide a declaration/definition of alloca() +#include + +#ifdef TEST +#include "shared/upytesthelper/upytesthelper.h" +#undef MP_PLAT_PRINT_STRN +#define MP_PLAT_PRINT_STRN(str, len) upytest_output(str, len) +#endif diff --git a/ports/qemu-riscv/mphalport.h b/ports/qemu-riscv/mphalport.h new file mode 100644 index 0000000000000..0cbdfc039144b --- /dev/null +++ b/ports/qemu-riscv/mphalport.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016-2018 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 + +#include "uart.h" + +#define mp_hal_stdin_rx_chr() (0) +#define mp_hal_stdout_tx_strn_cooked(s, l) uart_tx_strn((s), (l)) diff --git a/ports/qemu-riscv/qstrdefsport.h b/ports/qemu-riscv/qstrdefsport.h new file mode 100644 index 0000000000000..00d3e2ae3c555 --- /dev/null +++ b/ports/qemu-riscv/qstrdefsport.h @@ -0,0 +1,2 @@ +// qstrs specific to this port +// *FORMAT-OFF* diff --git a/ports/qemu-riscv/startup.c b/ports/qemu-riscv/startup.c new file mode 100644 index 0000000000000..298bd605ed552 --- /dev/null +++ b/ports/qemu-riscv/startup.c @@ -0,0 +1,86 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Alessandro Gatti + * + * 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 +#include +#include + +#include "uart.h" + +extern void set_interrupt_table(void); +extern int main(int argc, char **argv); + +void _entry_point(void) { + // Set interrupt table + set_interrupt_table(); + // Enable UART + uart_init(); + // Now that we have a basic system up and running we can call main + main(0, 0); + // Finished + exit(0); +} + +void exit(int status) { + uint32_t semihosting_arguments[2] = { 0 }; + + // Exit via QEMU's RISC-V semihosting support. + __asm volatile ( + ".option push \n" // Transient options + ".option norvc \n" // Do not emit compressed instructions + ".align 4 \n" // 16 bytes alignment + "mv a1, %0 \n" // Load buffer + "li t0, 0x20026 \n" // ADP_Stopped_ApplicationExit + "sw t0, 0(a1) \n" // ADP_Stopped_ApplicationExit + "sw %1, 4(a1) \n" // Exit code + "addi a0, zero, 0x20 \n" // TARGET_SYS_EXIT_EXTENDED + "slli zero, zero, 0x1F \n" // Entry NOP + "ebreak \n" // Give control to the debugger + "srai zero, zero, 7 \n" // Semihosting call + ".option pop \n" // Restore previous options set + : + : "r" (semihosting_arguments), "r" (status) + : "memory" + ); + + // Should never reach here. + for (;;) { + } +} + +#ifndef NDEBUG +void __assert_func(const char *file, int line, const char *func, const char *expr) { + (void)func; + printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); + exit(1); +} +#endif + +// Picolibc requires `stdout` to be explicitly defined. + +#ifdef _PICOLIBC__ +FILE *const stdout; +#endif diff --git a/ports/qemu-riscv/test_main.c b/ports/qemu-riscv/test_main.c new file mode 100644 index 0000000000000..ca796766aca5a --- /dev/null +++ b/ports/qemu-riscv/test_main.c @@ -0,0 +1,66 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Ilya Dmitrichenko + * + * 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 +#include + +#include "py/compile.h" +#include "py/runtime.h" +#include "py/stackctrl.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "shared/runtime/gchelper.h" +#include "lib/tinytest/tinytest.h" +#include "lib/tinytest/tinytest_macros.h" + +#define HEAP_SIZE (200 * 1024) + +#include "genhdr/tests.h" + +int main() { + mp_stack_ctrl_init(); + mp_stack_set_limit(10240); + static uint32_t heap[HEAP_SIZE / sizeof(uint32_t)]; + upytest_set_heap(heap, (char *)heap + HEAP_SIZE); + int r = tinytest_main(0, NULL, groups); + printf("status: %d\n", r); + return r; +} + +void gc_collect(void) { + gc_collect_start(); + gc_helper_collect_regs_and_stack(); + gc_collect_end(); +} + +mp_lexer_t *mp_lexer_new_from_file(qstr filename) { + mp_raise_OSError(MP_ENOENT); +} + +void nlr_jump_fail(void *val) { + printf("uncaught NLR\n"); + exit(1); +} diff --git a/ports/qemu-riscv/tests_profile.txt b/ports/qemu-riscv/tests_profile.txt new file mode 100644 index 0000000000000..2f3bb7300ddf4 --- /dev/null +++ b/ports/qemu-riscv/tests_profile.txt @@ -0,0 +1,53 @@ +# Port-specific tests exclusion list. + +exclude_tests = exclude_tests.union( + ( + # Native code generation is not yet supported. + "micropython/native_closure.py", + "micropython/native_const.py", + "micropython/native_const_intbig.py", + "micropython/native_for.py", + "micropython/native_fun_attrs.py", + "micropython/native_gen.py", + "micropython/native_misc.py", + "micropython/native_try.py", + "micropython/native_try_deep.py", + "micropython/native_while.py", + "micropython/native_with.py", + + # Viper code generator is not yet supported. + "micropython/viper_addr.py", + "micropython/viper_args.py", + "micropython/viper_binop_arith.py", + "micropython/viper_binop_arith_uint.py", + "micropython/viper_binop_bitwise_uint.py", + "micropython/viper_binop_comp.py", + "micropython/viper_binop_comp_imm.py", + "micropython/viper_binop_comp_uint.py", + "micropython/viper_binop_divmod.py", + "micropython/viper_binop_multi_comp.py", + "micropython/viper_cond.py", + "micropython/viper_const.py", + "micropython/viper_const_intbig.py", + "micropython/viper_error.py", + "micropython/viper_globals.py", + "micropython/viper_import.py", + "micropython/viper_misc.py", + "micropython/viper_misc2.py", + "micropython/viper_misc3.py", + "micropython/viper_misc_intbig.py", + "micropython/viper_ptr16_load.py", + "micropython/viper_ptr16_store.py", + "micropython/viper_ptr32_load.py", + "micropython/viper_ptr32_store.py", + "micropython/viper_ptr8_load.py", + "micropython/viper_ptr8_store.py", + "micropython/viper_storeattr.py", + "micropython/viper_subscr.py", + "micropython/viper_subscr_multi.py", + "micropython/viper_try.py", + "micropython/viper_types.py", + "micropython/viper_unop.py", + "micropython/viper_with.py", + ) +) diff --git a/ports/qemu-riscv/uart.c b/ports/qemu-riscv/uart.c new file mode 100644 index 0000000000000..a266d6e6fdc8b --- /dev/null +++ b/ports/qemu-riscv/uart.c @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Alessandro Gatti + * + * 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 +#include + +#include "uart.h" + +#if defined(QEMU_SOC_VIRT) + +volatile unsigned char *uart_buffer = (volatile unsigned char *)0x10000000; + +void uart_init(void) { +} + +void uart_tx_strn(const char *buffer, size_t length) { + for (size_t index = 0; index < length; index++) { + *uart_buffer = buffer[index]; + } +} + +#endif // QEMU_SOC_VIRT diff --git a/ports/qemu-riscv/uart.h b/ports/qemu-riscv/uart.h new file mode 100644 index 0000000000000..fdaae424e19d1 --- /dev/null +++ b/ports/qemu-riscv/uart.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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. + */ + +#ifndef MICROPY_INCLUDED_QEMU_RISCV_UART_H +#define MICROPY_INCLUDED_QEMU_RISCV_UART_H + +void uart_init(void); +void uart_tx_strn(const char *buf, size_t len); + +#endif // MICROPY_INCLUDED_QEMU_RISCV_UART_H diff --git a/ports/qemu-riscv/virt.ld b/ports/qemu-riscv/virt.ld new file mode 100644 index 0000000000000..64675ad4ee856 --- /dev/null +++ b/ports/qemu-riscv/virt.ld @@ -0,0 +1,98 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Alessandro Gatti + * + * 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. + */ + +/* Output format is 32 bits little endian. */ +OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv"); + +/* + * Memory layout: + * + * 0x8000_0000: .text + * .........: .rodata + * 0x8040_0000: .data + * .........: _global_pointer + * .........: .bss + * 0x8060_0000: .stack + * 0x8060_0000: _sstack + * 0x8060_FFFF: _estack + */ +MEMORY +{ + ROM (xr) : ORIGIN = 0x80000000, LENGTH = 4M + RAM (xrw) : ORIGIN = ORIGIN(ROM) + LENGTH(ROM), LENGTH = 2M + STACK (rw) : ORIGIN = ORIGIN(RAM) + LENGTH(RAM), LENGTH = 64K +} + +SECTIONS +{ + /* Code + Read-Only data segment */ + + .text : ALIGN (4K) + { + *(.start) + *(.text) + . = ALIGN (4K); + *(.rodata) + _sirodata = .; + } > ROM + + .rodata : AT (_sirodata) ALIGN (4K) + { + *(.rodata) + } > ROM + + /* Data + BSS segment */ + + .data : ALIGN (4K) + { + *(.data) + _sibss = .; + } > RAM + + .bss : AT (_sibss) ALIGN (4K) + { + /* Mark global pointer address. */ + _global_pointer = .; + + /* Mark BSS start. */ + . = . + 4; + _sbss = .; + *(.bss) + /* Mark BSS end. */ + _ebss = .; + } > RAM + + /* Isolated stack segment. */ + + .stack : ALIGN(4K) + { + /* Mark stack start. */ + _sstack = .; + . = LENGTH(STACK); + /* Mark stack end. */ + _estack = .; + } > STACK +} diff --git a/tests/run-tests.py b/tests/run-tests.py index 1e1bd160af3c9..fa6c2a0331a10 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -678,6 +678,8 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): ) # RA fsp rtc function doesn't support nano sec info elif args.target == "qemu-arm": skip_tests.add("misc/print_exception.py") # requires sys stdfiles + elif args.target == "qemu-riscv": + skip_tests.add("misc/print_exception.py") # requires sys stdfiles elif args.target == "webassembly": skip_tests.add("basics/string_format_modulo.py") # can't print nulls to stdout skip_tests.add("basics/string_strip.py") # can't print nulls to stdout @@ -1048,6 +1050,7 @@ def main(): LOCAL_TARGETS = ( "unix", "qemu-arm", + "qemu-riscv", "webassembly", ) EXTERNAL_TARGETS = ( @@ -1151,6 +1154,12 @@ def main(): "inlineasm", "ports/qemu-arm", ) + elif args.target == "qemu-riscv": + if not args.write_exp: + raise ValueError("--target=qemu-riscv must be used with --write-exp") + # Generate expected output files for qemu run. + # This list should match the test_dirs tuple in tinytest-codegen.py. + test_dirs += ("float",) elif args.target == "webassembly": test_dirs += ("float", "ports/webassembly") else: From 1b10cb843cd678cdf1a6112739a452eec0293fcd Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 31 May 2024 14:38:52 +0200 Subject: [PATCH 0039/1300] github/workflows: Add qemu-riscv port to CI. Signed-off-by: Alessandro Gatti --- .github/workflows/ports_qemu-riscv.yml | 33 ++++++++++++++++++++++++++ tools/ci.sh | 24 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 .github/workflows/ports_qemu-riscv.yml diff --git a/.github/workflows/ports_qemu-riscv.yml b/.github/workflows/ports_qemu-riscv.yml new file mode 100644 index 0000000000000..39ba06adb6cbd --- /dev/null +++ b/.github/workflows/ports_qemu-riscv.yml @@ -0,0 +1,33 @@ +name: qemu-riscv port + +on: + push: + pull_request: + paths: + - '.github/workflows/*.yml' + - 'tools/**' + - 'py/**' + - 'extmod/**' + - 'shared/**' + - 'lib/**' + - 'drivers/**' + - 'ports/qemu-arm/main.c' + - 'ports/qemu-riscv/**' + - 'tests/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_and_test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install packages + run: source tools/ci.sh && ci_qemu_riscv_setup + - name: Build and run test suite + run: source tools/ci.sh && ci_qemu_riscv_build + - name: Print failures + if: failure() + run: grep --before-context=100 --text "FAIL" ports/qemu-riscv/build/console.out diff --git a/tools/ci.sh b/tools/ci.sh index b0746178b6689..7ba4ac4e57532 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -17,6 +17,11 @@ function ci_gcc_arm_setup { arm-none-eabi-gcc --version } +function ci_gcc_riscv_setup { + sudo apt-get install gcc-riscv64-unknown-elf picolibc-riscv64-unknown-elf + riscv64-unknown-elf-gcc --version +} + ######################################################################################## # c code formatting @@ -259,6 +264,25 @@ function ci_qemu_arm_build { make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test BOARD=sabrelite test } +######################################################################################## +# ports/qemu-riscv + +function ci_qemu_riscv_setup { + ci_gcc_riscv_setup + sudo apt-get update + sudo apt-get install qemu-system + qemu-system-riscv32 --version +} + +function ci_qemu_riscv_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/qemu-riscv submodules + make ${MAKEOPTS} -C ports/qemu-riscv + make ${MAKEOPTS} -C ports/qemu-riscv clean + make ${MAKEOPTS} -C ports/qemu-riscv -f Makefile.test submodules + make ${MAKEOPTS} -C ports/qemu-riscv -f Makefile.test test +} + ######################################################################################## # ports/renesas-ra From 411d66586c0979d441404f8f68d7557aa8151c6a Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 10 Jun 2024 10:02:36 +0200 Subject: [PATCH 0040/1300] extmod/modplatform: Add picolibc to the recognised libcs list. This adds picolibc to the list of the recognised libc options. Signed-off-by: Alessandro Gatti --- extmod/modplatform.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extmod/modplatform.h b/extmod/modplatform.h index 56a50e53c5445..b932551c7ccb2 100644 --- a/extmod/modplatform.h +++ b/extmod/modplatform.h @@ -83,6 +83,9 @@ #elif defined(__NEWLIB__) #define MICROPY_PLATFORM_LIBC_LIB "newlib" #define MICROPY_PLATFORM_LIBC_VER _NEWLIB_VERSION +#elif defined(_PICOLIBC__) +#define MICROPY_PLATFORM_LIBC_LIB "picolibc" +#define MICROPY_PLATFORM_LIBC_VER _PICOLIBC_VERSION #else #define MICROPY_PLATFORM_LIBC_LIB "" #define MICROPY_PLATFORM_LIBC_VER "" From 0466560b12bce9e9007df620e4f6d273e922e40e Mon Sep 17 00:00:00 2001 From: Jason Kridner Date: Wed, 22 Feb 2023 00:09:22 -0500 Subject: [PATCH 0041/1300] zephyr/modzsensor: Add additional sensor type constants. Signed-off-by: Jason Kridner --- ports/zephyr/modzsensor.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ports/zephyr/modzsensor.c b/ports/zephyr/modzsensor.c index 2a7278a61481e..58775b980ad3f 100644 --- a/ports/zephyr/modzsensor.c +++ b/ports/zephyr/modzsensor.c @@ -128,11 +128,24 @@ static const mp_rom_map_elem_t mp_module_zsensor_globals_table[] = { C(MAGN_Y), C(MAGN_Z), C(DIE_TEMP), + C(AMBIENT_TEMP), C(PRESS), C(PROX), C(HUMIDITY), C(LIGHT), + C(IR), + C(RED), + C(GREEN), + C(BLUE), C(ALTITUDE), + C(PM_1_0), + C(PM_2_5), + C(PM_10), + C(DISTANCE), + C(CO2), + C(VOC), + C(GAS_RES), + C(VOLTAGE), #undef C }; From c211c052915fdb6007d79abf70329918381c3974 Mon Sep 17 00:00:00 2001 From: Jason Kridner Date: Wed, 22 Feb 2023 00:21:16 -0500 Subject: [PATCH 0042/1300] zephyr/modsocket: Add socket.recvfrom method. Signed-off-by: Jason Kridner --- ports/zephyr/modsocket.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ports/zephyr/modsocket.c b/ports/zephyr/modsocket.c index 12c4c934bcd2f..7732e0a64a40d 100644 --- a/ports/zephyr/modsocket.c +++ b/ports/zephyr/modsocket.c @@ -292,6 +292,35 @@ static mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { } static MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); +static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { + socket_obj_t *socket = self_in; + socket_check_closed(socket); + + mp_int_t max_len = mp_obj_get_int(len_in); + vstr_t vstr; + // +1 to accommodate for trailing \0 + vstr_init_len(&vstr, max_len + 1); + struct sockaddr_in6 saddr; + socklen_t slen = sizeof(saddr); + + ssize_t recv_len = zsock_recvfrom(socket->ctx, vstr.buf, max_len, 0, (struct sockaddr *)&saddr, &slen); + if (recv_len == -1) { + vstr_clear(&vstr); + mp_raise_OSError(errno); + } + + mp_obj_t ret[2]; + if (recv_len == 0) { + ret[0] = mp_const_empty_bytes; + } else { + vstr.len = recv_len; + ret[0] = mp_obj_new_bytes_from_vstr(&vstr); + } + ret[1] = format_inet_addr((struct sockaddr *)&saddr, 0); + return mp_obj_new_tuple(2, ret); +} +static MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); + static mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) { (void)n_args; // always 4 mp_warning(MP_WARN_CAT(RuntimeWarning), "setsockopt() not implemented"); @@ -336,6 +365,7 @@ static const mp_rom_map_elem_t socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_accept), MP_ROM_PTR(&socket_accept_obj) }, { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&socket_send_obj) }, { MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&socket_recv_obj) }, + { MP_ROM_QSTR(MP_QSTR_recvfrom), MP_ROM_PTR(&socket_recvfrom_obj) }, { MP_ROM_QSTR(MP_QSTR_setsockopt), MP_ROM_PTR(&socket_setsockopt_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, From 2b0e64beaf5998bae794d2d67827f70a8bf0fe74 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Tue, 2 Jan 2024 14:21:37 -0600 Subject: [PATCH 0043/1300] zephyr: Link MicroPython with the Zephyr kernel library. Unlike most other Zephyr libraries, libkernel.a is not built as a whole-archive. This change also fixes a linker error observed on nucleo_wb55rg while preparing an upgrade to Zephyr v3.5.0, caused by an undefined reference to `z_impl_k_busy_wait`. Signed-off-by: Maureen Helm --- ports/zephyr/CMakeLists.txt | 1 + ports/zephyr/src/zephyr_start.c | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index 743907e623a25..c048d92d2ae24 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -109,6 +109,7 @@ zephyr_library_compile_definitions( ) zephyr_library_sources(${MICROPY_SOURCE_QSTR}) +zephyr_library_link_libraries(kernel) add_dependencies(${MICROPY_TARGET} zephyr_generated_headers) diff --git a/ports/zephyr/src/zephyr_start.c b/ports/zephyr/src/zephyr_start.c index b24e501c33363..bcd68f92ce052 100644 --- a/ports/zephyr/src/zephyr_start.c +++ b/ports/zephyr/src/zephyr_start.c @@ -36,8 +36,4 @@ void main(void) { zephyr_getchar_init(); #endif real_main(); - - // This is needed so the linker includes k_timer_init, z_impl_k_timer_start - // and z_impl_k_timer_stop, as used by libmicropython.a. - k_timer_start(NULL, K_MSEC(0), K_MSEC(0)); } From a053e639147d97c4a306ab272c12d9520a80e805 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 17 Jun 2024 23:32:09 +1000 Subject: [PATCH 0044/1300] webassembly/objjsproxy: Implement proxying of JS iterable protocol. This allows Python to iterate over JavaScript objects that provide Symbol.iterator. Signed-off-by: Damien George --- ports/webassembly/objjsproxy.c | 33 ++++++++++--------- tests/ports/webassembly/js_proxy_iterator.mjs | 14 ++++++++ .../webassembly/js_proxy_iterator.mjs.exp | 4 +++ 3 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 tests/ports/webassembly/js_proxy_iterator.mjs create mode 100644 tests/ports/webassembly/js_proxy_iterator.mjs.exp diff --git a/ports/webassembly/objjsproxy.c b/ports/webassembly/objjsproxy.c index 5c09e003fdccc..cbfe8be49baf2 100644 --- a/ports/webassembly/objjsproxy.c +++ b/ports/webassembly/objjsproxy.c @@ -153,14 +153,21 @@ EM_JS(void, js_reflect_construct, (int f_ref, uint32_t n_args, uint32_t * args, proxy_convert_js_to_mp_obj_jsside(ret, out); }); -EM_JS(int, js_get_len, (int f_ref), { - return proxy_js_ref[f_ref].length; +EM_JS(void, js_get_iter, (int f_ref, uint32_t * out), { + const f = proxy_js_ref[f_ref]; + const ret = f[Symbol.iterator](); + proxy_convert_js_to_mp_obj_jsside(ret, out); }); -EM_JS(void, js_subscr_int, (int f_ref, int idx, uint32_t * out), { +EM_JS(bool, js_iter_next, (int f_ref, uint32_t * out), { const f = proxy_js_ref[f_ref]; - const ret = f[idx]; - proxy_convert_js_to_mp_obj_jsside(ret, out); + const ret = f.next(); + if (ret.done) { + return false; + } else { + proxy_convert_js_to_mp_obj_jsside(ret.value, out); + return true; + } }); EM_JS(void, js_subscr_load, (int f_ref, uint32_t * index_ref, uint32_t * out), { @@ -320,17 +327,13 @@ void mp_obj_jsproxy_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { typedef struct _jsproxy_it_t { mp_obj_base_t base; mp_fun_1_t iternext; - mp_obj_jsproxy_t *obj; - uint16_t cur; - uint16_t len; + mp_obj_jsproxy_t *iter; } jsproxy_it_t; static mp_obj_t jsproxy_it_iternext(mp_obj_t self_in) { jsproxy_it_t *self = MP_OBJ_TO_PTR(self_in); - if (self->cur < self->len) { - uint32_t out[3]; - js_subscr_int(self->obj->ref, self->cur, out); - self->cur += 1; + uint32_t out[3]; + if (js_iter_next(self->iter->ref, out)) { return proxy_convert_js_to_mp_obj_cside(out); } else { return MP_OBJ_STOP_ITERATION; @@ -343,9 +346,9 @@ static mp_obj_t jsproxy_new_it(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { jsproxy_it_t *o = (jsproxy_it_t *)iter_buf; o->base.type = &mp_type_polymorph_iter; o->iternext = jsproxy_it_iternext; - o->obj = self; - o->cur = 0; - o->len = js_get_len(self->ref); + uint32_t out[3]; + js_get_iter(self->ref, out); + o->iter = proxy_convert_js_to_mp_obj_cside(out); return MP_OBJ_FROM_PTR(o); } diff --git a/tests/ports/webassembly/js_proxy_iterator.mjs b/tests/ports/webassembly/js_proxy_iterator.mjs new file mode 100644 index 0000000000000..34b54b4837a85 --- /dev/null +++ b/tests/ports/webassembly/js_proxy_iterator.mjs @@ -0,0 +1,14 @@ +// Test accessing JavaScript iterables (objects with Symbol.iterator) from Python. + +const mp = await (await import(process.argv[2])).loadMicroPython(); + +mp.runPython(` +import js + +for v in js.Set.new([1, 2]): + print(v) + +url_search_params = js.URLSearchParams.new("one=1&two=2") +for key in url_search_params.keys(): + print(key, list(url_search_params.getAll(key))) +`); diff --git a/tests/ports/webassembly/js_proxy_iterator.mjs.exp b/tests/ports/webassembly/js_proxy_iterator.mjs.exp new file mode 100644 index 0000000000000..e8d11286c60a2 --- /dev/null +++ b/tests/ports/webassembly/js_proxy_iterator.mjs.exp @@ -0,0 +1,4 @@ +1 +2 +one ['1'] +two ['2'] From e9c898cb3312c2e2cf9e0da1d099541bf7bdf4d2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 18 Jun 2024 17:35:24 +1000 Subject: [PATCH 0045/1300] webassembly/asyncio: Support top-level await of asyncio Task and Event. This change allows doing a top-level await on an asyncio primitive like Task and Event. This feature enables a better interaction and synchronisation between JavaScript and Python, because `api.runPythonAsync` can now be used (called from JavaScript) to await on the completion of asyncio primitives. Signed-off-by: Damien George --- ports/webassembly/asyncio/core.py | 24 ++++++++++---- ports/webassembly/modjsffi.c | 15 --------- ports/webassembly/proxy_c.c | 33 ++++++++++++++++++- ports/webassembly/qstrdefsport.h | 1 + .../webassembly/asyncio_top_level_await.mjs | 25 ++++++++++++++ .../asyncio_top_level_await.mjs.exp | 7 ++++ 6 files changed, 83 insertions(+), 22 deletions(-) create mode 100644 tests/ports/webassembly/asyncio_top_level_await.mjs create mode 100644 tests/ports/webassembly/asyncio_top_level_await.mjs.exp diff --git a/ports/webassembly/asyncio/core.py b/ports/webassembly/asyncio/core.py index f6f9d19e5c154..cc26e7b8d5a82 100644 --- a/ports/webassembly/asyncio/core.py +++ b/ports/webassembly/asyncio/core.py @@ -50,9 +50,6 @@ def __next__(self): # Pause task execution for the given time (integer in milliseconds, uPy extension) # Use a SingletonGenerator to do it without allocating on the heap def sleep_ms(t, sgen=SingletonGenerator()): - if cur_task is None: - # Support top-level asyncio.sleep, via a JavaScript Promise. - return jsffi.async_timeout_ms(t) assert sgen.state is None sgen.state = ticks_add(ticks(), max(0, t)) return sgen @@ -69,6 +66,18 @@ def sleep(t): asyncio_timer = None +class TopLevelCoro: + @staticmethod + def set(resolve, reject): + TopLevelCoro.resolve = resolve + TopLevelCoro.reject = reject + _schedule_run_iter(0) + + @staticmethod + def send(value): + TopLevelCoro.resolve() + + class ThenableEvent: def __init__(self, thenable): self.result = None # Result of the thenable @@ -122,12 +131,12 @@ def _run_iter(): dt = max(0, ticks_diff(t.ph_key, ticks())) else: # No tasks can be woken so finished running - cur_task = None + cur_task = _top_level_task return if dt > 0: # schedule to call again later - cur_task = None + cur_task = _top_level_task _schedule_run_iter(dt) return @@ -198,11 +207,14 @@ def create_task(coro): return t +# Task used to suspend and resume top-level await. +_top_level_task = Task(TopLevelCoro, globals()) + ################################################################################ # Event loop wrapper -cur_task = None +cur_task = _top_level_task class Loop: diff --git a/ports/webassembly/modjsffi.c b/ports/webassembly/modjsffi.c index 202e1d7ec0149..ac3d860235240 100644 --- a/ports/webassembly/modjsffi.c +++ b/ports/webassembly/modjsffi.c @@ -62,20 +62,6 @@ static mp_obj_t mp_jsffi_to_js(mp_obj_t arg) { } static MP_DEFINE_CONST_FUN_OBJ_1(mp_jsffi_to_js_obj, mp_jsffi_to_js); -// *FORMAT-OFF* -EM_JS(void, promise_with_timeout_ms, (double ms, uint32_t * out), { - const ret = new Promise((resolve) => setTimeout(resolve, ms)); - proxy_convert_js_to_mp_obj_jsside(ret, out); -}); -// *FORMAT-ON* - -static mp_obj_t mp_jsffi_async_timeout_ms(mp_obj_t arg) { - uint32_t out[PVN]; - promise_with_timeout_ms(mp_obj_get_float_to_d(arg), out); - return proxy_convert_js_to_mp_obj_cside(out); -} -static MP_DEFINE_CONST_FUN_OBJ_1(mp_jsffi_async_timeout_ms_obj, mp_jsffi_async_timeout_ms); - // *FORMAT-OFF* EM_JS(void, js_get_proxy_js_ref_info, (uint32_t * out), { let used = 0; @@ -121,7 +107,6 @@ static const mp_rom_map_elem_t mp_module_jsffi_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_JsException), MP_ROM_PTR(&mp_type_JsException) }, { MP_ROM_QSTR(MP_QSTR_create_proxy), MP_ROM_PTR(&mp_jsffi_create_proxy_obj) }, { MP_ROM_QSTR(MP_QSTR_to_js), MP_ROM_PTR(&mp_jsffi_to_js_obj) }, - { MP_ROM_QSTR(MP_QSTR_async_timeout_ms), MP_ROM_PTR(&mp_jsffi_async_timeout_ms_obj) }, { MP_ROM_QSTR(MP_QSTR_mem_info), MP_ROM_PTR(&mp_jsffi_mem_info_obj) }, }; static MP_DEFINE_CONST_DICT(mp_module_jsffi_globals, mp_module_jsffi_globals_table); diff --git a/ports/webassembly/proxy_c.c b/ports/webassembly/proxy_c.c index 6e6f928a2ee4b..a8c444faaebca 100644 --- a/ports/webassembly/proxy_c.c +++ b/ports/webassembly/proxy_c.c @@ -470,6 +470,12 @@ EM_JS(void, js_then_continue, (int jsref, uint32_t * py_resume, uint32_t * resol }); // *FORMAT-ON* +EM_JS(void, create_promise, (uint32_t * out_set, uint32_t * out_promise), { + const out_set_js = proxy_convert_mp_to_js_obj_jsside(out_set); + const promise = new Promise(out_set_js); + proxy_convert_js_to_mp_obj_jsside(promise, out_promise); +}); + static mp_obj_t proxy_resume_execute(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t resolve, mp_obj_t reject) { if (throw_value != MP_OBJ_NULL && throw_value != mp_const_none) { if (send_value == mp_const_none) { @@ -483,6 +489,9 @@ static mp_obj_t proxy_resume_execute(mp_obj_t self_in, mp_obj_t send_value, mp_o } } else { throw_value = MP_OBJ_NULL; + if (send_value == mp_const_undefined) { + send_value = mp_const_none; + } } mp_obj_t ret_value; @@ -496,7 +505,29 @@ static mp_obj_t proxy_resume_execute(mp_obj_t self_in, mp_obj_t send_value, mp_o js_then_resolve(out_ret_value, out_resolve); return mp_const_none; } else if (ret_kind == MP_VM_RETURN_YIELD) { - // ret_value should be a JS thenable + // If ret_value is None then there has been a top-level await of an asyncio primitive. + // Otherwise, ret_value should be a JS thenable. + + if (ret_value == mp_const_none) { + // Waiting on an asyncio primitive to complete, eg a Task or Event. + // + // Completion of this primitive will occur when the asyncio.core._top_level_task + // Task is made runable and its coroutine's send() method is called. Need to + // construct a Promise that resolves when that send() method is called, because + // that will resume the top-level await from the JavaScript side. + // + // This is accomplished via the asyncio.core.TopLevelCoro class and its methods. + mp_obj_t asyncio = mp_import_name(MP_QSTR_asyncio_dot_core, mp_const_none, MP_OBJ_NEW_SMALL_INT(0)); + mp_obj_t asyncio_core = mp_load_attr(asyncio, MP_QSTR_core); + mp_obj_t top_level_coro = mp_load_attr(asyncio_core, MP_QSTR_TopLevelCoro); + mp_obj_t top_level_coro_set = mp_load_attr(top_level_coro, MP_QSTR_set); + uint32_t out_set[PVN]; + proxy_convert_mp_to_js_obj_cside(top_level_coro_set, out_set); + uint32_t out_promise[PVN]; + create_promise(out_set, out_promise); + ret_value = proxy_convert_js_to_mp_obj_cside(out_promise); + } + mp_obj_t py_resume = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&resume_obj), self_in); int ref = mp_obj_jsproxy_get_ref(ret_value); uint32_t out_py_resume[PVN]; diff --git a/ports/webassembly/qstrdefsport.h b/ports/webassembly/qstrdefsport.h index 472d05f4375f6..421344bd494da 100644 --- a/ports/webassembly/qstrdefsport.h +++ b/ports/webassembly/qstrdefsport.h @@ -1,3 +1,4 @@ // qstrs specific to this port // *FORMAT-OFF* Q(/lib) +Q(asyncio.core) diff --git a/tests/ports/webassembly/asyncio_top_level_await.mjs b/tests/ports/webassembly/asyncio_top_level_await.mjs new file mode 100644 index 0000000000000..d8a9cad422e9b --- /dev/null +++ b/tests/ports/webassembly/asyncio_top_level_await.mjs @@ -0,0 +1,25 @@ +// Test top-level await on asyncio primitives: Task, Event. + +const mp = await (await import(process.argv[2])).loadMicroPython(); + +await mp.runPythonAsync(` +import asyncio + +async def task(event): + print("task set event") + event.set() + print("task sleep") + await asyncio.sleep(0.1) + print("task end") + +event = asyncio.Event() +t = asyncio.create_task(task(event)) + +print("top-level wait event") +await event.wait() +print("top-level wait task") +await t +print("top-level end") +`); + +console.log("finished"); diff --git a/tests/ports/webassembly/asyncio_top_level_await.mjs.exp b/tests/ports/webassembly/asyncio_top_level_await.mjs.exp new file mode 100644 index 0000000000000..7232c5d4f0d2d --- /dev/null +++ b/tests/ports/webassembly/asyncio_top_level_await.mjs.exp @@ -0,0 +1,7 @@ +top-level wait event +task set event +task sleep +top-level wait task +task end +top-level end +finished From 8ac9c8f392000febb6ca58c470b653589dd7c710 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 19 Jun 2024 17:27:31 +1000 Subject: [PATCH 0046/1300] extmod/modasyncio: Add support for a callback on TaskQueue push. Allows passing in a callback to `TaskQueue()` that is called when something is pushed on to the queue. Signed-off-by: Damien George --- extmod/modasyncio.c | 17 ++++++++++++++++- py/mpconfig.h | 4 ++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/extmod/modasyncio.c b/extmod/modasyncio.c index 4f82370d238a9..6161500bf5e7c 100644 --- a/extmod/modasyncio.c +++ b/extmod/modasyncio.c @@ -53,6 +53,9 @@ typedef struct _mp_obj_task_t { typedef struct _mp_obj_task_queue_t { mp_obj_base_t base; mp_obj_task_t *heap; + #if MICROPY_PY_ASYNCIO_TASK_QUEUE_PUSH_CALLBACK + mp_obj_t push_callback; + #endif } mp_obj_task_queue_t; static const mp_obj_type_t task_queue_type; @@ -86,9 +89,16 @@ static int task_lt(mp_pairheap_t *n1, mp_pairheap_t *n2) { static mp_obj_t task_queue_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { (void)args; - mp_arg_check_num(n_args, n_kw, 0, 0, false); + mp_arg_check_num(n_args, n_kw, 0, MICROPY_PY_ASYNCIO_TASK_QUEUE_PUSH_CALLBACK ? 1 : 0, false); mp_obj_task_queue_t *self = mp_obj_malloc(mp_obj_task_queue_t, type); self->heap = (mp_obj_task_t *)mp_pairheap_new(task_lt); + #if MICROPY_PY_ASYNCIO_TASK_QUEUE_PUSH_CALLBACK + if (n_args == 1) { + self->push_callback = args[0]; + } else { + self->push_callback = MP_OBJ_NULL; + } + #endif return MP_OBJ_FROM_PTR(self); } @@ -113,6 +123,11 @@ static mp_obj_t task_queue_push(size_t n_args, const mp_obj_t *args) { task->ph_key = args[2]; } self->heap = (mp_obj_task_t *)mp_pairheap_push(task_lt, TASK_PAIRHEAP(self->heap), TASK_PAIRHEAP(task)); + #if MICROPY_PY_ASYNCIO_TASK_QUEUE_PUSH_CALLBACK + if (self->push_callback != MP_OBJ_NULL) { + mp_call_function_1(self->push_callback, MP_OBJ_NEW_SMALL_INT(0)); + } + #endif return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(task_queue_push_obj, 2, 3, task_queue_push); diff --git a/py/mpconfig.h b/py/mpconfig.h index 61cfe96da8027..2cbf5b88efab0 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1610,6 +1610,10 @@ typedef double mp_float_t; #define MICROPY_PY_ASYNCIO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +#ifndef MICROPY_PY_ASYNCIO_TASK_QUEUE_PUSH_CALLBACK +#define MICROPY_PY_ASYNCIO_TASK_QUEUE_PUSH_CALLBACK (0) +#endif + #ifndef MICROPY_PY_UCTYPES #define MICROPY_PY_UCTYPES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif From 13195a678d0155a52818beaa5ffc4f4678a4226d Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 19 Jun 2024 15:04:12 +1000 Subject: [PATCH 0047/1300] webassembly/asyncio: Schedule run loop when tasks are pushed to queue. In the webassembly port there is no asyncio run loop running at the top level. Instead the Python asyncio run loop is scheduled through setTimeout and run by the outer JavaScript event loop. Because tasks can become runable from an external (to Python) event (eg a JavaScript callback), the run loop must be scheduled whenever a task is pushed to the asyncio task queue, otherwise tasks may be waiting forever on the queue. Signed-off-by: Damien George --- ports/webassembly/asyncio/core.py | 5 +- ports/webassembly/mpconfigport.h | 1 + .../webassembly/asyncio_top_level_await.mjs | 65 +++++++++++++++++++ .../asyncio_top_level_await.mjs.exp | 12 ++++ 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/ports/webassembly/asyncio/core.py b/ports/webassembly/asyncio/core.py index cc26e7b8d5a82..47846fc25a331 100644 --- a/ports/webassembly/asyncio/core.py +++ b/ports/webassembly/asyncio/core.py @@ -71,7 +71,6 @@ class TopLevelCoro: def set(resolve, reject): TopLevelCoro.resolve = resolve TopLevelCoro.reject = reject - _schedule_run_iter(0) @staticmethod def send(value): @@ -90,7 +89,6 @@ def set(self, value=None): if self.waiting: _task_queue.push(self.waiting) self.waiting = None - _schedule_run_iter(0) def remove(self, task): self.waiting = None @@ -203,7 +201,6 @@ def create_task(coro): raise TypeError("coroutine expected") t = Task(coro, globals()) _task_queue.push(t) - _schedule_run_iter(0) return t @@ -253,7 +250,7 @@ def current_task(): def new_event_loop(): global _task_queue - _task_queue = TaskQueue() # TaskQueue of Task instances. + _task_queue = TaskQueue(_schedule_run_iter) # TaskQueue of Task instances. return Loop diff --git a/ports/webassembly/mpconfigport.h b/ports/webassembly/mpconfigport.h index ece7868fb95ab..ab56162ca2b07 100644 --- a/ports/webassembly/mpconfigport.h +++ b/ports/webassembly/mpconfigport.h @@ -56,6 +56,7 @@ #define MICROPY_USE_INTERNAL_PRINTF (0) #define MICROPY_EPOCH_IS_1970 (1) +#define MICROPY_PY_ASYNCIO_TASK_QUEUE_PUSH_CALLBACK (1) #define MICROPY_PY_RANDOM_SEED_INIT_FUNC (mp_js_random_u32()) #define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1) #define MICROPY_PY_TIME_TIME_TIME_NS (1) diff --git a/tests/ports/webassembly/asyncio_top_level_await.mjs b/tests/ports/webassembly/asyncio_top_level_await.mjs index d8a9cad422e9b..234b7a6ce6fb9 100644 --- a/tests/ports/webassembly/asyncio_top_level_await.mjs +++ b/tests/ports/webassembly/asyncio_top_level_await.mjs @@ -2,6 +2,71 @@ const mp = await (await import(process.argv[2])).loadMicroPython(); +/**********************************************************/ +// Top-level await for an Event which is set by a JavaScript +// callback. + +console.log("= TEST 1 =========="); + +await mp.runPythonAsync(` +import asyncio +import js + +event = asyncio.Event() + +def callback(): + print("callback set event") + event.set() + +js.setTimeout(callback, 100) + +print("top-level wait event") +await event.wait() +print("top-level end") +`); + +console.log("finished"); + +/**********************************************************/ +// Top-level await for a Task which is cancelled by a +// JavaScript callback. + +console.log("= TEST 2 =========="); + +await mp.runPythonAsync(` +import asyncio +import js +import time + +async def task(): + print("task start") + await asyncio.sleep(5) + print("task end") + +def callback(): + print("callback cancel task") + t.cancel() + +t = asyncio.create_task(task()) +js.setTimeout(callback, 100) + +print("top-level wait task") +try: + t0 = time.time() + await t +except asyncio.CancelledError: + dt = time.time() - t0 + print("top-level task CancelledError", dt < 1) +`); + +console.log("finished"); + +/**********************************************************/ +// Top-level await for an Event and a Task, with the task +// setting the event. + +console.log("= TEST 3 =========="); + await mp.runPythonAsync(` import asyncio diff --git a/tests/ports/webassembly/asyncio_top_level_await.mjs.exp b/tests/ports/webassembly/asyncio_top_level_await.mjs.exp index 7232c5d4f0d2d..66fefd2dcef86 100644 --- a/tests/ports/webassembly/asyncio_top_level_await.mjs.exp +++ b/tests/ports/webassembly/asyncio_top_level_await.mjs.exp @@ -1,3 +1,15 @@ += TEST 1 ========== +top-level wait event +callback set event +top-level end +finished += TEST 2 ========== +top-level wait task +task start +callback cancel task +top-level task CancelledError True +finished += TEST 3 ========== top-level wait event task set event task sleep From 88513d12268f5a5c3f3488f1a2d70cb605129537 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 19 Jun 2024 15:17:21 +1000 Subject: [PATCH 0048/1300] webassembly/api: Allow specifying the pystack size. This allows increasing the Python recursion depth if needed. Also increase the default to 2k words. There is enough RAM in the browser/node context for this to be increased, and having a larger pystack allows more complex code to run without hitting the limit. Signed-off-by: Damien George --- ports/webassembly/api.js | 17 ++++++++++++----- ports/webassembly/main.c | 12 ++++++------ tests/ports/webassembly/heap_expand.mjs.exp | 10 +++++----- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/ports/webassembly/api.js b/ports/webassembly/api.js index c00edf575fa96..0718d13d6786f 100644 --- a/ports/webassembly/api.js +++ b/ports/webassembly/api.js @@ -25,6 +25,7 @@ */ // Options: +// - pystack: size in words of the MicroPython Python stack. // - heapsize: size in bytes of the MicroPython GC heap. // - url: location to load `micropython.mjs`. // - stdin: function to return input characters. @@ -34,10 +35,11 @@ // - stderr: same behaviour as stdout but for error output. // - linebuffer: whether to buffer line-by-line to stdout/stderr. export async function loadMicroPython(options) { - const { heapsize, url, stdin, stdout, stderr, linebuffer } = Object.assign( - { heapsize: 1024 * 1024, linebuffer: true }, - options, - ); + const { pystack, heapsize, url, stdin, stdout, stderr, linebuffer } = + Object.assign( + { pystack: 2 * 1024, heapsize: 1024 * 1024, linebuffer: true }, + options, + ); let Module = {}; Module.locateFile = (path, scriptDirectory) => url || scriptDirectory + path; @@ -96,7 +98,12 @@ export async function loadMicroPython(options) { ); return proxy_convert_mp_to_js_obj_jsside_with_free(value); }; - Module.ccall("mp_js_init", "null", ["number"], [heapsize]); + Module.ccall( + "mp_js_init", + "null", + ["number", "number"], + [pystack, heapsize], + ); Module.ccall("proxy_c_init", "null", [], []); return { _module: Module, diff --git a/ports/webassembly/main.c b/ports/webassembly/main.c index 6502ed16d3aae..c542f0cd72efe 100644 --- a/ports/webassembly/main.c +++ b/ports/webassembly/main.c @@ -66,7 +66,12 @@ void external_call_depth_dec(void) { --external_call_depth; } -void mp_js_init(int heap_size) { +void mp_js_init(int pystack_size, int heap_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); + #endif + #if MICROPY_ENABLE_GC char *heap = (char *)malloc(heap_size * sizeof(char)); gc_init(heap, heap + heap_size); @@ -80,11 +85,6 @@ void mp_js_init(int heap_size) { MP_STATE_MEM(gc_alloc_threshold) = 16 * 1024 / MICROPY_BYTES_PER_GC_BLOCK; #endif - #if MICROPY_ENABLE_PYSTACK - static mp_obj_t pystack[1024]; - mp_pystack_init(pystack, &pystack[MP_ARRAY_SIZE(pystack)]); - #endif - mp_init(); #if MICROPY_VFS_POSIX diff --git a/tests/ports/webassembly/heap_expand.mjs.exp b/tests/ports/webassembly/heap_expand.mjs.exp index ee14908409d86..5efa8567f7707 100644 --- a/tests/ports/webassembly/heap_expand.mjs.exp +++ b/tests/ports/webassembly/heap_expand.mjs.exp @@ -17,11 +17,11 @@ 135109888 134978800 134716640 -135216848 -136217216 -138218032 -142219616 -150222864 +135216784 +136217152 +138217840 +142219296 +150222224 1 2 4 From 5040b13dd47e1d3dad401f45ba23f4b17fd2d670 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 20 Jun 2024 08:22:56 -0400 Subject: [PATCH 0049/1300] py/objdeque: Fix deque type flags based on option settings. This fixes a minor issue in the changes made by 7dff38fdc190d7b731fad8319d2ae8aa13fde18a: the type flags for deque were meant to be conditionalized based on MICROPY_PY_COLLECTIONS_DEQUE_ITER, but the computed conditionalized value wasn't used. Signed-off-by: Dan Halbert --- py/objdeque.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/objdeque.c b/py/objdeque.c index 583537017fdb2..2ad771284df53 100644 --- a/py/objdeque.c +++ b/py/objdeque.c @@ -263,7 +263,7 @@ static MP_DEFINE_CONST_DICT(deque_locals_dict, deque_locals_dict_table); MP_DEFINE_CONST_OBJ_TYPE( mp_type_deque, MP_QSTR_deque, - MP_TYPE_FLAG_ITER_IS_GETITER, + DEQUE_TYPE_FLAGS, make_new, deque_make_new, unary_op, deque_unary_op, DEQUE_TYPE_SUBSCR From 407464348d64759e7b4d9a129ff367074c5797bd Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 21 Jun 2024 14:35:43 +1000 Subject: [PATCH 0050/1300] tests/cpydiff: Remove deque difference test. Because `collections.deque` is now a built-in type in MicroPython. Signed-off-by: Damien George --- tests/cpydiff/modules_deque.py | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 tests/cpydiff/modules_deque.py diff --git a/tests/cpydiff/modules_deque.py b/tests/cpydiff/modules_deque.py deleted file mode 100644 index 4d2746d1f864e..0000000000000 --- a/tests/cpydiff/modules_deque.py +++ /dev/null @@ -1,10 +0,0 @@ -""" -categories: Modules,deque -description: Deque not implemented -cause: Unknown -workaround: Use regular lists. micropython-lib has implementation of collections.deque. -""" -import collections - -D = collections.deque() -print(D) From 5a778ebc378d7a1bc9716177950c9e8ac000bb56 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 12 Jun 2024 19:07:14 +1000 Subject: [PATCH 0051/1300] tests/thread: Re-enable GC before stress_schedule test ends. Otherwise GC stays disabled (not re-enabled by soft reset) and later test runs fail with MemoryError. Signed-off-by: Angus Gratton --- tests/thread/stress_schedule.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/thread/stress_schedule.py b/tests/thread/stress_schedule.py index a5d7dc824edea..97876f0f77ca8 100644 --- a/tests/thread/stress_schedule.py +++ b/tests/thread/stress_schedule.py @@ -52,6 +52,8 @@ def thread(): thread_run = False time.sleep_ms(20) +gc.enable() + if n < _NUM_TASKS: # Not all the tasks were scheduled, likely the scheduler stopped working. print(n) From 8338f663523d675847b8c0b9b92977b76995de8f Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 8 Jun 2024 11:00:08 +0200 Subject: [PATCH 0052/1300] py/asmrv32: Add RISC-V RV32IMC native code emitter. This adds a native code generation backend for RISC-V RV32I CPUs, currently limited to the I, M, and C instruction sets. Signed-off-by: Alessandro Gatti --- py/asmbase.h | 1 + py/asmrv32.c | 595 ++++++++++++++++++++++++++++++++++++++++++++++++ py/asmrv32.h | 464 +++++++++++++++++++++++++++++++++++++ py/compile.c | 3 + py/emit.h | 3 + py/emitglue.c | 2 +- py/emitnative.c | 32 ++- py/emitnrv32.c | 44 ++++ py/mpconfig.h | 7 +- py/py.cmake | 2 + py/py.mk | 2 + 11 files changed, 1152 insertions(+), 3 deletions(-) create mode 100644 py/asmrv32.c create mode 100644 py/asmrv32.h create mode 100644 py/emitnrv32.c diff --git a/py/asmbase.h b/py/asmbase.h index 352d2f54cc810..461393fe77fb7 100644 --- a/py/asmbase.h +++ b/py/asmbase.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_PY_ASMBASE_H #include +#include #include #define MP_ASM_PASS_COMPUTE (1) diff --git a/py/asmrv32.c b/py/asmrv32.c new file mode 100644 index 0000000000000..228edfc22c102 --- /dev/null +++ b/py/asmrv32.c @@ -0,0 +1,595 @@ +/* + * This file is part of the MicroPython project, https://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Alessandro Gatti + * + * 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 +#include +#include + +#include "py/emit.h" +#include "py/mpconfig.h" + +// wrapper around everything in this file +#if MICROPY_EMIT_RV32 + +#include "py/asmrv32.h" + +#if MICROPY_DEBUG_VERBOSE +#define DEBUG_PRINT (1) +#define DEBUG_printf DEBUG_printf +#else +#define DEBUG_printf(...) (void)0 +#endif + +#ifndef MP_POPCOUNT +#ifdef _MSC_VER +#include +#define MP_POPCOUNT __popcnt +#else +#if defined __has_builtin +#if __has_builtin(__builtin_popcount) +#define MP_POPCOUNT __builtin_popcount +#endif +#else +static uint32_t fallback_popcount(uint32_t value) { + value = value - ((value >> 1) & 0x55555555); + value = (value & 0x33333333) + ((value >> 2) & 0x33333333); + value = (value + (value >> 4)) & 0x0F0F0F0F; + return value * 0x01010101; +} +#define MP_POPCOUNT fallback_popcount +#endif +#endif +#endif + +#define INTERNAL_TEMPORARY ASM_RV32_REG_T4 +#define AVAILABLE_REGISTERS_COUNT 32 + +#define FIT_UNSIGNED(value, bits) (((value) & ~((1U << (bits)) - 1)) == 0) +#define FIT_SIGNED(value, bits) \ + ((((value) & ~((1U << ((bits) - 1)) - 1)) == 0) || \ + (((value) & ~((1U << ((bits) - 1)) - 1)) == ~((1U << ((bits) - 1)) - 1))) + +/////////////////////////////////////////////////////////////////////////////// + +void asm_rv32_emit_word_opcode(asm_rv32_t *state, mp_uint_t word) { + uint8_t *cursor = mp_asm_base_get_cur_to_write_bytes(&state->base, sizeof(uint32_t)); + if (cursor == NULL) { + return; + } + + #if MP_ENDIANNESS_LITTLE + cursor[0] = word & 0xFF; + cursor[1] = (word >> 8) & 0xFF; + cursor[2] = (word >> 16) & 0xFF; + cursor[3] = (word >> 24) & 0xFF; + #else + cursor[0] = (word >> 24) & 0xFF; + cursor[1] = (word >> 16) & 0xFF; + cursor[2] = (word >> 8) & 0xFF; + cursor[3] = word & 0xFF; + #endif +} + +void asm_rv32_emit_halfword_opcode(asm_rv32_t *state, mp_uint_t word) { + uint8_t *cursor = mp_asm_base_get_cur_to_write_bytes(&state->base, sizeof(uint16_t)); + if (cursor == NULL) { + return; + } + + #if MP_ENDIANNESS_LITTLE + cursor[0] = word & 0xFF; + cursor[1] = (word >> 8) & 0xFF; + #else + cursor[0] = (word >> 8) & 0xFF; + cursor[1] = word & 0xFF; + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +static void split_immediate(mp_int_t immediate, mp_uint_t *upper, mp_uint_t *lower) { + assert(upper != NULL && "Upper pointer is NULL."); + assert(lower != NULL && "Lower pointer is NULL."); + + mp_uint_t unsigned_immediate = *((mp_uint_t *)&immediate); + *upper = unsigned_immediate & 0xFFFFF000; + *lower = unsigned_immediate & 0x00000FFF; + + // Turn the lower half from unsigned to signed. + if ((*lower & 0x800) != 0) { + *upper += 0x1000; + *lower -= 0x1000; + } +} + +static void load_upper_immediate(asm_rv32_t *state, mp_uint_t rd, mp_uint_t immediate) { + // if immediate fits in 17 bits and is ≠ 0: + // c.lui rd, HI(immediate) + // else: + // lui rd, HI(immediate) + if (FIT_SIGNED(immediate, 17) && ((immediate >> 12) != 0)) { + asm_rv32_opcode_clui(state, rd, immediate); + } else { + asm_rv32_opcode_lui(state, rd, immediate); + } +} + +static void load_lower_immediate(asm_rv32_t *state, mp_uint_t rd, mp_uint_t immediate) { + // WARNING: This must be executed on a register that has either been + // previously cleared or was the target of a LUI/C.LUI or + // AUIPC opcode. + + if (immediate == 0) { + return; + } + + // if LO(immediate) fits in 6 bits: + // c.addi rd, LO(immediate) + // else: + // addi rd, rd, LO(immediate) + if (FIT_SIGNED(immediate, 6)) { + asm_rv32_opcode_caddi(state, rd, immediate); + } else { + asm_rv32_opcode_addi(state, rd, rd, immediate); + } +} + +static void load_full_immediate(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(immediate, &upper, &lower); + + // if immediate fits in 17 bits: + // c.lui rd, HI(immediate) + // else: + // lui rd, HI(immediate) + // if LO(immediate) fits in 6 bits && LO(immediate) != 0: + // c.addi rd, LO(immediate) + // else: + // addi rd, rd, LO(immediate) + load_upper_immediate(state, rd, upper); + load_lower_immediate(state, rd, lower); +} + +void asm_rv32_emit_optimised_load_immediate(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { + if (FIT_SIGNED(immediate, 6)) { + // c.li rd, immediate + asm_rv32_opcode_cli(state, rd, immediate); + return; + } + + if (FIT_SIGNED(immediate, 12)) { + // addi rd, zero, immediate + asm_rv32_opcode_addi(state, rd, ASM_RV32_REG_ZERO, immediate); + return; + } + + load_full_immediate(state, rd, immediate); +} + +// RV32 does not have dedicated push/pop opcodes, so series of loads and +// stores are generated in their place. + +static void emit_registers_store(asm_rv32_t *state, mp_uint_t registers_mask) { + mp_uint_t offset = 0; + for (mp_uint_t register_index = 0; register_index < AVAILABLE_REGISTERS_COUNT; register_index++) { + if (registers_mask & (1U << register_index)) { + assert(FIT_UNSIGNED(offset >> 2, 6) && "Registers save stack offset out of range."); + // c.swsp register, offset + asm_rv32_opcode_cswsp(state, register_index, offset); + offset += sizeof(uint32_t); + } + } +} + +static void emit_registers_load(asm_rv32_t *state, mp_uint_t registers_mask) { + mp_uint_t offset = 0; + for (mp_uint_t register_index = 0; register_index < AVAILABLE_REGISTERS_COUNT; register_index++) { + if (registers_mask & (1U << register_index)) { + assert(FIT_UNSIGNED(offset >> 2, 6) && "Registers load stack offset out of range."); + // c.lwsp register, offset + asm_rv32_opcode_clwsp(state, register_index, offset); + offset += sizeof(uint32_t); + } + } +} + +static void adjust_stack(asm_rv32_t *state, mp_int_t stack_size) { + if (stack_size == 0) { + return; + } + + if (FIT_SIGNED(stack_size, 6)) { + // c.addi sp, stack_size + asm_rv32_opcode_caddi(state, ASM_RV32_REG_SP, stack_size); + return; + } + + if (FIT_SIGNED(stack_size, 12)) { + // addi sp, sp, stack_size + asm_rv32_opcode_addi(state, ASM_RV32_REG_SP, ASM_RV32_REG_SP, stack_size); + return; + } + + // li temporary, stack_size + // c.add sp, temporary + load_full_immediate(state, REG_TEMP0, stack_size); + asm_rv32_opcode_cadd(state, ASM_RV32_REG_SP, REG_TEMP0); +} + +// Generate a generic function entry prologue code sequence, setting up the +// stack to hold all the tainted registers and an arbitrary amount of space +// for locals. +static void emit_function_prologue(asm_rv32_t *state, mp_uint_t registers) { + mp_uint_t registers_count = MP_POPCOUNT(registers); + state->stack_size = (registers_count + state->locals_count) * sizeof(uint32_t); + mp_uint_t old_saved_registers_mask = state->saved_registers_mask; + // Move stack pointer up. + adjust_stack(state, -state->stack_size); + // Store registers at the top of the saved stack area. + emit_registers_store(state, registers); + state->locals_stack_offset = registers_count * sizeof(uint32_t); + state->saved_registers_mask = old_saved_registers_mask; +} + +// Restore registers and reset the stack pointer to its initial value. +static void emit_function_epilogue(asm_rv32_t *state, mp_uint_t registers) { + mp_uint_t old_saved_registers_mask = state->saved_registers_mask; + // Restore registers from the top of the stack area. + emit_registers_load(state, registers); + // Move stack pointer down. + adjust_stack(state, state->stack_size); + state->saved_registers_mask = old_saved_registers_mask; +} + +/////////////////////////////////////////////////////////////////////////////// + +void asm_rv32_entry(asm_rv32_t *state, mp_uint_t locals) { + state->saved_registers_mask |= (1U << REG_FUN_TABLE) | (1U << REG_LOCAL_1) | \ + (1U << REG_LOCAL_2) | (1U << REG_LOCAL_3); + state->locals_count = locals; + emit_function_prologue(state, state->saved_registers_mask); +} + +void asm_rv32_exit(asm_rv32_t *state) { + emit_function_epilogue(state, state->saved_registers_mask); + // c.jr ra + asm_rv32_opcode_cjr(state, ASM_RV32_REG_RA); +} + +void asm_rv32_end_pass(asm_rv32_t *state) { + (void)state; +} + +void asm_rv32_emit_call_ind(asm_rv32_t *state, mp_uint_t index) { + mp_uint_t offset = index * ASM_WORD_SIZE; + state->saved_registers_mask |= (1U << ASM_RV32_REG_RA); + + if (FIT_UNSIGNED(offset, 11)) { + // lw temporary, offset(fun_table) + // c.jalr temporary + asm_rv32_opcode_lw(state, INTERNAL_TEMPORARY, REG_FUN_TABLE, offset); + asm_rv32_opcode_cjalr(state, INTERNAL_TEMPORARY); + return; + } + + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(offset, &upper, &lower); + + // TODO: Can this clobber REG_TEMP[0:2]? + + // lui temporary, HI(index) ; Or c.lui if possible + // c.add temporary, fun_table + // lw temporary, LO(index)(temporary) + // c.jalr temporary + load_upper_immediate(state, INTERNAL_TEMPORARY, upper); + asm_rv32_opcode_cadd(state, INTERNAL_TEMPORARY, REG_FUN_TABLE); + asm_rv32_opcode_lw(state, INTERNAL_TEMPORARY, INTERNAL_TEMPORARY, lower); + asm_rv32_opcode_cjalr(state, INTERNAL_TEMPORARY); +} + +void asm_rv32_emit_jump_if_reg_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t label) { + ptrdiff_t displacement = (ptrdiff_t)(state->base.label_offsets[label] - state->base.code_offset); + + // The least significant bit is ignored anyway. + if (FIT_SIGNED(displacement, 13)) { + // beq rs1, rs2, displacement + asm_rv32_opcode_beq(state, rs1, rs2, displacement); + return; + } + + // Compensate for the initial BNE opcode. + displacement -= ASM_WORD_SIZE; + + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(displacement, &upper, &lower); + + // TODO: Can this clobber REG_TEMP[0:2]? + + // bne rs1, rs2, 12 ; PC + 0 + // auipc temporary, HI(displacement) ; PC + 4 + // jalr zero, temporary, LO(displacement) ; PC + 8 + // ... ; PC + 12 + asm_rv32_opcode_bne(state, rs1, rs2, 12); + asm_rv32_opcode_auipc(state, INTERNAL_TEMPORARY, upper); + asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, INTERNAL_TEMPORARY, lower); +} + +void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_t label) { + ptrdiff_t displacement = (ptrdiff_t)(state->base.label_offsets[label] - state->base.code_offset); + + // The least significant bit is ignored anyway. + if (FIT_SIGNED(displacement, 13)) { + // bne rs, zero, displacement + asm_rv32_opcode_bne(state, rs, ASM_RV32_REG_ZERO, displacement); + return; + } + + // Compensate for the initial BEQ opcode. + displacement -= ASM_WORD_SIZE; + + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(displacement, &upper, &lower); + + // TODO: Can this clobber REG_TEMP[0:2]? + + // beq rs1, zero, 12 ; PC + 0 + // auipc temporary, HI(displacement) ; PC + 4 + // jalr zero, temporary, LO(displacement) ; PC + 8 + // ... ; PC + 12 + asm_rv32_opcode_beq(state, rs, ASM_RV32_REG_ZERO, 12); + asm_rv32_opcode_auipc(state, INTERNAL_TEMPORARY, upper); + asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, INTERNAL_TEMPORARY, lower); +} + +void asm_rv32_emit_mov_local_reg(asm_rv32_t *state, mp_uint_t local, mp_uint_t rs) { + mp_uint_t offset = state->locals_stack_offset + (local * ASM_WORD_SIZE); + + if (FIT_UNSIGNED(offset >> 2, 6)) { + // c.swsp rs, offset + asm_rv32_opcode_cswsp(state, rs, offset); + return; + } + + if (FIT_UNSIGNED(offset, 11)) { + // sw rs, offset(sp) + asm_rv32_opcode_sw(state, rs, ASM_RV32_REG_SP, offset); + return; + } + + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(offset, &upper, &lower); + + // TODO: Can this clobber REG_TEMP[0:2]? + + // lui temporary, HI(offset) ; Or c.lui if possible + // c.add temporary, sp + // sw rs, LO(offset)(temporary) + load_upper_immediate(state, INTERNAL_TEMPORARY, upper); + asm_rv32_opcode_cadd(state, INTERNAL_TEMPORARY, ASM_RV32_REG_SP); + asm_rv32_opcode_sw(state, rs, INTERNAL_TEMPORARY, lower); +} + +void asm_rv32_emit_mov_reg_local(asm_rv32_t *state, mp_uint_t rd, mp_uint_t local) { + mp_uint_t offset = state->locals_stack_offset + (local * ASM_WORD_SIZE); + + if (FIT_UNSIGNED(offset >> 2, 6)) { + // c.lwsp rd, offset + asm_rv32_opcode_clwsp(state, rd, offset); + return; + } + + if (FIT_UNSIGNED(offset, 11)) { + // lw rd, offset(sp) + asm_rv32_opcode_lw(state, rd, ASM_RV32_REG_SP, offset); + return; + } + + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(offset, &upper, &lower); + + // lui rd, HI(offset) ; Or c.lui if possible + // c.add rd, sp + // lw rd, LO(offset)(rd) + load_upper_immediate(state, rd, upper); + asm_rv32_opcode_cadd(state, rd, ASM_RV32_REG_SP); + asm_rv32_opcode_lw(state, rd, rd, lower); +} + +void asm_rv32_emit_mov_reg_local_addr(asm_rv32_t *state, mp_uint_t rd, mp_uint_t local) { + mp_uint_t offset = state->locals_stack_offset + (local * ASM_WORD_SIZE); + + if (FIT_SIGNED(offset, 11)) { + // addi rd, sp, offset + asm_rv32_opcode_addi(state, rd, ASM_RV32_REG_SP, offset); + return; + } + + // li rd, offset + // c.add rd, sp + load_full_immediate(state, rd, offset); + asm_rv32_opcode_cadd(state, rd, ASM_RV32_REG_SP); +} + +void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { + mp_int_t scaled_offset = offset * sizeof(ASM_WORD_SIZE); + + if (FIT_SIGNED(scaled_offset, 12)) { + // lw rd, offset(rs) + asm_rv32_opcode_lw(state, rd, rs, scaled_offset); + return; + } + + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(scaled_offset, &upper, &lower); + + // lui rd, HI(offset) ; Or c.lui if possible + // c.add rd, rs + // lw rd, LO(offset)(rd) + load_upper_immediate(state, rd, upper); + asm_rv32_opcode_cadd(state, rd, rs); + asm_rv32_opcode_lw(state, rd, rd, lower); +} + +void asm_rv32_emit_jump(asm_rv32_t *state, mp_uint_t label) { + ptrdiff_t displacement = (ptrdiff_t)(state->base.label_offsets[label] - state->base.code_offset); + + // The least significant bit is ignored anyway. + if (FIT_SIGNED(displacement, 13)) { + // c.j displacement + asm_rv32_opcode_cj(state, displacement); + return; + } + + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(displacement, &upper, &lower); + + // TODO: Can this clobber REG_TEMP[0:2]? + + // auipc temporary, HI(displacement) + // jalr zero, temporary, LO(displacement) + asm_rv32_opcode_auipc(state, INTERNAL_TEMPORARY, upper); + asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, INTERNAL_TEMPORARY, lower); +} + +void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { + mp_int_t scaled_offset = offset * ASM_WORD_SIZE; + + if (FIT_SIGNED(scaled_offset, 12)) { + // sw rd, offset(rs) + asm_rv32_opcode_sw(state, rd, rs, scaled_offset); + return; + } + + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(scaled_offset, &upper, &lower); + + // lui rd, HI(offset) ; Or c.lui if possible + // c.add rd, rs + // sw rd, LO(offset)(rd) + load_upper_immediate(state, rd, upper); + asm_rv32_opcode_cadd(state, rd, rs); + asm_rv32_opcode_sw(state, rd, rd, lower); +} + +void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t label) { + ptrdiff_t displacement = (ptrdiff_t)(state->base.label_offsets[label] - state->base.code_offset); + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(displacement, &upper, &lower); + + // Compressed instructions are not used even if they may allow for code size + // savings as the code changes size between compute and emit passes + // otherwise. If that happens then the assertion at asmbase.c:93 triggers + // when built in debug mode. + + // auipc rd, HI(relative) + // addi rd, rd, LO(relative) + asm_rv32_opcode_auipc(state, rd, upper); + asm_rv32_opcode_addi(state, rd, rd, lower); +} + +void asm_rv32_emit_load16_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { + mp_int_t scaled_offset = offset * sizeof(uint16_t); + + if (FIT_SIGNED(scaled_offset, 12)) { + // lhu rd, offset(rs) + asm_rv32_opcode_lhu(state, rd, rs, scaled_offset); + return; + } + + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(scaled_offset, &upper, &lower); + + // lui rd, HI(offset) ; Or c.lui if possible + // c.add rd, rs + // lhu rd, LO(offset)(rd) + load_upper_immediate(state, rd, upper); + asm_rv32_opcode_cadd(state, rd, rs); + asm_rv32_opcode_lhu(state, rd, rd, lower); +} + +void asm_rv32_emit_optimised_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) { + if (rs == rd) { + // c.li rd, 0 + asm_rv32_opcode_cli(state, rd, 0); + return; + } + + // xor rd, rd, rs + asm_rv32_opcode_xor(state, rd, rd, rs); +} + +void asm_rv32_meta_comparison_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd) { + // c.li rd, 1 ; + // beq rs1, rs2, 8 ; PC + 0 + // addi rd, zero, 0 ; PC + 4 + // ... ; PC + 8 + asm_rv32_opcode_cli(state, rd, 1); + asm_rv32_opcode_beq(state, rs1, rs2, 8); + asm_rv32_opcode_addi(state, rd, ASM_RV32_REG_ZERO, 0); +} + +void asm_rv32_meta_comparison_ne(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd) { + // sub rd, rs1, rs2 + // sltu rd, zero, rd + asm_rv32_opcode_sub(state, rd, rs1, rs2); + asm_rv32_opcode_sltu(state, rd, ASM_RV32_REG_ZERO, rd); +} + +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); + } +} + +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) { + // c.li rd, 1 ; + // beq rs1, rs2, 8 ; PC + 0 + // slt(u) rd, rs1, rs2 ; PC + 4 + // ... ; 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); + } +} + +#endif // MICROPY_EMIT_RV32 diff --git a/py/asmrv32.h b/py/asmrv32.h new file mode 100644 index 0000000000000..4061fd5f86be1 --- /dev/null +++ b/py/asmrv32.h @@ -0,0 +1,464 @@ +/* + * This file is part of the MicroPython project, https://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Alessandro Gatti + * + * 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_PY_ASMRV32_H +#define MICROPY_INCLUDED_PY_ASMRV32_H + +#include + +#include "py/asmbase.h" +#include "py/emit.h" +#include "py/misc.h" +#include "py/persistentcode.h" + +#define ASM_RV32_REG_X0 (0) // Zero +#define ASM_RV32_REG_X1 (1) // RA +#define ASM_RV32_REG_X2 (2) // SP +#define ASM_RV32_REG_X3 (3) // GP +#define ASM_RV32_REG_X4 (4) // TP +#define ASM_RV32_REG_X5 (5) // T0 +#define ASM_RV32_REG_X6 (6) // T1 +#define ASM_RV32_REG_X7 (7) // T2 +#define ASM_RV32_REG_X8 (8) // S0 +#define ASM_RV32_REG_X9 (9) // S1 +#define ASM_RV32_REG_X10 (10) // A0 +#define ASM_RV32_REG_X11 (11) // A1 +#define ASM_RV32_REG_X12 (12) // A2 +#define ASM_RV32_REG_X13 (13) // A3 +#define ASM_RV32_REG_X14 (14) // A4 +#define ASM_RV32_REG_X15 (15) // A5 +#define ASM_RV32_REG_X16 (16) // A6 +#define ASM_RV32_REG_X17 (17) // A7 +#define ASM_RV32_REG_X18 (18) // S2 +#define ASM_RV32_REG_X19 (19) // S3 +#define ASM_RV32_REG_X20 (20) // S4 +#define ASM_RV32_REG_X21 (21) // S5 +#define ASM_RV32_REG_X22 (22) // S6 +#define ASM_RV32_REG_X23 (23) // S7 +#define ASM_RV32_REG_X24 (24) // S8 +#define ASM_RV32_REG_X25 (25) // S9 +#define ASM_RV32_REG_X26 (26) // S10 +#define ASM_RV32_REG_X27 (27) // S11 +#define ASM_RV32_REG_X28 (28) // T3 +#define ASM_RV32_REG_X29 (29) // T4 +#define ASM_RV32_REG_X30 (30) // T5 +#define ASM_RV32_REG_X31 (31) // T6 + +// Alternate register names. + +#define ASM_RV32_REG_ZERO (ASM_RV32_REG_X0) +#define ASM_RV32_REG_RA (ASM_RV32_REG_X1) +#define ASM_RV32_REG_SP (ASM_RV32_REG_X2) +#define ASM_RV32_REG_GP (ASM_RV32_REG_X3) +#define ASM_RV32_REG_TP (ASM_RV32_REG_X4) +#define ASM_RV32_REG_T0 (ASM_RV32_REG_X5) +#define ASM_RV32_REG_T1 (ASM_RV32_REG_X6) +#define ASM_RV32_REG_T2 (ASM_RV32_REG_X7) +#define ASM_RV32_REG_A0 (ASM_RV32_REG_X10) +#define ASM_RV32_REG_A1 (ASM_RV32_REG_X11) +#define ASM_RV32_REG_A2 (ASM_RV32_REG_X12) +#define ASM_RV32_REG_A3 (ASM_RV32_REG_X13) +#define ASM_RV32_REG_A4 (ASM_RV32_REG_X14) +#define ASM_RV32_REG_A5 (ASM_RV32_REG_X15) +#define ASM_RV32_REG_A6 (ASM_RV32_REG_X16) +#define ASM_RV32_REG_A7 (ASM_RV32_REG_X17) +#define ASM_RV32_REG_T3 (ASM_RV32_REG_X28) +#define ASM_RV32_REG_T4 (ASM_RV32_REG_X29) +#define ASM_RV32_REG_T5 (ASM_RV32_REG_X30) +#define ASM_RV32_REG_T6 (ASM_RV32_REG_X31) +#define ASM_RV32_REG_FP (ASM_RV32_REG_X8) +#define ASM_RV32_REG_S0 (ASM_RV32_REG_X8) +#define ASM_RV32_REG_S1 (ASM_RV32_REG_X9) +#define ASM_RV32_REG_S2 (ASM_RV32_REG_X18) +#define ASM_RV32_REG_S3 (ASM_RV32_REG_X19) +#define ASM_RV32_REG_S4 (ASM_RV32_REG_X20) +#define ASM_RV32_REG_S5 (ASM_RV32_REG_X21) +#define ASM_RV32_REG_S6 (ASM_RV32_REG_X22) +#define ASM_RV32_REG_S7 (ASM_RV32_REG_X23) +#define ASM_RV32_REG_S8 (ASM_RV32_REG_X24) +#define ASM_RV32_REG_S9 (ASM_RV32_REG_X25) +#define ASM_RV32_REG_S10 (ASM_RV32_REG_X26) +#define ASM_RV32_REG_S11 (ASM_RV32_REG_X27) + +typedef struct _asm_rv32_t { + // Opaque emitter state. + mp_asm_base_t base; + // Which registers are tainted and need saving/restoring. + mp_uint_t saved_registers_mask; + // How many locals must be stored on the stack. + mp_uint_t locals_count; + // The computed function stack size. + mp_uint_t stack_size; + // The stack offset where stack-based locals start to be stored. + mp_uint_t locals_stack_offset; +} asm_rv32_t; + +void asm_rv32_entry(asm_rv32_t *state, mp_uint_t locals); +void asm_rv32_exit(asm_rv32_t *state); +void asm_rv32_end_pass(asm_rv32_t *state); + +//////////////////////////////////////////////////////////////////////////////// + +#define RV32_ENCODE_TYPE_B(op, ft3, rs1, rs2, imm) \ + ((op & 0b1111111) | ((ft3 & 0b111) << 12) | ((imm & 0b100000000000) >> 4) | \ + ((imm & 0b11110) << 7) | ((rs1 & 0b11111) << 15) | \ + ((rs2 & 0b11111) << 20) | ((imm & 0b11111100000) << 20) | \ + ((imm & 0b1000000000000) << 19)) + +#define RV32_ENCODE_TYPE_I(op, ft3, rd, rs, imm) \ + ((op & 0b1111111) | ((rd & 0b11111) << 7) | ((ft3 & 0b111) << 12) | \ + ((rs & 0b11111) << 15) | ((imm & 0b111111111111) << 20)) + +#define RV32_ENCODE_TYPE_J(op, rd, imm) \ + ((op & 0b1111111) | ((rd & 0b11111) << 7) | (imm & 0b11111111000000000000) | \ + ((imm & 0b100000000000) << 9) | ((imm & 0b11111111110) << 20) | \ + ((imm & 0b100000000000000000000) << 11)) + +#define RV32_ENCODE_TYPE_R(op, ft3, ft7, rd, rs1, rs2) \ + ((op & 0b1111111) | ((rd & 0b11111) << 7) | ((ft3 & 0b111) << 12) | \ + ((rs1 & 0b11111) << 15) | ((rs2 & 0b11111) << 20) | \ + ((ft7 & 0b1111111) << 25)) + +#define RV32_ENCODE_TYPE_S(op, ft3, rs1, rs2, imm) \ + ((op & 0b1111111) | ((imm & 0b11111) << 7) | ((ft3 & 0b111) << 12) | \ + ((rs1 & 0b11111) << 15) | ((rs2 & 0b11111) << 20) | \ + ((imm & 0b111111100000) << 20)) + +#define RV32_ENCODE_TYPE_U(op, rd, imm) \ + ((op & 0b1111111) | ((rd & 0b11111) << 7) | \ + (imm & 0b11111111111111111111000000000000)) + +#define RV32_ENCODE_TYPE_CI(op, ft3, rd, imm) \ + ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rd & 0b11111) << 7) | \ + (((imm) & 0b100000) << 7) | (((imm) & 0b11111) << 2)) + +#define RV32_ENCODE_TYPE_CJ(op, ft3, imm) \ + ((op & 0b11) | ((ft3 & 0b111) << 13) | \ + ((imm & 0b1110) << 2) | ((imm & 0b1100000000) << 1) | \ + ((imm & 0b100000000000) << 1) | ((imm & 0b10000000000) >> 2) | \ + ((imm & 0b10000000) >> 1) | ((imm & 0b1000000) << 1) | \ + ((imm & 0b100000) >> 3) | ((imm & 0b10000) << 7)) + +#define RV32_ENCODE_TYPE_CR(op, ft4, rs1, rs2) \ + ((op & 0b11) | ((rs2 & 0b11111) << 2) | ((rs1 & 0b11111) << 7) | \ + ((ft4 & 0b1111) << 12)) + +#define RV32_ENCODE_TYPE_CSS(op, ft3, rs, imm) \ + ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rs & 0b11111) << 2) | ((imm) & 0b111111) << 7) + +void asm_rv32_emit_word_opcode(asm_rv32_t *state, mp_uint_t opcode); +void asm_rv32_emit_halfword_opcode(asm_rv32_t *state, mp_uint_t opcode); + +// ADD RD, RS1, RS2 +static inline void asm_rv32_opcode_add(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000000 ..... ..... 000 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b000, 0b0000000, rd, rs1, rs2)); +} + +// ADDI RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_addi(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { + // I: ............ ..... 000 ..... 0010011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0010011, 0b000, rd, rs, immediate)); +} + +// AND RD, RS1, RS2 +static inline void asm_rv32_opcode_and(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000000 ..... ..... 111 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b111, 0b0000000, rd, rs1, rs2)); +} + +// AUIPC RD, offset +static inline void asm_rv32_opcode_auipc(asm_rv32_t *state, mp_uint_t rd, mp_int_t offset) { + // U: .................... ..... 0010111 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_U(0b0010111, rd, offset)); +} + +// BEQ RS1, RS2, OFFSET +static inline void asm_rv32_opcode_beq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_int_t offset) { + // B: . ...... ..... ..... 000 .... . 1100011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_B(0b1100011, 0b000, rs1, rs2, offset)); +} + +// BNE RS1, RS2, OFFSET +static inline void asm_rv32_opcode_bne(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_int_t offset) { + // B: . ...... ..... ..... 001 .... . 1100011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_B(0b1100011, 0b001, rs1, rs2, offset)); +} + +// C.ADD RD, RS +static inline void asm_rv32_opcode_cadd(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) { + // CR: 1001 ..... ..... 10 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0b10, 0b1001, rd, rs)); +} + +// C.ADDI RD, IMMEDIATE +static inline void asm_rv32_opcode_caddi(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { + // CI: 000 . ..... ..... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0b01, 0b000, rd, immediate)); +} + +// C.J OFFSET +static inline void asm_rv32_opcode_cj(asm_rv32_t *state, mp_uint_t offset) { + // CJ: 101 ........... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CJ(0b01, 0b001, offset)); +} + +// C.JALR RS +static inline void asm_rv32_opcode_cjalr(asm_rv32_t *state, mp_uint_t rs) { + // CR: 1001 ..... 00000 10 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0b10, 0b1001, rs, 0)); +} + +// C.JR RS +static inline void asm_rv32_opcode_cjr(asm_rv32_t *state, mp_uint_t rs) { + // CR: 1000 ..... 00000 10 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0b10, 0b1000, rs, 0)); +} + +// C.LI RD, IMMEDIATE +static inline void asm_rv32_opcode_cli(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { + // CI: 010 . ..... ..... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0b01, 0b010, rd, immediate)); +} + +// C.LUI RD, IMMEDIATE +static inline void asm_rv32_opcode_clui(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { + // CI: 011 . ..... ..... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0b01, 0b011, rd, immediate >> 12)); +} + +// C.LWSP RD, OFFSET +static inline void asm_rv32_opcode_clwsp(asm_rv32_t *state, mp_uint_t rd, mp_uint_t offset) { + // CI: 010 . ..... ..... 10 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0b10, 0b010, rd, ((offset & 0b11000000) >> 6) | (offset & 0b111100))); +} + +// C.MV RD, RS +static inline void asm_rv32_opcode_cmv(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) { + // CR: 1000 ..... ..... 10 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0b10, 0b1000, rd, rs)); +} + +// C.SWSP RS, OFFSET +static inline void asm_rv32_opcode_cswsp(asm_rv32_t *state, mp_uint_t rs, mp_uint_t offset) { + // CSS: 010 ...... ..... 10 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CSS(0b10, 0b110, rs, ((offset & 0b11000000) >> 6) | (offset & 0b111100))); +} + +// JALR RD, RS, offset +static inline void asm_rv32_opcode_jalr(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { + // I: ............ ..... 000 ..... 1100111 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b1100111, 0b000, rd, rs, offset)); +} + +// LBU RD, OFFSET(RS) +static inline void asm_rv32_opcode_lbu(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { + // I: ............ ..... 100 ..... 0000011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0000011, 0b100, rd, rs, offset)); +} + +// LHU RD, OFFSET(RS) +static inline void asm_rv32_opcode_lhu(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { + // I: ............ ..... 101 ..... 0000011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0000011, 0b101, rd, rs, offset)); +} + +// LUI RD, immediate +static inline void asm_rv32_opcode_lui(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { + // U: .................... ..... 0110111 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_U(0b0110111, rd, immediate)); +} + +// LW RD, OFFSET(RS) +static inline void asm_rv32_opcode_lw(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { + // I: ............ ..... 010 ..... 0000011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0000011, 0b010, rd, rs, offset)); +} + +// MUL RD, RS1, RS2 +static inline void asm_rv32m_opcode_mul(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000001 ..... ..... 000 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b000, 0b0000001, rd, rs1, rs2)); +} + +// OR RD, RS1, RS2 +static inline void asm_rv32_opcode_or(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000000 ..... ..... 110 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b110, 0b0000000, rd, rs1, rs2)); +} + +// SLL RD, RS1, RS2 +static inline void asm_rv32_opcode_sll(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000000 ..... ..... 001 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b001, 0b0000000, rd, rs1, rs2)); +} + +// SLLI RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_slli(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_uint_t immediate) { + // I: 0000000..... ..... 001 ..... 0010011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0010011, 0b001, rd, rs, immediate & 0x1F)); +} + +// SRL RD, RS1, RS2 +static inline void asm_rv32_opcode_srl(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000000 ..... ..... 101 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b101, 0b0000000, rd, rs1, rs2)); +} + +// SLT RD, RS1, RS2 +static inline void asm_rv32_opcode_slt(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000000 ..... ..... 010 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b010, 0b0000000, rd, rs1, rs2)); +} + +// SLTU RD, RS1, RS2 +static inline void asm_rv32_opcode_sltu(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000000 ..... ..... 011 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b011, 0b0000000, rd, rs1, rs2)); +} + +// SRA RD, RS1, RS2 +static inline void asm_rv32_opcode_sra(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0100000 ..... ..... 101 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b101, 0b0100000, rd, rs1, rs2)); +} + +// SUB RD, RS1, RS2 +static inline void asm_rv32_opcode_sub(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0100000 ..... ..... 000 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b000, 0b0100000, rd, rs1, rs2)); +} + +// SB RS2, OFFSET(RS1) +static inline void asm_rv32_opcode_sb(asm_rv32_t *state, mp_uint_t rs2, mp_uint_t rs1, mp_int_t offset) { + // S: ....... ..... ..... 000 ..... 0100011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0b0100011, 0b000, rs1, rs2, offset)); +} + +// SH RS2, OFFSET(RS1) +static inline void asm_rv32_opcode_sh(asm_rv32_t *state, mp_uint_t rs2, mp_uint_t rs1, mp_int_t offset) { + // S: ....... ..... ..... 001 ..... 0100011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0b0100011, 0b001, rs1, rs2, offset)); +} + +// SW RS2, OFFSET(RS1) +static inline void asm_rv32_opcode_sw(asm_rv32_t *state, mp_uint_t rs2, mp_uint_t rs1, mp_int_t offset) { + // S: ....... ..... ..... 010 ..... 0100011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0b0100011, 0b010, rs1, rs2, offset)); +} + +// XOR RD, RS1, RS2 +static inline void asm_rv32_opcode_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000000 ..... ..... 100 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b100, 0b0000000, rd, rs1, rs2)); +} + +// XORI RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_xori(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { + // I: ............ ..... 100 ..... 0010011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0010011, 0b100, rd, rs, immediate)); +} + +#define ASM_WORD_SIZE (4) + +#define REG_RET ASM_RV32_REG_A0 +#define REG_ARG_1 ASM_RV32_REG_A0 +#define REG_ARG_2 ASM_RV32_REG_A1 +#define REG_ARG_3 ASM_RV32_REG_A2 +#define REG_ARG_4 ASM_RV32_REG_A3 +#define REG_TEMP0 ASM_RV32_REG_T1 +#define REG_TEMP1 ASM_RV32_REG_T2 +#define REG_TEMP2 ASM_RV32_REG_T3 +// S0 may be used as the frame pointer by the compiler. +#define REG_FUN_TABLE ASM_RV32_REG_S2 +#define REG_LOCAL_1 ASM_RV32_REG_S3 +#define REG_LOCAL_2 ASM_RV32_REG_S4 +#define REG_LOCAL_3 ASM_RV32_REG_S5 + +void asm_rv32_meta_comparison_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd); +void asm_rv32_meta_comparison_ne(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd); +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); +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); + +#ifdef GENERIC_ASM_API + +void asm_rv32_emit_call_ind(asm_rv32_t *state, mp_uint_t index); +void asm_rv32_emit_jump(asm_rv32_t *state, mp_uint_t label); +void asm_rv32_emit_jump_if_reg_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t label); +void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_t label); +void asm_rv32_emit_load16_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset); +void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset); +void asm_rv32_emit_mov_local_reg(asm_rv32_t *state, mp_uint_t local, mp_uint_t rs); +void asm_rv32_emit_mov_reg_local(asm_rv32_t *state, mp_uint_t rd, mp_uint_t local); +void asm_rv32_emit_mov_reg_local_addr(asm_rv32_t *state, mp_uint_t rd, mp_uint_t local); +void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t label); +void asm_rv32_emit_optimised_load_immediate(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate); +void asm_rv32_emit_optimised_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs); +void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_uint_t base, mp_int_t offset); + +#define ASM_T asm_rv32_t +#define ASM_ENTRY(state, labels) asm_rv32_entry(state, labels) +#define ASM_EXIT(state) asm_rv32_exit(state) +#define ASM_END_PASS(state) asm_rv32_end_pass(state) + +#define ASM_ADD_REG_REG(state, rd, rs) asm_rv32_opcode_cadd(state, rd, rs) +#define ASM_AND_REG_REG(state, rd, rs) asm_rv32_opcode_and(state, rd, rs, rd) +#define ASM_ASR_REG_REG(state, rd, rs) asm_rv32_opcode_sra(state, rd, rd, rs) +#define ASM_CALL_IND(state, index) asm_rv32_emit_call_ind(state, index) +#define ASM_JUMP(state, label) asm_rv32_emit_jump(state, label) +#define ASM_JUMP_IF_REG_EQ(state, rs1, rs2, label) asm_rv32_emit_jump_if_reg_eq(state, rs1, rs2, label) +#define ASM_JUMP_IF_REG_NONZERO(state, rs, label, bool_test) asm_rv32_emit_jump_if_reg_nonzero(state, rs, label) +#define ASM_JUMP_IF_REG_ZERO(state, rs, label, bool_test) asm_rv32_emit_jump_if_reg_eq(state, rs, ASM_RV32_REG_ZERO, label) +#define ASM_JUMP_REG(state, rs) asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, rs, 0) +#define ASM_LOAD16_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load16_reg_reg_offset(state, rd, rs, offset) +#define ASM_LOAD16_REG_REG(state, rd, rs) asm_rv32_opcode_lhu(state, rd, rs, 0) +#define ASM_LOAD32_REG_REG(state, rd, rs) asm_rv32_opcode_lw(state, rd, rs, 0) +#define ASM_LOAD8_REG_REG(state, rd, rs) asm_rv32_opcode_lbu(state, rd, rs, 0) +#define ASM_LOAD_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load_reg_reg_offset(state, rd, rs, offset) +#define ASM_LOAD_REG_REG(state, rd, rs) ASM_LOAD32_REG_REG(state, rd, rs) +#define ASM_LSL_REG_REG(state, rd, rs) asm_rv32_opcode_sll(state, rd, rd, rs) +#define ASM_LSR_REG_REG(state, rd, rs) asm_rv32_opcode_srl(state, rd, rd, rs) +#define ASM_MOV_LOCAL_REG(state, local, rs) asm_rv32_emit_mov_local_reg(state, local, rs) +#define ASM_MOV_REG_IMM(state, rd, imm) asm_rv32_emit_optimised_load_immediate(state, rd, imm) +#define ASM_MOV_REG_LOCAL_ADDR(state, rd, local) asm_rv32_emit_mov_reg_local_addr(state, rd, local) +#define ASM_MOV_REG_LOCAL(state, rd, local) asm_rv32_emit_mov_reg_local(state, rd, local) +#define ASM_MOV_REG_PCREL(state, rd, label) asm_rv32_emit_mov_reg_pcrel(state, rd, label) +#define ASM_MOV_REG_REG(state, rd, rs) asm_rv32_opcode_cmv(state, rd, rs) +#define ASM_MUL_REG_REG(state, rd, rs) asm_rv32m_opcode_mul(state, rd, rd, rs) +#define ASM_NEG_REG(state, rd) asm_rv32_opcode_sub(state, rd, ASM_RV32_REG_ZERO, rd) +#define ASM_NOT_REG(state, rd) asm_rv32_opcode_xori(state, rd, rd, -1) +#define ASM_OR_REG_REG(state, rd, rs) asm_rv32_opcode_or(state, rd, rd, rs) +#define ASM_STORE16_REG_REG(state, rs1, rs2) asm_rv32_opcode_sh(state, rs1, rs2, 0) +#define ASM_STORE32_REG_REG(state, rs1, rs2) asm_rv32_opcode_sw(state, rs1, rs2, 0) +#define ASM_STORE8_REG_REG(state, rs1, rs2) asm_rv32_opcode_sb(state, rs1, rs2, 0) +#define ASM_STORE_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_store_reg_reg_offset(state, rd, rs, offset) +#define ASM_STORE_REG_REG(state, rs1, rs2) ASM_STORE32_REG_REG(state, rs1, rs2) +#define ASM_SUB_REG_REG(state, rd, rs) asm_rv32_opcode_sub(state, rd, rd, rs) +#define ASM_XOR_REG_REG(state, rd, rs) asm_rv32_emit_optimised_xor(state, rd, rs) + +#endif + +#endif // MICROPY_INCLUDED_PY_ASMRV32_H diff --git a/py/compile.c b/py/compile.c index 62757de3c083c..9b012f8787004 100644 --- a/py/compile.c +++ b/py/compile.c @@ -102,6 +102,7 @@ static const emit_method_table_t *emit_native_table[] = { &emit_native_thumb_method_table, &emit_native_xtensa_method_table, &emit_native_xtensawin_method_table, + &emit_native_rv32_method_table, }; #elif MICROPY_EMIT_NATIVE @@ -118,6 +119,8 @@ static const emit_method_table_t *emit_native_table[] = { #define NATIVE_EMITTER(f) emit_native_xtensa_##f #elif MICROPY_EMIT_XTENSAWIN #define NATIVE_EMITTER(f) emit_native_xtensawin_##f +#elif MICROPY_EMIT_RV32 +#define NATIVE_EMITTER(f) emit_native_rv32_##f #else #error "unknown native emitter" #endif diff --git a/py/emit.h b/py/emit.h index 26f978ba598ab..0c5019f07ac3f 100644 --- a/py/emit.h +++ b/py/emit.h @@ -201,6 +201,7 @@ extern const emit_method_table_t emit_native_thumb_method_table; extern const emit_method_table_t emit_native_arm_method_table; extern const emit_method_table_t emit_native_xtensa_method_table; extern const emit_method_table_t emit_native_xtensawin_method_table; +extern const emit_method_table_t emit_native_rv32_method_table; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops; @@ -213,6 +214,7 @@ emit_t *emit_native_thumb_new(mp_emit_common_t *emit_common, mp_obj_t *error_slo emit_t *emit_native_arm_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); emit_t *emit_native_xtensa_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); emit_t *emit_native_xtensawin_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_rv32_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); void emit_bc_set_max_num_labels(emit_t *emit, mp_uint_t max_num_labels); @@ -223,6 +225,7 @@ void emit_native_thumb_free(emit_t *emit); void emit_native_arm_free(emit_t *emit); void emit_native_xtensa_free(emit_t *emit); void emit_native_xtensawin_free(emit_t *emit); +void emit_native_rv32_free(emit_t *emit); void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope); bool mp_emit_bc_end_pass(emit_t *emit); diff --git a/py/emitglue.c b/py/emitglue.c index 6b6d5ccba2195..444e480477e54 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -158,7 +158,7 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons } DEBUG_printf("\n"); - #ifdef WRITE_CODE + #if WRITE_CODE FILE *fp_write_code = fopen("out-code", "wb"); fwrite(fun_data, fun_len, 1, fp_write_code); fclose(fp_write_code); diff --git a/py/emitnative.c b/py/emitnative.c index 0b84a2ec8a85d..7557e4ba48fac 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -59,7 +59,7 @@ #endif // wrapper around everything in this file -#if N_X64 || N_X86 || N_THUMB || N_ARM || N_XTENSA || N_XTENSAWIN +#if N_X64 || N_X86 || N_THUMB || N_ARM || N_XTENSA || N_XTENSAWIN || N_RV32 // C stack layout for native functions: // 0: nlr_buf_t [optional] @@ -2522,6 +2522,36 @@ static void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { } else { asm_xtensa_setcc_reg_reg_reg(emit->as, cc & ~0x80, REG_RET, reg_rhs, REG_ARG_2); } + #elif N_RV32 + (void)op_idx; + switch (op) { + case MP_BINARY_OP_LESS: + asm_rv32_meta_comparison_lt(emit->as, REG_ARG_2, reg_rhs, REG_RET, vtype_lhs == VTYPE_UINT); + break; + + case MP_BINARY_OP_MORE: + asm_rv32_meta_comparison_lt(emit->as, reg_rhs, REG_ARG_2, REG_RET, vtype_lhs == VTYPE_UINT); + break; + + case MP_BINARY_OP_EQUAL: + asm_rv32_meta_comparison_eq(emit->as, REG_ARG_2, reg_rhs, REG_RET); + break; + + case MP_BINARY_OP_LESS_EQUAL: + asm_rv32_meta_comparison_le(emit->as, REG_ARG_2, reg_rhs, REG_RET, vtype_lhs == VTYPE_UINT); + break; + + case MP_BINARY_OP_MORE_EQUAL: + asm_rv32_meta_comparison_le(emit->as, reg_rhs, REG_ARG_2, REG_RET, vtype_lhs == VTYPE_UINT); + break; + + case MP_BINARY_OP_NOT_EQUAL: + asm_rv32_meta_comparison_ne(emit->as, reg_rhs, REG_ARG_2, REG_RET); + break; + + default: + break; + } #else #error not implemented #endif diff --git a/py/emitnrv32.c b/py/emitnrv32.c new file mode 100644 index 0000000000000..4a44100093141 --- /dev/null +++ b/py/emitnrv32.c @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, https://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Alessandro Gatti + * + * 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. + */ + +// RISC-V RV32 specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_RV32 + +// this is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#include "py/asmrv32.h" + +// Word indices of REG_LOCAL_x in nlr_buf_t +#define NLR_BUF_IDX_LOCAL_1 (6) // S3 + +#define N_RV32 (1) +#define EXPORT_FUN(name) emit_native_rv32_##name +#include "py/emitnative.c" + +#endif diff --git a/py/mpconfig.h b/py/mpconfig.h index 2cbf5b88efab0..efb30ac04dada 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -406,8 +406,13 @@ #define MICROPY_EMIT_XTENSAWIN (0) #endif +// Whether to emit RISC-V RV32 native code +#ifndef MICROPY_EMIT_RV32 +#define MICROPY_EMIT_RV32 (0) +#endif + // Convenience definition for whether any native emitter is enabled -#define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN) +#define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN || MICROPY_EMIT_RV32) // Some architectures cannot read byte-wise from executable memory. In this case // the prelude for a native function (which usually sits after the machine code) diff --git a/py/py.cmake b/py/py.cmake index 74a433c97a3f1..f47661c409a14 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -9,6 +9,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/argcheck.c ${MICROPY_PY_DIR}/asmarm.c ${MICROPY_PY_DIR}/asmbase.c + ${MICROPY_PY_DIR}/asmrv32.c ${MICROPY_PY_DIR}/asmthumb.c ${MICROPY_PY_DIR}/asmx64.c ${MICROPY_PY_DIR}/asmx86.c @@ -25,6 +26,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/emitinlinethumb.c ${MICROPY_PY_DIR}/emitinlinextensa.c ${MICROPY_PY_DIR}/emitnarm.c + ${MICROPY_PY_DIR}/emitnrv32.c ${MICROPY_PY_DIR}/emitnthumb.c ${MICROPY_PY_DIR}/emitnx64.c ${MICROPY_PY_DIR}/emitnx86.c diff --git a/py/py.mk b/py/py.mk index 1378968c9646b..0b429f9efe7ac 100644 --- a/py/py.mk +++ b/py/py.mk @@ -114,6 +114,8 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ emitnxtensa.o \ emitinlinextensa.o \ emitnxtensawin.o \ + asmrv32.o \ + emitnrv32.o \ formatfloat.o \ parsenumbase.o \ parsenum.o \ From 99f5659cf5fa84d3b9dc8bbd286315fcf56ad899 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sun, 16 Jun 2024 20:40:28 +0200 Subject: [PATCH 0053/1300] mpy-cross: Add RISC-V RV32IMC support in MPY files. MPY files can now hold generated RV32IMC native code. This can be accomplished by passing the `-march=rv32imc` flag to mpy-cross. Signed-off-by: Alessandro Gatti --- docs/reference/mpyfiles.rst | 2 +- mpy-cross/main.c | 6 +++++- mpy-cross/mpconfigport.h | 1 + mpy-cross/mpy_cross/__init__.py | 1 + py/persistentcode.h | 3 +++ tools/mpy-tool.py | 7 +++++-- 6 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/reference/mpyfiles.rst b/docs/reference/mpyfiles.rst index 8e8284928dacb..be0114701aea7 100644 --- a/docs/reference/mpyfiles.rst +++ b/docs/reference/mpyfiles.rst @@ -58,7 +58,7 @@ If importing an .mpy file fails then try the following: sys_mpy = sys.implementation._mpy arch = [None, 'x86', 'x64', 'armv6', 'armv6m', 'armv7m', 'armv7em', 'armv7emsp', 'armv7emdp', - 'xtensa', 'xtensawin'][sys_mpy >> 10] + 'xtensa', 'xtensawin', 'rv32imc'][sys_mpy >> 10] print('mpy version:', sys_mpy & 0xff) print('mpy sub-version:', sys_mpy >> 8 & 3) print('mpy flags:', end='') diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 0ac2e891d6dea..a9066215d0381 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -129,7 +129,8 @@ static int usage(char **argv) { "\n" "Target specific options:\n" "-msmall-int-bits=number : set the maximum bits used to encode a small-int\n" - "-march= : set architecture for native emitter; x86, x64, armv6, armv6m, armv7m, armv7em, armv7emsp, armv7emdp, xtensa, xtensawin\n" + "-march= : set architecture for native emitter;\n" + " x86, x64, armv6, armv6m, armv7m, armv7em, armv7emsp, armv7emdp, xtensa, xtensawin, rv32imc\n" "\n" "Implementation specific options:\n", argv[0] ); @@ -312,6 +313,9 @@ MP_NOINLINE int main_(int argc, char **argv) { } else if (strcmp(arch, "xtensawin") == 0) { mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_XTENSAWIN; mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_XTENSAWIN; + } else if (strcmp(arch, "rv32imc") == 0) { + mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_RV32IMC; + mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_RV32I; } else if (strcmp(arch, "host") == 0) { #if defined(__i386__) || defined(_M_IX86) mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X86; diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index 4304c552bc45b..009aed33393a3 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -46,6 +46,7 @@ #define MICROPY_EMIT_XTENSA (1) #define MICROPY_EMIT_INLINE_XTENSA (1) #define MICROPY_EMIT_XTENSAWIN (1) +#define MICROPY_EMIT_RV32 (1) #define MICROPY_DYNAMIC_COMPILER (1) #define MICROPY_COMP_CONST_FOLDING (1) diff --git a/mpy-cross/mpy_cross/__init__.py b/mpy-cross/mpy_cross/__init__.py index 5020033e8c808..91cd6f99335b3 100644 --- a/mpy-cross/mpy_cross/__init__.py +++ b/mpy-cross/mpy_cross/__init__.py @@ -43,6 +43,7 @@ "NATIVE_ARCH_ARMV7EMDP": "armv7emdp", "NATIVE_ARCH_XTENSA": "xtensa", "NATIVE_ARCH_XTENSAWIN": "xtensawin", + "NATIVE_ARCH_RV32IMC": "rv32imc", } globals().update(NATIVE_ARCHS) diff --git a/py/persistentcode.h b/py/persistentcode.h index d2b310f241f8d..3f3c67764d1c8 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -71,6 +71,8 @@ #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSA) #elif MICROPY_EMIT_XTENSAWIN #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSAWIN) +#elif MICROPY_EMIT_RV32 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_RV32IMC) #else #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_NONE) #endif @@ -95,6 +97,7 @@ enum { MP_NATIVE_ARCH_ARMV7EMDP, MP_NATIVE_ARCH_XTENSA, MP_NATIVE_ARCH_XTENSAWIN, + MP_NATIVE_ARCH_RV32IMC, }; enum { diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 86c4ba7579358..3b06572c3a308 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -113,6 +113,7 @@ class Config: MP_NATIVE_ARCH_ARMV7EMDP = 8 MP_NATIVE_ARCH_XTENSA = 9 MP_NATIVE_ARCH_XTENSAWIN = 10 +MP_NATIVE_ARCH_RV32IMC = 11 MP_PERSISTENT_OBJ_FUN_TABLE = 0 MP_PERSISTENT_OBJ_NONE = 1 @@ -1094,8 +1095,10 @@ def __init__( ): # ARMV6 or Xtensa -- four byte align. self.fun_data_attributes += " __attribute__ ((aligned (4)))" - elif MP_NATIVE_ARCH_ARMV6M <= config.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP: - # ARMVxxM -- two byte align. + elif ( + MP_NATIVE_ARCH_ARMV6M <= config.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP + ) or config.native_arch == MP_NATIVE_ARCH_RV32IMC: + # ARMVxxM or RV32IMC -- two byte align. self.fun_data_attributes += " __attribute__ ((aligned (2)))" def disassemble(self): From 3dd1130f6d85e9d378609fb244332d52a6caa059 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 14 Jun 2024 19:43:47 +0200 Subject: [PATCH 0054/1300] py/emitnative: Emit better load/store sequences for RISC-V RV32IMC. Selected load/store code sequences have been optimised for RV32IMC when the chance to use fewer and smaller opcodes was possible. Signed-off-by: Alessandro Gatti --- py/emitnative.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/py/emitnative.c b/py/emitnative.c index 7557e4ba48fac..3294fd9ea3a96 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -178,6 +178,12 @@ static const uint8_t reg_local_table[MAX_REGS_FOR_LOCAL_VARS] = {REG_LOCAL_1, RE *emit->error_slot = mp_obj_new_exception_msg_varg(&mp_type_ViperTypeError, __VA_ARGS__); \ } while (0) +#if N_RV32 +#define FIT_SIGNED(value, bits) \ + ((((value) & ~((1U << ((bits) - 1)) - 1)) == 0) || \ + (((value) & ~((1U << ((bits) - 1)) - 1)) == ~((1U << ((bits) - 1)) - 1))) +#endif + typedef enum { STACK_VALUE, STACK_REG, @@ -1519,6 +1525,11 @@ static void emit_native_load_subscr(emit_t *emit) { asm_thumb_ldrb_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); break; } + #elif N_RV32 + if (FIT_SIGNED(index_value, 12)) { + asm_rv32_opcode_lbu(emit->as, REG_RET, reg_base, index_value); + break; + } #endif need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value); @@ -1537,6 +1548,11 @@ static void emit_native_load_subscr(emit_t *emit) { asm_thumb_ldrh_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); break; } + #elif N_RV32 + if (FIT_SIGNED(index_value, 11)) { + asm_rv32_opcode_lhu(emit->as, REG_RET, reg_base, index_value << 1); + break; + } #endif need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1); @@ -1555,6 +1571,11 @@ static void emit_native_load_subscr(emit_t *emit) { asm_thumb_ldr_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); break; } + #elif N_RV32 + if (FIT_SIGNED(index_value, 10)) { + asm_rv32_opcode_lw(emit->as, REG_RET, reg_base, index_value << 2); + break; + } #endif need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2); @@ -1596,6 +1617,12 @@ static void emit_native_load_subscr(emit_t *emit) { } case VTYPE_PTR32: { // pointer to word-size memory + #if N_RV32 + asm_rv32_opcode_slli(emit->as, REG_TEMP2, reg_index, 2); + asm_rv32_opcode_cadd(emit->as, REG_ARG_1, REG_TEMP2); + asm_rv32_opcode_lw(emit->as, REG_RET, REG_ARG_1, 0); + break; + #endif ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base @@ -1751,6 +1778,11 @@ static void emit_native_store_subscr(emit_t *emit) { asm_thumb_strb_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); break; } + #elif N_RV32 + if (FIT_SIGNED(index_value, 12)) { + asm_rv32_opcode_sb(emit->as, reg_value, reg_base, index_value); + break; + } #endif ASM_MOV_REG_IMM(emit->as, reg_index, index_value); #if N_ARM @@ -1772,6 +1804,11 @@ static void emit_native_store_subscr(emit_t *emit) { asm_thumb_strh_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); break; } + #elif N_RV32 + if (FIT_SIGNED(index_value, 11)) { + asm_rv32_opcode_sh(emit->as, reg_value, reg_base, index_value << 1); + break; + } #endif ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 2*index to base @@ -1789,8 +1826,12 @@ static void emit_native_store_subscr(emit_t *emit) { asm_thumb_str_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); break; } - #endif - #if N_ARM + #elif N_RV32 + if (FIT_SIGNED(index_value, 10)) { + asm_rv32_opcode_sw(emit->as, reg_value, reg_base, index_value << 2); + break; + } + #elif N_ARM ASM_MOV_REG_IMM(emit->as, reg_index, index_value); asm_arm_str_reg_reg_reg(emit->as, reg_value, reg_base, reg_index); return; @@ -1855,6 +1896,11 @@ static void emit_native_store_subscr(emit_t *emit) { #if N_ARM asm_arm_str_reg_reg_reg(emit->as, reg_value, REG_ARG_1, reg_index); break; + #elif N_RV32 + asm_rv32_opcode_slli(emit->as, REG_TEMP2, reg_index, 2); + asm_rv32_opcode_cadd(emit->as, REG_ARG_1, REG_TEMP2); + asm_rv32_opcode_sw(emit->as, reg_value, REG_ARG_1, 0); + break; #endif ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base From c1882e48669bfd637efd086e6a16a5cdbd47fdea Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 17 Jun 2024 22:07:26 +0200 Subject: [PATCH 0055/1300] qemu-riscv: Enable native code generation by default. This turns on the native RV32IMC code generator for the QEMU-based RISC-V port, and removes tests that relies on native code generation from the exclusion list (ie enables these tests). Signed-off-by: Alessandro Gatti --- ports/qemu-riscv/mpconfigport.h | 1 + ports/qemu-riscv/tests_profile.txt | 52 +----------------------------- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/ports/qemu-riscv/mpconfigport.h b/ports/qemu-riscv/mpconfigport.h index 8f50828e0f1a4..38bc70e986adb 100644 --- a/ports/qemu-riscv/mpconfigport.h +++ b/ports/qemu-riscv/mpconfigport.h @@ -30,6 +30,7 @@ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) +#define MICROPY_EMIT_RV32 (1) #define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) #define MICROPY_MEM_STATS (1) #define MICROPY_ENABLE_GC (1) diff --git a/ports/qemu-riscv/tests_profile.txt b/ports/qemu-riscv/tests_profile.txt index 2f3bb7300ddf4..1079ca18124dc 100644 --- a/ports/qemu-riscv/tests_profile.txt +++ b/ports/qemu-riscv/tests_profile.txt @@ -1,53 +1,3 @@ # Port-specific tests exclusion list. -exclude_tests = exclude_tests.union( - ( - # Native code generation is not yet supported. - "micropython/native_closure.py", - "micropython/native_const.py", - "micropython/native_const_intbig.py", - "micropython/native_for.py", - "micropython/native_fun_attrs.py", - "micropython/native_gen.py", - "micropython/native_misc.py", - "micropython/native_try.py", - "micropython/native_try_deep.py", - "micropython/native_while.py", - "micropython/native_with.py", - - # Viper code generator is not yet supported. - "micropython/viper_addr.py", - "micropython/viper_args.py", - "micropython/viper_binop_arith.py", - "micropython/viper_binop_arith_uint.py", - "micropython/viper_binop_bitwise_uint.py", - "micropython/viper_binop_comp.py", - "micropython/viper_binop_comp_imm.py", - "micropython/viper_binop_comp_uint.py", - "micropython/viper_binop_divmod.py", - "micropython/viper_binop_multi_comp.py", - "micropython/viper_cond.py", - "micropython/viper_const.py", - "micropython/viper_const_intbig.py", - "micropython/viper_error.py", - "micropython/viper_globals.py", - "micropython/viper_import.py", - "micropython/viper_misc.py", - "micropython/viper_misc2.py", - "micropython/viper_misc3.py", - "micropython/viper_misc_intbig.py", - "micropython/viper_ptr16_load.py", - "micropython/viper_ptr16_store.py", - "micropython/viper_ptr32_load.py", - "micropython/viper_ptr32_store.py", - "micropython/viper_ptr8_load.py", - "micropython/viper_ptr8_store.py", - "micropython/viper_storeattr.py", - "micropython/viper_subscr.py", - "micropython/viper_subscr_multi.py", - "micropython/viper_try.py", - "micropython/viper_types.py", - "micropython/viper_unop.py", - "micropython/viper_with.py", - ) -) +exclude_tests = exclude_tests.union(()) From e2ae03e97963c2d338752bc4cbe8bd87981014fb Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 29 Feb 2024 11:13:31 +1100 Subject: [PATCH 0056/1300] py/emitnative: Add more DEBUG_printf statements. Signed-off-by: Damien George --- py/emitnative.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/py/emitnative.c b/py/emitnative.c index 3294fd9ea3a96..99e5a24c1b465 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -1459,6 +1459,7 @@ static void emit_native_load_attr(emit_t *emit, qstr qst) { } static void emit_native_load_method(emit_t *emit, qstr qst, bool is_super) { + DEBUG_printf("load_method(%s, %d)\n", qstr_str(qst), is_super); if (is_super) { emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_2, 3); // arg2 = dest ptr emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_2, 2); // arg2 = dest ptr @@ -2149,6 +2150,7 @@ static void emit_native_setup_with(emit_t *emit, mp_uint_t label) { } static void emit_native_setup_block(emit_t *emit, mp_uint_t label, int kind) { + DEBUG_printf("setup_block(%d, %d)\n", (int)label, kind); if (kind == MP_EMIT_SETUP_BLOCK_WITH) { emit_native_setup_with(emit, label); } else { @@ -2233,6 +2235,8 @@ static void emit_native_end_finally(emit_t *emit) { // if exc == None: pass // else: raise exc // the check if exc is None is done in the MP_F_NATIVE_RAISE stub + DEBUG_printf("end_finally\n"); + emit_native_pre(emit); ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit)); emit_call(emit, MP_F_NATIVE_RAISE); @@ -2257,6 +2261,8 @@ static void emit_native_get_iter(emit_t *emit, bool use_stack) { // perhaps the difficult one, as we want to rewrite for loops using native code // in cases where we iterate over a Python object, can we use normal runtime calls? + DEBUG_printf("get_iter(%d)\n", use_stack); + vtype_kind_t vtype; emit_pre_pop_reg(emit, &vtype, REG_ARG_1); assert(vtype == VTYPE_PYOBJ); @@ -2838,6 +2844,7 @@ static void emit_native_call_function(emit_t *emit, mp_uint_t n_positional, mp_u } static void emit_native_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { + DEBUG_printf("call_method(%d, %d, %d)\n", n_positional, n_keyword, star_flags); if (star_flags) { emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 3); // pointer to args emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW_VAR, 1, REG_ARG_1, n_positional | (n_keyword << 8), REG_ARG_2); @@ -2904,6 +2911,7 @@ static void emit_native_return_value(emit_t *emit) { } static void emit_native_raise_varargs(emit_t *emit, mp_uint_t n_args) { + DEBUG_printf("raise_varargs(%d)\n", n_args); (void)n_args; assert(n_args == 1); vtype_kind_t vtype_exc; @@ -2919,6 +2927,8 @@ static void emit_native_raise_varargs(emit_t *emit, mp_uint_t n_args) { static void emit_native_yield(emit_t *emit, int kind) { // Note: 1 (yield) or 3 (yield from) labels are reserved for this function, starting at *emit->label_slot + DEBUG_printf("yield(%d)\n", kind); + if (emit->do_viper_types) { mp_raise_NotImplementedError(MP_ERROR_TEXT("native yield")); } From 9dbc787ce8dda9df39eb68c03de144155b2253f0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 7 Mar 2024 11:38:27 +1100 Subject: [PATCH 0057/1300] py/emitndebug: Add native debug emitter. This emitter prints out pseudo-machine instructions, instead of the usual output of the native emitter. It can be enabled on any port via `MICROPY_EMIT_NATIVE_DEBUG` (make sure other native emitters are disabled) but the easiest way to use it is with mpy-cross: $ mpy-cross -march=debug file.py Signed-off-by: Damien George --- mpy-cross/main.c | 7 +- mpy-cross/mpconfigport.h | 4 + py/asmbase.c | 6 + py/compile.c | 3 + py/emit.h | 3 + py/emitnative.c | 6 +- py/emitndebug.c | 285 +++++++++++++++++++++++++++++++++++++++ py/mpconfig.h | 2 +- py/persistentcode.h | 1 + py/py.cmake | 1 + py/py.mk | 1 + 11 files changed, 315 insertions(+), 4 deletions(-) create mode 100644 py/emitndebug.c diff --git a/mpy-cross/main.c b/mpy-cross/main.c index a9066215d0381..72b6e10fce9d2 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -54,7 +54,7 @@ static void stdout_print_strn(void *env, const char *str, size_t len) { (void)dummy; } -static const mp_print_t mp_stdout_print = {NULL, stdout_print_strn}; +const mp_print_t mp_stdout_print = {NULL, stdout_print_strn}; static void stderr_print_strn(void *env, const char *str, size_t len) { (void)env; @@ -130,7 +130,7 @@ static int usage(char **argv) { "Target specific options:\n" "-msmall-int-bits=number : set the maximum bits used to encode a small-int\n" "-march= : set architecture for native emitter;\n" - " x86, x64, armv6, armv6m, armv7m, armv7em, armv7emsp, armv7emdp, xtensa, xtensawin, rv32imc\n" + " x86, x64, armv6, armv6m, armv7m, armv7em, armv7emsp, armv7emdp, xtensa, xtensawin, rv32imc, debug\n" "\n" "Implementation specific options:\n", argv[0] ); @@ -316,6 +316,9 @@ MP_NOINLINE int main_(int argc, char **argv) { } else if (strcmp(arch, "rv32imc") == 0) { mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_RV32IMC; mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_RV32I; + } else if (strcmp(arch, "debug") == 0) { + mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_DEBUG; + mp_dynamic_compiler.nlr_buf_num_regs = 0; } else if (strcmp(arch, "host") == 0) { #if defined(__i386__) || defined(_M_IX86) mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X86; diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index 009aed33393a3..8723472eb710c 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -47,6 +47,8 @@ #define MICROPY_EMIT_INLINE_XTENSA (1) #define MICROPY_EMIT_XTENSAWIN (1) #define MICROPY_EMIT_RV32 (1) +#define MICROPY_EMIT_NATIVE_DEBUG (1) +#define MICROPY_EMIT_NATIVE_DEBUG_PRINTER (&mp_stdout_print) #define MICROPY_DYNAMIC_COMPILER (1) #define MICROPY_COMP_CONST_FOLDING (1) @@ -164,3 +166,5 @@ typedef int ssize_t; typedef mp_off_t off_t; #endif + +extern const struct _mp_print_t mp_stdout_print; diff --git a/py/asmbase.c b/py/asmbase.c index cf64e3f3d054f..3fce543a7f485 100644 --- a/py/asmbase.c +++ b/py/asmbase.c @@ -30,6 +30,7 @@ #include "py/obj.h" #include "py/misc.h" #include "py/asmbase.h" +#include "py/persistentcode.h" #if MICROPY_EMIT_MACHINE_CODE @@ -91,6 +92,11 @@ void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label) { } else { // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT assert(as->label_offsets[label] == as->code_offset); + #if MICROPY_DYNAMIC_COMPILER && MICROPY_EMIT_NATIVE_DEBUG + if (mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_DEBUG) { + mp_printf(MICROPY_EMIT_NATIVE_DEBUG_PRINTER, "label(label_%u)\n", (unsigned int)label); + } + #endif } } diff --git a/py/compile.c b/py/compile.c index 9b012f8787004..3429e384c0050 100644 --- a/py/compile.c +++ b/py/compile.c @@ -103,6 +103,7 @@ static const emit_method_table_t *emit_native_table[] = { &emit_native_xtensa_method_table, &emit_native_xtensawin_method_table, &emit_native_rv32_method_table, + &emit_native_debug_method_table, }; #elif MICROPY_EMIT_NATIVE @@ -121,6 +122,8 @@ static const emit_method_table_t *emit_native_table[] = { #define NATIVE_EMITTER(f) emit_native_xtensawin_##f #elif MICROPY_EMIT_RV32 #define NATIVE_EMITTER(f) emit_native_rv32_##f +#elif MICROPY_EMIT_NATIVE_DEBUG +#define NATIVE_EMITTER(f) emit_native_debug_##f #else #error "unknown native emitter" #endif diff --git a/py/emit.h b/py/emit.h index 0c5019f07ac3f..9aad6ebafb89b 100644 --- a/py/emit.h +++ b/py/emit.h @@ -202,6 +202,7 @@ extern const emit_method_table_t emit_native_arm_method_table; extern const emit_method_table_t emit_native_xtensa_method_table; extern const emit_method_table_t emit_native_xtensawin_method_table; extern const emit_method_table_t emit_native_rv32_method_table; +extern const emit_method_table_t emit_native_debug_method_table; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops; @@ -215,6 +216,7 @@ emit_t *emit_native_arm_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, emit_t *emit_native_xtensa_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); emit_t *emit_native_xtensawin_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); emit_t *emit_native_rv32_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_debug_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); void emit_bc_set_max_num_labels(emit_t *emit, mp_uint_t max_num_labels); @@ -226,6 +228,7 @@ void emit_native_arm_free(emit_t *emit); void emit_native_xtensa_free(emit_t *emit); void emit_native_xtensawin_free(emit_t *emit); void emit_native_rv32_free(emit_t *emit); +void emit_native_debug_free(emit_t *emit); void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope); bool mp_emit_bc_end_pass(emit_t *emit); diff --git a/py/emitnative.c b/py/emitnative.c index 99e5a24c1b465..9d3f698b25abb 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -59,7 +59,7 @@ #endif // wrapper around everything in this file -#if N_X64 || N_X86 || N_THUMB || N_ARM || N_XTENSA || N_XTENSAWIN || N_RV32 +#if N_X64 || N_X86 || N_THUMB || N_ARM || N_XTENSA || N_XTENSAWIN || N_RV32 || N_DEBUG // C stack layout for native functions: // 0: nlr_buf_t [optional] @@ -348,6 +348,8 @@ static void emit_native_mov_reg_state_addr(emit_t *emit, int reg_dest, int local static void emit_native_mov_reg_qstr(emit_t *emit, int arg_reg, qstr qst) { #if MICROPY_PERSISTENT_CODE_SAVE ASM_LOAD16_REG_REG_OFFSET(emit->as, arg_reg, REG_QSTR_TABLE, mp_emit_common_use_qstr(emit->emit_common, qst)); + #elif defined(ASM_MOV_REG_QSTR) + ASM_MOV_REG_QSTR(emit->as, arg_reg, qst); #else ASM_MOV_REG_IMM(emit->as, arg_reg, qst); #endif @@ -2604,6 +2606,8 @@ static void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { default: break; } + #elif N_DEBUG + asm_debug_setcc_reg_reg_reg(emit->as, op_idx, REG_RET, REG_ARG_2, reg_rhs); #else #error not implemented #endif diff --git a/py/emitndebug.c b/py/emitndebug.c new file mode 100644 index 0000000000000..bd896a75c8ddf --- /dev/null +++ b/py/emitndebug.c @@ -0,0 +1,285 @@ +// native-debug specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_NATIVE_DEBUG + +#include "py/asmbase.h" +#include "py/nativeglue.h" + +#define asm_debug_printf(as, fmt, ...) \ + do { \ + if (as->base.pass == MP_ASM_PASS_EMIT) { \ + if (fmt[0] != 'E') { \ + mp_printf(MICROPY_EMIT_NATIVE_DEBUG_PRINTER, " "); \ + } \ + if (as->base.suppress) { \ + mp_printf(MICROPY_EMIT_NATIVE_DEBUG_PRINTER, "dead_code "); \ + } \ + mp_printf(MICROPY_EMIT_NATIVE_DEBUG_PRINTER, fmt, __VA_ARGS__); \ + } \ + } while (0) + +enum { + ASM_DEBUG_REG_R00, + ASM_DEBUG_REG_R01, + ASM_DEBUG_REG_R02, + ASM_DEBUG_REG_R03, + ASM_DEBUG_REG_R04, + ASM_DEBUG_REG_R05, + ASM_DEBUG_REG_R06, + ASM_DEBUG_REG_R07, + ASM_DEBUG_REG_R08, + ASM_DEBUG_REG_R09, + ASM_DEBUG_REG_R10, + ASM_DEBUG_REG_R11, +}; + +typedef struct _asm_debug_t { + mp_asm_base_t base; +} asm_debug_t; + +static const char *const reg_name_table[] = { + "r_ret", + "r_arg1", + "r_arg2", + "r_arg3", + "r_arg4", + "r_temp0", + "r_temp1", + "r_temp2", + "r_local1", + "r_local2", + "r_local3", + "r_fun_table", +}; + +static const char *const fun_name_table[MP_F_NUMBER_OF] = { + [MP_F_CONVERT_OBJ_TO_NATIVE] = "convert_obj_to_native", + [MP_F_CONVERT_NATIVE_TO_OBJ] = "convert_native_to_obj", + [MP_F_NATIVE_SWAP_GLOBALS] = "native_swap_globals", + [MP_F_LOAD_NAME] = "load_name", + [MP_F_LOAD_GLOBAL] = "load_global", + [MP_F_LOAD_BUILD_CLASS] = "load_build_class", + [MP_F_LOAD_ATTR] = "load_attr", + [MP_F_LOAD_METHOD] = "load_method", + [MP_F_LOAD_SUPER_METHOD] = "load_super_method", + [MP_F_STORE_NAME] = "store_name", + [MP_F_STORE_GLOBAL] = "store_global", + [MP_F_STORE_ATTR] = "store_attr", + [MP_F_OBJ_SUBSCR] = "obj_subscr", + [MP_F_OBJ_IS_TRUE] = "obj_is_true", + [MP_F_UNARY_OP] = "unary_op", + [MP_F_BINARY_OP] = "binary_op", + [MP_F_BUILD_TUPLE] = "build_tuple", + [MP_F_BUILD_LIST] = "build_list", + [MP_F_BUILD_MAP] = "build_map", + [MP_F_BUILD_SET] = "build_set", + [MP_F_STORE_SET] = "store_set", + [MP_F_LIST_APPEND] = "list_append", + [MP_F_STORE_MAP] = "store_map", + [MP_F_MAKE_FUNCTION_FROM_PROTO_FUN] = "make_function_from_proto_fun", + [MP_F_NATIVE_CALL_FUNCTION_N_KW] = "native_call_function_n_kw", + [MP_F_CALL_METHOD_N_KW] = "call_method_n_kw", + [MP_F_CALL_METHOD_N_KW_VAR] = "call_method_n_kw_var", + [MP_F_NATIVE_GETITER] = "native_getiter", + [MP_F_NATIVE_ITERNEXT] = "native_iternext", + [MP_F_NLR_PUSH] = "nlr_push", + [MP_F_NLR_POP] = "nlr_pop", + [MP_F_NATIVE_RAISE] = "native_raise", + [MP_F_IMPORT_NAME] = "import_name", + [MP_F_IMPORT_FROM] = "import_from", + [MP_F_IMPORT_ALL] = "import_all", + [MP_F_NEW_SLICE] = "new_slice", + [MP_F_UNPACK_SEQUENCE] = "unpack_sequence", + [MP_F_UNPACK_EX] = "unpack_ex", + [MP_F_DELETE_NAME] = "delete_name", + [MP_F_DELETE_GLOBAL] = "delete_global", + [MP_F_NEW_CLOSURE] = "new_closure", + [MP_F_ARG_CHECK_NUM_SIG] = "arg_check_num_sig", + [MP_F_SETUP_CODE_STATE] = "setup_code_state", + [MP_F_SMALL_INT_FLOOR_DIVIDE] = "small_int_floor_divide", + [MP_F_SMALL_INT_MODULO] = "small_int_modulo", + [MP_F_NATIVE_YIELD_FROM] = "native_yield_from", + [MP_F_SETJMP] = "setjmp", +}; + +static void asm_debug_end_pass(asm_debug_t *as) { + (void)as; +} + +static void asm_debug_entry(asm_debug_t *as, int num_locals) { + asm_debug_printf(as, "ENTRY(num_locals=%d)\n", num_locals); +} + +static void asm_debug_exit(asm_debug_t *as) { + asm_debug_printf(as, "EXIT(%u)\n", 0); +} + +static void asm_debug_fun(asm_debug_t *as, const char *op, int fun_idx) { + asm_debug_printf(as, "%s(%s)\n", op, fun_name_table[fun_idx]); +} + +static void asm_debug_reg(asm_debug_t *as, const char *op, int reg) { + asm_debug_printf(as, "%s(%s)\n", op, reg_name_table[reg]); +} + +static void asm_debug_label(asm_debug_t *as, const char *op, unsigned int label) { + asm_debug_printf(as, "%s(label_%u)\n", op, label); +} + +static void asm_debug_reg_imm(asm_debug_t *as, const char *op, int reg, int imm) { + asm_debug_printf(as, "%s(%s, %d=0x%x)\n", op, reg_name_table[reg], imm, imm); +} + +#if !MICROPY_PERSISTENT_CODE_SAVE +static void asm_debug_reg_qstr(asm_debug_t *as, const char *op, int reg, int qst) { + asm_debug_printf(as, "%s(%s, %s)\n", op, reg_name_table[reg], qstr_str(qst)); +} +#endif + +static void asm_debug_reg_reg(asm_debug_t *as, const char *op, int reg1, int reg2) { + asm_debug_printf(as, "%s(%s, %s)\n", op, reg_name_table[reg1], reg_name_table[reg2]); +} + +static void asm_debug_reg_local(asm_debug_t *as, const char *op, int reg, unsigned int local) { + asm_debug_printf(as, "%s(%s, local_%u)\n", op, reg_name_table[reg], local); +} + +static void asm_debug_reg_label(asm_debug_t *as, const char *op, int reg, unsigned int label) { + asm_debug_printf(as, "%s(%s, label_%u)\n", op, reg_name_table[reg], label); +} + +static void asm_debug_local_reg(asm_debug_t *as, const char *op, int local, int reg) { + asm_debug_printf(as, "%s(local_%d, %s)\n", op, local, reg_name_table[reg]); +} + +static void asm_debug_reg_label_bool(asm_debug_t *as, const char *op, int reg, unsigned int label, bool b) { + asm_debug_printf(as, "%s(%s, label_%u, %s)\n", op, reg_name_table[reg], label, b ? "true" : "false"); +} + +static void asm_debug_reg_reg_offset(asm_debug_t *as, const char *op, int reg1, int reg2, int offset) { + asm_debug_printf(as, "%s(%s, %s, %d)\n", op, reg_name_table[reg1], reg_name_table[reg2], offset); +} + +static void asm_debug_reg_reg_label(asm_debug_t *as, const char *op, int reg1, int reg2, unsigned int label) { + asm_debug_printf(as, "%s(%s, %s, label_%u)\n", op, reg_name_table[reg1], reg_name_table[reg2], label); +} + +static void asm_debug_setcc_reg_reg_reg(asm_debug_t *as, int op, int reg1, int reg2, int reg3) { + asm_debug_printf(as, "setcc(%d, %s, %s, %s)\n", op, reg_name_table[reg1], reg_name_table[reg2], reg_name_table[reg3]); +} + +// The following macros provide a (mostly) arch-independent API to +// generate native code, and are used by the native emitter. + +#define ASM_WORD_SIZE (8) + +#define REG_RET ASM_DEBUG_REG_R00 +#define REG_ARG_1 ASM_DEBUG_REG_R01 +#define REG_ARG_2 ASM_DEBUG_REG_R02 +#define REG_ARG_3 ASM_DEBUG_REG_R03 +#define REG_ARG_4 ASM_DEBUG_REG_R04 + +#define REG_TEMP0 ASM_DEBUG_REG_R05 +#define REG_TEMP1 ASM_DEBUG_REG_R06 +#define REG_TEMP2 ASM_DEBUG_REG_R07 + +#define REG_LOCAL_1 ASM_DEBUG_REG_R08 +#define REG_LOCAL_2 ASM_DEBUG_REG_R09 +#define REG_LOCAL_3 ASM_DEBUG_REG_R10 +#define REG_LOCAL_NUM (3) + +// Holds a pointer to mp_fun_table +#define REG_FUN_TABLE ASM_DEBUG_REG_R11 + +#define ASM_T asm_debug_t +#define ASM_END_PASS asm_debug_end_pass +#define ASM_ENTRY(as, num_locals) \ + asm_debug_entry(as, num_locals) +#define ASM_EXIT(as) \ + asm_debug_exit(as) + +#define ASM_JUMP(as, label) \ + asm_debug_label(as, "jump", label) +#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ + asm_debug_reg_label_bool(as, "jump_if_reg_zero", reg, label, bool_test) +#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \ + asm_debug_reg_label_bool(as, "jump_if_reg_nonzero", reg, label, bool_test) +#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ + asm_debug_reg_reg_label(as, "jump_if_reg_eq", reg1, reg2, label) +#define ASM_JUMP_REG(as, reg) \ + asm_debug_reg(as, "jump_reg", reg) +#define ASM_CALL_IND(as, idx) \ + asm_debug_fun(as, "call_ind", idx) + +#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) \ + asm_debug_local_reg(as, "mov_local_reg", local_num, reg_src) +#define ASM_MOV_REG_IMM(as, reg_dest, imm) \ + asm_debug_reg_imm(as, "mov_reg_imm", reg_dest, imm) +#define ASM_MOV_REG_QSTR(as, reg_dest, qst) \ + asm_debug_reg_qstr(as, "mov_reg_qstr", reg_dest, qst) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) \ + asm_debug_reg_local(as, "mov_reg_local", reg_dest, local_num) +#define ASM_MOV_REG_REG(as, reg_dest, reg_src) \ + asm_debug_reg_reg(as, "mov_reg_reg", reg_dest, reg_src) +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) \ + asm_debug_reg_local(as, "mov_reg_local_addr", reg_dest, local_num) +#define ASM_MOV_REG_PCREL(as, reg_dest, label) \ + asm_debug_reg_label(as, "mov_reg_pcrel", reg_dest, label) + +#define ASM_NOT_REG(as, reg_dest) \ + asm_debug_reg(as, "not", reg_dest) +#define ASM_NEG_REG(as, reg_dest) \ + asm_debug_reg(as, "neg", reg_dest) +#define ASM_LSL_REG_REG(as, reg_dest, reg_src) \ + asm_debug_reg_reg(as, "lsl", reg_dest, reg_src) +#define ASM_LSR_REG_REG(as, reg_dest, reg_src) \ + asm_debug_reg_reg(as, "lsr", reg_dest, reg_src) +#define ASM_ASR_REG_REG(as, reg_dest, reg_src) \ + asm_debug_reg_reg(as, "asr", reg_dest, reg_src) +#define ASM_OR_REG_REG(as, reg_dest, reg_src) \ + asm_debug_reg_reg(as, "or", reg_dest, reg_src) +#define ASM_XOR_REG_REG(as, reg_dest, reg_src) \ + asm_debug_reg_reg(as, "xor", reg_dest, reg_src) +#define ASM_AND_REG_REG(as, reg_dest, reg_src) \ + asm_debug_reg_reg(as, "and", reg_dest, reg_src) +#define ASM_ADD_REG_REG(as, reg_dest, reg_src) \ + asm_debug_reg_reg(as, "add", reg_dest, reg_src) +#define ASM_SUB_REG_REG(as, reg_dest, reg_src) \ + asm_debug_reg_reg(as, "sub", reg_dest, reg_src) +#define ASM_MUL_REG_REG(as, reg_dest, reg_src) \ + asm_debug_reg_reg(as, "mul", reg_dest, reg_src) + +#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) \ + asm_debug_reg_reg(as, "load", reg_dest, reg_base) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) \ + asm_debug_reg_reg_offset(as, "load", reg_dest, reg_base, word_offset) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) \ + asm_debug_reg_reg(as, "load8", reg_dest, reg_base) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) \ + asm_debug_reg_reg(as, "load16", reg_dest, reg_base) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) \ + asm_debug_reg_reg_offset(as, "load16", reg_dest, reg_base, uint16_offset) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) \ + asm_debug_reg_reg(as, "load32", reg_dest, reg_base) + +#define ASM_STORE_REG_REG(as, reg_src, reg_base) \ + asm_debug_reg_reg(as, "store", reg_src, reg_base) +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) \ + asm_debug_reg_reg_offset(as, "store", reg_src, reg_base, word_offset) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) \ + asm_debug_reg_reg(as, "store8", reg_src, reg_base) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) \ + asm_debug_reg_reg(as, "store16", reg_src, reg_base) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) \ + asm_debug_reg_reg(as, "store32", reg_src, reg_base) + +// Word indices of REG_LOCAL_x in nlr_buf_t +#define NLR_BUF_IDX_LOCAL_1 (5) // rbx + +#define N_DEBUG (1) +#define EXPORT_FUN(name) emit_native_debug_##name +#include "py/emitnative.c" + +#endif diff --git a/py/mpconfig.h b/py/mpconfig.h index efb30ac04dada..bc6bf75fe57c5 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -412,7 +412,7 @@ #endif // Convenience definition for whether any native emitter is enabled -#define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN || MICROPY_EMIT_RV32) +#define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN || MICROPY_EMIT_RV32 || MICROPY_EMIT_NATIVE_DEBUG) // Some architectures cannot read byte-wise from executable memory. In this case // the prelude for a native function (which usually sits after the machine code) diff --git a/py/persistentcode.h b/py/persistentcode.h index 3f3c67764d1c8..f0b7f70f7d7fa 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -98,6 +98,7 @@ enum { MP_NATIVE_ARCH_XTENSA, MP_NATIVE_ARCH_XTENSAWIN, MP_NATIVE_ARCH_RV32IMC, + MP_NATIVE_ARCH_DEBUG, // this entry should always be last }; enum { diff --git a/py/py.cmake b/py/py.cmake index f47661c409a14..ccd0577c386d6 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -26,6 +26,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/emitinlinethumb.c ${MICROPY_PY_DIR}/emitinlinextensa.c ${MICROPY_PY_DIR}/emitnarm.c + ${MICROPY_PY_DIR}/emitndebug.c ${MICROPY_PY_DIR}/emitnrv32.c ${MICROPY_PY_DIR}/emitnthumb.c ${MICROPY_PY_DIR}/emitnx64.c diff --git a/py/py.mk b/py/py.mk index 0b429f9efe7ac..cd9392edbba4c 100644 --- a/py/py.mk +++ b/py/py.mk @@ -116,6 +116,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ emitnxtensawin.o \ asmrv32.o \ emitnrv32.o \ + emitndebug.o \ formatfloat.o \ parsenumbase.o \ parsenum.o \ From a19214d897f7891d177dc22bb8fc555f7e6b65bb Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Apr 2024 09:29:13 +1000 Subject: [PATCH 0058/1300] py/emitnative: Place thrown value in dedicated local variable. A value thrown/injected into a native generator needs to be stored in a dedicated variable outside `nlr_buf_t`, following the `inject_exc` variable in `py/vm.c`. Signed-off-by: Damien George --- py/emitnative.c | 26 ++++++++++++++++++-------- tests/run-tests.py | 3 --- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/py/emitnative.c b/py/emitnative.c index 9d3f698b25abb..db31f51e652ca 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -129,6 +129,7 @@ // Whether a slot is needed to store LOCAL_IDX_EXC_HANDLER_UNWIND #define NEED_EXC_HANDLER_UNWIND(emit) ((emit)->scope->exc_stack_size > 0) +#define NEED_THROW_VAL(emit) ((emit)->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) // Whether registers can be used to store locals (only true if there are no // exception handlers, because otherwise an nlr_jump will restore registers to @@ -139,6 +140,7 @@ #define LOCAL_IDX_EXC_VAL(emit) (NLR_BUF_IDX_RET_VAL) #define LOCAL_IDX_EXC_HANDLER_PC(emit) (NLR_BUF_IDX_LOCAL_1) #define LOCAL_IDX_EXC_HANDLER_UNWIND(emit) (SIZEOF_NLR_BUF + 1) // this needs a dedicated variable outside nlr_buf_t +#define LOCAL_IDX_THROW_VAL(emit) (SIZEOF_NLR_BUF + 2) // needs a dedicated variable outside nlr_buf_t, following inject_exc in py/vm.c #define LOCAL_IDX_RET_VAL(emit) (SIZEOF_NLR_BUF) // needed when NEED_GLOBAL_EXC_HANDLER is true #define LOCAL_IDX_FUN_OBJ(emit) ((emit)->code_state_start + OFFSETOF_CODE_STATE_FUN_BC) #define LOCAL_IDX_OLD_GLOBALS(emit) ((emit)->code_state_start + OFFSETOF_CODE_STATE_IP) @@ -426,7 +428,9 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop if (NEED_GLOBAL_EXC_HANDLER(emit)) { emit->code_state_start = SIZEOF_NLR_BUF; // for nlr_buf_t emit->code_state_start += 1; // for return_value - if (NEED_EXC_HANDLER_UNWIND(emit)) { + if (NEED_THROW_VAL(emit)) { + emit->code_state_start += 2; + } else if (NEED_EXC_HANDLER_UNWIND(emit)) { emit->code_state_start += 1; } } @@ -545,11 +549,11 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop ASM_MOV_REG_REG(emit->as, REG_GENERATOR_STATE, REG_PARENT_ARG_1); #endif - // Put throw value into LOCAL_IDX_EXC_VAL slot, for yield/yield-from + // Put throw value into LOCAL_IDX_THROW_VAL slot, for yield/yield-from #if N_X86 asm_x86_mov_arg_to_r32(emit->as, 1, REG_PARENT_ARG_2); #endif - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_PARENT_ARG_2); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_PARENT_ARG_2); // Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_GENERATOR_STATE, LOCAL_IDX_FUN_OBJ(emit)); @@ -1252,8 +1256,10 @@ static void emit_native_global_exc_entry(emit_t *emit) { // This is the first entry of the generator - // Check LOCAL_IDX_EXC_VAL for any injected value - ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit)); + // Check LOCAL_IDX_THROW_VAL for any injected value + ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_THROW_VAL(emit)); + ASM_MOV_REG_IMM(emit->as, REG_ARG_2, (mp_uint_t)MP_OBJ_NULL); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_ARG_2); emit_call(emit, MP_F_NATIVE_RAISE); } } @@ -2988,18 +2994,22 @@ static void emit_native_yield(emit_t *emit, int kind) { emit_native_adjust_stack_size(emit, 1); // send_value if (kind == MP_EMIT_YIELD_VALUE) { - // Check LOCAL_IDX_EXC_VAL for any injected value - ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit)); + // Check LOCAL_IDX_THROW_VAL for any injected value + ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_THROW_VAL(emit)); + ASM_MOV_REG_IMM(emit->as, REG_ARG_2, (mp_uint_t)MP_OBJ_NULL); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_ARG_2); emit_call(emit, MP_F_NATIVE_RAISE); } else { // Label loop entry emit_native_label_assign(emit, *emit->label_slot + 2); // Get the next item from the delegate generator + ASM_MOV_REG_LOCAL(emit->as, REG_ARG_3, LOCAL_IDX_THROW_VAL(emit)); // throw_value + ASM_MOV_REG_IMM(emit->as, REG_ARG_2, (mp_uint_t)MP_OBJ_NULL); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_ARG_2); vtype_kind_t vtype; emit_pre_pop_reg(emit, &vtype, REG_ARG_2); // send_value emit_access_stack(emit, 1, &vtype, REG_ARG_1); // generator - ASM_MOV_REG_LOCAL(emit->as, REG_ARG_3, LOCAL_IDX_EXC_VAL(emit)); // throw_value emit_post_push_reg(emit, VTYPE_PYOBJ, REG_ARG_3); emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 1); // ret_value emit_call(emit, MP_F_NATIVE_YIELD_FROM); diff --git a/tests/run-tests.py b/tests/run-tests.py index fa6c2a0331a10..a551f35c679e3 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -731,10 +731,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): 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("extmod/asyncio_event.py") # unknown issue skip_tests.add("extmod/asyncio_lock.py") # requires async with - skip_tests.add("extmod/asyncio_micropython.py") # unknown issue - skip_tests.add("extmod/asyncio_wait_for.py") # unknown issue skip_tests.add("misc/features.py") # requires raise_varargs skip_tests.add( "misc/print_exception.py" From 038125be79569548f5ebc0a336bda587ea7d63ca Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 29 Feb 2024 11:13:56 +1100 Subject: [PATCH 0059/1300] py/emitnative: Fix native async with. The code generating the entry to the finally handler of an async-with statement was simply wrong for the case of the native emitter. Among other things the layout of the stack was incorrect. This is fixed by this commit. The setup of the async-with finally handler is now put in a dedicated emit function, for both the bytecode and native emitters to implement in their own way (the bytecode emitter is unchanged, just factored to a function). With this fix all of the async-with tests now work when using the native emitter. Signed-off-by: Damien George --- py/compile.c | 18 +++++------------- py/emit.h | 6 ++++++ py/emitbc.c | 24 ++++++++++++++++++++++++ py/emitnative.c | 38 +++++++++++++++++++++++++++++++++++--- tests/run-tests.py | 4 ---- 5 files changed, 70 insertions(+), 20 deletions(-) diff --git a/py/compile.c b/py/compile.c index 3429e384c0050..d2af6aaf2cb6b 100644 --- a/py/compile.c +++ b/py/compile.c @@ -1899,19 +1899,7 @@ static void compile_async_with_stmt_helper(compiler_t *comp, size_t n, mp_parse_ // Handle case 1: call __aexit__ // Stack: (..., ctx_mgr) - EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); // to tell end_finally there's no exception - EMIT(rot_two); - EMIT_ARG(jump, l_aexit_no_exc); // jump to code below to call __aexit__ - - // Start of "finally" block - // At this point we have case 2 or 3, we detect which one by the TOS being an exception or not - EMIT_ARG(label_assign, l_finally_block); - - // Detect if TOS an exception or not - EMIT(dup_top); - EMIT_LOAD_GLOBAL(MP_QSTR_BaseException); - EMIT_ARG(binary_op, MP_BINARY_OP_EXCEPTION_MATCH); - EMIT_ARG(pop_jump_if, false, l_ret_unwind_jump); // if not an exception then we have case 3 + EMIT_ARG(async_with_setup_finally, l_aexit_no_exc, l_finally_block, l_ret_unwind_jump); // Handle case 2: call __aexit__ and either swallow or re-raise the exception // Stack: (..., ctx_mgr, exc) @@ -1937,6 +1925,7 @@ static void compile_async_with_stmt_helper(compiler_t *comp, size_t n, mp_parse_ EMIT_ARG(pop_jump_if, false, l_end); EMIT(pop_top); // pop exception EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); // replace with None to swallow exception + // Stack: (..., None) EMIT_ARG(jump, l_end); EMIT_ARG(adjust_stack_size, 2); @@ -1946,6 +1935,8 @@ static void compile_async_with_stmt_helper(compiler_t *comp, size_t n, mp_parse_ EMIT(rot_three); EMIT(rot_three); EMIT_ARG(label_assign, l_aexit_no_exc); + // We arrive here from either case 1 (a jump) or case 3 (fall through) + // Stack: case 1: (..., None, ctx_mgr) or case 3: (..., X, INT, ctx_mgr) EMIT_ARG(load_method, MP_QSTR___aexit__, false); EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); EMIT(dup_top); @@ -1953,6 +1944,7 @@ static void compile_async_with_stmt_helper(compiler_t *comp, size_t n, mp_parse_ EMIT_ARG(call_method, 3, 0, 0); compile_yield_from(comp); EMIT(pop_top); + // Stack: case 1: (..., None) or case 3: (..., X, INT) EMIT_ARG(adjust_stack_size, -1); // End of "finally" block diff --git a/py/emit.h b/py/emit.h index 9aad6ebafb89b..623b163490164 100644 --- a/py/emit.h +++ b/py/emit.h @@ -144,6 +144,9 @@ typedef struct _emit_method_table_t { void (*unwind_jump)(emit_t *emit, mp_uint_t label, mp_uint_t except_depth); void (*setup_block)(emit_t *emit, mp_uint_t label, int kind); void (*with_cleanup)(emit_t *emit, mp_uint_t label); + #if MICROPY_PY_ASYNC_AWAIT + void (*async_with_setup_finally)(emit_t *emit, mp_uint_t label_aexit_no_exc, mp_uint_t label_finally_block, mp_uint_t label_ret_unwind_jump); + #endif void (*end_finally)(emit_t *emit); void (*get_iter)(emit_t *emit, bool use_stack); void (*for_iter)(emit_t *emit, mp_uint_t label); @@ -264,6 +267,9 @@ void mp_emit_bc_jump_if_or_pop(emit_t *emit, bool cond, mp_uint_t label); void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_depth); void mp_emit_bc_setup_block(emit_t *emit, mp_uint_t label, int kind); void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label); +#if MICROPY_PY_ASYNC_AWAIT +void mp_emit_bc_async_with_setup_finally(emit_t *emit, mp_uint_t label_aexit_no_exc, mp_uint_t label_finally_block, mp_uint_t label_ret_unwind_jump); +#endif void mp_emit_bc_end_finally(emit_t *emit); void mp_emit_bc_get_iter(emit_t *emit, bool use_stack); void mp_emit_bc_for_iter(emit_t *emit, mp_uint_t label); diff --git a/py/emitbc.c b/py/emitbc.c index 05754cfabf65d..f6bb229ba0d87 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -666,6 +666,27 @@ void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label) { mp_emit_bc_adjust_stack_size(emit, -4); } +#if MICROPY_PY_ASYNC_AWAIT +void mp_emit_bc_async_with_setup_finally(emit_t *emit, mp_uint_t label_aexit_no_exc, mp_uint_t label_finally_block, mp_uint_t label_ret_unwind_jump) { + // The async-with body has executed and no exception was raised, the execution fell through to this point. + // Stack: (..., ctx_mgr) + + // Finish async-with body and prepare to enter "finally" block. + mp_emit_bc_load_const_tok(emit, MP_TOKEN_KW_NONE); // to tell end_finally there's no exception + mp_emit_bc_rot_two(emit); + mp_emit_bc_jump(emit, label_aexit_no_exc); // jump to code to call __aexit__ + + // Start of "finally" block which is entered via one of: an exception propagating out, a return, an unwind jump. + mp_emit_bc_label_assign(emit, label_finally_block); + + // Detect which case we have by the TOS being an exception or not. + mp_emit_bc_dup_top(emit); + mp_emit_bc_load_global(emit, MP_QSTR_BaseException, MP_EMIT_IDOP_GLOBAL_GLOBAL); + mp_emit_bc_binary_op(emit, MP_BINARY_OP_EXCEPTION_MATCH); + mp_emit_bc_pop_jump_if(emit, false, label_ret_unwind_jump); // if not an exception then we have return or unwind jump. +} +#endif + void mp_emit_bc_end_finally(emit_t *emit) { emit_write_bytecode_byte(emit, -1, MP_BC_END_FINALLY); } @@ -862,6 +883,9 @@ const emit_method_table_t emit_bc_method_table = { mp_emit_bc_unwind_jump, mp_emit_bc_setup_block, mp_emit_bc_with_cleanup, + #if MICROPY_PY_ASYNC_AWAIT + mp_emit_bc_async_with_setup_finally, + #endif mp_emit_bc_end_finally, mp_emit_bc_get_iter, mp_emit_bc_for_iter, diff --git a/py/emitnative.c b/py/emitnative.c index db31f51e652ca..88ebf0bfcf3de 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -1145,7 +1145,7 @@ static void emit_native_label_assign(emit_t *emit, mp_uint_t l) { if (is_finally) { // Label is at start of finally handler: store TOS into exception slot vtype_kind_t vtype; - emit_pre_pop_reg(emit, &vtype, REG_TEMP0); + emit_access_stack(emit, 1, &vtype, REG_TEMP0); ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); } @@ -1201,6 +1201,10 @@ static void emit_native_global_exc_entry(emit_t *emit) { ASM_XOR_REG_REG(emit->as, REG_TEMP0, REG_TEMP0); ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_UNWIND(emit), REG_TEMP0); + // clear nlr.ret_val, because it's passed to mp_native_raise regardless + // of whether there was an exception or not + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); + // Put PC of start code block into REG_LOCAL_1 ASM_MOV_REG_PCREL(emit->as, REG_LOCAL_1, start_label); @@ -2235,8 +2239,34 @@ static void emit_native_with_cleanup(emit_t *emit, mp_uint_t label) { emit_native_label_assign(emit, *emit->label_slot + 1); // Exception is in nlr_buf.ret_val slot + adjust_stack(emit, 1); } +#if MICROPY_PY_ASYNC_AWAIT +static void emit_native_async_with_setup_finally(emit_t *emit, mp_uint_t label_aexit_no_exc, mp_uint_t label_finally_block, mp_uint_t label_ret_unwind_jump) { + // The async-with body has executed and no exception was raised, the execution fell through to this point. + // Stack: (..., ctx_mgr) + + // Insert a dummy value into the stack so the stack has the same layout to execute the code starting at label_aexit_no_exc + emit_native_adjust_stack_size(emit, 1); // push dummy value, it won't ever be used + emit_native_rot_two(emit); + emit_native_load_const_tok(emit, MP_TOKEN_KW_NONE); // to tell end_finally there's no exception + emit_native_rot_two(emit); + // Stack: (..., , None, ctx_mgr) + emit_native_jump(emit, label_aexit_no_exc); // jump to code to call __aexit__ + emit_native_adjust_stack_size(emit, -1); + + // Start of "finally" block which is entered via one of: an exception propagating out, a return, an unwind jump. + emit_native_label_assign(emit, label_finally_block); + + // Detect which case we have by the local exception slot holding an exception or not. + emit_pre_pop_discard(emit); + ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit)); // get exception + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_ARG_1); + ASM_JUMP_IF_REG_ZERO(emit->as, REG_ARG_1, label_ret_unwind_jump, false); // if not an exception then we have return or unwind jump. +} +#endif + static void emit_native_end_finally(emit_t *emit) { // logic: // exc = pop_stack @@ -2245,7 +2275,7 @@ static void emit_native_end_finally(emit_t *emit) { // the check if exc is None is done in the MP_F_NATIVE_RAISE stub DEBUG_printf("end_finally\n"); - emit_native_pre(emit); + emit_pre_pop_discard(emit); ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit)); emit_call(emit, MP_F_NATIVE_RAISE); @@ -3033,7 +3063,6 @@ static void emit_native_start_except_handler(emit_t *emit) { } static void emit_native_end_except_handler(emit_t *emit) { - adjust_stack(emit, -1); // pop the exception (end_finally didn't use it) } const emit_method_table_t EXPORT_FUN(method_table) = { @@ -3082,6 +3111,9 @@ const emit_method_table_t EXPORT_FUN(method_table) = { emit_native_unwind_jump, emit_native_setup_block, emit_native_with_cleanup, + #if MICROPY_PY_ASYNC_AWAIT + emit_native_async_with_setup_finally, + #endif emit_native_end_finally, emit_native_get_iter, emit_native_for_iter, diff --git a/tests/run-tests.py b/tests/run-tests.py index a551f35c679e3..a6caff7bbd8ea 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -717,9 +717,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # 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/async_%s.py" % t for t in "with with2 with_break with_return".split()} - ) # require async_with skip_tests.update( {"basics/%s.py" % t for t in "try_reraise try_reraise2".split()} ) # require raise_varargs @@ -731,7 +728,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): 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("extmod/asyncio_lock.py") # requires async with skip_tests.add("misc/features.py") # requires raise_varargs skip_tests.add( "misc/print_exception.py" From 0619f261a82ead92bbd05ba387ebb3292ae62de9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Jun 2024 14:25:18 +1000 Subject: [PATCH 0060/1300] tests/basics: Add tests to test repeated throw into the same generator. Signed-off-by: Damien George --- tests/basics/gen_yield_from_throw_repeat.py | 22 +++++++++++++++++++++ tests/basics/generator_throw_repeat.py | 16 +++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/basics/gen_yield_from_throw_repeat.py create mode 100644 tests/basics/generator_throw_repeat.py diff --git a/tests/basics/gen_yield_from_throw_repeat.py b/tests/basics/gen_yield_from_throw_repeat.py new file mode 100644 index 0000000000000..96636a624432e --- /dev/null +++ b/tests/basics/gen_yield_from_throw_repeat.py @@ -0,0 +1,22 @@ +# Test throwing repeatedly into the same generator, where that generator +# is yielding from another generator. + + +def yielder(): + yield 4 + yield 5 + + +def gen(): + while True: + try: + print("gen received:", (yield from yielder())) + except ValueError as exc: + print(repr(exc)) + + +g = gen() +for i in range(2): + print("send, got:", g.send(None)) + print("throw, got:", g.throw(ValueError("a", i))) + print("throw, got:", g.throw(ValueError("b", i))) diff --git a/tests/basics/generator_throw_repeat.py b/tests/basics/generator_throw_repeat.py new file mode 100644 index 0000000000000..6d6ef60a9b0d5 --- /dev/null +++ b/tests/basics/generator_throw_repeat.py @@ -0,0 +1,16 @@ +# Test throwing repeatedly into the same generator. + + +def gen(): + while True: + try: + print("gen received:", (yield "value")) + except ValueError as exc: + print(repr(exc)) + + +g = gen() +for i in range(2): + print("send, got:", g.send(None)) + print("throw, got:", g.throw(ValueError("a", i))) + print("throw, got:", g.throw(ValueError("b", i))) From cebc9b0ae2b12c61eac39a3c599edb3b1b65dd54 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Jun 2024 12:11:26 +1000 Subject: [PATCH 0061/1300] tools/mpremote: Fix absolute path usage in remote mounted VFS. Prior to this fix the current working path in the remote VFS would always be prepended to the requested path to get the full path, even if the requested path was already absolute, ie starting with "/". So `os.chdir("/remote/dir1")` would set the working path to "/dir1/", and a subsequent call with an absolute path like `os.listdir("/remote/dir2")` would try to list the directory "/dir1/dir2/". Fixes issue #15308. Signed-off-by: Damien George --- tools/mpremote/mpremote/transport_serial.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tools/mpremote/mpremote/transport_serial.py b/tools/mpremote/mpremote/transport_serial.py index 3b4cd00078752..a97b7e0a08264 100644 --- a/tools/mpremote/mpremote/transport_serial.py +++ b/tools/mpremote/mpremote/transport_serial.py @@ -838,6 +838,9 @@ class RemoteFS: def __init__(self, cmd): self.cmd = cmd + def _abspath(self, path): + return path if path.startswith("/") else self.path + path + def mount(self, readonly, mkfs): pass @@ -859,7 +862,7 @@ def getcwd(self): def remove(self, path): c = self.cmd c.begin(CMD_REMOVE) - c.wr_str(self.path + path) + c.wr_str(self._abspath(path)) res = c.rd_s32() c.end() if res < 0: @@ -868,8 +871,8 @@ def remove(self, path): def rename(self, old, new): c = self.cmd c.begin(CMD_RENAME) - c.wr_str(self.path + old) - c.wr_str(self.path + new) + c.wr_str(self._abspath(old)) + c.wr_str(self._abspath(new)) res = c.rd_s32() c.end() if res < 0: @@ -878,7 +881,7 @@ def rename(self, old, new): def mkdir(self, path): c = self.cmd c.begin(CMD_MKDIR) - c.wr_str(self.path + path) + c.wr_str(self._abspath(path)) res = c.rd_s32() c.end() if res < 0: @@ -887,7 +890,7 @@ def mkdir(self, path): def rmdir(self, path): c = self.cmd c.begin(CMD_RMDIR) - c.wr_str(self.path + path) + c.wr_str(self._abspath(path)) res = c.rd_s32() c.end() if res < 0: @@ -896,7 +899,7 @@ def rmdir(self, path): def stat(self, path): c = self.cmd c.begin(CMD_STAT) - c.wr_str(self.path + path) + c.wr_str(self._abspath(path)) res = c.rd_s8() if res < 0: c.end() @@ -912,7 +915,7 @@ def stat(self, path): def ilistdir(self, path): c = self.cmd c.begin(CMD_ILISTDIR_START) - c.wr_str(self.path + path) + c.wr_str(self._abspath(path)) res = c.rd_s8() c.end() if res < 0: @@ -933,7 +936,7 @@ def next(): def open(self, path, mode): c = self.cmd c.begin(CMD_OPEN) - c.wr_str(self.path + path) + c.wr_str(self._abspath(path)) c.wr_str(mode) fd = c.rd_s8() c.end() From d933210d960a6f9337e85753e9568619bbfd54ec Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 16 Apr 2024 17:07:05 +1000 Subject: [PATCH 0062/1300] py/misc: Move mp_clz and mp_ctz intrinsics into misc.h. Signed-off-by: Angus Gratton --- py/asmthumb.c | 18 +----------------- py/misc.h | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/py/asmthumb.c b/py/asmthumb.c index 0df79e5fd6203..420815e80269a 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -35,23 +35,7 @@ #include "py/mpstate.h" #include "py/asmthumb.h" - -#ifdef _MSC_VER -#include - -static uint32_t mp_clz(uint32_t x) { - unsigned long lz = 0; - return _BitScanReverse(&lz, x) ? (sizeof(x) * 8 - 1) - lz : 0; -} - -static uint32_t mp_ctz(uint32_t x) { - unsigned long tz = 0; - return _BitScanForward(&tz, x) ? tz : 0; -} -#else -#define mp_clz(x) __builtin_clz(x) -#define mp_ctz(x) __builtin_ctz(x) -#endif +#include "py/misc.h" #define UNSIGNED_FIT5(x) ((uint32_t)(x) < 32) #define UNSIGNED_FIT7(x) ((uint32_t)(x) < 128) diff --git a/py/misc.h b/py/misc.h index eea3e8b0fe7f5..9f8a8c1e13e53 100644 --- a/py/misc.h +++ b/py/misc.h @@ -334,4 +334,22 @@ typedef const char *mp_rom_error_text_t; // For now, forward directly to MP_COMPRESSED_ROM_TEXT. #define MP_ERROR_TEXT(x) (mp_rom_error_text_t)MP_COMPRESSED_ROM_TEXT(x) +// Portable implementations of CLZ and CTZ intrinsics +#ifdef _MSC_VER +#include + +static uint32_t mp_clz(uint32_t x) { + unsigned long lz = 0; + return _BitScanReverse(&lz, x) ? (sizeof(x) * 8 - 1) - lz : 0; +} + +static uint32_t mp_ctz(uint32_t x) { + unsigned long tz = 0; + return _BitScanForward(&tz, x) ? tz : 0; +} +#else +#define mp_clz(x) __builtin_clz(x) +#define mp_ctz(x) __builtin_ctz(x) +#endif + #endif // MICROPY_INCLUDED_PY_MISC_H From 908ab1ceca15ee6fd0ef82ca4cba770a3ec41894 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 29 Nov 2023 11:23:16 +1100 Subject: [PATCH 0063/1300] py/objint: Fix int.to_bytes() buffer size checks. Fixes and improvements to `int.to_bytes()` are: - No longer overflows if byte size is 0 (closes #13041). - Raises OverflowError in any case where number won't fit into byte length (now matches CPython, previously MicroPython would return a truncated bytes object). - Document that `micropython int.to_bytes()` doesn't implement the optional signed kwarg, but will behave as if `signed=True` when the integer is negative (this is the current behaviour). Add tests for this also. Requires changes for small ints, MPZ large ints, and "long long" large ints. Adds a new set of unit tests for ints between 32 and 64 bits to increase coverage of "long long" large ints, which are otherwise untested. Tested on unix port (64 bit small ints, MPZ long ints) and Zephyr STM32WB board (32 bit small ints, long long large ints). This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/library/builtins.rst | 4 ++ py/misc.h | 33 +++++++++++++ py/mpz.c | 32 ++++++++----- py/mpz.h | 9 ++-- py/objint.c | 37 +++++++++++---- py/objint.h | 3 +- py/objint_longlong.c | 20 +++++++- py/objint_mpz.c | 4 +- tests/basics/int_bytes.py | 73 +++++++++++++++++++++++++++++ tests/basics/int_bytes_int64.py | 52 ++++++++++++++++++++ tests/basics/int_bytes_intbig.py | 48 +++++++++++++++++++ tests/cpydiff/types_int_to_bytes.py | 16 +++++++ 12 files changed, 302 insertions(+), 29 deletions(-) create mode 100644 tests/basics/int_bytes_int64.py create mode 100644 tests/cpydiff/types_int_to_bytes.py diff --git a/docs/library/builtins.rst b/docs/library/builtins.rst index 7a0229c2aaaf6..e489375b1f917 100644 --- a/docs/library/builtins.rst +++ b/docs/library/builtins.rst @@ -82,6 +82,10 @@ Functions and types In MicroPython, `byteorder` parameter must be positional (this is compatible with CPython). + .. note:: The optional ``signed`` kwarg from CPython is not supported. + MicroPython currently converts negative integers as signed, + and positive as unsigned. (:ref:`Details `.) + .. function:: isinstance() .. function:: issubclass() diff --git a/py/misc.h b/py/misc.h index 9f8a8c1e13e53..cf1810d4e784b 100644 --- a/py/misc.h +++ b/py/misc.h @@ -343,13 +343,46 @@ static uint32_t mp_clz(uint32_t x) { return _BitScanReverse(&lz, x) ? (sizeof(x) * 8 - 1) - lz : 0; } +static uint32_t mp_clzl(unsigned long x) { + unsigned long lz = 0; + return _BitScanReverse(&lz, x) ? (sizeof(x) * 8 - 1) - lz : 0; +} + +#ifdef _WIN64 +static uint32_t mp_clzll(unsigned long long x) { + unsigned long lz = 0; + return _BitScanReverse64(&lz, x) ? (sizeof(x) * 8 - 1) - lz : 0; +} +#else +// Microsoft don't ship _BitScanReverse64 on Win32, so emulate it +static uint32_t mp_clzll(unsigned long long x) { + unsigned long h = x >> 32; + return h ? mp_clzl(h) : (mp_clzl(x) + 32); +} +#endif + static uint32_t mp_ctz(uint32_t x) { unsigned long tz = 0; return _BitScanForward(&tz, x) ? tz : 0; } #else #define mp_clz(x) __builtin_clz(x) +#define mp_clzl(x) __builtin_clzl(x) +#define mp_clzll(x) __builtin_clzll(x) #define mp_ctz(x) __builtin_ctz(x) #endif +// mp_int_t can be larger than long, i.e. Windows 64-bit, nan-box variants +static inline uint32_t mp_clz_mpi(mp_int_t x) { + MP_STATIC_ASSERT(sizeof(mp_int_t) == sizeof(long long) + || sizeof(mp_int_t) == sizeof(long)); + + // ugly, but should compile to single intrinsic unless O0 is set + if (sizeof(mp_int_t) == sizeof(long)) { + return mp_clzl(x); + } else { + return mp_clzll(x); + } +} + #endif // MICROPY_INCLUDED_PY_MISC_H diff --git a/py/mpz.c b/py/mpz.c index 502d4e1c138f0..750664ad9aaf8 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -1589,7 +1589,7 @@ bool mpz_as_uint_checked(const mpz_t *i, mp_uint_t *value) { return true; } -void mpz_as_bytes(const mpz_t *z, bool big_endian, size_t len, byte *buf) { +bool mpz_as_bytes(const mpz_t *z, bool big_endian, bool as_signed, size_t len, byte *buf) { byte *b = buf; if (big_endian) { b += len; @@ -1598,6 +1598,8 @@ void mpz_as_bytes(const mpz_t *z, bool big_endian, size_t len, byte *buf) { int bits = 0; mpz_dbl_dig_t d = 0; mpz_dbl_dig_t carry = 1; + size_t olen = len; // bytes in output buffer + bool ok = true; for (size_t zlen = z->len; zlen > 0; --zlen) { bits += DIG_SIZE; d = (d << DIG_SIZE) | *zdig++; @@ -1607,28 +1609,32 @@ void mpz_as_bytes(const mpz_t *z, bool big_endian, size_t len, byte *buf) { val = (~val & 0xff) + carry; carry = val >> 8; } + + if (!olen) { + // Buffer is full, only OK if all remaining bytes are zeroes + ok = ok && ((byte)val == 0); + continue; + } + if (big_endian) { *--b = val; - if (b == buf) { - return; - } } else { *b++ = val; - if (b == buf + len) { - return; - } } + olen--; } } - // fill remainder of buf with zero/sign extension of the integer - if (big_endian) { - len = b - buf; + if (as_signed && olen == 0 && len > 0) { + // If output exhausted then ensure there was enough space for the sign bit + byte most_sig = big_endian ? buf[0] : buf[len - 1]; + ok = ok && (bool)(most_sig & 0x80) == (bool)z->neg; } else { - len = buf + len - b; - buf = b; + // fill remainder of buf with zero/sign extension of the integer + memset(big_endian ? buf : b, z->neg ? 0xff : 0x00, olen); } - memset(buf, z->neg ? 0xff : 0x00, len); + + return ok; } #if MICROPY_PY_BUILTINS_FLOAT diff --git a/py/mpz.h b/py/mpz.h index d27f5724047ae..6f1ac930b0214 100644 --- a/py/mpz.h +++ b/py/mpz.h @@ -93,9 +93,9 @@ typedef int8_t mpz_dbl_dig_signed_t; typedef struct _mpz_t { // Zero has neg=0, len=0. Negative zero is not allowed. size_t neg : 1; - size_t fixed_dig : 1; - size_t alloc : (8 * sizeof(size_t) - 2); - size_t len; + size_t fixed_dig : 1; // flag, 'dig' buffer cannot be reallocated + size_t alloc : (8 * sizeof(size_t) - 2); // number of entries allocated in 'dig' + size_t len; // number of entries used in 'dig' mpz_dig_t *dig; } mpz_t; @@ -145,7 +145,8 @@ static inline size_t mpz_max_num_bits(const mpz_t *z) { mp_int_t mpz_hash(const mpz_t *z); bool mpz_as_int_checked(const mpz_t *z, mp_int_t *value); bool mpz_as_uint_checked(const mpz_t *z, mp_uint_t *value); -void mpz_as_bytes(const mpz_t *z, bool big_endian, size_t len, byte *buf); +// Returns true if 'z' fit into 'len' bytes of 'buf' without overflowing, 'buf' is truncated otherwise. +bool mpz_as_bytes(const mpz_t *z, bool big_endian, bool as_signed, size_t len, byte *buf); #if MICROPY_PY_BUILTINS_FLOAT mp_float_t mpz_as_float(const mpz_t *z); #endif diff --git a/py/objint.c b/py/objint.c index 6caa608f33035..467a4714ef197 100644 --- a/py/objint.c +++ b/py/objint.c @@ -421,29 +421,50 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_from_bytes_fun_obj, 3, 4, int_fro static MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, MP_ROM_PTR(&int_from_bytes_fun_obj)); static mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { - // TODO: Support signed param (assumes signed=False) + // TODO: Support signed (currently behaves as if signed=(val < 0)) (void)n_args; + bool overflow; - mp_int_t len = mp_obj_get_int(args[1]); - if (len < 0) { + mp_int_t dlen = mp_obj_get_int(args[1]); + if (dlen < 0) { mp_raise_ValueError(NULL); } bool big_endian = args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little); vstr_t vstr; - vstr_init_len(&vstr, len); + vstr_init_len(&vstr, dlen); byte *data = (byte *)vstr.buf; - memset(data, 0, len); #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE if (!mp_obj_is_small_int(args[0])) { - mp_obj_int_to_bytes_impl(args[0], big_endian, len, data); + overflow = !mp_obj_int_to_bytes_impl(args[0], big_endian, dlen, data); } else #endif { mp_int_t val = MP_OBJ_SMALL_INT_VALUE(args[0]); - size_t l = MIN((size_t)len, sizeof(val)); - mp_binary_set_int(l, big_endian, data + (big_endian ? (len - l) : 0), val); + int slen = 0; // Number of bytes to represent val + + // This logic has a twin in objint_longlong.c + if (val > 0) { + slen = (sizeof(mp_int_t) * 8 - mp_clz_mpi(val) + 7) / 8; + } else if (val < -1) { + slen = (sizeof(mp_int_t) * 8 - mp_clz_mpi(~val) + 8) / 8; + } else { + // clz of 0 is defined, so 0 and -1 map to 0 and 1 + slen = -val; + } + + if (slen <= dlen) { + memset(data, val < 0 ? 0xFF : 0x00, dlen); + mp_binary_set_int(slen, big_endian, data + (big_endian ? (dlen - slen) : 0), val); + overflow = false; + } else { + overflow = true; + } + } + + if (overflow) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("buffer too small")); } return mp_obj_new_bytes_from_vstr(&vstr); diff --git a/py/objint.h b/py/objint.h index 5eed87705dedb..28930e35adb49 100644 --- a/py/objint.h +++ b/py/objint.h @@ -55,7 +55,8 @@ char *mp_obj_int_formatted_impl(char **buf, size_t *buf_size, size_t *fmt_size, int base, const char *prefix, char base_char, char comma); mp_int_t mp_obj_int_hash(mp_obj_t self_in); mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf); -void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf); +// Returns true if 'self_in' fit into 'len' bytes of 'buf' without overflowing, 'buf' is truncated otherwise. +bool mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf); int mp_obj_int_sign(mp_obj_t self_in); mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in); mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in); diff --git a/py/objint_longlong.c b/py/objint_longlong.c index ee499e0265b32..00fe5636c1607 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -57,10 +57,27 @@ mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf return mp_obj_new_int_from_ll(value); } -void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) { +bool mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) { assert(mp_obj_is_exact_type(self_in, &mp_type_int)); mp_obj_int_t *self = self_in; long long val = self->val; + size_t slen; // Number of bytes to represent val + + // This logic has a twin in objint.c + if (val > 0) { + slen = (sizeof(long long) * 8 - mp_clzll(val) + 7) / 8; + } else if (val < -1) { + slen = (sizeof(long long) * 8 - mp_clzll(~val) + 8) / 8; + } else { + // clz of 0 is defined, so 0 and -1 map to 0 and 1 + slen = -val; + } + + if (slen > len) { + return false; // Would overflow + // TODO: Determine whether to copy and truncate, as some callers probably expect this...? + } + if (big_endian) { byte *b = buf + len; while (b > buf) { @@ -73,6 +90,7 @@ void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byt val >>= 8; } } + return true; } int mp_obj_int_sign(mp_obj_t self_in) { diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 600316a42abba..4a1a685bbd42e 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -112,10 +112,10 @@ mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf return MP_OBJ_FROM_PTR(o); } -void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) { +bool mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) { assert(mp_obj_is_exact_type(self_in, &mp_type_int)); mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); - mpz_as_bytes(&self->mpz, big_endian, len, buf); + return mpz_as_bytes(&self->mpz, big_endian, self->mpz.neg, len, buf); } int mp_obj_int_sign(mp_obj_t self_in) { diff --git a/tests/basics/int_bytes.py b/tests/basics/int_bytes.py index d1837ea75ca4a..15c12640e951b 100644 --- a/tests/basics/int_bytes.py +++ b/tests/basics/int_bytes.py @@ -1,3 +1,5 @@ +import sys + print((10).to_bytes(1, "little")) print((111111).to_bytes(4, "little")) print((100).to_bytes(10, "little")) @@ -20,3 +22,74 @@ (1).to_bytes(-1, "little") except ValueError: print("ValueError") + +# zero byte destination should also raise an error +try: + (1).to_bytes(0, "little") +except OverflowError: + print("OverflowError") + +# except for converting 0 to a zero-length byte array +print((0).to_bytes(0, "big")) + +# byte length can fit the integer directly +print((0xFF).to_bytes(1, "little")) +print((0xFF).to_bytes(1, "big")) +print((0xEFF).to_bytes(2, "little")) +print((0xEFF).to_bytes(2, "big")) +print((0xCDEFF).to_bytes(3, "little")) +print((0xCDEFF).to_bytes(3, "big")) + +# OverFlowError if not big enough + +try: + (0x123).to_bytes(1, "big") +except OverflowError: + print("OverflowError") + +try: + (0x12345).to_bytes(2, "big") +except OverflowError: + print("OverflowError") + +try: + (0x1234567).to_bytes(3, "big") +except OverflowError: + print("OverflowError") + + +# negative representations + +# MicroPython int.to_bytes() behaves as if signed=True for negative numbers +if "micropython" in repr(sys.implementation): + + def to_bytes_compat(i, l, e): + return i.to_bytes(l, e) +else: + # Implement MicroPython compatible behaviour for CPython + def to_bytes_compat(i, l, e): + return i.to_bytes(l, e, signed=i < 0) + + +print(to_bytes_compat(-1, 1, "little")) +print(to_bytes_compat(-1, 3, "little")) +print(to_bytes_compat(-1, 1, "big")) +print(to_bytes_compat(-1, 3, "big")) +print(to_bytes_compat(-128, 1, "big")) +print(to_bytes_compat(-32768, 2, "big")) +print(to_bytes_compat(-(1 << 23), 3, "big")) + +try: + print(to_bytes_compat(-129, 1, "big")) +except OverflowError: + print("OverflowError") + +try: + print(to_bytes_compat(-32769, 2, "big")) +except OverflowError: + print("OverflowError") + +try: + print(to_bytes_compat(-(1 << 23) - 1, 2, "big")) +except OverflowError: + print("OverflowError") diff --git a/tests/basics/int_bytes_int64.py b/tests/basics/int_bytes_int64.py new file mode 100644 index 0000000000000..032dbccc5b14e --- /dev/null +++ b/tests/basics/int_bytes_int64.py @@ -0,0 +1,52 @@ +import sys + +# Depending on the port, the numbers in this test may be implemented as "small" +# native 64 bit ints, arbitrary precision large ints, or large integers using 64-bit +# long longs. + +try: + x = int.from_bytes(b"\x6F\xAB\xCD\x12\x34\x56\x78\xFB", "big") +except OverflowError: + print("SKIP") # Port can't represent this size of integer at all + raise SystemExit + +print(hex(x)) +b = x.to_bytes(8, "little") +print(b) +print(x.to_bytes(8, "big")) + +# padding in output +print(x.to_bytes(20, "little")) +print(x.to_bytes(20, "big")) + +# check that extra zero bytes don't change the internal int value +print(int.from_bytes(b + bytes(10), "little") == x) + +# can't write to a zero-length bytes object +try: + x.to_bytes(0, "little") +except OverflowError: + print("OverflowError") + +# or one that it too short +try: + x.to_bytes(7, "big") +except OverflowError: + print("OverflowError") + +# negative representations + +# MicroPython int.to_bytes() behaves as if signed=True for negative numbers +if "micropython" in repr(sys.implementation): + + def to_bytes_compat(i, l, e): + return i.to_bytes(l, e) +else: + # Implement MicroPython compatible behaviour for CPython + def to_bytes_compat(i, l, e): + return i.to_bytes(l, e, signed=i < 0) + + +print(to_bytes_compat(-x, 8, "little")) +print(to_bytes_compat(-x, 20, "big")) +print(to_bytes_compat(-x, 20, "little")) diff --git a/tests/basics/int_bytes_intbig.py b/tests/basics/int_bytes_intbig.py index 147362bef1378..13cf5d0085890 100644 --- a/tests/basics/int_bytes_intbig.py +++ b/tests/basics/int_bytes_intbig.py @@ -1,3 +1,5 @@ +import sys + print((2**64).to_bytes(9, "little")) print((2**64).to_bytes(9, "big")) @@ -10,5 +12,51 @@ print(il.to_bytes(20, "little")) print(ib.to_bytes(20, "big")) +# check padding comes out correctly +print(il.to_bytes(40, "little")) +print(ib.to_bytes(40, "big")) + # check that extra zero bytes don't change the internal int value print(int.from_bytes(b + bytes(10), "little") == int.from_bytes(b, "little")) + +# can't write to a zero-length bytes object +try: + ib.to_bytes(0, "little") +except OverflowError: + print("OverflowError") + +# or one that it too short +try: + ib.to_bytes(18, "big") +except OverflowError: + print("OverflowError") + +# negative representations + +# MicroPython int.to_bytes() behaves as if signed=True for negative numbers +if "micropython" in repr(sys.implementation): + + def to_bytes_compat(i, l, e): + return i.to_bytes(l, e) +else: + # Implement MicroPython compatible behaviour for CPython + def to_bytes_compat(i, l, e): + return i.to_bytes(l, e, signed=i < 0) + + +print(to_bytes_compat(-ib, 20, "big")) +print(to_bytes_compat(ib * -ib, 40, "big")) + +# case where an additional byte is needed for sign bit +ib = (2**64) - 1 +print(ib.to_bytes(8, "little")) + +ib *= -1 + +try: + print(to_bytes_compat(ib, 8, "little")) +except OverflowError: + print("OverflowError") + +print(to_bytes_compat(ib, 9, "little")) +print(to_bytes_compat(ib, 9, "big")) diff --git a/tests/cpydiff/types_int_to_bytes.py b/tests/cpydiff/types_int_to_bytes.py new file mode 100644 index 0000000000000..6530a2a32ecb7 --- /dev/null +++ b/tests/cpydiff/types_int_to_bytes.py @@ -0,0 +1,16 @@ +""" +categories: Types,int +description: ``to_bytes`` method doesn't implement signed parameter. +cause: The ``signed`` keyword-only parameter is not implemented for ``int.to_bytes()``. + +When the integer is negative, MicroPython behaves the same as CPython ``int.to_bytes(..., signed=True)`` + +When the integer is non-negative, MicroPython behaves the same as CPython ``int.to_bytes(..., signed=False)``. + +(The difference is subtle, but in CPython a positive integer converted with ``signed=True`` may require one byte more in the output length, in order to fit the 0 sign bit.) + +workaround: Take care when calling ``to_bytes()`` on an integer value which may be negative. +""" + +x = -1 +print(x.to_bytes(1, "big")) From cfa55b4ca1e24dc60dadc839c49fcee475cd40ef Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 12 Jun 2024 17:19:10 +1000 Subject: [PATCH 0064/1300] rp2: Fix recursive atomic sections when core1 is active. mp_thread_begin_atomic_section() is expected to be recursive (i.e. for nested machine.disable_irq() calls, or if Python code calls disable_irq() and then the Python runtime calls mp_handle_pending() which also enters an atomic section to check the scheduler state). On rp2 when not using core1 the atomic sections are recursive. However when core1 was active (i.e. _thread) then there was a bug that caused the core to live-lock if an atomic section recursed. Adds a test case specifically for mutual exclusion and recursive atomic sections when using two threads. Without this fix the test immediately hangs on rp2. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/mpthreadport.c | 8 +++--- ports/rp2/mutex_extra.c | 13 ++++++--- ports/rp2/mutex_extra.h | 4 +-- tests/thread/disable_irq.py | 51 +++++++++++++++++++++++++++++++++ tests/thread/disable_irq.py.exp | 2 ++ 5 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 tests/thread/disable_irq.py create mode 100644 tests/thread/disable_irq.py.exp diff --git a/ports/rp2/mpthreadport.c b/ports/rp2/mpthreadport.c index 8d8f13a089059..5b8e804f95fd8 100644 --- a/ports/rp2/mpthreadport.c +++ b/ports/rp2/mpthreadport.c @@ -46,13 +46,13 @@ static uint32_t *core1_stack = NULL; static size_t core1_stack_num_words = 0; // Thread mutex. -static mutex_t atomic_mutex; +static recursive_mutex_t atomic_mutex; uint32_t mp_thread_begin_atomic_section(void) { if (core1_entry) { // When both cores are executing, we also need to provide // full mutual exclusion. - return mutex_enter_blocking_and_disable_interrupts(&atomic_mutex); + return recursive_mutex_enter_blocking_and_disable_interrupts(&atomic_mutex); } else { return save_and_disable_interrupts(); } @@ -60,7 +60,7 @@ uint32_t mp_thread_begin_atomic_section(void) { void mp_thread_end_atomic_section(uint32_t state) { if (atomic_mutex.owner != LOCK_INVALID_OWNER_ID) { - mutex_exit_and_restore_interrupts(&atomic_mutex, state); + recursive_mutex_exit_and_restore_interrupts(&atomic_mutex, state); } else { restore_interrupts(state); } @@ -70,7 +70,7 @@ void mp_thread_end_atomic_section(uint32_t state) { void mp_thread_init(void) { assert(get_core_num() == 0); - mutex_init(&atomic_mutex); + recursive_mutex_init(&atomic_mutex); // Allow MICROPY_BEGIN_ATOMIC_SECTION to be invoked from core1. multicore_lockout_victim_init(); diff --git a/ports/rp2/mutex_extra.c b/ports/rp2/mutex_extra.c index 6df57e64ce50f..7a70a40acc13d 100644 --- a/ports/rp2/mutex_extra.c +++ b/ports/rp2/mutex_extra.c @@ -9,22 +9,27 @@ // These functions are taken from lib/pico-sdk/src/common/pico_sync/mutex.c and modified // so that they atomically obtain the mutex and disable interrupts. -uint32_t __time_critical_func(mutex_enter_blocking_and_disable_interrupts)(mutex_t * mtx) { +uint32_t __time_critical_func(recursive_mutex_enter_blocking_and_disable_interrupts)(recursive_mutex_t * mtx) { lock_owner_id_t caller = lock_get_caller_owner_id(); do { uint32_t save = spin_lock_blocking(mtx->core.spin_lock); - if (!lock_is_owner_id_valid(mtx->owner)) { + if (mtx->owner == caller || !lock_is_owner_id_valid(mtx->owner)) { mtx->owner = caller; + uint __unused total = ++mtx->enter_count; spin_unlock_unsafe(mtx->core.spin_lock); + assert(total); // check for overflow return save; } lock_internal_spin_unlock_with_wait(&mtx->core, save); } while (true); } -void __time_critical_func(mutex_exit_and_restore_interrupts)(mutex_t * mtx, uint32_t save) { +void __time_critical_func(recursive_mutex_exit_and_restore_interrupts)(recursive_mutex_t * mtx, uint32_t save) { spin_lock_unsafe_blocking(mtx->core.spin_lock); assert(lock_is_owner_id_valid(mtx->owner)); - mtx->owner = LOCK_INVALID_OWNER_ID; + assert(mtx->enter_count); + if (!--mtx->enter_count) { + mtx->owner = LOCK_INVALID_OWNER_ID; + } lock_internal_spin_unlock_with_notify(&mtx->core, save); } diff --git a/ports/rp2/mutex_extra.h b/ports/rp2/mutex_extra.h index be7cb96dcc53d..61b6b40355f8d 100644 --- a/ports/rp2/mutex_extra.h +++ b/ports/rp2/mutex_extra.h @@ -28,7 +28,7 @@ #include "pico/mutex.h" -uint32_t mutex_enter_blocking_and_disable_interrupts(mutex_t *mtx); -void mutex_exit_and_restore_interrupts(mutex_t *mtx, uint32_t save); +uint32_t recursive_mutex_enter_blocking_and_disable_interrupts(recursive_mutex_t *mtx); +void recursive_mutex_exit_and_restore_interrupts(recursive_mutex_t *mtx, uint32_t save); #endif // MICROPY_INCLUDED_RP2_MUTEX_EXTRA_H diff --git a/tests/thread/disable_irq.py b/tests/thread/disable_irq.py new file mode 100644 index 0000000000000..3f1ac74f30877 --- /dev/null +++ b/tests/thread/disable_irq.py @@ -0,0 +1,51 @@ +# Ensure that disabling IRQs creates mutual exclusion between threads +# (also tests nesting of disable_irq across threads) +import machine +import time +import _thread + +if not hasattr(machine, "disable_irq"): + print("SKIP") + raise SystemExit + +count = 0 +thread_done = False + + +def inc_count(): + global count + a = machine.disable_irq() + try: + count += 1 + i = 0 + while i < 20: + b = machine.disable_irq() + try: + count += 1 + count -= 1 + i += 1 + finally: + machine.enable_irq(b) + finally: + machine.enable_irq(a) + + +def inc_count_multiple(times): + for _ in range(times): + inc_count() + + +def thread_entry(inc_times): + global thread_done + inc_count_multiple(inc_times) + thread_done = True + + +_thread.start_new_thread(thread_entry, (1000,)) +inc_count_multiple(1000) + +time.sleep(1) + +print("count", count, thread_done) +if count == 2000: + print("PASS") diff --git a/tests/thread/disable_irq.py.exp b/tests/thread/disable_irq.py.exp new file mode 100644 index 0000000000000..2174b91d0d044 --- /dev/null +++ b/tests/thread/disable_irq.py.exp @@ -0,0 +1,2 @@ +count 2000 True +PASS From 5dcffb53ab953e8264be33e7cb56280b7e20c345 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 25 Jun 2024 16:19:15 +1000 Subject: [PATCH 0065/1300] rp2/clocks_extra: Implement custom clocks_init function. Adapts pico-sdk clocks_init() into clocks_init_optional_usb() which takes an argument to initialise USB clocks or not. To avoid a code size increase the SDK clocks_init() function is linker wrapped to become clocks_init_optional_usb(true). This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- LICENSE | 1 + ports/rp2/CMakeLists.txt | 2 + ports/rp2/clocks_extra.c | 102 +++++++++++++++++++++++++++++++++++++++ ports/rp2/clocks_extra.h | 33 +++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 ports/rp2/clocks_extra.c create mode 100644 ports/rp2/clocks_extra.h diff --git a/LICENSE b/LICENSE index 469ae1927396d..341698fa49a42 100644 --- a/LICENSE +++ b/LICENSE @@ -73,6 +73,7 @@ used during the build process and is not part of the compiled source code. /ppp_set_auth.* (Apache-2.0) /rp2 /mutex_extra.c (BSD-3-clause) + /clocks_extra.c (BSD-3-clause) /stm32 /usbd*.c (MCD-ST Liberty SW License Agreement V2) /stm32_it.* (MIT + BSD-3-clause) diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index d3ecee586054c..8f5680092c925 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -117,6 +117,7 @@ set(MICROPY_SOURCE_DRIVERS ) set(MICROPY_SOURCE_PORT + clocks_extra.c fatfs_port.c help.c machine_bitstream.c @@ -453,6 +454,7 @@ target_compile_options(${MICROPY_TARGET} PRIVATE target_link_options(${MICROPY_TARGET} PRIVATE -Wl,--defsym=__micropy_c_heap_size__=${MICROPY_C_HEAP_SIZE} -Wl,--wrap=dcd_event_handler + -Wl,--wrap=clocks_init ) # Apply optimisations to performance-critical source code. diff --git a/ports/rp2/clocks_extra.c b/ports/rp2/clocks_extra.c new file mode 100644 index 0000000000000..e2c97962cb43d --- /dev/null +++ b/ports/rp2/clocks_extra.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "pico.h" +#include "clocks_extra.h" +#include "hardware/regs/clocks.h" +#include "hardware/platform_defs.h" +#include "hardware/clocks.h" +#include "hardware/watchdog.h" +#include "hardware/pll.h" +#include "hardware/xosc.h" +#include "hardware/irq.h" +#include "hardware/gpio.h" + +#define RTC_CLOCK_FREQ_HZ (USB_CLK_KHZ * KHZ / 1024) + +// Wrap the SDK's clocks_init() function to save code size +void __wrap_clocks_init(void) { + clocks_init_optional_usb(true); +} + +// Copy of clocks_init() from pico-sdk, with USB +// PLL and clock init made optional (for light sleep wakeup). +void clocks_init_optional_usb(bool init_usb) { + // Start tick in watchdog, the argument is in 'cycles per microsecond' i.e. MHz + watchdog_start_tick(XOSC_KHZ / KHZ); + + // Modification: removed FPGA check here + + // Disable resus that may be enabled from previous software + clocks_hw->resus.ctrl = 0; + + // Enable the xosc + xosc_init(); + + // Before we touch PLLs, switch sys and ref cleanly away from their aux sources. + hw_clear_bits(&clocks_hw->clk[clk_sys].ctrl, CLOCKS_CLK_SYS_CTRL_SRC_BITS); + while (clocks_hw->clk[clk_sys].selected != 0x1) { + tight_loop_contents(); + } + hw_clear_bits(&clocks_hw->clk[clk_ref].ctrl, CLOCKS_CLK_REF_CTRL_SRC_BITS); + while (clocks_hw->clk[clk_ref].selected != 0x1) { + tight_loop_contents(); + } + + /// \tag::pll_init[] + pll_init(pll_sys, PLL_COMMON_REFDIV, PLL_SYS_VCO_FREQ_KHZ * KHZ, PLL_SYS_POSTDIV1, PLL_SYS_POSTDIV2); + if (init_usb) { + pll_init(pll_usb, PLL_COMMON_REFDIV, PLL_USB_VCO_FREQ_KHZ * KHZ, PLL_USB_POSTDIV1, PLL_USB_POSTDIV2); + } + /// \end::pll_init[] + + // Configure clocks + // CLK_REF = XOSC (usually) 12MHz / 1 = 12MHz + clock_configure(clk_ref, + CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, + 0, // No aux mux + XOSC_KHZ * KHZ, + XOSC_KHZ * KHZ); + + /// \tag::configure_clk_sys[] + // CLK SYS = PLL SYS (usually) 125MHz / 1 = 125MHz + clock_configure(clk_sys, + CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, + SYS_CLK_KHZ * KHZ, + SYS_CLK_KHZ * KHZ); + /// \end::configure_clk_sys[] + + if (init_usb) { + // CLK USB = PLL USB 48MHz / 1 = 48MHz + clock_configure(clk_usb, + 0, // No GLMUX + CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, + USB_CLK_KHZ * KHZ, + USB_CLK_KHZ * KHZ); + } + + // CLK ADC = PLL USB 48MHZ / 1 = 48MHz + clock_configure(clk_adc, + 0, // No GLMUX + CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, + USB_CLK_KHZ * KHZ, + USB_CLK_KHZ * KHZ); + + // CLK RTC = PLL USB 48MHz / 1024 = 46875Hz + clock_configure(clk_rtc, + 0, // No GLMUX + CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, + USB_CLK_KHZ * KHZ, + RTC_CLOCK_FREQ_HZ); + + // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable + // Normally choose clk_sys or clk_usb + clock_configure(clk_peri, + 0, + CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, + SYS_CLK_KHZ * KHZ, + SYS_CLK_KHZ * KHZ); +} diff --git a/ports/rp2/clocks_extra.h b/ports/rp2/clocks_extra.h new file mode 100644 index 0000000000000..40f77f53bd850 --- /dev/null +++ b/ports/rp2/clocks_extra.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 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_RP2_CLOCKS_EXTRA_H +#define MICROPY_INCLUDED_RP2_CLOCKS_EXTRA_H + +#include "hardware/clocks.h" + +void clocks_init_optional_usb(bool init_usb); + +#endif // MICROPY_INCLUDED_RP2_CLOCKS_EXTRA_H From 068d9bf2cfa9110ae44008da45bfd0419cb45b52 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 25 Jun 2024 16:20:59 +1000 Subject: [PATCH 0066/1300] rp2: Fix USB PLL glitch during wake from light sleep. Follow-up to a84c7a0ed93, this commit works most of the time but has an intermittent bug where USB doesn't resume as expected after waking from light sleep. Turns out waking calls clocks_init() which will re-initialise the USB PLL. Most of the time this is OK but occasionally it seems like the clock glitches the USB peripheral and it stops working until the next hard reset. Adds a machine.lightsleep() test that consistently hangs in the first two dozen iterations on rp2 without this fix. Passed over 100 times in a row with this fix. The test is currently rp2-only as it seems similar lightsleep USB issues exist on other ports (both pyboard and ESP32-S3 native USB don't send any data to the host after waking, until they receive something from the host first.) This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/modmachine.c | 4 ++-- tests/ports/rp2/rp2_lightsleep.py | 31 +++++++++++++++++++++++++++ tests/ports/rp2/rp2_lightsleep.py.exp | 2 ++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/ports/rp2/rp2_lightsleep.py create mode 100644 tests/ports/rp2/rp2_lightsleep.py.exp diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 4eebb3d16c64e..9cc15080cdad0 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -31,7 +31,7 @@ #include "mp_usbd.h" #include "modmachine.h" #include "uart.h" -#include "hardware/clocks.h" +#include "clocks_extra.h" #include "hardware/pll.h" #include "hardware/structs/rosc.h" #include "hardware/structs/scb.h" @@ -213,7 +213,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB; // Bring back all clocks. - clocks_init(); + clocks_init_optional_usb(disable_usb); MICROPY_END_ATOMIC_SECTION(my_interrupts); } diff --git a/tests/ports/rp2/rp2_lightsleep.py b/tests/ports/rp2/rp2_lightsleep.py new file mode 100644 index 0000000000000..3587def68fcd8 --- /dev/null +++ b/tests/ports/rp2/rp2_lightsleep.py @@ -0,0 +1,31 @@ +# This test is mostly intended for ensuring USB serial stays stable over +# lightsleep, but can double as a general lightsleep test. +# +# In theory this should run on any port, but native USB REPL doesn't currently +# recover automatically on all ports. On pyboard and ESP32-S3 the host needs to +# send something to the port before it responds again. Possibly the same for other +# ports with native USB. +# +# A range of sleep periods (1 to 512ms) are tested. The total nominal sleep time +# is 10.23 seconds, but on most ports this will finish much earlier as interrupts +# happen before each timeout expires. +try: + from machine import lightsleep, Pin +except ImportError: + print("SKIP") + raise SystemExit + +from sys import stdout, platform + +try: + led = Pin(Pin.board.LED, Pin.OUT) +except AttributeError: + led = None + +for n in range(100): + if led: + led.toggle() + stdout.write(chr(ord("a") + (n % 26))) + lightsleep(2 ** (n % 10)) + +print("\nDONE") diff --git a/tests/ports/rp2/rp2_lightsleep.py.exp b/tests/ports/rp2/rp2_lightsleep.py.exp new file mode 100644 index 0000000000000..81d1c231c9a5e --- /dev/null +++ b/tests/ports/rp2/rp2_lightsleep.py.exp @@ -0,0 +1,2 @@ +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuv +DONE From f60c71d131db76b4d6a0fe465afe7783a65ab2a7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 25 Jun 2024 16:48:39 +1000 Subject: [PATCH 0067/1300] rp2: Don't disable USB if going to DORMANT mode. In this mode, XOSC is stopped so can't really keep the USB PLL enabled. Signed-off-by: Angus Gratton --- ports/rp2/modmachine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 9cc15080cdad0..2cc79369f6149 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -138,7 +138,8 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { #if MICROPY_HW_ENABLE_USBDEV // Only disable the USB clock if a USB host has not configured the device - bool disable_usb = !tud_mounted(); + // or if going to DORMANT mode. + bool disable_usb = !(tud_mounted() && n_args > 0); #else bool disable_usb = true; #endif From e35f13a22df63cfa165a5fac7f8236a0d0711e77 Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Thu, 13 Jun 2024 17:22:23 +0100 Subject: [PATCH 0068/1300] rp2/pendsv: Fix variable typo in assert so it compiles. Fixes issue #15276. Signed-off-by: Peter Harper --- ports/rp2/pendsv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/rp2/pendsv.c b/ports/rp2/pendsv.c index d24b4cb0122b3..68bb65f041c61 100644 --- a/ports/rp2/pendsv.c +++ b/ports/rp2/pendsv.c @@ -84,7 +84,7 @@ void PendSV_Handler(void) { return; } // Core 0 should not already have locked pendsv_mutex - assert(pensv_mutex.enter_count == 1); + assert(pendsv_mutex.enter_count == 1); #if MICROPY_PY_NETWORK_CYW43 CYW43_STAT_INC(PENDSV_RUN_COUNT); From 9111fa58312440fbddb4e67e9966386b3890d8d6 Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Thu, 13 Jun 2024 17:23:35 +0100 Subject: [PATCH 0069/1300] shared/tinyusb/mp_usbd_runtime: Fix pointer comparison in assert. Addresses build warning "comparison of distinct pointer types lacks a cast". Fixes issue #15276. Signed-off-by: Peter Harper --- shared/tinyusb/mp_usbd_runtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/tinyusb/mp_usbd_runtime.c b/shared/tinyusb/mp_usbd_runtime.c index e10d1cb3f62d2..fe28a4a727791 100644 --- a/shared/tinyusb/mp_usbd_runtime.c +++ b/shared/tinyusb/mp_usbd_runtime.c @@ -196,7 +196,7 @@ static uint8_t _runtime_dev_count_itfs(tusb_desc_interface_t const *itf_desc) { const tusb_desc_configuration_t *cfg_desc = (const void *)tud_descriptor_configuration_cb(0); const uint8_t *p_desc = (const void *)cfg_desc; const uint8_t *p_end = p_desc + cfg_desc->wTotalLength; - assert(p_desc <= itf_desc && itf_desc < p_end); + assert(p_desc <= (const uint8_t *)itf_desc && (const uint8_t *)itf_desc < p_end); while (p_desc != (const void *)itf_desc && p_desc < p_end) { const uint8_t *next = tu_desc_next(p_desc); From 3d93fed0aab85f5a7a64088026822ea55cc63541 Mon Sep 17 00:00:00 2001 From: Yoctopuce Date: Thu, 13 Jun 2024 11:23:57 +0200 Subject: [PATCH 0070/1300] py/objarray: Fix buffer overflow in case of memory allocation failure. If `array.append()` fails with an exception due to heap exhaustion, the next attempt to grow the buffer will cause a buffer overflow because the free slot count is increased before performing the allocation, and will stay as if the allocation succeeded. Signed-off-by: Yoctopuce --- py/objarray.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/py/objarray.c b/py/objarray.c index 803af2cd270c7..9d68aa70616ca 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -392,8 +392,9 @@ static mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg) { if (self->free == 0) { size_t item_sz = mp_binary_get_size('@', self->typecode, NULL); // TODO: alloc policy - self->free = 8; - self->items = m_renew(byte, self->items, item_sz * self->len, item_sz * (self->len + self->free)); + size_t add_cnt = 8; + self->items = m_renew(byte, self->items, item_sz * self->len, item_sz * (self->len + add_cnt)); + self->free = add_cnt; mp_seq_clear(self->items, self->len + 1, self->len + self->free, item_sz); } mp_binary_set_val_array(self->typecode, self->items, self->len, arg); From 206dc09456727415248c8d10f60b0121d391d38c Mon Sep 17 00:00:00 2001 From: tharuka <78165134+tharuka-pavith@users.noreply.github.com> Date: Fri, 3 May 2024 10:41:05 +0530 Subject: [PATCH 0071/1300] README: Clean up a few bits of grammar. The word "Select" could be confusing in this context, eg it could be misunderstood as the `select` module. Signed-off-by: tharuka <78165134+tharuka-pavith@users.noreply.github.com> --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a20e0aaf13a28..02e0b3abb357b 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Python 3.5 and some select features from later versions). The following core datatypes are provided: `str`(including basic Unicode support), `bytes`, `bytearray`, `tuple`, `list`, `dict`, `set`, `frozenset`, `array.array`, `collections.namedtuple`, classes and instances. Builtin modules include -`os`, `sys`, `time`, `re`, and `struct`, etc. Select ports have support for +`os`, `sys`, `time`, `re`, and `struct`, etc. Some ports have support for `_thread` module (multithreading), `socket` and `ssl` for networking, and `asyncio`. Note that only a subset of Python 3 functionality is implemented for the data types and modules. @@ -35,8 +35,8 @@ DAC, PWM, SPI, I2C, CAN, Bluetooth, and USB. Getting started --------------- -See the [online documentation](https://docs.micropython.org/) for API -references and information about using MicroPython and information about how +See the [online documentation](https://docs.micropython.org/) for the API +reference and information about using MicroPython and information about how it is implemented. We use [GitHub Discussions](https://github.com/micropython/micropython/discussions) From 0c28a5ab068e2cfd25bc7706019c4c695ba78f1e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 11 Jun 2024 11:06:48 +1000 Subject: [PATCH 0072/1300] github: Add Pull Request template. Provides pull request submitters with contributor documentation, and prompts them to provide relevant information about testing, and how they came to implement this change. Sections are deliberately small so they don't crowd out the GitHub Pull Request description text field. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- .github/pull_request_template.md | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000000..e11cebddb3710 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,33 @@ + + +### Summary + + + + +### Testing + + + + +### Trade-offs and Alternatives + + + From 880f7bc0406a1ef249c833e1a88c023cd339ecf6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 24 Jun 2024 15:54:54 +1000 Subject: [PATCH 0073/1300] shared/tinyusb/mp_usbd_cdc: Fix short CDC TX timeouts. The `mp_event_wait_ms()` function may return earlier than the requested timeout, and if that happens repeatedly (eg due to lots of USB data and IRQs) then the loop waiting for CDC TX FIFO space to become available may exit much earlier than MICROPY_HW_USB_CDC_TX_TIMEOUT, even when there is no space. Fix this by using `mp_hal_ticks_ms()` to compute a more accurate timeout. The `basics/int_big_mul.py` test fails on RPI_PICO without this fix. Signed-off-by: Damien George --- shared/tinyusb/mp_usbd_cdc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/tinyusb/mp_usbd_cdc.c b/shared/tinyusb/mp_usbd_cdc.c index 674d6b089947e..63d015cb461f5 100644 --- a/shared/tinyusb/mp_usbd_cdc.c +++ b/shared/tinyusb/mp_usbd_cdc.c @@ -102,9 +102,9 @@ mp_uint_t mp_usbd_cdc_tx_strn(const char *str, mp_uint_t len) { n = CFG_TUD_CDC_EP_BUFSIZE; } if (tud_cdc_connected()) { - int timeout = 0; // If CDC port is connected but the buffer is full, wait for up to USC_CDC_TIMEOUT ms. - while (n > tud_cdc_write_available() && timeout++ < MICROPY_HW_USB_CDC_TX_TIMEOUT) { + mp_uint_t t0 = mp_hal_ticks_ms(); + while (n > tud_cdc_write_available() && (mp_uint_t)(mp_hal_ticks_ms() - t0) < MICROPY_HW_USB_CDC_TX_TIMEOUT) { mp_event_wait_ms(1); // Explicitly run the USB stack as the scheduler may be locked (eg we From 74f52374e49e3b2370c5fec0e9cfbadc7ec8121e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 27 Jun 2024 11:17:59 +1000 Subject: [PATCH 0074/1300] extmod/extmod.mk: Disable maybe-uninitialized warnings in libm_dbl. These warnings are emitted by arm-none-eabi-gcc 14.1.0 with -O2 enabled. Signed-off-by: Damien George --- extmod/extmod.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/extmod/extmod.mk b/extmod/extmod.mk index f7c6f9988e675..2207c21f0d8e9 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -167,6 +167,7 @@ SRC_LIB_LIBM_DBL_SQRT_HW_C += lib/libm_dbl/thumb_vfp_sqrt.c # Too many warnings in libm_dbl, disable for now. $(BUILD)/lib/libm_dbl/%.o: CFLAGS += -Wno-double-promotion -Wno-float-conversion +$(BUILD)/lib/libm_dbl/__rem_pio2_large.o: CFLAGS += -Wno-maybe-uninitialized ################################################################################ # VFS FAT FS From 096adca0c88cd779b3e5e391e42db1d46f87bba1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 27 Jun 2024 11:19:16 +1000 Subject: [PATCH 0075/1300] stm32/pin: Decrease machine_pin_obj_t.pin width from 5 to 4 bits. Compiling using arm-none-eabi-gcc 14.1.0 with -O2 will give warnings about possible overflow indexing extint arrays, such as `pyb_extint_callback`. This is due to `machine_pin_obj_t.pin` having a bit-width of 5, and so a possible value up to 31, which is usually larger than `PYB_EXTI_NUM_VECTORS`. To fix this, change `machine_pin_obj_t.pin` to a bit-width of 4. Only 4 bits are needed for ST MCUs, which have up to 16 pins per port. Signed-off-by: Damien George --- ports/stm32/pin.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/pin.h b/ports/stm32/pin.h index 04479bd042f71..33f34ebcc2838 100644 --- a/ports/stm32/pin.h +++ b/ports/stm32/pin.h @@ -45,8 +45,8 @@ typedef struct { typedef struct { mp_obj_base_t base; qstr name; - uint32_t port : 4; - uint32_t pin : 5; // Some ARM processors use 32 bits/PORT + uint32_t port : 4; // Allows GPIOA through GPIOP + uint32_t pin : 4; // ST MCUs have a maximum of 16 pins per port uint32_t num_af : 4; uint32_t adc_channel : 5; // Some ARM processors use 32 bits/PORT uint32_t adc_num : 3; // 1 bit per ADC From 706a4b44777280372ab5f77dd548f3764e82dbae Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 27 Jun 2024 11:24:00 +1000 Subject: [PATCH 0076/1300] tools/ci.sh: Build an stm32 board with -O2 enabled. To test building with full optimisations. Signed-off-by: Damien George --- tools/ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci.sh b/tools/ci.sh index 7ba4ac4e57532..9c54d55a4e846 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -355,7 +355,7 @@ function ci_stm32_pyb_build { git submodule update --init lib/mynewt-nimble make ${MAKEOPTS} -C ports/stm32 BOARD=PYBV11 MICROPY_PY_NETWORK_WIZNET5K=5200 USER_C_MODULES=../../examples/usercmodule make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF2 - make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF6 NANBOX=1 MICROPY_BLUETOOTH_NIMBLE=0 MICROPY_BLUETOOTH_BTSTACK=1 + make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF6 COPT=-O2 NANBOX=1 MICROPY_BLUETOOTH_NIMBLE=0 MICROPY_BLUETOOTH_BTSTACK=1 make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBV10 CFLAGS_EXTRA='-DMBOOT_FSLOAD=1 -DMBOOT_VFS_LFS2=1' make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBD_SF6 make ${MAKEOPTS} -C ports/stm32/mboot BOARD=STM32F769DISC CFLAGS_EXTRA='-DMBOOT_ADDRESS_SPACE_64BIT=1 -DMBOOT_SDCARD_ADDR=0x100000000ULL -DMBOOT_SDCARD_BYTE_SIZE=0x400000000ULL -DMBOOT_FSLOAD=1 -DMBOOT_VFS_FAT=1' From b4213c9c920afb8da5d3d123b26527e26dceb877 Mon Sep 17 00:00:00 2001 From: Yoctopuce Date: Wed, 26 Jun 2024 10:10:32 +0200 Subject: [PATCH 0077/1300] tools/mpy-tool.py: Implement freezing of long-long ints. Allow inclusion of large integer constants in frozen files using long-long representation (mpy-cross option -mlongint-impl=longlong). Signed-off-by: Yoctopuce --- tools/mpy-tool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 3b06572c3a308..5007bbbaca013 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -735,8 +735,8 @@ def freeze_constant_obj(self, obj_name, obj): elif config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_NONE: raise FreezeError(self, "target does not support long int") elif config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_LONGLONG: - # TODO - raise FreezeError(self, "freezing int to long-long is not implemented") + print("static const mp_obj_int_t %s = {{&mp_type_int}, %d};" % (obj_name, obj)) + return "MP_ROM_PTR(&%s)" % obj_name elif config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_MPZ: neg = 0 if obj < 0: From 3af1425be7a786f79fe02902ca2f6079bacde809 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Sun, 23 Jun 2024 14:17:47 +0200 Subject: [PATCH 0078/1300] tools/mpremote: Fix mpremote mip install with multiple lib in sys.path. This is a fix for an algorithmic error in mpremote mip, that throws an error due to a '\n' used in the concatenation and split when there is more than one lib path in `sys.path`. Signed-off-by: Jos Verlinde --- tools/mpremote/mpremote/mip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mpremote/mpremote/mip.py b/tools/mpremote/mpremote/mip.py index 46c9686ad1987..c86bf01f4f9d8 100644 --- a/tools/mpremote/mpremote/mip.py +++ b/tools/mpremote/mpremote/mip.py @@ -179,9 +179,9 @@ def do_mip(state, args): if args.target is None: state.transport.exec("import sys") lib_paths = ( - state.transport.eval("'\\n'.join(p for p in sys.path if p.endswith('/lib'))") + state.transport.eval("'|'.join(p for p in sys.path if p.endswith('/lib'))") .decode() - .split("\n") + .split("|") ) if lib_paths and lib_paths[0]: args.target = lib_paths[0] From 43ebbec0c5c7a1b404ffdc738a8ab5f6fa0250cd Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 5 Jun 2024 22:48:16 +1000 Subject: [PATCH 0079/1300] esp32: Rework board variant support to require mpconfigvariant file. This commit reworks board variants on the esp32 port. It's a simple change that moves the board variant configuration from an "if" statement within `mpconfigboard.cmake` into separate files for each variant, with the name of the variant encoded in the filename: `mpconfigvariant_VARIANT.cmake`. Optionally, the base variant can have its own options in `mpconfigvariant.cmake` (this is an optional file, but all other variants of the base must have a corresponding mpconfigvariant file). There are two benefits to this: - The build system now gives an error if the variant that you specified doesn't exist (because the mpconfigvariant file must exist with the variant name you specify). - No more error-prone if-logic needed in the .cmake files. The way to build a variant is unchanged, still via: $ make BOARD_VARIANT=VARIANT Signed-off-by: Damien George --- ports/esp32/CMakeLists.txt | 12 +++++ .../boards/ESP32_GENERIC/mpconfigboard.cmake | 47 ------------------- .../ESP32_GENERIC/mpconfigvariant_D2WD.cmake | 11 +++++ .../ESP32_GENERIC/mpconfigvariant_OTA.cmake | 8 ++++ .../mpconfigvariant_SPIRAM.cmake | 8 ++++ .../mpconfigvariant_UNICORE.cmake | 8 ++++ .../ESP32_GENERIC_S3/mpconfigboard.cmake | 19 -------- .../mpconfigvariant_FLASH_4M.cmake | 4 ++ .../mpconfigvariant_SPIRAM_OCT.cmake | 9 ++++ 9 files changed, 60 insertions(+), 66 deletions(-) create mode 100644 ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_D2WD.cmake create mode 100644 ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_OTA.cmake create mode 100644 ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_SPIRAM.cmake create mode 100644 ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_UNICORE.cmake create mode 100644 ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake create mode 100644 ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_SPIRAM_OCT.cmake diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt index 3fe0830025579..d3927cd48d82a 100644 --- a/ports/esp32/CMakeLists.txt +++ b/ports/esp32/CMakeLists.txt @@ -19,6 +19,13 @@ if(NOT EXISTS ${MICROPY_BOARD_DIR}/mpconfigboard.cmake) message(FATAL_ERROR "Invalid MICROPY_BOARD specified: ${MICROPY_BOARD}") endif() +# If a board variant is specified, check that it exists. +if(MICROPY_BOARD_VARIANT) + if(NOT EXISTS ${MICROPY_BOARD_DIR}/mpconfigvariant_${MICROPY_BOARD_VARIANT}.cmake) + message(FATAL_ERROR "Invalid MICROPY_BOARD_VARIANT specified: ${MICROPY_BOARD_VARIANT}") + endif() +endif() + # Define the output sdkconfig so it goes in the build directory. set(SDKCONFIG ${CMAKE_BINARY_DIR}/sdkconfig) @@ -35,6 +42,11 @@ endif() # - SDKCONFIG_DEFAULTS # - IDF_TARGET include(${MICROPY_BOARD_DIR}/mpconfigboard.cmake) +if(NOT MICROPY_BOARD_VARIANT) + include(${MICROPY_BOARD_DIR}/mpconfigvariant.cmake OPTIONAL) +else() + include(${MICROPY_BOARD_DIR}/mpconfigvariant_${MICROPY_BOARD_VARIANT}.cmake) +endif() # Set the frozen manifest file. Note if MICROPY_FROZEN_MANIFEST is set from the cmake # command line, then it will override the default and any manifest set by the board. diff --git a/ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake b/ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake index a3e8f616bc009..9d0077a15e8d2 100644 --- a/ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake +++ b/ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake @@ -3,50 +3,3 @@ set(SDKCONFIG_DEFAULTS ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.ble ) - -if(MICROPY_BOARD_VARIANT STREQUAL "D2WD") - set(SDKCONFIG_DEFAULTS - ${SDKCONFIG_DEFAULTS} - boards/ESP32_GENERIC/sdkconfig.d2wd - ) - - list(APPEND MICROPY_DEF_BOARD - MICROPY_HW_MCU_NAME="ESP32-D2WD" - # Disable some options to reduce firmware size. - MICROPY_OPT_COMPUTED_GOTO=0 - MICROPY_PY_NETWORK_LAN=0 - ) -endif() - -if(MICROPY_BOARD_VARIANT STREQUAL "OTA") - set(SDKCONFIG_DEFAULTS - ${SDKCONFIG_DEFAULTS} - boards/ESP32_GENERIC/sdkconfig.ota - ) - - list(APPEND MICROPY_DEF_BOARD - MICROPY_HW_BOARD_NAME="Generic ESP32 module with OTA" - ) -endif() - -if(MICROPY_BOARD_VARIANT STREQUAL "SPIRAM") - set(SDKCONFIG_DEFAULTS - ${SDKCONFIG_DEFAULTS} - boards/sdkconfig.spiram - ) - - list(APPEND MICROPY_DEF_BOARD - MICROPY_HW_BOARD_NAME="Generic ESP32 module with SPIRAM" - ) -endif() - -if(MICROPY_BOARD_VARIANT STREQUAL "UNICORE") - set(SDKCONFIG_DEFAULTS - ${SDKCONFIG_DEFAULTS} - boards/ESP32_GENERIC/sdkconfig.unicore - ) - - list(APPEND MICROPY_DEF_BOARD - MICROPY_HW_MCU_NAME="ESP32-UNICORE" - ) -endif() diff --git a/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_D2WD.cmake b/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_D2WD.cmake new file mode 100644 index 0000000000000..170c214bda073 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_D2WD.cmake @@ -0,0 +1,11 @@ +set(SDKCONFIG_DEFAULTS + ${SDKCONFIG_DEFAULTS} + boards/ESP32_GENERIC/sdkconfig.d2wd +) + +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_MCU_NAME="ESP32-D2WD" + # Disable some options to reduce firmware size. + MICROPY_OPT_COMPUTED_GOTO=0 + MICROPY_PY_NETWORK_LAN=0 +) diff --git a/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_OTA.cmake b/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_OTA.cmake new file mode 100644 index 0000000000000..7f649c36f6cc5 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_OTA.cmake @@ -0,0 +1,8 @@ +set(SDKCONFIG_DEFAULTS + ${SDKCONFIG_DEFAULTS} + boards/ESP32_GENERIC/sdkconfig.ota +) + +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_BOARD_NAME="Generic ESP32 module with OTA" +) diff --git a/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_SPIRAM.cmake b/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_SPIRAM.cmake new file mode 100644 index 0000000000000..11e9c4eda8188 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_SPIRAM.cmake @@ -0,0 +1,8 @@ +set(SDKCONFIG_DEFAULTS + ${SDKCONFIG_DEFAULTS} + boards/sdkconfig.spiram +) + +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_BOARD_NAME="Generic ESP32 module with SPIRAM" +) diff --git a/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_UNICORE.cmake b/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_UNICORE.cmake new file mode 100644 index 0000000000000..beed6eb09c781 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC/mpconfigvariant_UNICORE.cmake @@ -0,0 +1,8 @@ +set(SDKCONFIG_DEFAULTS + ${SDKCONFIG_DEFAULTS} + boards/ESP32_GENERIC/sdkconfig.unicore +) + +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_MCU_NAME="ESP32-UNICORE" +) diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake index 1f7440353b6cc..2bfdad21df495 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake +++ b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake @@ -8,22 +8,3 @@ set(SDKCONFIG_DEFAULTS boards/sdkconfig.spiram_sx boards/ESP32_GENERIC_S3/sdkconfig.board ) - -if(MICROPY_BOARD_VARIANT STREQUAL "SPIRAM_OCT") - set(SDKCONFIG_DEFAULTS - ${SDKCONFIG_DEFAULTS} - boards/sdkconfig.240mhz - boards/sdkconfig.spiram_oct - ) - - list(APPEND MICROPY_DEF_BOARD - MICROPY_HW_BOARD_NAME="Generic ESP32S3 module with Octal-SPIRAM" - ) -endif() - -if(MICROPY_BOARD_VARIANT STREQUAL "FLASH_4M") - set(SDKCONFIG_DEFAULTS - ${SDKCONFIG_DEFAULTS} - boards/ESP32_GENERIC_S3/sdkconfig.flash_4m - ) -endif() diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake new file mode 100644 index 0000000000000..e832cdb60fd72 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake @@ -0,0 +1,4 @@ +set(SDKCONFIG_DEFAULTS + ${SDKCONFIG_DEFAULTS} + boards/ESP32_GENERIC_S3/sdkconfig.flash_4m +) diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_SPIRAM_OCT.cmake b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_SPIRAM_OCT.cmake new file mode 100644 index 0000000000000..8b2eda27242c3 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_SPIRAM_OCT.cmake @@ -0,0 +1,9 @@ +set(SDKCONFIG_DEFAULTS + ${SDKCONFIG_DEFAULTS} + boards/sdkconfig.240mhz + boards/sdkconfig.spiram_oct +) + +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_BOARD_NAME="Generic ESP32S3 module with Octal-SPIRAM" +) From daa948fe05ee165a00a02f0b529c15601fe5ac5f Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 5 Jun 2024 23:09:09 +1000 Subject: [PATCH 0080/1300] esp8266: Rework board variant support to require mpconfigvariant file. Following how esp32 has been reworked, each variant now has a corresponding `mpconfigvariant_VARIANT.mk` file associated with it. The base variant also has a `mpconfigvariant.mk` file because it has options that none of the other variants use. Signed-off-by: Damien George --- ports/esp8266/Makefile | 15 +++++- .../boards/ESP8266_GENERIC/mpconfigboard.mk | 51 +------------------ .../boards/ESP8266_GENERIC/mpconfigvariant.mk | 12 +++++ .../mpconfigvariant_FLASH_1M.mk | 10 ++++ .../mpconfigvariant_FLASH_512K.mk | 7 +++ .../ESP8266_GENERIC/mpconfigvariant_OTA.mk | 10 ++++ 6 files changed, 53 insertions(+), 52 deletions(-) create mode 100644 ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant.mk create mode 100644 ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_1M.mk create mode 100644 ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_512K.mk create mode 100644 ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_OTA.mk diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index 1b9e9623b6297..cd94a38a726e3 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -16,6 +16,12 @@ endif $(error Invalid BOARD specified: $(BOARD_DIR)) endif +ifneq ($(BOARD_VARIANT),) +ifeq ($(wildcard $(BOARD_DIR)/mpconfigvariant_$(BOARD_VARIANT).mk),) +$(error Invalid BOARD_VARIANT specified: $(BOARD_VARIANT)) +endif +endif + # If the build directory is not given, make it reflect the board name (and # optionally the board variant). ifneq ($(BOARD_VARIANT),) @@ -26,8 +32,13 @@ endif include ../../py/mkenv.mk -# Optional --include $(BOARD_DIR)/mpconfigboard.mk +# Include board specific .mk file, and optional board variant .mk file. +include $(BOARD_DIR)/mpconfigboard.mk +ifeq ($(BOARD_VARIANT),) +-include $(BOARD_DIR)/mpconfigvariant.mk +else +include $(BOARD_DIR)/mpconfigvariant_$(BOARD_VARIANT).mk +endif # qstr definitions (must come before including py.mk) QSTR_DEFS = qstrdefsport.h #$(BUILD)/pins_qstr.h diff --git a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.mk b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.mk index 69dbeb5fb9988..a9d8908c38bdf 100644 --- a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.mk +++ b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.mk @@ -1,50 +1 @@ -ifeq ($(BOARD_VARIANT),) -LD_FILES = boards/esp8266_2MiB.ld - -MICROPY_PY_ESPNOW ?= 1 -MICROPY_PY_BTREE ?= 1 -MICROPY_VFS_FAT ?= 1 -MICROPY_VFS_LFS2 ?= 1 - -# Add asyncio and extra micropython-lib packages (in addition to the port manifest). -FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest_2MiB.py - -# Configure mpconfigboard.h. -CFLAGS += -DMICROPY_ESP8266_2M -endif - -ifeq ($(BOARD_VARIANT),FLASH_1M) -LD_FILES = boards/esp8266_1MiB.ld - -MICROPY_PY_ESPNOW ?= 1 -MICROPY_PY_BTREE ?= 1 -MICROPY_VFS_LFS2 ?= 1 - -# Note: Implicitly uses the port manifest. - -# Configure mpconfigboard.h. -CFLAGS += -DMICROPY_ESP8266_1M -endif - -ifeq ($(BOARD_VARIANT),OTA) -LD_FILES = boards/esp8266_ota.ld - -MICROPY_PY_ESPNOW ?= 1 -MICROPY_PY_BTREE ?= 1 -MICROPY_VFS_LFS2 ?= 1 - -# Note: Implicitly uses the port manifest. - -# Configure mpconfigboard.h. -CFLAGS += -DMICROPY_ESP8266_1M -endif - -ifeq ($(BOARD_VARIANT),FLASH_512K) -LD_FILES = boards/esp8266_512kiB.ld - -# Note: Use the minimal manifest.py. -FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest_512kiB.py - -# Configure mpconfigboard.h. -CFLAGS += -DMICROPY_ESP8266_512K -endif +# There are no common .mk settings among the variants, so this file is empty. diff --git a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant.mk b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant.mk new file mode 100644 index 0000000000000..926fc15dab55a --- /dev/null +++ b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant.mk @@ -0,0 +1,12 @@ +LD_FILES = boards/esp8266_2MiB.ld + +MICROPY_PY_ESPNOW ?= 1 +MICROPY_PY_BTREE ?= 1 +MICROPY_VFS_FAT ?= 1 +MICROPY_VFS_LFS2 ?= 1 + +# Add asyncio and extra micropython-lib packages (in addition to the port manifest). +FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest_2MiB.py + +# Configure mpconfigboard.h. +CFLAGS += -DMICROPY_ESP8266_2M diff --git a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_1M.mk b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_1M.mk new file mode 100644 index 0000000000000..6baad11470976 --- /dev/null +++ b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_1M.mk @@ -0,0 +1,10 @@ +LD_FILES = boards/esp8266_1MiB.ld + +MICROPY_PY_ESPNOW ?= 1 +MICROPY_PY_BTREE ?= 1 +MICROPY_VFS_LFS2 ?= 1 + +# Note: Implicitly uses the port manifest. + +# Configure mpconfigboard.h. +CFLAGS += -DMICROPY_ESP8266_1M diff --git a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_512K.mk b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_512K.mk new file mode 100644 index 0000000000000..bbbdbb9091dee --- /dev/null +++ b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_512K.mk @@ -0,0 +1,7 @@ +LD_FILES = boards/esp8266_512kiB.ld + +# Note: Use the minimal manifest.py. +FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest_512kiB.py + +# Configure mpconfigboard.h. +CFLAGS += -DMICROPY_ESP8266_512K diff --git a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_OTA.mk b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_OTA.mk new file mode 100644 index 0000000000000..759eab3dc750b --- /dev/null +++ b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_OTA.mk @@ -0,0 +1,10 @@ +LD_FILES = boards/esp8266_ota.ld + +MICROPY_PY_ESPNOW ?= 1 +MICROPY_PY_BTREE ?= 1 +MICROPY_VFS_LFS2 ?= 1 + +# Note: Implicitly uses the port manifest. + +# Configure mpconfigboard.h. +CFLAGS += -DMICROPY_ESP8266_1M From 81b1bfcfef67577707691a3941879c63df619dcd Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 13 Jun 2024 13:23:43 +1000 Subject: [PATCH 0081/1300] stm32: Rework board variant support to require mpconfigvariant file. Following how the board variants now work in the esp8266 port. Signed-off-by: Damien George --- ports/stm32/Makefile | 13 +++++++++++++ ports/stm32/boards/PYBLITEV10/mpconfigboard.mk | 18 ------------------ .../boards/PYBLITEV10/mpconfigvariant_DP.mk | 1 + .../PYBLITEV10/mpconfigvariant_DP_THREAD.mk | 2 ++ .../PYBLITEV10/mpconfigvariant_NETWORK.mk | 1 + .../PYBLITEV10/mpconfigvariant_THREAD.mk | 1 + ports/stm32/boards/PYBV10/mpconfigboard.mk | 18 ------------------ .../stm32/boards/PYBV10/mpconfigvariant_DP.mk | 1 + .../boards/PYBV10/mpconfigvariant_DP_THREAD.mk | 2 ++ .../boards/PYBV10/mpconfigvariant_NETWORK.mk | 1 + .../boards/PYBV10/mpconfigvariant_THREAD.mk | 1 + ports/stm32/boards/PYBV11/mpconfigboard.mk | 18 ------------------ .../stm32/boards/PYBV11/mpconfigvariant_DP.mk | 1 + .../boards/PYBV11/mpconfigvariant_DP_THREAD.mk | 2 ++ .../boards/PYBV11/mpconfigvariant_NETWORK.mk | 1 + .../boards/PYBV11/mpconfigvariant_THREAD.mk | 1 + 16 files changed, 28 insertions(+), 54 deletions(-) create mode 100644 ports/stm32/boards/PYBLITEV10/mpconfigvariant_DP.mk create mode 100644 ports/stm32/boards/PYBLITEV10/mpconfigvariant_DP_THREAD.mk create mode 100644 ports/stm32/boards/PYBLITEV10/mpconfigvariant_NETWORK.mk create mode 100644 ports/stm32/boards/PYBLITEV10/mpconfigvariant_THREAD.mk create mode 100644 ports/stm32/boards/PYBV10/mpconfigvariant_DP.mk create mode 100644 ports/stm32/boards/PYBV10/mpconfigvariant_DP_THREAD.mk create mode 100644 ports/stm32/boards/PYBV10/mpconfigvariant_NETWORK.mk create mode 100644 ports/stm32/boards/PYBV10/mpconfigvariant_THREAD.mk create mode 100644 ports/stm32/boards/PYBV11/mpconfigvariant_DP.mk create mode 100644 ports/stm32/boards/PYBV11/mpconfigvariant_DP_THREAD.mk create mode 100644 ports/stm32/boards/PYBV11/mpconfigvariant_NETWORK.mk create mode 100644 ports/stm32/boards/PYBV11/mpconfigvariant_THREAD.mk diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 50ac48e5a18eb..497e409c66563 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -13,6 +13,12 @@ ifeq ($(wildcard $(BOARD_DIR)/.),) $(error Invalid BOARD specified: $(BOARD_DIR)) endif +ifneq ($(BOARD_VARIANT),) +ifeq ($(wildcard $(BOARD_DIR)/mpconfigvariant_$(BOARD_VARIANT).mk),) +$(error Invalid BOARD_VARIANT specified: $(BOARD_VARIANT)) +endif +endif + # If the build directory is not given, make it reflect the board name (and # optionally the board variant). ifneq ($(BOARD_VARIANT),) @@ -23,7 +29,14 @@ endif include ../../py/mkenv.mk -include mpconfigport.mk + +# Include board specific .mk file, and optional board variant .mk file. include $(BOARD_DIR)/mpconfigboard.mk +ifeq ($(BOARD_VARIANT),) +-include $(BOARD_DIR)/mpconfigvariant.mk +else +include $(BOARD_DIR)/mpconfigvariant_$(BOARD_VARIANT).mk +endif # qstr definitions (must come before including py.mk) QSTR_DEFS += qstrdefsport.h diff --git a/ports/stm32/boards/PYBLITEV10/mpconfigboard.mk b/ports/stm32/boards/PYBLITEV10/mpconfigboard.mk index 1fe609cbb5849..603fa8865e2a0 100644 --- a/ports/stm32/boards/PYBLITEV10/mpconfigboard.mk +++ b/ports/stm32/boards/PYBLITEV10/mpconfigboard.mk @@ -5,23 +5,5 @@ LD_FILES = boards/stm32f411.ld boards/common_ifs.ld TEXT0_ADDR = 0x08000000 TEXT1_ADDR = 0x08020000 -# Provide different variants for the downloads page. -ifeq ($(BOARD_VARIANT),DP) -MICROPY_FLOAT_IMPL=double -endif - -ifeq ($(BOARD_VARIANT),THREAD) -CFLAGS += -DMICROPY_PY_THREAD=1 -endif - -ifeq ($(BOARD_VARIANT),DP_THREAD) -MICROPY_FLOAT_IMPL=double -CFLAGS += -DMICROPY_PY_THREAD=1 -endif - -ifeq ($(BOARD_VARIANT),NETWORK) -MICROPY_PY_NETWORK_WIZNET5K=5200 -endif - # PYB-specific frozen modules FROZEN_MANIFEST ?= boards/PYBV10/manifest.py diff --git a/ports/stm32/boards/PYBLITEV10/mpconfigvariant_DP.mk b/ports/stm32/boards/PYBLITEV10/mpconfigvariant_DP.mk new file mode 100644 index 0000000000000..73b32b4901dbf --- /dev/null +++ b/ports/stm32/boards/PYBLITEV10/mpconfigvariant_DP.mk @@ -0,0 +1 @@ +MICROPY_FLOAT_IMPL = double diff --git a/ports/stm32/boards/PYBLITEV10/mpconfigvariant_DP_THREAD.mk b/ports/stm32/boards/PYBLITEV10/mpconfigvariant_DP_THREAD.mk new file mode 100644 index 0000000000000..af6ec87ecfc12 --- /dev/null +++ b/ports/stm32/boards/PYBLITEV10/mpconfigvariant_DP_THREAD.mk @@ -0,0 +1,2 @@ +MICROPY_FLOAT_IMPL = double +CFLAGS += -DMICROPY_PY_THREAD=1 diff --git a/ports/stm32/boards/PYBLITEV10/mpconfigvariant_NETWORK.mk b/ports/stm32/boards/PYBLITEV10/mpconfigvariant_NETWORK.mk new file mode 100644 index 0000000000000..2e2988aa93056 --- /dev/null +++ b/ports/stm32/boards/PYBLITEV10/mpconfigvariant_NETWORK.mk @@ -0,0 +1 @@ +MICROPY_PY_NETWORK_WIZNET5K = 5200 diff --git a/ports/stm32/boards/PYBLITEV10/mpconfigvariant_THREAD.mk b/ports/stm32/boards/PYBLITEV10/mpconfigvariant_THREAD.mk new file mode 100644 index 0000000000000..eab5c1c0d2333 --- /dev/null +++ b/ports/stm32/boards/PYBLITEV10/mpconfigvariant_THREAD.mk @@ -0,0 +1 @@ +CFLAGS += -DMICROPY_PY_THREAD=1 diff --git a/ports/stm32/boards/PYBV10/mpconfigboard.mk b/ports/stm32/boards/PYBV10/mpconfigboard.mk index 1a4d6763193ae..38f0b55f6c7c9 100644 --- a/ports/stm32/boards/PYBV10/mpconfigboard.mk +++ b/ports/stm32/boards/PYBV10/mpconfigboard.mk @@ -15,23 +15,5 @@ endif # MicroPython settings MICROPY_VFS_LFS2 = 1 -# Provide different variants for the downloads page. -ifeq ($(BOARD_VARIANT),DP) -MICROPY_FLOAT_IMPL=double -endif - -ifeq ($(BOARD_VARIANT),THREAD) -CFLAGS += -DMICROPY_PY_THREAD=1 -endif - -ifeq ($(BOARD_VARIANT),DP_THREAD) -MICROPY_FLOAT_IMPL=double -CFLAGS += -DMICROPY_PY_THREAD=1 -endif - -ifeq ($(BOARD_VARIANT),NETWORK) -MICROPY_PY_NETWORK_WIZNET5K=5200 -endif - # PYB-specific frozen modules FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/PYBV10/mpconfigvariant_DP.mk b/ports/stm32/boards/PYBV10/mpconfigvariant_DP.mk new file mode 100644 index 0000000000000..73b32b4901dbf --- /dev/null +++ b/ports/stm32/boards/PYBV10/mpconfigvariant_DP.mk @@ -0,0 +1 @@ +MICROPY_FLOAT_IMPL = double diff --git a/ports/stm32/boards/PYBV10/mpconfigvariant_DP_THREAD.mk b/ports/stm32/boards/PYBV10/mpconfigvariant_DP_THREAD.mk new file mode 100644 index 0000000000000..af6ec87ecfc12 --- /dev/null +++ b/ports/stm32/boards/PYBV10/mpconfigvariant_DP_THREAD.mk @@ -0,0 +1,2 @@ +MICROPY_FLOAT_IMPL = double +CFLAGS += -DMICROPY_PY_THREAD=1 diff --git a/ports/stm32/boards/PYBV10/mpconfigvariant_NETWORK.mk b/ports/stm32/boards/PYBV10/mpconfigvariant_NETWORK.mk new file mode 100644 index 0000000000000..2e2988aa93056 --- /dev/null +++ b/ports/stm32/boards/PYBV10/mpconfigvariant_NETWORK.mk @@ -0,0 +1 @@ +MICROPY_PY_NETWORK_WIZNET5K = 5200 diff --git a/ports/stm32/boards/PYBV10/mpconfigvariant_THREAD.mk b/ports/stm32/boards/PYBV10/mpconfigvariant_THREAD.mk new file mode 100644 index 0000000000000..eab5c1c0d2333 --- /dev/null +++ b/ports/stm32/boards/PYBV10/mpconfigvariant_THREAD.mk @@ -0,0 +1 @@ +CFLAGS += -DMICROPY_PY_THREAD=1 diff --git a/ports/stm32/boards/PYBV11/mpconfigboard.mk b/ports/stm32/boards/PYBV11/mpconfigboard.mk index 6dcb2fd1b4cd0..89e4a1d8a4d2a 100644 --- a/ports/stm32/boards/PYBV11/mpconfigboard.mk +++ b/ports/stm32/boards/PYBV11/mpconfigboard.mk @@ -15,23 +15,5 @@ endif # MicroPython settings MICROPY_VFS_LFS2 = 1 -# Provide different variants for the downloads page. -ifeq ($(BOARD_VARIANT),DP) -MICROPY_FLOAT_IMPL=double -endif - -ifeq ($(BOARD_VARIANT),THREAD) -CFLAGS += -DMICROPY_PY_THREAD=1 -endif - -ifeq ($(BOARD_VARIANT),DP_THREAD) -MICROPY_FLOAT_IMPL=double -CFLAGS += -DMICROPY_PY_THREAD=1 -endif - -ifeq ($(BOARD_VARIANT),NETWORK) -MICROPY_PY_NETWORK_WIZNET5K=5200 -endif - # PYB-specific frozen modules FROZEN_MANIFEST ?= boards/PYBV10/manifest.py diff --git a/ports/stm32/boards/PYBV11/mpconfigvariant_DP.mk b/ports/stm32/boards/PYBV11/mpconfigvariant_DP.mk new file mode 100644 index 0000000000000..73b32b4901dbf --- /dev/null +++ b/ports/stm32/boards/PYBV11/mpconfigvariant_DP.mk @@ -0,0 +1 @@ +MICROPY_FLOAT_IMPL = double diff --git a/ports/stm32/boards/PYBV11/mpconfigvariant_DP_THREAD.mk b/ports/stm32/boards/PYBV11/mpconfigvariant_DP_THREAD.mk new file mode 100644 index 0000000000000..af6ec87ecfc12 --- /dev/null +++ b/ports/stm32/boards/PYBV11/mpconfigvariant_DP_THREAD.mk @@ -0,0 +1,2 @@ +MICROPY_FLOAT_IMPL = double +CFLAGS += -DMICROPY_PY_THREAD=1 diff --git a/ports/stm32/boards/PYBV11/mpconfigvariant_NETWORK.mk b/ports/stm32/boards/PYBV11/mpconfigvariant_NETWORK.mk new file mode 100644 index 0000000000000..2e2988aa93056 --- /dev/null +++ b/ports/stm32/boards/PYBV11/mpconfigvariant_NETWORK.mk @@ -0,0 +1 @@ +MICROPY_PY_NETWORK_WIZNET5K = 5200 diff --git a/ports/stm32/boards/PYBV11/mpconfigvariant_THREAD.mk b/ports/stm32/boards/PYBV11/mpconfigvariant_THREAD.mk new file mode 100644 index 0000000000000..eab5c1c0d2333 --- /dev/null +++ b/ports/stm32/boards/PYBV11/mpconfigvariant_THREAD.mk @@ -0,0 +1 @@ +CFLAGS += -DMICROPY_PY_THREAD=1 From 5dff78f38edc0354e854e6c73af61c5064afe9d3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 24 Jun 2024 12:37:13 +1000 Subject: [PATCH 0082/1300] rp2: Rework board variant support to require mpconfigvariant file. Following how the board variants now work in the esp32 port. Signed-off-by: Damien George --- ports/rp2/CMakeLists.txt | 12 ++++++++++++ ports/rp2/boards/WEACTSTUDIO/mpconfigboard.cmake | 16 ---------------- .../rp2/boards/WEACTSTUDIO/mpconfigvariant.cmake | 1 + .../WEACTSTUDIO/mpconfigvariant_FLASH_2M.cmake | 1 + .../WEACTSTUDIO/mpconfigvariant_FLASH_4M.cmake | 1 + .../WEACTSTUDIO/mpconfigvariant_FLASH_8M.cmake | 1 + 6 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 ports/rp2/boards/WEACTSTUDIO/mpconfigvariant.cmake create mode 100644 ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_2M.cmake create mode 100644 ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_4M.cmake create mode 100644 ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_8M.cmake diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 8f5680092c925..8fff4251487ff 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -38,10 +38,22 @@ if(NOT EXISTS ${MICROPY_BOARD_DIR}/mpconfigboard.cmake) message(FATAL_ERROR "Invalid MICROPY_BOARD specified: ${MICROPY_BOARD}") endif() +# If a board variant is specified, check that it exists. +if(MICROPY_BOARD_VARIANT) + if(NOT EXISTS ${MICROPY_BOARD_DIR}/mpconfigvariant_${MICROPY_BOARD_VARIANT}.cmake) + message(FATAL_ERROR "Invalid MICROPY_BOARD_VARIANT specified: ${MICROPY_BOARD_VARIANT}") + endif() +endif() + set(MICROPY_USER_FROZEN_MANIFEST ${MICROPY_FROZEN_MANIFEST}) # Include board config, it may override MICROPY_FROZEN_MANIFEST include(${MICROPY_BOARD_DIR}/mpconfigboard.cmake) +if(NOT MICROPY_BOARD_VARIANT) + include(${MICROPY_BOARD_DIR}/mpconfigvariant.cmake OPTIONAL) +else() + include(${MICROPY_BOARD_DIR}/mpconfigvariant_${MICROPY_BOARD_VARIANT}.cmake) +endif() # Set the PICO_BOARD if it's not already set (allow a board to override it). if(NOT PICO_BOARD) diff --git a/ports/rp2/boards/WEACTSTUDIO/mpconfigboard.cmake b/ports/rp2/boards/WEACTSTUDIO/mpconfigboard.cmake index 848b50f604db6..d922644bf6265 100644 --- a/ports/rp2/boards/WEACTSTUDIO/mpconfigboard.cmake +++ b/ports/rp2/boards/WEACTSTUDIO/mpconfigboard.cmake @@ -6,19 +6,3 @@ list(APPEND PICO_BOARD_HEADER_DIRS ${MICROPY_BOARD_DIR}) # Freeze board.py set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) - -# Select the 16MB variant as the default -set(PICO_BOARD "weactstudio_16MiB") - -# Provide different variants for the downloads page -if(MICROPY_BOARD_VARIANT STREQUAL "FLASH_2M") - set(PICO_BOARD "weactstudio_2MiB") -endif() - -if(MICROPY_BOARD_VARIANT STREQUAL "FLASH_4M") - set(PICO_BOARD "weactstudio_4MiB") -endif() - -if(MICROPY_BOARD_VARIANT STREQUAL "FLASH_8M") - set(PICO_BOARD "weactstudio_8MiB") -endif() diff --git a/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant.cmake b/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant.cmake new file mode 100644 index 0000000000000..4e68b2112f1d8 --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant.cmake @@ -0,0 +1 @@ +set(PICO_BOARD "weactstudio_16MiB") diff --git a/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_2M.cmake b/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_2M.cmake new file mode 100644 index 0000000000000..b8d7202dada5d --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_2M.cmake @@ -0,0 +1 @@ +set(PICO_BOARD "weactstudio_2MiB") diff --git a/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_4M.cmake b/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_4M.cmake new file mode 100644 index 0000000000000..75afb1ba40062 --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_4M.cmake @@ -0,0 +1 @@ +set(PICO_BOARD "weactstudio_4MiB") diff --git a/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_8M.cmake b/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_8M.cmake new file mode 100644 index 0000000000000..359e47c01161e --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO/mpconfigvariant_FLASH_8M.cmake @@ -0,0 +1 @@ +set(PICO_BOARD "weactstudio_8MiB") From 95c19e05ffd204f8f375b6e04e4ae45770ec0dbc Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 24 Jun 2024 13:03:44 +1000 Subject: [PATCH 0083/1300] webassembly/objjsproxy: Lookup attributes without testing they exist. In JavaScript when accessing an attribute such as `obj.attr` a value of `undefined` is returned if the attribute does not exist. This is unlike Python semantics where an `AttributeError` is raised. Furthermore, in some cases in JavaScript (eg a Proxy instance) `attr in obj` can return false yet `obj.attr` is still valid and returns something other than `undefined`. So the source of truth for whether a JavaScript attribute exists is to just right away attempt `obj.attr`. To more closely match these JavaScript semantics when proxying a JavaScript object through to Python, change the attribute lookup logic on a `JsProxy` so that it immediately attempts `obj.attr` instead of first testing if the attribute exists via `attr in obj`. This allows JavaScript objects which dynamically create attributes to work correctly on the Python side, with both `obj.attr` and `obj["attr"]`. Note that `obj["attr"]` already works in all cases because it immediately does the subscript access without first testing if the attribute exists. As a benefit, this new behaviour matches the Pyodide behaviour. Signed-off-by: Damien George --- ports/webassembly/objjsproxy.c | 10 ++++-- .../ports/webassembly/js_proxy_attribute.mjs | 34 +++++++++++++++++++ .../webassembly/js_proxy_attribute.mjs.exp | 9 +++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 tests/ports/webassembly/js_proxy_attribute.mjs create mode 100644 tests/ports/webassembly/js_proxy_attribute.mjs.exp diff --git a/ports/webassembly/objjsproxy.c b/ports/webassembly/objjsproxy.c index cbfe8be49baf2..167d4382bc630 100644 --- a/ports/webassembly/objjsproxy.c +++ b/ports/webassembly/objjsproxy.c @@ -46,8 +46,14 @@ EM_JS(bool, has_attr, (int jsref, const char *str), { EM_JS(bool, lookup_attr, (int jsref, const char *str, uint32_t * out), { const base = proxy_js_ref[jsref]; const attr = UTF8ToString(str); - if (attr in base) { - let value = base[attr]; + + // Attempt to lookup the requested attribute from the base object: + // - If the value is not `undefined` then the attribute exists with that value. + // - Otherwise if the value is `undefined` and the `in` operator returns true, then + // that attribute does exist and is intended to have a value of `undefined`. + // - Otherwise, the attribute does not exist. + let value = base[attr]; + if (value !== undefined || attr in base) { if (typeof value === "function") { if (base !== globalThis) { if ("_ref" in value) { diff --git a/tests/ports/webassembly/js_proxy_attribute.mjs b/tests/ports/webassembly/js_proxy_attribute.mjs new file mode 100644 index 0000000000000..5d950122778bf --- /dev/null +++ b/tests/ports/webassembly/js_proxy_attribute.mjs @@ -0,0 +1,34 @@ +// Test lookup of attributes on JsProxy objects. + +const mp = await (await import(process.argv[2])).loadMicroPython(); + +// Simple attribute names and values. +globalThis.obj1 = { a: 1, b: 2 }; + +// Unconventional attribute names and values. +globalThis.obj2 = { undefined: "undefined", undef: undefined }; + +// Dynamically created attribute names and values. +globalThis.obj3 = new Proxy(new Map(), { + get(map, name) { + if (!map.has(name)) { + console.log("creating attribute", name); + map.set(name, name); + } + return map.get(name); + }, +}); + +mp.runPython(` +import js + +print(js.obj1.a, js.obj1.b) +print(js.obj1["a"], js.obj1["b"]) + +print(js.obj2.undefined, js.obj2.undef) + +print(js.obj3.c) +print(js.obj3["c"]) +print(hasattr(js.obj3, "d")) +print(js.obj3.d) +`); diff --git a/tests/ports/webassembly/js_proxy_attribute.mjs.exp b/tests/ports/webassembly/js_proxy_attribute.mjs.exp new file mode 100644 index 0000000000000..7de85a56648ab --- /dev/null +++ b/tests/ports/webassembly/js_proxy_attribute.mjs.exp @@ -0,0 +1,9 @@ +1 2 +1 2 +undefined +creating attribute c +c +c +creating attribute d +True +d From 0dd25a369e70118829b3f176151c50440286e3fe Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 28 Jun 2024 22:32:11 +1000 Subject: [PATCH 0084/1300] rp2/boards/WEACTSTUDIO: Fix variant names in board.json. It looks like the variants for this board were never being built properly, because the auto-build system used the variant name from `board.json` which did not match the variant names in the original `mpconfigboard.mk`. Eg `FLASH_2MB` in `board.json` but `FLASH_2M` in `mpconfigboard.mk`. This mistake is apparent since 5dff78f38edc0354e854e6c73af61c5064afe9d3, which made it a build error to specify an invalid variant. Fix this by using the correct variant names in `board.json`. Signed-off-by: Damien George --- ports/rp2/boards/WEACTSTUDIO/board.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/rp2/boards/WEACTSTUDIO/board.json b/ports/rp2/boards/WEACTSTUDIO/board.json index 223bbdc07b34a..8881b40e732a7 100644 --- a/ports/rp2/boards/WEACTSTUDIO/board.json +++ b/ports/rp2/boards/WEACTSTUDIO/board.json @@ -15,9 +15,9 @@ "product": "WeAct Studio RP2040", "url": "https://github.com/WeActTC/WeActStudio.RP2040CoreBoard", "variants": { - "FLASH_2MB": "2 MiB Flash", - "FLASH_4MB": "4 MiB Flash", - "FLASH_8MB": "8 MiB Flash" + "FLASH_2M": "2 MiB Flash", + "FLASH_4M": "4 MiB Flash", + "FLASH_8M": "8 MiB Flash" }, "vendor": "WeAct" } From 0e261443be9a9201fd786b54aa2d0198f21543f7 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 27 Jun 2024 14:51:12 +0100 Subject: [PATCH 0085/1300] rp2: Replace CMSIS funcs with Pico SDK equivalents. Pico SDK defines `__dsb()` and `__sev()` so use those instead of the CMSIS equivalents. This matches the use of `__wfi()` in lieu of `__WFI()` and lowers the dependency on CMSIS headers. And then, move the include of "RP2040.h" from the widely-included "mphalport.h" to specific source files that need this header, to keep its inclusion contained. Signed-off-by: Phil Howard --- ports/rp2/machine_uart.c | 4 ++-- ports/rp2/main.c | 1 + ports/rp2/mphalport.h | 1 - ports/rp2/mpnetworkport.c | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index ccbf8f781ac82..d9e97685f1f25 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -369,9 +369,9 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, uart_init(self->uart, self->baudrate); uart_set_format(self->uart, self->bits, self->stop, self->parity); - __DSB(); // make sure UARTLCR_H register is written to + __dsb(); // make sure UARTLCR_H register is written to uart_set_fifo_enabled(self->uart, true); - __DSB(); // make sure UARTLCR_H register is written to + __dsb(); // make sure UARTLCR_H register is written to gpio_set_function(self->tx, GPIO_FUNC_UART); gpio_set_function(self->rx, GPIO_FUNC_UART); if (self->invert & UART_INVERT_RX) { diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 5fb47cc4028ef..3b4d351a78792 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -47,6 +47,7 @@ #include "genhdr/mpversion.h" #include "mp_usbd.h" +#include "RP2040.h" // cmsis, for PendSV_IRQn and SCB/SCB_SCR_SEVONPEND_Msk #include "pico/stdlib.h" #include "pico/binary_info.h" #include "pico/unique_id.h" diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h index bc09f8c4a572f..16ac4259a6feb 100644 --- a/ports/rp2/mphalport.h +++ b/ports/rp2/mphalport.h @@ -30,7 +30,6 @@ #include "pico/time.h" #include "hardware/clocks.h" #include "hardware/structs/systick.h" -#include "RP2040.h" // cmsis, for __WFI #include "pendsv.h" #define SYSTICK_MAX (0xffffff) diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c index 4690a4e511024..e58adb3ba3595 100644 --- a/ports/rp2/mpnetworkport.c +++ b/ports/rp2/mpnetworkport.c @@ -43,6 +43,7 @@ static soft_timer_entry_t mp_network_soft_timer; #include "lib/cyw43-driver/src/cyw43.h" #include "lib/cyw43-driver/src/cyw43_stats.h" #include "hardware/irq.h" +#include "RP2040.h" // cmsis, for NVIC_SetPriority and PendSV_IRQn #define CYW43_IRQ_LEVEL GPIO_IRQ_LEVEL_HIGH #define CYW43_SHARED_IRQ_HANDLER_PRIORITY PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY @@ -57,7 +58,7 @@ static void gpio_irq_handler(void) { // CYW43_POST_POLL_HOOK which is called at the end of cyw43_poll_func. gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, CYW43_IRQ_LEVEL, false); cyw43_has_pending = 1; - __SEV(); + __sev(); pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, cyw43_poll); CYW43_STAT_INC(IRQ_COUNT); } From 0600e4f27333092884358c967123dcc040d7a1cc Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 25 Jun 2024 15:45:13 +0200 Subject: [PATCH 0086/1300] py/asmrv32: Make some code sequences smaller. This commit changes a few code sequences to use more compressed opcodes where possible. The sequences in question are the ones that show up the most in the test suite and require the least amount of code changes, namely short offset loads from memory to RET/ARG registers, indirect calls through the function table, register-based jumps, locals' offset calculation, reg-is-null jumps, and register comparisons. There are no speed losses or gains from these changes, but there is an average 15-20% generated code size reduction. Signed-off-by: Alessandro Gatti --- py/asmrv32.c | 71 +++++++++++++++++++++++++++++++++++++++++----------- py/asmrv32.h | 57 +++++++++++++++++++++++++++++++++++------ 2 files changed, 105 insertions(+), 23 deletions(-) diff --git a/py/asmrv32.c b/py/asmrv32.c index 228edfc22c102..7dd58becae6a6 100644 --- a/py/asmrv32.c +++ b/py/asmrv32.c @@ -64,9 +64,14 @@ static uint32_t fallback_popcount(uint32_t value) { #endif #endif -#define INTERNAL_TEMPORARY ASM_RV32_REG_T4 +#define INTERNAL_TEMPORARY ASM_RV32_REG_S0 #define AVAILABLE_REGISTERS_COUNT 32 +#define IS_IN_C_REGISTER_WINDOW(register_number) \ + (((register_number) >= ASM_RV32_REG_X8) && ((register_number) <= ASM_RV32_REG_X15)) +#define MAP_IN_C_REGISTER_WINDOW(register_number) \ + ((register_number) - ASM_RV32_REG_X8) + #define FIT_UNSIGNED(value, bits) (((value) & ~((1U << (bits)) - 1)) == 0) #define FIT_SIGNED(value, bits) \ ((((value) & ~((1U << ((bits) - 1)) - 1)) == 0) || \ @@ -269,7 +274,7 @@ static void emit_function_epilogue(asm_rv32_t *state, mp_uint_t registers) { void asm_rv32_entry(asm_rv32_t *state, mp_uint_t locals) { state->saved_registers_mask |= (1U << REG_FUN_TABLE) | (1U << REG_LOCAL_1) | \ - (1U << REG_LOCAL_2) | (1U << REG_LOCAL_3); + (1U << REG_LOCAL_2) | (1U << REG_LOCAL_3) | (1U << INTERNAL_TEMPORARY); state->locals_count = locals; emit_function_prologue(state, state->saved_registers_mask); } @@ -288,6 +293,14 @@ void asm_rv32_emit_call_ind(asm_rv32_t *state, mp_uint_t index) { mp_uint_t offset = index * ASM_WORD_SIZE; state->saved_registers_mask |= (1U << ASM_RV32_REG_RA); + if (IS_IN_C_REGISTER_WINDOW(REG_FUN_TABLE) && IS_IN_C_REGISTER_WINDOW(INTERNAL_TEMPORARY) && FIT_SIGNED(offset, 7)) { + // c.lw temporary, offset(fun_table) + // c.jalr temporary + asm_rv32_opcode_clw(state, MAP_IN_C_REGISTER_WINDOW(INTERNAL_TEMPORARY), MAP_IN_C_REGISTER_WINDOW(REG_FUN_TABLE), offset); + asm_rv32_opcode_cjalr(state, INTERNAL_TEMPORARY); + return; + } + if (FIT_UNSIGNED(offset, 11)) { // lw temporary, offset(fun_table) // c.jalr temporary @@ -343,6 +356,12 @@ void asm_rv32_emit_jump_if_reg_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_t label) { ptrdiff_t displacement = (ptrdiff_t)(state->base.label_offsets[label] - state->base.code_offset); + if (FIT_SIGNED(displacement, 9) && IS_IN_C_REGISTER_WINDOW(rs)) { + // c.bnez rs', displacement + asm_rv32_opcode_cbnez(state, MAP_IN_C_REGISTER_WINDOW(rs), displacement); + return; + } + // The least significant bit is ignored anyway. if (FIT_SIGNED(displacement, 13)) { // bne rs, zero, displacement @@ -350,8 +369,8 @@ void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_ return; } - // Compensate for the initial BEQ opcode. - displacement -= ASM_WORD_SIZE; + // Compensate for the initial C.BEQZ/BEQ opcode. + displacement -= IS_IN_C_REGISTER_WINDOW(rs) ? ASM_HALFWORD_SIZE : ASM_WORD_SIZE; mp_uint_t upper = 0; mp_uint_t lower = 0; @@ -359,11 +378,21 @@ void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_ // TODO: Can this clobber REG_TEMP[0:2]? - // beq rs1, zero, 12 ; PC + 0 - // auipc temporary, HI(displacement) ; PC + 4 - // jalr zero, temporary, LO(displacement) ; PC + 8 - // ... ; PC + 12 - asm_rv32_opcode_beq(state, rs, ASM_RV32_REG_ZERO, 12); + // if rs1 in C window (the offset always fits): + // c.beqz rs', 10 ; PC + 0 + // auipc temporary, HI(displacement) ; PC + 2 + // jalr zero, temporary, LO(displacement) ; PC + 6 + // ... ; PC + 10 + // else: + // beq rs, zero, 12 ; PC + 0 + // auipc temporary, HI(displacement) ; PC + 4 + // jalr zero, temporary, LO(displacement) ; PC + 8 + // ... ; PC + 12 + if (IS_IN_C_REGISTER_WINDOW(rs)) { + asm_rv32_opcode_cbeqz(state, MAP_IN_C_REGISTER_WINDOW(rs), 10); + } else { + asm_rv32_opcode_beq(state, rs, ASM_RV32_REG_ZERO, 12); + } asm_rv32_opcode_auipc(state, INTERNAL_TEMPORARY, upper); asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, INTERNAL_TEMPORARY, lower); } @@ -427,7 +456,13 @@ void asm_rv32_emit_mov_reg_local(asm_rv32_t *state, mp_uint_t rd, mp_uint_t loca void asm_rv32_emit_mov_reg_local_addr(asm_rv32_t *state, mp_uint_t rd, mp_uint_t local) { mp_uint_t offset = state->locals_stack_offset + (local * ASM_WORD_SIZE); - if (FIT_SIGNED(offset, 11)) { + if (FIT_UNSIGNED(offset, 10) && offset != 0 && IS_IN_C_REGISTER_WINDOW(rd)) { + // c.addi4spn rd', offset + asm_rv32_opcode_caddi4spn(state, MAP_IN_C_REGISTER_WINDOW(rd), offset); + return; + } + + if (FIT_UNSIGNED(offset, 11)) { // addi rd, sp, offset asm_rv32_opcode_addi(state, rd, ASM_RV32_REG_SP, offset); return; @@ -442,6 +477,12 @@ void asm_rv32_emit_mov_reg_local_addr(asm_rv32_t *state, mp_uint_t rd, mp_uint_t void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { mp_int_t scaled_offset = offset * sizeof(ASM_WORD_SIZE); + if (IS_IN_C_REGISTER_WINDOW(rd) && IS_IN_C_REGISTER_WINDOW(rs) && FIT_SIGNED(offset, 7)) { + // c.lw rd', offset(rs') + asm_rv32_opcode_clw(state, MAP_IN_C_REGISTER_WINDOW(rd), MAP_IN_C_REGISTER_WINDOW(rs), scaled_offset); + return; + } + if (FIT_SIGNED(scaled_offset, 12)) { // lw rd, offset(rs) asm_rv32_opcode_lw(state, rd, rs, scaled_offset); @@ -554,12 +595,12 @@ void asm_rv32_emit_optimised_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) void asm_rv32_meta_comparison_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd) { // c.li rd, 1 ; - // beq rs1, rs2, 8 ; PC + 0 - // addi rd, zero, 0 ; PC + 4 - // ... ; PC + 8 + // beq rs1, rs2, 6 ; PC + 0 + // c.li rd, 0 ; PC + 4 + // ... ; PC + 6 asm_rv32_opcode_cli(state, rd, 1); - asm_rv32_opcode_beq(state, rs1, rs2, 8); - asm_rv32_opcode_addi(state, rd, ASM_RV32_REG_ZERO, 0); + asm_rv32_opcode_beq(state, rs1, rs2, 6); + asm_rv32_opcode_cli(state, rd, 0); } void asm_rv32_meta_comparison_ne(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd) { diff --git a/py/asmrv32.h b/py/asmrv32.h index 4061fd5f86be1..7f7602471c1d6 100644 --- a/py/asmrv32.h +++ b/py/asmrv32.h @@ -151,10 +151,21 @@ void asm_rv32_end_pass(asm_rv32_t *state); ((op & 0b1111111) | ((rd & 0b11111) << 7) | \ (imm & 0b11111111111111111111000000000000)) +#define RV32_ENCODE_TYPE_CB(op, ft3, rs, imm) \ + ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rs & 0b111) << 7) | \ + (((imm) & 0b100000000) << 4) | (((imm) & 0b11000000) >> 1) | \ + (((imm) & 0b100000) >> 3) | (((imm) & 0b11000) << 7) | \ + (((imm) & 0b110) << 2)) + #define RV32_ENCODE_TYPE_CI(op, ft3, rd, imm) \ ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rd & 0b11111) << 7) | \ (((imm) & 0b100000) << 7) | (((imm) & 0b11111) << 2)) +#define RV32_ENCODE_TYPE_CIW(op, ft3, rd, imm) \ + ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rd & 0b111) << 2) | \ + ((imm & 0b1111000000) << 1) | ((imm & 0b110000) << 7) | \ + ((imm & 0b1000) << 2) | ((imm & 0b100) << 4)) + #define RV32_ENCODE_TYPE_CJ(op, ft3, imm) \ ((op & 0b11) | ((ft3 & 0b111) << 13) | \ ((imm & 0b1110) << 2) | ((imm & 0b1100000000) << 1) | \ @@ -162,12 +173,18 @@ void asm_rv32_end_pass(asm_rv32_t *state); ((imm & 0b10000000) >> 1) | ((imm & 0b1000000) << 1) | \ ((imm & 0b100000) >> 3) | ((imm & 0b10000) << 7)) +#define RV32_ENCODE_TYPE_CL(op, ft3, rd, rs, imm) \ + ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rd & 0b111) << 2) | \ + ((rs & 0b111) << 7) | ((imm & 0b1000000) >> 1) | \ + ((imm & 0b111000) << 7) | ((imm & 0b100) << 4)) + #define RV32_ENCODE_TYPE_CR(op, ft4, rs1, rs2) \ ((op & 0b11) | ((rs2 & 0b11111) << 2) | ((rs1 & 0b11111) << 7) | \ ((ft4 & 0b1111) << 12)) #define RV32_ENCODE_TYPE_CSS(op, ft3, rs, imm) \ - ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rs & 0b11111) << 2) | ((imm) & 0b111111) << 7) + ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rs & 0b11111) << 2) | \ + ((imm) & 0b111111) << 7) void asm_rv32_emit_word_opcode(asm_rv32_t *state, mp_uint_t opcode); void asm_rv32_emit_halfword_opcode(asm_rv32_t *state, mp_uint_t opcode); @@ -220,10 +237,28 @@ static inline void asm_rv32_opcode_caddi(asm_rv32_t *state, mp_uint_t rd, mp_int asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0b01, 0b000, rd, immediate)); } +// C.ADDI4SPN RD', IMMEDIATE +static inline void asm_rv32_opcode_caddi4spn(asm_rv32_t *state, mp_uint_t rd, mp_uint_t immediate) { + // CIW: 000 ........ ... 00 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CIW(0b00, 0b000, rd, immediate)); +} + +// C.BEQZ RS', IMMEDIATE +static inline void asm_rv32_opcode_cbeqz(asm_rv32_t *state, mp_uint_t rs, mp_int_t offset) { + // CB: 110 ... ... ..... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CB(0b01, 0b110, rs, offset)); +} + +// C.BNEZ RS', IMMEDIATE +static inline void asm_rv32_opcode_cbnez(asm_rv32_t *state, mp_uint_t rs, mp_int_t offset) { + // CB: 111 ... ... ..... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CB(0b01, 0b111, rs, offset)); +} + // C.J OFFSET -static inline void asm_rv32_opcode_cj(asm_rv32_t *state, mp_uint_t offset) { +static inline void asm_rv32_opcode_cj(asm_rv32_t *state, mp_int_t offset) { // CJ: 101 ........... 01 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CJ(0b01, 0b001, offset)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CJ(0b01, 0b101, offset)); } // C.JALR RS @@ -250,6 +285,12 @@ static inline void asm_rv32_opcode_clui(asm_rv32_t *state, mp_uint_t rd, mp_int_ asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0b01, 0b011, rd, immediate >> 12)); } +// C.LW RD', OFFSET(RS') +static inline void asm_rv32_opcode_clw(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { + // CL: 010 ... ... .. ... 00 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CL(0b00, 0b010, rd, rs, offset)); +} + // C.LWSP RD, OFFSET static inline void asm_rv32_opcode_clwsp(asm_rv32_t *state, mp_uint_t rd, mp_uint_t offset) { // CI: 010 . ..... ..... 10 @@ -383,6 +424,7 @@ static inline void asm_rv32_opcode_xori(asm_rv32_t *state, mp_uint_t rd, mp_uint } #define ASM_WORD_SIZE (4) +#define ASM_HALFWORD_SIZE (2) #define REG_RET ASM_RV32_REG_A0 #define REG_ARG_1 ASM_RV32_REG_A0 @@ -392,8 +434,7 @@ static inline void asm_rv32_opcode_xori(asm_rv32_t *state, mp_uint_t rd, mp_uint #define REG_TEMP0 ASM_RV32_REG_T1 #define REG_TEMP1 ASM_RV32_REG_T2 #define REG_TEMP2 ASM_RV32_REG_T3 -// S0 may be used as the frame pointer by the compiler. -#define REG_FUN_TABLE ASM_RV32_REG_S2 +#define REG_FUN_TABLE ASM_RV32_REG_S1 #define REG_LOCAL_1 ASM_RV32_REG_S3 #define REG_LOCAL_2 ASM_RV32_REG_S4 #define REG_LOCAL_3 ASM_RV32_REG_S5 @@ -432,10 +473,10 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_ #define ASM_JUMP_IF_REG_EQ(state, rs1, rs2, label) asm_rv32_emit_jump_if_reg_eq(state, rs1, rs2, label) #define ASM_JUMP_IF_REG_NONZERO(state, rs, label, bool_test) asm_rv32_emit_jump_if_reg_nonzero(state, rs, label) #define ASM_JUMP_IF_REG_ZERO(state, rs, label, bool_test) asm_rv32_emit_jump_if_reg_eq(state, rs, ASM_RV32_REG_ZERO, label) -#define ASM_JUMP_REG(state, rs) asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, rs, 0) +#define ASM_JUMP_REG(state, rs) asm_rv32_opcode_cjr(state, rs) #define ASM_LOAD16_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load16_reg_reg_offset(state, rd, rs, offset) #define ASM_LOAD16_REG_REG(state, rd, rs) asm_rv32_opcode_lhu(state, rd, rs, 0) -#define ASM_LOAD32_REG_REG(state, rd, rs) asm_rv32_opcode_lw(state, rd, rs, 0) +#define ASM_LOAD32_REG_REG(state, rd, rs) ASM_LOAD_REG_REG_OFFSET(state, rd, rs, 0) #define ASM_LOAD8_REG_REG(state, rd, rs) asm_rv32_opcode_lbu(state, rd, rs, 0) #define ASM_LOAD_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load_reg_reg_offset(state, rd, rs, offset) #define ASM_LOAD_REG_REG(state, rd, rs) ASM_LOAD32_REG_REG(state, rd, rs) @@ -452,7 +493,7 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_ #define ASM_NOT_REG(state, rd) asm_rv32_opcode_xori(state, rd, rd, -1) #define ASM_OR_REG_REG(state, rd, rs) asm_rv32_opcode_or(state, rd, rd, rs) #define ASM_STORE16_REG_REG(state, rs1, rs2) asm_rv32_opcode_sh(state, rs1, rs2, 0) -#define ASM_STORE32_REG_REG(state, rs1, rs2) asm_rv32_opcode_sw(state, rs1, rs2, 0) +#define ASM_STORE32_REG_REG(state, rs1, rs2) ASM_STORE_REG_REG_OFFSET(state, rs1, rs2, 0) #define ASM_STORE8_REG_REG(state, rs1, rs2) asm_rv32_opcode_sb(state, rs1, rs2, 0) #define ASM_STORE_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_store_reg_reg_offset(state, rd, rs, offset) #define ASM_STORE_REG_REG(state, rs1, rs2) ASM_STORE32_REG_REG(state, rs1, rs2) From 557d31ed2c75e90b2f2c534c7019cb51aab5a7c8 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 6 Oct 2022 13:44:54 +1100 Subject: [PATCH 0087/1300] py/objint: Try to convert big-int back to small-int after binary op. Before this change, long/mpz ints propagated into all future calculations, even if their value could fit in a small-int object. With this change, the result of a big-int binary op will now be converted to a small-int object if the value fits in a small-int. For example, a relatively common operation like `x = a * b // c` where a,b,c all small ints would always result in a long/mpz int, even if it didn't need to, and then this would impact all future calculations with x. This adds +24 bytes on PYBV11 but avoids heap allocations and potential surprises (e.g. `big-big` is now a small `0`, and can safely be accessed with MP_OBJ_SMALL_INT_VALUE). Performance tests are unchanged on PYBV10, except for `bm_pidigits.py` which makes heavy use of big-ints and gains about 8% in speed. Unix coverage tests have been updated to cover mpz code that is now unreachable by normal Python code (removing the unreachable code would lead to some surprising gaps in the internal C functions and the functionality may be needed in the future, so it is kept because it has minimal overhead). This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared --- ports/unix/coverage.c | 11 +++++++++++ py/objint_longlong.c | 23 ++++++++--------------- py/objint_mpz.c | 12 ++++++++++++ tests/basics/int_big_to_small.py | 22 ++++++++++++++++++++++ tests/basics/int_big_to_small.py.exp | 25 +++++++++++++++++++++++++ tests/ports/unix/extra_coverage.py.exp | 2 ++ 6 files changed, 80 insertions(+), 15 deletions(-) create mode 100644 tests/basics/int_big_to_small.py create mode 100644 tests/basics/int_big_to_small.py.exp diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 803f849535cbe..b05e2dade60a8 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -4,6 +4,7 @@ #include "py/obj.h" #include "py/objfun.h" +#include "py/objint.h" #include "py/objstr.h" #include "py/runtime.h" #include "py/gc.h" @@ -454,6 +455,13 @@ static mp_obj_t extra_coverage(void) { mpz_mul_inpl(&mpz, &mpz2, &mpz); mpz_as_uint_checked(&mpz, &value); mp_printf(&mp_plat_print, "%d\n", (int)value); + + // mpz_not_inpl with argument==0, testing ~0 + mpz_set_from_int(&mpz, 0); + mpz_not_inpl(&mpz, &mpz); + mp_int_t value_signed; + mpz_as_int_checked(&mpz, &value_signed); + mp_printf(&mp_plat_print, "%d\n", (int)value_signed); } // runtime utils @@ -470,6 +478,9 @@ static mp_obj_t extra_coverage(void) { // call mp_call_function_2_protected with invalid args mp_call_function_2_protected(MP_OBJ_FROM_PTR(&mp_builtin_divmod_obj), mp_obj_new_str("abc", 3), mp_obj_new_str("abc", 3)); + // mp_obj_int_get_checked with mp_obj_int_t that has a value that is a small integer + mp_printf(&mp_plat_print, "%d\n", mp_obj_int_get_checked(mp_obj_int_new_mpz())); + // mp_obj_int_get_uint_checked with non-negative small-int mp_printf(&mp_plat_print, "%d\n", (int)mp_obj_int_get_uint_checked(MP_OBJ_NEW_SMALL_INT(1))); diff --git a/py/objint_longlong.c b/py/objint_longlong.c index 00fe5636c1607..1940b815386a6 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -247,25 +247,21 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i } mp_obj_t mp_obj_new_int(mp_int_t value) { - if (MP_SMALL_INT_FITS(value)) { - return MP_OBJ_NEW_SMALL_INT(value); - } return mp_obj_new_int_from_ll(value); } mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value) { - // SMALL_INT accepts only signed numbers, so make sure the input - // value fits completely in the small-int positive range. - if ((value & ~MP_SMALL_INT_POSITIVE_MASK) == 0) { - return MP_OBJ_NEW_SMALL_INT(value); - } return mp_obj_new_int_from_ll(value); } mp_obj_t mp_obj_new_int_from_ll(long long val) { + if ((long long)(mp_int_t)val == val && MP_SMALL_INT_FITS(val)) { + return MP_OBJ_NEW_SMALL_INT(val); + } + mp_obj_int_t *o = mp_obj_malloc(mp_obj_int_t, &mp_type_int); o->val = val; - return o; + return MP_OBJ_FROM_PTR(o); } mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) { @@ -273,19 +269,16 @@ mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) { if (val >> (sizeof(unsigned long long) * 8 - 1) != 0) { mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("ulonglong too large")); } - mp_obj_int_t *o = mp_obj_malloc(mp_obj_int_t, &mp_type_int); - o->val = val; - return o; + return mp_obj_new_int_from_ll(val); } mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base) { // TODO this does not honor the given length of the string, but it all cases it should anyway be null terminated // TODO check overflow - mp_obj_int_t *o = mp_obj_malloc(mp_obj_int_t, &mp_type_int); char *endptr; - o->val = strtoll(*str, &endptr, base); + mp_obj_t result = mp_obj_new_int_from_ll(strtoll(*str, &endptr, base)); *str = endptr; - return o; + return result; } mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in) { diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 4a1a685bbd42e..6f2ea616c779c 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -312,6 +312,14 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i return MP_OBJ_NULL; // op not supported } + // Check if the result fits in a small-int, and if so just return that. + mp_int_t res_small; + if (mpz_as_int_checked(&res->mpz, &res_small)) { + if (MP_SMALL_INT_FITS(res_small)) { + return MP_OBJ_NEW_SMALL_INT(res_small); + } + } + return MP_OBJ_FROM_PTR(res); } else { @@ -425,6 +433,10 @@ mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { const mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t value; if (mpz_as_int_checked(&self->mpz, &value)) { + // mp_obj_int_t objects should always contain a value that is a large + // integer (if the value fits in a small-int then it should have been + // converted to a small-int object), and so this code-path should never + // be taken in normal circumstances. return value; } else { // overflow diff --git a/tests/basics/int_big_to_small.py b/tests/basics/int_big_to_small.py new file mode 100644 index 0000000000000..64280d0c635f5 --- /dev/null +++ b/tests/basics/int_big_to_small.py @@ -0,0 +1,22 @@ +try: + import micropython + micropython.heap_lock +except: + print("SKIP") + raise SystemExit + +# All less than small int max. +for d in (0, 27, 1<<29, -1861, -(1<<29)): + i = 1<<70 + print(i) + j = (1<<70) + d + print(j) + # k should now be a small int. + k = j - i + print(k) + + # Now verify that working with k doesn't allocate (i.e. it's a small int). + micropython.heap_lock() + print(k + 20) + print(k // 20) + micropython.heap_unlock() diff --git a/tests/basics/int_big_to_small.py.exp b/tests/basics/int_big_to_small.py.exp new file mode 100644 index 0000000000000..1d4986e044372 --- /dev/null +++ b/tests/basics/int_big_to_small.py.exp @@ -0,0 +1,25 @@ +1180591620717411303424 +1180591620717411303424 +0 +20 +0 +1180591620717411303424 +1180591620717411303451 +27 +47 +1 +1180591620717411303424 +1180591620717948174336 +536870912 +536870932 +26843545 +1180591620717411303424 +1180591620717411301563 +-1861 +-1841 +-94 +1180591620717411303424 +1180591620716874432512 +-536870912 +-536870892 +-26843546 diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp index adffc2d5824a4..5e806ebe57ccf 100644 --- a/tests/ports/unix/extra_coverage.py.exp +++ b/tests/ports/unix/extra_coverage.py.exp @@ -88,9 +88,11 @@ data 1 12345 6 +-1 # runtime utils TypeError: unsupported type for __abs__: 'str' TypeError: unsupported types for __divmod__: 'str', 'str' +0 1 2 OverflowError: overflow converting long int to machine word From bb6a4669b28b1cf9b314cb728cc7b319541a1cc0 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 1 Jul 2024 22:44:02 +0200 Subject: [PATCH 0088/1300] py/asmrv32: Do not use binary literals. As per discussion in #15347, non-standard binary literals have been removed in favour of their hexadecimal counterparts. Signed-off-by: Alessandro Gatti --- py/asmrv32.h | 167 ++++++++++++++++++++++++--------------------------- 1 file changed, 79 insertions(+), 88 deletions(-) diff --git a/py/asmrv32.h b/py/asmrv32.h index 7f7602471c1d6..77a5b0ab67ac4 100644 --- a/py/asmrv32.h +++ b/py/asmrv32.h @@ -122,69 +122,60 @@ void asm_rv32_end_pass(asm_rv32_t *state); //////////////////////////////////////////////////////////////////////////////// -#define RV32_ENCODE_TYPE_B(op, ft3, rs1, rs2, imm) \ - ((op & 0b1111111) | ((ft3 & 0b111) << 12) | ((imm & 0b100000000000) >> 4) | \ - ((imm & 0b11110) << 7) | ((rs1 & 0b11111) << 15) | \ - ((rs2 & 0b11111) << 20) | ((imm & 0b11111100000) << 20) | \ - ((imm & 0b1000000000000) << 19)) - -#define RV32_ENCODE_TYPE_I(op, ft3, rd, rs, imm) \ - ((op & 0b1111111) | ((rd & 0b11111) << 7) | ((ft3 & 0b111) << 12) | \ - ((rs & 0b11111) << 15) | ((imm & 0b111111111111) << 20)) - -#define RV32_ENCODE_TYPE_J(op, rd, imm) \ - ((op & 0b1111111) | ((rd & 0b11111) << 7) | (imm & 0b11111111000000000000) | \ - ((imm & 0b100000000000) << 9) | ((imm & 0b11111111110) << 20) | \ - ((imm & 0b100000000000000000000) << 11)) - -#define RV32_ENCODE_TYPE_R(op, ft3, ft7, rd, rs1, rs2) \ - ((op & 0b1111111) | ((rd & 0b11111) << 7) | ((ft3 & 0b111) << 12) | \ - ((rs1 & 0b11111) << 15) | ((rs2 & 0b11111) << 20) | \ - ((ft7 & 0b1111111) << 25)) - -#define RV32_ENCODE_TYPE_S(op, ft3, rs1, rs2, imm) \ - ((op & 0b1111111) | ((imm & 0b11111) << 7) | ((ft3 & 0b111) << 12) | \ - ((rs1 & 0b11111) << 15) | ((rs2 & 0b11111) << 20) | \ - ((imm & 0b111111100000) << 20)) - -#define RV32_ENCODE_TYPE_U(op, rd, imm) \ - ((op & 0b1111111) | ((rd & 0b11111) << 7) | \ - (imm & 0b11111111111111111111000000000000)) +#define RV32_ENCODE_TYPE_B(op, ft3, rs1, rs2, imm) \ + ((op & 0x7F) | ((ft3 & 0x07) << 12) | ((imm & 0x800) >> 4) | \ + ((imm & 0x1E) << 7) | ((rs1 & 0x1F) << 15) | ((rs2 & 0x1F) << 20) | \ + ((imm & 0x7E0) << 20) | ((imm & 0x1000) << 19)) + +#define RV32_ENCODE_TYPE_I(op, ft3, rd, rs, imm) \ + ((op & 0x7F) | ((rd & 0x1F) << 7) | ((ft3 & 0x07) << 12) | \ + ((rs & 0x1F) << 15) | ((imm & 0xFFF) << 20)) + +#define RV32_ENCODE_TYPE_J(op, rd, imm) \ + ((op & 0x7F) | ((rd & 0x1F) << 7) | (imm & 0xFF000) | \ + ((imm & 0x800) << 9) | ((imm & 0x7FE) << 20) | ((imm & 0x100000) << 11)) + +#define RV32_ENCODE_TYPE_R(op, ft3, ft7, rd, rs1, rs2) \ + ((op & 0x7F) | ((rd & 0x1F) << 7) | ((ft3 & 0x07) << 12) | \ + ((rs1 & 0x1F) << 15) | ((rs2 & 0x1F) << 20) | ((ft7 & 0x7F) << 25)) + +#define RV32_ENCODE_TYPE_S(op, ft3, rs1, rs2, imm) \ + ((op & 0x7F) | ((imm & 0x1F) << 7) | ((ft3 & 0x07) << 12) | \ + ((rs1 & 0x1F) << 15) | ((rs2 & 0x1F) << 20) | ((imm & 0xFE0) << 20)) + +#define RV32_ENCODE_TYPE_U(op, rd, imm) \ + ((op & 0x7F) | ((rd & 0x1F) << 7) | (imm & 0xFFFFF000)) #define RV32_ENCODE_TYPE_CB(op, ft3, rs, imm) \ - ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rs & 0b111) << 7) | \ - (((imm) & 0b100000000) << 4) | (((imm) & 0b11000000) >> 1) | \ - (((imm) & 0b100000) >> 3) | (((imm) & 0b11000) << 7) | \ - (((imm) & 0b110) << 2)) + ((op & 0x03) | ((ft3 & 0x07) << 13) | ((rs & 0x07) << 7) | \ + (((imm) & 0x100) << 4) | (((imm) & 0xC0) >> 1) | (((imm) & 0x20) >> 3) | \ + (((imm) & 0x18) << 7) | (((imm) & 0x06) << 2)) #define RV32_ENCODE_TYPE_CI(op, ft3, rd, imm) \ - ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rd & 0b11111) << 7) | \ - (((imm) & 0b100000) << 7) | (((imm) & 0b11111) << 2)) + ((op & 0x03) | ((ft3 & 0x07) << 13) | ((rd & 0x1F) << 7) | \ + (((imm) & 0x20) << 7) | (((imm) & 0x1F) << 2)) #define RV32_ENCODE_TYPE_CIW(op, ft3, rd, imm) \ - ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rd & 0b111) << 2) | \ - ((imm & 0b1111000000) << 1) | ((imm & 0b110000) << 7) | \ - ((imm & 0b1000) << 2) | ((imm & 0b100) << 4)) + ((op & 0x03) | ((ft3 & 0x07) << 13) | ((rd & 0x07) << 2) | \ + ((imm & 0x3C0) << 1) | ((imm & 0x30) << 7) | \ + ((imm & 0x08) << 2) | ((imm & 0x04) << 4)) #define RV32_ENCODE_TYPE_CJ(op, ft3, imm) \ - ((op & 0b11) | ((ft3 & 0b111) << 13) | \ - ((imm & 0b1110) << 2) | ((imm & 0b1100000000) << 1) | \ - ((imm & 0b100000000000) << 1) | ((imm & 0b10000000000) >> 2) | \ - ((imm & 0b10000000) >> 1) | ((imm & 0b1000000) << 1) | \ - ((imm & 0b100000) >> 3) | ((imm & 0b10000) << 7)) + ((op & 0x03) | ((ft3 & 0x07) << 13) | ((imm & 0x0E) << 2) | \ + ((imm & 0x300) << 1) | ((imm & 0x800) << 1) | ((imm & 0x400) >> 2) | \ + ((imm & 0x80) >> 1) | ((imm & 0x40) << 1) | ((imm & 0x20) >> 3) | \ + ((imm & 0x10) << 7)) #define RV32_ENCODE_TYPE_CL(op, ft3, rd, rs, imm) \ - ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rd & 0b111) << 2) | \ - ((rs & 0b111) << 7) | ((imm & 0b1000000) >> 1) | \ - ((imm & 0b111000) << 7) | ((imm & 0b100) << 4)) + ((op & 0x03) | ((ft3 & 0x07) << 13) | ((rd & 0x07) << 2) | \ + ((rs & 0x07) << 7) | ((imm & 0x40) >> 1) | ((imm & 0x38) << 7) | \ + ((imm & 0x04) << 4)) #define RV32_ENCODE_TYPE_CR(op, ft4, rs1, rs2) \ - ((op & 0b11) | ((rs2 & 0b11111) << 2) | ((rs1 & 0b11111) << 7) | \ - ((ft4 & 0b1111) << 12)) + ((op & 0x03) | ((rs2 & 0x1F) << 2) | ((rs1 & 0x1F) << 7) | ((ft4 & 0x0F) << 12)) #define RV32_ENCODE_TYPE_CSS(op, ft3, rs, imm) \ - ((op & 0b11) | ((ft3 & 0b111) << 13) | ((rs & 0b11111) << 2) | \ - ((imm) & 0b111111) << 7) + ((op & 0x03) | ((ft3 & 0x07) << 13) | ((rs & 0x1F) << 2) | ((imm) & 0x3F) << 7) void asm_rv32_emit_word_opcode(asm_rv32_t *state, mp_uint_t opcode); void asm_rv32_emit_halfword_opcode(asm_rv32_t *state, mp_uint_t opcode); @@ -192,235 +183,235 @@ void asm_rv32_emit_halfword_opcode(asm_rv32_t *state, mp_uint_t opcode); // ADD RD, RS1, RS2 static inline void asm_rv32_opcode_add(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000000 ..... ..... 000 ..... 0110011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b000, 0b0000000, rd, rs1, rs2)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x00, 0x00, rd, rs1, rs2)); } // ADDI RD, RS, IMMEDIATE static inline void asm_rv32_opcode_addi(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { // I: ............ ..... 000 ..... 0010011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0010011, 0b000, rd, rs, immediate)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x13, 0x00, rd, rs, immediate)); } // AND RD, RS1, RS2 static inline void asm_rv32_opcode_and(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000000 ..... ..... 111 ..... 0110011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b111, 0b0000000, rd, rs1, rs2)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x07, 0x00, rd, rs1, rs2)); } // AUIPC RD, offset static inline void asm_rv32_opcode_auipc(asm_rv32_t *state, mp_uint_t rd, mp_int_t offset) { // U: .................... ..... 0010111 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_U(0b0010111, rd, offset)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_U(0x17, rd, offset)); } // BEQ RS1, RS2, OFFSET static inline void asm_rv32_opcode_beq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_int_t offset) { // B: . ...... ..... ..... 000 .... . 1100011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_B(0b1100011, 0b000, rs1, rs2, offset)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_B(0x63, 0x00, rs1, rs2, offset)); } // BNE RS1, RS2, OFFSET static inline void asm_rv32_opcode_bne(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_int_t offset) { // B: . ...... ..... ..... 001 .... . 1100011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_B(0b1100011, 0b001, rs1, rs2, offset)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_B(0x63, 0x01, rs1, rs2, offset)); } // C.ADD RD, RS static inline void asm_rv32_opcode_cadd(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) { // CR: 1001 ..... ..... 10 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0b10, 0b1001, rd, rs)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0x02, 0x09, rd, rs)); } // C.ADDI RD, IMMEDIATE static inline void asm_rv32_opcode_caddi(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { // CI: 000 . ..... ..... 01 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0b01, 0b000, rd, immediate)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0x01, 0x00, rd, immediate)); } // C.ADDI4SPN RD', IMMEDIATE static inline void asm_rv32_opcode_caddi4spn(asm_rv32_t *state, mp_uint_t rd, mp_uint_t immediate) { // CIW: 000 ........ ... 00 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CIW(0b00, 0b000, rd, immediate)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CIW(0x00, 0x00, rd, immediate)); } // C.BEQZ RS', IMMEDIATE static inline void asm_rv32_opcode_cbeqz(asm_rv32_t *state, mp_uint_t rs, mp_int_t offset) { // CB: 110 ... ... ..... 01 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CB(0b01, 0b110, rs, offset)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CB(0x01, 0x06, rs, offset)); } // C.BNEZ RS', IMMEDIATE static inline void asm_rv32_opcode_cbnez(asm_rv32_t *state, mp_uint_t rs, mp_int_t offset) { // CB: 111 ... ... ..... 01 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CB(0b01, 0b111, rs, offset)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CB(0x01, 0x07, rs, offset)); } // C.J OFFSET static inline void asm_rv32_opcode_cj(asm_rv32_t *state, mp_int_t offset) { // CJ: 101 ........... 01 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CJ(0b01, 0b101, offset)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CJ(0x01, 0x05, offset)); } // C.JALR RS static inline void asm_rv32_opcode_cjalr(asm_rv32_t *state, mp_uint_t rs) { // CR: 1001 ..... 00000 10 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0b10, 0b1001, rs, 0)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0x02, 0x09, rs, 0)); } // C.JR RS static inline void asm_rv32_opcode_cjr(asm_rv32_t *state, mp_uint_t rs) { // CR: 1000 ..... 00000 10 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0b10, 0b1000, rs, 0)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0x02, 0x08, rs, 0)); } // C.LI RD, IMMEDIATE static inline void asm_rv32_opcode_cli(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { // CI: 010 . ..... ..... 01 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0b01, 0b010, rd, immediate)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0x01, 0x02, rd, immediate)); } // C.LUI RD, IMMEDIATE static inline void asm_rv32_opcode_clui(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { // CI: 011 . ..... ..... 01 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0b01, 0b011, rd, immediate >> 12)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0x01, 0x03, rd, immediate >> 12)); } // C.LW RD', OFFSET(RS') static inline void asm_rv32_opcode_clw(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { // CL: 010 ... ... .. ... 00 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CL(0b00, 0b010, rd, rs, offset)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CL(0x00, 0x02, rd, rs, offset)); } // C.LWSP RD, OFFSET static inline void asm_rv32_opcode_clwsp(asm_rv32_t *state, mp_uint_t rd, mp_uint_t offset) { // CI: 010 . ..... ..... 10 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0b10, 0b010, rd, ((offset & 0b11000000) >> 6) | (offset & 0b111100))); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0x02, 0x02, rd, ((offset & 0xC0) >> 6) | (offset & 0x3C))); } // C.MV RD, RS static inline void asm_rv32_opcode_cmv(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) { // CR: 1000 ..... ..... 10 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0b10, 0b1000, rd, rs)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0x02, 0x08, rd, rs)); } // C.SWSP RS, OFFSET static inline void asm_rv32_opcode_cswsp(asm_rv32_t *state, mp_uint_t rs, mp_uint_t offset) { // CSS: 010 ...... ..... 10 - asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CSS(0b10, 0b110, rs, ((offset & 0b11000000) >> 6) | (offset & 0b111100))); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CSS(0x02, 0x06, rs, ((offset & 0xC0) >> 6) | (offset & 0x3C))); } // JALR RD, RS, offset static inline void asm_rv32_opcode_jalr(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { // I: ............ ..... 000 ..... 1100111 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b1100111, 0b000, rd, rs, offset)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x67, 0x00, rd, rs, offset)); } // LBU RD, OFFSET(RS) static inline void asm_rv32_opcode_lbu(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { // I: ............ ..... 100 ..... 0000011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0000011, 0b100, rd, rs, offset)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x03, 0x04, rd, rs, offset)); } // LHU RD, OFFSET(RS) static inline void asm_rv32_opcode_lhu(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { // I: ............ ..... 101 ..... 0000011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0000011, 0b101, rd, rs, offset)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x03, 0x05, rd, rs, offset)); } // LUI RD, immediate static inline void asm_rv32_opcode_lui(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { // U: .................... ..... 0110111 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_U(0b0110111, rd, immediate)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_U(0x37, rd, immediate)); } // LW RD, OFFSET(RS) static inline void asm_rv32_opcode_lw(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { // I: ............ ..... 010 ..... 0000011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0000011, 0b010, rd, rs, offset)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x03, 0x02, rd, rs, offset)); } // MUL RD, RS1, RS2 static inline void asm_rv32m_opcode_mul(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000001 ..... ..... 000 ..... 0110011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b000, 0b0000001, rd, rs1, rs2)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x00, 0x01, rd, rs1, rs2)); } // OR RD, RS1, RS2 static inline void asm_rv32_opcode_or(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000000 ..... ..... 110 ..... 0110011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b110, 0b0000000, rd, rs1, rs2)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x06, 0x00, rd, rs1, rs2)); } // SLL RD, RS1, RS2 static inline void asm_rv32_opcode_sll(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000000 ..... ..... 001 ..... 0110011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b001, 0b0000000, rd, rs1, rs2)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x01, 0x00, rd, rs1, rs2)); } // SLLI RD, RS, IMMEDIATE static inline void asm_rv32_opcode_slli(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_uint_t immediate) { // I: 0000000..... ..... 001 ..... 0010011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0010011, 0b001, rd, rs, immediate & 0x1F)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x13, 0x01, rd, rs, immediate & 0x1F)); } // SRL RD, RS1, RS2 static inline void asm_rv32_opcode_srl(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000000 ..... ..... 101 ..... 0110011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b101, 0b0000000, rd, rs1, rs2)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x05, 0x00, rd, rs1, rs2)); } // SLT RD, RS1, RS2 static inline void asm_rv32_opcode_slt(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000000 ..... ..... 010 ..... 0110011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b010, 0b0000000, rd, rs1, rs2)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x02, 0x00, rd, rs1, rs2)); } // SLTU RD, RS1, RS2 static inline void asm_rv32_opcode_sltu(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000000 ..... ..... 011 ..... 0110011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b011, 0b0000000, rd, rs1, rs2)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x03, 0x00, rd, rs1, rs2)); } // SRA RD, RS1, RS2 static inline void asm_rv32_opcode_sra(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0100000 ..... ..... 101 ..... 0110011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b101, 0b0100000, rd, rs1, rs2)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x05, 0x20, rd, rs1, rs2)); } // SUB RD, RS1, RS2 static inline void asm_rv32_opcode_sub(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0100000 ..... ..... 000 ..... 0110011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b000, 0b0100000, rd, rs1, rs2)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x00, 0x20, rd, rs1, rs2)); } // SB RS2, OFFSET(RS1) static inline void asm_rv32_opcode_sb(asm_rv32_t *state, mp_uint_t rs2, mp_uint_t rs1, mp_int_t offset) { // S: ....... ..... ..... 000 ..... 0100011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0b0100011, 0b000, rs1, rs2, offset)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0x23, 0x00, rs1, rs2, offset)); } // SH RS2, OFFSET(RS1) static inline void asm_rv32_opcode_sh(asm_rv32_t *state, mp_uint_t rs2, mp_uint_t rs1, mp_int_t offset) { // S: ....... ..... ..... 001 ..... 0100011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0b0100011, 0b001, rs1, rs2, offset)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0x23, 0x01, rs1, rs2, offset)); } // SW RS2, OFFSET(RS1) static inline void asm_rv32_opcode_sw(asm_rv32_t *state, mp_uint_t rs2, mp_uint_t rs1, mp_int_t offset) { // S: ....... ..... ..... 010 ..... 0100011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0b0100011, 0b010, rs1, rs2, offset)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0x23, 0x02, rs1, rs2, offset)); } // XOR RD, RS1, RS2 static inline void asm_rv32_opcode_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000000 ..... ..... 100 ..... 0110011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0b0110011, 0b100, 0b0000000, rd, rs1, rs2)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x04, 0x00, rd, rs1, rs2)); } // XORI RD, RS, IMMEDIATE static inline void asm_rv32_opcode_xori(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { // I: ............ ..... 100 ..... 0010011 - asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0b0010011, 0b100, rd, rs, immediate)); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x13, 0x04, rd, rs, immediate)); } #define ASM_WORD_SIZE (4) From 57008a1e69166bc55c315a54f966694031be83f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20D=C3=B6rre?= Date: Wed, 12 Jun 2024 06:56:51 +0000 Subject: [PATCH 0089/1300] extmod/machine_usb_device: Add USBDevice.remote_wakeup method. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simply exposes the TinyUSB function. Signed-off-by: Felix Dörre --- docs/library/machine.USBDevice.rst | 7 +++++++ extmod/machine_usb_device.c | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/docs/library/machine.USBDevice.rst b/docs/library/machine.USBDevice.rst index 5fc71651612b1..a47fda2a28d35 100644 --- a/docs/library/machine.USBDevice.rst +++ b/docs/library/machine.USBDevice.rst @@ -215,6 +215,13 @@ Methods drivers. Placing a different string at any of these indexes overrides that string in the built-in driver. +.. method:: USBDevice.remote_wakeup(self) + + Wake up host if we are in suspend mode and the REMOTE_WAKEUP feature + is enabled by the host. This has to be enabled in the USB attributes, + and on the host. Returns ``True`` if remote wakeup was enabled and + active and the host was woken up. + .. method:: USBDevice.submit_xfer(self, ep, buffer /) Submit a USB transfer on endpoint number ``ep``. ``buffer`` must be diff --git a/extmod/machine_usb_device.c b/extmod/machine_usb_device.c index 69c3f38487006..5c4e84c38d1fe 100644 --- a/extmod/machine_usb_device.c +++ b/extmod/machine_usb_device.c @@ -157,6 +157,11 @@ static mp_obj_t usb_device_active(size_t n_args, const mp_obj_t *args) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usb_device_active_obj, 1, 2, usb_device_active); +static mp_obj_t usb_remote_wakeup(mp_obj_t self) { + return mp_obj_new_bool(tud_remote_wakeup()); +} +static MP_DEFINE_CONST_FUN_OBJ_1(usb_remote_wakeup_obj, usb_remote_wakeup); + static mp_obj_t usb_device_stall(size_t n_args, const mp_obj_t *args) { mp_obj_usb_device_t *self = (mp_obj_usb_device_t *)MP_OBJ_TO_PTR(args[0]); int epnum = mp_obj_get_int(args[1]); @@ -272,6 +277,7 @@ static const mp_rom_map_elem_t usb_device_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_submit_xfer), MP_ROM_PTR(&usb_device_submit_xfer_obj) }, { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&usb_device_active_obj) }, { MP_ROM_QSTR(MP_QSTR_stall), MP_ROM_PTR(&usb_device_stall_obj) }, + { MP_ROM_QSTR(MP_QSTR_remote_wakeup), MP_ROM_PTR(&usb_remote_wakeup_obj) }, // Built-in driver constants { MP_ROM_QSTR(MP_QSTR_BUILTIN_NONE), MP_ROM_PTR(&mp_type_usb_device_builtin_none) }, From 838794ebccb0536606bc8e67fda2be5a47c2dffd Mon Sep 17 00:00:00 2001 From: Sylvain Zimmer Date: Wed, 5 Jun 2024 11:41:38 +0200 Subject: [PATCH 0090/1300] extmod/mbedtls: Enable GCM and ECDHE-RSA in common mbedtls config. Enable support for cipher suites like TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, as suggested in https://github.com/micropython/micropython/issues/14204#issuecomment-2024366349 and https://github.com/micropython/micropython/issues/10485#issuecomment-1396426824 Tests have been run on the top 500 domains from moz.com. Without this patch, 155 out of 500 fail to connect because of TLS issues. This patch fixes them all. And it seems all existing mbedtls flags are needed to get good coverage of those top 500 domains. The `ssl_poll.py` test has the cipher bits increased from 512 to 1024 in its test key/cert so that it can work with ECDHE-RSA which is now the chosen cipher. Signed-off-by: Sylvain Zimmer Signed-off-by: Damien George --- extmod/mbedtls/mbedtls_config_common.h | 2 + tests/extmod/ssl_poll.py | 66 ++++++++++++++++---------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/extmod/mbedtls/mbedtls_config_common.h b/extmod/mbedtls/mbedtls_config_common.h index db1562f279732..4028bdf56d29b 100644 --- a/extmod/mbedtls/mbedtls_config_common.h +++ b/extmod/mbedtls/mbedtls_config_common.h @@ -46,6 +46,7 @@ #define MBEDTLS_ECP_DP_SECP256K1_ENABLED #define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED #define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED +#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED #define MBEDTLS_CAN_ECDH #define MBEDTLS_PK_CAN_ECDSA_SIGN #define MBEDTLS_PKCS1_V15 @@ -72,6 +73,7 @@ #define MBEDTLS_ECP_C #define MBEDTLS_ENTROPY_C #define MBEDTLS_ERROR_C +#define MBEDTLS_GCM_C #define MBEDTLS_MD_C #define MBEDTLS_MD5_C #define MBEDTLS_OID_C diff --git a/tests/extmod/ssl_poll.py b/tests/extmod/ssl_poll.py index 347e5f7d37a42..42d7fb3f84982 100644 --- a/tests/extmod/ssl_poll.py +++ b/tests/extmod/ssl_poll.py @@ -2,7 +2,6 @@ import select import ssl import io - import binascii except ImportError: print("SKIP") raise SystemExit @@ -18,32 +17,47 @@ # This self-signed key/cert pair is randomly generated and to be used for # testing/demonstration only. You should always generate your own key/cert. -key = binascii.unhexlify( - b"3082013b020100024100cc20643fd3d9c21a0acba4f48f61aadd675f52175a9dcf07fbef" - b"610a6a6ba14abb891745cd18a1d4c056580d8ff1a639460f867013c8391cdc9f2e573b0f" - b"872d0203010001024100bb17a54aeb3dd7ae4edec05e775ca9632cf02d29c2a089b563b0" - b"d05cdf95aeca507de674553f28b4eadaca82d5549a86058f9996b07768686a5b02cb240d" - b"d9f1022100f4a63f5549e817547dca97b5c658038e8593cb78c5aba3c4642cc4cd031d86" - b"8f022100d598d870ffe4a34df8de57047a50b97b71f4d23e323f527837c9edae88c79483" - b"02210098560c89a70385c36eb07fd7083235c4c1184e525d838aedf7128958bedfdbb102" - b"2051c0dab7057a8176ca966f3feb81123d4974a733df0f958525f547dfd1c271f9022044" - b"6c2cafad455a671a8cf398e642e1be3b18a3d3aec2e67a9478f83c964c4f1f" +# They has been generated using the following commands: +# $ openssl req -x509 -newkey rsa:1024 -keyout rsa_key.pem -out rsa_cert.pem -days 3650 -nodes -subj '/CN=micropython.local/O=MicroPython/C=AU' +# $ openssl pkey -in rsa_key.pem -out rsa_key.der -outform DER +# $ openssl x509 -in rsa_cert.pem -out rsa_cert.der -outform DER +key = bytes.fromhex( + "3082025d02010002818100eca28b2f8230237ae45e7a77ef495c05a786f423cc65caf6bc" + "1813d50eacf9d2d011a0e43a20fde947ff957075e4b3b6ded46c33f63af42597aac1c4cb" + "bb2d1a6aad91755707d8fc560e222b38c940a480da89ee849049613bd88d1ab283423aba" + "ba591c4fcc8ce89d19646e6b9f80de4efa7bbda68c569a5cbdb4235c3fc9990203010001" + "0281810089d4df978b90388a534c88af252ca8b20e7377ef0616609338da196b27422fbd" + "d03e04660b49be3bcc191dd5448632fb986d489d3795fd318c5704c879168c5cd0fa8551" + "f7f86073b95b092ffdb4f39c867a306a02f94cf3009df7055dab1f9277dd8914268d53a2" + "bd4de2cdf2ac90d8cd248b48868cb911781779750c344ae5024100f8c0d2cebfaccbdb1b" + "d8bc7519c84889cc6d5dff8fb994cf1a9492881289de66d689afb942d10dc0dc0f65464e" + "7cca7a53e451e6a8cfab0069d05065d56ba4bf024100f38757e2fc7f786e1a653c8a8b51" + "5b06a1d85db31998090fc4d52a88b8c5557e0a7bac10995e7e76cef6bfb59b67c01cbba7" + "edcfa7d3d623615a92459d07efa702407b149579fcf717caeb455b4229a6a2d5a3d3bd7d" + "d4f4833fd22c0f30cad372bab98e58e736bb4fadcc74c5ac7aeb5e1816e852e9e93f0fa2" + "5db8d7fefb118eeb024100caf0a0a6c1b02055f09d28c473b10a600e8356222853f04939" + "c84237c97278fa1e164d9f4f8fd56780b553b12d9e5a1417b5ab91fed3a381bc6153bfbb" + "4a9fc5024043eb4ff7e4dd6c12c6d6dc50977ee5d5f9730af4469d1f642321320fb4b969" + "90ed841e41bedda49ff89a0c28acf132e4af5f3ac406b1548d0f135aff6bf23ef1" ) -cert = binascii.unhexlify( - b"308201d53082017f020203e8300d06092a864886f70d01010505003075310b3009060355" - b"0406130258583114301206035504080c0b54686550726f76696e63653110300e06035504" - b"070c075468654369747931133011060355040a0c0a436f6d70616e7958595a3113301106" - b"0355040b0c0a436f6d70616e7958595a3114301206035504030c0b546865486f73744e61" - b"6d65301e170d3139313231383033333935355a170d3239313231353033333935355a3075" - b"310b30090603550406130258583114301206035504080c0b54686550726f76696e636531" - b"10300e06035504070c075468654369747931133011060355040a0c0a436f6d70616e7958" - b"595a31133011060355040b0c0a436f6d70616e7958595a3114301206035504030c0b5468" - b"65486f73744e616d65305c300d06092a864886f70d0101010500034b003048024100cc20" - b"643fd3d9c21a0acba4f48f61aadd675f52175a9dcf07fbef610a6a6ba14abb891745cd18" - b"a1d4c056580d8ff1a639460f867013c8391cdc9f2e573b0f872d0203010001300d06092a" - b"864886f70d0101050500034100b0513fe2829e9ecbe55b6dd14c0ede7502bde5d46153c8" - b"e960ae3ebc247371b525caeb41bbcf34686015a44c50d226e66aef0a97a63874ca5944ef" - b"979b57f0b3" +cert = bytes.fromhex( + "3082025a308201c3a003020102021475fd479918562f4c6cb08f63e8afbdfa3b884f8630" + "0d06092a864886f70d01010b0500303f311a301806035504030c116d6963726f70797468" + "6f6e2e6c6f63616c31143012060355040a0c0b4d6963726f507974686f6e310b30090603" + "55040613024155301e170d3234303730323030353931355a170d33343036333030303539" + "31355a303f311a301806035504030c116d6963726f707974686f6e2e6c6f63616c311430" + "12060355040a0c0b4d6963726f507974686f6e310b300906035504061302415530819f30" + "0d06092a864886f70d010101050003818d0030818902818100eca28b2f8230237ae45e7a" + "77ef495c05a786f423cc65caf6bc1813d50eacf9d2d011a0e43a20fde947ff957075e4b3" + "b6ded46c33f63af42597aac1c4cbbb2d1a6aad91755707d8fc560e222b38c940a480da89" + "ee849049613bd88d1ab283423ababa591c4fcc8ce89d19646e6b9f80de4efa7bbda68c56" + "9a5cbdb4235c3fc9990203010001a3533051301d0603551d0e04160414409545477a659a" + "16da174810ba9ad192ef962089301f0603551d23041830168014409545477a659a16da17" + "4810ba9ad192ef962089300f0603551d130101ff040530030101ff300d06092a864886f7" + "0d01010b0500038181007182e78cecceef00f98d0ee117cd9dc2f9fc84d581e7b1d9d43b" + "74db45e188368e264f79628e2bda89a545d50cd9925ad50f8e25decc9130164bdb9220c8" + "f49776d784511e9c4b94305cc2cb3eaf8204e42d31ba8aabd1d296b9ef51035b6df1ab75" + "89681f0026073ccac6bed5d8bd9235a4bb717b696ce518de4e35b751fa13" ) From 75350f9c8e2e433f997bb30cceed4b967ad55a19 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 2 Jul 2024 10:31:44 +1000 Subject: [PATCH 0091/1300] rp2/mbedtls: Remove config options that are now in the common settings. Signed-off-by: Damien George --- ports/rp2/mbedtls/mbedtls_config_port.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ports/rp2/mbedtls/mbedtls_config_port.h b/ports/rp2/mbedtls/mbedtls_config_port.h index 4e4c6e263fa59..b3f19f3a07dad 100644 --- a/ports/rp2/mbedtls/mbedtls_config_port.h +++ b/ports/rp2/mbedtls/mbedtls_config_port.h @@ -28,10 +28,6 @@ // Set mbedtls configuration #define MBEDTLS_ECP_NIST_OPTIM -#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED - -// Enable mbedtls modules -#define MBEDTLS_GCM_C // Time hook #include From 462fa5f94ff7a90afb42e72ce145309fa671ddce Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 2 Jul 2024 12:33:34 +0100 Subject: [PATCH 0092/1300] rp2/rp2_pio: Replace PIO_NUM macro with pio_get_index. The `PIO_NUM` macro was defined when `rp2_pio.c` was first conceived. There's now a Pico SDK function for this, `pio_get_index()`, which is already used in some parts of the code. This commit removes `PIO_NUM` in favour of using `pio_get_index()` everywhere. Signed-off-by: Phil Howard --- ports/rp2/rp2_pio.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/ports/rp2/rp2_pio.c b/ports/rp2/rp2_pio.c index b882bbbc50c8c..447ea244c75a5 100644 --- a/ports/rp2/rp2_pio.c +++ b/ports/rp2/rp2_pio.c @@ -37,8 +37,6 @@ #include "hardware/irq.h" #include "hardware/pio.h" -#define PIO_NUM(pio) ((pio) == pio0 ? 0 : 1) - typedef struct _rp2_pio_obj_t { mp_obj_base_t base; PIO pio; @@ -82,7 +80,7 @@ static void pio_irq0(PIO pio) { pio->irq = ints >> 8; // Call handler if it is registered, for PIO irqs. - rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[PIO_NUM(pio)]); + rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[pio_get_index(pio)]); if (irq != NULL && (ints & irq->trigger)) { irq->flags = ints & irq->trigger; mp_irq_handler(&irq->base); @@ -90,7 +88,7 @@ static void pio_irq0(PIO pio) { // Call handler if it is registered, for StateMachine irqs. for (size_t i = 0; i < 4; ++i) { - rp2_state_machine_irq_obj_t *irq = MP_STATE_PORT(rp2_state_machine_irq_obj[PIO_NUM(pio) * 4 + i]); + rp2_state_machine_irq_obj_t *irq = MP_STATE_PORT(rp2_state_machine_irq_obj[pio_get_index(pio) * 4 + i]); if (irq != NULL && ((ints >> (8 + i)) & irq->trigger)) { irq->flags = 1; mp_irq_handler(&irq->base); @@ -280,7 +278,7 @@ static mp_obj_t rp2_pio_add_program(mp_obj_t self_in, mp_obj_t prog_in) { uint offset = rp2_pio_add_managed_program(self->pio, &pio_program); // Store the program offset in the program object. - prog[PROG_OFFSET_PIO0 + PIO_NUM(self->pio)] = MP_OBJ_NEW_SMALL_INT(offset); + prog[PROG_OFFSET_PIO0 + pio_get_index(self->pio)] = MP_OBJ_NEW_SMALL_INT(offset); return mp_const_none; } @@ -301,12 +299,12 @@ static mp_obj_t rp2_pio_remove_program(size_t n_args, const mp_obj_t *args) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(prog[PROG_DATA], &bufinfo, MP_BUFFER_READ); length = bufinfo.len / 2; - offset = mp_obj_get_int(prog[PROG_OFFSET_PIO0 + PIO_NUM(self->pio)]); + offset = mp_obj_get_int(prog[PROG_OFFSET_PIO0 + pio_get_index(self->pio)]); if (offset < 0) { mp_raise_ValueError("prog not in instruction memory"); } // Invalidate the program offset in the program object. - prog[PROG_OFFSET_PIO0 + PIO_NUM(self->pio)] = MP_OBJ_NEW_SMALL_INT(-1); + prog[PROG_OFFSET_PIO0 + pio_get_index(self->pio)] = MP_OBJ_NEW_SMALL_INT(-1); } // Remove the program from the instruction memory. @@ -354,7 +352,7 @@ static mp_obj_t rp2_pio_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // Get the IRQ object. - rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[PIO_NUM(self->pio)]); + rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[pio_get_index(self->pio)]); // Allocate the IRQ object if it doesn't already exist. if (irq == NULL) { @@ -364,7 +362,7 @@ static mp_obj_t rp2_pio_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k irq->base.parent = MP_OBJ_FROM_PTR(self); irq->base.handler = mp_const_none; irq->base.ishard = false; - MP_STATE_PORT(rp2_pio_irq_obj[PIO_NUM(self->pio)]) = irq; + MP_STATE_PORT(rp2_pio_irq_obj[pio_get_index(self->pio)]) = irq; } if (n_args > 1 || kw_args->used != 0) { @@ -426,7 +424,7 @@ MP_DEFINE_CONST_OBJ_TYPE( static mp_uint_t rp2_pio_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { rp2_pio_obj_t *self = MP_OBJ_TO_PTR(self_in); - rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[PIO_NUM(self->pio)]); + rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[pio_get_index(self->pio)]); irq_set_enabled(self->irq, false); irq->flags = 0; irq->trigger = new_trigger; @@ -436,7 +434,7 @@ static mp_uint_t rp2_pio_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { static mp_uint_t rp2_pio_irq_info(mp_obj_t self_in, mp_uint_t info_type) { rp2_pio_obj_t *self = MP_OBJ_TO_PTR(self_in); - rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[PIO_NUM(self->pio)]); + rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[pio_get_index(self->pio)]); if (info_type == MP_IRQ_INFO_FLAGS) { return irq->flags; } else if (info_type == MP_IRQ_INFO_TRIGGERS) { @@ -537,10 +535,10 @@ static mp_obj_t rp2_state_machine_init_helper(const rp2_state_machine_obj_t *sel mp_obj_get_array_fixed_n(args[ARG_prog].u_obj, PROG_MAX_FIELDS, &prog); // Get and the program offset, and load it into memory if it's not already there. - mp_int_t offset = mp_obj_get_int(prog[PROG_OFFSET_PIO0 + PIO_NUM(self->pio)]); + mp_int_t offset = mp_obj_get_int(prog[PROG_OFFSET_PIO0 + pio_get_index(self->pio)]); if (offset < 0) { - rp2_pio_add_program(&rp2_pio_obj[PIO_NUM(self->pio)], args[ARG_prog].u_obj); - offset = mp_obj_get_int(prog[PROG_OFFSET_PIO0 + PIO_NUM(self->pio)]); + rp2_pio_add_program(&rp2_pio_obj[pio_get_index(self->pio)], args[ARG_prog].u_obj); + offset = mp_obj_get_int(prog[PROG_OFFSET_PIO0 + pio_get_index(self->pio)]); } rp2_state_machine_initial_pc[self->id] = offset; @@ -907,7 +905,7 @@ MP_DEFINE_CONST_OBJ_TYPE( static mp_uint_t rp2_state_machine_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { rp2_state_machine_obj_t *self = MP_OBJ_TO_PTR(self_in); - rp2_state_machine_irq_obj_t *irq = MP_STATE_PORT(rp2_state_machine_irq_obj[PIO_NUM(self->pio)]); + rp2_state_machine_irq_obj_t *irq = MP_STATE_PORT(rp2_state_machine_irq_obj[pio_get_index(self->pio)]); irq_set_enabled(self->irq, false); irq->flags = 0; irq->trigger = new_trigger; @@ -917,7 +915,7 @@ static mp_uint_t rp2_state_machine_irq_trigger(mp_obj_t self_in, mp_uint_t new_t static mp_uint_t rp2_state_machine_irq_info(mp_obj_t self_in, mp_uint_t info_type) { rp2_state_machine_obj_t *self = MP_OBJ_TO_PTR(self_in); - rp2_state_machine_irq_obj_t *irq = MP_STATE_PORT(rp2_state_machine_irq_obj[PIO_NUM(self->pio)]); + rp2_state_machine_irq_obj_t *irq = MP_STATE_PORT(rp2_state_machine_irq_obj[pio_get_index(self->pio)]); if (info_type == MP_IRQ_INFO_FLAGS) { return irq->flags; } else if (info_type == MP_IRQ_INFO_TRIGGERS) { From f61fac0ba6e2684b8bb4b11c7a768b0cb805c198 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 2 Jul 2024 12:41:29 +0100 Subject: [PATCH 0093/1300] rp2/rp2_pio: Replace explicit pio ternary expression with pio_get_index. There are three changes here: - Fix `rp2_pio_print` to use `pio_get_index()` too, since it had its own copy of the ternary expression. - Remove a ternary from `rp2_pio_state_machine` and calculate it from `pio_get_index`. - Remove a ternary on `GPIO_FUNC_PIO0` vs `GPIO_FUNC_PIO1`. These constants are sequentially ordered so we can calculate them too. Signed-off-by: Phil Howard --- ports/rp2/rp2_pio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/rp2/rp2_pio.c b/ports/rp2/rp2_pio.c index 447ea244c75a5..410269e2d3d31 100644 --- a/ports/rp2/rp2_pio.c +++ b/ports/rp2/rp2_pio.c @@ -226,7 +226,7 @@ static void asm_pio_init_gpio(PIO pio, uint32_t sm, asm_pio_config_t *config) { pio_sm_set_pins_with_mask(pio, sm, config->pinvals << config->base, pinmask); pio_sm_set_pindirs_with_mask(pio, sm, config->pindirs << config->base, pinmask); for (size_t i = 0; i < config->count; ++i) { - gpio_set_function(config->base + i, pio == pio0 ? GPIO_FUNC_PIO0 : GPIO_FUNC_PIO1); + gpio_set_function(config->base + i, GPIO_FUNC_PIO0 + pio_get_index(pio)); } } @@ -242,7 +242,7 @@ static rp2_pio_obj_t rp2_pio_obj[] = { static void rp2_pio_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { rp2_pio_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "PIO(%u)", self->pio == pio0 ? 0 : 1); + mp_printf(print, "PIO(%u)", pio_get_index(self->pio)); } // constructor(id) @@ -326,7 +326,7 @@ static mp_obj_t rp2_pio_state_machine(size_t n_args, const mp_obj_t *pos_args, m } // Return the correct StateMachine object. - const rp2_state_machine_obj_t *sm = rp2_state_machine_get_object((self->pio == pio0 ? 0 : 4) + sm_id); + const rp2_state_machine_obj_t *sm = rp2_state_machine_get_object(pio_get_index(self->pio) * 4 + sm_id); if (n_args > 2 || kw_args->used > 0) { // Configuration arguments given so init this StateMachine. From 2b8de7436bbdb54da99bf6bdb79359876fd3bc1d Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 24 Jan 2024 16:48:43 +0100 Subject: [PATCH 0094/1300] rp2/machine_adc: Initialise ADC GPIO when a Pin is referenced by int. The change closes the gap in the API when an integer is used as Pin reference. With the change, e.g. ADC(26), ADC(Pin(26)) and ADC("GP26") behave identically and the GPIO is initialised in ACD/high-Z mode. Only when using ADC channel numbers 0-3 are the corresponding GPIO left uninitialised, and then the user is responsible for configuring the GPIO. Signed-off-by: robert-hh --- ports/rp2/machine_adc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ports/rp2/machine_adc.c b/ports/rp2/machine_adc.c index e5a428563d1ce..3594f4b124022 100644 --- a/ports/rp2/machine_adc.c +++ b/ports/rp2/machine_adc.c @@ -74,14 +74,14 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args const machine_pin_obj_t *pin = NULL; if (mp_obj_is_int(source)) { - // Get and validate channel number. channel = mp_obj_get_int(source); - if (ADC_IS_VALID_GPIO(channel)) { - channel = ADC_CHANNEL_FROM_GPIO(channel); - } else if (!(channel >= 0 && channel <= ADC_CHANNEL_TEMPSENSOR)) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid channel")); + if (!(channel >= 0 && channel <= ADC_CHANNEL_TEMPSENSOR)) { + // Not a valid ADC channel, fallback to searching for a pin. + channel = -1; } - } else { + } + + if (channel == -1) { // Get GPIO and check it has ADC capabilities. pin = machine_pin_find(source); bool valid_adc_pin = false; From f36a5654a87838e68fb313272098777f76a94865 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 2 Feb 2024 13:47:12 +0100 Subject: [PATCH 0095/1300] docs/rp2/quickref: Document the use of channel numbers for ADC. Signed-off-by: robert-hh --- docs/rp2/quickref.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/rp2/quickref.rst b/docs/rp2/quickref.rst index 11a808c110278..ecf3f31861c22 100644 --- a/docs/rp2/quickref.rst +++ b/docs/rp2/quickref.rst @@ -187,6 +187,14 @@ Use the :ref:`machine.ADC ` class:: adc = ADC(Pin(26)) # create ADC object on ADC pin adc.read_u16() # read value, 0-65535 across voltage range 0.0v - 3.3v +The argument of the constructor ADC specifies either a Pin by number, name of as +Pin object, or a channel number in the range 0 - 3 or ADC.CORE_TEMP for the +internal temperature sensor. If a pin is specified, +the pin is initialized in high-Z mode. If a channel number is used, the pin +is not initialized and configuring is left to the user code. After hard reset, +RP2040 pins operate in current sink mode at about 60µA. If the pin is not +otherwise configured, that may lead to wrong ADC readings. + Software SPI bus ---------------- From 289b2dd87960a4cdf019013cecd489f0d0cabc26 Mon Sep 17 00:00:00 2001 From: Jon Foster Date: Mon, 1 Apr 2024 19:19:29 +0100 Subject: [PATCH 0096/1300] py/objstr: Add new mp_obj_new_str_from_cstr() helper function. There were lots of places where this pattern was duplicated, to convert a standard C string to a MicroPython string: x = mp_obj_new_str(s, strlen(s)); This commit provides a simpler method that removes this code duplication: x = mp_obj_new_str_from_cstr(s); This gives clearer, and probably smaller, code. Signed-off-by: Jon Foster --- py/obj.h | 1 + py/objstr.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/py/obj.h b/py/obj.h index 86952565c55d6..82f67fad4d96c 100644 --- a/py/obj.h +++ b/py/obj.h @@ -971,6 +971,7 @@ mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, uns mp_obj_t mp_obj_new_int_from_ll(long long val); // this must return a multi-precision integer object (or raise an overflow exception) mp_obj_t mp_obj_new_int_from_ull(unsigned long long val); // this must return a multi-precision integer object (or raise an overflow exception) mp_obj_t mp_obj_new_str(const char *data, size_t len); // will check utf-8 (raises UnicodeError) +mp_obj_t mp_obj_new_str_from_cstr(const char *str); // // accepts null-terminated string, will check utf-8 (raises UnicodeError) mp_obj_t mp_obj_new_str_via_qstr(const char *data, size_t len); // input data must be valid utf-8 mp_obj_t mp_obj_new_str_from_vstr(vstr_t *vstr); // will check utf-8 (raises UnicodeError) #if MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK diff --git a/py/objstr.c b/py/objstr.c index c7e4ebf53b753..346a6259f7423 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -2308,6 +2308,10 @@ mp_obj_t mp_obj_new_str(const char *data, size_t len) { } } +mp_obj_t mp_obj_new_str_from_cstr(const char *str) { + return mp_obj_new_str(str, strlen(str)); +} + mp_obj_t mp_obj_str_intern(mp_obj_t str) { GET_STR_DATA_LEN(str, data, len); return mp_obj_new_str_via_qstr((const char *)data, len); From 92484d8822635c125a7648d5b056ff7f78d62ab3 Mon Sep 17 00:00:00 2001 From: Jon Foster Date: Mon, 1 Apr 2024 19:23:49 +0100 Subject: [PATCH 0097/1300] all: Use new mp_obj_new_str_from_cstr() function. Use new function mp_obj_new_str_from_cstr() where appropriate. It simplifies the code, and makes it smaller too. Signed-off-by: Jon Foster --- extmod/modlwip.c | 2 +- extmod/modnetwork.c | 2 +- extmod/modopenamp.c | 2 +- extmod/modopenamp_remoteproc_store.c | 2 +- extmod/modtls_mbedtls.c | 6 +++--- extmod/modwebrepl.c | 2 +- extmod/network_esp_hosted.c | 2 +- extmod/network_lwip.c | 4 ++-- extmod/network_ninaw10.c | 2 +- extmod/vfs.c | 2 +- extmod/vfs_fat.c | 4 ++-- extmod/vfs_lfsx.c | 2 +- extmod/vfs_posix.c | 4 ++-- ports/cc3200/mods/modwlan.c | 6 +++--- ports/esp32/esp32_partition.c | 2 +- ports/esp32/modsocket.c | 4 ++-- ports/esp32/network_common.c | 2 +- ports/esp32/network_ppp.c | 2 +- ports/esp32/network_wlan.c | 2 +- ports/esp8266/network_wlan.c | 2 +- ports/unix/coverage.c | 8 ++++---- ports/unix/main.c | 2 +- ports/unix/modffi.c | 2 +- ports/unix/modjni.c | 2 +- ports/unix/modos.c | 2 +- ports/zephyr/main.c | 4 ++-- ports/zephyr/modsocket.c | 2 +- py/binary.c | 2 +- 28 files changed, 40 insertions(+), 40 deletions(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index bcee3f0ff1a93..b07c1d8159c2f 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -373,7 +373,7 @@ mp_obj_t lwip_format_inet_addr(const ip_addr_t *ip, mp_uint_t port) { char ipstr[IPADDR_STRLEN_MAX]; ipaddr_ntoa_r(ip, ipstr, sizeof(ipstr)); mp_obj_t tuple[2] = { - tuple[0] = mp_obj_new_str(ipstr, strlen(ipstr)), + tuple[0] = mp_obj_new_str_from_cstr(ipstr), tuple[1] = mp_obj_new_int(port), }; return mp_obj_new_tuple(2, tuple); diff --git a/extmod/modnetwork.c b/extmod/modnetwork.c index 88f9d37180bcd..f3d7d0faa8e1d 100644 --- a/extmod/modnetwork.c +++ b/extmod/modnetwork.c @@ -127,7 +127,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_network_country_obj, 0, 1, network_count mp_obj_t mod_network_hostname(size_t n_args, const mp_obj_t *args) { if (n_args == 0) { - return mp_obj_new_str(mod_network_hostname_data, strlen(mod_network_hostname_data)); + return mp_obj_new_str_from_cstr(mod_network_hostname_data); } else { size_t len; const char *str = mp_obj_str_get_data(args[0], &len); diff --git a/extmod/modopenamp.c b/extmod/modopenamp.c index 7a19e55a660d9..29135d57a80b3 100644 --- a/extmod/modopenamp.c +++ b/extmod/modopenamp.c @@ -263,7 +263,7 @@ static void openamp_ns_callback(struct rpmsg_device *rdev, const char *name, uin // the Name Service (NS) announcement containing the name of the channel. virtio_dev_obj_t *virtio_device = metal_container_of(rdev, virtio_dev_obj_t, rvdev); if (virtio_device->ns_callback != mp_const_none) { - mp_call_function_2(virtio_device->ns_callback, mp_obj_new_int(dest), mp_obj_new_str(name, strlen(name))); + mp_call_function_2(virtio_device->ns_callback, mp_obj_new_int(dest), mp_obj_new_str_from_cstr(name)); } } diff --git a/extmod/modopenamp_remoteproc_store.c b/extmod/modopenamp_remoteproc_store.c index 262e6f2ec79b1..aed652c485553 100644 --- a/extmod/modopenamp_remoteproc_store.c +++ b/extmod/modopenamp_remoteproc_store.c @@ -70,7 +70,7 @@ void *mp_openamp_remoteproc_store_alloc(void) { static int openamp_remoteproc_store_open(void *store, const char *path, const void **image_data) { metal_log(METAL_LOG_DEBUG, "store_open(): %s\n", path); mp_obj_t args[2] = { - mp_obj_new_str(path, strlen(path)), + mp_obj_new_str_from_cstr(path), MP_OBJ_NEW_QSTR(MP_QSTR_rb), }; diff --git a/extmod/modtls_mbedtls.c b/extmod/modtls_mbedtls.c index 6db6ac1958dd2..5a9487ab89323 100644 --- a/extmod/modtls_mbedtls.c +++ b/extmod/modtls_mbedtls.c @@ -307,7 +307,7 @@ static mp_obj_t ssl_context_get_ciphers(mp_obj_t self_in) { mp_obj_t list = mp_obj_new_list(0, NULL); for (const int *cipher_list = mbedtls_ssl_list_ciphersuites(); *cipher_list; ++cipher_list) { const char *cipher_name = mbedtls_ssl_get_ciphersuite_name(*cipher_list); - mp_obj_list_append(list, MP_OBJ_FROM_PTR(mp_obj_new_str(cipher_name, strlen(cipher_name)))); + mp_obj_list_append(list, MP_OBJ_FROM_PTR(mp_obj_new_str_from_cstr(cipher_name))); } return list; } @@ -572,8 +572,8 @@ static mp_obj_t mod_ssl_cipher(mp_obj_t o_in) { mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in); const char *cipher_suite = mbedtls_ssl_get_ciphersuite(&o->ssl); const char *tls_version = mbedtls_ssl_get_version(&o->ssl); - mp_obj_t tuple[2] = {mp_obj_new_str(cipher_suite, strlen(cipher_suite)), - mp_obj_new_str(tls_version, strlen(tls_version))}; + mp_obj_t tuple[2] = {mp_obj_new_str_from_cstr(cipher_suite), + mp_obj_new_str_from_cstr(tls_version)}; return mp_obj_new_tuple(2, tuple); } diff --git a/extmod/modwebrepl.c b/extmod/modwebrepl.c index 9d09f65a37c1b..bac223d95243f 100644 --- a/extmod/modwebrepl.c +++ b/extmod/modwebrepl.c @@ -146,7 +146,7 @@ static void handle_op(mp_obj_webrepl_t *self) { // Handle operations requiring opened file mp_obj_t open_args[2] = { - mp_obj_new_str(self->hdr.fname, strlen(self->hdr.fname)), + mp_obj_new_str_from_cstr(self->hdr.fname), MP_OBJ_NEW_QSTR(MP_QSTR_rb) }; diff --git a/extmod/network_esp_hosted.c b/extmod/network_esp_hosted.c index 0747343d87ea6..04cfc36b323ce 100644 --- a/extmod/network_esp_hosted.c +++ b/extmod/network_esp_hosted.c @@ -230,7 +230,7 @@ static mp_obj_t network_esp_hosted_config(size_t n_args, const mp_obj_t *args, m case MP_QSTR_essid: { esp_hosted_netinfo_t netinfo; esp_hosted_wifi_netinfo(&netinfo); - return mp_obj_new_str(netinfo.ssid, strlen(netinfo.ssid)); + return mp_obj_new_str_from_cstr(netinfo.ssid); } case MP_QSTR_security: { esp_hosted_netinfo_t netinfo; diff --git a/extmod/network_lwip.c b/extmod/network_lwip.c index 329a45c46fb99..71dc295e1846e 100644 --- a/extmod/network_lwip.c +++ b/extmod/network_lwip.c @@ -113,7 +113,7 @@ mp_obj_t mod_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwa case MP_QSTR_dns: { char addr_str[IPADDR_STRLEN_MAX]; ipaddr_ntoa_r(dns_getserver(0), addr_str, sizeof(addr_str)); - return mp_obj_new_str(addr_str, strlen(addr_str)); + return mp_obj_new_str_from_cstr(addr_str); } case MP_QSTR_prefer: { return MP_OBJ_NEW_SMALL_INT(mp_mod_network_prefer_dns_use_ip_version); @@ -219,7 +219,7 @@ mp_obj_t mod_network_nic_ipconfig(struct netif *netif, size_t n_args, const mp_o char addr_str[IPADDR_STRLEN_MAX]; ipaddr_ntoa_r(netif_ip_addr6(netif, i), addr_str, sizeof(addr_str)); mp_obj_t tuple[4] = { - mp_obj_new_str(addr_str, strlen(addr_str)), + mp_obj_new_str_from_cstr(addr_str), MP_OBJ_NEW_SMALL_INT(netif_ip6_addr_state(netif, i)), MP_OBJ_NEW_SMALL_INT(netif_ip6_addr_pref_life(netif, i)), // preferred MP_OBJ_NEW_SMALL_INT(netif_ip6_addr_valid_life(netif, i)) diff --git a/extmod/network_ninaw10.c b/extmod/network_ninaw10.c index 702adcd9762e5..62961f871fb8d 100644 --- a/extmod/network_ninaw10.c +++ b/extmod/network_ninaw10.c @@ -536,7 +536,7 @@ static mp_obj_t network_ninaw10_config(size_t n_args, const mp_obj_t *args, mp_m case MP_QSTR_ssid: { nina_netinfo_t netinfo; nina_netinfo(&netinfo); - return mp_obj_new_str(netinfo.ssid, strlen(netinfo.ssid)); + return mp_obj_new_str_from_cstr(netinfo.ssid); } case MP_QSTR_security: { nina_netinfo_t netinfo; diff --git a/extmod/vfs.c b/extmod/vfs.c index ae09b8afe8d08..5d564459c0c0b 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -139,7 +139,7 @@ mp_import_stat_t mp_vfs_import_stat(const char *path) { } // delegate to vfs.stat() method - mp_obj_t path_o = mp_obj_new_str(path_out, strlen(path_out)); + mp_obj_t path_o = mp_obj_new_str_from_cstr(path_out); mp_obj_t stat; nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 38c16633630e5..4f01432f54968 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -144,7 +144,7 @@ static mp_obj_t mp_vfs_fat_ilistdir_it_iternext(mp_obj_t self_in) { // make 4-tuple with info about this entry mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL)); if (self->is_str) { - t->items[0] = mp_obj_new_str(fn, strlen(fn)); + t->items[0] = mp_obj_new_str_from_cstr(fn); } else { t->items[0] = mp_obj_new_bytes((const byte *)fn, strlen(fn)); } @@ -291,7 +291,7 @@ static mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) { if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } - return mp_obj_new_str(buf, strlen(buf)); + return mp_obj_new_str_from_cstr(buf); } static MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getcwd_obj, fat_vfs_getcwd); diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index 19da4417e6d00..4b10ca3aa597c 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -192,7 +192,7 @@ static mp_obj_t MP_VFS_LFSx(ilistdir_it_iternext)(mp_obj_t self_in) { // make 4-tuple with info about this entry mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL)); if (self->is_str) { - t->items[0] = mp_obj_new_str(info.name, strlen(info.name)); + t->items[0] = mp_obj_new_str_from_cstr(info.name); } else { t->items[0] = mp_obj_new_bytes((const byte *)info.name, strlen(info.name)); } diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c index ed4c06e36731e..bd9a6d84062cf 100644 --- a/extmod/vfs_posix.c +++ b/extmod/vfs_posix.c @@ -194,7 +194,7 @@ static mp_obj_t vfs_posix_getcwd(mp_obj_t self_in) { } #endif } - return mp_obj_new_str(ret, strlen(ret)); + return mp_obj_new_str_from_cstr(ret); } static MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_getcwd_obj, vfs_posix_getcwd); @@ -234,7 +234,7 @@ static mp_obj_t vfs_posix_ilistdir_it_iternext(mp_obj_t self_in) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); if (self->is_str) { - t->items[0] = mp_obj_new_str(fn, strlen(fn)); + t->items[0] = mp_obj_new_str_from_cstr(fn); } else { t->items[0] = mp_obj_new_bytes((const byte *)fn, strlen(fn)); } diff --git a/ports/cc3200/mods/modwlan.c b/ports/cc3200/mods/modwlan.c index 34b5adab28b8c..578f7bd73f173 100644 --- a/ports/cc3200/mods/modwlan.c +++ b/ports/cc3200/mods/modwlan.c @@ -1185,7 +1185,7 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wlan_mode_obj, 1, 2, wlan_mode); static mp_obj_t wlan_ssid(size_t n_args, const mp_obj_t *args) { wlan_obj_t *self = args[0]; if (n_args == 1) { - return mp_obj_new_str((const char *)self->ssid, strlen((const char *)self->ssid)); + return mp_obj_new_str_from_cstr((const char *)self->ssid); } else { size_t len; const char *ssid = mp_obj_str_get_data(args[1], &len); @@ -1205,7 +1205,7 @@ static mp_obj_t wlan_auth(size_t n_args, const mp_obj_t *args) { } else { mp_obj_t security[2]; security[0] = mp_obj_new_int(self->auth); - security[1] = mp_obj_new_str((const char *)self->key, strlen((const char *)self->key)); + security[1] = mp_obj_new_str_from_cstr((const char *)self->key); return mp_obj_new_tuple(2, security); } } else { @@ -1309,7 +1309,7 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(wlan_irq_obj, 1, wlan_irq); // mp_obj_t connections = mp_obj_new_list(0, NULL); // // if (wlan_is_connected()) { -// device[0] = mp_obj_new_str((const char *)wlan_obj.ssid_o, strlen((const char *)wlan_obj.ssid_o)); +// device[0] = mp_obj_new_str_from_cstr((const char *)wlan_obj.ssid_o); // device[1] = mp_obj_new_bytes((const byte *)wlan_obj.bssid, SL_BSSID_LENGTH); // // add the device to the list // mp_obj_list_append(connections, mp_obj_new_tuple(MP_ARRAY_SIZE(device), device)); diff --git a/ports/esp32/esp32_partition.c b/ports/esp32/esp32_partition.c index e5d48096204b8..cad707dfc0d23 100644 --- a/ports/esp32/esp32_partition.c +++ b/ports/esp32/esp32_partition.c @@ -156,7 +156,7 @@ static mp_obj_t esp32_partition_info(mp_obj_t self_in) { MP_OBJ_NEW_SMALL_INT(self->part->subtype), mp_obj_new_int_from_uint(self->part->address), mp_obj_new_int_from_uint(self->part->size), - mp_obj_new_str(&self->part->label[0], strlen(&self->part->label[0])), + mp_obj_new_str_from_cstr(&self->part->label[0]), mp_obj_new_bool(self->part->encrypted), }; return mp_obj_new_tuple(6, tuple); diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 44f945c4503b0..916eb79bd9265 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -951,7 +951,7 @@ static mp_obj_t esp_socket_getaddrinfo(size_t n_args, const mp_obj_t *args) { mp_obj_new_int(resi->ai_family), mp_obj_new_int(resi->ai_socktype), mp_obj_new_int(resi->ai_protocol), - mp_obj_new_str(resi->ai_canonname, strlen(resi->ai_canonname)), + mp_obj_new_str_from_cstr(resi->ai_canonname), mp_const_none }; @@ -962,7 +962,7 @@ static mp_obj_t esp_socket_getaddrinfo(size_t n_args, const mp_obj_t *args) { char buf[16]; ip4addr_ntoa_r(&ip4_addr, buf, sizeof(buf)); mp_obj_t inaddr_objs[2] = { - mp_obj_new_str(buf, strlen(buf)), + mp_obj_new_str_from_cstr(buf), mp_obj_new_int(ntohs(addr->sin_port)) }; addrinfo_objs[4] = mp_obj_new_tuple(2, inaddr_objs); diff --git a/ports/esp32/network_common.c b/ports/esp32/network_common.c index 6e957909eafe7..09daa5c045e18 100644 --- a/ports/esp32/network_common.c +++ b/ports/esp32/network_common.c @@ -328,7 +328,7 @@ mp_obj_t esp_ifname(esp_netif_t *netif) { char ifname[NETIF_NAMESIZE + 1] = {0}; mp_obj_t ret = mp_const_none; if (esp_netif_get_netif_impl_name(netif, ifname) == ESP_OK && ifname[0] != 0) { - ret = mp_obj_new_str((char *)ifname, strlen(ifname)); + ret = mp_obj_new_str_from_cstr((char *)ifname); } return ret; } diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index 47a9b72dc15cf..f2813f430d482 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -344,7 +344,7 @@ static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs char ifname[NETIF_NAMESIZE + 1] = {0}; netif_index_to_name(netif_get_index(pppif), ifname); if (ifname[0] != 0) { - val = mp_obj_new_str((char *)ifname, strlen(ifname)); + val = mp_obj_new_str_from_cstr((char *)ifname); } } break; diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index 6d051f0256291..7e802166c116c 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -640,7 +640,7 @@ static mp_obj_t network_wlan_config(size_t n_args, const mp_obj_t *args, mp_map_ case MP_QSTR_essid: switch (self->if_id) { case ESP_IF_WIFI_STA: - val = mp_obj_new_str((char *)cfg.sta.ssid, strlen((char *)cfg.sta.ssid)); + val = mp_obj_new_str_from_cstr((char *)cfg.sta.ssid); break; case ESP_IF_WIFI_AP: val = mp_obj_new_str((char *)cfg.ap.ssid, cfg.ap.ssid_len); diff --git a/ports/esp8266/network_wlan.c b/ports/esp8266/network_wlan.c index 57ad5d8305c1d..bf6ac2538896b 100644 --- a/ports/esp8266/network_wlan.c +++ b/ports/esp8266/network_wlan.c @@ -626,7 +626,7 @@ static mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs case MP_QSTR_ssid: case MP_QSTR_essid: if (self->if_id == STATION_IF) { - val = mp_obj_new_str((char *)cfg.sta.ssid, strlen((char *)cfg.sta.ssid)); + val = mp_obj_new_str_from_cstr((char *)cfg.sta.ssid); } else { val = mp_obj_new_str((char *)cfg.ap.ssid, cfg.ap.ssid_len); } diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index b05e2dade60a8..99a6d8c8c11f1 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -388,7 +388,7 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "# str\n"); // intern string - mp_printf(&mp_plat_print, "%d\n", mp_obj_is_qstr(mp_obj_str_intern(mp_obj_new_str("intern me", 9)))); + mp_printf(&mp_plat_print, "%d\n", mp_obj_is_qstr(mp_obj_str_intern(mp_obj_new_str_from_cstr("intern me")))); } // bytearray @@ -471,12 +471,12 @@ static mp_obj_t extra_coverage(void) { // call mp_call_function_1_protected mp_call_function_1_protected(MP_OBJ_FROM_PTR(&mp_builtin_abs_obj), MP_OBJ_NEW_SMALL_INT(1)); // call mp_call_function_1_protected with invalid args - mp_call_function_1_protected(MP_OBJ_FROM_PTR(&mp_builtin_abs_obj), mp_obj_new_str("abc", 3)); + mp_call_function_1_protected(MP_OBJ_FROM_PTR(&mp_builtin_abs_obj), mp_obj_new_str_from_cstr("abc")); // call mp_call_function_2_protected mp_call_function_2_protected(MP_OBJ_FROM_PTR(&mp_builtin_divmod_obj), MP_OBJ_NEW_SMALL_INT(1), MP_OBJ_NEW_SMALL_INT(1)); // call mp_call_function_2_protected with invalid args - mp_call_function_2_protected(MP_OBJ_FROM_PTR(&mp_builtin_divmod_obj), mp_obj_new_str("abc", 3), mp_obj_new_str("abc", 3)); + mp_call_function_2_protected(MP_OBJ_FROM_PTR(&mp_builtin_divmod_obj), mp_obj_new_str_from_cstr("abc"), mp_obj_new_str_from_cstr("abc")); // mp_obj_int_get_checked with mp_obj_int_t that has a value that is a small integer mp_printf(&mp_plat_print, "%d\n", mp_obj_int_get_checked(mp_obj_int_new_mpz())); @@ -733,7 +733,7 @@ static mp_obj_t extra_coverage(void) { // mp_obj_is_integer accepts ints and booleans mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_integer(MP_OBJ_NEW_SMALL_INT(1)), mp_obj_is_integer(mp_obj_new_int_from_ll(1))); mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_integer(mp_const_true), mp_obj_is_integer(mp_const_false)); - mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_integer(mp_obj_new_str("1", 1)), mp_obj_is_integer(mp_const_none)); + mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_integer(mp_obj_new_str_from_cstr("1")), mp_obj_is_integer(mp_const_none)); // mp_obj_is_int accepts small int and object ints mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_int(MP_OBJ_NEW_SMALL_INT(1)), mp_obj_is_int(mp_obj_new_int_from_ll(1))); diff --git a/ports/unix/main.c b/ports/unix/main.c index 0cf11d37cf8d9..79d1e88ad13f0 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -639,7 +639,7 @@ MP_NOINLINE int main_(int argc, char **argv) { return invalid_args(); } mp_obj_t import_args[4]; - import_args[0] = mp_obj_new_str(argv[a + 1], strlen(argv[a + 1])); + import_args[0] = mp_obj_new_str_from_cstr(argv[a + 1]); import_args[1] = import_args[2] = mp_const_none; // Ask __import__ to handle imported module specially - set its __name__ // to __main__, and also return this leaf module, not top-level package diff --git a/ports/unix/modffi.c b/ports/unix/modffi.c index b7d03e84dde6f..b3cd3380ccab9 100644 --- a/ports/unix/modffi.c +++ b/ports/unix/modffi.c @@ -174,7 +174,7 @@ static mp_obj_t return_ffi_value(ffi_union_t *val, char type) { if (!s) { return mp_const_none; } - return mp_obj_new_str(s, strlen(s)); + return mp_obj_new_str_from_cstr(s); } case 'v': return mp_const_none; diff --git a/ports/unix/modjni.c b/ports/unix/modjni.c index d953e7e015237..dbce61aec1135 100644 --- a/ports/unix/modjni.c +++ b/ports/unix/modjni.c @@ -337,7 +337,7 @@ static mp_obj_t new_jobject(jobject jo) { return mp_const_none; } else if (JJ(IsInstanceOf, jo, String_class)) { const char *s = JJ(GetStringUTFChars, jo, NULL); - mp_obj_t ret = mp_obj_new_str(s, strlen(s)); + mp_obj_t ret = mp_obj_new_str_from_cstr(s); JJ(ReleaseStringUTFChars, jo, s); return ret; } else if (JJ(IsInstanceOf, jo, Class_class)) { diff --git a/ports/unix/modos.c b/ports/unix/modos.c index d5e963583b7a4..92211d689c7d0 100644 --- a/ports/unix/modos.c +++ b/ports/unix/modos.c @@ -40,7 +40,7 @@ static mp_obj_t mp_os_getenv(size_t n_args, const mp_obj_t *args) { } return mp_const_none; } - return mp_obj_new_str(s, strlen(s)); + return mp_obj_new_str_from_cstr(s); } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_os_getenv_obj, 1, 2, mp_os_getenv); diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 314bbfd263b45..2ae5fd22273c1 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -103,7 +103,7 @@ static void vfs_init(void) { int ret = 0; #ifdef CONFIG_DISK_DRIVER_SDMMC - mp_obj_t args[] = { mp_obj_new_str(CONFIG_SDMMC_VOLUME_NAME, strlen(CONFIG_SDMMC_VOLUME_NAME)) }; + mp_obj_t args[] = { mp_obj_new_str_from_cstr(CONFIG_SDMMC_VOLUME_NAME) }; bdev = MP_OBJ_TYPE_GET_SLOT(&zephyr_disk_access_type, make_new)(&zephyr_disk_access_type, ARRAY_SIZE(args), 0, args); mount_point_str = "/sd"; #elif defined(CONFIG_FLASH_MAP) && FLASH_AREA_LABEL_EXISTS(storage) @@ -113,7 +113,7 @@ static void vfs_init(void) { #endif if ((bdev != NULL)) { - mount_point = mp_obj_new_str(mount_point_str, strlen(mount_point_str)); + mount_point = mp_obj_new_str_from_cstr(mount_point_str); ret = mp_vfs_mount_and_chdir_protected(bdev, mount_point); // TODO: if this failed, make a new file system and try to mount again } diff --git a/ports/zephyr/modsocket.c b/ports/zephyr/modsocket.c index 7732e0a64a40d..cef9933b28fa7 100644 --- a/ports/zephyr/modsocket.c +++ b/ports/zephyr/modsocket.c @@ -92,7 +92,7 @@ static mp_obj_t format_inet_addr(struct sockaddr *addr, mp_obj_t port) { net_addr_ntop(addr->sa_family, &sockaddr_in6->sin6_addr, buf, sizeof(buf)); mp_obj_tuple_t *tuple = mp_obj_new_tuple(addr->sa_family == AF_INET ? 2 : 4, NULL); - tuple->items[0] = mp_obj_new_str(buf, strlen(buf)); + tuple->items[0] = mp_obj_new_str_from_cstr(buf); // We employ the fact that port offset is the same for IPv4 & IPv6 // not filled in // tuple->items[1] = mp_obj_new_int(ntohs(((struct sockaddr_in*)addr)->sin_port)); diff --git a/py/binary.c b/py/binary.c index 7c01cfa1c8284..4fc8f751ad39a 100644 --- a/py/binary.c +++ b/py/binary.c @@ -338,7 +338,7 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * return (mp_obj_t)(mp_uint_t)val; } else if (val_type == 'S') { const char *s_val = (const char *)(uintptr_t)(mp_uint_t)val; - return mp_obj_new_str(s_val, strlen(s_val)); + return mp_obj_new_str_from_cstr(s_val); #if MICROPY_PY_BUILTINS_FLOAT } else if (val_type == 'e') { return mp_obj_new_float_from_f(mp_decode_half_float(val)); From 0b58d02f70cb883bf19ceec5536be59984f22d90 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 5 Jul 2024 11:27:25 +1000 Subject: [PATCH 0098/1300] esp32,esp8266: Use new mp_obj_new_str_from_cstr() function. These were missed in the previous commit. Signed-off-by: Damien George --- ports/esp32/network_common.c | 2 +- ports/esp8266/network_wlan.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/esp32/network_common.c b/ports/esp32/network_common.c index 09daa5c045e18..d6bba32146c5c 100644 --- a/ports/esp32/network_common.c +++ b/ports/esp32/network_common.c @@ -166,7 +166,7 @@ static mp_obj_t esp_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map case MP_QSTR_dns: { char addr_str[IPADDR_STRLEN_MAX]; ipaddr_ntoa_r(dns_getserver(0), addr_str, sizeof(addr_str)); - return mp_obj_new_str(addr_str, strlen(addr_str)); + return mp_obj_new_str_from_cstr(addr_str); } default: { mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); diff --git a/ports/esp8266/network_wlan.c b/ports/esp8266/network_wlan.c index bf6ac2538896b..44154ff6d8e54 100644 --- a/ports/esp8266/network_wlan.c +++ b/ports/esp8266/network_wlan.c @@ -345,7 +345,7 @@ static mp_obj_t esp_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map char addr_str[IPADDR_STRLEN_MAX]; ip_addr_t dns_addr = dns_getserver(0); ipaddr_ntoa_r(&dns_addr, addr_str, sizeof(addr_str)); - return mp_obj_new_str(addr_str, strlen(addr_str)); + return mp_obj_new_str_from_cstr(addr_str); } default: { mp_raise_ValueError(MP_ERROR_TEXT("unexpected key")); From 4d16a9cced42ad0a298f1be8b993357abe7398ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20D=C3=B6rre?= Date: Mon, 18 Mar 2024 00:57:22 +0000 Subject: [PATCH 0099/1300] docs: Update docs to replace ifconfig with ipconfig. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow up to 1c6012b0b5c62f18130217f30e73ad3ce4c8c9e6 Signed-off-by: Felix Dörre --- docs/esp32/quickref.rst | 6 +-- docs/esp8266/quickref.rst | 4 +- docs/esp8266/tutorial/network_basics.rst | 12 ++--- docs/library/network.LAN.rst | 2 +- docs/library/network.WIZNET5K.rst | 17 +------ docs/library/network.WLAN.rst | 2 +- docs/library/network.WLANWiPy.rst | 14 ++---- docs/library/network.rst | 58 +++++++++++++++++++++++- docs/mimxrt/quickref.rst | 2 +- docs/reference/mpremote.rst | 4 +- docs/wipy/quickref.rst | 6 +-- docs/wipy/tutorial/wlan.rst | 5 +- 12 files changed, 84 insertions(+), 48 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 2be1dbadc353a..3d3fbe0b1ab34 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -89,7 +89,7 @@ The :mod:`network` module:: wlan.isconnected() # check if the station is connected to an AP wlan.connect('ssid', 'key') # connect to an AP wlan.config('mac') # get the interface's MAC address - wlan.ifconfig() # get the interface's IP/netmask/gw/DNS addresses + wlan.ipconfig('addr4') # get the interface's IPv4 addresses ap = network.WLAN(network.AP_IF) # create access-point interface ap.config(ssid='ESP-AP') # set the SSID of the access point @@ -107,7 +107,7 @@ A useful function for connecting to your local WiFi network is:: wlan.connect('ssid', 'key') while not wlan.isconnected(): pass - print('network config:', wlan.ifconfig()) + print('network config:', wlan.ipconfig('addr4')) Once the network is established the :mod:`socket ` module can be used to create and use TCP/UDP sockets as usual, and the ``requests`` module for @@ -130,7 +130,7 @@ To use the wired interfaces one has to specify the pins and mode :: lan = network.LAN(mdc=PIN_MDC, ...) # Set the pin and mode configuration lan.active(True) # activate the interface - lan.ifconfig() # get the interface's IP/netmask/gw/DNS addresses + lan.ipconfig('addr4') # get the interface's IPv4 addresses The keyword arguments for the constructor defining the PHY type and interface are: diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index ed2199737099a..b130ce65db2b7 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -59,7 +59,7 @@ The :mod:`network` module:: wlan.isconnected() # check if the station is connected to an AP wlan.connect('ssid', 'key') # connect to an AP wlan.config('mac') # get the interface's MAC address - wlan.ifconfig() # get the interface's IP/netmask/gw/DNS addresses + wlan.ipconfig('addr4') # get the interface's IPv4 addresses ap = network.WLAN(network.AP_IF) # create access-point interface ap.active(True) # activate the interface @@ -76,7 +76,7 @@ A useful function for connecting to your local WiFi network is:: wlan.connect('ssid', 'key') while not wlan.isconnected(): pass - print('network config:', wlan.ifconfig()) + print('network config:', wlan.ipconfig('addr4')) Once the network is established the :mod:`socket ` module can be used to create and use TCP/UDP sockets as usual. diff --git a/docs/esp8266/tutorial/network_basics.rst b/docs/esp8266/tutorial/network_basics.rst index dc3cd3dd5e070..9d74a6283ac47 100644 --- a/docs/esp8266/tutorial/network_basics.rst +++ b/docs/esp8266/tutorial/network_basics.rst @@ -19,10 +19,10 @@ You can check if the interfaces are active by:: You can also check the network settings of the interface by:: - >>> ap_if.ifconfig() - ('192.168.4.1', '255.255.255.0', '192.168.4.1', '8.8.8.8') + >>> ap_if.ipconfig('addr4') + ('192.168.4.1', '255.255.255.0') -The returned values are: IP address, netmask, gateway, DNS. +The returned values are: IP address and netmask. Configuration of the WiFi ------------------------- @@ -45,8 +45,8 @@ To check if the connection is established use:: Once established you can check the IP address:: - >>> sta_if.ifconfig() - ('192.168.0.2', '255.255.255.0', '192.168.0.1', '8.8.8.8') + >>> sta_if.ipconfig('addr4') + ('192.168.0.2', '255.255.255.0') You can then disable the access-point interface if you no longer need it:: @@ -64,7 +64,7 @@ connect to your WiFi network:: sta_if.connect('', '') while not sta_if.isconnected(): pass - print('network config:', sta_if.ifconfig()) + print('network config:', sta_if.ipconfig('addr4')) Sockets ------- diff --git a/docs/library/network.LAN.rst b/docs/library/network.LAN.rst index 375e02cefecd0..31ce8e98411c2 100644 --- a/docs/library/network.LAN.rst +++ b/docs/library/network.LAN.rst @@ -10,7 +10,7 @@ Example usage:: import network nic = network.LAN(0) - print(nic.ifconfig()) + print(nic.ipconfig("addr4")) # now use socket as usual ... diff --git a/docs/library/network.WIZNET5K.rst b/docs/library/network.WIZNET5K.rst index c13d43a376ece..a19bd45282399 100644 --- a/docs/library/network.WIZNET5K.rst +++ b/docs/library/network.WIZNET5K.rst @@ -13,7 +13,7 @@ Example usage:: import network nic = network.WIZNET5K(pyb.SPI(1), pyb.Pin.board.X5, pyb.Pin.board.X4) - print(nic.ifconfig()) + print(nic.ipconfig("addr4")) # now use socket as usual ... @@ -51,20 +51,7 @@ Constructors Methods ------- -.. method:: WIZNET5K.isconnected() - - Returns ``True`` if the physical Ethernet link is connected and up. - Returns ``False`` otherwise. - -.. method:: WIZNET5K.ifconfig([(ip, subnet, gateway, dns)]) - - Get/set IP address, subnet mask, gateway and DNS. - - When called with no arguments, this method returns a 4-tuple with the above information. - - To set the above values, pass a 4-tuple with the required information. For example:: - - nic.ifconfig(('192.168.0.4', '255.255.255.0', '192.168.0.1', '8.8.8.8')) +This class implements most methods from `AbstractNIC `, which are documented there. Additional methods are: .. method:: WIZNET5K.regs() diff --git a/docs/library/network.WLAN.rst b/docs/library/network.WLAN.rst index 68cd49769afff..c1eb520961fcb 100644 --- a/docs/library/network.WLAN.rst +++ b/docs/library/network.WLAN.rst @@ -107,7 +107,7 @@ Methods Get or set general network interface parameters. These methods allow to work with additional parameters beyond standard IP configuration (as dealt with by - `WLAN.ifconfig()`). These include network-specific and hardware-specific + `AbstractNIC.ipconfig()`). These include network-specific and hardware-specific parameters. For setting parameters, keyword argument syntax should be used, multiple parameters can be set at once. For querying, parameters name should be quoted as a string, and only one parameter can be queries at time:: diff --git a/docs/library/network.WLANWiPy.rst b/docs/library/network.WLANWiPy.rst index 2a5ba118454bc..4ac82e92c6a58 100644 --- a/docs/library/network.WLANWiPy.rst +++ b/docs/library/network.WLANWiPy.rst @@ -20,7 +20,7 @@ This class provides a driver for the WiFi network processor in the WiPy. Example wlan.connect('your-ssid', auth=(WLAN.WPA2, 'your-key')) while not wlan.isconnected(): time.sleep_ms(50) - print(wlan.ifconfig()) + print(wlan.ipconfig("addr4")) # now use socket as usual ... @@ -96,16 +96,10 @@ Methods In case of STA mode, returns ``True`` if connected to a WiFi access point and has a valid IP address. In AP mode returns ``True`` when a station is connected, ``False`` otherwise. -.. method:: WLANWiPy.ifconfig(if_id=0, config=['dhcp' or configtuple]) +.. method:: WLANWiPy.ipconfig('param') + WLANWiPy.ipconfig(param=value, ...) - With no parameters given returns a 4-tuple of *(ip, subnet_mask, gateway, DNS_server)*. - - if ``'dhcp'`` is passed as a parameter then the DHCP client is enabled and the IP params - are negotiated with the AP. - - If the 4-tuple config is given then a static IP is configured. For instance:: - - wlan.ifconfig(config=('192.168.0.4', '255.255.255.0', '192.168.0.1', '8.8.8.8')) + See :meth:`AbstractNIC.ipconfig `. Supported parameters are: ``dhcp4``, ``addr4``, ``gw4``. .. method:: WLANWiPy.mode([mode]) diff --git a/docs/library/network.rst b/docs/library/network.rst index cc508842942a5..6a436fa85e052 100644 --- a/docs/library/network.rst +++ b/docs/library/network.rst @@ -24,7 +24,7 @@ For example:: print("Waiting for connection...") while not nic.isconnected(): time.sleep(1) - print(nic.ifconfig()) + print(nic.ipconfig("addr4")) # now use socket as usual import socket @@ -113,8 +113,48 @@ parameter should be `id`. connected to the AP. The list contains tuples of the form (MAC, RSSI). +.. method:: AbstractNIC.ipconfig('param') + AbstractNIC.ipconfig(param=value, ...) + + Get or set interface-specific IP-configuration interface parameters. + Supported parameters are the following (availability of a particular + parameter depends on the port and the specific network interface): + + * ``dhcp4`` (``True/False``) obtain an IPv4 address, gateway and dns + server via DHCP. This method does not block and wait for an address + to be obtained. To check if an address was obtained, use the read-only + property ``has_dhcp4``. + * ``gw4`` Get/set the IPv4 default-gateway. + * ``dhcp6`` (``True/False``) obtain a DNS server via stateless DHCPv6. + Obtaining IP Addresses via DHCPv6 is currently not implemented. + * ``autoconf6`` (``True/False``) obtain a stateless IPv6 address via + the network prefix shared in router advertisements. To check if a + stateless address was obtained, use the read-only + property ``has_autoconf6``. + * ``addr4`` (e.g. ``192.168.0.4/24``) obtain the current IPv4 address + and network mask as ``(ip, subnet)``-tuple, regardless of how this + address was obtained. This method can be used to set a static IPv4 + address either as ``(ip, subnet)``-tuple or in CIDR-notation. + * ``addr6`` (e.g. ``fe80::1234:5678``) obtain a list of current IPv6 + addresses as ``(ip, state, preferred_lifetime, valid_lifetime)``-tuple. + This include link-local, slaac and static addresses. + ``preferred_lifetime`` and ``valid_lifetime`` represent the remaining + valid and preferred lifetime of each IPv6 address, in seconds. + ``state`` indicates the current state of the address: + + * ``0x08`` - ``0x0f`` indicates the address is tentative, counting the + number of probes sent. + * ``0x10`` The address is deprecated (but still valid) + * ``0x30`` The address is preferred (and valid) + * ``0x40`` The address is duplicated and can not be used. + + This method can be used to set a static IPv6 + address, by setting this parameter to the address, like ``fe80::1234:5678``. + .. method:: AbstractNIC.ifconfig([(ip, subnet, gateway, dns)]) + .. note:: This function is deprecated, use `ipconfig()` instead. + Get/set IP-level network interface parameters: IP address, subnet mask, gateway and DNS server. When called with no arguments, this method returns a 4-tuple with the above information. To set the above values, pass a @@ -127,7 +167,7 @@ parameter should be `id`. Get or set general network interface parameters. These methods allow to work with additional parameters beyond standard IP configuration (as dealt with by - `ifconfig()`). These include network-specific and hardware-specific + `ipconfig()`). These include network-specific and hardware-specific parameters. For setting parameters, the keyword argument syntax should be used, and multiple parameters can be set at once. For querying, a parameter name should be quoted as a string, and only one @@ -195,6 +235,20 @@ The following are functions available in the network module. The default hostname is typically the name of the board. +.. function:: ipconfig('param') + ipconfig(param=value, ...) + + Get or set global IP-configuration parameters. + Supported parameters are the following (availability of a particular + parameter depends on the port and the specific network interface): + + * ``dns`` Get/set DNS server. This method can support both, IPv4 and + IPv6 addresses. + * ``prefer`` (``4/6``) Specify which address type to return, if a domain + name has both A and AAAA records. Note, that this does not clear the + local DNS cache, so that any previously obtained addresses might not + change. + .. function:: phy_mode([mode]) Get or set the PHY mode. diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index 1a73929bedbe3..79d2a1551e4c3 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -528,7 +528,7 @@ Ethernet. Example usage:: lan.active(True) If there is a DHCP server in the LAN, the IP address is supplied by that server. -Otherwise, the IP address can be set with lan.ifconfig(). The default address +Otherwise, the IP address can be set with lan.ipconfig(addr4="..."). The default address is 192.168.0.1. Teensy 4.1 does not have an Ethernet jack on the board, but PJRC offers an diff --git a/docs/reference/mpremote.rst b/docs/reference/mpremote.rst index bf25e29d93d9c..3ed8a3dbc3be5 100644 --- a/docs/reference/mpremote.rst +++ b/docs/reference/mpremote.rst @@ -469,9 +469,9 @@ An example ``config.py`` might look like: for ap in wl.scan(): print(ap) """,], # Print out nearby WiFi networks. - "wl_ifconfig": [ + "wl_ipconfig": [ "exec", - "import network; sta_if = network.WLAN(network.STA_IF); print(sta_if.ifconfig())", + "import network; sta_if = network.WLAN(network.STA_IF); print(sta_if.ipconfig('addr4'))", """,], # Print ip address of station interface. "test": ["mount", ".", "exec", "import test"], # Mount current directory and run test.py. "demo": ["run", "path/to/demo.py"], # Execute demo.py on the device. diff --git a/docs/wipy/quickref.rst b/docs/wipy/quickref.rst index d6abb8d1c7552..9068ee8a37cbf 100644 --- a/docs/wipy/quickref.rst +++ b/docs/wipy/quickref.rst @@ -184,18 +184,18 @@ WLAN (WiFi) See :ref:`network.WLAN ` and :mod:`machine`. :: - import machine + import machine, network from network import WLAN # configure the WLAN subsystem in station mode (the default is AP) wlan = WLAN(mode=WLAN.STA) # go for fixed IP settings - wlan.ifconfig(config=('192.168.0.107', '255.255.255.0', '192.168.0.1', '8.8.8.8')) + network.ipconfig(dns='8.8.8.8') + wlan.ipconfig(addr4='192.168.0.107/24', gw4='192.168.0.1') wlan.scan() # scan for available networks wlan.connect(ssid='mynetwork', auth=(WLAN.WPA2, 'mynetworkkey')) while not wlan.isconnected(): pass - print(wlan.ifconfig()) # enable wake on WLAN wlan.irq(trigger=WLAN.ANY_EVENT, wake=machine.SLEEP) # go to sleep diff --git a/docs/wipy/tutorial/wlan.rst b/docs/wipy/tutorial/wlan.rst index bdfd3e0a54737..acc67a4b281cb 100644 --- a/docs/wipy/tutorial/wlan.rst +++ b/docs/wipy/tutorial/wlan.rst @@ -50,14 +50,15 @@ Assigning a static IP address when booting If you want your WiPy to connect to your home router after boot-up, and with a fixed IP address so that you can access it via telnet or FTP, use the following script as /flash/boot.py:: - import machine + import machine, network from network import WLAN wlan = WLAN() # get current object, without changing the mode if machine.reset_cause() != machine.SOFT_RESET: wlan.init(WLAN.STA) # configuration below MUST match your home router settings!! - wlan.ifconfig(config=('192.168.178.107', '255.255.255.0', '192.168.178.1', '8.8.8.8')) + network.ipconfig(dns='8.8.8.8') + wlan.ipconfig(addr4='192.168.0.107/24', gw4='192.168.0.1') if not wlan.isconnected(): # change the line below to match your network ssid, security and password From 633586a7162ea508b472ab5e82a512b38eff370f Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 5 Jul 2024 17:04:24 +1000 Subject: [PATCH 0100/1300] tests/thread/stress_aes.py: Fix logic waiting for finished threads. Because the main thread executes `thread_entry()` it means there's an additional one added to `count`, so the test must wait for the count to reach `n_thread + 1`. Signed-off-by: Damien George --- tests/thread/stress_aes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/thread/stress_aes.py b/tests/thread/stress_aes.py index b25da855aeffc..9f4a4e6aebad5 100644 --- a/tests/thread/stress_aes.py +++ b/tests/thread/stress_aes.py @@ -282,6 +282,6 @@ def thread_entry(n_loop): for i in range(n_thread): _thread.start_new_thread(thread_entry, (n_loop,)) thread_entry(n_loop) - while count.value < n_thread: + while count.value < n_thread + 1: time.sleep(1) print("done") From 358e501e75c028133cff1045d2062ae31206a22c Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 5 Jul 2024 17:07:30 +1000 Subject: [PATCH 0101/1300] tests/stress/bytecode_limit.py: Make test more robust with low memory. A target may have enough RAM to run the n=433 test but then run out of RAM on the n=432 test. So allow the test to skip on the n=432 case before it prints any output. Signed-off-by: Damien George --- tests/stress/bytecode_limit.py | 6 +++++- tests/stress/bytecode_limit.py.exp | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/stress/bytecode_limit.py b/tests/stress/bytecode_limit.py index ad090637f6a68..948d7668da551 100644 --- a/tests/stress/bytecode_limit.py +++ b/tests/stress/bytecode_limit.py @@ -3,14 +3,18 @@ body = " with f()()() as a:\n try:\n f()()()\n except Exception:\n pass\n" # Test overflow of jump offset. +# Print results at the end in case an intermediate value of n fails with MemoryError. +results = [] for n in (433, 432, 431, 430): try: exec("cond = 0\nif cond:\n" + body * n + "else:\n print('cond false')\n") + results.append((n, "ok")) except MemoryError: print("SKIP") raise SystemExit except RuntimeError: - print("RuntimeError") + results.append((n, "RuntimeError")) +print(results) # Test changing size of code info (source line/bytecode mapping) due to changing # bytecode size in the final passes. This test is very specific to how the diff --git a/tests/stress/bytecode_limit.py.exp b/tests/stress/bytecode_limit.py.exp index 1d892250b01e6..cda52b1b97348 100644 --- a/tests/stress/bytecode_limit.py.exp +++ b/tests/stress/bytecode_limit.py.exp @@ -1,5 +1,4 @@ -RuntimeError -RuntimeError cond false cond false +[(433, 'RuntimeError'), (432, 'RuntimeError'), (431, 'ok'), (430, 'ok')] [123] From 2be45dd682f0ce01e4a1061375e96ce2c501a187 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 6 Jul 2024 09:18:22 +0200 Subject: [PATCH 0102/1300] extmod/modmachine: Allow more than one argument to machine.freq(). The limit is set by a `MICROPY_PY_MACHINE_FREQ_NUM_ARGS_MAX` define, which defaults to 1 and is set for stm32 to 4. For stm32 this fixes a regression introduced in commit e1ec6af654b1c5c4a973b6c6b029ee68bb92eb89 where the maximum number of arguments was changed from 4 to 1. Signed-off-by: robert-hh --- extmod/modmachine.c | 2 +- ports/stm32/mpconfigport.h | 1 + py/mpconfig.h | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/extmod/modmachine.c b/extmod/modmachine.c index 2a7e315bbb0ce..2fe72817b6c2a 100644 --- a/extmod/modmachine.c +++ b/extmod/modmachine.c @@ -109,7 +109,7 @@ static mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { return mp_const_none; } } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, MICROPY_PY_MACHINE_FREQ_NUM_ARGS_MAX, machine_freq); static mp_obj_t machine_lightsleep(size_t n_args, const mp_obj_t *args) { mp_machine_lightsleep(n_args, args); diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 9e1e24cf23828..bd1ab671e071d 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -113,6 +113,7 @@ #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/stm32/modmachine.c" #define MICROPY_PY_MACHINE_RESET (1) #define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1) +#define MICROPY_PY_MACHINE_FREQ_NUM_ARGS_MAX (4) #define MICROPY_PY_MACHINE_BOOTLOADER (1) #define MICROPY_PY_MACHINE_ADC (1) #define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/stm32/machine_adc.c" diff --git a/py/mpconfig.h b/py/mpconfig.h index bc6bf75fe57c5..346d0c21e1222 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1736,6 +1736,11 @@ typedef double mp_float_t; #define MICROPY_PY_MACHINE_RESET (0) #endif +// Maximum number of arguments for machine.freq() +#ifndef MICROPY_PY_MACHINE_FREQ_NUM_ARGS_MAX +#define MICROPY_PY_MACHINE_FREQ_NUM_ARGS_MAX (1) +#endif + // Whether to include: bitstream #ifndef MICROPY_PY_MACHINE_BITSTREAM #define MICROPY_PY_MACHINE_BITSTREAM (0) From 20b00ca501f5e7ff096ae3e31c3e7e7d99963d23 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 24 Apr 2024 19:58:06 +0200 Subject: [PATCH 0103/1300] extmod/network_nina: Fix the AP security mode constants. The only AP security mode supported is actually WPA/WPA2 not WEP. The firmware command `0x19` starts the AP using `WIFI_AUTH_WPA_WPA2_PSK` mode. There are no functional changes in this commit, it just fixes the constant names and removes the useless sanity checks for WEP. Signed-off-by: iabdalkader --- drivers/ninaw10/nina_wifi_drv.c | 8 ++++---- extmod/network_ninaw10.c | 14 +------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/drivers/ninaw10/nina_wifi_drv.c b/drivers/ninaw10/nina_wifi_drv.c index 6e4df84294c3d..1aaeec5a09c84 100644 --- a/drivers/ninaw10/nina_wifi_drv.c +++ b/drivers/ninaw10/nina_wifi_drv.c @@ -95,7 +95,7 @@ typedef enum { // AP mode commands. NINA_CMD_START_AP_OPEN = 0x18, - NINA_CMD_START_AP_WEP = 0x19, + NINA_CMD_START_AP_WPA = 0x19, // AP mode scan commands. NINA_CMD_AP_START_SCAN = 0x36, @@ -395,7 +395,7 @@ int nina_start_ap(const char *ssid, uint8_t security, const char *key, uint16_t uint8_t status = NINA_STATUS_AP_FAILED; if ((key == NULL && security != NINA_SEC_OPEN) || - (security != NINA_SEC_OPEN && security != NINA_SEC_WEP)) { + (security != NINA_SEC_OPEN && security != NINA_SEC_WPA_PSK)) { return -1; } @@ -406,8 +406,8 @@ int nina_start_ap(const char *ssid, uint8_t security, const char *key, uint16_t return -1; } break; - case NINA_SEC_WEP: - if (nina_send_command_read_ack(NINA_CMD_START_AP_WEP, + case NINA_SEC_WPA_PSK: + if (nina_send_command_read_ack(NINA_CMD_START_AP_WPA, 3, ARG_8BITS, NINA_ARGS(ARG_STR(ssid), ARG_STR(key), ARG_BYTE(channel))) != SPI_ACK) { return -1; } diff --git a/extmod/network_ninaw10.c b/extmod/network_ninaw10.c index 62961f871fb8d..a9abd5776e053 100644 --- a/extmod/network_ninaw10.c +++ b/extmod/network_ninaw10.c @@ -266,7 +266,7 @@ static mp_obj_t network_ninaw10_connect(mp_uint_t n_args, const mp_obj_t *pos_ar static const mp_arg_t allowed_args[] = { { MP_QSTR_ssid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_key, MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NINA_SEC_WPA_PSK} }, { MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, }; @@ -277,7 +277,6 @@ static mp_obj_t network_ninaw10_connect(mp_uint_t n_args, const mp_obj_t *pos_ar // get ssid const char *ssid = mp_obj_str_get_str(args[ARG_ssid].u_obj); - if (strlen(ssid) == 0) { mp_raise_ValueError(MP_ERROR_TEXT("SSID can't be empty")); } @@ -290,12 +289,6 @@ static mp_obj_t network_ninaw10_connect(mp_uint_t n_args, const mp_obj_t *pos_ar // get security mode mp_uint_t security = args[ARG_security].u_int; - if (security == -1 && self->itf == MOD_NETWORK_STA_IF) { - security = NINA_SEC_WPA_PSK; - } else if (security == -1 && self->itf == MOD_NETWORK_AP_IF) { - security = NINA_SEC_WEP; - } - // Ensure that the key is not empty if a security mode is used. if (security != NINA_SEC_OPEN && strlen(key) == 0) { mp_raise_ValueError(MP_ERROR_TEXT("key can't be empty")); @@ -326,11 +319,6 @@ static mp_obj_t network_ninaw10_connect(mp_uint_t n_args, const mp_obj_t *pos_ar soft_timer_reinsert(&mp_wifi_poll_timer, NINAW10_POLL_INTERVAL); } else { mp_uint_t channel = args[ARG_channel].u_int; - - if (security != NINA_SEC_OPEN && security != NINA_SEC_WEP) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("AP mode only supports WEP or OPEN security modes")); - } - // Initialize WiFi in AP mode. if (nina_start_ap(ssid, security, key, channel) != 0) { mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("failed to start in AP mode")); From ee1036023ef199636d96e70c6c7ed587ccaab92e Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 15 May 2023 11:17:27 +0200 Subject: [PATCH 0104/1300] extmod/machine_spi: Support firstbit=LSB for machine.SoftSPI. Being able to send data out in LSB format can be useful, and having support in the low-level driver is much better than requiring Python code to reorder the bits before sending them / after receiving them. In particular if the hardware does not support the LSB format (eg RP2040) then one needs to use the SoftSPI in LSB mode. For this change a default definition of `MICROPY_PY_MACHINE_SPI_MSB/_LSB` was added to `py/mpconfig.h`, making them available to all ports. The identical defines in `esp32/mpconfigport.h` were deleted. Resolves issues #5340, #11404. Signed-off-by: robert-hh --- drivers/bus/softspi.c | 30 ++++++++++++++++++++---------- drivers/bus/spi.h | 1 + extmod/machine_spi.c | 20 ++++++++------------ ports/esp32/mpconfigport.h | 2 -- py/mpconfig.h | 6 ++++++ 5 files changed, 35 insertions(+), 24 deletions(-) diff --git a/drivers/bus/softspi.c b/drivers/bus/softspi.c index bc12d89d3b7e6..7d687a1a2fdf4 100644 --- a/drivers/bus/softspi.c +++ b/drivers/bus/softspi.c @@ -44,28 +44,36 @@ int mp_soft_spi_ioctl(void *self_in, uint32_t cmd) { return 0; } +static uint8_t swap_bits(uint8_t byte) { + const static uint8_t swap_table[16] = { + 0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, + 0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f + }; + return ((swap_table[byte & 0x0f] << 4) | swap_table[byte >> 4]); +} + void mp_soft_spi_transfer(void *self_in, size_t len, const uint8_t *src, uint8_t *dest) { mp_soft_spi_obj_t *self = (mp_soft_spi_obj_t*)self_in; uint32_t delay_half = self->delay_half; - // only MSB transfer is implemented - // If a port defines MICROPY_HW_SOFTSPI_MIN_DELAY, and the configured // delay_half is equal to this value, then the software SPI implementation // will run as fast as possible, limited only by CPU speed and GPIO time. #ifdef MICROPY_HW_SOFTSPI_MIN_DELAY if (delay_half == MICROPY_HW_SOFTSPI_MIN_DELAY) { for (size_t i = 0; i < len; ++i) { - uint8_t data_out = src[i]; + uint8_t data_out = self->firstbit != MICROPY_PY_MACHINE_SPI_MSB ? + src[i] : swap_bits(src[i]); uint8_t data_in = 0; - for (int j = 0; j < 8; ++j, data_out <<= 1) { - mp_hal_pin_write(self->mosi, (data_out >> 7) & 1); + for (int j = 0; j < 8; ++j, data_out >>= 1) { + mp_hal_pin_write(self->mosi, data_out & 1); mp_hal_pin_write(self->sck, 1 - self->polarity); data_in = (data_in << 1) | mp_hal_pin_read(self->miso); mp_hal_pin_write(self->sck, self->polarity); } if (dest != NULL) { - dest[i] = data_in; + dest[i] = self->firstbit == MICROPY_PY_MACHINE_SPI_MSB ? + data_in : swap_bits(data_in); } } return; @@ -73,10 +81,11 @@ void mp_soft_spi_transfer(void *self_in, size_t len, const uint8_t *src, uint8_t #endif for (size_t i = 0; i < len; ++i) { - uint8_t data_out = src[i]; + uint8_t data_out = self->firstbit != MICROPY_PY_MACHINE_SPI_MSB ? + src[i] : swap_bits(src[i]); uint8_t data_in = 0; - for (int j = 0; j < 8; ++j, data_out <<= 1) { - mp_hal_pin_write(self->mosi, (data_out >> 7) & 1); + for (int j = 0; j < 8; ++j, data_out >>= 1) { + mp_hal_pin_write(self->mosi, data_out & 1); if (self->phase == 0) { mp_hal_delay_us_fast(delay_half); mp_hal_pin_write(self->sck, 1 - self->polarity); @@ -94,7 +103,8 @@ void mp_soft_spi_transfer(void *self_in, size_t len, const uint8_t *src, uint8_t } } if (dest != NULL) { - dest[i] = data_in; + dest[i] = self->firstbit == MICROPY_PY_MACHINE_SPI_MSB ? + data_in : swap_bits(data_in); } } } diff --git a/drivers/bus/spi.h b/drivers/bus/spi.h index 6d1b9c2f832ef..df7f790df80f3 100644 --- a/drivers/bus/spi.h +++ b/drivers/bus/spi.h @@ -42,6 +42,7 @@ typedef struct _mp_soft_spi_obj_t { uint32_t delay_half; // microsecond delay for half SCK period uint8_t polarity; uint8_t phase; + uint8_t firstbit; mp_hal_pin_obj_t sck; mp_hal_pin_obj_t mosi; mp_hal_pin_obj_t miso; diff --git a/extmod/machine_spi.c b/extmod/machine_spi.c index a1d18c905247a..5be30e9476b90 100644 --- a/extmod/machine_spi.c +++ b/extmod/machine_spi.c @@ -33,12 +33,6 @@ #include "extmod/modmachine.h" -// if a port didn't define MSB/LSB constants then provide them -#ifndef MICROPY_PY_MACHINE_SPI_MSB -#define MICROPY_PY_MACHINE_SPI_MSB (0) -#define MICROPY_PY_MACHINE_SPI_LSB (1) -#endif - /******************************************************************************/ // MicroPython bindings for generic machine.SPI @@ -154,9 +148,9 @@ static uint32_t baudrate_to_delay_half(uint32_t baudrate) { static void mp_machine_soft_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { mp_machine_soft_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "SoftSPI(baudrate=%u, polarity=%u, phase=%u," + mp_printf(print, "SoftSPI(baudrate=%u, polarity=%u, phase=%u, firstbit=%u," " sck=" MP_HAL_PIN_FMT ", mosi=" MP_HAL_PIN_FMT ", miso=" MP_HAL_PIN_FMT ")", - baudrate_from_delay_half(self->spi.delay_half), self->spi.polarity, self->spi.phase, + baudrate_from_delay_half(self->spi.delay_half), self->spi.polarity, self->spi.phase, self->spi.firstbit, mp_hal_pin_name(self->spi.sck), mp_hal_pin_name(self->spi.mosi), mp_hal_pin_name(self->spi.miso)); } @@ -185,9 +179,7 @@ static mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n if (args[ARG_bits].u_int != 8) { mp_raise_ValueError(MP_ERROR_TEXT("bits must be 8")); } - if (args[ARG_firstbit].u_int != MICROPY_PY_MACHINE_SPI_MSB) { - mp_raise_ValueError(MP_ERROR_TEXT("firstbit must be MSB")); - } + self->spi.firstbit = args[ARG_firstbit].u_int; if (args[ARG_sck].u_obj == MP_OBJ_NULL || args[ARG_mosi].u_obj == MP_OBJ_NULL || args[ARG_miso].u_obj == MP_OBJ_NULL) { @@ -206,11 +198,12 @@ static mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n static void mp_machine_soft_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t *)self_in; - enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_sck, ARG_mosi, ARG_miso }; + enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; static const mp_arg_t allowed_args[] = { { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_polarity, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_phase, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, @@ -227,6 +220,9 @@ static void mp_machine_soft_spi_init(mp_obj_base_t *self_in, size_t n_args, cons if (args[ARG_phase].u_int != -1) { self->spi.phase = args[ARG_phase].u_int; } + if (args[ARG_firstbit].u_int != -1) { + self->spi.firstbit = args[ARG_firstbit].u_int; + } if (args[ARG_sck].u_obj != MP_OBJ_NULL) { self->spi.sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj); } diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 0afb12f85c1ac..558fa6b8c602c 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -136,8 +136,6 @@ #define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) #define MICROPY_PY_MACHINE_SPI (1) -#define MICROPY_PY_MACHINE_SPI_MSB (0) -#define MICROPY_PY_MACHINE_SPI_LSB (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) #ifndef MICROPY_PY_MACHINE_DAC #define MICROPY_PY_MACHINE_DAC (SOC_DAC_SUPPORTED) diff --git a/py/mpconfig.h b/py/mpconfig.h index 346d0c21e1222..98893ceb6d97b 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1784,6 +1784,12 @@ typedef double mp_float_t; #define MICROPY_PY_MACHINE_SOFTSPI (0) #endif +// Values of SPI.MSB and SPI.LSB constants +#ifndef MICROPY_PY_MACHINE_SPI_MSB +#define MICROPY_PY_MACHINE_SPI_MSB (0) +#define MICROPY_PY_MACHINE_SPI_LSB (1) +#endif + // Whether to provide the "machine.Timer" class #ifndef MICROPY_PY_MACHINE_TIMER #define MICROPY_PY_MACHINE_TIMER (0) From 4fdad8eabef17aa8b920484e0af7db5ad9889167 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 8 Jul 2024 16:55:56 +0200 Subject: [PATCH 0105/1300] extmod/modre: Rename re_exec to re_exec_helper to avoid clash on BSD. The `re_exec` symbol is the name of a FreeBSD regex function, so needs to be renamed to avoid a clash when building on FreeBSD. (This clash was fixed once before but then accidentally reintroduced by the u-module renaming in 7f5d5c72718af773db751269c6ae14037b9c0727.) Fixes issue #15430. clarify as helper function --- extmod/modre.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extmod/modre.c b/extmod/modre.c index 2a3fdfd3505f3..f3d2e302a0374 100644 --- a/extmod/modre.c +++ b/extmod/modre.c @@ -194,7 +194,8 @@ static void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t mp_printf(print, "", self); } -static mp_obj_t re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { +// 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; if (mp_obj_is_type(args[0], (mp_obj_type_t *)&re_type)) { @@ -223,12 +224,12 @@ static mp_obj_t re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { } static mp_obj_t re_match(size_t n_args, const mp_obj_t *args) { - return re_exec(true, n_args, args); + return re_exec_helper(true, n_args, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_match_obj, 2, 4, re_match); static mp_obj_t re_search(size_t n_args, const mp_obj_t *args) { - return re_exec(false, n_args, args); + return re_exec_helper(false, n_args, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_search_obj, 2, 4, re_search); From abbce268afb351d623c2073a4e4ae62a9e71889a Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 14 Jul 2024 23:47:21 +1000 Subject: [PATCH 0106/1300] github/workflows: Use macos-latest for unix macos CI. macos-11.0 is no longer available. With this change in the macos version, some tests which previously failed now pass, and some different tests now fail. Exclude those that fail from the CI until they can be fixed properly. Signed-off-by: Damien George --- .github/workflows/ports_unix.yml | 2 +- tools/ci.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index 4473847db61ef..c83f67db875b3 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -194,7 +194,7 @@ jobs: run: tests/run-tests.py --print-failures macos: - runs-on: macos-11.0 + runs-on: macos-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 diff --git a/tools/ci.sh b/tools/ci.sh index 9c54d55a4e846..53fcc8b5a3857 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -647,9 +647,9 @@ function ci_unix_macos_build { function ci_unix_macos_run_tests { # Issues with macOS tests: - # - import_pkg7 has a problem with relative imports - # - random_basic has a problem with getrandbits(0) - (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-standard/micropython ./run-tests.py --exclude 'import_pkg7.py' --exclude 'random_basic.py') + # - float_parse and float_parse_doubleprec parse/print floats out by a few mantissa bits + # - ffi_callback crashes for an unknown reason + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-standard/micropython ./run-tests.py --exclude '(float_parse|float_parse_doubleprec|ffi_callback).py') } function ci_unix_qemu_mips_setup { From 55e75c4ad4131e72a452e6190473098e8a0521c2 Mon Sep 17 00:00:00 2001 From: Lennart Date: Mon, 8 Jul 2024 14:30:01 +0200 Subject: [PATCH 0107/1300] unix/modtermios: Add more baudrate options. This adds some more baudrate option as they are available in the termios.h header - up to a point that seems reasonable in an embedded context. Signed-off-by: Lennart Schierling --- ports/unix/modtermios.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ports/unix/modtermios.c b/ports/unix/modtermios.c index b1ad9a450e0c8..d7b94038aa138 100644 --- a/ports/unix/modtermios.c +++ b/ports/unix/modtermios.c @@ -141,6 +141,27 @@ static const mp_rom_map_elem_t mp_module_termios_globals_table[] = { #ifdef B115200 C(B115200), #endif + #ifdef B230400 + C(B230400), + #endif + #ifdef B460800 + C(B460800), + #endif + #ifdef B500000 + C(B500000), + #endif + #ifdef B576000 + C(B576000), + #endif + #ifdef B921600 + C(B921600), + #endif + #ifdef B1000000 + C(B1000000), + #endif + #ifdef B1152000 + C(B1152000) + #endif #undef C }; From 220088fff679aa00feaaa1a842bdb1dddc10ff26 Mon Sep 17 00:00:00 2001 From: Leo Chung Date: Tue, 18 Jul 2023 13:46:28 +0800 Subject: [PATCH 0108/1300] unix/mpbthciport: Remove thread detached attribute. A detached thread is not joinable, and the behavior maybe undefined. Signed-off-by: Leo Chung --- ports/unix/mpbthciport.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ports/unix/mpbthciport.c b/ports/unix/mpbthciport.c index 95c39f559910d..765146677e8c5 100644 --- a/ports/unix/mpbthciport.c +++ b/ports/unix/mpbthciport.c @@ -196,10 +196,7 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { } // Create a thread to run the polling loop. - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&hci_poll_thread_id, &attr, &hci_poll_thread, NULL); + pthread_create(&hci_poll_thread_id, NULL, &hci_poll_thread, NULL); return 0; } From b4b4d161c2fba54de7f4b297af66a2a807d62f98 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 17 Jul 2024 14:59:55 +1000 Subject: [PATCH 0109/1300] unix/mpbtstackport_usb: Remove thread detached attribute. As in the previous commit, the thread is later joined so can't be detached. Signed-off-by: Damien George --- ports/unix/mpbtstackport_usb.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ports/unix/mpbtstackport_usb.c b/ports/unix/mpbtstackport_usb.c index 8b1d1fff2189f..a924fc3ca912a 100644 --- a/ports/unix/mpbtstackport_usb.c +++ b/ports/unix/mpbtstackport_usb.c @@ -110,10 +110,7 @@ static void *btstack_thread(void *arg) { void mp_bluetooth_btstack_port_start(void) { // Create a thread to run the btstack loop. - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&bstack_thread_id, &attr, &btstack_thread, NULL); + pthread_create(&bstack_thread_id, NULL, &btstack_thread, NULL); } #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_USB From fce3cea2440e0242fc4cf5c1825368795f540470 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jul 2024 10:03:28 +1000 Subject: [PATCH 0110/1300] tests/extmod: Make get_event_loop tests compatible with CPython 3.12. Follow up to 2e852522b178e6e9b2f0cdb954ba44aa9e7d7c0d: instead of having .exp files for the get_event_loop tests, tweak them so they are compatible with CPython 3.12. This requires calling `asyncio.set_event_loop()` so there is an active event loop and `asyncio.get_event_loop()` succeeds without a warning. Signed-off-by: Damien George --- tests/extmod/asyncio_get_event_loop.py | 6 +++++- tests/extmod/asyncio_get_event_loop.py.exp | 2 -- tests/extmod/asyncio_new_event_loop.py | 6 +++++- tests/extmod/asyncio_new_event_loop.py.exp | 6 ------ 4 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 tests/extmod/asyncio_get_event_loop.py.exp delete mode 100644 tests/extmod/asyncio_new_event_loop.py.exp diff --git a/tests/extmod/asyncio_get_event_loop.py b/tests/extmod/asyncio_get_event_loop.py index bbc1a6b2dce95..6ecbb13b57a6a 100644 --- a/tests/extmod/asyncio_get_event_loop.py +++ b/tests/extmod/asyncio_get_event_loop.py @@ -1,5 +1,4 @@ # Test get_event_loop() -# Note: CPython deprecated get_event_loop() so this test needs a .exp try: import asyncio @@ -7,6 +6,11 @@ print("SKIP") raise SystemExit +# CPython 3.12 deprecated calling get_event_loop() when there is no current event +# loop, so to make this test run on CPython requires setting the event loop. +if hasattr(asyncio, "set_event_loop"): + asyncio.set_event_loop(asyncio.new_event_loop()) + async def main(): print("start") diff --git a/tests/extmod/asyncio_get_event_loop.py.exp b/tests/extmod/asyncio_get_event_loop.py.exp deleted file mode 100644 index 5d0fb3b2d2edd..0000000000000 --- a/tests/extmod/asyncio_get_event_loop.py.exp +++ /dev/null @@ -1,2 +0,0 @@ -start -end diff --git a/tests/extmod/asyncio_new_event_loop.py b/tests/extmod/asyncio_new_event_loop.py index 5bb31f1292bb1..bebc3bf70cc57 100644 --- a/tests/extmod/asyncio_new_event_loop.py +++ b/tests/extmod/asyncio_new_event_loop.py @@ -1,5 +1,4 @@ # Test Loop.new_event_loop() -# Note: CPython deprecated get_event_loop() so this test needs a .exp try: import asyncio @@ -7,6 +6,11 @@ print("SKIP") raise SystemExit +# CPython 3.12 deprecated calling get_event_loop() when there is no current event +# loop, so to make this test run on CPython requires setting the event loop. +if hasattr(asyncio, "set_event_loop"): + asyncio.set_event_loop(asyncio.new_event_loop()) + async def task(): for i in range(4): diff --git a/tests/extmod/asyncio_new_event_loop.py.exp b/tests/extmod/asyncio_new_event_loop.py.exp deleted file mode 100644 index 9e104fda39c94..0000000000000 --- a/tests/extmod/asyncio_new_event_loop.py.exp +++ /dev/null @@ -1,6 +0,0 @@ -start -task 0 -stop -start -task 0 -stop From e00d80d9e27552760603b507c77aad6f5705174f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jul 2024 12:35:42 +1000 Subject: [PATCH 0111/1300] py: Remove 5 TODOs in emitbc, objrange and repl. These TODOs don't need to be done: - Calling functions with keyword arguments is less common than without them, so adding an extra byte overhead to all calls regardless of whether they use keywords or not would overall increase generated bytecode size. - Restricting `range` objects to machine-sized ints has been adequate for a long time now, so no need to change that and make it more complicated and slower. - Printing spaces in tab completion does not need to be optimised. Signed-off-by: Damien George --- py/emitbc.c | 4 ++-- py/objrange.c | 2 -- py/repl.c | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/py/emitbc.c b/py/emitbc.c index f6bb229ba0d87..0fbda56fdb006 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -794,10 +794,10 @@ static void emit_bc_call_function_method_helper(emit_t *emit, int stack_adj, mp_ // each positional arg is one object, each kwarg is two objects, the key // and the value and one extra object for the star args bitmap. stack_adj -= (int)n_positional + 2 * (int)n_keyword + 1; - emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base + 1, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? + emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base + 1, (n_keyword << 8) | n_positional); } else { stack_adj -= (int)n_positional + 2 * (int)n_keyword; - emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? + emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base, (n_keyword << 8) | n_positional); } } diff --git a/py/objrange.c b/py/objrange.c index 9a4ecd3fcd7f2..5ccb04fba60c8 100644 --- a/py/objrange.c +++ b/py/objrange.c @@ -33,7 +33,6 @@ typedef struct _mp_obj_range_it_t { mp_obj_base_t base; - // TODO make these values generic objects or something mp_int_t cur; mp_int_t stop; mp_int_t step; @@ -72,7 +71,6 @@ static mp_obj_t mp_obj_new_range_iterator(mp_int_t cur, mp_int_t stop, mp_int_t typedef struct _mp_obj_range_t { mp_obj_base_t base; - // TODO make these values generic objects or something mp_int_t start; mp_int_t stop; mp_int_t step; diff --git a/py/repl.c b/py/repl.c index b79a2d3c42783..87c171cc87283 100644 --- a/py/repl.c +++ b/py/repl.c @@ -225,7 +225,6 @@ static void print_completions(const mp_print_t *print, gap += WORD_SLOT_LEN; } if (line_len + gap + d_len <= MAX_LINE_LEN) { - // TODO optimise printing of gap? for (int j = 0; j < gap; ++j) { mp_print_str(print, " "); } From 96007e7de55810b20134b050c80cbc7a6d1d5a57 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jul 2024 12:44:44 +1000 Subject: [PATCH 0112/1300] py/lexer: Add static assert that token enum values all fit in a byte. Signed-off-by: Damien George --- py/lexer.c | 4 +++- py/lexer.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/py/lexer.c b/py/lexer.c index 48497663c58d1..98a10c87b2e5a 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -228,7 +228,6 @@ static const char *const tok_enc = "=e=" // = == "!."; // start of special cases: != . ... -// TODO static assert that number of tokens is less than 256 so we can safely make this table with byte sized entries static const uint8_t tok_enc_kind[] = { MP_TOKEN_DEL_PAREN_OPEN, MP_TOKEN_DEL_PAREN_CLOSE, MP_TOKEN_DEL_BRACKET_OPEN, MP_TOKEN_DEL_BRACKET_CLOSE, @@ -774,6 +773,9 @@ void mp_lexer_to_next(mp_lexer_t *lex) { } else { // search for encoded delimiter or operator + // assert that the token enum value fits in a byte, so they all fit in tok_enc_kind + MP_STATIC_ASSERT(MP_TOKEN_NUMBER_OF <= 256); + const char *t = tok_enc; size_t tok_enc_index = 0; for (; *t != 0 && !is_char(lex, *t); t += 1) { diff --git a/py/lexer.h b/py/lexer.h index e0b506b20b66e..6e6c3e8f23e06 100644 --- a/py/lexer.h +++ b/py/lexer.h @@ -152,6 +152,8 @@ typedef enum _mp_token_kind_t { MP_TOKEN_DEL_SEMICOLON, MP_TOKEN_DEL_EQUAL, MP_TOKEN_DEL_MINUS_MORE, + + MP_TOKEN_NUMBER_OF, } mp_token_kind_t; // this data structure is exposed for efficiency From 1548132979ab6facd0d3d98fd381c5178330fa20 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jul 2024 12:45:05 +1000 Subject: [PATCH 0113/1300] py/sequence: Remove unused len argument from mp_seq_extract_slice. Also put this function inside the `MICROPY_PY_BUILTINS_SLICE` guard, because it's only usable when that option is enabled. Signed-off-by: Damien George --- py/obj.h | 2 +- py/objlist.c | 2 +- py/sequence.c | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/py/obj.h b/py/obj.h index 82f67fad4d96c..46d42baf4bfc1 100644 --- a/py/obj.h +++ b/py/obj.h @@ -1258,7 +1258,7 @@ bool mp_seq_cmp_bytes(mp_uint_t op, const byte *data1, size_t len1, const byte * bool mp_seq_cmp_objs(mp_uint_t op, const mp_obj_t *items1, size_t len1, const mp_obj_t *items2, size_t len2); mp_obj_t mp_seq_index_obj(const mp_obj_t *items, size_t len, size_t n_args, const mp_obj_t *args); mp_obj_t mp_seq_count_obj(const mp_obj_t *items, size_t len, mp_obj_t value); -mp_obj_t mp_seq_extract_slice(size_t len, const mp_obj_t *seq, mp_bound_slice_t *indexes); +mp_obj_t mp_seq_extract_slice(const mp_obj_t *seq, mp_bound_slice_t *indexes); // Helper to clear stale pointers from allocated, but unused memory, to preclude GC problems #define mp_seq_clear(start, len, alloc_len, item_sz) memset((byte *)(start) + (len) * (item_sz), 0, ((alloc_len) - (len)) * (item_sz)) diff --git a/py/objlist.c b/py/objlist.c index 2198beb839f14..9c8cd0e86c844 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -188,7 +188,7 @@ static mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { if (mp_obj_is_type(index, &mp_type_slice)) { mp_bound_slice_t slice; if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) { - return mp_seq_extract_slice(self->len, self->items, &slice); + return mp_seq_extract_slice(self->items, &slice); } mp_obj_list_t *res = list_new(slice.stop - slice.start); mp_seq_copy(res->items, self->items + slice.start, res->len, mp_obj_t); diff --git a/py/sequence.c b/py/sequence.c index 58386074312ed..9eb67ada6e82e 100644 --- a/py/sequence.c +++ b/py/sequence.c @@ -63,11 +63,7 @@ bool mp_seq_get_fast_slice_indexes(mp_uint_t len, mp_obj_t slice, mp_bound_slice return indexes->step == 1; } -#endif - -mp_obj_t mp_seq_extract_slice(size_t len, const mp_obj_t *seq, mp_bound_slice_t *indexes) { - (void)len; // TODO can we remove len from the arg list? - +mp_obj_t mp_seq_extract_slice(const mp_obj_t *seq, mp_bound_slice_t *indexes) { mp_int_t start = indexes->start, stop = indexes->stop; mp_int_t step = indexes->step; @@ -87,6 +83,8 @@ mp_obj_t mp_seq_extract_slice(size_t len, const mp_obj_t *seq, mp_bound_slice_t return res; } +#endif + // Special-case comparison function for sequences of bytes // Don't pass MP_BINARY_OP_NOT_EQUAL here bool mp_seq_cmp_bytes(mp_uint_t op, const byte *data1, size_t len1, const byte *data2, size_t len2) { From 5147dc5de5ac1eb0c733005e4fbd665bcffd74d4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 19 Jul 2024 10:35:16 +1000 Subject: [PATCH 0114/1300] py/gc: Remove commented-out functions. These are old, unused, and most of them no longer compile. The `gc_test()` function is superseded by the test suite. Signed-off-by: Damien George --- py/gc.c | 77 --------------------------------------------------------- 1 file changed, 77 deletions(-) diff --git a/py/gc.c b/py/gc.c index 8a03ce5264e89..bee44925076f0 100644 --- a/py/gc.c +++ b/py/gc.c @@ -880,16 +880,6 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) { return ret_ptr; } -/* -void *gc_alloc(mp_uint_t n_bytes) { - return _gc_alloc(n_bytes, false); -} - -void *gc_alloc_with_finaliser(mp_uint_t n_bytes) { - return _gc_alloc(n_bytes, true); -} -*/ - // force the freeing of a piece of memory // TODO: freeing here does not call finaliser void gc_free(void *ptr) { @@ -992,35 +982,6 @@ size_t gc_nbytes(const void *ptr) { return 0; } -#if 0 -// old, simple realloc that didn't expand memory in place -void *gc_realloc(void *ptr, mp_uint_t n_bytes) { - mp_uint_t n_existing = gc_nbytes(ptr); - if (n_bytes <= n_existing) { - return ptr; - } else { - bool has_finaliser; - if (ptr == NULL) { - has_finaliser = false; - } else { - #if MICROPY_ENABLE_FINALISER - has_finaliser = FTB_GET(BLOCK_FROM_PTR((mp_uint_t)ptr)); - #else - has_finaliser = false; - #endif - } - void *ptr2 = gc_alloc(n_bytes, has_finaliser); - if (ptr2 == NULL) { - return ptr2; - } - memcpy(ptr2, ptr, n_existing); - gc_free(ptr); - return ptr2; - } -} - -#else // Alternative gc_realloc impl - void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { // check for pure allocation if (ptr_in == NULL) { @@ -1170,7 +1131,6 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { gc_free(ptr_in); return ptr_out; } -#endif // Alternative gc_realloc impl void gc_dump_info(const mp_print_t *print) { gc_info_t info; @@ -1314,41 +1274,4 @@ void gc_dump_alloc_table(const mp_print_t *print) { GC_EXIT(); } -#if 0 -// For testing the GC functions -void gc_test(void) { - mp_uint_t len = 500; - mp_uint_t *heap = malloc(len); - gc_init(heap, heap + len / sizeof(mp_uint_t)); - void *ptrs[100]; - { - mp_uint_t **p = gc_alloc(16, false); - p[0] = gc_alloc(64, false); - p[1] = gc_alloc(1, false); - p[2] = gc_alloc(1, false); - p[3] = gc_alloc(1, false); - mp_uint_t ***p2 = gc_alloc(16, false); - p2[0] = p; - p2[1] = p; - ptrs[0] = p2; - } - for (int i = 0; i < 25; i += 2) { - mp_uint_t *p = gc_alloc(i, false); - printf("p=%p\n", p); - if (i & 3) { - // ptrs[i] = p; - } - } - - printf("Before GC:\n"); - gc_dump_alloc_table(&mp_plat_print); - printf("Starting GC...\n"); - gc_collect_start(); - gc_collect_root(ptrs, sizeof(ptrs) / sizeof(void *)); - gc_collect_end(); - printf("After GC:\n"); - gc_dump_alloc_table(&mp_plat_print); -} -#endif - #endif // MICROPY_ENABLE_GC From 77bd8fe5b80b0e7e02cdb6b4272c401ae3dca638 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 25 Jun 2024 10:51:10 +1000 Subject: [PATCH 0115/1300] webassembly: Reuse PyProxy objects when they are the same Python object. This commit makes it so that PyProxy objects are reused (on the JavaScript side) when they correspond to an existing Python object that is the same object. For example, proxying the same Python function to JavaScript, the same PyProxy instance is now used. This means that if `foo` is a Python function then accessing it on the JavaScript side such as `api.globals().get("foo")` has the property that: api.globals().get("foo") === api.globals().get("foo") Prior to this commit the above was not true because new PyProxy instances were created each time `foo` was accessed. Signed-off-by: Damien George --- ports/webassembly/proxy_c.c | 53 ++++++++++++++++--- ports/webassembly/proxy_js.js | 32 +++++++++++ tests/ports/webassembly/heap_expand.mjs.exp | 42 +++++++-------- tests/ports/webassembly/py_proxy_identity.mjs | 26 +++++++++ .../webassembly/py_proxy_identity.mjs.exp | 3 ++ 5 files changed, 129 insertions(+), 27 deletions(-) create mode 100644 tests/ports/webassembly/py_proxy_identity.mjs create mode 100644 tests/ports/webassembly/py_proxy_identity.mjs.exp diff --git a/ports/webassembly/proxy_c.c b/ports/webassembly/proxy_c.c index a8c444faaebca..00abc43bf2b3f 100644 --- a/ports/webassembly/proxy_c.c +++ b/ports/webassembly/proxy_c.c @@ -49,6 +49,7 @@ enum { PROXY_KIND_MP_GENERATOR = 7, PROXY_KIND_MP_OBJECT = 8, PROXY_KIND_MP_JSPROXY = 9, + PROXY_KIND_MP_EXISTING = 10, }; enum { @@ -79,40 +80,76 @@ static size_t proxy_c_ref_next; void proxy_c_init(void) { MP_STATE_PORT(proxy_c_ref) = mp_obj_new_list(0, NULL); + MP_STATE_PORT(proxy_c_dict) = mp_obj_new_dict(0); mp_obj_list_append(MP_STATE_PORT(proxy_c_ref), MP_OBJ_NULL); proxy_c_ref_next = PROXY_C_REF_NUM_STATIC; } MP_REGISTER_ROOT_POINTER(mp_obj_t proxy_c_ref); +MP_REGISTER_ROOT_POINTER(mp_obj_t proxy_c_dict); // obj cannot be MP_OBJ_NULL. static inline size_t proxy_c_add_obj(mp_obj_t obj) { // Search for the first free slot in proxy_c_ref. + size_t id = 0; mp_obj_list_t *l = (mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref)); while (proxy_c_ref_next < l->len) { if (l->items[proxy_c_ref_next] == MP_OBJ_NULL) { // Free slot found, reuse it. - size_t id = proxy_c_ref_next; + id = proxy_c_ref_next; ++proxy_c_ref_next; l->items[id] = obj; - return id; + break; } ++proxy_c_ref_next; } - // No free slots, so grow proxy_c_ref by one (append at the end of the list). - size_t id = l->len; - mp_obj_list_append(MP_STATE_PORT(proxy_c_ref), obj); - proxy_c_ref_next = l->len; + if (id == 0) { + // No free slots, so grow proxy_c_ref by one (append at the end of the list). + id = l->len; + mp_obj_list_append(MP_STATE_PORT(proxy_c_ref), obj); + proxy_c_ref_next = l->len; + } + + // Add the object to proxy_c_dict, keyed by the object pointer, with value the object id. + mp_obj_t obj_key = mp_obj_new_int_from_uint((uintptr_t)obj); + mp_map_elem_t *elem = mp_map_lookup(mp_obj_dict_get_map(MP_STATE_PORT(proxy_c_dict)), obj_key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + elem->value = mp_obj_new_int_from_uint(id); + return id; } +EM_JS(int, js_check_existing, (int c_ref), { + return proxy_js_check_existing(c_ref); +}); + +// obj cannot be MP_OBJ_NULL. +static inline int proxy_c_check_existing(mp_obj_t obj) { + mp_obj_t obj_key = mp_obj_new_int_from_uint((uintptr_t)obj); + mp_map_elem_t *elem = mp_map_lookup(mp_obj_dict_get_map(MP_STATE_PORT(proxy_c_dict)), obj_key, MP_MAP_LOOKUP); + if (elem == NULL) { + return -1; + } + uint32_t c_ref = mp_obj_int_get_truncated(elem->value); + return js_check_existing(c_ref); +} + static inline mp_obj_t proxy_c_get_obj(uint32_t c_ref) { return ((mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref)))->items[c_ref]; } void proxy_c_free_obj(uint32_t c_ref) { if (c_ref >= PROXY_C_REF_NUM_STATIC) { + // Remove the object from proxy_c_dict if the c_ref in that dict corresponds to this object. + // (It may be that this object exists in the dict but with a different c_ref from a more + // recent proxy of this object.) + mp_obj_t obj_key = mp_obj_new_int_from_uint((uintptr_t)proxy_c_get_obj(c_ref)); + mp_map_elem_t *elem = mp_map_lookup(mp_obj_dict_get_map(MP_STATE_PORT(proxy_c_dict)), obj_key, MP_MAP_LOOKUP); + if (elem != NULL && mp_obj_int_get_truncated(elem->value) == c_ref) { + mp_map_lookup(mp_obj_dict_get_map(MP_STATE_PORT(proxy_c_dict)), obj_key, MP_MAP_LOOKUP_REMOVE_IF_FOUND); + } + + // Clear the slot in proxy_c_ref used by this object, so the GC can reclaim the object. ((mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref)))->items[c_ref] = MP_OBJ_NULL; proxy_c_ref_next = MIN(proxy_c_ref_next, c_ref); } @@ -143,6 +180,7 @@ mp_obj_t proxy_convert_js_to_mp_obj_cside(uint32_t *value) { void proxy_convert_mp_to_js_obj_cside(mp_obj_t obj, uint32_t *out) { uint32_t kind; + int js_ref; if (obj == MP_OBJ_NULL) { kind = PROXY_KIND_MP_NULL; } else if (obj == mp_const_none) { @@ -168,6 +206,9 @@ void proxy_convert_mp_to_js_obj_cside(mp_obj_t obj, uint32_t *out) { } else if (mp_obj_is_jsproxy(obj)) { kind = PROXY_KIND_MP_JSPROXY; out[1] = mp_obj_jsproxy_get_ref(obj); + } else if ((js_ref = proxy_c_check_existing(obj)) >= 0) { + kind = PROXY_KIND_MP_EXISTING; + out[1] = js_ref; } else if (mp_obj_get_type(obj) == &mp_type_JsException) { mp_obj_exception_t *exc = MP_OBJ_TO_PTR(obj); if (exc->args->len > 0 && mp_obj_is_jsproxy(exc->args->items[0])) { diff --git a/ports/webassembly/proxy_js.js b/ports/webassembly/proxy_js.js index fe92b5725a049..9e7c233e30bfc 100644 --- a/ports/webassembly/proxy_js.js +++ b/ports/webassembly/proxy_js.js @@ -40,6 +40,7 @@ const PROXY_KIND_MP_CALLABLE = 6; const PROXY_KIND_MP_GENERATOR = 7; const PROXY_KIND_MP_OBJECT = 8; const PROXY_KIND_MP_JSPROXY = 9; +const PROXY_KIND_MP_EXISTING = 10; const PROXY_KIND_JS_UNDEFINED = 0; const PROXY_KIND_JS_NULL = 1; @@ -61,13 +62,39 @@ class PythonError extends Error { function proxy_js_init() { globalThis.proxy_js_ref = [globalThis, undefined]; globalThis.proxy_js_ref_next = PROXY_JS_REF_NUM_STATIC; + globalThis.proxy_js_map = new Map(); + globalThis.proxy_js_existing = [undefined]; globalThis.pyProxyFinalizationRegistry = new FinalizationRegistry( (cRef) => { + globalThis.proxy_js_map.delete(cRef); Module.ccall("proxy_c_free_obj", "null", ["number"], [cRef]); }, ); } +// Check if the c_ref (Python proxy index) has a corresponding JavaScript-side PyProxy +// associated with it. If so, take a concrete reference to this PyProxy from the WeakRef +// and put it in proxy_js_existing, to be referenced and reused by PROXY_KIND_MP_EXISTING. +function proxy_js_check_existing(c_ref) { + const existing_obj = globalThis.proxy_js_map.get(c_ref)?.deref(); + if (existing_obj === undefined) { + return -1; + } + + // Search for a free slot in proxy_js_existing. + for (let i = 0; i < globalThis.proxy_js_existing.length; ++i) { + if (globalThis.proxy_js_existing[i] === undefined) { + // Free slot found, put existing_obj here and return the index. + globalThis.proxy_js_existing[i] = existing_obj; + return i; + } + } + + // No free slot, so append to proxy_js_existing and return the new index. + globalThis.proxy_js_existing.push(existing_obj); + return globalThis.proxy_js_existing.length - 1; +} + // js_obj cannot be undefined function proxy_js_add_obj(js_obj) { // Search for the first free slot in proxy_js_ref. @@ -241,6 +268,10 @@ function proxy_convert_mp_to_js_obj_jsside(value) { // js proxy const id = Module.getValue(value + 4, "i32"); obj = proxy_js_ref[id]; + } else if (kind === PROXY_KIND_MP_EXISTING) { + const id = Module.getValue(value + 4, "i32"); + obj = globalThis.proxy_js_existing[id]; + globalThis.proxy_js_existing[id] = undefined; } else { // obj const id = Module.getValue(value + 4, "i32"); @@ -257,6 +288,7 @@ function proxy_convert_mp_to_js_obj_jsside(value) { obj = new Proxy(target, py_proxy_handler); } globalThis.pyProxyFinalizationRegistry.register(obj, id); + globalThis.proxy_js_map.set(id, new WeakRef(obj)); } return obj; } diff --git a/tests/ports/webassembly/heap_expand.mjs.exp b/tests/ports/webassembly/heap_expand.mjs.exp index 5efa8567f7707..56341351492f5 100644 --- a/tests/ports/webassembly/heap_expand.mjs.exp +++ b/tests/ports/webassembly/heap_expand.mjs.exp @@ -1,27 +1,27 @@ -135241360 135241328 135241296 135241264 -135241216 -135241168 -135241088 -135240944 -135240640 -135240112 -135239072 -135237008 -135232896 -135224688 -135208288 -135175504 -135109888 -134978800 -134716640 -135216784 -136217152 -138217840 -142219296 -150222224 +135241232 +135241184 +135241136 +135241056 +135240912 +135240608 +135240080 +135239040 +135236976 +135232864 +135224656 +135208256 +135175472 +135109856 +134978768 +134716608 +135216752 +136217120 +138217808 +142219264 +150222192 1 2 4 diff --git a/tests/ports/webassembly/py_proxy_identity.mjs b/tests/ports/webassembly/py_proxy_identity.mjs new file mode 100644 index 0000000000000..d4a720b738a6c --- /dev/null +++ b/tests/ports/webassembly/py_proxy_identity.mjs @@ -0,0 +1,26 @@ +// Test identity of PyProxy when they are the same Python object. + +const mp = await (await import(process.argv[2])).loadMicroPython(); + +mp.runPython(` +l = [] +`); + +const l1 = mp.globals.get("l"); +const l2 = mp.globals.get("l"); +console.log(l1, l2); +console.log(l1 === l2); + +globalThis.eventTarget = new EventTarget(); +globalThis.event = new Event("event"); + +mp.runPython(` +import js + +def callback(ev): + print("callback", ev) +js.eventTarget.addEventListener("event", callback) +js.eventTarget.dispatchEvent(js.event) +js.eventTarget.removeEventListener("event", callback) +js.eventTarget.dispatchEvent(js.event) +`); diff --git a/tests/ports/webassembly/py_proxy_identity.mjs.exp b/tests/ports/webassembly/py_proxy_identity.mjs.exp new file mode 100644 index 0000000000000..01ccf0d892653 --- /dev/null +++ b/tests/ports/webassembly/py_proxy_identity.mjs.exp @@ -0,0 +1,3 @@ +PyProxy { _ref: 3 } PyProxy { _ref: 3 } +true +callback From 847ee20d9b18c36e2160aaadfe71d2f7814648b1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 19 Jul 2024 17:46:33 +1000 Subject: [PATCH 0116/1300] tests/multi_bluetooth/perf_gatt_notify.py: Reduce connection interval. To test that the notification ping-pong can be low latency. Signed-off-by: Damien George --- tests/multi_bluetooth/perf_gatt_notify.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/multi_bluetooth/perf_gatt_notify.py b/tests/multi_bluetooth/perf_gatt_notify.py index 8acccc6470d60..ac791a3ebfa26 100644 --- a/tests/multi_bluetooth/perf_gatt_notify.py +++ b/tests/multi_bluetooth/perf_gatt_notify.py @@ -110,9 +110,9 @@ def instance1(): ((char_handle,),) = ble.gatts_register_services(SERVICES) multitest.next() try: - # Connect to peripheral and then disconnect. + # Connect to peripheral, with a short connection interval to reduce notify latency. print("gap_connect") - ble.gap_connect(*BDADDR) + ble.gap_connect(BDADDR[0], BDADDR[1], 2000, 12500, 12500) conn_handle = wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS) # Discover characteristics. From a734ee9057a760b0316eca110d71db1524142fec Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 17 Jul 2024 20:43:51 +0200 Subject: [PATCH 0117/1300] shared/tinyusb/mp_usbd_cdc: Skip writing to an uninitialized USB device. During execution of `boot.py` the USB device is not yet initialized. Any attempt to write to the CDC (eg calling `print()`) would lock up the device. This commit skips writing when the USB device is not initialized. Any output from `boot.py` is lost, but the device does not lock up. Also removed unnecessary declaration of `tusb_init()`. Signed-off-by: robert-hh --- shared/tinyusb/mp_usbd.h | 1 - shared/tinyusb/mp_usbd_cdc.c | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/shared/tinyusb/mp_usbd.h b/shared/tinyusb/mp_usbd.h index ef32348451391..31234566b6ca4 100644 --- a/shared/tinyusb/mp_usbd.h +++ b/shared/tinyusb/mp_usbd.h @@ -125,7 +125,6 @@ inline static bool mp_usb_device_builtin_enabled(const mp_obj_usb_device_t *usbd static inline void mp_usbd_init(void) { // Without runtime USB support, this can be a thin wrapper wrapper around tusb_init() - extern bool tusb_init(void); tusb_init(); } diff --git a/shared/tinyusb/mp_usbd_cdc.c b/shared/tinyusb/mp_usbd_cdc.c index 63d015cb461f5..c6a88e467d398 100644 --- a/shared/tinyusb/mp_usbd_cdc.c +++ b/shared/tinyusb/mp_usbd_cdc.c @@ -95,6 +95,9 @@ void tud_cdc_rx_cb(uint8_t itf) { } mp_uint_t mp_usbd_cdc_tx_strn(const char *str, mp_uint_t len) { + if (!tusb_inited()) { + return 0; + } size_t i = 0; while (i < len) { uint32_t n = len - i; From 69c25ea8653566ec97690b5121bd10b753c89426 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jul 2024 13:20:31 +1000 Subject: [PATCH 0118/1300] shared/runtime/pyexec: Make a raised SystemExit always do a forced exit. The current situation with SystemExit and soft reset is the following: - `sys.exit()` follows CPython and just raises `SystemExit`. - On the unix port, raising `SystemExit` quits the application/MicroPython, whether at the REPL or in code (this follows CPython behaviour). - On bare-metal ports, raising `SystemExit` at the REPL does nothing, raising it in code will stop the code and drop into the REPL. - `machine.soft_reset()` raises `SystemExit` but with a special flag set, and bare-metal targets check this flag when it propagates to the top-level and do a soft reset when they receive it. The original idea here was that a bare-metal target can't "quit" like the unix port can, and so dropping to the REPL was considered the same as "quit". But this bare-metal behaviour is arguably inconsistent with unix, and "quit" should mean terminate everything, including REPL access. This commit changes the behaviour to the following, which is more consistent: - Raising `SystemExit` on a bare-metal port will do a soft reset (unless the exception is caught by the application). - `machine.soft_reset()` is now equivalent to `sys.exit()`. - unix port behaviour remains unchanged. Tested running the test suite on an stm32 board and everything still passes, in particular tests that skip by raising `SystemExit` still correctly skip. Signed-off-by: Damien George --- extmod/modmachine.c | 1 - ports/qemu-arm/modmachine.c | 3 --- ports/unix/modmachine.c | 3 --- shared/runtime/pyexec.c | 6 +----- shared/runtime/pyexec.h | 5 ----- 5 files changed, 1 insertion(+), 17 deletions(-) diff --git a/extmod/modmachine.c b/extmod/modmachine.c index 2fe72817b6c2a..a423d8067cfba 100644 --- a/extmod/modmachine.c +++ b/extmod/modmachine.c @@ -62,7 +62,6 @@ NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args); #endif static mp_obj_t machine_soft_reset(void) { - pyexec_system_exit = PYEXEC_FORCED_EXIT; mp_raise_type(&mp_type_SystemExit); } static MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset); diff --git a/ports/qemu-arm/modmachine.c b/ports/qemu-arm/modmachine.c index 5f6db937c3384..a897c5670e49b 100644 --- a/ports/qemu-arm/modmachine.c +++ b/ports/qemu-arm/modmachine.c @@ -27,9 +27,6 @@ // This file is never compiled standalone, it's included directly from // extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE. -// This variable is needed for machine.soft_reset(), but the variable is otherwise unused. -int pyexec_system_exit = 0; - static void mp_machine_idle(void) { // Do nothing. } diff --git a/ports/unix/modmachine.c b/ports/unix/modmachine.c index 6f3ab80944061..d1cdbe8619aba 100644 --- a/ports/unix/modmachine.c +++ b/ports/unix/modmachine.c @@ -36,9 +36,6 @@ #define MICROPY_PAGE_MASK (MICROPY_PAGE_SIZE - 1) #endif -// This variable is needed for machine.soft_reset(), but the variable is otherwise unused. -int pyexec_system_exit = 0; - uintptr_t mod_machine_mem_get_addr(mp_obj_t addr_o, uint align) { uintptr_t addr = mp_obj_get_int_truncated(addr_o); if ((addr & (align - 1)) != 0) { diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index a305e6a5df6dd..9dc4446ed4d66 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -44,7 +44,6 @@ #include "genhdr/mpversion.h" pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; -int pyexec_system_exit = 0; #if MICROPY_REPL_INFO static bool repl_display_debugging_info = 0; @@ -74,9 +73,6 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input MICROPY_BOARD_BEFORE_PYTHON_EXEC(input_kind, exec_flags); #endif - // by default a SystemExit exception returns 0 - pyexec_system_exit = 0; - nlr_buf_t nlr; nlr.ret_val = NULL; if (nlr_push(&nlr) == 0) { @@ -146,7 +142,7 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input // check for SystemExit if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { // at the moment, the value of SystemExit is unused - ret = pyexec_system_exit; + ret = PYEXEC_FORCED_EXIT; } else { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); ret = 0; diff --git a/shared/runtime/pyexec.h b/shared/runtime/pyexec.h index 64c5ef94340d3..5779d3e09a8c4 100644 --- a/shared/runtime/pyexec.h +++ b/shared/runtime/pyexec.h @@ -35,11 +35,6 @@ typedef enum { extern pyexec_mode_kind_t pyexec_mode_kind; -// Set this to the value (eg PYEXEC_FORCED_EXIT) that will be propagated through -// the pyexec functions if a SystemExit exception is raised by the running code. -// It will reset to 0 at the start of each execution (eg each REPL entry). -extern int pyexec_system_exit; - #define PYEXEC_FORCED_EXIT (0x100) int pyexec_raw_repl(void); From 5f3ecc29f8f0d69d15710ded3278fe055f532517 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jul 2024 13:24:27 +1000 Subject: [PATCH 0119/1300] extmod/modmachine: Use sys.exit as implementation of machine.soft_reset. It does the same thing, raising `SystemExit`. Signed-off-by: Damien George --- extmod/modmachine.c | 12 ++++++------ py/builtin.h | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/extmod/modmachine.c b/extmod/modmachine.c index a423d8067cfba..5906835949861 100644 --- a/extmod/modmachine.c +++ b/extmod/modmachine.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "py/builtin.h" #include "py/runtime.h" #if MICROPY_PY_MACHINE @@ -35,6 +36,10 @@ #include "drivers/dht/dht.h" #endif +#if !MICROPY_PY_SYS_EXIT +#error MICROPY_PY_MACHINE requires MICROPY_PY_SYS_EXIT +#endif + // The port must provide implementations of these low-level machine functions. static void mp_machine_idle(void); @@ -61,11 +66,6 @@ NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args); #include MICROPY_PY_MACHINE_INCLUDEFILE #endif -static mp_obj_t machine_soft_reset(void) { - mp_raise_type(&mp_type_SystemExit); -} -static MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset); - #if MICROPY_PY_MACHINE_BOOTLOADER NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args) { mp_machine_bootloader(n_args, args); @@ -156,7 +156,7 @@ static const mp_rom_map_elem_t machine_module_globals_table[] = { #endif // Reset related functions. - { MP_ROM_QSTR(MP_QSTR_soft_reset), MP_ROM_PTR(&machine_soft_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_soft_reset), MP_ROM_PTR(&mp_sys_exit_obj) }, #if MICROPY_PY_MACHINE_BOOTLOADER { MP_ROM_QSTR(MP_QSTR_bootloader), MP_ROM_PTR(&machine_bootloader_obj) }, #endif diff --git a/py/builtin.h b/py/builtin.h index 81d0789802b9c..6efe3e8facabd 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -126,6 +126,8 @@ MP_DECLARE_CONST_FUN_OBJ_2(mp_op_getitem_obj); MP_DECLARE_CONST_FUN_OBJ_3(mp_op_setitem_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_op_delitem_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_exit_obj); + // Modules needed by the runtime. extern const mp_obj_dict_t mp_module_builtins_globals; extern const mp_obj_module_t mp_module___main__; From 8159dcc276dc07bcb28d6ab3a3b7d2a5c1d90ab5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 19 Jul 2024 22:36:15 +1000 Subject: [PATCH 0120/1300] extmod/modos: Include os.sep entry if MICROPY_VFS is enabled. This simplifies configuration by removing the `MICROPY_PY_OS_SEP` option and instead including `os.sep` if `MICROPY_VFS` is enabled. That matches the configuration of all existing ports that enabled `os.sep` (they also had `MICROPY_VFS` enabled), and brings consistency to other ports. Fixes issue #15116. Signed-off-by: Damien George --- extmod/modos.c | 4 +--- ports/nrf/mpconfigport.h | 1 - ports/renesas-ra/mpconfigport.h | 1 - ports/stm32/mpconfigport.h | 1 - ports/unix/variants/mpconfigvariant_common.h | 1 - ports/windows/mpconfigport.h | 1 - 6 files changed, 1 insertion(+), 8 deletions(-) diff --git a/extmod/modos.c b/extmod/modos.c index 7f1e31fba7793..e7f7fc818cf08 100644 --- a/extmod/modos.c +++ b/extmod/modos.c @@ -153,9 +153,6 @@ static const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_putenv), MP_ROM_PTR(&mp_os_putenv_obj) }, { MP_ROM_QSTR(MP_QSTR_unsetenv), MP_ROM_PTR(&mp_os_unsetenv_obj) }, #endif - #if MICROPY_PY_OS_SEP - { MP_ROM_QSTR(MP_QSTR_sep), MP_ROM_QSTR(MP_QSTR__slash_) }, - #endif #if MICROPY_PY_OS_SYNC { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&mp_os_sync_obj) }, #endif @@ -170,6 +167,7 @@ static const mp_rom_map_elem_t os_module_globals_table[] = { #endif #if MICROPY_VFS + { MP_ROM_QSTR(MP_QSTR_sep), MP_ROM_QSTR(MP_QSTR__slash_) }, { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 0b9b667eeaf31..48b58143acdd0 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -151,7 +151,6 @@ #define MICROPY_PY_OS_INCLUDEFILE "ports/nrf/modules/os/modos.c" #define MICROPY_PY_OS_DUPTERM (1) #define MICROPY_PY_OS_DUPTERM_STREAM_DETACHED_ATTACHED (1) -#define MICROPY_PY_OS_SEP (1) #define MICROPY_PY_OS_SYNC (MICROPY_VFS) #define MICROPY_PY_OS_UNAME (1) #define MICROPY_PY_OS_URANDOM (MICROPY_HW_ENABLE_RNG) diff --git a/ports/renesas-ra/mpconfigport.h b/ports/renesas-ra/mpconfigport.h index 52effd64f9295..a23a2c1065fa2 100644 --- a/ports/renesas-ra/mpconfigport.h +++ b/ports/renesas-ra/mpconfigport.h @@ -120,7 +120,6 @@ #define MICROPY_PY_OS_DUPTERM (3) #define MICROPY_PY_OS_DUPTERM_BUILTIN_STREAM (1) #define MICROPY_PY_OS_DUPTERM_STREAM_DETACHED_ATTACHED (1) -#define MICROPY_PY_OS_SEP (1) #define MICROPY_PY_OS_SYNC (1) #define MICROPY_PY_OS_UNAME (1) #define MICROPY_PY_OS_URANDOM (MICROPY_HW_ENABLE_RNG) diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index bd1ab671e071d..25fc9e11f9d68 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -99,7 +99,6 @@ #define MICROPY_PY_OS_DUPTERM (3) #define MICROPY_PY_OS_DUPTERM_BUILTIN_STREAM (1) #define MICROPY_PY_OS_DUPTERM_STREAM_DETACHED_ATTACHED (1) -#define MICROPY_PY_OS_SEP (1) #define MICROPY_PY_OS_SYNC (1) #define MICROPY_PY_OS_UNAME (1) #define MICROPY_PY_OS_URANDOM (MICROPY_HW_ENABLE_RNG) diff --git a/ports/unix/variants/mpconfigvariant_common.h b/ports/unix/variants/mpconfigvariant_common.h index 2e34055bf773f..cea0397414325 100644 --- a/ports/unix/variants/mpconfigvariant_common.h +++ b/ports/unix/variants/mpconfigvariant_common.h @@ -95,7 +95,6 @@ #define MICROPY_PY_OS_INCLUDEFILE "ports/unix/modos.c" #define MICROPY_PY_OS_ERRNO (1) #define MICROPY_PY_OS_GETENV_PUTENV_UNSETENV (1) -#define MICROPY_PY_OS_SEP (1) #define MICROPY_PY_OS_SYSTEM (1) #define MICROPY_PY_OS_URANDOM (1) diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index 55e44c6f5c8c5..fabc9072d6c70 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -136,7 +136,6 @@ #define MICROPY_PY_OS_INCLUDEFILE "ports/unix/modos.c" #define MICROPY_PY_OS_ERRNO (1) #define MICROPY_PY_OS_GETENV_PUTENV_UNSETENV (1) -#define MICROPY_PY_OS_SEP (1) #define MICROPY_PY_OS_STATVFS (0) #define MICROPY_PY_OS_SYSTEM (1) #define MICROPY_PY_OS_URANDOM (1) From 6db91dfefb1a7ba0026106d8f0c6589630e9a012 Mon Sep 17 00:00:00 2001 From: Michael Vornovitsky Date: Sat, 11 Nov 2023 20:10:51 -0500 Subject: [PATCH 0121/1300] extmod/modbtree: Add checks for already-closed database. Fixes use-after-free when accessing the database after it is closed with `btree_close`. `btree_close` always succeeds when called with an already-closed database. The new test checks that operations that access the underlying database (get, set, flush, seq) fail with a `ValueError` when the btree is already closed. It also checks that closing and printing the btree succeed when the btree is already closed. Fixes issue #12543. Signed-off-by: Michael Vornovitsky --- extmod/modbtree.c | 22 +++++++++++++++++- tests/extmod/btree_closed.py | 39 ++++++++++++++++++++++++++++++++ tests/extmod/btree_closed.py.exp | 5 ++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/extmod/btree_closed.py create mode 100644 tests/extmod/btree_closed.py.exp diff --git a/extmod/modbtree.c b/extmod/modbtree.c index 55c13ac911a73..f48645a8270dc 100644 --- a/extmod/modbtree.c +++ b/extmod/modbtree.c @@ -89,6 +89,12 @@ void __dbpanic(DB *db) { mp_printf(&mp_plat_print, "__dbpanic(%p)\n", db); } +static void check_btree_is_open(mp_obj_btree_t *self) { + if (!self->db) { + mp_raise_ValueError(MP_ERROR_TEXT("database closed")); + } +} + static mp_obj_btree_t *btree_new(DB *db, mp_obj_t stream) { mp_obj_btree_t *o = mp_obj_malloc(mp_obj_btree_t, (mp_obj_type_t *)&btree_type); o->stream = stream; @@ -114,19 +120,28 @@ static void btree_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind static mp_obj_t btree_flush(mp_obj_t self_in) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); + check_btree_is_open(self); return MP_OBJ_NEW_SMALL_INT(__bt_sync(self->db, 0)); } static MP_DEFINE_CONST_FUN_OBJ_1(btree_flush_obj, btree_flush); static mp_obj_t btree_close(mp_obj_t self_in) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); - return MP_OBJ_NEW_SMALL_INT(__bt_close(self->db)); + int res; + if (self->db) { + res = __bt_close(self->db); + self->db = NULL; + } else { + res = RET_SUCCESS; // Closing an already-closed DB always succeeds. + } + return MP_OBJ_NEW_SMALL_INT(res); } static MP_DEFINE_CONST_FUN_OBJ_1(btree_close_obj, btree_close); static mp_obj_t btree_put(size_t n_args, const mp_obj_t *args) { (void)n_args; mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]); + check_btree_is_open(self); DBT key, val; buf_to_dbt(args[1], &key); buf_to_dbt(args[2], &val); @@ -136,6 +151,7 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_put_obj, 3, 4, btree_put); static mp_obj_t btree_get(size_t n_args, const mp_obj_t *args) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]); + check_btree_is_open(self); DBT key, val; buf_to_dbt(args[1], &key); int res = __bt_get(self->db, &key, &val, 0); @@ -153,6 +169,7 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_get_obj, 2, 3, btree_get); static mp_obj_t btree_seq(size_t n_args, const mp_obj_t *args) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]); + check_btree_is_open(self); int flags = MP_OBJ_SMALL_INT_VALUE(args[1]); DBT key, val; if (n_args > 2) { @@ -225,6 +242,7 @@ static mp_obj_t btree_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { static mp_obj_t btree_iternext(mp_obj_t self_in) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); + check_btree_is_open(self); DBT key, val; int res; bool desc = self->flags & FLAG_DESC; @@ -281,6 +299,7 @@ static mp_obj_t btree_iternext(mp_obj_t self_in) { static mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); + check_btree_is_open(self); if (value == MP_OBJ_NULL) { // delete DBT key; @@ -314,6 +333,7 @@ static mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { static mp_obj_t btree_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(lhs_in); + check_btree_is_open(self); switch (op) { case MP_BINARY_OP_CONTAINS: { DBT key, val; diff --git a/tests/extmod/btree_closed.py b/tests/extmod/btree_closed.py new file mode 100644 index 0000000000000..ae61cd00836d2 --- /dev/null +++ b/tests/extmod/btree_closed.py @@ -0,0 +1,39 @@ +try: + import btree + import io +except ImportError: + print("SKIP") + raise SystemExit + +f = io.BytesIO() +db = btree.open(f) + +db[b"foo"] = b"42" + +db.close() + +# Accessing an already-closed database should fail. +try: + print(db[b"foo"]) +except ValueError: + print("ValueError") + +try: + db[b"bar"] = b"43" +except ValueError: + print("ValueError") + +try: + db.flush() +except ValueError: + print("ValueError") + +try: + for k, v in db.items(): + pass +except ValueError: + print("ValueError") + +# Closing and printing an already-closed database should not fail. +db.close() +print(db) diff --git a/tests/extmod/btree_closed.py.exp b/tests/extmod/btree_closed.py.exp new file mode 100644 index 0000000000000..312edfd13dba6 --- /dev/null +++ b/tests/extmod/btree_closed.py.exp @@ -0,0 +1,5 @@ +ValueError +ValueError +ValueError +ValueError + From 444d7bacbec51321cea955802b62dff9318dcbf6 Mon Sep 17 00:00:00 2001 From: stijn Date: Wed, 8 Nov 2023 13:43:28 +0100 Subject: [PATCH 0122/1300] extmod/moductypes: Validate the descriptor tuple. Fixes various null dereferencing, out-of-bounds memory accesses and `assert(0)` failures in the case of an invalid `uctypes` descriptor. By design `uctypes` can crash because it accesses arbitrary memory, but at least describing the descriptor layout should be forced to be correct and not crash. Fixes issue #12702. Signed-off-by: stijn --- extmod/moductypes.c | 17 +++++++++-- tests/extmod/uctypes_sizeof.py | 41 ++++++++++++++++++++++++++- tests/extmod/uctypes_sizeof.py.exp | 8 ++++++ tests/extmod/uctypes_sizeof_od.py | 6 ---- tests/extmod/uctypes_sizeof_od.py.exp | 1 - 5 files changed, 63 insertions(+), 10 deletions(-) diff --git a/extmod/moductypes.c b/extmod/moductypes.c index fa743eb637374..00a69a275a0ee 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -143,6 +143,10 @@ static inline mp_uint_t uctypes_struct_scalar_size(int val_type) { // Get size of aggregate type descriptor static mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_uint_t *max_field_size) { + if (t->len == 0) { + syntax_error(); + } + mp_uint_t total_size = 0; mp_int_t offset_ = MP_OBJ_SMALL_INT_VALUE(t->items[0]); @@ -150,8 +154,15 @@ static mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_ switch (agg_type) { case STRUCT: + if (t->len != 2) { + syntax_error(); + } return uctypes_struct_size(t->items[1], layout_type, max_field_size); case PTR: + // Second field ignored, but should still be present for consistency. + if (t->len != 2) { + syntax_error(); + } if (sizeof(void *) > *max_field_size) { *max_field_size = sizeof(void *); } @@ -167,15 +178,17 @@ static mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_ if (item_s > *max_field_size) { *max_field_size = item_s; } - } else { + } else if (t->len == 3) { // Elements of array are aggregates item_s = uctypes_struct_size(t->items[2], layout_type, max_field_size); + } else { + syntax_error(); } return item_s * arr_sz; } default: - assert(0); + syntax_error(); } return total_size; diff --git a/tests/extmod/uctypes_sizeof.py b/tests/extmod/uctypes_sizeof.py index 6e52232e39eba..d295cc85b6828 100644 --- a/tests/extmod/uctypes_sizeof.py +++ b/tests/extmod/uctypes_sizeof.py @@ -43,8 +43,47 @@ print(uctypes.sizeof(S.sub)) assert uctypes.sizeof(S.sub) == 1 -# invalid descriptor +# invalid descriptors try: print(uctypes.sizeof([])) except TypeError: print("TypeError") + +try: + print(uctypes.sizeof(())) +except TypeError: + print("TypeError") + +try: + print(uctypes.sizeof(("garbage",))) +except TypeError: + print("TypeError") + +try: + # PTR * 3 is intended to be an invalid agg_type (STRUCT, PTR, ARRAY are valid ones). + print(uctypes.sizeof((uctypes.PTR * 3,))) +except TypeError: + print("TypeError") + +try: + print(uctypes.sizeof((0, {}, "garbage"))) +except TypeError: + print("TypeError") + +try: + print(uctypes.sizeof((uctypes.PTR | 0,))) +except TypeError: + print("TypeError") + +try: + print(uctypes.sizeof((uctypes.ARRAY | 0,))) +except TypeError: + print("TypeError") + +try: + print(uctypes.sizeof((uctypes.ARRAY | 0, 1, {}, "garbage"))) +except TypeError: + print("TypeError") + +# empty descriptor +print(uctypes.sizeof({})) diff --git a/tests/extmod/uctypes_sizeof.py.exp b/tests/extmod/uctypes_sizeof.py.exp index b35b11aa0cea9..edcc05a441f79 100644 --- a/tests/extmod/uctypes_sizeof.py.exp +++ b/tests/extmod/uctypes_sizeof.py.exp @@ -5,3 +5,11 @@ TypeError 6 1 TypeError +TypeError +TypeError +TypeError +TypeError +TypeError +TypeError +TypeError +0 diff --git a/tests/extmod/uctypes_sizeof_od.py b/tests/extmod/uctypes_sizeof_od.py index 375f05f5e2ce0..8aff363631508 100644 --- a/tests/extmod/uctypes_sizeof_od.py +++ b/tests/extmod/uctypes_sizeof_od.py @@ -45,9 +45,3 @@ print(uctypes.sizeof(S.sub)) assert uctypes.sizeof(S.sub) == 1 - -# invalid descriptor -try: - print(uctypes.sizeof([])) -except TypeError: - print("TypeError") diff --git a/tests/extmod/uctypes_sizeof_od.py.exp b/tests/extmod/uctypes_sizeof_od.py.exp index b35b11aa0cea9..fb74def602b97 100644 --- a/tests/extmod/uctypes_sizeof_od.py.exp +++ b/tests/extmod/uctypes_sizeof_od.py.exp @@ -4,4 +4,3 @@ TypeError 6 1 -TypeError From 390390ec37d01c3f0ae0af7c489d36ac1e84fbe1 Mon Sep 17 00:00:00 2001 From: Terence Stenvold Date: Fri, 19 Jul 2024 12:38:44 +0200 Subject: [PATCH 0123/1300] extmod/vfs_fat: Set default volume label on mkfs if it's defined. Using mkfs doesn't set a volume label for FAT filesystems. This commit will set the volume label if `MICROPY_HW_FLASH_FS_LABEL` is defined. --- extmod/vfs_fat.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 4f01432f54968..ee1169b8c3193 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -114,6 +114,11 @@ static mp_obj_t fat_vfs_mkfs(mp_obj_t bdev_in) { mp_raise_OSError(fresult_to_errno_table[res]); } + // set the filesystem label if it's configured + #ifdef MICROPY_HW_FLASH_FS_LABEL + f_setlabel(&vfs->fatfs, MICROPY_HW_FLASH_FS_LABEL); + #endif + return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_mkfs_fun_obj, fat_vfs_mkfs); From 29943546343c92334e8518695a11fc0e2ceea68b Mon Sep 17 00:00:00 2001 From: Junwha Date: Wed, 3 Jan 2024 02:25:28 +0900 Subject: [PATCH 0124/1300] extmod/vfs: Fix buffer overflow of string comparison in umount. The comparison between the given unmount string and existing mount strings were made by the given string, which leads to buffer overflow. Fixes issue #13006. Signed-off-by: Junwha --- extmod/vfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/vfs.c b/extmod/vfs.c index 5d564459c0c0b..e545c9af93657 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -273,7 +273,7 @@ mp_obj_t mp_vfs_umount(mp_obj_t mnt_in) { mnt_str = mp_obj_str_get_data(mnt_in, &mnt_len); } for (mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table); *vfsp != NULL; vfsp = &(*vfsp)->next) { - if ((mnt_str != NULL && !memcmp(mnt_str, (*vfsp)->str, mnt_len + 1)) || (*vfsp)->obj == mnt_in) { + if ((mnt_str != NULL && mnt_len == (*vfsp)->len && !memcmp(mnt_str, (*vfsp)->str, mnt_len)) || (*vfsp)->obj == mnt_in) { vfs = *vfsp; *vfsp = (*vfsp)->next; break; From 337742f6c70a7b9d407df687774bb9c9cc6a1656 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 17 Jul 2024 15:38:55 +1000 Subject: [PATCH 0125/1300] esp32/mpthreadport: Fix uneven GIL allocation between Python threads. Explicitly yield each time a thread mutex is unlocked. Key to understanding this bug is that Python threads run at equal RTOS priority, and although ESP-IDF FreeRTOS (and I think vanilla FreeRTOS) scheduler will round-robin equal priority tasks in the ready state it does not make a similar guarantee for tasks moving between ready and waiting. The pathological case of this bug is when one Python thread task is busy (i.e. never blocks) it will hog the CPU more than expected, sometimes for an unbounded amount of time. This happens even though it periodically unlocks the GIL to allow another task to run. Assume T1 is busy and T2 is blocked waiting for the GIL. T1 is executing and hits a condition to yield execution: 1. T1 calls MP_THREAD_GIL_EXIT 2. FreeRTOS sees T2 is waiting for the GIL and moves it to the Ready list (but does not preempt, as T2 is same priority, so T1 keeps running). 3. T1 immediately calls MP_THREAD_GIL_ENTER and re-takes the GIL. 4. Pre-emptive context switch happens, T2 wakes up, sees GIL is not available, and goes on the waiting list for the GIL again. To break this cycle step 4 must happen before step 3, but this may be a very narrow window of time so it may not happen regularly - and quantisation of the timing of the tick interrupt to trigger a context switch may mean it never happens. Yielding at the end of step 2 maximises the chance for another task to run. Adds a test that fails on esp32 before this fix and passes afterwards. Fixes issue #15423. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/mpthreadport.c | 5 ++++ tests/thread/thread_coop.py | 53 +++++++++++++++++++++++++++++++++ tests/thread/thread_coop.py.exp | 2 ++ 3 files changed, 60 insertions(+) create mode 100644 tests/thread/thread_coop.py create mode 100644 tests/thread/thread_coop.py.exp diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c index 12a6ede8668e6..34fef9f7beace 100644 --- a/ports/esp32/mpthreadport.c +++ b/ports/esp32/mpthreadport.c @@ -221,6 +221,11 @@ int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) { void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { xSemaphoreGive(mutex->handle); + // Python threads run at equal priority, so pre-emptively yield here to + // prevent pathological imbalances where a thread unlocks and then + // immediately re-locks a mutex before a context switch can occur, leaving + // another thread waiting for an unbounded period of time. + taskYIELD(); } void mp_thread_deinit(void) { diff --git a/tests/thread/thread_coop.py b/tests/thread/thread_coop.py new file mode 100644 index 0000000000000..aefc4af074db5 --- /dev/null +++ b/tests/thread/thread_coop.py @@ -0,0 +1,53 @@ +# Threads should be semi-cooperative, to the point where one busy +# thread can't starve out another. +# +# (Note on ports without the GIL this one should always be true, on ports with GIL it's +# a test of the GIL behaviour.) + +import _thread +import sys +from time import ticks_ms, ticks_diff, sleep_ms + + +done = False + +ITERATIONS = 5 +SLEEP_MS = 250 +MAX_DELTA = 30 + +if sys.platform in ("win32", "linux", "darwin"): + # Conventional operating systems get looser timing restrictions + SLEEP_MS = 300 + MAX_DELTA = 100 + + +def busy_thread(): + while not done: + pass + + +def test_sleeps(): + global done + ok = True + for _ in range(ITERATIONS): + t0 = ticks_ms() + sleep_ms(SLEEP_MS) + t1 = ticks_ms() + d = ticks_diff(t1, t0) + if d < SLEEP_MS - MAX_DELTA or d > SLEEP_MS + MAX_DELTA: + print("Slept too long ", d) + ok = False + print("OK" if ok else "Not OK") + done = True + + +# make the thread the busy one, and check sleep time on main task +_thread.start_new_thread(busy_thread, ()) +test_sleeps() + +sleep_ms(100) +done = False + +# now swap them +_thread.start_new_thread(test_sleeps, ()) +busy_thread() diff --git a/tests/thread/thread_coop.py.exp b/tests/thread/thread_coop.py.exp new file mode 100644 index 0000000000000..2c94e48371001 --- /dev/null +++ b/tests/thread/thread_coop.py.exp @@ -0,0 +1,2 @@ +OK +OK From 46c3df0229c81d5a6274e57315558404273b2fae Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 17 Jul 2024 16:28:20 +1000 Subject: [PATCH 0126/1300] tests/run-tests.py: Enable thread tests on esp32. Before the fix in parent commit, some of these tests hung indefinitely. After, they seem to consistently pass. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/run-tests.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index a6caff7bbd8ea..e80d5b4633e31 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -1121,7 +1121,9 @@ def main(): test_dirs += ("float", "inlineasm", "ports/renesas-ra") elif args.target == "rp2": test_dirs += ("float", "stress", "inlineasm", "thread", "ports/rp2") - elif args.target in ("esp8266", "esp32", "minimal", "nrf"): + elif args.target == "esp32": + test_dirs += ("float", "thread") + elif args.target in ("esp8266", "minimal", "nrf"): test_dirs += ("float",) elif args.target == "wipy": # run WiPy tests From 594c4229b7aaa7b62ef2b24f47feeb7071714a0d Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 3 Apr 2024 19:01:39 +0200 Subject: [PATCH 0127/1300] esp32/machine_timer: Limit timer numbers for ESP32C3. The ESP32C3 has only two timers in one group. In the code this is reflected as two groups with one timer. Signed-off-by: robert-hh --- ports/esp32/machine_timer.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index 5e249e46e62c7..011f87ba9eb30 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -54,7 +54,7 @@ typedef struct _machine_timer_obj_t { mp_uint_t index; mp_uint_t repeat; - // ESP32 timers are 64-bit + // ESP32 timers are 64 or 54-bit uint64_t period; mp_obj_t callback; @@ -84,13 +84,22 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr machine_timer_obj_t *self = self_in; qstr mode = self->repeat ? MP_QSTR_PERIODIC : MP_QSTR_ONE_SHOT; uint64_t period = self->period / (TIMER_SCALE / 1000); // convert to ms + #if CONFIG_IDF_TARGET_ESP32C3 + mp_printf(print, "Timer(%u, mode=%q, period=%lu)", self->group, mode, period); + #else mp_printf(print, "Timer(%u, mode=%q, period=%lu)", (self->group << 1) | self->index, mode, period); + #endif } static mp_obj_t machine_timer_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, MP_OBJ_FUN_ARGS_MAX, true); + #if CONFIG_IDF_TARGET_ESP32C3 + mp_uint_t group = mp_obj_get_int(args[0]) & 1; + mp_uint_t index = 0; + #else mp_uint_t group = (mp_obj_get_int(args[0]) >> 1) & 1; mp_uint_t index = mp_obj_get_int(args[0]) & 1; + #endif machine_timer_obj_t *self = NULL; From 44527ada5f120f5f131bcd759e86e4e5323555aa Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 3 Nov 2023 21:27:24 +0100 Subject: [PATCH 0128/1300] unix/main: Fix GCC builds for RISC-V 64 bits. This contains a workaround to silence a possibly incorrect warning when building the Unix port with GCC targeting RISC-V 64 bits. Fixes issue #12838. Signed-off-by: Alessandro Gatti --- ports/unix/main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ports/unix/main.c b/ports/unix/main.c index 79d1e88ad13f0..d9ee6eec447b2 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -562,7 +562,12 @@ MP_NOINLINE int main_(int argc, char **argv) { // First entry is empty. We've already added an empty entry to sys.path, so skip it. ++path; } - bool path_remaining = *path; + // GCC targeting RISC-V 64 reports a warning about `path_remaining` being clobbered by + // either setjmp or vfork if that variable it is allocated on the stack. This may + // probably be a compiler error as it occurs on a few recent GCC releases (up to 14.1.0) + // but LLVM doesn't report any warnings. + static bool path_remaining; + path_remaining = *path; while (path_remaining) { char *path_entry_end = strchr(path, PATHLIST_SEP_CHAR); if (path_entry_end == NULL) { From eced9d86a709f3f267edc7bc08e52115d0c0c9df Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 5 Jul 2024 15:44:45 +1000 Subject: [PATCH 0129/1300] rp2: Fix power consumption when sleeping with a timeout. Fixes a regression introduced in 3af006efb39ad0b7aa7c0401c93329b654bca617 where WFE never blocked in `mp_wfe_or_timeout()` function and would busy-wait instead. This increases power consumption measurably. Root cause is that `mp_wfe_or_timeout()` calls soft timer functions that (after the regression) call `recursive_mutex_enter()` and `recursive_mutex_exit()`. The exit calls `lock_internal_spin_unlock_with_notify()` and the default pico-sdk implementation of this macro issues a SEV which negates the WFE that follows it, meaning the CPU never suspends. See https://forums.raspberrypi.com/viewtopic.php?p=2233908 for more details. The fix in this comment adds a custom "nowait" variant mutex that doesn't do WFE/SEV, and uses this one for PendSV. This will use more power when there's contention for the PendSV mutex as the other core will spin, but this shouldn't happen very often. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/mutex_extra.c | 19 +++++++++++++++++++ ports/rp2/mutex_extra.h | 24 ++++++++++++++++++++++++ ports/rp2/pendsv.c | 22 +++++++++++++--------- 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/ports/rp2/mutex_extra.c b/ports/rp2/mutex_extra.c index 7a70a40acc13d..db84191701e49 100644 --- a/ports/rp2/mutex_extra.c +++ b/ports/rp2/mutex_extra.c @@ -33,3 +33,22 @@ void __time_critical_func(recursive_mutex_exit_and_restore_interrupts)(recursive } lock_internal_spin_unlock_with_notify(&mtx->core, save); } + +void __time_critical_func(recursive_mutex_nowait_enter_blocking)(recursive_mutex_nowait_t * mtx) { + while (!recursive_mutex_try_enter(&mtx->mutex, NULL)) { + tight_loop_contents(); + } +} + +void __time_critical_func(recursive_mutex_nowait_exit)(recursive_mutex_nowait_t * wrapper) { + recursive_mutex_t *mtx = &wrapper->mutex; + // Rest of this function is a copy of recursive_mutex_exit(), with + // lock_internal_spin_unlock_with_notify() removed. + uint32_t save = spin_lock_blocking(mtx->core.spin_lock); + assert(lock_is_owner_id_valid(mtx->owner)); + assert(mtx->enter_count); + if (!--mtx->enter_count) { + mtx->owner = LOCK_INVALID_OWNER_ID; + } + spin_unlock(mtx->core.spin_lock, save); +} diff --git a/ports/rp2/mutex_extra.h b/ports/rp2/mutex_extra.h index 61b6b40355f8d..5f4a2c3643b46 100644 --- a/ports/rp2/mutex_extra.h +++ b/ports/rp2/mutex_extra.h @@ -31,4 +31,28 @@ uint32_t recursive_mutex_enter_blocking_and_disable_interrupts(recursive_mutex_t *mtx); void recursive_mutex_exit_and_restore_interrupts(recursive_mutex_t *mtx, uint32_t save); +// Alternative version of recursive_mutex_t that doesn't issue WFE and SEV +// instructions. This means it will use more power (busy-waits), but exiting +// this mutex doesn't disrupt the calling CPU's event state in the same way a +// recursive mutex does (because recurse_mutex_exit() executes SEV each time the +// mutex is released.) +// +// Implement as a wrapper type because no longer compatible with the normal +// recursive_mutex functions. + +typedef struct { + recursive_mutex_t mutex; +} recursive_mutex_nowait_t; + +inline static void recursive_mutex_nowait_init(recursive_mutex_nowait_t *mtx) { + recursive_mutex_init(&mtx->mutex); +} + +inline static bool recursive_mutex_nowait_try_enter(recursive_mutex_nowait_t *mtx, uint32_t *owner_out) { + return recursive_mutex_try_enter(&mtx->mutex, owner_out); +} + +void recursive_mutex_nowait_enter_blocking(recursive_mutex_nowait_t *mtx); +void recursive_mutex_nowait_exit(recursive_mutex_nowait_t *mtx); + #endif // MICROPY_INCLUDED_RP2_MUTEX_EXTRA_H diff --git a/ports/rp2/pendsv.c b/ports/rp2/pendsv.c index 68bb65f041c61..6cfe624c3037f 100644 --- a/ports/rp2/pendsv.c +++ b/ports/rp2/pendsv.c @@ -25,8 +25,8 @@ */ #include -#include "pico/mutex.h" #include "py/mpconfig.h" +#include "mutex_extra.h" #include "pendsv.h" #include "RP2040.h" @@ -35,21 +35,25 @@ #endif static pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS]; -static recursive_mutex_t pendsv_mutex; + +// Using the nowait variant here as softtimer updates PendSV from the loop of mp_wfe_or_timeout(), +// where we don't want the CPU event bit to be set. +static recursive_mutex_nowait_t pendsv_mutex; void pendsv_init(void) { - recursive_mutex_init(&pendsv_mutex); + recursive_mutex_nowait_init(&pendsv_mutex); } void pendsv_suspend(void) { // Recursive Mutex here as either core may call pendsv_suspend() and expect // both mutual exclusion (other core can't enter pendsv_suspend() at the // same time), and that no PendSV handler will run. - recursive_mutex_enter_blocking(&pendsv_mutex); + recursive_mutex_nowait_enter_blocking(&pendsv_mutex); } void pendsv_resume(void) { - recursive_mutex_exit(&pendsv_mutex); + recursive_mutex_nowait_exit(&pendsv_mutex); + // Run pendsv if needed. Find an entry with a dispatch and call pendsv dispatch // with it. If pendsv runs it will service all slots. int count = PENDSV_DISPATCH_NUM_SLOTS; @@ -63,7 +67,7 @@ void pendsv_resume(void) { void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { pendsv_dispatch_table[slot] = f; - if (pendsv_mutex.enter_count == 0) { + if (pendsv_mutex.mutex.enter_count == 0) { // There is a race here where other core calls pendsv_suspend() before // ISR can execute, but dispatch will happen later when other core // calls pendsv_resume(). @@ -78,13 +82,13 @@ void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { // PendSV interrupt handler to perform background processing. void PendSV_Handler(void) { - if (!recursive_mutex_try_enter(&pendsv_mutex, NULL)) { + if (!recursive_mutex_nowait_try_enter(&pendsv_mutex, NULL)) { // Failure here means core 1 holds pendsv_mutex. ISR will // run again after core 1 calls pendsv_resume(). return; } // Core 0 should not already have locked pendsv_mutex - assert(pendsv_mutex.enter_count == 1); + assert(pendsv_mutex.mutex.enter_count == 1); #if MICROPY_PY_NETWORK_CYW43 CYW43_STAT_INC(PENDSV_RUN_COUNT); @@ -98,5 +102,5 @@ void PendSV_Handler(void) { } } - recursive_mutex_exit(&pendsv_mutex); + recursive_mutex_nowait_exit(&pendsv_mutex); } From 9db16cfe31c70a75bfd8a8d93401ff7f6010b1ae Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 3 Jul 2024 15:52:31 +1000 Subject: [PATCH 0130/1300] rp2: Fix wakeup from WFE on core1. If core1 executes `mp_wfe_or_timeout()` then it needs to receive an interrupt or a SEV to resume execution, but the soft timer interrupt only fires on core 0. This fix adds a SEV to the soft timer interrupt handler. This issue was masked by the issue fixed in the previous commit, as WFE previously wasn't suspending properly. Verified via the existing thread_sleep2 test. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/mphalport.c | 7 +++++++ ports/rp2/mpthreadport.c | 2 +- ports/rp2/mpthreadport.h | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index 3fe5357c89664..ad51cf8e21f43 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -228,6 +228,13 @@ static void soft_timer_hardware_callback(unsigned int alarm_num) { // The timer alarm ISR needs to call here and trigger PendSV dispatch via // a second ISR, as PendSV may be currently suspended by the other CPU. pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); + + // This ISR only runs on core0, but if core1 is running Python code then it + // may be blocked in WFE so wake it up as well. Unfortunately this also sets + // the event flag on core0, so a subsequent WFE on this core will not suspend + if (core1_entry != NULL) { + __sev(); + } } void soft_timer_init(void) { diff --git a/ports/rp2/mpthreadport.c b/ports/rp2/mpthreadport.c index 5b8e804f95fd8..0d3b343cab067 100644 --- a/ports/rp2/mpthreadport.c +++ b/ports/rp2/mpthreadport.c @@ -39,7 +39,7 @@ extern uint8_t __StackTop, __StackBottom; void *core_state[2]; // This will be non-NULL while Python code is executing. -static void *(*core1_entry)(void *) = NULL; +core_entry_func_t core1_entry = NULL; static void *core1_arg = NULL; static uint32_t *core1_stack = NULL; diff --git a/ports/rp2/mpthreadport.h b/ports/rp2/mpthreadport.h index 7982de96a8deb..67a0da0e9373a 100644 --- a/ports/rp2/mpthreadport.h +++ b/ports/rp2/mpthreadport.h @@ -32,6 +32,10 @@ typedef struct mutex mp_thread_mutex_t; extern void *core_state[2]; +typedef void *(*core_entry_func_t)(void *); + +extern core_entry_func_t core1_entry; + void mp_thread_init(void); void mp_thread_deinit(void); void mp_thread_gc_others(void); From e1ecc232dc7ef2bd5f60cc5b93c5081c3e3c1fee Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 8 Jul 2024 11:45:12 +0100 Subject: [PATCH 0131/1300] rp2/rp2_pio: Disable correct IRQ for PIO1. Fix a typo that was disabling PIO0_IRQ_1 instead of PIO1_IRQ_0. Signed-off-by: Phil Howard --- ports/rp2/rp2_pio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/rp2/rp2_pio.c b/ports/rp2/rp2_pio.c index 410269e2d3d31..da62e6bec9e8d 100644 --- a/ports/rp2/rp2_pio.c +++ b/ports/rp2/rp2_pio.c @@ -149,7 +149,7 @@ void rp2_pio_init(void) { void rp2_pio_deinit(void) { // Disable and clear interrupts. - irq_set_mask_enabled((1u << PIO0_IRQ_0) | (1u << PIO0_IRQ_1), false); + irq_set_mask_enabled((1u << PIO0_IRQ_0) | (1u << PIO1_IRQ_0), false); irq_remove_handler(PIO0_IRQ_0, pio0_irq0); irq_remove_handler(PIO1_IRQ_0, pio1_irq0); From 81daba31c5f7541986b4cc1b12d2cf9c41e025e1 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 23 Jul 2024 16:42:42 +1000 Subject: [PATCH 0132/1300] docs: Specify that machine.idle() returns at least every 1ms. A lot of existing code (i.e. micropython-lib lps22h, lcd160cr sensor drivers, lora sync_modem driver, usb-device-hid) calls machine.idle() inside a tight loop that is polling some condition. This reduces the power usage compared to constantly looping, but can be faster than calling a sleep function. However on a tickless port there's not always an interrupt before the condition they are polling for, so it's difficult to restructure this code if machine.idle() doesn't have any upper limit on execution time. This commit specifies an upper limit of 1ms before machine.idle() resumes execution. This is already the case for all ports except rp2. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/library/machine.rst | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/library/machine.rst b/docs/library/machine.rst index 532266d1d9102..7d2eb26a7ea34 100644 --- a/docs/library/machine.rst +++ b/docs/library/machine.rst @@ -127,14 +127,20 @@ Power related functions .. function:: idle() - Gates the clock to the CPU, useful to reduce power consumption at any time during - short or long periods. Peripherals continue working and execution resumes as soon - as any interrupt is triggered (on many ports this includes system timer - interrupt occurring at regular intervals on the order of millisecond). + Gates the clock to the CPU, useful to reduce power consumption at any time + during short or long periods. Peripherals continue working and execution + resumes as soon as any interrupt is triggered, or at most one millisecond + after the CPU was paused. + + It is recommended to call this function inside any tight loop that is + continuously checking for an external change (i.e. polling). This will reduce + power consumption without significantly impacting performance. To reduce + power consumption further then see the :func:`lightsleep`, + :func:`time.sleep()` and :func:`time.sleep_ms()` functions. .. function:: sleep() - .. note:: This function is deprecated, use `lightsleep()` instead with no arguments. + .. note:: This function is deprecated, use :func:`lightsleep()` instead with no arguments. .. function:: lightsleep([time_ms]) deepsleep([time_ms]) From ba98533454eef5ab5783039f9929351c8f54d005 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 25 Jun 2024 15:30:02 +1000 Subject: [PATCH 0133/1300] rp2: Stop machine.idle() blocking indefinitely. Updates rp2 port to always resume from idle within 1ms max. When rp2 port went tickless the behaviour of machine.idle() changed as there is no longer a tick interrupt to wake it up every millisecond. On a quiet system it would now block indefinitely. No other port does this. See parent commit for justification of why this change is useful. Also adds a test case that fails without this change. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/modmachine.c | 2 +- tests/ports/rp2/rp2_machine_idle.py | 36 +++++++++++++++++++++++++ tests/ports/rp2/rp2_machine_idle.py.exp | 1 + 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/ports/rp2/rp2_machine_idle.py create mode 100644 tests/ports/rp2/rp2_machine_idle.py.exp diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 2cc79369f6149..229000cc1797c 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -103,7 +103,7 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { } static void mp_machine_idle(void) { - __wfe(); + MICROPY_INTERNAL_WFE(1); } static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { diff --git a/tests/ports/rp2/rp2_machine_idle.py b/tests/ports/rp2/rp2_machine_idle.py new file mode 100644 index 0000000000000..f9c28284782f8 --- /dev/null +++ b/tests/ports/rp2/rp2_machine_idle.py @@ -0,0 +1,36 @@ +import machine +import time + +# Verify that machine.idle() resumes execution within 0.1 and 1.1ms (should be +# 1ms max but allowing for some overhead). +# +# (A minimum sleep time for machine.idle() isn't specified but in a system like +# this with no active interrupts then we should expect some idle time before +# resuming. If it's consistently resuming immediately then that indicates a bug +# is preventing proper idle.) +# +# This test doesn't contain any rp2-specific code, but rp2 is currently the only +# tickless port - which is what led to the bug this is a regression test for. +# Some other ports (unix, esp32) have idle behaviour that resumes immediately on +# a quiet system, so this test is also less useful for those. +# +# Verification uses the average idle time, as individual iterations will always +# have outliers due to interrupts, scheduler, etc. + +ITERATIONS = 500 +total = 0 + +for _ in range(ITERATIONS): + before = time.ticks_us() + machine.idle() + total += time.ticks_diff(time.ticks_us(), before) + +total /= 1000 # us to ms +average = total / ITERATIONS + +# print(f"Total {total}ms average {average}ms") # uncomment for debug + +if 0.1 < average < 1.1: + print("PASS") +else: + print(f"Total {total}ms average {average}ms, out of spec") diff --git a/tests/ports/rp2/rp2_machine_idle.py.exp b/tests/ports/rp2/rp2_machine_idle.py.exp new file mode 100644 index 0000000000000..7ef22e9a431ad --- /dev/null +++ b/tests/ports/rp2/rp2_machine_idle.py.exp @@ -0,0 +1 @@ +PASS From 7fe8f030eea0015962e729eae1f1c309dc83a469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20D=C3=B6rre?= Date: Thu, 25 Aug 2022 17:26:37 +0000 Subject: [PATCH 0134/1300] rp2/lwip_inc: Enable IPv6 per default on rp2 port. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having IPv6 support is important, especially for IoT-Devices which might be many, requiring individual IP-addresses. In particular direct access via link-local addresses and having deterministic SLAAC-addresses can be quite convenient. Also in IPv6-only networks or for connecting to IPv6-only services, this is very useful. For the Pico W, there is enough flash and RAM that enabling IPv6 by default is the right choice. Should IPv6 support in a network exist (i.e. there are Router Advertisements), but not provide connectivity, connecting by domain name should not be a problem as DNS will default to return the IPv4-address (if that exists), unless reconfigured at runtime to prefer IPv6. In any case a user can disable obtaining SLAAC-addresses with: .ipconfig(autoconf6=False) Signed-off-by: Felix Dörre --- ports/rp2/lwip_inc/lwipopts.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ports/rp2/lwip_inc/lwipopts.h b/ports/rp2/lwip_inc/lwipopts.h index 691b228db5c93..5f37ecdac2e9d 100644 --- a/ports/rp2/lwip_inc/lwipopts.h +++ b/ports/rp2/lwip_inc/lwipopts.h @@ -26,7 +26,10 @@ #define LWIP_NETIF_EXT_STATUS_CALLBACK 1 #define LWIP_NETIF_STATUS_CALLBACK 1 -#define LWIP_IPV6 0 +#define LWIP_IPV4 1 +#define LWIP_IPV6 1 +#define LWIP_ND6_NUM_DESTINATIONS 4 +#define LWIP_ND6_QUEUEING 0 #define LWIP_DHCP 1 #define LWIP_DHCP_CHECK_LINK_UP 1 #define DHCP_DOES_ARP_CHECK 0 // to speed DHCP up From 19b1333cb1376ef60376a07e8e76a41854014840 Mon Sep 17 00:00:00 2001 From: Laurens Valk Date: Mon, 5 Dec 2022 16:51:20 +0100 Subject: [PATCH 0135/1300] examples/usercmodule/cexample: Add more advanced native class. This adds a separate `AdvancedTimer` class that demonstrates a few more advanced concepts usch as custom handlers for printing and attributes. Signed-off-by: Laurens Valk --- examples/usercmodule/cexample/examplemodule.c | 82 +++++++++++++++++++ tests/misc/cexample_class.py | 17 ++++ tests/misc/cexample_class.py.exp | 6 ++ tests/misc/cexample_module.py | 1 + 4 files changed, 106 insertions(+) diff --git a/examples/usercmodule/cexample/examplemodule.c b/examples/usercmodule/cexample/examplemodule.c index 2988fbd565f08..d13515e72cdd5 100644 --- a/examples/usercmodule/cexample/examplemodule.c +++ b/examples/usercmodule/cexample/examplemodule.c @@ -68,6 +68,87 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &example_Timer_locals_dict ); +// What follows is a *separate* class definition that demonstrates more +// advanced techniques to implement other Python-like features, such as: +// +// - A custom representation for __repr__ and __str__. +// - Custom attribute handling to create a read/write "property". +// +// It re-uses some of the elements of the basic Timer class. This is allowed +// because they both use example_Timer_obj_t as the instance structure. + +// Handles AdvancedTimer.__repr__, AdvancedTimer.__str__. +static void example_AdvancedTimer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + + // Get the elapsed time. In this case, it's also a demonstration of calling + // the equivalent of self.time() in the C API. This is not usually very + // efficient, but it can sometimes be useful. + mp_uint_t elapsed = mp_obj_get_int(example_Timer_time(self_in)); + + // We'll make all representations print at least the class name. + mp_printf(print, "%q()", MP_QSTR_AdvancedTimer); + + // Decide what else to print based on print kind. + if (kind == PRINT_STR) { + // For __str__, let's attempt to make it more readable. + mp_printf(print, " # created %d seconds ago", elapsed / 1000); + } +} + +// Handles AdvancedTimer.seconds for reading and writing. +static void example_AdvancedTimer_attribute_handler(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + + // In this example, we only want to handle the .seconds attribute in a + // special way. + if (attr != MP_QSTR_seconds) { + // Attribute not found, continue lookup in locals dict. This way, + // methods like .time() will be handled normally. + dest[1] = MP_OBJ_SENTINEL; + return; + } + + // Get reference to AdvancedTimer instance. + example_Timer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // Check if this is a read operation. + if (dest[0] == MP_OBJ_NULL) { + // It's read, so "return" elapsed seconds by storing it in dest[0]. + mp_uint_t elapsed = mp_hal_ticks_ms() - self->start_time; + dest[0] = mp_obj_new_int_from_uint(elapsed / 1000); + return; + } + // Check if this is a delete or store operation. + else if (dest[0] == MP_OBJ_SENTINEL) { + // It's delete or store. Now check which one. + if (dest[1] == MP_OBJ_NULL) { + // It's delete. But in this example we don't want to allow it + // so we just return. + return; + } else { + // It's write. First, get the value that the user is trying to set. + mp_uint_t desired_ms = mp_obj_get_int(dest[1]) * 1000; + // Use it to update the start time. This way, the next read will + // report the updated time. + self->start_time = mp_hal_ticks_ms() - desired_ms; + + // Indicate successful store. + dest[0] = MP_OBJ_NULL; + return; + } + } +} + +// This defines the type(AdvancedTimer) object. +MP_DEFINE_CONST_OBJ_TYPE( + example_type_AdvancedTimer, + MP_QSTR_AdvancedTimer, + MP_TYPE_FLAG_NONE, + attr, example_AdvancedTimer_attribute_handler, + print, example_AdvancedTimer_print, + make_new, example_Timer_make_new, + locals_dict, &example_Timer_locals_dict + ); + // Define all attributes of the module. // Table entries are key/value pairs of the attribute name (a string) // and the MicroPython object reference. @@ -77,6 +158,7 @@ static const mp_rom_map_elem_t example_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cexample) }, { MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) }, { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&example_type_Timer) }, + { MP_ROM_QSTR(MP_QSTR_AdvancedTimer), MP_ROM_PTR(&example_type_AdvancedTimer) }, }; static MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table); diff --git a/tests/misc/cexample_class.py b/tests/misc/cexample_class.py index 6b8718ad8cc5f..06d741922d289 100644 --- a/tests/misc/cexample_class.py +++ b/tests/misc/cexample_class.py @@ -22,3 +22,20 @@ print(timer) print(0 <= t_start <= TOLERANCE_MS) print(SLEEP_MS - TOLERANCE_MS <= t_end <= SLEEP_MS + TOLERANCE_MS) + +advanced_timer = cexample.AdvancedTimer() + +time.sleep_ms(100) + +print(repr(advanced_timer)) +print(str(advanced_timer)) + +print(advanced_timer.seconds) +advanced_timer.seconds = 123 +print(advanced_timer.seconds) +print(advanced_timer.time() < 123000 + TOLERANCE_MS) + +try: + advanced_timer.seconds = "bad input" +except TypeError: + print("TypeError") diff --git a/tests/misc/cexample_class.py.exp b/tests/misc/cexample_class.py.exp index b9a06602a316a..a86d4d14f78df 100644 --- a/tests/misc/cexample_class.py.exp +++ b/tests/misc/cexample_class.py.exp @@ -1,3 +1,9 @@ True True +AdvancedTimer() +AdvancedTimer() # created 0 seconds ago +0 +123 +True +TypeError diff --git a/tests/misc/cexample_module.py b/tests/misc/cexample_module.py index c1da2ecf7ab24..979c1fa24b376 100644 --- a/tests/misc/cexample_module.py +++ b/tests/misc/cexample_module.py @@ -12,5 +12,6 @@ d = dir(cexample) d.index("add_ints") d.index("Timer") +d.index("AdvancedTimer") print(cexample.add_ints(1, 3)) From 9ca668f881865958cbcc0e6341849a6133c4c7b4 Mon Sep 17 00:00:00 2001 From: Laurens Valk Date: Mon, 21 Nov 2022 21:35:26 +0100 Subject: [PATCH 0136/1300] py/objtype: Avoid crash on calling members of uninitialized native type. When subclassing a native type, calling native members in `__init__` before `super().__init__()` has been called could cause a crash. In this situation, `self` in `mp_convert_member_lookup` is the `native_base_init_wrapper_obj`. The check added in this commit ensures that an `AttributeError` is raised before this happens, which is consistent with other failed lookups. Also fix a typo in a related comment. Signed-off-by: Laurens Valk --- py/objtype.c | 8 ++++++- tests/misc/cexample_subclass.py | 37 +++++++++++++++++++++++++++++ tests/misc/cexample_subclass.py.exp | 5 ++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/misc/cexample_subclass.py create mode 100644 tests/misc/cexample_subclass.py.exp diff --git a/py/objtype.c b/py/objtype.c index b6d600e9434d7..f7a65a6ca5442 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -82,7 +82,7 @@ static int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t } } -// This wrapper function is allows a subclass of a native type to call the +// This wrapper function allows a subclass of a native type to call the // __init__() method (corresponding to type->make_new) of the native type. static mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) { mp_obj_instance_t *self = MP_OBJ_TO_PTR(args[0]); @@ -170,6 +170,12 @@ static void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_t if (obj != NULL && mp_obj_is_native_type(type) && type != &mp_type_object /* object is not a real type */) { // If we're dealing with native base class, then it applies to native sub-object obj_obj = obj->subobj[0]; + #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG + if (obj_obj == MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj)) { + // But we shouldn't attempt lookups on object that is not yet instantiated. + mp_raise_msg(&mp_type_AttributeError, MP_ERROR_TEXT("call super().__init__() first")); + } + #endif // MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG } else { obj_obj = MP_OBJ_FROM_PTR(obj); } diff --git a/tests/misc/cexample_subclass.py b/tests/misc/cexample_subclass.py new file mode 100644 index 0000000000000..9f52a2c737add --- /dev/null +++ b/tests/misc/cexample_subclass.py @@ -0,0 +1,37 @@ +# test subclassing custom native class + +try: + from cexample import AdvancedTimer +except ImportError: + print("SKIP") + raise SystemExit + + +class SubTimer(AdvancedTimer): + def __init__(self): + # At this point, self does not yet represent a AdvancedTimer instance. + print(self) + + # So lookups via type.attr handler will fail. + try: + self.seconds + except AttributeError: + print("AttributeError") + + # Also applies to builtin methods. + try: + self.time() + except AttributeError: + print("AttributeError") + + # Initialize base class. + super().__init__(self) + + # Now you can access methods and attributes normally. + self.time() + print(self.seconds) + self.seconds = 123 + print(self.seconds) + + +watch = SubTimer() diff --git a/tests/misc/cexample_subclass.py.exp b/tests/misc/cexample_subclass.py.exp new file mode 100644 index 0000000000000..a035649e475db --- /dev/null +++ b/tests/misc/cexample_subclass.py.exp @@ -0,0 +1,5 @@ + +AttributeError +AttributeError +0 +123 From d1bf0eeb0fb0c485da076075a15ffdfd5fb68303 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 17 Nov 2022 14:15:46 -0600 Subject: [PATCH 0137/1300] tests/cpydiff: Add diff for overriding __init__. This adds a CPython diff that explains why calling `super().__init__()` is required in MicroPython when subclassing a native type (because `__new__` and `__init__` are not separate functions). Signed-off-by: David Lechner --- tests/cpydiff/core_class_super_init.py | 31 ++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tests/cpydiff/core_class_super_init.py diff --git a/tests/cpydiff/core_class_super_init.py b/tests/cpydiff/core_class_super_init.py new file mode 100644 index 0000000000000..1774f61dd82e4 --- /dev/null +++ b/tests/cpydiff/core_class_super_init.py @@ -0,0 +1,31 @@ +""" +categories: Core,Classes +description: When inheriting native types, calling a method in ``__init__(self, ...)`` before ``super().__init__()`` raises an ``AttributeError`` (or segfaults if ``MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG`` is not enabled). +cause: MicroPython does not have separate ``__new__`` and ``__init__`` methods in native types. +workaround: Call ``super().__init__()`` first. +""" + + +class L1(list): + def __init__(self, a): + self.append(a) + + +try: + L1(1) + print("OK") +except AttributeError: + print("AttributeError") + + +class L2(list): + def __init__(self, a): + super().__init__() + self.append(a) + + +try: + L2(1) + print("OK") +except AttributeError: + print("AttributeError") From 093d0c0a17751c11c24f82594bb8208ca1ea9744 Mon Sep 17 00:00:00 2001 From: stijn Date: Wed, 8 Nov 2023 16:42:56 +0100 Subject: [PATCH 0138/1300] py/objtype: Validate super() arguments. This fixes various null dereferencing and out-of-bounds access because super_attr assumes the held obj is effectively an object of the held type, which is now verified. Fixes issue #12830. Signed-off-by: stijn --- py/objtype.c | 9 ++++++++- tests/basics/builtin_super.py | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/basics/builtin_super.py diff --git a/py/objtype.c b/py/objtype.c index f7a65a6ca5442..16ff5310dac6f 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_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); /******************************************************************************/ @@ -1260,9 +1261,15 @@ static mp_obj_t super_make_new(const mp_obj_type_t *type_in, size_t n_args, size // 0 arguments are turned into 2 in the compiler // 1 argument is not yet implemented mp_arg_check_num(n_args, n_kw, 2, 2, false); - if (!mp_obj_is_type(args[0], &mp_type_type)) { + + // Per CPython: "If the second argument is an object, isinstance(obj, type) must be true. + // If the second argument is a type, issubclass(type2, type) must be true (this is useful for classmethods)." + const mp_obj_type_t *second_arg_type = mp_obj_get_type(args[1]); + mp_obj_t second_arg_obj = second_arg_type == &mp_type_type ? args[1] : MP_OBJ_FROM_PTR(second_arg_type); + if (mp_obj_is_subclass(second_arg_obj, args[0]) == mp_const_false) { mp_raise_TypeError(NULL); } + mp_obj_super_t *o = m_new_obj(mp_obj_super_t); *o = (mp_obj_super_t) {{type_in}, args[0], args[1]}; return MP_OBJ_FROM_PTR(o); diff --git a/tests/basics/builtin_super.py b/tests/basics/builtin_super.py new file mode 100644 index 0000000000000..5f7c3ae017e7e --- /dev/null +++ b/tests/basics/builtin_super.py @@ -0,0 +1,15 @@ +# Check that super rejects invalid arguments. +try: + super(str, 0) +except TypeError: + print("TypeError") + +try: + super(str, int) +except TypeError: + print("TypeError") + +try: + super(0, int) +except TypeError: + print("TypeError") From 07bf3179f6d51a0e80fc48f06490f4e4d0d8488d Mon Sep 17 00:00:00 2001 From: stijn Date: Mon, 22 Jul 2024 13:41:02 +0200 Subject: [PATCH 0139/1300] py/misc: Fix msvc and C++ compatibility. Use explicit casts to suppress warnings about implicit conversions, add a workaround for constant expression conditional, and make functions static inline (as is done in the rest of the codebase) to suppress 'warning C4505: unreferenced function with internal linkage has been removed'. (Follow up to fix commit 908ab1ceca15ee6fd0ef82ca4cba770a3ec41894) Signed-off-by: stijn --- py/misc.h | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/py/misc.h b/py/misc.h index cf1810d4e784b..96b13c151a6b9 100644 --- a/py/misc.h +++ b/py/misc.h @@ -338,38 +338,44 @@ typedef const char *mp_rom_error_text_t; #ifdef _MSC_VER #include -static uint32_t mp_clz(uint32_t x) { +static inline uint32_t mp_clz(uint32_t x) { unsigned long lz = 0; return _BitScanReverse(&lz, x) ? (sizeof(x) * 8 - 1) - lz : 0; } -static uint32_t mp_clzl(unsigned long x) { +static inline uint32_t mp_clzl(unsigned long x) { unsigned long lz = 0; return _BitScanReverse(&lz, x) ? (sizeof(x) * 8 - 1) - lz : 0; } #ifdef _WIN64 -static uint32_t mp_clzll(unsigned long long x) { +static inline uint32_t mp_clzll(unsigned long long x) { unsigned long lz = 0; return _BitScanReverse64(&lz, x) ? (sizeof(x) * 8 - 1) - lz : 0; } #else // Microsoft don't ship _BitScanReverse64 on Win32, so emulate it -static uint32_t mp_clzll(unsigned long long x) { +static inline uint32_t mp_clzll(unsigned long long x) { unsigned long h = x >> 32; return h ? mp_clzl(h) : (mp_clzl(x) + 32); } #endif -static uint32_t mp_ctz(uint32_t x) { +static inline uint32_t mp_ctz(uint32_t x) { unsigned long tz = 0; return _BitScanForward(&tz, x) ? tz : 0; } + +// Workaround for 'warning C4127: conditional expression is constant'. +static inline bool mp_check(bool value) { + return value; +} #else #define mp_clz(x) __builtin_clz(x) #define mp_clzl(x) __builtin_clzl(x) #define mp_clzll(x) __builtin_clzll(x) #define mp_ctz(x) __builtin_ctz(x) +#define mp_check(x) (x) #endif // mp_int_t can be larger than long, i.e. Windows 64-bit, nan-box variants @@ -378,10 +384,10 @@ static inline uint32_t mp_clz_mpi(mp_int_t x) { || sizeof(mp_int_t) == sizeof(long)); // ugly, but should compile to single intrinsic unless O0 is set - if (sizeof(mp_int_t) == sizeof(long)) { - return mp_clzl(x); + if (mp_check(sizeof(mp_int_t) == sizeof(long))) { + return mp_clzl((unsigned long)x); } else { - return mp_clzll(x); + return mp_clzll((unsigned long long)x); } } From 233f5ce661d31c8e606ff727ac0c6de27d32a715 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 24 Jul 2024 17:17:39 +1000 Subject: [PATCH 0140/1300] py/runtime: Fix self arg passed to classmethod when accessed via super. Thanks to @AJMansfield for the original test case. Signed-off-by: Damien George --- py/runtime.c | 4 ++++ tests/basics/subclass_classmethod.py | 31 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/py/runtime.c b/py/runtime.c index 1836f5d92a851..fd0a8e690135a 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1153,6 +1153,10 @@ void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t // base type (which is what is passed in the `type` argument to this function). if (self != MP_OBJ_NULL) { type = mp_obj_get_type(self); + if (type == &mp_type_type) { + // `self` is already a type, so use `self` directly. + type = MP_OBJ_TO_PTR(self); + } } dest[0] = ((mp_obj_static_class_method_t *)MP_OBJ_TO_PTR(member))->fun; dest[1] = MP_OBJ_FROM_PTR(type); diff --git a/tests/basics/subclass_classmethod.py b/tests/basics/subclass_classmethod.py index 00a2ebd7cd3bb..3089726aa9bf5 100644 --- a/tests/basics/subclass_classmethod.py +++ b/tests/basics/subclass_classmethod.py @@ -35,3 +35,34 @@ class B(A): B.bar() # class calling classmethod B().bar() # instance calling classmethod B().baz() # instance calling normal method + +# super inside a classmethod +# ensure the argument of the super method that is called is the child type + + +class C: + @classmethod + def f(cls): + print("C.f", cls.__name__) # cls should be D + + @classmethod + def g(cls): + print("C.g", cls.__name__) # cls should be D + + +class D(C): + @classmethod + def f(cls): + print("D.f", cls.__name__) + super().f() + + @classmethod + def g(cls): + print("D.g", cls.__name__) + super(D, cls).g() + + +D.f() +D.g() +D().f() +D().g() From a0c7bf12d21bd51e8af4acab466a6c124c480fa7 Mon Sep 17 00:00:00 2001 From: stijn Date: Tue, 26 Mar 2024 16:09:15 +0100 Subject: [PATCH 0141/1300] github/workflows: Improve MSYS2-based CI builds. Install the mingw variant of Python since it behaves more like a 'real' Windows CPython than the msys2 variant: os.name == 'nt', not 'posix'. Note that os.sep is still '/' though so we don't actually need to skip the import_file test. This way one single Python version can be used both for running run-tests.py and getting the expected test output. Signed-off-by: stijn --- .github/workflows/ports_windows.yml | 15 ++------------- ports/windows/Makefile | 3 +++ ports/windows/README.md | 2 +- tests/run-tests.py | 9 ++++----- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ports_windows.yml b/.github/workflows/ports_windows.yml index be2a2a8dac332..7647749f7710b 100644 --- a/.github/workflows/ports_windows.yml +++ b/.github/workflows/ports_windows.yml @@ -108,16 +108,6 @@ jobs: run: shell: msys2 {0} steps: - - name: Get Python path - id: python_path - shell: python - run: | - import os - import sys - output = f"python={os.fspath(sys.executable)}" - print(output) - with open(os.environ["GITHUB_OUTPUT"], "w") as f: - f.write(output) - uses: msys2/setup-msys2@v2 with: msystem: ${{ matrix.sys }} @@ -126,7 +116,7 @@ jobs: make mingw-w64-${{ matrix.env }}-gcc pkg-config - python3 + mingw-w64-${{ matrix.env }}-python3 git diffutils - uses: actions/checkout@v4 @@ -138,8 +128,7 @@ jobs: run: make -C ports/windows -j2 VARIANT=${{ matrix.variant }} - name: Run tests id: test - # msys python breaks tests so we need to use "real" windows python - run: MICROPY_CPYTHON3=$(cygpath "${{ steps.python_path.outputs.python }}") make -C ports/windows test_full VARIANT=${{ matrix.variant }} + run: make -C ports/windows test_full VARIANT=${{ matrix.variant }} - name: Print failures if: failure() && steps.test.conclusion == 'failure' working-directory: tests diff --git a/ports/windows/Makefile b/ports/windows/Makefile index bb635167da349..cf0a927014b1a 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -101,6 +101,9 @@ include $(TOP)/py/mkrules.mk .PHONY: test test_full +# Note for recent gcc versions like 13.2: +# - mingw64-x86_64 gcc builds will pass the math_domain_special test +# - mingw64-ucrt64 gcc builds will pass all of the below tests RUN_TESTS_SKIP += -e math_fun -e float2int_double -e float_parse -e math_domain_special test: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py diff --git a/ports/windows/README.md b/ports/windows/README.md index 2b3ed44599918..57ec0e19149be 100644 --- a/ports/windows/README.md +++ b/ports/windows/README.md @@ -45,7 +45,7 @@ Install MSYS2 from http://repo.msys2.org/distrib, start the msys2.exe shell and install the build tools: pacman -Syuu - pacman -S make mingw-w64-x86_64-gcc pkg-config python3 + pacman -S make mingw-w64-x86_64-gcc pkg-config mingw-w64-x86_64-python3 Start the mingw64.exe shell and build: diff --git a/tests/run-tests.py b/tests/run-tests.py index e80d5b4633e31..a91b3e9e95ab0 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -3,6 +3,7 @@ import os import subprocess import sys +import sysconfig import platform import argparse import inspect @@ -583,10 +584,6 @@ 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 os.getenv("MSYSTEM") is not None: - # fails due to wrong path separator - skip_tests.add("import/import_file.py") - if upy_float_precision == 0: skip_tests.add("extmod/uctypes_le_float.py") skip_tests.add("extmod/uctypes_native_float.py") @@ -711,7 +708,9 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # Some tests use unsupported features on Windows if os.name == "nt": - skip_tests.add("import/import_file.py") # works but CPython prints forward slashes + if not sysconfig.get_platform().startswith("mingw"): + # 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 From 1f907a2f5cc912744442a1cdc3b08a19abccc71b Mon Sep 17 00:00:00 2001 From: stijn Date: Tue, 26 Mar 2024 16:11:11 +0100 Subject: [PATCH 0142/1300] tests/run-tests.py: Make Windows test skipping more granular. Signed-off-by: stijn --- .github/workflows/ports_windows.yml | 2 ++ tests/run-tests.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ports_windows.yml b/.github/workflows/ports_windows.yml index 7647749f7710b..91a3192a10a48 100644 --- a/.github/workflows/ports_windows.yml +++ b/.github/workflows/ports_windows.yml @@ -42,6 +42,8 @@ jobs: configuration: Debug - visualstudio: '2019' configuration: Debug + env: + CI_BUILD_CONFIGURATION: ${{ matrix.configuration }} runs-on: ${{ matrix.runner }} steps: - name: Install Visual Studio 2017 diff --git a/tests/run-tests.py b/tests/run-tests.py index a91b3e9e95ab0..e7ba0de565cfe 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -580,7 +580,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if os.getenv("GITHUB_ACTIONS") == "true": skip_tests.add("thread/stress_schedule.py") # has reliability issues - if os.getenv("RUNNER_OS") == "Windows": + if os.getenv("RUNNER_OS") == "Windows" and os.getenv("CI_BUILD_CONFIGURATION") == "Debug": # fails with stack overflow on Debug builds skip_tests.add("misc/sys_settrace_features.py") From 17f254df3589ce4a127ad4575ed2c031d5ab81b3 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 24 Jul 2024 02:29:52 +0200 Subject: [PATCH 0143/1300] github/workflows: Add RISC-V 64 bits Unix port to CI. Signed-off-by: Alessandro Gatti --- .github/workflows/ports_unix.yml | 14 ++++++++++++++ tools/ci.sh | 27 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index c83f67db875b3..1707fdc9fb37d 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -235,3 +235,17 @@ jobs: - name: Print failures if: failure() run: tests/run-tests.py --print-failures + + qemu_riscv64: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install packages + run: source tools/ci.sh && ci_unix_qemu_riscv64_setup + - name: Build + run: source tools/ci.sh && ci_unix_qemu_riscv64_build + - name: Run main test suite + run: source tools/ci.sh && ci_unix_qemu_riscv64_run_tests + - name: Print failures + if: failure() + run: tests/run-tests.py --print-failures diff --git a/tools/ci.sh b/tools/ci.sh index 53fcc8b5a3857..b0a1022c3b195 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -427,6 +427,11 @@ CI_UNIX_OPTS_QEMU_ARM=( MICROPY_STANDALONE=1 ) +CI_UNIX_OPTS_QEMU_RISCV64=( + CROSS_COMPILE=riscv64-linux-gnu- + VARIANT=coverage +) + function ci_unix_build_helper { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix "$@" submodules @@ -692,6 +697,28 @@ function ci_unix_qemu_arm_run_tests { (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py --exclude 'vfs_posix.*\.py') } +function ci_unix_qemu_riscv64_setup { + . /etc/os-release + for repository in "${VERSION_CODENAME}" "${VERSION_CODENAME}-updates" "${VERSION_CODENAME}-security" + do + sudo add-apt-repository -y -n "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports ${repository} main" + done + sudo apt-get update + sudo dpkg --add-architecture riscv64 + sudo apt-get install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu libffi-dev:riscv64 + sudo apt-get install qemu-user + qemu-riscv64 --version +} + +function ci_unix_qemu_riscv64_build { + ci_unix_build_helper "${CI_UNIX_OPTS_QEMU_RISCV64[@]}" +} + +function ci_unix_qemu_riscv64_run_tests { + file ./ports/unix/build-coverage/micropython + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py) +} + ######################################################################################## # ports/windows From e1fe62f4fca9506980bb19bbc973bf21740625e0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 25 Jul 2024 16:51:07 +1000 Subject: [PATCH 0144/1300] tests/multi_net: Fix skipping of SSLContext tests when .der don't exist. The `sslcontext_server_client_ciphers.py` test was using stat to test for the .der files after it already tried to open them for reading. That is now fixed. And `sslcontext_server_client.py` is adjusted to use the same pattern for skipping the test. Signed-off-by: Damien George --- tests/multi_net/sslcontext_server_client.py | 12 ++++-------- tests/multi_net/sslcontext_server_client_ciphers.py | 10 ++++------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/tests/multi_net/sslcontext_server_client.py b/tests/multi_net/sslcontext_server_client.py index 473c9c376e38a..6516de53f7dfb 100644 --- a/tests/multi_net/sslcontext_server_client.py +++ b/tests/multi_net/sslcontext_server_client.py @@ -15,18 +15,14 @@ keyfile = "ec_key.der" try: - os.stat(certfile) - os.stat(keyfile) + with open(certfile, "rb") as cf: + cert = cadata = cf.read() + with open(keyfile, "rb") as kf: + key = kf.read() except OSError: print("SKIP") raise SystemExit -with open(certfile, "rb") as cf: - cert = cadata = cf.read() - -with open(keyfile, "rb") as kf: - key = kf.read() - # Server def instance0(): diff --git a/tests/multi_net/sslcontext_server_client_ciphers.py b/tests/multi_net/sslcontext_server_client_ciphers.py index c168b444a992b..3334d9d9e4c17 100644 --- a/tests/multi_net/sslcontext_server_client_ciphers.py +++ b/tests/multi_net/sslcontext_server_client_ciphers.py @@ -13,14 +13,12 @@ # These are test certificates. See tests/README.md for details. cert = cafile = "ec_cert.der" key = "ec_key.der" -with open(cafile, "rb") as f: - cadata = f.read() -with open(key, "rb") as f: - keydata = f.read() try: - os.stat(cafile) - os.stat(key) + with open(cafile, "rb") as f: + cadata = f.read() + with open(key, "rb") as f: + keydata = f.read() except OSError: print("SKIP") raise SystemExit From 19075695da82e30850e8f595c58521d2b3787b4a Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 28 May 2024 19:38:36 +1000 Subject: [PATCH 0145/1300] nrf: Consolidate all stdio functions. Consolidate CDC, UART and NUS stdio interfaces into the one handler. Allows any/all of them to be enabled separately. Updates UART REPL to use similar define to other platforms: `MICROPY_HW_ENABLE_UART_REPL`. USB now uses the shared/tinyusb CDC implementation. Signed-off-by: Andrew Leech --- ports/nrf/Makefile | 25 ++-- ports/nrf/bluetooth_conf.h | 13 +- .../ARDUINO_NANO_33_BLE_SENSE/mpconfigboard.h | 1 + ports/nrf/boards/PCA10056/mpconfigboard.h | 4 + ports/nrf/boards/PCA10059/mpconfigboard.mk | 4 +- .../boards/SEEED_XIAO_NRF52/mpconfigboard.h | 1 + ports/nrf/drivers/bluetooth/ble_drv.c | 4 +- ports/nrf/drivers/bluetooth/ble_uart.c | 29 ++-- ports/nrf/drivers/bluetooth/ble_uart.h | 4 + ports/nrf/drivers/usb/tusb_config.h | 14 +- ports/nrf/drivers/usb/usb_cdc.c | 136 ++---------------- ports/nrf/drivers/usb/usb_descriptors.c | 117 --------------- ports/nrf/main.c | 10 +- ports/nrf/mpconfigport.h | 39 +++-- ports/nrf/mphalport.c | 86 +++++++---- ports/nrf/mphalport.h | 4 + 16 files changed, 155 insertions(+), 336 deletions(-) delete mode 100644 ports/nrf/drivers/usb/usb_descriptors.c diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 1691719dbb1b1..d955d183a1b4c 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -80,15 +80,16 @@ INC += -I./modules/music INC += -I./modules/ble INC += -I./modules/board INC += -I./modules/nrf -INC += -I../../shared/readline -INC += -I./drivers/bluetooth INC += -I./drivers +INC += -I./drivers/bluetooth +INC += -I./drivers/usb INC += -I../../lib/nrfx/ INC += -I../../lib/nrfx/drivers INC += -I../../lib/nrfx/drivers/include INC += -I../../lib/nrfx/mdk INC += -I../../lib/nrfx/hal INC += -I../../lib/nrfx/drivers/src/ +INC += -I../../shared/readline MCU_VARIANT_UPPER = $(shell echo $(MCU_VARIANT) | tr '[:lower:]' '[:upper:]') MCU_SUB_VARIANT_UPPER = $(shell echo $(MCU_SUB_VARIANT) | tr '[:lower:]' '[:upper:]') @@ -183,9 +184,12 @@ SRC_SHARED_C += $(addprefix shared/,\ libc/string0.c \ readline/readline.c \ runtime/pyexec.c \ + runtime/stdout_helpers.c \ runtime/sys_stdio_mphal.c \ runtime/interrupt_char.c \ + tinyusb/mp_usbd.c \ tinyusb/mp_usbd_cdc.c \ + tinyusb/mp_usbd_descriptor.c \ timeutils/timeutils.c \ ) @@ -200,13 +204,13 @@ endif SRC_NRFX += $(addprefix lib/nrfx/drivers/src/,\ prs/nrfx_prs.c \ nrfx_uart.c \ - nrfx_uarte.c \ + nrfx_uarte.c \ nrfx_adc.c \ nrfx_saadc.c \ nrfx_temp.c \ nrfx_rng.c \ nrfx_twi.c \ - nrfx_twim.c \ + nrfx_twim.c \ nrfx_spi.c \ nrfx_spim.c \ nrfx_rtc.c \ @@ -234,21 +238,17 @@ SRC_C += \ $(wildcard $(BOARD_DIR)/*.c) \ ifeq ($(MCU_SUB_VARIANT), nrf52840) +# Add support for USB using TinyUSB. -INC += -I./drivers/usb INC += -I../../lib/tinyusb/src - # If SoftDevice is selected. ifneq ($(SD), ) # For external tinyusb drivers to enable SoftDevice mode. CFLAGS += -DSOFTDEVICE_PRESENT endif -SRC_C += $(addprefix drivers/usb/,\ - usb_cdc.c \ - usb_descriptors.c \ - ) +SRC_C += drivers/usb/usb_cdc.c SRC_C += $(addprefix lib/tinyusb/src/,\ common/tusb_fifo.c \ @@ -259,6 +259,7 @@ SRC_C += $(addprefix lib/tinyusb/src/,\ portable/nordic/nrf5x/dcd_nrf5x.c \ ) +LDFLAGS += -Wl,--wrap=dcd_event_handler endif DRIVERS_SRC_C += $(addprefix modules/,\ @@ -331,9 +332,9 @@ hex: $(BUILD)/$(OUTPUT_FILENAME).hex $(BUILD)/$(OUTPUT_FILENAME).hex: $(BUILD)/$(OUTPUT_FILENAME).elf $(OBJCOPY) -O ihex $< $@ -FLASHER ?= +FLASHER ?= jlink -ifeq ($(FLASHER),) +ifeq ($(FLASHER), jlink) ifeq ($(MCU_VARIANT), nrf91) diff --git a/ports/nrf/bluetooth_conf.h b/ports/nrf/bluetooth_conf.h index 58d47e2188686..a7deebdfdce5a 100644 --- a/ports/nrf/bluetooth_conf.h +++ b/ports/nrf/bluetooth_conf.h @@ -3,10 +3,15 @@ // SD specific configurations. +#ifndef MICROPY_PY_BLE_NUS +// Nordic UART Service +// If enabled, REPL will be available on this interface. +#define MICROPY_PY_BLE_NUS (0) +#endif + #if (BLUETOOTH_SD == 110) #define MICROPY_PY_BLE (1) -#define MICROPY_PY_BLE_NUS (0) #define BLUETOOTH_WEBBLUETOOTH_REPL (0) #define MICROPY_PY_UBLUEPY (1) #define MICROPY_PY_UBLUEPY_PERIPHERAL (1) @@ -14,7 +19,6 @@ #elif (BLUETOOTH_SD == 132) #define MICROPY_PY_BLE (1) -#define MICROPY_PY_BLE_NUS (0) #define BLUETOOTH_WEBBLUETOOTH_REPL (0) #define MICROPY_PY_UBLUEPY (1) #define MICROPY_PY_UBLUEPY_PERIPHERAL (1) @@ -23,7 +27,6 @@ #elif (BLUETOOTH_SD == 140) #define MICROPY_PY_BLE (1) -#define MICROPY_PY_BLE_NUS (0) #define BLUETOOTH_WEBBLUETOOTH_REPL (0) #define MICROPY_PY_UBLUEPY (1) #define MICROPY_PY_UBLUEPY_PERIPHERAL (1) @@ -39,8 +42,4 @@ #define MICROPY_PY_BLE (0) #endif -#ifndef MICROPY_PY_BLE_NUS -#define MICROPY_PY_BLE_NUS (0) -#endif - #endif diff --git a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/mpconfigboard.h b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/mpconfigboard.h index 9b26b358da1b1..a4bb1d899e067 100644 --- a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/mpconfigboard.h +++ b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/mpconfigboard.h @@ -18,6 +18,7 @@ #define MICROPY_PY_MACHINE_ADC (1) #define MICROPY_PY_MACHINE_TEMP (1) +#define MICROPY_HW_ENABLE_USBDEV (1) #define MICROPY_HW_USB_CDC (1) #define MICROPY_HW_HAS_LED (1) #define MICROPY_HW_HAS_SWITCH (0) diff --git a/ports/nrf/boards/PCA10056/mpconfigboard.h b/ports/nrf/boards/PCA10056/mpconfigboard.h index 5b1b3a36ee26a..b3ad76a7f4464 100644 --- a/ports/nrf/boards/PCA10056/mpconfigboard.h +++ b/ports/nrf/boards/PCA10056/mpconfigboard.h @@ -37,6 +37,9 @@ #define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_USBDEV (1) +#define MICROPY_HW_USB_CDC (1) + #define MICROPY_HW_HAS_LED (1) #define MICROPY_HW_LED_COUNT (4) #define MICROPY_HW_LED_PULLUP (1) @@ -47,6 +50,7 @@ #define MICROPY_HW_LED4 (16) // LED4 // UART config +#define MICROPY_HW_ENABLE_UART_REPL (1) #define MICROPY_HW_UART1_RX (8) #define MICROPY_HW_UART1_TX (6) #define MICROPY_HW_UART1_CTS (7) diff --git a/ports/nrf/boards/PCA10059/mpconfigboard.mk b/ports/nrf/boards/PCA10059/mpconfigboard.mk index 3a26a92b7dc1e..c6f087ad01da5 100644 --- a/ports/nrf/boards/PCA10059/mpconfigboard.mk +++ b/ports/nrf/boards/PCA10059/mpconfigboard.mk @@ -9,9 +9,7 @@ ifeq ($(DFU),1) BOOTLOADER=open_bootloader BOOTLOADER_VERSION_MAJOR=1 BOOTLOADER_VERSION_MINOR=2 -FLASHER=nrfutil -else -FLASHER=segger +FLASHER ?= nrfutil endif LD_FILES += boards/nrf52840_1M_256k.ld diff --git a/ports/nrf/boards/SEEED_XIAO_NRF52/mpconfigboard.h b/ports/nrf/boards/SEEED_XIAO_NRF52/mpconfigboard.h index 25a34541a4a53..1faa256d80b3c 100644 --- a/ports/nrf/boards/SEEED_XIAO_NRF52/mpconfigboard.h +++ b/ports/nrf/boards/SEEED_XIAO_NRF52/mpconfigboard.h @@ -32,6 +32,7 @@ #define MICROPY_BOARD_DEINIT XIAO_board_deinit #define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) XIAO_board_enter_bootloader() +#define MICROPY_HW_ENABLE_USBDEV (1) #define MICROPY_HW_USB_CDC (1) #define MICROPY_PY_MACHINE_UART (1) #define MICROPY_PY_MACHINE_HW_PWM (1) diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index b1caa187d5fc5..4bede3852a637 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -39,7 +39,7 @@ #include "mphalport.h" -#if MICROPY_HW_USB_CDC +#if MICROPY_HW_ENABLE_USBDEV && MICROPY_HW_USB_CDC #include "usb_cdc.h" #endif @@ -941,7 +941,7 @@ static void sd_evt_handler(uint32_t evt_id) { // unhandled event! break; } -#if MICROPY_HW_USB_CDC +#if MICROPY_HW_ENABLE_USBDEV && MICROPY_HW_USB_CDC // Forward SOC events to USB CDC driver. usb_cdc_sd_event_handler(evt_id); #endif diff --git a/ports/nrf/drivers/bluetooth/ble_uart.c b/ports/nrf/drivers/bluetooth/ble_uart.c index a342a1e00610c..69f86b489f313 100644 --- a/ports/nrf/drivers/bluetooth/ble_uart.c +++ b/ports/nrf/drivers/bluetooth/ble_uart.c @@ -97,20 +97,16 @@ static ubluepy_advertise_data_t m_adv_data_uart_service; static ubluepy_advertise_data_t m_adv_data_eddystone_url; #endif // BLUETOOTH_WEBBLUETOOTH_REPL -int mp_hal_stdin_rx_chr(void) { - while (!ble_uart_enabled()) { - // wait for connection +int mp_ble_uart_stdin_rx_chr(void) { + if (ble_uart_enabled() && !isBufferEmpty(mp_rx_ring_buffer)) { + uint8_t byte = -1; + bufferRead(mp_rx_ring_buffer, byte); + return (int)byte; } - while (isBufferEmpty(mp_rx_ring_buffer)) { - ; - } - - uint8_t byte; - bufferRead(mp_rx_ring_buffer, byte); - return (int)byte; + return -1; } -mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { +mp_uint_t mp_ble_uart_stdout_tx_strn(const char *str, size_t len) { // Not connected: drop output if (!ble_uart_enabled()) return 0; @@ -150,17 +146,8 @@ void ble_uart_tx_char(char c) { (uint8_t *)&c); } -void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) { - for (const char *top = str + len; str < top; str++) { - if (*str == '\n') { - ble_uart_tx_char('\r'); - } - ble_uart_tx_char(*str); - } -} - #if MICROPY_PY_SYS_STDFILES -uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { +uintptr_t mp_ble_uart_stdio_poll(uintptr_t poll_flags) { uintptr_t ret = 0; if ((poll_flags & MP_STREAM_POLL_RD) && ble_uart_enabled() && !isBufferEmpty(mp_rx_ring_buffer)) { diff --git a/ports/nrf/drivers/bluetooth/ble_uart.h b/ports/nrf/drivers/bluetooth/ble_uart.h index e67176a26feb6..c00e1f4323f07 100644 --- a/ports/nrf/drivers/bluetooth/ble_uart.h +++ b/ports/nrf/drivers/bluetooth/ble_uart.h @@ -37,6 +37,10 @@ void ble_uart_advertise(void); bool ble_uart_connected(void); bool ble_uart_enabled(void); +uintptr_t mp_ble_uart_stdio_poll(uintptr_t poll_flags); +int mp_ble_uart_stdin_rx_chr(void); +mp_uint_t mp_ble_uart_stdout_tx_strn(const char *str, size_t len); + #endif // BLUETOOTH_SD #endif // BLUETOOTH_LE_UART_H__ diff --git a/ports/nrf/drivers/usb/tusb_config.h b/ports/nrf/drivers/usb/tusb_config.h index 619578ad6ee3a..935d9c2291376 100644 --- a/ports/nrf/drivers/usb/tusb_config.h +++ b/ports/nrf/drivers/usb/tusb_config.h @@ -26,19 +26,9 @@ #ifndef MICROPY_INCLUDED_NRF_TUSB_CONFIG_H #define MICROPY_INCLUDED_NRF_TUSB_CONFIG_H -// Common configuration - -#define CFG_TUSB_MCU OPT_MCU_NRF5X -#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE - -#define CFG_TUSB_MEM_SECTION -#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4) +#include "shared/tinyusb/tusb_config.h" // Device configuration - -#define CFG_TUD_ENDOINT0_SIZE (64) -#define CFG_TUD_CDC (1) -#define CFG_TUD_CDC_RX_BUFSIZE (64) -#define CFG_TUD_CDC_TX_BUFSIZE (64) +#define CFG_TUSB_MCU OPT_MCU_NRF5X #endif // MICROPY_INCLUDED_NRF_TUSB_CONFIG_H diff --git a/ports/nrf/drivers/usb/usb_cdc.c b/ports/nrf/drivers/usb/usb_cdc.c index aef7354972722..4d6583fef02ca 100644 --- a/ports/nrf/drivers/usb/usb_cdc.c +++ b/ports/nrf/drivers/usb/usb_cdc.c @@ -27,7 +27,7 @@ #include "py/mphal.h" -#if MICROPY_HW_USB_CDC +#if MICROPY_HW_ENABLE_USBDEV && MICROPY_HW_USB_CDC #include "nrfx.h" #include "nrfx_power.h" @@ -37,6 +37,9 @@ #include "py/runtime.h" #include "shared/runtime/interrupt_char.h" #include "shared/tinyusb/mp_usbd.h" +#include "shared/tinyusb/mp_usbd_cdc.h" +#include "extmod/misc.h" +#include #ifdef BLUETOOTH_SD #include "nrf_sdm.h" @@ -46,13 +49,6 @@ extern void tusb_hal_nrf_power_event(uint32_t event); -static void cdc_task(bool tx); - -static uint8_t rx_ringbuf_array[1024]; -static uint8_t tx_ringbuf_array[1024]; -static volatile ringbuf_t rx_ringbuf; -static volatile ringbuf_t tx_ringbuf; - static void board_init(void) { // Config clock source. #ifndef BLUETOOTH_SD @@ -105,89 +101,27 @@ static void board_init(void) { #endif } -static bool cdc_rx_any(void) { - return rx_ringbuf.iput != rx_ringbuf.iget; -} - -static int cdc_rx_char(void) { - return ringbuf_get((ringbuf_t*)&rx_ringbuf); -} - -static bool cdc_tx_any(void) { - return tx_ringbuf.iput != tx_ringbuf.iget; -} - -static int cdc_tx_char(void) { - return ringbuf_get((ringbuf_t*)&tx_ringbuf); -} - -static void cdc_task(bool tx) -{ - if ( tud_cdc_connected() ) { - // connected and there are data available - while (tud_cdc_available()) { - int c = tud_cdc_read_char(); - if (c == mp_interrupt_char) { - rx_ringbuf.iget = 0; - rx_ringbuf.iput = 0; - mp_sched_keyboard_interrupt(); - } else { - ringbuf_put((ringbuf_t*)&rx_ringbuf, c); - } - } - - if (tx) { - int chars = 0; - while (cdc_tx_any()) { - if (chars < 64) { - tud_cdc_write_char(cdc_tx_char()); - chars++; - } else { - chars = 0; - tud_cdc_write_flush(); - } - } - - tud_cdc_write_flush(); - } - } -} - -static void usb_cdc_loop(void) { - tud_task(); - cdc_task(true); -} - -void tud_cdc_rx_cb(uint8_t itf) { - cdc_task(false); +void mp_usbd_port_get_serial_number(char *serial_buf) { + uint32_t deviceid[2]; + deviceid[0] = nrf_ficr_deviceid_get(NRF_FICR, 0); + deviceid[1] = nrf_ficr_deviceid_get(NRF_FICR, 1); + MP_STATIC_ASSERT(sizeof(deviceid) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); + mp_usbd_hex_str(serial_buf, (uint8_t *)deviceid, sizeof(deviceid)); } int usb_cdc_init(void) { static bool initialized = false; if (!initialized) { - -#if BLUETOOTH_SD + #if BLUETOOTH_SD // Initialize the clock and BLE stack. ble_drv_stack_enable(); -#endif - + #endif board_init(); initialized = true; } - rx_ringbuf.buf = rx_ringbuf_array; - rx_ringbuf.size = sizeof(rx_ringbuf_array); - rx_ringbuf.iget = 0; - rx_ringbuf.iput = 0; - - tx_ringbuf.buf = tx_ringbuf_array; - tx_ringbuf.size = sizeof(tx_ringbuf_array); - tx_ringbuf.iget = 0; - tx_ringbuf.iput = 0; - mp_usbd_init(); - return 0; } @@ -205,52 +139,6 @@ void usb_cdc_sd_event_handler(uint32_t soc_evt) { } #endif -uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { - uintptr_t ret = 0; - if (poll_flags & MP_STREAM_POLL_RD) { - usb_cdc_loop(); - if (cdc_rx_any()) { - ret |= MP_STREAM_POLL_RD; - } - } - if (poll_flags & MP_STREAM_POLL_WR) { - ret |= MP_STREAM_POLL_WR; - } - return ret; -} - -int mp_hal_stdin_rx_chr(void) { - for (;;) { - usb_cdc_loop(); - if (cdc_rx_any()) { - return cdc_rx_char(); - } - MICROPY_EVENT_POLL_HOOK - } - - return 0; -} - -mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { - for (const char *top = str + len; str < top; str++) { - ringbuf_put((ringbuf_t*)&tx_ringbuf, *str); - usb_cdc_loop(); - } - return len; -} - -void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) { - - for (const char *top = str + len; str < top; str++) { - if (*str == '\n') { - ringbuf_put((ringbuf_t*)&tx_ringbuf, '\r'); - usb_cdc_loop(); - } - ringbuf_put((ringbuf_t*)&tx_ringbuf, *str); - usb_cdc_loop(); - } -} - void USBD_IRQHandler(void) { tud_int_handler(0); } diff --git a/ports/nrf/drivers/usb/usb_descriptors.c b/ports/nrf/drivers/usb/usb_descriptors.c deleted file mode 100644 index b1edfcc18fd8f..0000000000000 --- a/ports/nrf/drivers/usb/usb_descriptors.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 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 "tusb.h" - -#ifndef MICROPY_HW_USB_VID -#define MICROPY_HW_USB_VID (0xf055) -#define MICROPY_HW_USB_PID (0x9802) -#endif - -#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) -#define USBD_MAX_POWER_MA (250) - -#define USBD_ITF_CDC (0) // needs 2 interfaces -#define USBD_ITF_MAX (2) - -#define USBD_CDC_EP_CMD (0x81) -#define USBD_CDC_EP_OUT (0x02) -#define USBD_CDC_EP_IN (0x82) -#define USBD_CDC_CMD_MAX_SIZE (8) -#define USBD_CDC_IN_OUT_MAX_SIZE (64) - -#define USBD_STR_0 (0x00) -#define USBD_STR_MANUF (0x01) -#define USBD_STR_PRODUCT (0x02) -#define USBD_STR_SERIAL (0x03) -#define USBD_STR_CDC (0x04) - -// Note: descriptors returned from callbacks must exist long enough for transfer to complete - -static const tusb_desc_device_t usbd_desc_device = { - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - .bDeviceClass = TUSB_CLASS_MISC, - .bDeviceSubClass = MISC_SUBCLASS_COMMON, - .bDeviceProtocol = MISC_PROTOCOL_IAD, - .bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE, - .idVendor = MICROPY_HW_USB_VID, - .idProduct = MICROPY_HW_USB_PID, - .bcdDevice = 0x0100, - .iManufacturer = USBD_STR_MANUF, - .iProduct = USBD_STR_PRODUCT, - .iSerialNumber = USBD_STR_SERIAL, - .bNumConfigurations = 1, -}; - -static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { - TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, - TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA), - - TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, - USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), -}; - -static const char *const usbd_desc_str[] = { - [USBD_STR_MANUF] = "MicroPython", - [USBD_STR_PRODUCT] = "Board in FS mode", - [USBD_STR_SERIAL] = "000000000000", // TODO - [USBD_STR_CDC] = "Board CDC", -}; - -const uint8_t *tud_descriptor_device_cb(void) { - return (const uint8_t*)&usbd_desc_device; -} - -const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { - (void)index; - return usbd_desc_cfg; -} - -const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { - #define DESC_STR_MAX (20) - static uint16_t desc_str[DESC_STR_MAX]; - - uint8_t len; - if (index == 0) { - desc_str[1] = 0x0409; // supported language is English - len = 1; - } else { - if (index >= sizeof(usbd_desc_str) / sizeof(usbd_desc_str[0])) { - return NULL; - } - const char* str = usbd_desc_str[index]; - for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len) { - desc_str[1 + len] = str[len]; - } - } - - // first byte is length (including header), second byte is string type - desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2 * len + 2); - - return desc_str; -} diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 07bf3e8adb5c6..62848ba8d6e27 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -73,7 +73,7 @@ #include "softpwm.h" #endif -#if MICROPY_HW_USB_CDC +#if MICROPY_HW_ENABLE_USBDEV && MICROPY_HW_USB_CDC #include "usb_cdc.h" #endif @@ -165,11 +165,11 @@ void NORETURN _start(void) { uart_init0(); #endif - #if (MICROPY_PY_BLE_NUS == 0) && (MICROPY_HW_USB_CDC == 0) + #if MICROPY_HW_ENABLE_UART_REPL { mp_obj_t args[2] = { - MP_OBJ_NEW_SMALL_INT(0), - MP_OBJ_NEW_SMALL_INT(115200), + MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL), + MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL_BAUD), }; MP_STATE_VM(dupterm_objs[0]) = MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, make_new)((mp_obj_t)&machine_uart_type, MP_ARRAY_SIZE(args), 0, args); } @@ -265,7 +265,7 @@ void NORETURN _start(void) { ret_code = pyexec_file_if_exists("boot.py"); #endif - #if MICROPY_HW_USB_CDC + #if MICROPY_HW_ENABLE_USBDEV && MICROPY_HW_USB_CDC usb_cdc_init(); #endif diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 48b58143acdd0..1c789f779b8e7 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -158,8 +158,7 @@ #define MICROPY_STREAMS_NON_BLOCK (1) #define MICROPY_CAN_OVERRIDE_BUILTINS (1) #define MICROPY_USE_INTERNAL_ERRNO (1) -#if MICROPY_HW_USB_CDC_1200BPS_TOUCH -#define MICROPY_HW_ENABLE_USBDEV (1) +#if MICROPY_HW_ENABLE_USBDEV #define MICROPY_ENABLE_SCHEDULER (1) #define MICROPY_SCHEDULER_STATIC_NODES (1) #endif @@ -297,6 +296,24 @@ #define MICROPY_PY_BLE_NUS (0) #endif +// Whether to enable the REPL on a UART. +#ifndef MICROPY_HW_ENABLE_UART_REPL +// note: if both uart repl and cdc are enabled, uart hwfc can cause the cdc to lock up. +#define MICROPY_HW_ENABLE_UART_REPL (!MICROPY_PY_BLE_NUS && !MICROPY_HW_USB_CDC) +#endif + +#if MICROPY_HW_ENABLE_UART_REPL + +#ifndef MICROPY_HW_UART_REPL +#define MICROPY_HW_UART_REPL (0) +#endif + +#ifndef MICROPY_HW_UART_REPL_BAUD +#define MICROPY_HW_UART_REPL_BAUD (115200) +#endif + +#endif + // type definitions for the specific machine #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) @@ -331,17 +348,21 @@ long unsigned int rng_generate_random_word(void); #define MP_STATE_PORT MP_STATE_VM -#if MICROPY_HW_USB_CDC -#include "device/usbd.h" -#define MICROPY_HW_USBDEV_TASK_HOOK extern void tud_task(void); tud_task(); -#define MICROPY_EXCLUDE_SHARED_TINYUSB_USBD_CDC (1) -#else -#define MICROPY_HW_USBDEV_TASK_HOOK ; +#if MICROPY_HW_ENABLE_USBDEV +#ifndef MICROPY_HW_USB_CDC +#define MICROPY_HW_USB_CDC (1) +#endif + +#ifndef MICROPY_HW_USB_VID +#define MICROPY_HW_USB_VID (0xf055) +#endif +#ifndef MICROPY_HW_USB_PID +#define MICROPY_HW_USB_PID (0x9802) #endif +#endif // MICROPY_HW_ENABLE_USBDEV #define MICROPY_EVENT_POLL_HOOK \ do { \ - MICROPY_HW_USBDEV_TASK_HOOK \ extern void mp_handle_pending(bool); \ mp_handle_pending(true); \ __WFI(); \ diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c index 06c6ba5cc2e76..9bb51deb12153 100644 --- a/ports/nrf/mphalport.c +++ b/ports/nrf/mphalport.c @@ -29,11 +29,15 @@ #include "py/mpstate.h" #include "py/mphal.h" #include "py/mperrno.h" +#include "py/ringbuf.h" #include "py/runtime.h" #include "py/stream.h" +#include "extmod/misc.h" #include "uart.h" #include "nrfx_errors.h" #include "nrfx_config.h" +#include "drivers/bluetooth/ble_uart.h" +#include "shared/tinyusb/mp_usbd_cdc.h" #if MICROPY_PY_TIME_TICKS #include "nrfx_rtc.h" @@ -50,6 +54,14 @@ #include "soc/nrfx_coredep.h" #endif +#ifndef MICROPY_HW_STDIN_BUFFER_LEN +#define MICROPY_HW_STDIN_BUFFER_LEN 512 +#endif + +static uint8_t stdin_ringbuf_array[MICROPY_HW_STDIN_BUFFER_LEN]; +ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, 0 }; + + void mp_nrf_start_lfclk(void) { if (!nrf_clock_lf_start_task_status_get(NRF_CLOCK)) { // Check if the clock was recently stopped but is still running. @@ -200,47 +212,73 @@ void mp_hal_set_interrupt_char(int c) { } #endif -#if !MICROPY_PY_BLE_NUS && !MICROPY_HW_USB_CDC uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { uintptr_t ret = 0; - if ((poll_flags & MP_STREAM_POLL_RD) && MP_STATE_VM(dupterm_objs[0]) != MP_OBJ_NULL - && uart_rx_any(MP_STATE_VM(dupterm_objs[0]))) { - ret |= MP_STREAM_POLL_RD; - } - if ((poll_flags & MP_STREAM_POLL_WR) && MP_STATE_VM(dupterm_objs[0]) != MP_OBJ_NULL) { - ret |= MP_STREAM_POLL_WR; - } + #if MICROPY_HW_ENABLE_USBDEV && MICROPY_HW_USB_CDC + ret |= mp_usbd_cdc_poll_interfaces(poll_flags); + #endif + #if MICROPY_PY_BLE_NUS && MICROPY_PY_SYS_STDFILES + ret |= mp_ble_uart_stdio_poll(poll_flags); + #endif + #if MICROPY_PY_OS_DUPTERM + ret |= mp_os_dupterm_poll(poll_flags); + #endif return ret; } int mp_hal_stdin_rx_chr(void) { for (;;) { - if (MP_STATE_VM(dupterm_objs[0]) != MP_OBJ_NULL && uart_rx_any(MP_STATE_VM(dupterm_objs[0]))) { - return uart_rx_char(MP_STATE_VM(dupterm_objs[0])); + #if MICROPY_HW_ENABLE_USBDEV && MICROPY_HW_USB_CDC + mp_usbd_cdc_poll_interfaces(0); + #endif + int c = ringbuf_get(&stdin_ringbuf); + if (c != -1) { + return c; + } + #if MICROPY_PY_BLE_NUS + c = mp_ble_uart_stdin_rx_chr(); + if (c != -1) { + return c; + } + #endif + #if MICROPY_PY_OS_DUPTERM + int dupterm_c = mp_os_dupterm_rx_chr(); + if (dupterm_c >= 0) { + return dupterm_c; } + #endif MICROPY_EVENT_POLL_HOOK } return 0; } +// Send string of given length mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { - if (MP_STATE_VM(dupterm_objs[0]) != MP_OBJ_NULL) { - uart_tx_strn(MP_STATE_VM(dupterm_objs[0]), str, len); - return len; + mp_uint_t ret = len; + bool did_write = false; + #if MICROPY_HW_ENABLE_USBDEV && MICROPY_HW_USB_CDC + mp_uint_t cdc_res = mp_usbd_cdc_tx_strn(str, len); + if (cdc_res > 0) { + did_write = true; + ret = MIN(cdc_res, ret); } - return 0; -} - -void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) { - if (MP_STATE_VM(dupterm_objs[0]) != MP_OBJ_NULL) { - uart_tx_strn_cooked(MP_STATE_VM(dupterm_objs[0]), str, len); + #endif + #if MICROPY_PY_BLE_NUS + mp_uint_t ble_res = mp_ble_uart_stdout_tx_strn(str, len); + if (ble_res > 0) { + did_write = true; + ret = MIN(ble_res, ret); } -} -#endif - -void mp_hal_stdout_tx_str(const char *str) { - mp_hal_stdout_tx_strn(str, strlen(str)); + #endif + #if MICROPY_PY_OS_DUPTERM + int dupterm_res = mp_os_dupterm_tx_strn(str, len); + if (dupterm_res >= 0) { + did_write = true; + ret = MIN((mp_uint_t)dupterm_res, ret); + } + #endif + return did_write ? ret : 0; } #if MICROPY_PY_TIME_TICKS diff --git a/ports/nrf/mphalport.h b/ports/nrf/mphalport.h index 7efe05a15fc8e..50c61aefcdd59 100644 --- a/ports/nrf/mphalport.h +++ b/ports/nrf/mphalport.h @@ -28,10 +28,12 @@ #define __NRF52_HAL #include "py/mpconfig.h" +#include "py/ringbuf.h" #include #include "pin.h" #include "nrf_gpio.h" #include "nrfx_config.h" +#include "shared/runtime/interrupt_char.h" typedef enum { @@ -43,6 +45,8 @@ typedef enum extern const unsigned char mp_hal_status_to_errno_table[4]; +extern ringbuf_t stdin_ringbuf; + NORETURN void mp_hal_raise(HAL_StatusTypeDef status); void mp_hal_set_interrupt_char(int c); // -1 to disable From 56c161738496c03f0d651264861ff0c334b04e5d Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 29 May 2024 11:48:19 +1000 Subject: [PATCH 0146/1300] nrf/modules/machine/uart: Support sending data stored in flash. Signed-off-by: Andrew Leech --- ports/nrf/modules/machine/uart.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/ports/nrf/modules/machine/uart.c b/ports/nrf/modules/machine/uart.c index de26fa1b12787..90a67bcbc08ad 100644 --- a/ports/nrf/modules/machine/uart.c +++ b/ports/nrf/modules/machine/uart.c @@ -29,6 +29,7 @@ // This file is never compiled standalone, it's included directly from // extmod/machine_uart.c via MICROPY_PY_MACHINE_UART_INCLUDEFILE. +#include #include "py/mperrno.h" #include "py/mphal.h" #include "py/ringbuf.h" @@ -317,10 +318,30 @@ static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t return size; } -static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { - machine_uart_obj_t *self = self_in; +static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { + #if !NRFX_UART_ENABLED + if (!nrfx_is_in_ram(buf)) { + // Peripherals using EasyDMA require that transfer buffers are placed in DataRAM, + // they cannot access data directly from flash. + // If buf is in flash, copy to ram in chunks to send. + char rambuf[64]; + char *flashbuf = (char *)buf; + mp_uint_t remaining = size; + while (remaining) { + mp_uint_t chunk = MIN(sizeof(rambuf), remaining); + memcpy(rambuf, flashbuf, chunk); + if (mp_machine_uart_write(self_in, rambuf, chunk, errcode) == MP_STREAM_ERROR) { + return MP_STREAM_ERROR; + } + remaining -= chunk; + flashbuf += chunk; + } + return size; + } + #endif - nrfx_err_t err = nrfx_uart_tx(self->p_uart, buf_in, size); + machine_uart_obj_t *self = self_in; + nrfx_err_t err = nrfx_uart_tx(self->p_uart, buf, size); if (err == NRFX_SUCCESS) { while (nrfx_uart_tx_in_progress(self->p_uart)) { MICROPY_EVENT_POLL_HOOK; From 62e0fa04a7a6f9044db1bb0f20ea7a2e00599921 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 5 Jun 2024 12:01:12 +1000 Subject: [PATCH 0147/1300] nrf/Makefile: Enable LTO by default only on newer gcc. Older gcc/binutils linker does not support lto with wrap. Signed-off-by: Andrew Leech --- ports/nrf/Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index d955d183a1b4c..621f7d33c60c8 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -129,7 +129,15 @@ CFLAGS_MCU_m4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 -mfpu=fpv4-s CFLAGS_MCU_m0 = $(CFLAGS_CORTEX_M) -fshort-enums -mtune=cortex-m0 -mcpu=cortex-m0 -mfloat-abi=soft +# linker wrap does not work with lto on older gcc/binutils: https://sourceware.org/bugzilla/show_bug.cgi?id=24406 +GCC_VERSION = $(shell arm-none-eabi-gcc --version | sed -n -E 's:^arm.*([0-9]+\.[0-9]+\.[0-9]+).*$$:\1:p') +GCC_MAJOR_VERS = $(word 1,$(subst ., ,$(GCC_VERSION))) +ifeq ($(shell test $(GCC_MAJOR_VERS) -ge 10; echo $$?),0) LTO ?= 1 +else +LTO ?= 0 +endif + ifeq ($(LTO),1) CFLAGS += -flto else From 5e80416e6d6168ec5f1ad43191d4ddac1332c641 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 5 Jun 2024 12:22:03 +1000 Subject: [PATCH 0148/1300] nrf/modules/machine/soft_pwm: Ensure duty_width is always valid. Signed-off-by: Andrew Leech --- ports/nrf/modules/machine/soft_pwm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/nrf/modules/machine/soft_pwm.c b/ports/nrf/modules/machine/soft_pwm.c index 2fa3362658986..1891a6d6ee077 100644 --- a/ports/nrf/modules/machine/soft_pwm.c +++ b/ports/nrf/modules/machine/soft_pwm.c @@ -199,6 +199,8 @@ static void machine_soft_pwm_start(machine_pwm_obj_t *self) { duty_width = self->duty * DUTY_FULL_SCALE / 65536; } else if (self->duty_mode == DUTY_NS) { duty_width = (uint64_t)self->duty * self->freq * DUTY_FULL_SCALE / 1000000000ULL; + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid duty mode")); } pwm_set_duty_cycle(self->pwm_pin, duty_width); } From 4a134d212e74fa6da7cc2d91ec4026e6f5218f0e Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 9 Jun 2024 15:31:59 +0200 Subject: [PATCH 0149/1300] nrf/modules/machine/pin: Disable IRQ with pin.irq(handler=None). Before, the input was still set to `pin.irq()` mode, only the handler was disabled. That prevented switching the pin between input and output mode. Signed-off-by: robert-hh --- ports/nrf/modules/machine/pin.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/ports/nrf/modules/machine/pin.c b/ports/nrf/modules/machine/pin.c index 2191cc9521d01..f46394d769c95 100644 --- a/ports/nrf/modules/machine/pin.c +++ b/ports/nrf/modules/machine/pin.c @@ -537,19 +537,24 @@ static mp_obj_t pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar nrfx_gpiote_pin_t pin = self->pin; - nrfx_gpiote_in_config_t config = NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(true); - if (args[ARG_trigger].u_int == NRF_GPIOTE_POLARITY_LOTOHI) { - config.sense = NRF_GPIOTE_POLARITY_LOTOHI; - } else if (args[ARG_trigger].u_int == NRF_GPIOTE_POLARITY_HITOLO) { - config.sense = NRF_GPIOTE_POLARITY_HITOLO; - } - config.pull = NRF_GPIO_PIN_PULLUP; + if (args[ARG_handler].u_obj != mp_const_none) { + nrfx_gpiote_in_config_t config = NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(true); + if (args[ARG_trigger].u_int == NRF_GPIOTE_POLARITY_LOTOHI) { + config.sense = NRF_GPIOTE_POLARITY_LOTOHI; + } else if (args[ARG_trigger].u_int == NRF_GPIOTE_POLARITY_HITOLO) { + config.sense = NRF_GPIOTE_POLARITY_HITOLO; + } + config.pull = NRF_GPIO_PIN_PULLUP; + config.skip_gpio_setup = true; - nrfx_err_t err_code = nrfx_gpiote_in_init(pin, &config, pin_common_irq_handler); - if (err_code == NRFX_ERROR_INVALID_STATE) { - // Re-init if already configured. + nrfx_err_t err_code = nrfx_gpiote_in_init(pin, &config, pin_common_irq_handler); + if (err_code == NRFX_ERROR_INVALID_STATE) { + // Re-init if already configured. + nrfx_gpiote_in_uninit(pin); + nrfx_gpiote_in_init(pin, &config, pin_common_irq_handler); + } + } else { nrfx_gpiote_in_uninit(pin); - nrfx_gpiote_in_init(pin, &config, pin_common_irq_handler); } MP_STATE_PORT(pin_irq_handlers)[pin] = args[ARG_handler].u_obj; From 6007f3e2062cc65fc8416f241c682e37eb956c11 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 2 Jul 2024 22:49:47 +0200 Subject: [PATCH 0150/1300] esp32/mpconfigport: Enable the RV32 emitter for ESP32C3 targets. The RV32 code emitter assumed that the arch-specific NLR was used instead of the setjmp/longjmp based NLR code. If the latter NLR provider was chosen, the emitter would allocate space on the stack for the NLR buffer but would not fill it in. This change turns off setjmp()-based NLR and GCREGS for the ESP32C3 target, in favour of more platform-tailored alternatives. As setjmp() NLR is now disabled by default, the RV32 emitter can be safely enabled by default as well for the target in question. Signed-off-by: Alessandro Gatti --- ports/esp32/mpconfigport.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 558fa6b8c602c..b9e53a4fa8cd9 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -18,9 +18,8 @@ // object representation and NLR handling #define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_A) +#if !CONFIG_IDF_TARGET_ESP32C3 #define MICROPY_NLR_SETJMP (1) -#if CONFIG_IDF_TARGET_ESP32C3 -#define MICROPY_GCREGS_SETJMP (1) #endif // memory allocation policies @@ -44,6 +43,8 @@ #define MICROPY_PERSISTENT_CODE_LOAD (1) #if !CONFIG_IDF_TARGET_ESP32C3 #define MICROPY_EMIT_XTENSAWIN (1) +#else +#define MICROPY_EMIT_RV32 (1) #endif // workaround for xtensa-esp32-elf-gcc esp-2020r3, which can generate wrong code for loops From 70a7e0ff2f86cc7359785ad356cecc4449f7b850 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Fri, 26 Jul 2024 15:22:53 +1000 Subject: [PATCH 0151/1300] nrf/Makefile: Fix GCC_VERSION check. Previously it was truncating the first digit of the version if the major number had more than one digit. Signed-off-by: Andrew Leech --- ports/nrf/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 621f7d33c60c8..c4150c292cb7c 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -130,7 +130,7 @@ CFLAGS_MCU_m4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 -mfpu=fpv4-s CFLAGS_MCU_m0 = $(CFLAGS_CORTEX_M) -fshort-enums -mtune=cortex-m0 -mcpu=cortex-m0 -mfloat-abi=soft # linker wrap does not work with lto on older gcc/binutils: https://sourceware.org/bugzilla/show_bug.cgi?id=24406 -GCC_VERSION = $(shell arm-none-eabi-gcc --version | sed -n -E 's:^arm.*([0-9]+\.[0-9]+\.[0-9]+).*$$:\1:p') +GCC_VERSION = $(shell arm-none-eabi-gcc -dumpversion) GCC_MAJOR_VERS = $(word 1,$(subst ., ,$(GCC_VERSION))) ifeq ($(shell test $(GCC_MAJOR_VERS) -ge 10; echo $$?),0) LTO ?= 1 From a3100be4b226982c0c4aa162139617be23198c66 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 19 Jul 2024 18:56:36 +0300 Subject: [PATCH 0152/1300] stm32/boards: Swap FMC banks on ARDUINO_GIGA and ARDUINO_PORTENTA_H7. Swap FMC banks to remap the SDRAM bank1 address to 0x60000000. Arduino's M4 firmware uses address 0x60000000 by default. When the elf loader tries to load that it will fail because by default NOR/PSRAM is mapped at that address, not SDRAM bank1. (Note that the region at 0xC0000000 has an XN attribute by default, so switching the M4 firmware address will not work.) Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h | 1 + ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h | 1 + 2 files changed, 2 insertions(+) diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h index b278fa005b757..d09a074402e7b 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h @@ -226,6 +226,7 @@ extern struct _spi_bdev_t spi_bdev; #define MICROPY_HW_SDRAM_SIZE (64 / 8 * 1024 * 1024) // 64 Mbit #define MICROPY_HW_SDRAM_STARTUP_TEST (1) #define MICROPY_HW_SDRAM_TEST_FAIL_ON_ERROR (true) +#define MICROPY_HW_FMC_SWAP_BANKS (1) // Timing configuration for 200MHz/2=100MHz (10ns) #define MICROPY_HW_SDRAM_CLOCK_PERIOD 2 diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h index ace63e016b189..3a012446b465e 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h @@ -240,6 +240,7 @@ extern struct _spi_bdev_t spi_bdev; #define MICROPY_HW_SDRAM_SIZE (64 / 8 * 1024 * 1024) // 64 Mbit #define MICROPY_HW_SDRAM_STARTUP_TEST (1) #define MICROPY_HW_SDRAM_TEST_FAIL_ON_ERROR (true) +#define MICROPY_HW_FMC_SWAP_BANKS (1) // Timing configuration for 200MHz/2=100MHz (10ns) #define MICROPY_HW_SDRAM_CLOCK_PERIOD 2 From 6f27e1c9681de46e0c6ef859dd8be4fc6fd7cf1d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 19 Jul 2024 19:04:13 +0300 Subject: [PATCH 0153/1300] lib/arduino-lib: Update submodule to the latest. Signed-off-by: iabdalkader --- lib/arduino-lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arduino-lib b/lib/arduino-lib index 89424753e18ed..0f0993582f7f8 160000 --- a/lib/arduino-lib +++ b/lib/arduino-lib @@ -1 +1 @@ -Subproject commit 89424753e18ed58b7d8041085c9d2e1d162f09ca +Subproject commit 0f0993582f7f882f06371d21cd15748932795272 From 1754c587f9375f35c73d83982396b4f5232566ce Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 25 Jul 2024 11:57:09 +1000 Subject: [PATCH 0154/1300] esp32: Fix heap corruption triggered by bluetooth.active(0). It seems like at some point Espressif NimBLE team changed nimble_port_init and nimble_port_deinit to manage HCI init internally: https://github.com/espressif/esp-nimble/commit/f8a79b04c9743543b8959727d7 This change is included in all the IDF versions that MicroPython supports. As a result, existing code that called esp_nimble_hci_deinit() explicitly would trigger a use-after-free bug and heap corruption (specifically this calls through to ble_transport_deinit() which calls os_mempool_free(). The second time this writes out to a bunch of memory pools where the backing buffers have already been freed.) Symptoms were intermittent random crashes after de-activating Bluetooth (running multi_bluetooth/ble_gatt_data_transfer.py could sometimes reproduce). Setting Heap Poisoning to Comprehensive in menuconfig caused the bug to be detected every time. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/mpnimbleport.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ports/esp32/mpnimbleport.c b/ports/esp32/mpnimbleport.c index 669aeb746a58d..ce4b77727a190 100644 --- a/ports/esp32/mpnimbleport.c +++ b/ports/esp32/mpnimbleport.c @@ -32,7 +32,6 @@ #define DEBUG_printf(...) // printf("nimble (esp32): " __VA_ARGS__) -#include "esp_nimble_hci.h" #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" @@ -45,14 +44,13 @@ static void ble_host_task(void *param) { } void mp_bluetooth_nimble_port_hci_init(void) { - DEBUG_printf("mp_bluetooth_nimble_port_hci_init\n"); - esp_nimble_hci_init(); + // On ESP-IDF the standard nimble_port_init() function calls + // esp_nimble_init() which initialises the HCI } void mp_bluetooth_nimble_port_hci_deinit(void) { - DEBUG_printf("mp_bluetooth_nimble_port_hci_deinit\n"); - - esp_nimble_hci_deinit(); + // As above, this is handled by ESP-IDF nimble_port_deinit() + // (called below) } void mp_bluetooth_nimble_port_start(void) { From 066243ea74050523e807211f208e24fac08f69fc Mon Sep 17 00:00:00 2001 From: George Hopkins Date: Sun, 21 Jul 2024 16:48:48 +0200 Subject: [PATCH 0155/1300] py/py.mk: Add SRC_USERMOD_LIB_ASM to include assembly files. Introduce SRC_USERMOD_LIB_ASM to allow users to include assembly files as part of their user modules. It could be used to include optimized functions or outputs of other programming languages. Signed-off-by: George Hopkins --- docs/develop/cmodules.rst | 2 +- py/py.mk | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/develop/cmodules.rst b/docs/develop/cmodules.rst index 75dbc953c06f8..c5aa919b7645a 100644 --- a/docs/develop/cmodules.rst +++ b/docs/develop/cmodules.rst @@ -59,7 +59,7 @@ A MicroPython user C module is a directory with the following files: SRC_USERMOD_LIB_C += $(EXAMPLE_MOD_DIR)/utils/algorithm.c Similarly, use ``SRC_USERMOD_CXX`` and ``SRC_USERMOD_LIB_CXX`` for C++ - source files. + source files. If you want to include assembly files use ``SRC_USERMOD_LIB_ASM``. If you have custom compiler options (like ``-I`` to add directories to search for header files), these should be added to ``CFLAGS_USERMOD`` for C code diff --git a/py/py.mk b/py/py.mk index cd9392edbba4c..ff3dff96a5715 100644 --- a/py/py.mk +++ b/py/py.mk @@ -36,9 +36,10 @@ ifneq ($(USER_C_MODULES),) # C/C++ files that are included in the QSTR/module build SRC_USERMOD_C := SRC_USERMOD_CXX := -# Other C/C++ files (e.g. libraries or helpers) +# Other C/C++/Assembly files (e.g. libraries or helpers) SRC_USERMOD_LIB_C := SRC_USERMOD_LIB_CXX := +SRC_USERMOD_LIB_ASM := # Optionally set flags CFLAGS_USERMOD := CXXFLAGS_USERMOD := @@ -60,6 +61,7 @@ SRC_USERMOD_PATHFIX_C += $(patsubst $(USER_C_MODULES)/%.c,%.c,$(SRC_USERMOD_C)) SRC_USERMOD_PATHFIX_CXX += $(patsubst $(USER_C_MODULES)/%.cpp,%.cpp,$(SRC_USERMOD_CXX)) SRC_USERMOD_PATHFIX_LIB_C += $(patsubst $(USER_C_MODULES)/%.c,%.c,$(SRC_USERMOD_LIB_C)) SRC_USERMOD_PATHFIX_LIB_CXX += $(patsubst $(USER_C_MODULES)/%.cpp,%.cpp,$(SRC_USERMOD_LIB_CXX)) +SRC_USERMOD_PATHFIX_LIB_ASM += $(patsubst $(USER_C_MODULES)/%.S,%.S,$(SRC_USERMOD_LIB_ASM)) CFLAGS += $(CFLAGS_USERMOD) CXXFLAGS += $(CXXFLAGS_USERMOD) @@ -70,6 +72,7 @@ PY_O += $(addprefix $(BUILD)/, $(SRC_USERMOD_PATHFIX_C:.c=.o)) PY_O += $(addprefix $(BUILD)/, $(SRC_USERMOD_PATHFIX_CXX:.cpp=.o)) PY_O += $(addprefix $(BUILD)/, $(SRC_USERMOD_PATHFIX_LIB_C:.c=.o)) PY_O += $(addprefix $(BUILD)/, $(SRC_USERMOD_PATHFIX_LIB_CXX:.cpp=.o)) +PY_O += $(addprefix $(BUILD)/, $(SRC_USERMOD_PATHFIX_LIB_ASM:.S=.o)) endif # py object files From d1685a3f5f81f20e9eea26f1274bf6171b287814 Mon Sep 17 00:00:00 2001 From: Tim Weber Date: Tue, 23 Jul 2024 23:59:29 +0200 Subject: [PATCH 0156/1300] docs/library/neopixel: Mention bitstream timing tuple. Signed-off-by: Tim Weber --- docs/library/neopixel.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/library/neopixel.rst b/docs/library/neopixel.rst index edcbc9345c304..b618e6012c382 100644 --- a/docs/library/neopixel.rst +++ b/docs/library/neopixel.rst @@ -43,7 +43,8 @@ Constructors - *pin* is a machine.Pin instance. - *n* is the number of LEDs in the strip. - *bpp* is 3 for RGB LEDs, and 4 for RGBW LEDs. - - *timing* is 0 for 400KHz, and 1 for 800kHz LEDs (most are 800kHz). + - *timing* is 0 for 400KHz, and 1 for 800kHz LEDs (most are 800kHz). You + may also supply a timing tuple as accepted by `machine.bitstream()`. Pixel access methods -------------------- From 9ba04cc7563ec934ca14d66aa18ae3563c8d1aea Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 1 Aug 2024 16:26:46 +1000 Subject: [PATCH 0157/1300] tests/extmod: Skip soft machine.Timer test on esp32 port. Also rename the test to reflect that it's a soft timer test. Signed-off-by: Angus Gratton --- tests/extmod/{machine_timer.py => machine_soft_timer.py} | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) rename tests/extmod/{machine_timer.py => machine_soft_timer.py} (82%) diff --git a/tests/extmod/machine_timer.py b/tests/extmod/machine_soft_timer.py similarity index 82% rename from tests/extmod/machine_timer.py rename to tests/extmod/machine_soft_timer.py index 1be01d184d442..bff9af5b820f2 100644 --- a/tests/extmod/machine_timer.py +++ b/tests/extmod/machine_soft_timer.py @@ -1,4 +1,11 @@ -# test machine.Timer +# test "soft" machine.Timer (no hardware ID) +import sys + + +if sys.platform == "esp32": + print("SKIP") # TODO: Implement soft timers for esp32 port + raise SystemExit + try: import time, machine as machine From 0d00d72b76092dc62d194e5cbda8a1b00a4c3ff7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 25 Jul 2024 12:19:11 +1000 Subject: [PATCH 0158/1300] esp32/machine_i2s: Ensure 2 DMA buffers and improve I2S error handling. ESP-IDF driver always requires at least two DMA buffers, so ensure that's the case. Failures during initialisation were being lost because ESP_ERROR_CHECK is configured as a no-op, so the failure was deferred until read() or write() was called on the port. Raise an error from init, instead. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/machine_i2s.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/ports/esp32/machine_i2s.c b/ports/esp32/machine_i2s.c index 6ba2f54c354c6..ba6377a76646d 100644 --- a/ports/esp32/machine_i2s.c +++ b/ports/esp32/machine_i2s.c @@ -151,7 +151,7 @@ static uint32_t get_dma_buf_count(uint8_t mode, i2s_data_bit_width_t bits, forma uint32_t dma_buf_count = ibuf / (DMA_BUF_LEN_IN_I2S_FRAMES * dma_frame_size_in_bytes); - return dma_buf_count; + return MAX(dma_buf_count, 2); // ESP-IDF requires at least two DMA buffers } static uint32_t fill_appbuf_from_dma(machine_i2s_obj_t *self, mp_buffer_info_t *appbuf) { @@ -363,9 +363,9 @@ static void mp_machine_i2s_init_helper(machine_i2s_obj_t *self, mp_arg_val_t *ar chan_config.auto_clear = true; if (mode == MICROPY_PY_MACHINE_I2S_CONSTANT_TX) { - ESP_ERROR_CHECK(i2s_new_channel(&chan_config, &self->i2s_chan_handle, NULL)); + check_esp_err(i2s_new_channel(&chan_config, &self->i2s_chan_handle, NULL)); } else { // rx - ESP_ERROR_CHECK(i2s_new_channel(&chan_config, NULL, &self->i2s_chan_handle)); + check_esp_err(i2s_new_channel(&chan_config, NULL, &self->i2s_chan_handle)); } i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(get_dma_bits(mode, bits), get_dma_format(mode, format)); @@ -394,9 +394,9 @@ static void mp_machine_i2s_init_helper(machine_i2s_obj_t *self, mp_arg_val_t *ar std_cfg.gpio_cfg.din = self->sd; } - ESP_ERROR_CHECK(i2s_channel_init_std_mode(self->i2s_chan_handle, &std_cfg)); - ESP_ERROR_CHECK(i2s_channel_register_event_callback(self->i2s_chan_handle, &i2s_callbacks, self)); - ESP_ERROR_CHECK(i2s_channel_enable(self->i2s_chan_handle)); + check_esp_err(i2s_channel_init_std_mode(self->i2s_chan_handle, &std_cfg)); + check_esp_err(i2s_channel_register_event_callback(self->i2s_chan_handle, &i2s_callbacks, self)); + check_esp_err(i2s_channel_enable(self->i2s_chan_handle)); } static machine_i2s_obj_t *mp_machine_i2s_make_new_instance(mp_int_t i2s_id) { @@ -419,9 +419,12 @@ static machine_i2s_obj_t *mp_machine_i2s_make_new_instance(mp_int_t i2s_id) { static void mp_machine_i2s_deinit(machine_i2s_obj_t *self) { if (!self->is_deinit) { - ESP_ERROR_CHECK(i2s_channel_disable(self->i2s_chan_handle)); - ESP_ERROR_CHECK(i2s_channel_register_event_callback(self->i2s_chan_handle, &i2s_callbacks_null, self)); - ESP_ERROR_CHECK(i2s_del_channel(self->i2s_chan_handle)); + if (self->i2s_chan_handle) { + i2s_channel_disable(self->i2s_chan_handle); + i2s_channel_register_event_callback(self->i2s_chan_handle, &i2s_callbacks_null, self); + i2s_del_channel(self->i2s_chan_handle); + self->i2s_chan_handle = NULL; + } if (self->non_blocking_mode_task != NULL) { vTaskDelete(self->non_blocking_mode_task); From 6d054247546eb278942a407e0de7e72b5f66d54f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 25 Jul 2024 12:20:45 +1000 Subject: [PATCH 0159/1300] tests/extmod: Add esp32 support to the machine_i2s_rate test. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/extmod/machine_i2s_rate.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/extmod/machine_i2s_rate.py b/tests/extmod/machine_i2s_rate.py index bf4b7235a9cdf..c8fa11514c96b 100644 --- a/tests/extmod/machine_i2s_rate.py +++ b/tests/extmod/machine_i2s_rate.py @@ -8,7 +8,10 @@ import time, sys +MAX_DELTA_MS = 4 + # Configure pins based on the board. +# Tuples of (i2s_id, sck_pin, ws_pin, sd_tx_pin, sd_rx_pin) # A board must have at least one instance to test, both TX and RX mode. if "pyboard" in sys.platform: i2s_instances = ((2, Pin("Y6"), Pin("Y5"), Pin("Y8"), Pin("Y8")),) @@ -22,6 +25,10 @@ (1, Pin("D26"), Pin("D27"), Pin("D7"), Pin("D8")), (2, Pin("D4"), Pin("D3"), Pin("D2"), None), ) +elif "esp32" in sys.platform: + i2s_instances = ((0, Pin(18), Pin(19), Pin(21), Pin(14)),) + # Allow for small additional RTOS overhead + MAX_DELTA_MS = 8 TEST_BYTES = b"01234567" RATE = 11025 # frames/sec @@ -73,11 +80,12 @@ def test(i2s_id, sck_pin, ws_pin, sd_pin, mode, bits_per_sample, frame_format): i2s.deinit() # Time should be in range of 400ms. - time_in_range = abs(dt - 400) <= 4 + time_delta = abs(dt - 400) + time_in_range = time_delta <= MAX_DELTA_MS # Print out test result if requested, or if time not in range. if print_results or not time_in_range: - print(mode_str, bits_per_sample, channels, time_in_range) + print(mode_str, bits_per_sample, channels, time_in_range or time_delta) print_results = True From 11becbe22321ca1d7bd631144976f246175730ce Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 3 Jul 2024 15:15:16 +0100 Subject: [PATCH 0160/1300] rp2/CMakeLists.txt: Add MICROPY_DEF_BOARD to compile definitions. Add MICROPY_DEF_BOARD as per esp32 port, allows board variants to override the board name with: list(APPEND MICROPY_DEF_BOARD MICROPY_HW_BOARD_NAME="New Board Name" ) Signed-off-by: Phil Howard --- ports/rp2/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 8fff4251487ff..904925ae3f5e5 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -493,6 +493,7 @@ set_source_files_properties( ) target_compile_definitions(${MICROPY_TARGET} PRIVATE + ${MICROPY_DEF_BOARD} FFCONF_H=\"${MICROPY_OOFATFS_DIR}/ffconf.h\" LFS1_NO_MALLOC LFS1_NO_DEBUG LFS1_NO_WARN LFS1_NO_ERROR LFS1_NO_ASSERT LFS2_NO_MALLOC LFS2_NO_DEBUG LFS2_NO_WARN LFS2_NO_ERROR LFS2_NO_ASSERT From cd1ab7645e6528b9de6587669ae4fe38a5f55853 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 3 Jul 2024 15:17:48 +0100 Subject: [PATCH 0161/1300] rp2/boards/PIMORONI_PICOLIPO: Refactor Pico LiPo to use board variants. Combine the 4MB and 16MB "PIMORONI_PICOLIPO" variants into a single board. Signed-off-by: Phil Howard --- .../board.json | 5 ++- .../mpconfigboard.cmake | 0 .../mpconfigboard.h | 12 ++----- .../PIMORONI_PICOLIPO/mpconfigvariant.cmake | 6 ++++ .../mpconfigvariant_FLASH_16M.cmake | 6 ++++ ports/rp2/boards/PIMORONI_PICOLIPO/pins.csv | 31 +++++++++++++++++++ .../mpconfigboard.cmake | 1 - .../PIMORONI_PICOLIPO_16MB/mpconfigboard.h | 20 ------------ .../boards/PIMORONI_PICOLIPO_16MB/pins.csv | 1 - .../boards/PIMORONI_PICOLIPO_4MB/board.json | 21 ------------- .../rp2/boards/PIMORONI_PICOLIPO_4MB/pins.csv | 1 - 11 files changed, 50 insertions(+), 54 deletions(-) rename ports/rp2/boards/{PIMORONI_PICOLIPO_16MB => PIMORONI_PICOLIPO}/board.json (81%) rename ports/rp2/boards/{PIMORONI_PICOLIPO_4MB => PIMORONI_PICOLIPO}/mpconfigboard.cmake (100%) rename ports/rp2/boards/{PIMORONI_PICOLIPO_4MB => PIMORONI_PICOLIPO}/mpconfigboard.h (71%) create mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigvariant.cmake create mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigvariant_FLASH_16M.cmake create mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO/pins.csv delete mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.cmake delete mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.h delete mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO_16MB/pins.csv delete mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO_4MB/board.json delete mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO_4MB/pins.csv diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/board.json b/ports/rp2/boards/PIMORONI_PICOLIPO/board.json similarity index 81% rename from ports/rp2/boards/PIMORONI_PICOLIPO_16MB/board.json rename to ports/rp2/boards/PIMORONI_PICOLIPO/board.json index c44ea555142c2..5354df1118d6d 100644 --- a/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/board.json +++ b/ports/rp2/boards/PIMORONI_PICOLIPO/board.json @@ -14,8 +14,11 @@ "PimoroniPicoLipo_1of3_1024x1024.jpg" ], "mcu": "rp2040", - "product": "Pico LiPo (16MiB)", + "product": "Pico LiPo", "thumbnail": "", "url": "https://shop.pimoroni.com/products/pimoroni-pico-lipo", + "variants": { + "FLASH_16M": "16 MiB Flash" + }, "vendor": "Pimoroni" } diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.cmake b/ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigboard.cmake similarity index 100% rename from ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.cmake rename to ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigboard.cmake diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.h b/ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigboard.h similarity index 71% rename from ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.h rename to ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigboard.h index 53ade7291532a..857d21b26ccb6 100644 --- a/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.h +++ b/ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigboard.h @@ -1,7 +1,9 @@ // https://shop.pimoroni.com/products/pimoroni-pico-lipo?variant=39386149093459 +#ifndef MICROPY_HW_BOARD_NAME #define MICROPY_HW_BOARD_NAME "Pimoroni Pico LiPo 4MB" -#define MICROPY_HW_FLASH_STORAGE_BYTES (3 * 1024 * 1024) +#endif +#define MICROPY_HW_FLASH_STORAGE_BYTES (PICO_FLASH_SIZE_BYTES - (1 * 1024 * 1024)) #define MICROPY_HW_USB_VID (0x2E8A) #define MICROPY_HW_USB_PID (0x1002) @@ -10,11 +12,3 @@ #define MICROPY_HW_UART1_RX (9) #define MICROPY_HW_UART1_CTS (10) #define MICROPY_HW_UART1_RTS (11) - -// User LED GPIO25 - -// VBUS_SENSE GPIO24 - -// BAT_SENSE GPIO29 - -// Boot button GPIO23 diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigvariant.cmake b/ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigvariant.cmake new file mode 100644 index 0000000000000..84c75ad020b47 --- /dev/null +++ b/ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigvariant.cmake @@ -0,0 +1,6 @@ +set(PICO_BOARD "pimoroni_picolipo_4mb") + +# Override the MicroPython board name +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_BOARD_NAME="Pimoroni Pico LiPo 4MB" +) \ No newline at end of file diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigvariant_FLASH_16M.cmake b/ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigvariant_FLASH_16M.cmake new file mode 100644 index 0000000000000..0cf7cbd8f9b2f --- /dev/null +++ b/ports/rp2/boards/PIMORONI_PICOLIPO/mpconfigvariant_FLASH_16M.cmake @@ -0,0 +1,6 @@ +set(PICO_BOARD "pimoroni_picolipo_16mb") + +# Override the MicroPython board name +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_BOARD_NAME="Pimoroni Pico LiPo 16MB" +) \ No newline at end of file diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO/pins.csv b/ports/rp2/boards/PIMORONI_PICOLIPO/pins.csv new file mode 100644 index 0000000000000..a309163234d82 --- /dev/null +++ b/ports/rp2/boards/PIMORONI_PICOLIPO/pins.csv @@ -0,0 +1,31 @@ +GP0,GPIO0 +GP1,GPIO1 +GP2,GPIO2 +GP3,GPIO3 +GP4,GPIO4 +GP5,GPIO5 +GP6,GPIO6 +GP7,GPIO7 +GP8,GPIO8 +GP9,GPIO9 +GP10,GPIO10 +GP11,GPIO11 +GP12,GPIO12 +GP13,GPIO13 +GP14,GPIO14 +GP15,GPIO15 +GP16,GPIO16 +GP17,GPIO17 +GP18,GPIO18 +GP19,GPIO19 +GP20,GPIO20 +GP21,GPIO21 +GP22,GPIO22 +GP25,GPIO25 +GP26,GPIO26 +GP27,GPIO27 +GP28,GPIO28 +BOOT,GPIO23 +VBUS_SENSE,GPIO24 +LED,GPIO25 +BAT_SENSE,GPIO29 \ No newline at end of file diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.cmake b/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.cmake deleted file mode 100644 index b98ff4495658f..0000000000000 --- a/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.cmake +++ /dev/null @@ -1 +0,0 @@ -# cmake file for Pimoroni Pico LiPo 16MB diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.h b/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.h deleted file mode 100644 index a90ef6783d669..0000000000000 --- a/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.h +++ /dev/null @@ -1,20 +0,0 @@ -// https://shop.pimoroni.com/products/pimoroni-pico-lipo?variant=39335427080275 - -#define MICROPY_HW_BOARD_NAME "Pimoroni Pico LiPo 16MB" -#define MICROPY_HW_FLASH_STORAGE_BYTES (15 * 1024 * 1024) - -#define MICROPY_HW_USB_VID (0x2E8A) -#define MICROPY_HW_USB_PID (0x1003) - -#define MICROPY_HW_UART1_TX (8) -#define MICROPY_HW_UART1_RX (9) -#define MICROPY_HW_UART1_CTS (10) -#define MICROPY_HW_UART1_RTS (11) - -// User LED GPIO25 - -// VBUS_SENSE GPIO24 - -// BAT_SENSE GPIO29 - -// Boot button GPIO23 diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/pins.csv b/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/pins.csv deleted file mode 100644 index e75354485d94a..0000000000000 --- a/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/pins.csv +++ /dev/null @@ -1 +0,0 @@ -LED,GPIO25 diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/board.json b/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/board.json deleted file mode 100644 index cb06c746b62b0..0000000000000 --- a/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/board.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "deploy": [ - "../deploy.md" - ], - "docs": "", - "features": [ - "Battery Charging", - "Dual-core", - "External Flash", - "JST-SH", - "USB-C" - ], - "images": [ - "PimoroniPicoLipo_1of3_1024x1024.jpg" - ], - "mcu": "rp2040", - "product": "Pico LiPo (4MiB)", - "thumbnail": "", - "url": "https://shop.pimoroni.com/products/pimoroni-pico-lipo", - "vendor": "Pimoroni" -} diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/pins.csv b/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/pins.csv deleted file mode 100644 index e75354485d94a..0000000000000 --- a/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/pins.csv +++ /dev/null @@ -1 +0,0 @@ -LED,GPIO25 From 1557014ea4e88a1dfbef028afa393e6d2c3e6a9e Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 3 Jul 2024 15:45:52 +0100 Subject: [PATCH 0162/1300] rp2/boards/PIMORONI_TINY2040: Add an 8MB variant to Tiny 2040. Add an 8MB "PIMORONI_TINY2040" variant. Signed-off-by: Phil Howard --- ports/rp2/boards/PIMORONI_TINY2040/board.json | 3 +++ .../rp2/boards/PIMORONI_TINY2040/mpconfigboard.h | 13 ++++--------- .../PIMORONI_TINY2040/mpconfigvariant.cmake | 6 ++++++ .../mpconfigvariant_FLASH_8M.cmake | 6 ++++++ ports/rp2/boards/PIMORONI_TINY2040/pins.csv | 16 ++++++++++++++++ 5 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 ports/rp2/boards/PIMORONI_TINY2040/mpconfigvariant.cmake create mode 100644 ports/rp2/boards/PIMORONI_TINY2040/mpconfigvariant_FLASH_8M.cmake diff --git a/ports/rp2/boards/PIMORONI_TINY2040/board.json b/ports/rp2/boards/PIMORONI_TINY2040/board.json index 207647f6b28f6..19aad2d11ea76 100644 --- a/ports/rp2/boards/PIMORONI_TINY2040/board.json +++ b/ports/rp2/boards/PIMORONI_TINY2040/board.json @@ -16,5 +16,8 @@ "product": "Tiny2040", "thumbnail": "", "url": "https://shop.pimoroni.com/products/tiny-2040", + "variants": { + "FLASH_8M": "8 MiB Flash" + }, "vendor": "Pimoroni" } diff --git a/ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.h b/ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.h index ea40a8071b77b..a38e09aca8de5 100644 --- a/ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.h +++ b/ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.h @@ -1,7 +1,9 @@ // https://shop.pimoroni.com/products/tiny-2040 -#define MICROPY_HW_BOARD_NAME "Pimoroni Tiny 2040" -#define MICROPY_HW_FLASH_STORAGE_BYTES (7 * 1024 * 1024) +#ifndef MICROPY_HW_BOARD_NAME +#define MICROPY_HW_BOARD_NAME "Pimoroni Tiny 2040 8MB" +#endif +#define MICROPY_HW_FLASH_STORAGE_BYTES (PICO_FLASH_SIZE_BYTES - (1 * 1024 * 1024)) #define MICROPY_HW_USB_VID (0x16D0) #define MICROPY_HW_USB_PID (0x08C7) @@ -9,10 +11,3 @@ // I2C0 (non-default) #define MICROPY_HW_I2C0_SCL (4) #define MICROPY_HW_I2C0_SDA (5) - -// RGB LED, active low -// Red LED 18 -// Green LED 19 -// Blue LED 20 - -// Boot button GPIO23 diff --git a/ports/rp2/boards/PIMORONI_TINY2040/mpconfigvariant.cmake b/ports/rp2/boards/PIMORONI_TINY2040/mpconfigvariant.cmake new file mode 100644 index 0000000000000..f7e122613b7cf --- /dev/null +++ b/ports/rp2/boards/PIMORONI_TINY2040/mpconfigvariant.cmake @@ -0,0 +1,6 @@ +set(PICO_BOARD "pimoroni_tiny2040_2mb") + +# Override the MicroPython board name +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_BOARD_NAME="Pimoroni Tiny 2040 2MB" +) \ No newline at end of file diff --git a/ports/rp2/boards/PIMORONI_TINY2040/mpconfigvariant_FLASH_8M.cmake b/ports/rp2/boards/PIMORONI_TINY2040/mpconfigvariant_FLASH_8M.cmake new file mode 100644 index 0000000000000..dd35f8f0acb09 --- /dev/null +++ b/ports/rp2/boards/PIMORONI_TINY2040/mpconfigvariant_FLASH_8M.cmake @@ -0,0 +1,6 @@ +set(PICO_BOARD "pimoroni_tiny2040") + +# Override the MicroPython board name +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_BOARD_NAME="Pimoroni Tiny 2040 8MB" +) \ No newline at end of file diff --git a/ports/rp2/boards/PIMORONI_TINY2040/pins.csv b/ports/rp2/boards/PIMORONI_TINY2040/pins.csv index e4add1b0be897..4f38743071c69 100644 --- a/ports/rp2/boards/PIMORONI_TINY2040/pins.csv +++ b/ports/rp2/boards/PIMORONI_TINY2040/pins.csv @@ -1 +1,17 @@ +GP0,GPIO0 +GP1,GPIO1 +GP2,GPIO2 +GP3,GPIO3 +GP4,GPIO4 +GP5,GPIO5 +GP6,GPIO6 +GP7,GPIO7 +LED_RED,GPIO18 +LED_GREEN,GPIO19 +LED_BLUE,GPIO20 +BOOT,GPIO23 +GP26,GPIO26 +GP27,GPIO27 +GP28,GPIO28 +GP29,GPIO29 LED,GPIO19 From e7ff0b8a31a7f36cc1b3dfb7e5fd7b8805ff85ce Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 4 Jul 2024 10:19:26 +0100 Subject: [PATCH 0163/1300] rp2/memmap_mp.ld: Lower the minimum GC heap to 32K. Reduce mimimum heap requirement. This value allows more room for large, static buffers in user C modules (such as graphics buffers or otherwise) which might be allocated outside of MicroPython's heap to guarantee alignment or avoid fragmentation. Signed-off-by: Phil Howard --- ports/rp2/memmap_mp.ld | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ports/rp2/memmap_mp.ld b/ports/rp2/memmap_mp.ld index 0ed9509b8281d..a5799cd88be0e 100644 --- a/ports/rp2/memmap_mp.ld +++ b/ports/rp2/memmap_mp.ld @@ -256,9 +256,13 @@ SECTIONS __StackBottom = __StackTop - SIZEOF(.stack_dummy); PROVIDE(__stack = __StackTop); - /* Check GC heap is at least 128 KB */ - /* On a RP2040 using all SRAM this should always be the case. */ - ASSERT((__GcHeapEnd - __GcHeapStart) > 128*1024, "GcHeap is too small") + /* Check GC heap is at least 32 KB */ + /* This is quarter the minimum RAM suggested for full-featured MicroPython. + * This value accounts for large static buffers included in user C or C++ + * modules, which might significantly reduce the available heap but also + * lower demand for memory at runtime. + */ + ASSERT((__GcHeapEnd - __GcHeapStart) > 32*1024, "GcHeap is too small") ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") /* todo assert on extra code */ From ad3552ae2cb5be65a8d61dc1c3bb2014d7ef4cae Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 8 Jul 2024 15:34:18 +0100 Subject: [PATCH 0164/1300] rp2/rp2_pio: Make PIO IRQ handlers have lazy initialisation. This change has no impact on vanilla MicroPython builds, but is intended to avoid RP2's PIO implementation from trampling PIO usage in USER_C_MODULES. This is consistent with PIOs tracking of used state machines and managed programs, and makes working with PIO in USER_C_MODULES much less of an uphill battle. Since PIO deinit runs before gc_sweep_all it's impossible to work around this wrinkle otherwise. A module finalizer does not get the opportunity to put the PIOs back into a state which wont crash rp2_pio_deinit. Changes are: - init: Avoid exclusive handlers being added to all PIOs and add them only when needed. - deinit: Only remove handlers we have set. - rp2_pio_irq: Add the exlusive handler if needed. - rp2_state_machine_irq: Add the exclusive handler if needed. Signed-off-by: Phil Howard --- ports/rp2/rp2_pio.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/ports/rp2/rp2_pio.c b/ports/rp2/rp2_pio.c index da62e6bec9e8d..0ca91656f84ef 100644 --- a/ports/rp2/rp2_pio.c +++ b/ports/rp2/rp2_pio.c @@ -104,6 +104,23 @@ static void pio1_irq0(void) { pio_irq0(pio1); } +// Returns the correct irq0 handler wrapper for a given pio +static inline irq_handler_t rp2_pio_get_irq_handler(PIO pio) { + return pio == pio0 ? pio0_irq0 : pio1_irq0; +} + +// Add the irq0 handler if it's not added, and if no other handler is present +void rp2_pio_irq_set_exclusive_handler(PIO pio, uint irq) { + irq_handler_t current = irq_get_exclusive_handler(irq); + // If the IRQ is set and isn't our handler, or a shared handler is set, then raise an error + if ((current && current != rp2_pio_get_irq_handler(pio)) || irq_has_shared_handler(irq)) { + mp_raise_ValueError("irq claimed by external resource"); + // If the IRQ is not set, add our handler + } else if (!current) { + irq_set_exclusive_handler(irq, rp2_pio_get_irq_handler(pio)); + } +} + // Calls pio_add_program() and keeps track of used instruction memory. static uint rp2_pio_add_managed_program(PIO pio, struct pio_program *pio_program) { uint offset = pio_add_program(pio, pio_program); @@ -143,15 +160,18 @@ void rp2_pio_init(void) { // Set up interrupts. memset(MP_STATE_PORT(rp2_pio_irq_obj), 0, sizeof(MP_STATE_PORT(rp2_pio_irq_obj))); memset(MP_STATE_PORT(rp2_state_machine_irq_obj), 0, sizeof(MP_STATE_PORT(rp2_state_machine_irq_obj))); - irq_set_exclusive_handler(PIO0_IRQ_0, pio0_irq0); - irq_set_exclusive_handler(PIO1_IRQ_0, pio1_irq0); } void rp2_pio_deinit(void) { // Disable and clear interrupts. - irq_set_mask_enabled((1u << PIO0_IRQ_0) | (1u << PIO1_IRQ_0), false); - irq_remove_handler(PIO0_IRQ_0, pio0_irq0); - irq_remove_handler(PIO1_IRQ_0, pio1_irq0); + if (irq_get_exclusive_handler(PIO0_IRQ_0) == pio0_irq0) { + irq_set_enabled(PIO0_IRQ_0, false); + irq_remove_handler(PIO0_IRQ_0, pio0_irq0); + } + if (irq_get_exclusive_handler(PIO1_IRQ_0) == pio1_irq0) { + irq_set_enabled(PIO1_IRQ_0, false); + irq_remove_handler(PIO1_IRQ_0, pio1_irq0); + } rp2_state_machine_reset_all(); @@ -379,6 +399,7 @@ static mp_obj_t rp2_pio_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k // Enable IRQ if a handler is given. if (args[ARG_handler].u_obj != mp_const_none) { + rp2_pio_irq_set_exclusive_handler(self->pio, self->irq); self->pio->inte0 = irq->trigger; irq_set_enabled(self->irq, true); } @@ -872,6 +893,7 @@ static mp_obj_t rp2_state_machine_irq(size_t n_args, const mp_obj_t *pos_args, m } if (self->pio->inte0) { + rp2_pio_irq_set_exclusive_handler(self->pio, self->irq); irq_set_enabled(self->irq, true); } } From d2e33fe3096eec60d7017b0f17c4ddb6910a4d0b Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 2 Aug 2024 14:16:57 +0100 Subject: [PATCH 0165/1300] rp2/machine_i2s: Deinit all active I2S instances on soft reset. Add `machine_i2s_deinit_all` to teardown any active I2S instances on soft reset. Prior to this fix, code using I2S required a try/finally in order to avoid a hard fault on soft reset. Fixes issue #14339. Signed-off-by: Phil Howard --- ports/rp2/machine_i2s.c | 9 +++++++++ ports/rp2/main.c | 1 + ports/rp2/modmachine.h | 1 + 3 files changed, 11 insertions(+) diff --git a/ports/rp2/machine_i2s.c b/ports/rp2/machine_i2s.c index 47eb5350a6439..b17d38336ccf1 100644 --- a/ports/rp2/machine_i2s.c +++ b/ports/rp2/machine_i2s.c @@ -171,6 +171,15 @@ void machine_i2s_init0(void) { } } +void machine_i2s_deinit_all(void) { + for (uint8_t i = 0; i < MAX_I2S_RP2; i++) { + machine_i2s_obj_t *i2s = MP_STATE_PORT(machine_i2s_obj[i]); + if (i2s) { + mp_machine_i2s_deinit(i2s); + } + } +} + static int8_t get_frame_mapping_index(int8_t bits, format_t format) { if (format == MONO) { if (bits == 16) { diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 3b4d351a78792..0f2721f174961 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -214,6 +214,7 @@ int main(int argc, char **argv) { #if MICROPY_PY_NETWORK mod_network_deinit(); #endif + machine_i2s_deinit_all(); rp2_dma_deinit(); rp2_pio_deinit(); #if MICROPY_PY_BLUETOOTH diff --git a/ports/rp2/modmachine.h b/ports/rp2/modmachine.h index 3d0c726cd917e..e17ad67b0301d 100644 --- a/ports/rp2/modmachine.h +++ b/ports/rp2/modmachine.h @@ -6,6 +6,7 @@ void machine_pin_init(void); void machine_pin_deinit(void); void machine_i2s_init0(void); +void machine_i2s_deinit_all(void); void machine_pwm_deinit_all(void); struct _machine_spi_obj_t *spi_from_mp_obj(mp_obj_t o); From afba3e054041bbad961fad61df7c4797ab35d9e3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 31 Jul 2024 12:33:33 +1000 Subject: [PATCH 0166/1300] py/emitnative: Fix case of clobbered REG_TEMP0 when loading const obj. The `emit_load_reg_with_object()` helper function will clobber `REG_TEMP0`. This is currently OK on architectures where `REG_RET` and `REG_TEMP0` are the same (all architectures except RV32), because all callers of `emit_load_reg_with_object()` use either `REG_RET` or `REG_TEMP0` as the destination register. But on RV32 these registers are different and so when `REG_RET` is the destination, `REG_TEMP0` is clobbered, leading to incorrectly generated machine code. This commit fixes the issue simply by using `REG_TEMP0` as the destination register for all uses of `emit_load_reg_with_object()`, and adds a comment to make sure the caller of this function is careful. Signed-off-by: Damien George --- py/emitnative.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/py/emitnative.c b/py/emitnative.c index 88ebf0bfcf3de..66c345b233d76 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -357,6 +357,7 @@ static void emit_native_mov_reg_qstr(emit_t *emit, int arg_reg, qstr qst) { #endif } +// This function may clobber REG_TEMP0 (and `reg_dest` can be REG_TEMP0). static void emit_native_mov_reg_qstr_obj(emit_t *emit, int reg_dest, qstr qst) { #if MICROPY_PERSISTENT_CODE_SAVE emit_load_reg_with_object(emit, reg_dest, MP_OBJ_NEW_QSTR(qst)); @@ -1117,6 +1118,7 @@ static exc_stack_entry_t *emit_native_pop_exc_stack(emit_t *emit) { return e; } +// This function will clobber REG_TEMP0 (and `reg` can be REG_TEMP0). static void emit_load_reg_with_object(emit_t *emit, int reg, mp_obj_t obj) { emit->scope->scope_flags |= MP_SCOPE_FLAG_HASCONSTS; size_t table_off = mp_emit_common_use_const_obj(emit->emit_common, obj); @@ -1391,9 +1393,9 @@ static void emit_native_load_const_str(emit_t *emit, qstr qst) { static void emit_native_load_const_obj(emit_t *emit, mp_obj_t obj) { emit_native_pre(emit); - need_reg_single(emit, REG_RET, 0); - emit_load_reg_with_object(emit, REG_RET, obj); - emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); + need_reg_single(emit, REG_TEMP0, 0); + emit_load_reg_with_object(emit, REG_TEMP0, obj); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_TEMP0); } static void emit_native_load_null(emit_t *emit) { From b0c89377d0f0a4624577da8ca956f7c33d6b1882 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 6 Aug 2024 09:59:22 +1000 Subject: [PATCH 0167/1300] py/modmath: Add option to work around -inf bug in a port's tgamma. This is needed for a workaround on esp32 port (in child commit), which produces incorrect results otherwise. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- py/modmath.c | 10 ++++++++++ py/mpconfig.h | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/py/modmath.c b/py/modmath.c index db30f0e62559c..4d51a28d0bbb0 100644 --- a/py/modmath.c +++ b/py/modmath.c @@ -196,7 +196,17 @@ MATH_FUN_1(erf, erf) // erfc(x): return the complementary error function of x MATH_FUN_1(erfc, erfc) // gamma(x): return the gamma function of x +#if MICROPY_PY_MATH_GAMMA_FIX_NEGINF +static mp_float_t MICROPY_FLOAT_C_FUN(tgamma_func)(mp_float_t x) { + if (isinf(x) && x < 0) { + math_error(); + } + return MICROPY_FLOAT_C_FUN(tgamma)(x); +} +MATH_FUN_1(gamma, tgamma_func) +#else MATH_FUN_1(gamma, tgamma) +#endif // lgamma(x): return the natural logarithm of the gamma function of x MATH_FUN_1(lgamma, lgamma) #endif diff --git a/py/mpconfig.h b/py/mpconfig.h index 98893ceb6d97b..94fdca7d7a97d 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1393,6 +1393,11 @@ typedef double mp_float_t; #define MICROPY_PY_MATH_POW_FIX_NAN (0) #endif +// Whether to provide fix for gamma(-inf) to raise ValueError +#ifndef MICROPY_PY_MATH_GAMMA_FIX_NEGINF +#define MICROPY_PY_MATH_GAMMA_FIX_NEGINF (0) +#endif + // Whether to provide "cmath" module #ifndef MICROPY_PY_CMATH #define MICROPY_PY_CMATH (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) From 6fead318322bb21e30fdcbdccf343e02dc51990f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 25 Jul 2024 12:11:32 +1000 Subject: [PATCH 0168/1300] esp32: Enable workaround for math.gamma(-inf) result. Without this commit, math.gamma(-float("inf")) returns inf instead of raising a math domain ValueError. Needed for float/math_domain_special.py test to pass on esp32. Root cause is an upstream libm bug, has been reported to ESP-IDF. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/mpconfigport.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index b9e53a4fa8cd9..3349e56e46d41 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -282,6 +282,10 @@ typedef long mp_off_t; #define MICROPY_PY_MACHINE_BOOTLOADER (0) #endif +// Workaround for upstream bug https://github.com/espressif/esp-idf/issues/14273 +// Can be removed if a fix is available in supported ESP-IDF versions. +#define MICROPY_PY_MATH_GAMMA_FIX_NEGINF (1) + #ifndef MICROPY_BOARD_STARTUP #define MICROPY_BOARD_STARTUP boardctrl_startup #endif From 5ff6c12c65b5ab5efdbc10718caa2375ca5ab775 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 3 Aug 2024 18:04:43 +1000 Subject: [PATCH 0169/1300] esp32/main: Store native code as linked list instead of list on GC heap. Finalisers that run during `gc_sweep_all()` may run native code, for example if an open file is closed and the underlying block device is implemented in native code, then the filesystem driver (eg FAT) may call into the native code. Therefore, native code must be freed after the call to `gc_sweep_all()`. That can only be achieved if the GC heap is not used to store the list of allocated native code blocks. Instead, this commit makes the native code blocks a linked list. Signed-off-by: Damien George --- ports/esp32/main.c | 48 ++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 62dd7ae3cccf6..ccec6c8cd81f4 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -78,6 +78,15 @@ #define MP_TASK_STACK_LIMIT_MARGIN (1024) #endif +typedef struct _native_code_node_t { + struct _native_code_node_t *next; + uint32_t data[]; +} native_code_node_t; + +static native_code_node_t *native_code_head = NULL; + +static void esp_native_code_free_all(void); + int vprintf_null(const char *format, va_list ap) { // do nothing: this is used as a log target during raw repl mode return 0; @@ -130,8 +139,6 @@ void mp_task(void *pvParameter) { mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib)); readline_init0(); - MP_STATE_PORT(native_code_pointers) = MP_OBJ_NULL; - // initialise peripherals machine_pins_init(); #if MICROPY_PY_MACHINE_I2S @@ -182,18 +189,11 @@ void mp_task(void *pvParameter) { mp_thread_deinit(); #endif - // Free any native code pointers that point to iRAM. - if (MP_STATE_PORT(native_code_pointers) != MP_OBJ_NULL) { - size_t len; - mp_obj_t *items; - mp_obj_list_get(MP_STATE_PORT(native_code_pointers), &len, &items); - for (size_t i = 0; i < len; ++i) { - heap_caps_free(MP_OBJ_TO_PTR(items[i])); - } - } - gc_sweep_all(); + // Free any native code pointers that point to iRAM. + esp_native_code_free_all(); + mp_hal_stdout_tx_str("MPY: soft reboot\r\n"); // deinitialise peripherals @@ -232,21 +232,27 @@ void nlr_jump_fail(void *val) { esp_restart(); } +static void esp_native_code_free_all(void) { + while (native_code_head != NULL) { + native_code_node_t *next = native_code_head->next; + heap_caps_free(native_code_head); + native_code_head = next; + } +} + void *esp_native_code_commit(void *buf, size_t len, void *reloc) { len = (len + 3) & ~3; - uint32_t *p = heap_caps_malloc(len, MALLOC_CAP_EXEC); - if (p == NULL) { - m_malloc_fail(len); - } - if (MP_STATE_PORT(native_code_pointers) == MP_OBJ_NULL) { - MP_STATE_PORT(native_code_pointers) = mp_obj_new_list(0, NULL); + size_t len_node = sizeof(native_code_node_t) + len; + native_code_node_t *node = heap_caps_malloc(len_node, MALLOC_CAP_EXEC); + if (node == NULL) { + m_malloc_fail(len_node); } - mp_obj_list_append(MP_STATE_PORT(native_code_pointers), MP_OBJ_TO_PTR(p)); + node->next = native_code_head; + native_code_head = node; + void *p = node->data; if (reloc) { mp_native_relocate(reloc, buf, (uintptr_t)p); } memcpy(p, buf, len); return p; } - -MP_REGISTER_ROOT_POINTER(mp_obj_t native_code_pointers); From a4b3825bd4d3eee3294c9a73c9b8a2ada0909ced Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Aug 2024 16:41:17 +1000 Subject: [PATCH 0170/1300] tests/extmod: Rename machine_timer exp file to machine_soft_timer. This was missed in 9ba04cc7563ec934ca14d66aa18ae3563c8d1aea Signed-off-by: Damien George --- tests/extmod/{machine_timer.py.exp => machine_soft_timer.py.exp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/extmod/{machine_timer.py.exp => machine_soft_timer.py.exp} (100%) diff --git a/tests/extmod/machine_timer.py.exp b/tests/extmod/machine_soft_timer.py.exp similarity index 100% rename from tests/extmod/machine_timer.py.exp rename to tests/extmod/machine_soft_timer.py.exp From 1e98c4cb75bf3015d816455fc46ba28d5bcd9275 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 25 Jul 2024 12:39:34 +1000 Subject: [PATCH 0171/1300] tests/extmod: Add machine_spi_rate test. Based on machine_i2s_rate, allows testing basic SPI functionality and timings. Implemented and confirmed working for rp2, esp32, and pyboard. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/extmod/machine_spi_rate.py | 152 +++++++++++++++++++++++++++ tests/extmod/machine_spi_rate.py.exp | 39 +++++++ 2 files changed, 191 insertions(+) create mode 100644 tests/extmod/machine_spi_rate.py create mode 100644 tests/extmod/machine_spi_rate.py.exp diff --git a/tests/extmod/machine_spi_rate.py b/tests/extmod/machine_spi_rate.py new file mode 100644 index 0000000000000..df9e3fbd0de85 --- /dev/null +++ b/tests/extmod/machine_spi_rate.py @@ -0,0 +1,152 @@ +# Test machine.SPI data transfer rates +import sys + + +try: + import time + from machine import Pin, SPI +except ImportError: + print("SKIP") + raise SystemExit + +MAX_DELTA_MS = 8 + +# Configure pins based on the port/board details. +# Values are tuples of (spi_id, sck, mosi, miso) +if "pyboard" in sys.platform: + spi_instances = ( + (1, None, None, None), # "explicit choice of sck/mosi/miso is not implemented" + (2, None, None, None), + ) +elif "rp2" in sys.platform: + spi_instances = ((0, Pin(18), Pin(19), Pin(16)),) +elif "esp32" in sys.platform: + spi_instances = [ + (1, Pin(18), Pin(19), Pin(21)), + ] + if "ESP32C3" not in str(sys.implementation): + spi_instances.append((2, Pin(18), Pin(19), Pin(21))) +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +def get_real_baudrate(spi): + # Return the 'real' baudrate for a SPI object, from parsing 'print' output + # i.e. + # SPI(id=1, baudrate=500000, polarity=0, phase=0, bits=8, firstbit=0, sck=14, mosi=13, miso=12) + # + # Note the 'real' rate may be quite different to the requested rate, i.e. + # on ports where the SPI hardware only supports power of 2 clock dividers. + # + # Implementation is somewhat fiddly and inefficient to avoid dependency on + # 're' module, + s = str(spi) + s = s.split("baudrate=")[1].split(",")[0] + return int(s) + + +def test_instances(): + print_results = True + for spi_id, sck, mosi, miso in spi_instances: + for baudrate in ( + 100_000, + 250_000, + 800_000, + 1_000_000, + 2_000_000, + 4_000_000, + 8_000_000, + ): + test_spi( + spi_id, + sck, + mosi, + miso, + baudrate, + 0, + 0, + print_results, + ) + + for baudrate in (100_000, 4_000_000): + # Test the other polarity and phase settings + for polarity, phase in ((0, 1), (1, 0), (1, 1)): + test_spi( + spi_id, + sck, + mosi, + miso, + baudrate, + polarity, + phase, + print_results, + ) + + # Ensure the same test output regardless of how many SPI instances are tested + print_results = False + + +wr_short = b"abcdefghijklmnop" * 10 +rd_short = bytearray(len(wr_short)) + +wr_long = wr_short * 20 +rd_long = bytearray(len(wr_long)) + + +def test_spi(spi_id, sck, mosi, miso, baudrate, polarity, phase, print_results): + if sck: + s = SPI( + spi_id, + sck=sck, + mosi=mosi, + miso=miso, + baudrate=baudrate, + polarity=polarity, + phase=phase, + ) + else: + s = SPI(spi_id, baudrate=baudrate, polarity=polarity, phase=phase) + + # to keep the test runtime down, use shorter buffer for lower baud rate + wr_buf = wr_long if baudrate > 500_000 else wr_short + rd_buf = rd_long if baudrate > 500_000 else rd_short + + real_baudrate = get_real_baudrate(s) + assert real_baudrate + transfer_time_ms = len(wr_buf) * 8 * 1000 // real_baudrate + + def test_write_readinto(): + s.write_readinto(wr_buf, rd_buf) + + def test_write(): + s.write(wr_buf) + + def test_readinto(): + s.readinto(rd_buf) + + for test_func, name in ( + (test_write_readinto, "write_readinto"), + (test_write, "write"), + (test_readinto, "readinto"), + ): + t0 = time.ticks_ms() + test_func() + transfer_time = time.ticks_diff(time.ticks_ms(), t0) + t_delta = abs(transfer_time - transfer_time_ms) + t_ok = t_delta < MAX_DELTA_MS + + if print_results or not t_ok: + print( + None if print_results else spi_id, + baudrate, + polarity, + phase, + name, + t_ok or t_delta, + ) + + s.deinit() + + +test_instances() diff --git a/tests/extmod/machine_spi_rate.py.exp b/tests/extmod/machine_spi_rate.py.exp new file mode 100644 index 0000000000000..6102016154d6a --- /dev/null +++ b/tests/extmod/machine_spi_rate.py.exp @@ -0,0 +1,39 @@ +None 100000 0 0 write_readinto True +None 100000 0 0 write True +None 100000 0 0 readinto True +None 250000 0 0 write_readinto True +None 250000 0 0 write True +None 250000 0 0 readinto True +None 800000 0 0 write_readinto True +None 800000 0 0 write True +None 800000 0 0 readinto True +None 1000000 0 0 write_readinto True +None 1000000 0 0 write True +None 1000000 0 0 readinto True +None 2000000 0 0 write_readinto True +None 2000000 0 0 write True +None 2000000 0 0 readinto True +None 4000000 0 0 write_readinto True +None 4000000 0 0 write True +None 4000000 0 0 readinto True +None 8000000 0 0 write_readinto True +None 8000000 0 0 write True +None 8000000 0 0 readinto True +None 100000 0 1 write_readinto True +None 100000 0 1 write True +None 100000 0 1 readinto True +None 100000 1 0 write_readinto True +None 100000 1 0 write True +None 100000 1 0 readinto True +None 100000 1 1 write_readinto True +None 100000 1 1 write True +None 100000 1 1 readinto True +None 4000000 0 1 write_readinto True +None 4000000 0 1 write True +None 4000000 0 1 readinto True +None 4000000 1 0 write_readinto True +None 4000000 1 0 write True +None 4000000 1 0 readinto True +None 4000000 1 1 write_readinto True +None 4000000 1 1 write True +None 4000000 1 1 readinto True From 1fe3b47c81de7b815a330b40e973fd3eb5894812 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 5 Aug 2024 12:43:10 +1000 Subject: [PATCH 0172/1300] qemu-arm: Fix tinytest test profile when updating set of dirs/files. Updating a set must use `.update()` rather than `.add()`. Also apply the same pattern to qemu-riscv to prevent the same issue when directories/files are added to that port's `tests_profile.txt` file. Signed-off-by: Damien George --- ports/qemu-arm/tests_profile.txt | 4 ++-- ports/qemu-riscv/tests_profile.txt | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ports/qemu-arm/tests_profile.txt b/ports/qemu-arm/tests_profile.txt index c55185a0eb3c5..101943b7c6ab0 100644 --- a/ports/qemu-arm/tests_profile.txt +++ b/ports/qemu-arm/tests_profile.txt @@ -1,10 +1,10 @@ # Port-specific test directories. -test_dirs.add(("inlineasm", "qemu-arm")) +test_dirs.update(("inlineasm", "ports/qemu-arm")) # Port-specific tests exclusion list. -exclude_tests.add( +exclude_tests.update( ( # inline asm FP tests (require Cortex-M4) "inlineasm/asmfpaddsub.py", diff --git a/ports/qemu-riscv/tests_profile.txt b/ports/qemu-riscv/tests_profile.txt index 1079ca18124dc..3d16e971be01e 100644 --- a/ports/qemu-riscv/tests_profile.txt +++ b/ports/qemu-riscv/tests_profile.txt @@ -1,3 +1,7 @@ +# Port-specific test directories. + +test_dirs.update(()) + # Port-specific tests exclusion list. -exclude_tests = exclude_tests.union(()) +exclude_tests.update(()) From aa0b8f340d0e660cf3c1688fa1e755fdbc3ef574 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Aug 2024 15:46:05 +1000 Subject: [PATCH 0173/1300] mpy-cross/main: Use MICROPY_BANNER_NAME_AND_VERSION for --version. Gives the same output and keeps things consistent across the code base. Signed-off-by: Damien George --- mpy-cross/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 72b6e10fce9d2..7ab95149c720c 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -248,7 +248,7 @@ MP_NOINLINE int main_(int argc, char **argv) { if (strcmp(argv[a], "-X") == 0) { a += 1; } else if (strcmp(argv[a], "--version") == 0) { - printf("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE + printf(MICROPY_BANNER_NAME_AND_VERSION "; mpy-cross emitting mpy v" MP_STRINGIFY(MPY_VERSION) "." MP_STRINGIFY(MPY_SUB_VERSION) "\n"); return 0; } else if (strcmp(argv[a], "-v") == 0) { From 55b2720687b6bcd71b8a5c9a5d87bea53c57743a Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 25 Jul 2024 18:42:30 +0200 Subject: [PATCH 0174/1300] shared/runtime/gchelper: Add RISC-V RV64I native gchelper. Add native gchelper support for 64 bits RISC-V RV64I targets. Now that RV64 is under CI, this also enables platform-specific ghelper in the Unix port. Also changes the data type holding the register contents to something more appropriate, so in the remote eventuality somebody wants to use this with RV128 all they have to do is update the `__riscv_xlen` check. Signed-off-by: Alessandro Gatti --- ports/unix/mpconfigport.h | 2 +- shared/runtime/gchelper.h | 2 +- shared/runtime/gchelper_generic.c | 29 ++++++++--------- shared/runtime/gchelper_rv64i.s | 52 +++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 shared/runtime/gchelper_rv64i.s diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 1086baea06c60..9c9d9228e760e 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -105,7 +105,7 @@ typedef long mp_off_t; // Always enable GC. #define MICROPY_ENABLE_GC (1) -#if !(defined(MICROPY_GCREGS_SETJMP) || defined(__x86_64__) || defined(__i386__) || defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) +#if !(defined(MICROPY_GCREGS_SETJMP) || defined(__x86_64__) || defined(__i386__) || defined(__thumb2__) || defined(__thumb__) || defined(__arm__) || (defined(__riscv) && (__riscv_xlen == 64))) // Fall back to setjmp() implementation for discovery of GC pointers in registers. #define MICROPY_GCREGS_SETJMP (1) #endif diff --git a/shared/runtime/gchelper.h b/shared/runtime/gchelper.h index a863fb9aa880a..1e85e06f46ef9 100644 --- a/shared/runtime/gchelper.h +++ b/shared/runtime/gchelper.h @@ -41,7 +41,7 @@ typedef uintptr_t gc_helper_regs_t[4]; typedef uintptr_t gc_helper_regs_t[10]; #elif defined(__aarch64__) typedef uintptr_t gc_helper_regs_t[11]; // x19-x29 -#elif defined(__riscv) && defined(__riscv_xlen) && (__riscv_xlen == 32) +#elif defined(__riscv) && (__riscv_xlen <= 64) typedef uintptr_t gc_helper_regs_t[12]; // S0-S11 #endif diff --git a/shared/runtime/gchelper_generic.c b/shared/runtime/gchelper_generic.c index f1087e1990f60..0937231374057 100644 --- a/shared/runtime/gchelper_generic.c +++ b/shared/runtime/gchelper_generic.c @@ -150,23 +150,24 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { arr[10] = x29; } -#elif defined(__riscv) && defined(__riscv_xlen) && (__riscv_xlen == 32) +#elif defined(__riscv) && (__riscv_xlen <= 64) -// Fallback implementation for RV32I, prefer gchelper_rv32i.s +// Fallback implementation for RV32I and RV64I, prefer gchelper_rv32i.s +// for RV32I targets or gchelper_rv64i.s for RV64I targets. static void gc_helper_get_regs(gc_helper_regs_t arr) { - register long s0 asm ("x8"); - register long s1 asm ("x9"); - register long s2 asm ("x18"); - register long s3 asm ("x19"); - register long s4 asm ("x20"); - register long s5 asm ("x21"); - register long s6 asm ("x22"); - register long s7 asm ("x23"); - register long s8 asm ("x24"); - register long s9 asm ("x25"); - register long s10 asm ("x26"); - register long s11 asm ("x27"); + register uintptr_t s0 asm ("x8"); + register uintptr_t s1 asm ("x9"); + register uintptr_t s2 asm ("x18"); + register uintptr_t s3 asm ("x19"); + register uintptr_t s4 asm ("x20"); + register uintptr_t s5 asm ("x21"); + register uintptr_t s6 asm ("x22"); + register uintptr_t s7 asm ("x23"); + register uintptr_t s8 asm ("x24"); + register uintptr_t s9 asm ("x25"); + register uintptr_t s10 asm ("x26"); + register uintptr_t s11 asm ("x27"); arr[0] = s0; arr[1] = s1; arr[2] = s2; diff --git a/shared/runtime/gchelper_rv64i.s b/shared/runtime/gchelper_rv64i.s new file mode 100644 index 0000000000000..7f3dcb8a39431 --- /dev/null +++ b/shared/runtime/gchelper_rv64i.s @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Alessandro Gatti + * + * 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. + */ + + .global gc_helper_get_regs_and_sp + .type gc_helper_get_regs_and_sp, @function + +gc_helper_get_regs_and_sp: + + /* Store registers into the given array. */ + + sw x8, 0(x10) /* Save S0. */ + sw x9, 8(x10) /* Save S1. */ + sw x18, 16(x10) /* Save S2. */ + sw x19, 24(x10) /* Save S3. */ + sw x20, 32(x10) /* Save S4. */ + sw x21, 40(x10) /* Save S5. */ + sw x22, 48(x10) /* Save S6. */ + sw x23, 56(x10) /* Save S7. */ + sw x24, 64(x10) /* Save S8. */ + sw x25, 72(x10) /* Save S9. */ + sw x26, 80(x10) /* Save S10. */ + sw x27, 88(x10) /* Save S11. */ + + /* Return the stack pointer. */ + + add x10, x0, x2 + jalr x0, x1, 0 + + .size gc_helper_get_regs_and_sp, .-gc_helper_get_regs_and_sp From 1216f2c313fbf68cb7f18c4914ddf843c31d9191 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 10 Jul 2024 16:19:46 +0300 Subject: [PATCH 0175/1300] extmod/libmetal: Remove source file listed twice in sources. This causes multiple definition of symbols on some builds. Signed-off-by: iabdalkader --- extmod/libmetal/libmetal.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/extmod/libmetal/libmetal.mk b/extmod/libmetal/libmetal.mk index 852b04289833b..7cda12a31333d 100644 --- a/extmod/libmetal/libmetal.mk +++ b/extmod/libmetal/libmetal.mk @@ -33,7 +33,6 @@ SRC_LIBMETAL_C := $(addprefix $(BUILD)/openamp/metal/,\ shmem.c \ softirq.c \ version.c \ - device.c \ system/micropython/condition.c \ system/micropython/device.c \ system/micropython/io.c \ From 4350cbcb48599d29d77c8b557aa96b65ae7d6aeb Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 10 Jul 2024 22:49:29 +0300 Subject: [PATCH 0176/1300] extmod/modopenamp_remoteproc: Fix entry point address int overflow. Signed-off-by: iabdalkader --- extmod/modopenamp_remoteproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/modopenamp_remoteproc.c b/extmod/modopenamp_remoteproc.c index 20e4a47c18718..82719dca96145 100644 --- a/extmod/modopenamp_remoteproc.c +++ b/extmod/modopenamp_remoteproc.c @@ -137,7 +137,7 @@ mp_obj_t openamp_remoteproc_make_new(const mp_obj_type_t *type, size_t n_args, s } if (mp_obj_is_int(args[ARG_entry].u_obj)) { - self->rproc.bootaddr = mp_obj_get_int(args[ARG_entry].u_obj); + self->rproc.bootaddr = mp_obj_int_get_truncated(args[ARG_entry].u_obj); } else { #if MICROPY_PY_OPENAMP_REMOTEPROC_ELFLD_ENABLE // Load firmware. From 7f49897adad10133cf1d55db9caed66326cd4416 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 11 Jul 2024 15:54:12 +0300 Subject: [PATCH 0177/1300] extmod/modopenamp: Add support for building Open-AMP on device side. Tested with two VMs each running on a different core. Signed-off-by: iabdalkader --- extmod/extmod.mk | 12 +++++++++- extmod/modopenamp.c | 33 ++++++++++++++++++---------- extmod/modopenamp_remoteproc.c | 4 ++-- extmod/modopenamp_remoteproc_store.c | 4 ++-- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/extmod/extmod.mk b/extmod/extmod.mk index 2207c21f0d8e9..c2298bc52130f 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -527,6 +527,7 @@ ifeq ($(MICROPY_PY_OPENAMP),1) OPENAMP_DIR = lib/open-amp LIBMETAL_DIR = lib/libmetal GIT_SUBMODULES += $(LIBMETAL_DIR) $(OPENAMP_DIR) +MICROPY_PY_OPENAMP_MODE ?= 0 include $(TOP)/extmod/libmetal/libmetal.mk INC += -I$(TOP)/$(OPENAMP_DIR) @@ -536,12 +537,21 @@ ifeq ($(MICROPY_PY_OPENAMP_REMOTEPROC),1) CFLAGS += -DMICROPY_PY_OPENAMP_REMOTEPROC=1 endif +ifeq ($(MICROPY_PY_OPENAMP_MODE),0) +CFLAGS += -DMICROPY_PY_OPENAMP_HOST=1 +CFLAGS_THIRDPARTY += -DVIRTIO_DRIVER_ONLY +else ifeq ($(MICROPY_PY_OPENAMP_MODE),1) +CFLAGS += -DMICROPY_PY_OPENAMP_DEVICE=1 +CFLAGS_THIRDPARTY += -DVIRTIO_DEVICE_ONLY +else +$(error Invalid Open-AMP mode specified: $(MICROPY_PY_OPENAMP_MODE)) +endif + CFLAGS_THIRDPARTY += \ -I$(BUILD)/openamp \ -I$(TOP)/$(OPENAMP_DIR) \ -I$(TOP)/$(OPENAMP_DIR)/lib/include/ \ -DMETAL_INTERNAL \ - -DVIRTIO_DRIVER_ONLY \ -DNO_ATOMIC_64_SUPPORT \ -DRPMSG_BUFFER_SIZE=512 \ diff --git a/extmod/modopenamp.c b/extmod/modopenamp.c index 29135d57a80b3..a10441d64a05a 100644 --- a/extmod/modopenamp.c +++ b/extmod/modopenamp.c @@ -52,11 +52,20 @@ #if MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE #define VIRTIO_DEV_ID 0xFF +#if MICROPY_PY_OPENAMP_HOST +#define VIRTIO_DEV_ROLE RPMSG_HOST +#else +#define VIRTIO_DEV_ROLE RPMSG_REMOTE +#endif // MICROPY_PY_OPENAMP_HOST #define VIRTIO_DEV_FEATURES (1 << VIRTIO_RPMSG_F_NS) #define VRING0_ID 0 // VRING0 ID (host to remote) fixed to 0 for linux compatibility #define VRING1_ID 1 // VRING1 ID (remote to host) fixed to 1 for linux compatibility +#if MICROPY_PY_OPENAMP_HOST #define VRING_NOTIFY_ID VRING0_ID +#else +#define VRING_NOTIFY_ID VRING1_ID +#endif // MICROPY_PY_OPENAMP_HOST #define VRING_COUNT 2 #define VRING_ALIGNMENT 32 @@ -71,13 +80,15 @@ #define VRING_BUFF_ADDR (METAL_SHM_ADDR + 0x2000) #define VRING_BUFF_SIZE (METAL_SHM_SIZE - 0x2000) +#if MICROPY_PY_OPENAMP_HOST static const char openamp_trace_buf[128]; #define MICROPY_PY_OPENAMP_TRACE_BUF ((uint32_t)openamp_trace_buf) #define MICROPY_PY_OPENAMP_TRACE_BUF_LEN sizeof(MICROPY_PY_OPENAMP_TRACE_BUF) +#endif // MICROPY_PY_OPENAMP_HOST #endif // MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE -#if MICROPY_PY_OPENAMP_REMOTEPROC +#if MICROPY_PY_OPENAMP_HOST && MICROPY_PY_OPENAMP_REMOTEPROC extern mp_obj_type_t openamp_remoteproc_type; #endif @@ -267,12 +278,11 @@ static void openamp_ns_callback(struct rpmsg_device *rdev, const char *name, uin } } -#if MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE +#if MICROPY_PY_OPENAMP_HOST && MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE // The shared resource table must be initialized manually by the host here, // because it's not located in the data region, so the startup code doesn't // know about it. -static void openamp_rsc_table_init(openamp_rsc_table_t **rsc_table_out) { - openamp_rsc_table_t *rsc_table = METAL_RSC_ADDR; +static void openamp_rsc_table_init(openamp_rsc_table_t *rsc_table) { memset(rsc_table, 0, METAL_RSC_SIZE); rsc_table->version = 1; @@ -299,9 +309,8 @@ static void openamp_rsc_table_init(openamp_rsc_table_t **rsc_table_out) { // Flush resource table. metal_cache_flush((uint32_t *)rsc_table, sizeof(openamp_rsc_table_t)); #endif - *rsc_table_out = rsc_table; } -#endif // MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE +#endif // MICROPY_PY_OPENAMP_HOST && MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE static mp_obj_t openamp_new_service_callback(mp_obj_t ns_callback) { if (MP_STATE_PORT(virtio_device) == NULL) { @@ -342,8 +351,10 @@ void openamp_init(void) { metal_init(&metal_params); // Initialize the shared resource table. - openamp_rsc_table_t *rsc_table; - openamp_rsc_table_init(&rsc_table); + openamp_rsc_table_t *rsc_table = METAL_RSC_ADDR; + #if MICROPY_PY_OPENAMP_HOST + openamp_rsc_table_init(rsc_table); + #endif // MICROPY_PY_OPENAMP_HOST if (metal_register_generic_device(&shm_device) != 0) { mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to register metal device")); @@ -368,7 +379,7 @@ void openamp_init(void) { } // Create virtio device. - struct virtio_device *vdev = rproc_virtio_create_vdev(RPMSG_HOST, VIRTIO_DEV_ID, + struct virtio_device *vdev = rproc_virtio_create_vdev(VIRTIO_DEV_ROLE, VIRTIO_DEV_ID, &rsc_table->vdev, rsc_io, NULL, metal_rproc_notify, NULL); if (vdev == NULL) { mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to create virtio device")); @@ -389,8 +400,8 @@ void openamp_init(void) { // The remote processor detects that the virtio device is ready by polling // the status field in the resource table. rpmsg_virtio_init_shm_pool(&virtio_device->shm_pool, (void *)VRING_BUFF_ADDR, (size_t)VRING_BUFF_SIZE); - rpmsg_init_vdev(&virtio_device->rvdev, vdev, openamp_ns_callback, shm_io, &virtio_device->shm_pool); + rpmsg_init_vdev(&virtio_device->rvdev, vdev, openamp_ns_callback, shm_io, &virtio_device->shm_pool); MP_STATE_PORT(virtio_device) = virtio_device; } @@ -399,7 +410,7 @@ static const mp_rom_map_elem_t globals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_ENDPOINT_ADDR_ANY), MP_ROM_INT(RPMSG_ADDR_ANY) }, { MP_ROM_QSTR(MP_QSTR_new_service_callback), MP_ROM_PTR(&openamp_new_service_callback_obj) }, { MP_ROM_QSTR(MP_QSTR_Endpoint), MP_ROM_PTR(&endpoint_type) }, - #if MICROPY_PY_OPENAMP_REMOTEPROC + #if MICROPY_PY_OPENAMP_HOST && MICROPY_PY_OPENAMP_REMOTEPROC { MP_ROM_QSTR(MP_QSTR_RemoteProc), MP_ROM_PTR(&openamp_remoteproc_type) }, #endif }; diff --git a/extmod/modopenamp_remoteproc.c b/extmod/modopenamp_remoteproc.c index 82719dca96145..ed61cb6bd442f 100644 --- a/extmod/modopenamp_remoteproc.c +++ b/extmod/modopenamp_remoteproc.c @@ -26,7 +26,7 @@ * OpenAMP's remoteproc class. */ -#if MICROPY_PY_OPENAMP_REMOTEPROC +#if MICROPY_PY_OPENAMP_HOST && MICROPY_PY_OPENAMP_REMOTEPROC #include "py/obj.h" #include "py/nlr.h" @@ -170,4 +170,4 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &openamp_remoteproc_dict ); -#endif // MICROPY_PY_OPENAMP_REMOTEPROC +#endif // MICROPY_PY_OPENAMP_HOST && MICROPY_PY_OPENAMP_REMOTEPROC diff --git a/extmod/modopenamp_remoteproc_store.c b/extmod/modopenamp_remoteproc_store.c index aed652c485553..03022ebc83518 100644 --- a/extmod/modopenamp_remoteproc_store.c +++ b/extmod/modopenamp_remoteproc_store.c @@ -26,7 +26,7 @@ * OpenAMP's remoteproc store. */ -#if MICROPY_PY_OPENAMP_REMOTEPROC +#if MICROPY_PY_OPENAMP_HOST && MICROPY_PY_OPENAMP_REMOTEPROC #include "py/obj.h" #include "py/nlr.h" @@ -141,4 +141,4 @@ const struct image_store_ops openamp_remoteproc_store_ops = { #endif // MICROPY_PY_OPENAMP_REMOTEPROC_STORE_ENABLE -#endif // MICROPY_PY_OPENAMP_REMOTEPROC +#endif // MICROPY_PY_OPENAMP_HOST && MICROPY_PY_OPENAMP_REMOTEPROC From bc7e39d549678e3c51fa667ad2097f6eb2da6987 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 13 Jul 2024 10:55:32 +0300 Subject: [PATCH 0178/1300] extmod/modopenamp: Fix Endpoint callback required arg. The callback arg is not actually required. Signed-off-by: iabdalkader --- extmod/modopenamp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/modopenamp.c b/extmod/modopenamp.c index a10441d64a05a..4ba8096f93a7a 100644 --- a/extmod/modopenamp.c +++ b/extmod/modopenamp.c @@ -221,7 +221,7 @@ static mp_obj_t endpoint_make_new(const mp_obj_type_t *type, size_t n_args, size enum { ARG_name, ARG_callback, ARG_src, ARG_dest }; static const mp_arg_t allowed_args[] = { { MP_QSTR_name, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } }, - { MP_QSTR_callback, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_callback, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_src, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } }, { MP_QSTR_dest, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } }, }; From 9a48ce3051744fac20762199d67ffc380c1de1f6 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 20 Jul 2024 10:37:26 +0300 Subject: [PATCH 0179/1300] stm32/mpu: Define the last used MPU region number. The reason for this change is that it makes allows custom code, that needs to use an MPU region, to find a free one by using this macro or starting from the max number and downwards, without concern that it might change in the future. Signed-off-by: iabdalkader --- ports/stm32/mpu.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ports/stm32/mpu.h b/ports/stm32/mpu.h index 5ef1466184aac..a87c04a58df5c 100644 --- a/ports/stm32/mpu.h +++ b/ports/stm32/mpu.h @@ -36,12 +36,18 @@ #define MPU_REGION_QSPI3 (MPU_REGION_NUMBER3) #define MPU_REGION_SDRAM1 (MPU_REGION_NUMBER4) #define MPU_REGION_SDRAM2 (MPU_REGION_NUMBER5) -#define MPU_REGION_OPENAMP (MPU_REGION_NUMBER15) // Only relevant on CPUs with D-Cache, must be higher priority than SDRAM #define MPU_REGION_DMA_UNCACHED_1 (MPU_REGION_NUMBER6) #define MPU_REGION_DMA_UNCACHED_2 (MPU_REGION_NUMBER7) +#ifdef MPU_REGION_NUMBER8 +#define MPU_REGION_OPENAMP (MPU_REGION_NUMBER8) +#define MPU_REGION_LAST_USED (MPU_REGION_NUMBER8) +#else +#define MPU_REGION_LAST_USED (MPU_REGION_NUMBER7) +#endif + // Attribute value to disable a region entirely, remove it from the MPU // (i.e. the MPU_REGION_ENABLE bit is unset.) #define MPU_CONFIG_DISABLE 0 @@ -135,6 +141,7 @@ static inline void mpu_config_end(uint32_t irq_state) { #define MPU_REGION_SIG (MPU_REGION_NUMBER0) #define MPU_REGION_ETH (MPU_REGION_NUMBER1) +#define MPU_REGION_LAST_USED (MPU_REGION_NUMBER1) #define ST_DEVICE_SIGNATURE_BASE (0x08fff800) #define ST_DEVICE_SIGNATURE_LIMIT (0x08ffffff) From 1743a7d7218646c2ebce5c36691638d2a60cc59a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 1 Aug 2024 17:02:17 +0300 Subject: [PATCH 0180/1300] extmod/modopenamp: Use mp_event_* functions for poll/wait. These are the new helper functions to use for polling/waiting. Signed-off-by: iabdalkader --- extmod/modopenamp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extmod/modopenamp.c b/extmod/modopenamp.c index 4ba8096f93a7a..7d5841c40001d 100644 --- a/extmod/modopenamp.c +++ b/extmod/modopenamp.c @@ -192,13 +192,13 @@ static mp_obj_t endpoint_send(uint n_args, const mp_obj_t *pos_args, mp_map_t *k for (mp_uint_t start = mp_hal_ticks_ms(); ;) { bytes = rpmsg_send_offchannel_raw(&self->ep, src, dest, rbuf.buf, rbuf.len, false); if (bytes > 0 || timeout == 0) { - MICROPY_EVENT_POLL_HOOK + mp_event_handle_nowait(); break; } if (timeout > 0 && (mp_hal_ticks_ms() - start > timeout)) { mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("timeout waiting for a free buffer")); } - MICROPY_EVENT_POLL_HOOK + mp_event_wait_ms(1); } return mp_obj_new_int(bytes); } From 67ebe086a8948b083d84da1bf83ec82e1a9f1b85 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 1 Aug 2024 17:05:11 +0300 Subject: [PATCH 0181/1300] stm32/mpmetalport: Use mp_event_handle_nowait() for metal_poll. Signed-off-by: iabdalkader --- ports/stm32/mpmetalport.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/stm32/mpmetalport.h b/ports/stm32/mpmetalport.h index 53f329d15b093..706249b6aa414 100644 --- a/ports/stm32/mpmetalport.h +++ b/ports/stm32/mpmetalport.h @@ -70,7 +70,8 @@ static inline int __metal_sleep_usec(unsigned int usec) { } static inline void metal_generic_default_poll(void) { - MICROPY_EVENT_POLL_HOOK + mp_event_handle_nowait(); + __WFI(); } #endif // MICROPY_INCLUDED_STM32_METAL_PORT_H From 91f4a6b9e902c066c98bda3799e01c8c6c0783ea Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 1 Aug 2024 17:06:19 +0300 Subject: [PATCH 0182/1300] mimxrt/mpmetalport: Use mp_event_handle_nowait() for metal_poll. Signed-off-by: iabdalkader --- ports/mimxrt/mpmetalport.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/mimxrt/mpmetalport.h b/ports/mimxrt/mpmetalport.h index 994d57cc39fb4..d197bbc029068 100644 --- a/ports/mimxrt/mpmetalport.h +++ b/ports/mimxrt/mpmetalport.h @@ -67,7 +67,8 @@ static inline int __metal_sleep_usec(unsigned int usec) { } static inline void metal_generic_default_poll(void) { - MICROPY_EVENT_POLL_HOOK + mp_event_handle_nowait(); + __WFI(); } #endif // MICROPY_INCLUDED_MIMXRT_METAL_PORT_H From 868d311a2361c9125d70833b8757a859a539e8cc Mon Sep 17 00:00:00 2001 From: Glenn Moloney Date: Sun, 30 Jun 2024 22:01:32 +1000 Subject: [PATCH 0183/1300] esp32/network_lan: Make LAN.active(state) succeed if already in state. This PR ensures that `network.LAN.active(True/False)` will succeed if the LAN is already in the desired state. Currently, `lan.active(True)` will raise an `OSError` exception if the LAN is already in the desired state. This is inconsistent with `network.WLAN.active(True/False)` and causes `lan.active(True)` to raise an exception after a soft reset (causing common network startup scripts to fail for LAN interfaces). Signed-off-by: Glenn Moloney --- ports/esp32/network_lan.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c index 7e7ebcc929185..b957c5f892456 100644 --- a/ports/esp32/network_lan.c +++ b/ports/esp32/network_lan.c @@ -312,13 +312,14 @@ static mp_obj_t lan_active(size_t n_args, const mp_obj_t *args) { lan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (n_args > 1) { - if (mp_obj_is_true(args[1])) { + bool make_active = mp_obj_is_true(args[1]); + if (make_active && !self->base.active) { esp_netif_set_hostname(self->base.netif, mod_network_hostname_data); self->base.active = (esp_eth_start(self->eth_handle) == ESP_OK); if (!self->base.active) { mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ethernet enable failed")); } - } else { + } else if (!make_active && self->base.active) { self->base.active = !(esp_eth_stop(self->eth_handle) == ESP_OK); if (self->base.active) { mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ethernet disable failed")); From 288a03625327b025f81bb6d4e7fb76d56343172c Mon Sep 17 00:00:00 2001 From: Glenn Moloney Date: Mon, 8 Jul 2024 17:33:53 +1000 Subject: [PATCH 0184/1300] esp32/network_lan: Ensure LAN MAC address is valid at LAN init. `get_lan()`: If the ethernet MAC address is uninitialised, set it to the address reserved by the ESP32 for the ETH interface. SPI LAN devices may be initialised with a MAC address of 00:00:00:00:00:00. So check that a valid unicast MAC address has been set (using `LAN.config(mac=...)`) when initialising the LAN interface. Fixes #15425. Signed-off-by: Glenn Moloney --- ports/esp32/network_lan.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c index b957c5f892456..416e02e12db85 100644 --- a/ports/esp32/network_lan.c +++ b/ports/esp32/network_lan.c @@ -36,6 +36,7 @@ #include "esp_eth.h" #include "esp_eth_mac.h" +#include "esp_mac.h" #include "esp_event.h" #include "esp_log.h" #include "esp_netif.h" @@ -93,6 +94,17 @@ static void eth_event_handler(void *arg, esp_event_base_t event_base, } } +static void set_mac_address(lan_if_obj_t *self, uint8_t *mac, size_t len) { + if (len != 6) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid buffer length")); + } + if (((mac[0] & 0x01) != 0) || + (esp_eth_ioctl(self->eth_handle, ETH_CMD_S_MAC_ADDR, mac) != ESP_OK) || + (esp_netif_set_mac(self->base.netif, mac) != ESP_OK)) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("failed setting MAC address")); + } +} + static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { lan_if_obj_t *self = &lan_obj; @@ -302,6 +314,14 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_netif_attach failed")); } + // If MAC address is unset, set it to the address reserved for the ESP32 ETH interface + uint8_t mac_addr[6]; + esp_eth_ioctl(self->eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + if ((mac_addr[0] | mac_addr[1] | mac_addr[2] | mac_addr[3] | mac_addr[4] | mac_addr[5]) == 0) { + esp_read_mac(mac_addr, ESP_MAC_ETH); // Get ESP32 MAC address for ETH iface + set_mac_address(self, mac_addr, sizeof(mac_addr)); + } + eth_status = ETH_INITIALIZED; return MP_OBJ_FROM_PTR(&lan_obj); @@ -356,15 +376,7 @@ static mp_obj_t lan_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs case MP_QSTR_mac: { mp_buffer_info_t bufinfo; mp_get_buffer_raise(kwargs->table[i].value, &bufinfo, MP_BUFFER_READ); - if (bufinfo.len != 6) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid buffer length")); - } - if ( - (esp_eth_ioctl(self->eth_handle, ETH_CMD_S_MAC_ADDR, bufinfo.buf) != ESP_OK) || - (esp_netif_set_mac(self->base.netif, bufinfo.buf) != ESP_OK) - ) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("failed setting MAC address")); - } + set_mac_address(self, bufinfo.buf, bufinfo.len); break; } default: From 4d6d84983f370e48e81fb05fe31802e0a13fb369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Z=C3=BCger?= Date: Fri, 26 Apr 2024 17:48:57 +0200 Subject: [PATCH 0185/1300] extmod/modtls_mbedtls: Fix DER parsing and calculation of key/cert len. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `mbedtls_pk_parse_key()` expects `key_len` to include the NULL terminator for PEM data but not for DER encoded data. This also applies to `mbedtls_x509_crt_parse()` and `cert_len`. Since all PEM data contains "-----BEGIN" this is used to check if the data is PEM (as per mbedtls code). This can be done for both v2 and v3 of mbedtls since the fundamental behaviour/expectation did not change. What changed is that in v3 the PKCS#8 DER parser now checks that the passed key buffer is fully utilized and no bytes are remaining (all other DER formats still do not check this). Signed-off-by: Peter Züger --- extmod/modtls_mbedtls.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/extmod/modtls_mbedtls.c b/extmod/modtls_mbedtls.c index 5a9487ab89323..d3fc26fad2a54 100644 --- a/extmod/modtls_mbedtls.c +++ b/extmod/modtls_mbedtls.c @@ -100,6 +100,12 @@ static void mbedtls_debug(void *ctx, int level, const char *file, int line, cons } #endif +#if defined(MBEDTLS_PEM_PARSE_C) +static int mbedtls_is_pem(const byte *data, size_t len) { + return (len >= 10) && (strstr((const char *)data, "-----BEGIN") != NULL); +} +#endif + static NORETURN void mbedtls_raise_error(int err) { // Handle special cases. if (err == MBEDTLS_ERR_SSL_ALLOC_FAILED) { @@ -347,12 +353,19 @@ static MP_DEFINE_CONST_FUN_OBJ_2(ssl_context_set_ciphers_obj, ssl_context_set_ci static void ssl_context_load_key(mp_obj_ssl_context_t *self, mp_obj_t key_obj, mp_obj_t cert_obj) { size_t key_len; const byte *key = (const byte *)mp_obj_str_get_data(key_obj, &key_len); - // len should include terminating null + + #if defined(MBEDTLS_PEM_PARSE_C) + // len should include terminating null if the data is PEM encoded + if (mbedtls_is_pem(key, key_len)) { + key_len += 1; + } + #endif + int ret; #if MBEDTLS_VERSION_NUMBER >= 0x03000000 - ret = mbedtls_pk_parse_key(&self->pkey, key, key_len + 1, NULL, 0, mbedtls_ctr_drbg_random, &self->ctr_drbg); + ret = mbedtls_pk_parse_key(&self->pkey, key, key_len, NULL, 0, mbedtls_ctr_drbg_random, &self->ctr_drbg); #else - ret = mbedtls_pk_parse_key(&self->pkey, key, key_len + 1, NULL, 0); + ret = mbedtls_pk_parse_key(&self->pkey, key, key_len, NULL, 0); #endif if (ret != 0) { mbedtls_raise_error(MBEDTLS_ERR_PK_BAD_INPUT_DATA); // use general error for all key errors @@ -360,8 +373,15 @@ static void ssl_context_load_key(mp_obj_ssl_context_t *self, mp_obj_t key_obj, m size_t cert_len; const byte *cert = (const byte *)mp_obj_str_get_data(cert_obj, &cert_len); - // len should include terminating null - ret = mbedtls_x509_crt_parse(&self->cert, cert, cert_len + 1); + + #if defined(MBEDTLS_PEM_PARSE_C) + // len should include terminating null if the data is PEM encoded + if (mbedtls_is_pem(cert, cert_len)) { + cert_len += 1; + } + #endif + + ret = mbedtls_x509_crt_parse(&self->cert, cert, cert_len); if (ret != 0) { mbedtls_raise_error(MBEDTLS_ERR_X509_BAD_INPUT_DATA); // use general error for all cert errors } @@ -383,8 +403,15 @@ static MP_DEFINE_CONST_FUN_OBJ_3(ssl_context_load_cert_chain_obj, ssl_context_lo static void ssl_context_load_cadata(mp_obj_ssl_context_t *self, mp_obj_t cadata_obj) { size_t cacert_len; const byte *cacert = (const byte *)mp_obj_str_get_data(cadata_obj, &cacert_len); - // len should include terminating null - int ret = mbedtls_x509_crt_parse(&self->cacert, cacert, cacert_len + 1); + + #if defined(MBEDTLS_PEM_PARSE_C) + // len should include terminating null if the data is PEM encoded + if (mbedtls_is_pem(cacert, cacert_len)) { + cacert_len += 1; + } + #endif + + int ret = mbedtls_x509_crt_parse(&self->cacert, cacert, cacert_len); if (ret != 0) { mbedtls_raise_error(MBEDTLS_ERR_X509_BAD_INPUT_DATA); // use general error for all cert errors } From 1473ed4c6460dd3d5df01e60eaa7db1c80baf8fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Z=C3=BCger?= Date: Mon, 22 Jul 2024 15:23:27 +0200 Subject: [PATCH 0186/1300] tests/extmod/ssl_keycert.py: Add test for PKCS8 formatted DER key. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Züger --- tests/extmod/ssl_keycert.py | 11 +++++++++++ tests/extmod/ssl_keycert.py.exp | 1 + 2 files changed, 12 insertions(+) diff --git a/tests/extmod/ssl_keycert.py b/tests/extmod/ssl_keycert.py index 53f064fdaff48..badc11e0339d5 100644 --- a/tests/extmod/ssl_keycert.py +++ b/tests/extmod/ssl_keycert.py @@ -9,6 +9,11 @@ key = b"0\x82\x019\x02\x01\x00\x02A\x00\xf9\xe0}\xbd\xd7\x9cI\x18\x06\xc3\xcb\xb5\xec@r\xfbD\x18\x80\xaaWoZ{\xcc\xa3\xeb!\"\x0fY\x9e]-\xee\xe4\t!BY\x9f{7\xf3\xf2\x8f}}\r|.\xa8<\ta\xb2\xd7W\xb3\xc9\x19A\xc39\x02\x03\x01\x00\x01\x02@\x07:\x9fh\xa6\x9c6\xe1#\x10\xf7\x0b\xc4Q\xf9\x01\x9b\xee\xb9\x8a4\r\\\xa8\xc8:\xd5\xca\x97\x99\xaa\x16\x04)\xa8\xf9\x13\xdeq\x0ev`\xa7\x83\xc5\x8b`\xdb\xef \x9d\x93\xe8g\x84\x96\xfaV\\\xf4R\xda\xd0\xa1\x02!\x00\xfeR\xbf\n\x91Su\x87L\x98{\xeb%\xed\xfb\x06u)@\xfe\x1b\xde\xa0\xc6@\xab\xc5\xedg\x8e\x10[\x02!\x00\xfb\x86=\x85\xa4'\xde\x85\xb5L\xe0)\x99\xfaL\x8c3A\x02\xa8<\xdew\xad\x00\xe3\x1d\x05\xd8\xb4N\xfb\x02 \x08\xb0M\x04\x90hx\x88q\xcew\xd5U\xcbf\x9b\x16\xdf\x9c\xef\xd1\x85\xee\x9a7Ug\x02\xb0Z\x03'\x02 9\xa0D\xe2$|\xf9\xefz]5\x92rs\xb5+\xfd\xe6,\x1c\xadmn\xcf\xd5?3|\x0em)\x17\x02 5Z\xcc/\xa5?\n\x04%\x9b{N\x9dX\xddI\xbe\xd2\xb0\xa0\x03BQ\x02\x82\xc2\xe0u)\xbd\xb8\xaf" +# This particular key was generated with: +# $ openssl genrsa -out key.pem 512 +# $ openssl rsa -in key.pem -outform DER -out key.der +keypkcs8 = b'0\x82\x01U\x02\x01\x000\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x04\x82\x01?0\x82\x01;\x02\x01\x00\x02A\x00\xae\x07\xbb\xbf \x8eL\\ G\x91\x1b\xc45\x89\x96\x0c\xca\x94\xa5\xc2%F\x84z\xea\xb2Y\xdd\x943\x1a\xe0\x1e\xfe%\xd3\xbc\x85\xf0\x05\x8eI\xca\x83\x8b\x86\xfd\x15P,\xa9t8\xc2s\x11\x7fk*[[w\xdd\x02\x03\x01\x00\x01\x02@?\x02-\xc7\x95Q\xef\xf3\xaa\xfc>]\x7f<\xa3\x03\xe0\xbd\xb7\xf4\x0b\xc6\xd31D\x0f\x95cT\x06D9\xed\xf0X\xa6\x18\xb4\xe7\x96\x8f\x91\xd6\xd7\xbfR\\V\xfd\x0e{\xd3\xa4e\xb4M\x01\xbb\xdf\xf1\xf0\xbcEA\x02!\x00\xd5\xd7f\xcc\x84\xf5Y&\xa7]\x00\x91\xc9\x8e\xb0\xf37\x108D\x7f&!JJ\x836\x83\x9f\n\xf2\x0f\x02!\x00\xd0W\x0ec\xa6\x86C\xf7\x8d\xbe\xf0\xbc2\xe3)|\xbb\xd6\xbb;\xb2\xafG\x05S\x0f-\x14X\n3S\x02 A\xf5\xb20\xcd\xa6<<\x8f\rA\xda\xb68<^\x99\x12x\xb8\xb0\x1b\x9b\xd3%\x8e\xb9\xa6\xf9\xcc\xcf\x83\x02!\x00\xa8.\xee\xdd\xc0\xeb8h\xda\x13^\xb0\xfe\x7fd\xd9]\xa5m\xc66k\xcap`\xe9\xaaav\xe4\xdd#\x02!\x00\xd1\xd0\x93\xe7wZ\xe0p\xd0M\x07\x9e\x13\xa7\xa7\x12\x95\x1e\x13h0O\xc0\x18\x1fa\x10")\x9f&U' + # Invalid key try: ssl.wrap_socket(io.BytesIO(), key=b"!") @@ -26,3 +31,9 @@ ssl.wrap_socket(io.BytesIO(), key=key, cert=b"!") except ValueError as er: print(repr(er)) + +# Valid key in PKCS8 format, invalid cert +try: + ssl.wrap_socket(io.BytesIO(), key=keypkcs8, cert=b"!") +except ValueError as er: + print(repr(er)) diff --git a/tests/extmod/ssl_keycert.py.exp b/tests/extmod/ssl_keycert.py.exp index b72d319c6a807..95140cb998013 100644 --- a/tests/extmod/ssl_keycert.py.exp +++ b/tests/extmod/ssl_keycert.py.exp @@ -1,3 +1,4 @@ ValueError('invalid key',) TypeError("can't convert 'NoneType' object to str implicitly",) ValueError('invalid cert',) +ValueError('invalid cert',) From 6c870dc5ecc048309f7fcb21324b1aed165afdb0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 13 Aug 2024 16:32:51 +1000 Subject: [PATCH 0187/1300] py/obj: Remove the legacy object API for version 2. These were changed in v1.11 (2019). Prepare to remove the compatibility macros as part of V2 changes. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- py/obj.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/py/obj.h b/py/obj.h index 46d42baf4bfc1..81ee75aebc3aa 100644 --- a/py/obj.h +++ b/py/obj.h @@ -1273,6 +1273,8 @@ mp_obj_t mp_seq_extract_slice(const mp_obj_t *seq, mp_bound_slice_t *indexes); memmove(((char *)dest) + (beg + slice_len) * (item_sz), ((char *)dest) + (end) * (item_sz), ((dest_len) + (len_adj) - ((beg) + (slice_len))) * (item_sz)); \ memmove(((char *)dest) + (beg) * (item_sz), slice, slice_len * (item_sz)); +#if !MICROPY_PREVIEW_VERSION_2 + // Provide translation for legacy API #define MP_OBJ_IS_SMALL_INT mp_obj_is_small_int #define MP_OBJ_IS_QSTR mp_obj_is_qstr @@ -1285,4 +1287,6 @@ mp_obj_t mp_seq_extract_slice(const mp_obj_t *seq, mp_bound_slice_t *indexes); #define MP_MAP_SLOT_IS_FILLED mp_map_slot_is_filled #define MP_SET_SLOT_IS_FILLED mp_set_slot_is_filled +#endif + #endif // MICROPY_INCLUDED_PY_OBJ_H From 86f2c285eb222f30c24df187ac66d3e0c80caf61 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 6 Aug 2024 15:51:22 +1000 Subject: [PATCH 0188/1300] py: Add new cstack API for stack checking, with limit margin macro. Currently the stack limit margin is hard-coded in each port's call to `mp_stack_set_limit()`, but on threaded ports it's fiddlier and can lead to bugs (such as incorrect thread stack margin on esp32). This commit provides a new API to initialise the C Stack in one function call, with a config macro to set the margin. Where possible the new call is inlined to reduce code size in thread-free ports. Intended replacement for `MP_TASK_STACK_LIMIT_MARGIN` on esp32. The previous `stackctrl.h` API is still present and unmodified apart from a deprecation comment. However it's not available when the `MICROPY_PREVIEW_VERSION_2` macro is set. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- examples/natmod/re/re.c | 2 +- extmod/modre.c | 4 +-- py/cstack.c | 57 +++++++++++++++++++++++++++++++++++++ py/cstack.h | 63 +++++++++++++++++++++++++++++++++++++++++ py/modmicropython.c | 8 +++--- py/modthread.c | 1 - py/mpconfig.h | 7 +++++ py/obj.c | 4 +-- py/objfun.c | 10 +++---- py/objgenerator.c | 4 +-- py/objlist.c | 4 +-- py/objstr.c | 4 +-- py/py.cmake | 1 + py/py.mk | 1 + py/runtime.c | 4 +-- py/runtime.h | 5 ++-- py/stackctrl.c | 7 +++++ py/stackctrl.h | 8 +++++- 18 files changed, 167 insertions(+), 27 deletions(-) create mode 100644 py/cstack.c create mode 100644 py/cstack.h diff --git a/examples/natmod/re/re.c b/examples/natmod/re/re.c index 7ae72a578f4e3..eb6d13778c387 100644 --- a/examples/natmod/re/re.c +++ b/examples/natmod/re/re.c @@ -11,7 +11,7 @@ const char *stack_top; -void mp_stack_check(void) { +void mp_cstack_check(void) { // Assumes descending stack on target volatile char dummy; if (stack_top - &dummy >= STACK_LIMIT) { diff --git a/extmod/modre.c b/extmod/modre.c index f3d2e302a0374..1a118009cbd1f 100644 --- a/extmod/modre.c +++ b/extmod/modre.c @@ -31,7 +31,7 @@ #include "py/runtime.h" #include "py/binary.h" #include "py/objstr.h" -#include "py/stackctrl.h" +#include "py/cstack.h" #if MICROPY_PY_BUILTINS_STR_UNICODE #include "py/unicode.h" @@ -39,7 +39,7 @@ #if MICROPY_PY_RE -#define re1_5_stack_chk() MP_STACK_CHECK() +#define re1_5_stack_chk() mp_cstack_check() #include "lib/re1.5/re1.5.h" diff --git a/py/cstack.c b/py/cstack.c new file mode 100644 index 0000000000000..fe4b16d652a50 --- /dev/null +++ b/py/cstack.c @@ -0,0 +1,57 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Paul Sokolovsky + * Copryight (c) 2024 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. + */ + +#include "py/runtime.h" +#include "py/cstack.h" + +void mp_cstack_init_with_sp_here(size_t stack_size) { + #if __GNUC__ >= 13 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdangling-pointer" + #endif + volatile int stack_dummy; + mp_cstack_init_with_top((void *)&stack_dummy, stack_size); + #if __GNUC__ >= 13 + #pragma GCC diagnostic pop + #endif +} + +mp_uint_t mp_cstack_usage(void) { + // Assumes descending stack + volatile int stack_dummy; + return MP_STATE_THREAD(stack_top) - (char *)&stack_dummy; +} + +#if MICROPY_STACK_CHECK + +void mp_cstack_check(void) { + if (mp_cstack_usage() >= MP_STATE_THREAD(stack_limit)) { + mp_raise_recursion_depth(); + } +} + +#endif // MICROPY_STACK_CHECK diff --git a/py/cstack.h b/py/cstack.h new file mode 100644 index 0000000000000..b12a18e13fcad --- /dev/null +++ b/py/cstack.h @@ -0,0 +1,63 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Paul Sokolovsky + * Copyright (c) 2024 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_PY_CSTACK_H +#define MICROPY_INCLUDED_PY_CSTACK_H + +#include "py/mpstate.h" + +// Both init functions below accept the full stack size. Set the +// MICROPY_STACK_CHECK_MARGIN to the number of bytes subtracted to account +// for stack usage between checks. + +void mp_cstack_init_with_sp_here(size_t stack_size); + +inline static void mp_cstack_init_with_top(void *top, size_t stack_size) { + MP_STATE_THREAD(stack_top) = (char *)top; + + #if MICROPY_STACK_CHECK + assert(stack_size > MICROPY_STACK_CHECK_MARGIN); // Should be enforced by port + MP_STATE_THREAD(stack_limit) = stack_size - MICROPY_STACK_CHECK_MARGIN; + #else + (void)stack_size; + #endif +} + +mp_uint_t mp_cstack_usage(void); + +#if MICROPY_STACK_CHECK + +void mp_cstack_check(void); + +#else + +inline static void mp_cstack_check(void) { + // No-op when stack checking is disabled +} + +#endif + +#endif // MICROPY_INCLUDED_PY_CSTACK_H diff --git a/py/modmicropython.c b/py/modmicropython.c index af6ad01795f67..daf03807c848a 100644 --- a/py/modmicropython.c +++ b/py/modmicropython.c @@ -27,7 +27,7 @@ #include #include "py/builtin.h" -#include "py/stackctrl.h" +#include "py/cstack.h" #include "py/runtime.h" #include "py/gc.h" #include "py/mphal.h" @@ -76,9 +76,9 @@ mp_obj_t mp_micropython_mem_info(size_t n_args, const mp_obj_t *args) { #endif #if MICROPY_STACK_CHECK mp_printf(&mp_plat_print, "stack: " UINT_FMT " out of " UINT_FMT "\n", - mp_stack_usage(), (mp_uint_t)MP_STATE_THREAD(stack_limit)); + mp_cstack_usage(), (mp_uint_t)MP_STATE_THREAD(stack_limit)); #else - mp_printf(&mp_plat_print, "stack: " UINT_FMT "\n", mp_stack_usage()); + mp_printf(&mp_plat_print, "stack: " UINT_FMT "\n", mp_cstack_usage()); #endif #if MICROPY_ENABLE_GC gc_dump_info(&mp_plat_print); @@ -111,7 +111,7 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_qstr_info_obj, 0, 1, m #if MICROPY_PY_MICROPYTHON_STACK_USE static mp_obj_t mp_micropython_stack_use(void) { - return MP_OBJ_NEW_SMALL_INT(mp_stack_usage()); + return MP_OBJ_NEW_SMALL_INT(mp_cstack_usage()); } static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_stack_use_obj, mp_micropython_stack_use); #endif diff --git a/py/modthread.c b/py/modthread.c index 2826fadeaa6b4..7742ff68471f1 100644 --- a/py/modthread.c +++ b/py/modthread.c @@ -28,7 +28,6 @@ #include #include "py/runtime.h" -#include "py/stackctrl.h" #if MICROPY_PY_THREAD diff --git a/py/mpconfig.h b/py/mpconfig.h index 94fdca7d7a97d..5c10007a194e7 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -690,6 +690,13 @@ #define MICROPY_STACK_CHECK (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +// Additional margin between the places in the runtime where Python stack is +// checked and the actual end of the C stack. Needs to be large enough to avoid +// overflows from function calls made between checks. +#ifndef MICROPY_STACK_CHECK_MARGIN +#define MICROPY_STACK_CHECK_MARGIN (0) +#endif + // Whether to have an emergency exception buffer #ifndef MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (0) diff --git a/py/obj.c b/py/obj.c index e43451dadd512..1606ad5209e7b 100644 --- a/py/obj.c +++ b/py/obj.c @@ -34,7 +34,7 @@ #include "py/objint.h" #include "py/objstr.h" #include "py/runtime.h" -#include "py/stackctrl.h" +#include "py/cstack.h" #include "py/stream.h" // for mp_obj_print // Allocates an object and also sets type, for mp_obj_malloc{,_var} macros. @@ -117,7 +117,7 @@ const char *mp_obj_get_type_str(mp_const_obj_t o_in) { void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { // There can be data structures nested too deep, or just recursive - MP_STACK_CHECK(); + mp_cstack_check(); #ifndef NDEBUG if (o_in == MP_OBJ_NULL) { mp_print_str(print, "(nil)"); diff --git a/py/objfun.c b/py/objfun.c index 1ebfa3d5af217..8279605e9874a 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -32,7 +32,7 @@ #include "py/objfun.h" #include "py/runtime.h" #include "py/bc.h" -#include "py/stackctrl.h" +#include "py/cstack.h" #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) @@ -194,7 +194,7 @@ static void dump_args(const mp_obj_t *a, size_t sz) { #if MICROPY_STACKLESS mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { - MP_STACK_CHECK(); + mp_cstack_check(); mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); size_t n_state, state_size; @@ -225,7 +225,7 @@ mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args #endif static mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { - MP_STACK_CHECK(); + mp_cstack_check(); DEBUG_printf("Input n_args: " UINT_FMT ", n_kw: " UINT_FMT "\n", n_args, n_kw); DEBUG_printf("Input pos args: "); @@ -397,7 +397,7 @@ mp_obj_t mp_obj_new_fun_bc(const mp_obj_t *def_args, const byte *code, const mp_ #if MICROPY_EMIT_NATIVE static mp_obj_t fun_native_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { - MP_STACK_CHECK(); + mp_cstack_check(); mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); mp_call_fun_t fun = mp_obj_fun_native_get_function_start(self); return fun(self_in, n_args, n_kw, args); @@ -431,7 +431,7 @@ MP_DEFINE_CONST_OBJ_TYPE( #if MICROPY_EMIT_NATIVE static mp_obj_t fun_viper_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { - MP_STACK_CHECK(); + mp_cstack_check(); mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); mp_call_fun_t fun = MICROPY_MAKE_POINTER_CALLABLE((void *)self->bytecode); return fun(self_in, n_args, n_kw, args); diff --git a/py/objgenerator.c b/py/objgenerator.c index 431cbad5a79d8..df9701094b80c 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -33,7 +33,7 @@ #include "py/objstr.h" #include "py/objgenerator.h" #include "py/objfun.h" -#include "py/stackctrl.h" +#include "py/cstack.h" // Instance of GeneratorExit exception - needed by generator.close() const mp_obj_exception_t mp_const_GeneratorExit_obj = {{&mp_type_GeneratorExit}, 0, 0, NULL, (mp_obj_tuple_t *)&mp_const_empty_tuple_obj}; @@ -151,7 +151,7 @@ static void gen_instance_print(const mp_print_t *print, mp_obj_t self_in, mp_pri } mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val) { - MP_STACK_CHECK(); + mp_cstack_check(); mp_check_self(mp_obj_is_type(self_in, &mp_type_gen_instance)); mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in); if (self->code_state.ip == 0) { diff --git a/py/objlist.c b/py/objlist.c index 9c8cd0e86c844..9a88de3892eb3 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -29,7 +29,7 @@ #include "py/objlist.h" #include "py/runtime.h" -#include "py/stackctrl.h" +#include "py/cstack.h" static mp_obj_t mp_obj_new_list_iterator(mp_obj_t list, size_t cur, mp_obj_iter_buf_t *iter_buf); static mp_obj_list_t *list_new(size_t n); @@ -291,7 +291,7 @@ static mp_obj_t list_pop(size_t n_args, const mp_obj_t *args) { } static void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn, mp_obj_t binop_less_result) { - MP_STACK_CHECK(); + mp_cstack_check(); while (head < tail) { mp_obj_t *h = head - 1; mp_obj_t *t = tail; diff --git a/py/objstr.c b/py/objstr.c index 346a6259f7423..757da827c0fa3 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -32,7 +32,7 @@ #include "py/objstr.h" #include "py/objlist.h" #include "py/runtime.h" -#include "py/stackctrl.h" +#include "py/cstack.h" #if MICROPY_PY_BUILTINS_STR_OP_MODULO static mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_t *args, mp_obj_t dict); @@ -1181,7 +1181,7 @@ static vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar // type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%" // recursively call the formatter to format any nested specifiers - MP_STACK_CHECK(); + mp_cstack_check(); vstr_t format_spec_vstr = mp_obj_str_format_helper(format_spec, str, arg_i, n_args, args, kwargs); const char *s = vstr_null_terminated_str(&format_spec_vstr); const char *stop = s + format_spec_vstr.len; diff --git a/py/py.cmake b/py/py.cmake index ccd0577c386d6..03c559c206d8c 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -20,6 +20,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/builtinhelp.c ${MICROPY_PY_DIR}/builtinimport.c ${MICROPY_PY_DIR}/compile.c + ${MICROPY_PY_DIR}/cstack.c ${MICROPY_PY_DIR}/emitbc.c ${MICROPY_PY_DIR}/emitcommon.c ${MICROPY_PY_DIR}/emitglue.c diff --git a/py/py.mk b/py/py.mk index ff3dff96a5715..0d4958ccba9ca 100644 --- a/py/py.mk +++ b/py/py.mk @@ -131,6 +131,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ nativeglue.o \ pairheap.o \ ringbuf.o \ + cstack.o \ stackctrl.o \ argcheck.o \ warning.o \ diff --git a/py/runtime.c b/py/runtime.c index fd0a8e690135a..acb45c94b0b74 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -43,7 +43,7 @@ #include "py/stream.h" #include "py/runtime.h" #include "py/builtin.h" -#include "py/stackctrl.h" +#include "py/cstack.h" #include "py/gc.h" #if MICROPY_DEBUG_VERBOSE // print debugging info @@ -1374,7 +1374,7 @@ mp_obj_t mp_iternext_allow_raise(mp_obj_t o_in) { // will always return MP_OBJ_STOP_ITERATION instead of raising StopIteration() (or any subclass thereof) // may raise other exceptions mp_obj_t mp_iternext(mp_obj_t o_in) { - MP_STACK_CHECK(); // enumerate, filter, map and zip can recursively call mp_iternext + mp_cstack_check(); // enumerate, filter, map and zip can recursively call mp_iternext const mp_obj_type_t *type = mp_obj_get_type(o_in); if (TYPE_HAS_ITERNEXT(type)) { MP_STATE_THREAD(stop_iteration_arg) = MP_OBJ_NULL; diff --git a/py/runtime.h b/py/runtime.h index 5465c06d8aee9..e8e5a758f8beb 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -28,7 +28,7 @@ #include "py/mpstate.h" #include "py/pystack.h" -#include "py/stackctrl.h" +#include "py/cstack.h" // For use with mp_call_function_1_from_nlr_jump_callback. #define MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, f, a) \ @@ -159,8 +159,7 @@ void mp_call_function_1_from_nlr_jump_callback(void *ctx_in); static inline void mp_thread_init_state(mp_state_thread_t *ts, size_t stack_size, mp_obj_dict_t *locals, mp_obj_dict_t *globals) { mp_thread_set_state(ts); - mp_stack_set_top(ts + 1); // need to include ts in root-pointer scan - mp_stack_set_limit(stack_size); + mp_cstack_init_with_top(ts + 1, stack_size); // need to include ts in root-pointer scan // GC starts off unlocked ts->gc_lock_depth = 0; diff --git a/py/stackctrl.c b/py/stackctrl.c index c2566ebad92b8..303e9cffff16f 100644 --- a/py/stackctrl.c +++ b/py/stackctrl.c @@ -24,7 +24,12 @@ * THE SOFTWARE. */ +// This API is deprecated, please use py/cstack.h instead + #include "py/runtime.h" + +#if !MICROPY_PREVIEW_VERSION_2 + #include "py/stackctrl.h" void mp_stack_ctrl_init(void) { @@ -62,3 +67,5 @@ void mp_stack_check(void) { } #endif // MICROPY_STACK_CHECK + +#endif // !MICROPY_PREVIEW_VERSION_2 diff --git a/py/stackctrl.h b/py/stackctrl.h index c21288b2b1f57..9bf7330ccc3f1 100644 --- a/py/stackctrl.h +++ b/py/stackctrl.h @@ -26,8 +26,12 @@ #ifndef MICROPY_INCLUDED_PY_STACKCTRL_H #define MICROPY_INCLUDED_PY_STACKCTRL_H +// This API is deprecated, please use py/cstack.h instead + #include "py/mpconfig.h" +#if !MICROPY_PREVIEW_VERSION_2 + void mp_stack_ctrl_init(void); void mp_stack_set_top(void *top); mp_uint_t mp_stack_usage(void); @@ -43,6 +47,8 @@ void mp_stack_check(void); #define mp_stack_set_limit(limit) (void)(limit) #define MP_STACK_CHECK() -#endif +#endif // MICROPY_STACK_CHECK + +#endif // !MICROPY_PREVIEW_VERSION_2 #endif // MICROPY_INCLUDED_PY_STACKCTRL_H From e3955f421d10111271dfb6a2774e742ed01c1d7b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 6 Aug 2024 16:13:33 +1000 Subject: [PATCH 0189/1300] esp32: Fix thread stack limit margin, change to new cstack API. This change moves that complexity out into the stack checker and fixes the bug where stack margin wasn't set correctly by ESP32-C3 threads. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/main.c | 12 ++---------- ports/esp32/mpconfigport.h | 5 +++++ ports/esp32/mpthreadport.c | 5 +---- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index ccec6c8cd81f4..ea641e6685c35 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -41,7 +41,7 @@ #include "esp_log.h" #include "esp_psram.h" -#include "py/stackctrl.h" +#include "py/cstack.h" #include "py/nlr.h" #include "py/compile.h" #include "py/runtime.h" @@ -71,13 +71,6 @@ // MicroPython runs as a task under FreeRTOS #define MP_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1) -// Set the margin for detecting stack overflow, depending on the CPU architecture. -#if CONFIG_IDF_TARGET_ESP32C3 -#define MP_TASK_STACK_LIMIT_MARGIN (2048) -#else -#define MP_TASK_STACK_LIMIT_MARGIN (1024) -#endif - typedef struct _native_code_node_t { struct _native_code_node_t *next; uint32_t data[]; @@ -132,8 +125,7 @@ void mp_task(void *pvParameter) { soft_reset: // initialise the stack pointer for the main thread - mp_stack_set_top((void *)sp); - mp_stack_set_limit(MICROPY_TASK_STACK_SIZE - MP_TASK_STACK_LIMIT_MARGIN); + mp_cstack_init_with_top((void *)sp, MICROPY_TASK_STACK_SIZE); gc_init(mp_task_heap, mp_task_heap + MICROPY_GC_INITIAL_HEAP_SIZE); mp_init(); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib)); diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 3349e56e46d41..40b3f11b4a433 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -62,6 +62,11 @@ // Python internal features #define MICROPY_READER_VFS (1) #define MICROPY_ENABLE_GC (1) +#if CONFIG_IDF_TARGET_ARCH_RISCV // RISC-V SoCs use more stack than Xtensa +#define MICROPY_STACK_CHECK_MARGIN (2048) // This may be unnecessarily conservative +#else +#define MICROPY_STACK_CHECK_MARGIN (1024) +#endif #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c index 34fef9f7beace..eac1e5ea3668d 100644 --- a/ports/esp32/mpthreadport.c +++ b/ports/esp32/mpthreadport.c @@ -38,7 +38,7 @@ #if MICROPY_PY_THREAD #define MP_THREAD_MIN_STACK_SIZE (4 * 1024) -#define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + 1024) +#define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + MICROPY_STACK_CHECK_MARGIN) #define MP_THREAD_PRIORITY (ESP_TASK_PRIO_MIN + 1) #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) && !CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP @@ -160,9 +160,6 @@ mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_s th->next = thread; thread = th; - // adjust the stack_size to provide room to recover from hitting the limit - *stack_size -= 1024; - mp_thread_mutex_unlock(&thread_mutex); return (mp_uint_t)th->id; From 80616aee71afedc3c7bfb013d87d8102981aa6b2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 6 Aug 2024 15:47:25 +1000 Subject: [PATCH 0190/1300] tests/run-tests.py: Enable stress tests on esp32 port. Now passing on ESP32-S3 and ESP32-C3. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/run-tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index e7ba0de565cfe..83344714c0375 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -1121,7 +1121,7 @@ def main(): elif args.target == "rp2": test_dirs += ("float", "stress", "inlineasm", "thread", "ports/rp2") elif args.target == "esp32": - test_dirs += ("float", "thread") + test_dirs += ("float", "stress", "thread") elif args.target in ("esp8266", "minimal", "nrf"): test_dirs += ("float",) elif args.target == "wipy": From fbc19596f052016dee10a6af56441f4fb532243c Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 6 Aug 2024 15:52:54 +1000 Subject: [PATCH 0191/1300] rp2: Switch to use new cstack API for stack limit checks. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/main.c | 5 ++--- ports/rp2/mpconfigport.h | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 0f2721f174961..1ce1d2bf31d65 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -27,11 +27,11 @@ #include #include "py/compile.h" +#include "py/cstack.h" #include "py/runtime.h" #include "py/gc.h" #include "py/mperrno.h" #include "py/mphal.h" -#include "py/stackctrl.h" #include "extmod/modbluetooth.h" #include "extmod/modnetwork.h" #include "shared/readline/readline.h" @@ -114,8 +114,7 @@ int main(int argc, char **argv) { mp_hal_time_ns_set_from_rtc(); // Initialise stack extents and GC heap. - mp_stack_set_top(&__StackTop); - mp_stack_set_limit(&__StackTop - &__StackBottom - 256); + mp_cstack_init_with_top(&__StackTop, &__StackTop - &__StackBottom); gc_init(&__GcHeapStart, &__GcHeapEnd); #if MICROPY_PY_LWIP diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 6ef994bb83272..3399212aacaa6 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -81,6 +81,7 @@ #define MICROPY_TRACKED_ALLOC (MICROPY_SSL_MBEDTLS || MICROPY_BLUETOOTH_BTSTACK) #define MICROPY_READER_VFS (1) #define MICROPY_ENABLE_GC (1) +#define MICROPY_STACK_CHECK_MARGIN (256) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) From a6fa85d8f96897863546423b2bbfc2c24237040f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 13 Aug 2024 16:12:25 +1000 Subject: [PATCH 0192/1300] unix: Switch stack limit check to new cstack API. Necessary to pass CI when testing the V2 preview APIs. Also adds an extra coverage test for the legacy stackctrl API, to maintain coverage and check for any regression. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/unix/coverage.c | 27 ++++++++++++++++++++++++++ ports/unix/main.c | 20 +++++++++---------- tests/ports/unix/extra_coverage.py.exp | 2 ++ 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 99a6d8c8c11f1..67052ea704e7c 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -7,6 +7,7 @@ #include "py/objint.h" #include "py/objstr.h" #include "py/runtime.h" +#include "py/stackctrl.h" #include "py/gc.h" #include "py/repl.h" #include "py/mpz.h" @@ -739,6 +740,32 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_int(MP_OBJ_NEW_SMALL_INT(1)), mp_obj_is_int(mp_obj_new_int_from_ll(1))); } + // Legacy stackctrl.h API, this has been replaced by cstack.h + { + mp_printf(&mp_plat_print, "# stackctrl\n"); + char *old_stack_top = MP_STATE_THREAD(stack_top); + size_t old_stack_limit = 0; + size_t new_stack_limit = SIZE_MAX; + #if MICROPY_STACK_CHECK + old_stack_limit = MP_STATE_THREAD(stack_limit); + MP_STACK_CHECK(); + #endif + + mp_stack_ctrl_init(); // Will set stack top incorrectly + mp_stack_set_top(old_stack_top); // ... and restore it + + #if MICROPY_STACK_CHECK + mp_stack_set_limit(MP_STATE_THREAD(stack_limit)); + MP_STACK_CHECK(); + new_stack_limit = MP_STATE_THREAD(stack_limit); + #endif + + // Nothing should have changed + mp_printf(&mp_plat_print, "%d %d\n", + old_stack_top == MP_STATE_THREAD(stack_top), + MICROPY_STACK_CHECK == 0 || old_stack_limit == new_stack_limit); + } + mp_printf(&mp_plat_print, "# end coverage.c\n"); mp_obj_streamtest_t *s = mp_obj_malloc(mp_obj_streamtest_t, &mp_type_stest_fileio); diff --git a/ports/unix/main.c b/ports/unix/main.c index d9ee6eec447b2..e732105c792b7 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -44,7 +44,7 @@ #include "py/repl.h" #include "py/gc.h" #include "py/objstr.h" -#include "py/stackctrl.h" +#include "py/cstack.h" #include "py/mphal.h" #include "py/mpthread.h" #include "extmod/misc.h" @@ -468,12 +468,20 @@ int main(int argc, char **argv) { #if MICROPY_PY_THREAD mp_thread_init(); #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 + // We should capture stack top ASAP after start, and it should be // captured guaranteedly before any other stack variables are allocated. // For this, actual main (renamed main_) should not be inlined into // this function. main_() itself may have other functions inlined (with // their own stack variables), that's why we need this main/main_ split. - mp_stack_ctrl_init(); + mp_cstack_init_with_sp_here(stack_size); return main_(argc, argv); } @@ -492,14 +500,6 @@ MP_NOINLINE int main_(int argc, char **argv) { signal(SIGPIPE, SIG_IGN); #endif - // Define a reasonable stack limit to detect stack overflow. - mp_uint_t stack_limit = 40000 * (sizeof(void *) / 4); - #if defined(__arm__) && !defined(__thumb2__) - // ARM (non-Thumb) architectures require more stack. - stack_limit *= 2; - #endif - mp_stack_set_limit(stack_limit); - pre_process_options(argc, argv); #if MICROPY_ENABLE_GC diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp index 5e806ebe57ccf..a2b11638a35ae 100644 --- a/tests/ports/unix/extra_coverage.py.exp +++ b/tests/ports/unix/extra_coverage.py.exp @@ -164,6 +164,8 @@ pop all: 1 2 4 5 1 1 0 0 1 1 +# stackctrl +1 1 # end coverage.c 0123456789 b'0123456789' 7300 From fb4ae1eeec521d3d0caf09ba234df3a4e98902e2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 14 Aug 2024 14:20:36 +1000 Subject: [PATCH 0193/1300] test/extmod: Fix machine_spi_rate test on ESP32-C3. Update to the test added in 1e98c4cb75bf3015d816455fc46ba28d5bcd9275, changes the SPI pins for ESP32-C3 (IO 18 and 19 are the native USB pins). Signed-off-by: Angus Gratton --- tests/extmod/machine_spi_rate.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/extmod/machine_spi_rate.py b/tests/extmod/machine_spi_rate.py index df9e3fbd0de85..6ee4619fa1193 100644 --- a/tests/extmod/machine_spi_rate.py +++ b/tests/extmod/machine_spi_rate.py @@ -21,11 +21,10 @@ elif "rp2" in sys.platform: spi_instances = ((0, Pin(18), Pin(19), Pin(16)),) elif "esp32" in sys.platform: - spi_instances = [ - (1, Pin(18), Pin(19), Pin(21)), - ] - if "ESP32C3" not in str(sys.implementation): - spi_instances.append((2, Pin(18), Pin(19), Pin(21))) + if "ESP32C3" in str(sys.implementation): + spi_instances = ((1, Pin(4), Pin(5), Pin(6)),) + else: + spi_instances = ((1, Pin(18), Pin(19), Pin(21)), (2, Pin(18), Pin(19), Pin(21))) else: print("Please add support for this test on this platform.") raise SystemExit From a4f9c0cc2ac913030458a9fd11f1d49b288cce18 Mon Sep 17 00:00:00 2001 From: Andrea Milazzo Date: Tue, 12 Mar 2024 09:45:19 +0100 Subject: [PATCH 0194/1300] esp32/adc: Add support for v5.2.1 calibration api. This new calibration routine exists for S3 in v5.1.1. It works for all platforms in 5.2.1. Signed-off-by: Andrew Leech --- ports/esp32/adc.c | 36 +++++++++++++++++++++++++++++++++--- ports/esp32/adc.h | 5 +++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/ports/esp32/adc.c b/ports/esp32/adc.c index c5886624ecb5a..7c9e0cfad622f 100644 --- a/ports/esp32/adc.c +++ b/ports/esp32/adc.c @@ -28,6 +28,7 @@ #include "py/mphal.h" #include "adc.h" #include "driver/adc.h" +#include "esp_adc/adc_cali_scheme.h" #define DEFAULT_VREF 1100 @@ -63,30 +64,59 @@ void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits) { adc1_config_width(self->width); } for (adc_atten_t atten = ADC_ATTEN_DB_0; atten < ADC_ATTEN_MAX; atten++) { + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 1) + if (self->handle[atten] != NULL) { + adc_cali_curve_fitting_config_t cali_config = { + .unit_id = self->unit_id, + .atten = atten, + .bitwidth = self->width, + }; + check_esp_err(adc_cali_create_scheme_curve_fitting(&cali_config, self->handle[atten])); + } + #else if (self->characteristics[atten] != NULL) { esp_adc_cal_characterize(self->unit_id, atten, self->width, DEFAULT_VREF, self->characteristics[atten]); } + #endif } } mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id) { - int raw; + int raw = 0; if (self->unit_id == ADC_UNIT_1) { raw = adc1_get_raw(channel_id); } else { + #if (SOC_ADC_PERIPH_NUM >= 2) check_esp_err(adc2_get_raw(channel_id, self->width, &raw)); + #endif } return raw; } mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { int raw = madcblock_read_helper(self, channel_id); + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 1) + adc_cali_handle_t *adc_handle = self->handle[atten]; + if (adc_handle == NULL) { + adc_cali_curve_fitting_config_t cali_config = { + .unit_id = self->unit_id, + .atten = atten, + .bitwidth = self->width, + }; + adc_handle = malloc(sizeof(adc_cali_handle_t)); + check_esp_err(adc_cali_create_scheme_curve_fitting(&cali_config, adc_handle)); + self->handle[atten] = adc_handle; + } + int uv; + check_esp_err(adc_cali_raw_to_voltage(*adc_handle, raw, &uv)); + #else esp_adc_cal_characteristics_t *adc_chars = self->characteristics[atten]; if (adc_chars == NULL) { adc_chars = malloc(sizeof(esp_adc_cal_characteristics_t)); esp_adc_cal_characterize(self->unit_id, atten, self->width, DEFAULT_VREF, adc_chars); self->characteristics[atten] = adc_chars; } - mp_int_t uv = esp_adc_cal_raw_to_voltage(raw, adc_chars) * 1000; - return uv; + mp_int_t uv = esp_adc_cal_raw_to_voltage(raw, adc_chars); + #endif + return (mp_int_t)uv * 1000; } diff --git a/ports/esp32/adc.h b/ports/esp32/adc.h index ae5c0d3c378c5..61771255d3dc0 100644 --- a/ports/esp32/adc.h +++ b/ports/esp32/adc.h @@ -30,6 +30,7 @@ #include "py/runtime.h" #include "esp_adc_cal.h" +#include "esp_adc/adc_cali_scheme.h" #define ADC_ATTEN_MAX SOC_ADC_ATTEN_NUM @@ -38,7 +39,11 @@ typedef struct _machine_adc_block_obj_t { adc_unit_t unit_id; mp_int_t bits; adc_bits_width_t width; + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 1) + adc_cali_handle_t *handle[ADC_ATTEN_MAX]; + #else esp_adc_cal_characteristics_t *characteristics[ADC_ATTEN_MAX]; + #endif } machine_adc_block_obj_t; typedef struct _machine_adc_obj_t { From 052693e4495354daabb4d61e97121283334c6665 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 23 Jul 2024 11:09:27 +1000 Subject: [PATCH 0195/1300] esp32/boards: Reduce IRAM usage. Disable unnecessary IRAM ISR functionality. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/boards/sdkconfig.base | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index fef25fe3087f5..ca36206d1bf31 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -99,6 +99,10 @@ CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv" # To reduce iRAM usage CONFIG_ESP32_WIFI_IRAM_OPT=n CONFIG_ESP32_WIFI_RX_IRAM_OPT=n +CONFIG_SPI_MASTER_ISR_IN_IRAM=n +CONFIG_SPI_SLAVE_ISR_IN_IRAM=n +CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=n +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=n # Legacy ADC Calibration Configuration # Only on: ESP32 From 74d04c026220b301a1966dbb0d6064d06132999e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 23 Jul 2024 11:40:45 +1000 Subject: [PATCH 0196/1300] esp32/adc: Use new ADC calibration API in all cases. Replaces the deprecated ESP32 calibration API with the "line" method instead. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/adc.c | 64 +++++++++++++++++++---------------------------- ports/esp32/adc.h | 6 +---- 2 files changed, 27 insertions(+), 43 deletions(-) diff --git a/ports/esp32/adc.c b/ports/esp32/adc.c index 7c9e0cfad622f..91db9ec525fe1 100644 --- a/ports/esp32/adc.c +++ b/ports/esp32/adc.c @@ -63,22 +63,6 @@ void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits) { if (self->unit_id == ADC_UNIT_1) { adc1_config_width(self->width); } - for (adc_atten_t atten = ADC_ATTEN_DB_0; atten < ADC_ATTEN_MAX; atten++) { - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 1) - if (self->handle[atten] != NULL) { - adc_cali_curve_fitting_config_t cali_config = { - .unit_id = self->unit_id, - .atten = atten, - .bitwidth = self->width, - }; - check_esp_err(adc_cali_create_scheme_curve_fitting(&cali_config, self->handle[atten])); - } - #else - if (self->characteristics[atten] != NULL) { - esp_adc_cal_characterize(self->unit_id, atten, self->width, DEFAULT_VREF, self->characteristics[atten]); - } - #endif - } } mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id) { @@ -93,30 +77,34 @@ mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t chan return raw; } -mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { - int raw = madcblock_read_helper(self, channel_id); - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 1) - adc_cali_handle_t *adc_handle = self->handle[atten]; - if (adc_handle == NULL) { - adc_cali_curve_fitting_config_t cali_config = { - .unit_id = self->unit_id, - .atten = atten, - .bitwidth = self->width, - }; - adc_handle = malloc(sizeof(adc_cali_handle_t)); - check_esp_err(adc_cali_create_scheme_curve_fitting(&cali_config, adc_handle)); - self->handle[atten] = adc_handle; +static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten) { + if (self->handle[atten] != NULL) { + return ESP_OK; } - int uv; - check_esp_err(adc_cali_raw_to_voltage(*adc_handle, raw, &uv)); + + #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED + adc_cali_curve_fitting_config_t cali_config = { + .unit_id = self->unit_id, + .atten = atten, + .bitwidth = self->width, + }; + return adc_cali_create_scheme_curve_fitting(&cali_config, &self->handle[atten]); #else - esp_adc_cal_characteristics_t *adc_chars = self->characteristics[atten]; - if (adc_chars == NULL) { - adc_chars = malloc(sizeof(esp_adc_cal_characteristics_t)); - esp_adc_cal_characterize(self->unit_id, atten, self->width, DEFAULT_VREF, adc_chars); - self->characteristics[atten] = adc_chars; - } - mp_int_t uv = esp_adc_cal_raw_to_voltage(raw, adc_chars); + adc_cali_line_fitting_config_t cali_config = { + .unit_id = self->unit_id, + .atten = atten, + .bitwidth = self->width, + }; + return adc_cali_create_scheme_line_fitting(&cali_config, &self->handle[atten]); #endif +} + +mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { + int raw = madcblock_read_helper(self, channel_id); + int uv; + + check_esp_err(ensure_adc_calibration(self, atten)); + check_esp_err(adc_cali_raw_to_voltage(self->handle[atten], raw, &uv)); + return (mp_int_t)uv * 1000; } diff --git a/ports/esp32/adc.h b/ports/esp32/adc.h index 61771255d3dc0..5688e0a29a706 100644 --- a/ports/esp32/adc.h +++ b/ports/esp32/adc.h @@ -39,11 +39,7 @@ typedef struct _machine_adc_block_obj_t { adc_unit_t unit_id; mp_int_t bits; adc_bits_width_t width; - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 1) - adc_cali_handle_t *handle[ADC_ATTEN_MAX]; - #else - esp_adc_cal_characteristics_t *characteristics[ADC_ATTEN_MAX]; - #endif + adc_cali_handle_t handle[ADC_ATTEN_MAX]; } machine_adc_block_obj_t; typedef struct _machine_adc_obj_t { From 10601b04eabb5619e51bdd5f04c7d77ea8e201eb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 23 Jul 2024 11:41:25 +1000 Subject: [PATCH 0197/1300] esp32/boards: Build using newlib nano formatting functions. Saves code size, MicroPython doesn't appear to rely on any of the missing formatters (64-bit integers, c99-style named arguments). Signed-off-by: Angus Gratton --- ports/esp32/boards/sdkconfig.base | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index ca36206d1bf31..84a423fa0ab7e 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -121,3 +121,9 @@ CONFIG_ETH_USE_SPI_ETHERNET=y CONFIG_ETH_SPI_ETHERNET_W5500=y CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL=y CONFIG_ETH_SPI_ETHERNET_DM9051=y + +# Using newlib "nano" formatting saves size on SoCs where "nano" formatting +# functions are in ROM. Note some newer chips (c2,c6) have "full" newlib +# formatting in ROM instead and should override this, check +# ESP_ROM_HAS_NEWLIB_NANO_FORMAT. +CONFIG_NEWLIB_NANO_FORMAT=y From 35a056ad9c9374472adf3578f9d6f0bfd2734674 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 23 Jul 2024 12:06:20 +1000 Subject: [PATCH 0198/1300] esp32/tools: Add metrics_esp32 size comparison script. Signed-off-by: Angus Gratton --- ports/esp32/tools/metrics_esp32.py | 192 +++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100755 ports/esp32/tools/metrics_esp32.py diff --git a/ports/esp32/tools/metrics_esp32.py b/ports/esp32/tools/metrics_esp32.py new file mode 100755 index 0000000000000..66a6a588ba212 --- /dev/null +++ b/ports/esp32/tools/metrics_esp32.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# MIT license; Copyright (c) 2024 Angus Gratton +# +# This is a utility script for MicroPython maintainers, similar to tools/metrics.py +# but particular to this port. It's for measuring the impact of an ESP-IDF update or +# config change at a high level. +# +# Specifically, it builds the esp32 MicroPython port for a collection of boards +# and outputs a Markdown table of binary sizes, static IRAM size, and static +# DRAM size (the latter generally inversely correlates to free heap at runtime.) +# +# To use: +# +# 1) Need to not be in an ESP-IDF venv already (i.e. don't source export.sh), +# but IDF_PATH has to be set. +# +# 2) Choose the versions you want to test and the board/variant pairs by +# editing the tuples below. +# +# 3) The IDF install script sometimes fails if it has to downgrade a package +# within a minor version. The "nuclear option" is to delete all the install +# environments and have this script recreate them as it runs: +# rm -rf ~/.espressif/python_env/* +# +# 4) Run this script from the ports/esp32 directory, i.e.: +# ./tools/metrics_esp32.py +# +# 5) If all goes well, it will run for a while and then print a Markdown +# formatted table of binary sizes, sorted by board+variant. +# +# Note that for ESP32-S3 and C3, IRAM and DRAM are exchangeable so the IRAM size +# column of the table is really D/IRAM. +import os +import re +import sys +import subprocess +from dataclasses import dataclass + +IDF_VERS = ("v5.2.2",) + +BUILDS = ( + ("ESP32_GENERIC", ""), + ("ESP32_GENERIC", "D2WD"), + ("ESP32_GENERIC", "SPIRAM"), + ("ESP32_GENERIC_S3", ""), + ("ESP32_GENERIC_S3", "SPIRAM_OCT"), +) + + +@dataclass +class BuildSizes: + idf_ver: str + board: str + variant: str + bin_size: str = "" + dram_size: str = "" + iram_size: str = "" + + def print_summary(self, include_ver=False): + print(f"BOARD={self.board} BOARD_VARIANT={self.variant}") + if include_ver: + print(f"IDF_VER {self.idf_ver}") + print(f"Binary size: {self.bin_size}") + print(f"IRAM size: {self.iram_size}") + print(f"DRAM size: {self.dram_size}") + + def print_table_heading(): + print( + "| BOARD | BOARD_VARIANT | IDF Version | Binary Size | Static IRAM Size | Static DRAM Size |" + ) + print( + "|-------|---------------|-------------|-------------|------------------|------------------|" + ) + + def print_table_row(self, print_board): + print( + "| " + + " | ".join( + ( + self.board if print_board else "", + self.variant if print_board else "", + self.idf_ver, + self.bin_size, + self.iram_size, + self.dram_size, + ) + ) + + " |" + ) + + def __lt__(self, other): + """sort by board, then variant, then IDF version to get an easy + to compare table""" + return (self.board, self.variant, self.idf_ver) < ( + other.board, + other.variant, + other.idf_ver, + ) + + def build_dir(self): + if self.variant: + return f"build-{self.board}_{self.variant}" + else: + return f"build-{self.board}" + + def run_make(self, target): + env = dict(os.environ) + env["BOARD"] = self.board + env["BOARD_VARIANT"] = self.variant + + try: + # IDF version changes as we go, so re-export the environment each time + cmd = f"source $IDF_PATH/export.sh; make {target}" + return subprocess.check_output( + cmd, shell=True, env=env, stderr=subprocess.STDOUT + ).decode() + except subprocess.CalledProcessError as e: + err_file = f"{self.build_dir()}/make-{target}-failed-{self.idf_ver}.log" + print(f"'make {target}' failed, writing to log to {err_file}", file=sys.stderr) + with open(err_file, "w") as f: + f.write(e.output.decode()) + raise + + def make_size(self): + try: + size_out = self.run_make("size") + # "Used static DRAM:" or "Used stat D/IRAM:" + RE_DRAM = r"Used stat(?:ic)? D.*: *(\d+) bytes" + RE_IRAM = r"Used static IRAM: *(\d+) bytes" + RE_BIN = r"Total image size: *(\d+) bytes" + self.dram_size = re.search(RE_DRAM, size_out).group(1) + self.iram_size = re.search(RE_IRAM, size_out).group(1) + self.bin_size = re.search(RE_BIN, size_out).group(1) + except subprocess.CalledProcessError: + self.bin_size = "build failed" + + +def main(do_clean): + if "IDF_PATH" not in os.environ: + raise RuntimeError("IDF_PATH must be set") + + sizes = [] + for idf_ver in IDF_VERS: + switch_ver(idf_ver) + for board, variant in BUILDS: + print(f"Building '{board}'/'{variant}'...", file=sys.stderr) + result = BuildSizes(idf_ver, board, variant) + result.run_make("clean") + result.make_size() + result.print_summary() + sizes.append(result) + + # print everything again as a table sorted by board+variant + last_bv = "" + BuildSizes.print_table_heading() + for build_sizes in sorted(sizes): + bv = (build_sizes.board, build_sizes.variant) + build_sizes.print_table_row(last_bv != bv) + last_bv = bv + + +def idf_git(*commands): + try: + subprocess.check_output( + ["git"] + list(commands), cwd=os.environ["IDF_PATH"], stderr=subprocess.STDOUT + ) + except subprocess.CalledProcessError as e: + print(f"git {' '.join(commands)} failed:") + print(e.output.decode()) + raise + + +def idf_install(): + try: + subprocess.check_output( + ["bash", "install.sh"], cwd=os.environ["IDF_PATH"], stderr=subprocess.STDOUT + ) + except subprocess.CalledProcessError as e: + print("IDF install.sh failed:") + print(e.output.decode()) + raise + + +def switch_ver(idf_ver): + print(f"Switching version to {idf_ver}...", file=sys.stderr) + idf_git("switch", "--detach", idf_ver) + idf_git("submodule", "update", "--init", "--recursive") + idf_install() + + +if __name__ == "__main__": + main("--no-clean" not in sys.argv) From fbb02d3aeeaa9da082f3ccd411d8e164a526046e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 23 Jul 2024 11:57:13 +1000 Subject: [PATCH 0199/1300] esp32: Add support for ESP-IDF v5.2.2. Keeping older versions, however if the update goes well then these may be dropped in the future. Signed-off-by: Angus Gratton --- ports/esp32/README.md | 8 ++++---- tools/ci.sh | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 2e03c55d3de18..a04ad0c6eea1b 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -28,7 +28,7 @@ manage the ESP32 microcontroller, as well as a way to manage the required build environment and toolchains needed to build the firmware. The ESP-IDF changes quickly and MicroPython only supports certain versions. -Currently MicroPython supports v5.0.4, v5.0.5, v5.1.2, v5.2.0. +Currently MicroPython supports v5.0.4, v5.0.5, v5.1.2, v5.2.0, v5.2.2. To install the ESP-IDF the full instructions can be found at the [Espressif Getting Started guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#installation-step-by-step). @@ -46,10 +46,10 @@ The steps to take are summarised below. To check out a copy of the IDF use git clone: ```bash -$ git clone -b v5.0.4 --recursive https://github.com/espressif/esp-idf.git +$ git clone -b v5.2.2 --recursive https://github.com/espressif/esp-idf.git ``` -You can replace `v5.0.4` with any other supported version. +You can replace `v5.2.2` with any other supported version. (You don't need a full recursive clone; see the `ci_esp32_setup` function in `tools/ci.sh` in this repository for more detailed set-up commands.) @@ -58,7 +58,7 @@ MicroPython and update the submodules using: ```bash $ cd esp-idf -$ git checkout v5.0.4 +$ git checkout v5.2.2 $ git submodule update --init --recursive ``` diff --git a/tools/ci.sh b/tools/ci.sh index b0a1022c3b195..03b6bf59aed4a 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -116,7 +116,7 @@ function ci_cc3200_build { # ports/esp32 # GitHub tag of ESP-IDF to use for CI (note: must be a tag or a branch) -IDF_VER=v5.0.4 +IDF_VER=v5.2.2 export IDF_CCACHE_ENABLE=1 From 0a11832cddbeed57bf0267afa0c833584817312d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 30 Jul 2024 10:39:47 +1000 Subject: [PATCH 0200/1300] esp32: Use the ESP-IDF default esp_console config for ESP32-C3. The ESP-IDF default on C3 is primary UART0, secondary USB serial/jtag. Previously MicroPython configured the primary as USB Serial/JTAG and manually worked with the UART0 console. However UART0 console stopped working this way in v5.2.2. The big change is that CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG is no longer set, as primary console is UART0. However CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG is set and IDF provides a macro CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED which is set if either primary or secondary esp_console is USB serial/jtag. So need to use that macro instead. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb | 2 -- ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board | 2 -- ports/esp32/machine_pin.c | 2 +- ports/esp32/machine_pin.h | 2 +- ports/esp32/main.c | 2 +- ports/esp32/mphalport.c | 6 +++--- ports/esp32/uart.h | 2 +- ports/esp32/usb.c | 4 ++-- ports/esp32/usb_serial_jtag.c | 4 ++-- 9 files changed, 11 insertions(+), 15 deletions(-) diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb b/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb index d9e7c7f61f620..9e75d07de1670 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb +++ b/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb @@ -3,5 +3,3 @@ CONFIG_ESP32C3_BROWNOUT_DET=y CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_7= CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_4=y CONFIG_ESP32C3_BROWNOUT_DET_LVL=4 -CONFIG_ESP_CONSOLE_UART_DEFAULT= -CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y diff --git a/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board b/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board index d9e7c7f61f620..9e75d07de1670 100644 --- a/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board +++ b/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board @@ -3,5 +3,3 @@ CONFIG_ESP32C3_BROWNOUT_DET=y CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_7= CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_4=y CONFIG_ESP32C3_BROWNOUT_DET_LVL=4 -CONFIG_ESP_CONSOLE_UART_DEFAULT= -CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index 1e7b86baeb379..17c01fc7b38e6 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -159,7 +159,7 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ } } - #if CONFIG_IDF_TARGET_ESP32C3 + #if CONFIG_IDF_TARGET_ESP32C3 && CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED if (index == 18 || index == 19) { CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE); } diff --git a/ports/esp32/machine_pin.h b/ports/esp32/machine_pin.h index 53f9c6bdad7cd..b46998725326c 100644 --- a/ports/esp32/machine_pin.h +++ b/ports/esp32/machine_pin.h @@ -87,7 +87,7 @@ #define MICROPY_HW_ENABLE_GPIO11 (1) #define MICROPY_HW_ENABLE_GPIO12 (1) #define MICROPY_HW_ENABLE_GPIO13 (1) -#if !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG +#if !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED #define MICROPY_HW_ENABLE_GPIO18 (1) #define MICROPY_HW_ENABLE_GPIO19 (1) #endif diff --git a/ports/esp32/main.c b/ports/esp32/main.c index ea641e6685c35..330ba64b467c7 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -99,7 +99,7 @@ void mp_task(void *pvParameter) { #if MICROPY_PY_THREAD mp_thread_init(pxTaskGetStackStart(NULL), MICROPY_TASK_STACK_SIZE / sizeof(uintptr_t)); #endif - #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG + #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED usb_serial_jtag_init(); #elif CONFIG_USB_OTG_SUPPORTED usb_init(); diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index fd1cbcdb985f1..df966b3a27813 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -100,7 +100,7 @@ void check_esp_err_(esp_err_t code, const char *func, const int line, const char uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { uintptr_t ret = 0; - #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG + #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED usb_serial_jtag_poll_rx(); #endif if ((poll_flags & MP_STREAM_POLL_RD) && stdin_ringbuf.iget != stdin_ringbuf.iput) { @@ -114,7 +114,7 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { int mp_hal_stdin_rx_chr(void) { for (;;) { - #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG + #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED usb_serial_jtag_poll_rx(); #endif int c = ringbuf_get(&stdin_ringbuf); @@ -133,7 +133,7 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { if (release_gil) { MP_THREAD_GIL_EXIT(); } - #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG + #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED usb_serial_jtag_tx_strn(str, len); did_write = true; #elif CONFIG_USB_OTG_SUPPORTED diff --git a/ports/esp32/uart.h b/ports/esp32/uart.h index 3d88eed8250fb..13c5e88307287 100644 --- a/ports/esp32/uart.h +++ b/ports/esp32/uart.h @@ -30,7 +30,7 @@ // Whether to enable the REPL on a UART. #ifndef MICROPY_HW_ENABLE_UART_REPL -#define MICROPY_HW_ENABLE_UART_REPL (!CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG) +#define MICROPY_HW_ENABLE_UART_REPL (!CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED) #endif #if MICROPY_HW_ENABLE_UART_REPL diff --git a/ports/esp32/usb.c b/ports/esp32/usb.c index 2a3a2595dea0d..5a0e6b8a92091 100644 --- a/ports/esp32/usb.c +++ b/ports/esp32/usb.c @@ -28,7 +28,7 @@ #include "py/mphal.h" #include "usb.h" -#if CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG +#if CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED #include "esp_timer.h" #ifndef NO_QSTR @@ -100,4 +100,4 @@ void usb_tx_strn(const char *str, size_t len) { } } -#endif // CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG +#endif // CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED diff --git a/ports/esp32/usb_serial_jtag.c b/ports/esp32/usb_serial_jtag.c index f4b4b7cb95834..f1148ab197ebb 100644 --- a/ports/esp32/usb_serial_jtag.c +++ b/ports/esp32/usb_serial_jtag.c @@ -28,7 +28,7 @@ #include "py/mphal.h" #include "usb_serial_jtag.h" -#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG +#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED #include "hal/usb_serial_jtag_ll.h" #include "esp_intr_alloc.h" @@ -117,4 +117,4 @@ void usb_serial_jtag_tx_strn(const char *str, size_t len) { } } -#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG +#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED From e71a324c149b0b03c3ad6080aab1719916ed68af Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 7 Aug 2024 12:17:09 +1000 Subject: [PATCH 0201/1300] esp32: Restore ESP32-C3 brownout detector settings to IDF defaults. Commit a66bd7a48925d318e1dd6ada756df947ffacdc12 added the ESP32_GENERIC_C3_USB board (now merged with ESP32_GENERIC_C3) and changed the brownout detector from the default level 7 (~2.51V) to level 4 (~2.92V). Raising the level again seems to fix random BOD resets on some of the cheaper ESP32-C3 dev boards (that likely skimp on power supply capacitance). Specifically, this change prevents random resets running multi_bluetooth tests on ESP32-C3 "SuperMini" board. Also removed from the LOLIN_C3_MINI board as it seems this config is a copy of the generic one. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb | 4 ---- ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board | 4 ---- 2 files changed, 8 deletions(-) diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb b/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb index 9e75d07de1670..44838c5349cc4 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb +++ b/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb @@ -1,5 +1 @@ CONFIG_ESP32C3_REV_MIN_3=y -CONFIG_ESP32C3_BROWNOUT_DET=y -CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_7= -CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_4=y -CONFIG_ESP32C3_BROWNOUT_DET_LVL=4 diff --git a/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board b/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board index 9e75d07de1670..44838c5349cc4 100644 --- a/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board +++ b/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board @@ -1,5 +1 @@ CONFIG_ESP32C3_REV_MIN_3=y -CONFIG_ESP32C3_BROWNOUT_DET=y -CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_7= -CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_4=y -CONFIG_ESP32C3_BROWNOUT_DET_LVL=4 From 092078852e83811a629028b09cde2374fdfd9103 Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Fri, 9 Aug 2024 14:58:48 +1000 Subject: [PATCH 0202/1300] esp32/boards: Remove BLE from list of features for ESP32-S2. Fixes issue #15618. Signed-off-by: Matt Trentini --- ports/esp32/boards/ESP32_GENERIC_S2/board.json | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/esp32/boards/ESP32_GENERIC_S2/board.json b/ports/esp32/boards/ESP32_GENERIC_S2/board.json index 35d06f2f7e6cc..6ebf16be1a21a 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S2/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_S2/board.json @@ -4,7 +4,6 @@ ], "docs": "", "features": [ - "BLE", "External Flash", "External RAM", "WiFi" From 0b75e180a3b9fe72681fb942873102cdf2f4cae1 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 10 Aug 2024 16:37:03 +0200 Subject: [PATCH 0203/1300] esp32/mphalport: Print debug strings even before the GIL is ready. If verbose debugging is enabled there is some stdout output happening before the GIL is ready (for example, GC initialisation), and the code assumed that no string printing occurred before the interpreter was fully initialised. Printing long strings would operate without holding the GIL, which would crash if string output would happen too early in the startup process. This commit addresses that issue, making sure verbose debugging output will work even before the interpreter is fully initialised (as if it is not yet ready there's no GIL to take care of). Also, the threshold that would indicate whether a string is "long" (and thus requiring a GIL release/lock operation) or not was hardcoded to 20 bytes. This commit makes that configurable, maintaining 20 bytes as a default. Fixes issue #15408. Signed-off-by: Alessandro Gatti --- ports/esp32/mpconfigport.h | 5 +++++ ports/esp32/mphalport.c | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 40b3f11b4a433..866b1bc25c5f4 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -314,3 +314,8 @@ void boardctrl_startup(void); #endif #endif #endif + +// The minimum string length threshold for string printing to stdout operations to be GIL-aware. +#ifndef MICROPY_PY_STRING_TX_GIL_THRESHOLD +#define MICROPY_PY_STRING_TX_GIL_THRESHOLD (20) +#endif diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index df966b3a27813..cc0e2ee7b4999 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -47,6 +47,10 @@ #include "usb_serial_jtag.h" #include "uart.h" +#if MICROPY_PY_STRING_TX_GIL_THRESHOLD < 0 +#error "MICROPY_PY_STRING_TX_GIL_THRESHOLD must be positive" +#endif + TaskHandle_t mp_main_task_handle; static uint8_t stdin_ringbuf_array[260]; @@ -129,7 +133,13 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { // Only release the GIL if many characters are being sent mp_uint_t ret = len; bool did_write = false; - bool release_gil = len > 20; + bool release_gil = len > MICROPY_PY_STRING_TX_GIL_THRESHOLD; + #if MICROPY_DEBUG_PRINTERS && MICROPY_DEBUG_VERBOSE && MICROPY_PY_THREAD_GIL + // If verbose debug output is enabled some strings are printed before the + // GIL mutex is set up. When that happens, no Python code is running and + // therefore the interpreter doesn't care about the GIL not being ready. + release_gil = release_gil && (MP_STATE_VM(gil_mutex).handle != NULL); + #endif if (release_gil) { MP_THREAD_GIL_EXIT(); } From 6565b3cb34954737428f98f0306b7e3cad146482 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 15 Aug 2024 16:00:08 +1000 Subject: [PATCH 0204/1300] esp32: Fix Python cstack size for bluetooth irq callbacks. This value should have been adjusted when the new cstack API was adopted in e3955f421d1, as otherwise the stack limit is too small especially on ESP32-C3 where the stack limit was 6144 - 2048 - 2048. Some extra margin is needed for bluetooth irq because invoke_irq_handler() isn't a top-level task function, NimBLE calls through multiple layers first. Measuring this overhead on IDF V5.2.2 (by putting an abort() in invoke_irq_handler() and then measuring the stack size) yielded 672 bytes on ESP32-S3 and 612 bytes on ESP32-C3, similar to the size reported in cd66aa05cf. Sticking with 1024 bytes for added safety margin. This means on Xtensa the total margin for the BLE task stays the same (2048 bytes) as before switching to cstack. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/mpconfigport.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 866b1bc25c5f4..723310b231b18 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -101,7 +101,10 @@ #define MICROPY_PY_BLUETOOTH (1) #define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS (1) #define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK (1) -#define MICROPY_PY_BLUETOOTH_SYNC_EVENT_STACK_SIZE (CONFIG_BT_NIMBLE_TASK_STACK_SIZE - 2048) +// Event stack size is the RTOS stack size minus an allowance for the stack used +// by the NimBLE functions that call into invoke_irq_handler(). +// MICROPY_STACK_CHECK_MARGIN is further subtracted from this value to set the stack limit. +#define MICROPY_PY_BLUETOOTH_SYNC_EVENT_STACK_SIZE (CONFIG_BT_NIMBLE_TASK_STACK_SIZE - 1024) #define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1) #define MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING (1) #define MICROPY_BLUETOOTH_NIMBLE (1) From b6a3aa10f5a223a43971ac22e6db7c967916d70a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 15 Aug 2024 16:06:19 +1000 Subject: [PATCH 0205/1300] esp32: Remove the increased stack limit margin for ESP32-C3. The extra limit for C3 dates from 6823514 which added C3 support. Measuring the minimum stack margins that can pass the stress tests I measured 768 bytes for ESP32-S3 and 512 bytes for ESP32-C3 on ESP-IDF V5.2.2 and similar on V5.0.4. i.e. The ESP32-C3 actually needs less stack margin not more! I think the extra margin for ESP32-C3 probably arose from: 1. Some toolchain inefficiency in the IDF V4.x RISC-V compiler codegen, that has since been improved. OR 2. The race condition that was fixed in e3955f42 where sometimes the limit wasn't set correctly at all. This seems to trigger more on C3, presumably some timing artifact, and I'd believe that some binaries might be more susceptible than others due to random factors. OR 3. Commit 6007f3e2062cc65fc8416f241c682e37eb956c11 which enabled custom NLR handling for ESP32-C3. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/mpconfigport.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 723310b231b18..5051afb799ead 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -62,11 +62,7 @@ // Python internal features #define MICROPY_READER_VFS (1) #define MICROPY_ENABLE_GC (1) -#if CONFIG_IDF_TARGET_ARCH_RISCV // RISC-V SoCs use more stack than Xtensa -#define MICROPY_STACK_CHECK_MARGIN (2048) // This may be unnecessarily conservative -#else #define MICROPY_STACK_CHECK_MARGIN (1024) -#endif #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) From 43f40f797f8851e2a170d3bad5dd38c608c2fd90 Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Sun, 21 Jan 2024 00:32:09 +1100 Subject: [PATCH 0206/1300] esp32/boards/M5STACK_ATOMS3_LITE: Add M5Stack AtomS3 Lite board. Signed-off-by: Matt Trentini --- .../boards/M5STACK_ATOMS3_LITE/board.json | 21 ++++++++++++++++++ .../esp32/boards/M5STACK_ATOMS3_LITE/board.md | 3 +++ .../boards/M5STACK_ATOMS3_LITE/deploy.md | 22 +++++++++++++++++++ .../M5STACK_ATOMS3_LITE/mpconfigboard.cmake | 8 +++++++ .../M5STACK_ATOMS3_LITE/mpconfigboard.h | 10 +++++++++ .../esp32/boards/M5STACK_ATOMS3_LITE/pins.csv | 11 ++++++++++ 6 files changed, 75 insertions(+) create mode 100644 ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json create mode 100644 ports/esp32/boards/M5STACK_ATOMS3_LITE/board.md create mode 100644 ports/esp32/boards/M5STACK_ATOMS3_LITE/deploy.md create mode 100644 ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.cmake create mode 100644 ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.h create mode 100644 ports/esp32/boards/M5STACK_ATOMS3_LITE/pins.csv diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json new file mode 100644 index 0000000000000..d00bb673be8a8 --- /dev/null +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json @@ -0,0 +1,21 @@ +{ + "deploy": [ + "deploy.md" + ], + "docs": "https://docs.m5stack.com/en/core/AtomS3%20Lite", + "features": [ + "BLE", + "WiFi", + "RGB LED", + "JST-PH", + "USB-C" + ], + "images": [ + "atoms3lite.jpg" + ], + "mcu": "esp32s3", + "product": "AtomS3 Lite", + "thumbnail": "", + "url": "https://shop.m5stack.com/products/atoms3-lite-esp32s3-dev-kit", + "vendor": "M5 Stack" +} diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.md b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.md new file mode 100644 index 0000000000000..8c93b5781ae50 --- /dev/null +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.md @@ -0,0 +1,3 @@ +The [AtomS3 Lite](https://shop.m5stack.com/products/atoms3-lite-esp32s3-dev-kit) +([docs](https://docs.m5stack.com/en/core/AtomS3%20Lite)) is an ESP32-S3 based +development board from [M5Stack](https://m5stack.com/). diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/deploy.md b/ports/esp32/boards/M5STACK_ATOMS3_LITE/deploy.md new file mode 100644 index 0000000000000..9a20a6a43dc6b --- /dev/null +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/deploy.md @@ -0,0 +1,22 @@ +Program your board using the `esptool.py` program, found +[here](https://github.com/espressif/esptool). + +To place the board in _bootloader mode_ - so `esptool`` can be used - press and +hold the reset button for two seconds. A green LED will flash behind the reset +button when the bootloader mode has been activated. + +If you are putting MicroPython on your board for the first time then you should +first erase the entire flash using: + +```bash +esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash +``` + +From then on program the firmware starting at address 0: + +```bash +esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0 board-20240105-v1.22.1.bin +``` + +After the firmware has been deployed, press the reset button to reset the device +and start the new firmware. diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.cmake b/ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.cmake new file mode 100644 index 0000000000000..10608fcec7f87 --- /dev/null +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.cmake @@ -0,0 +1,8 @@ +set(IDF_TARGET esp32s3) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.usb + boards/sdkconfig.ble + boards/ESP32_GENERIC_S3/sdkconfig.board +) diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.h b/ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.h new file mode 100644 index 0000000000000..14bde8438bb24 --- /dev/null +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.h @@ -0,0 +1,10 @@ +#define MICROPY_HW_BOARD_NAME "M5Stack AtomS3 Lite" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define MICROPY_PY_MACHINE_DAC (0) + +// Enable UART REPL for modules that have an external USB-UART and don't use native USB. +#define MICROPY_HW_ENABLE_UART_REPL (1) + +#define MICROPY_HW_I2C0_SCL (39) +#define MICROPY_HW_I2C0_SDA (38) diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/pins.csv b/ports/esp32/boards/M5STACK_ATOMS3_LITE/pins.csv new file mode 100644 index 0000000000000..cd3276599cb88 --- /dev/null +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/pins.csv @@ -0,0 +1,11 @@ +G1,GPIO1 +G2,GPIO2 +G5,GPIO5 +G6,GPIO6 +G7,GPIO7 +G8,GPIO8 +G38,GPIO38 +G39,GPIO39 +LED_RGB,GPIO35 +LED_IR,GPIO4 +BUTTON,GPIO41 From e9814e987bcc816fb67e38748a5afce466c45606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elvis=20Pf=C3=BCtzenreuter?= Date: Mon, 10 Jul 2023 22:31:36 -0300 Subject: [PATCH 0207/1300] esp32/boards/LILYGO_TTGO_LORA32: Add OLED rst seq for board v1.0. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elvis Pfützenreuter --- .../esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py | 7 ++++++- ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lora32.py | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py index 58072ee1b51f0..bfe02c35765b8 100644 --- a/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py @@ -4,7 +4,12 @@ class OLED(SSD1306_I2C): - def __init__(self, i2c): + def __init__(self, i2c, rstpin): + # Initialize the OLED display + if rstpin is not None: + rstpin.value(0) + sleep_ms(50) + rstpin.value(1) # must be held high after initialization super().__init__(128, 32, i2c) def test(self): diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lora32.py b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lora32.py index 067982a228592..e8aa4dbd74052 100644 --- a/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lora32.py +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lora32.py @@ -28,6 +28,7 @@ def __init__(self, define_helpers=True): # OLED self.OLED_SDA = const(21) self.OLED_SCL = const(22) + self.OLED_RST = None if define_helpers: self.create_helpers() @@ -35,7 +36,8 @@ def __init__(self, define_helpers=True): def create_helpers(self): self.led = Pin(self.LED, Pin.OUT) self.i2c = SoftI2C(scl=Pin(self.OLED_SCL), sda=Pin(self.OLED_SDA)) - self.oled = OLED(self.i2c) + rstpin = self.OLED_RST is not None and Pin(self.OLED_RST, Pin.OUT) or None + self.oled = OLED(self.i2c, rstpin) class Lora32v1_0(Lora32Base): From 6367099f8365c22b42474818ce5fdb9b35591ade Mon Sep 17 00:00:00 2001 From: Glenn Moloney Date: Tue, 13 Aug 2024 08:38:20 +1000 Subject: [PATCH 0208/1300] py/objstr: Skip whitespace in bytes.fromhex(). Skip whitespace characters between pairs of hex numbers. This makes `bytes.fromhex()` compatible with cpython. Includes simple test in `tests/basic/builtin_str_hex.py`. Signed-off-by: Glenn Moloney --- py/objstr.c | 24 +++++++++--------------- tests/basics/builtin_str_hex.py | 17 ++++++++++++++++- tests/basics/builtin_str_hex.py.exp | 11 +++++++++++ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/py/objstr.c b/py/objstr.c index 757da827c0fa3..fc0623eb7af2c 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -2014,27 +2014,21 @@ mp_obj_t mp_obj_bytes_fromhex(mp_obj_t type_in, mp_obj_t data) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); - if ((bufinfo.len & 1) != 0) { - mp_raise_ValueError(MP_ERROR_TEXT("odd-length string")); - } vstr_t vstr; vstr_init_len(&vstr, bufinfo.len / 2); byte *in = bufinfo.buf, *out = (byte *)vstr.buf; - byte hex_byte = 0; - for (mp_uint_t i = bufinfo.len; i--;) { - byte hex_ch = *in++; - if (unichar_isxdigit(hex_ch)) { - hex_byte += unichar_xdigit_value(hex_ch); - } else { - mp_raise_ValueError(MP_ERROR_TEXT("non-hex digit found")); + byte *in_end = in + bufinfo.len; + mp_uint_t ch1, ch2; + while (in < in_end) { + if (unichar_isspace(ch1 = *in++)) { + continue; // Skip whitespace between hex digit pairs } - if (i & 1) { - hex_byte <<= 4; - } else { - *out++ = hex_byte; - hex_byte = 0; + if (in == in_end || !unichar_isxdigit(ch1) || !unichar_isxdigit(ch2 = *in++)) { + mp_raise_ValueError(MP_ERROR_TEXT("non-hex digit")); } + *out++ = (byte)((unichar_xdigit_value(ch1) << 4) | unichar_xdigit_value(ch2)); } + vstr.len = out - (byte *)vstr.buf; // Length may be shorter due to whitespace in input return mp_obj_new_str_type_from_vstr(MP_OBJ_TO_PTR(type_in), &vstr); } diff --git a/tests/basics/builtin_str_hex.py b/tests/basics/builtin_str_hex.py index 7390c8eaee17c..9455883012c0d 100644 --- a/tests/basics/builtin_str_hex.py +++ b/tests/basics/builtin_str_hex.py @@ -20,5 +20,20 @@ "08090a0b0c0d0e0f", "7f80ff", "313233344142434461626364", + "ab\tcd\n ef ", + "ab cd ef", + "ab cd ef ", + " ab cd ef ", + # Invalid hex strings: + "abcde", # Odd number of hex digits + "ab cd e", + "a b cd ef", # Spaces between hex pairs + "ab cd e f ", + "abga", # Invalid hex digits + "ab_cd", + "ab:cd", ): - print(bytes.fromhex(x)) + try: + print(bytes.fromhex(x)) + except ValueError as e: + print("ValueError:", e) diff --git a/tests/basics/builtin_str_hex.py.exp b/tests/basics/builtin_str_hex.py.exp index 990dd8570767e..0309cad02d1a6 100644 --- a/tests/basics/builtin_str_hex.py.exp +++ b/tests/basics/builtin_str_hex.py.exp @@ -26,3 +26,14 @@ b'\x00\x01\x02\x03\x04\x05\x06\x07' b'\x08\t\n\x0b\x0c\r\x0e\x0f' b'\x7f\x80\xff' b'1234ABCDabcd' +b'\xab\xcd\xef' +b'\xab\xcd\xef' +b'\xab\xcd\xef' +b'\xab\xcd\xef' +ValueError: non-hex digit +ValueError: non-hex digit +ValueError: non-hex digit +ValueError: non-hex digit +ValueError: non-hex digit +ValueError: non-hex digit +ValueError: non-hex digit From 326e1149eccc2521527e62be66f1a0ae9f6600d0 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 31 Jul 2024 16:56:42 +0200 Subject: [PATCH 0209/1300] py/asmrv32: Fix short/long jumps scheduling. The RV32 emitter always scheduled short jumps even outside the emit compiler pass. Running the full test suite through the native emitter instead of just the tests that depend on the emitter at runtime (as in, `micropython/native_*` and `micropython/viper_* tests`) uncovered more places where the invalid behaviour was still present. Signed-off-by: Alessandro Gatti --- py/asmrv32.c | 69 ++++++++++++++++++++++++++++------------------------ py/asmrv32.h | 4 +-- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/py/asmrv32.c b/py/asmrv32.c index 7dd58becae6a6..a68401cf32fef 100644 --- a/py/asmrv32.c +++ b/py/asmrv32.c @@ -132,9 +132,9 @@ static void split_immediate(mp_int_t immediate, mp_uint_t *upper, mp_uint_t *low static void load_upper_immediate(asm_rv32_t *state, mp_uint_t rd, mp_uint_t immediate) { // if immediate fits in 17 bits and is ≠ 0: - // c.lui rd, HI(immediate) + // c.lui rd, HI(immediate) // else: - // lui rd, HI(immediate) + // lui rd, HI(immediate) if (FIT_SIGNED(immediate, 17) && ((immediate >> 12) != 0)) { asm_rv32_opcode_clui(state, rd, immediate); } else { @@ -270,6 +270,14 @@ static void emit_function_epilogue(asm_rv32_t *state, mp_uint_t registers) { state->saved_registers_mask = old_saved_registers_mask; } +static bool calculate_displacement_for_label(asm_rv32_t *state, mp_uint_t label, ptrdiff_t *displacement) { + assert(displacement != NULL && "Displacement pointer is NULL"); + + mp_uint_t label_offset = state->base.label_offsets[label]; + *displacement = (ptrdiff_t)(label_offset - state->base.code_offset); + return (label_offset != (mp_uint_t)-1) && (*displacement < 0); +} + /////////////////////////////////////////////////////////////////////////////// void asm_rv32_entry(asm_rv32_t *state, mp_uint_t locals) { @@ -326,10 +334,10 @@ void asm_rv32_emit_call_ind(asm_rv32_t *state, mp_uint_t index) { } void asm_rv32_emit_jump_if_reg_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t label) { - ptrdiff_t displacement = (ptrdiff_t)(state->base.label_offsets[label] - state->base.code_offset); + ptrdiff_t displacement = 0; + bool can_emit_short_jump = calculate_displacement_for_label(state, label, &displacement); - // The least significant bit is ignored anyway. - if (FIT_SIGNED(displacement, 13)) { + if (can_emit_short_jump && FIT_SIGNED(displacement, 13)) { // beq rs1, rs2, displacement asm_rv32_opcode_beq(state, rs1, rs2, displacement); return; @@ -354,31 +362,24 @@ void asm_rv32_emit_jump_if_reg_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs } void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_t label) { - ptrdiff_t displacement = (ptrdiff_t)(state->base.label_offsets[label] - state->base.code_offset); + ptrdiff_t displacement = 0; + bool can_emit_short_jump = calculate_displacement_for_label(state, label, &displacement); - if (FIT_SIGNED(displacement, 9) && IS_IN_C_REGISTER_WINDOW(rs)) { + if (can_emit_short_jump && FIT_SIGNED(displacement, 8) && IS_IN_C_REGISTER_WINDOW(rs)) { // c.bnez rs', displacement asm_rv32_opcode_cbnez(state, MAP_IN_C_REGISTER_WINDOW(rs), displacement); return; } - // The least significant bit is ignored anyway. - if (FIT_SIGNED(displacement, 13)) { + if (can_emit_short_jump && FIT_SIGNED(displacement, 13)) { // bne rs, zero, displacement asm_rv32_opcode_bne(state, rs, ASM_RV32_REG_ZERO, displacement); return; } - // Compensate for the initial C.BEQZ/BEQ opcode. - displacement -= IS_IN_C_REGISTER_WINDOW(rs) ? ASM_HALFWORD_SIZE : ASM_WORD_SIZE; - - mp_uint_t upper = 0; - mp_uint_t lower = 0; - split_immediate(displacement, &upper, &lower); - // TODO: Can this clobber REG_TEMP[0:2]? - // if rs1 in C window (the offset always fits): + // if rs1 in C window and displacement is negative: // c.beqz rs', 10 ; PC + 0 // auipc temporary, HI(displacement) ; PC + 2 // jalr zero, temporary, LO(displacement) ; PC + 6 @@ -388,11 +389,20 @@ void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_ // auipc temporary, HI(displacement) ; PC + 4 // jalr zero, temporary, LO(displacement) ; PC + 8 // ... ; PC + 12 - if (IS_IN_C_REGISTER_WINDOW(rs)) { + + if (can_emit_short_jump && IS_IN_C_REGISTER_WINDOW(rs)) { asm_rv32_opcode_cbeqz(state, MAP_IN_C_REGISTER_WINDOW(rs), 10); + // Compensate for the C.BEQZ opcode. + displacement -= ASM_HALFWORD_SIZE; } else { asm_rv32_opcode_beq(state, rs, ASM_RV32_REG_ZERO, 12); + // Compensate for the BEQ opcode. + displacement -= ASM_WORD_SIZE; } + + mp_uint_t upper = 0; + mp_uint_t lower = 0; + split_immediate(displacement, &upper, &lower); asm_rv32_opcode_auipc(state, INTERNAL_TEMPORARY, upper); asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, INTERNAL_TEMPORARY, lower); } @@ -502,10 +512,10 @@ void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_ } void asm_rv32_emit_jump(asm_rv32_t *state, mp_uint_t label) { - ptrdiff_t displacement = (ptrdiff_t)(state->base.label_offsets[label] - state->base.code_offset); + ptrdiff_t displacement = 0; + bool can_emit_short_jump = calculate_displacement_for_label(state, label, &displacement); - // The least significant bit is ignored anyway. - if (FIT_SIGNED(displacement, 13)) { + if (can_emit_short_jump && FIT_SIGNED(displacement, 12)) { // c.j displacement asm_rv32_opcode_cj(state, displacement); return; @@ -536,12 +546,12 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint mp_uint_t lower = 0; split_immediate(scaled_offset, &upper, &lower); - // lui rd, HI(offset) ; Or c.lui if possible - // c.add rd, rs - // sw rd, LO(offset)(rd) - load_upper_immediate(state, rd, upper); - asm_rv32_opcode_cadd(state, rd, rs); - asm_rv32_opcode_sw(state, rd, rd, lower); + // lui temporary, HI(offset) ; Or c.lui if possible + // c.add temporary, rs + // sw rd, LO(offset)(temporary) + load_upper_immediate(state, INTERNAL_TEMPORARY, upper); + asm_rv32_opcode_cadd(state, INTERNAL_TEMPORARY, rs); + asm_rv32_opcode_sw(state, rd, INTERNAL_TEMPORARY, lower); } void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t label) { @@ -550,11 +560,6 @@ void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t labe mp_uint_t lower = 0; split_immediate(displacement, &upper, &lower); - // Compressed instructions are not used even if they may allow for code size - // savings as the code changes size between compute and emit passes - // otherwise. If that happens then the assertion at asmbase.c:93 triggers - // when built in debug mode. - // auipc rd, HI(relative) // addi rd, rd, LO(relative) asm_rv32_opcode_auipc(state, rd, upper); diff --git a/py/asmrv32.h b/py/asmrv32.h index 77a5b0ab67ac4..775cf1ffc9630 100644 --- a/py/asmrv32.h +++ b/py/asmrv32.h @@ -331,7 +331,7 @@ static inline void asm_rv32_opcode_lw(asm_rv32_t *state, mp_uint_t rd, mp_uint_t } // MUL RD, RS1, RS2 -static inline void asm_rv32m_opcode_mul(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { +static inline void asm_rv32_opcode_mul(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000001 ..... ..... 000 ..... 0110011 asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x00, 0x01, rd, rs1, rs2)); } @@ -479,7 +479,7 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_ #define ASM_MOV_REG_LOCAL(state, rd, local) asm_rv32_emit_mov_reg_local(state, rd, local) #define ASM_MOV_REG_PCREL(state, rd, label) asm_rv32_emit_mov_reg_pcrel(state, rd, label) #define ASM_MOV_REG_REG(state, rd, rs) asm_rv32_opcode_cmv(state, rd, rs) -#define ASM_MUL_REG_REG(state, rd, rs) asm_rv32m_opcode_mul(state, rd, rd, rs) +#define ASM_MUL_REG_REG(state, rd, rs) asm_rv32_opcode_mul(state, rd, rd, rs) #define ASM_NEG_REG(state, rd) asm_rv32_opcode_sub(state, rd, ASM_RV32_REG_ZERO, rd) #define ASM_NOT_REG(state, rd) asm_rv32_opcode_xori(state, rd, rd, -1) #define ASM_OR_REG_REG(state, rd, rs) asm_rv32_opcode_or(state, rd, rd, rs) From da0e027fa5699ce2676568fecba1f55a30344f7b Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 1 Aug 2024 21:21:38 +0200 Subject: [PATCH 0210/1300] py/asmrv32: Emit C.LW opcodes only when necessary. The RV32 emitter sometimes generated short load opcodes even when it was not supposed to. This commit fixes an off-by-one error in its offset eligibility range calculation and corrects one case of offset calculation, operating on the raw label index number rather than its effective offset in the stack (C.LW assumes all loads are word-aligned). Signed-off-by: Alessandro Gatti --- py/asmrv32.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/asmrv32.c b/py/asmrv32.c index a68401cf32fef..f121c1ef6b620 100644 --- a/py/asmrv32.c +++ b/py/asmrv32.c @@ -301,7 +301,7 @@ void asm_rv32_emit_call_ind(asm_rv32_t *state, mp_uint_t index) { mp_uint_t offset = index * ASM_WORD_SIZE; state->saved_registers_mask |= (1U << ASM_RV32_REG_RA); - if (IS_IN_C_REGISTER_WINDOW(REG_FUN_TABLE) && IS_IN_C_REGISTER_WINDOW(INTERNAL_TEMPORARY) && FIT_SIGNED(offset, 7)) { + if (IS_IN_C_REGISTER_WINDOW(REG_FUN_TABLE) && IS_IN_C_REGISTER_WINDOW(INTERNAL_TEMPORARY) && FIT_UNSIGNED(offset, 6)) { // c.lw temporary, offset(fun_table) // c.jalr temporary asm_rv32_opcode_clw(state, MAP_IN_C_REGISTER_WINDOW(INTERNAL_TEMPORARY), MAP_IN_C_REGISTER_WINDOW(REG_FUN_TABLE), offset); @@ -487,7 +487,7 @@ void asm_rv32_emit_mov_reg_local_addr(asm_rv32_t *state, mp_uint_t rd, mp_uint_t void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { mp_int_t scaled_offset = offset * sizeof(ASM_WORD_SIZE); - if (IS_IN_C_REGISTER_WINDOW(rd) && IS_IN_C_REGISTER_WINDOW(rs) && FIT_SIGNED(offset, 7)) { + if (scaled_offset >= 0 && IS_IN_C_REGISTER_WINDOW(rd) && IS_IN_C_REGISTER_WINDOW(rs) && FIT_UNSIGNED(scaled_offset, 6)) { // c.lw rd', offset(rs') asm_rv32_opcode_clw(state, MAP_IN_C_REGISTER_WINDOW(rd), MAP_IN_C_REGISTER_WINDOW(rs), scaled_offset); return; From 7d8b2d89cc6b3734b6ecef36c49354fbf1406687 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 16 Aug 2024 13:57:02 +0200 Subject: [PATCH 0211/1300] py/asmrv32: Use REG_TEMP2 whenever possible. The RV32 emitter used an additional temporary register, as certain code sequences required extra storage. This commit removes its usage in all but one case, using REG_TEMP2 instead. Signed-off-by: Alessandro Gatti --- py/asmrv32.c | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/py/asmrv32.c b/py/asmrv32.c index f121c1ef6b620..3f3395842efb9 100644 --- a/py/asmrv32.c +++ b/py/asmrv32.c @@ -312,8 +312,8 @@ void asm_rv32_emit_call_ind(asm_rv32_t *state, mp_uint_t index) { if (FIT_UNSIGNED(offset, 11)) { // lw temporary, offset(fun_table) // c.jalr temporary - asm_rv32_opcode_lw(state, INTERNAL_TEMPORARY, REG_FUN_TABLE, offset); - asm_rv32_opcode_cjalr(state, INTERNAL_TEMPORARY); + asm_rv32_opcode_lw(state, REG_TEMP2, REG_FUN_TABLE, offset); + asm_rv32_opcode_cjalr(state, REG_TEMP2); return; } @@ -321,16 +321,14 @@ void asm_rv32_emit_call_ind(asm_rv32_t *state, mp_uint_t index) { mp_uint_t lower = 0; split_immediate(offset, &upper, &lower); - // TODO: Can this clobber REG_TEMP[0:2]? - // lui temporary, HI(index) ; Or c.lui if possible // c.add temporary, fun_table // lw temporary, LO(index)(temporary) // c.jalr temporary - load_upper_immediate(state, INTERNAL_TEMPORARY, upper); - asm_rv32_opcode_cadd(state, INTERNAL_TEMPORARY, REG_FUN_TABLE); - asm_rv32_opcode_lw(state, INTERNAL_TEMPORARY, INTERNAL_TEMPORARY, lower); - asm_rv32_opcode_cjalr(state, INTERNAL_TEMPORARY); + load_upper_immediate(state, REG_TEMP2, upper); + asm_rv32_opcode_cadd(state, REG_TEMP2, REG_FUN_TABLE); + asm_rv32_opcode_lw(state, REG_TEMP2, REG_TEMP2, lower); + asm_rv32_opcode_cjalr(state, REG_TEMP2); } void asm_rv32_emit_jump_if_reg_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t label) { @@ -350,15 +348,13 @@ void asm_rv32_emit_jump_if_reg_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs mp_uint_t lower = 0; split_immediate(displacement, &upper, &lower); - // TODO: Can this clobber REG_TEMP[0:2]? - // bne rs1, rs2, 12 ; PC + 0 // auipc temporary, HI(displacement) ; PC + 4 // jalr zero, temporary, LO(displacement) ; PC + 8 // ... ; PC + 12 asm_rv32_opcode_bne(state, rs1, rs2, 12); - asm_rv32_opcode_auipc(state, INTERNAL_TEMPORARY, upper); - asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, INTERNAL_TEMPORARY, lower); + asm_rv32_opcode_auipc(state, REG_TEMP2, upper); + asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, REG_TEMP2, lower); } void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_t label) { @@ -377,8 +373,6 @@ void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_ return; } - // TODO: Can this clobber REG_TEMP[0:2]? - // if rs1 in C window and displacement is negative: // c.beqz rs', 10 ; PC + 0 // auipc temporary, HI(displacement) ; PC + 2 @@ -403,8 +397,8 @@ void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_ mp_uint_t upper = 0; mp_uint_t lower = 0; split_immediate(displacement, &upper, &lower); - asm_rv32_opcode_auipc(state, INTERNAL_TEMPORARY, upper); - asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, INTERNAL_TEMPORARY, lower); + asm_rv32_opcode_auipc(state, REG_TEMP2, upper); + asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, REG_TEMP2, lower); } void asm_rv32_emit_mov_local_reg(asm_rv32_t *state, mp_uint_t local, mp_uint_t rs) { @@ -426,14 +420,12 @@ void asm_rv32_emit_mov_local_reg(asm_rv32_t *state, mp_uint_t local, mp_uint_t r mp_uint_t lower = 0; split_immediate(offset, &upper, &lower); - // TODO: Can this clobber REG_TEMP[0:2]? - // lui temporary, HI(offset) ; Or c.lui if possible // c.add temporary, sp // sw rs, LO(offset)(temporary) - load_upper_immediate(state, INTERNAL_TEMPORARY, upper); - asm_rv32_opcode_cadd(state, INTERNAL_TEMPORARY, ASM_RV32_REG_SP); - asm_rv32_opcode_sw(state, rs, INTERNAL_TEMPORARY, lower); + load_upper_immediate(state, REG_TEMP2, upper); + asm_rv32_opcode_cadd(state, REG_TEMP2, ASM_RV32_REG_SP); + asm_rv32_opcode_sw(state, rs, REG_TEMP2, lower); } void asm_rv32_emit_mov_reg_local(asm_rv32_t *state, mp_uint_t rd, mp_uint_t local) { @@ -525,12 +517,10 @@ void asm_rv32_emit_jump(asm_rv32_t *state, mp_uint_t label) { mp_uint_t lower = 0; split_immediate(displacement, &upper, &lower); - // TODO: Can this clobber REG_TEMP[0:2]? - // auipc temporary, HI(displacement) // jalr zero, temporary, LO(displacement) - asm_rv32_opcode_auipc(state, INTERNAL_TEMPORARY, upper); - asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, INTERNAL_TEMPORARY, lower); + asm_rv32_opcode_auipc(state, REG_TEMP2, upper); + asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, REG_TEMP2, lower); } void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { @@ -549,9 +539,9 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint // lui temporary, HI(offset) ; Or c.lui if possible // c.add temporary, rs // sw rd, LO(offset)(temporary) - load_upper_immediate(state, INTERNAL_TEMPORARY, upper); - asm_rv32_opcode_cadd(state, INTERNAL_TEMPORARY, rs); - asm_rv32_opcode_sw(state, rd, INTERNAL_TEMPORARY, lower); + load_upper_immediate(state, REG_TEMP2, upper); + asm_rv32_opcode_cadd(state, REG_TEMP2, rs); + asm_rv32_opcode_sw(state, rd, REG_TEMP2, lower); } void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t label) { From 36108a41d0d397fdbfcda060a05f0fa8e206aa96 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 27 Dec 2023 14:47:00 +0100 Subject: [PATCH 0212/1300] mimxrt/mimxrt_sdram: Fix pin config and comments. Signed-off-by: robert-hh --- ports/mimxrt/mimxrt_sdram.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/mimxrt/mimxrt_sdram.c b/ports/mimxrt/mimxrt_sdram.c index ea53daf5ca704..92f3aa1a5403e 100644 --- a/ports/mimxrt/mimxrt_sdram.c +++ b/ports/mimxrt/mimxrt_sdram.c @@ -35,10 +35,10 @@ extern uint8_t __sdram_start; #if defined(MIMXRT117x_SERIES) -// Pull Up, High drive strength -#define SDRAM_PIN_CONFIG (0x07UL) +// No pull, normal drive strength +#define SDRAM_PIN_CONFIG (0x0EUL) #else -// Pull up 22K, high slew rate +// No pull, maximum speed, Drive Strength 4, fast slew rate #define SDRAM_PIN_CONFIG (0xE1UL) #endif From 87adf11dd282fb5b121427ef5bdf133c2dfdfdac Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 7 Jun 2024 09:22:46 +0200 Subject: [PATCH 0213/1300] mimxrt/machine_pin: Clear IRQ flag when enabling or disabling IRQ. Preventing already pending IRQs to fire when not expected. Signed-off-by: robert-hh --- ports/mimxrt/machine_pin.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/mimxrt/machine_pin.c b/ports/mimxrt/machine_pin.c index ad4158c6bdee9..4f623214bc728 100644 --- a/ports/mimxrt/machine_pin.c +++ b/ports/mimxrt/machine_pin.c @@ -407,6 +407,7 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_ if (args[ARG_handler].u_obj == mp_const_none) { // remove the IRQ from the table, leave it to gc to free it. GPIO_PortDisableInterrupts(self->gpio, 1U << self->pin); + GPIO_PortClearInterruptFlags(self->gpio, 1U << self->pin); MP_STATE_PORT(machine_pin_irq_objects[index]) = NULL; return mp_const_none; } @@ -445,6 +446,8 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_ GPIO_PinSetInterruptConfig(self->gpio, self->pin, irq->trigger); // Enable the specific Pin interrupt GPIO_PortEnableInterrupts(self->gpio, 1U << self->pin); + // Clear previous IRQs + GPIO_PortClearInterruptFlags(self->gpio, 1U << self->pin); // Enable LEVEL1 interrupt again EnableIRQ(irq_num); } From 76dd4facb9b1cf81e7f7a262c931adffb126b382 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 10 Jul 2024 09:44:17 +0200 Subject: [PATCH 0214/1300] docs/mimxrt/quickref: Add a note about machine.RTC() subseconds. Telling that subseconds is not supported and returns always 0. This was changed in 913f9ad5ad9bb446c6e4310b6b5eda310098a1cf. Signed-off-by: robert-hh --- docs/mimxrt/quickref.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index 79d2a1551e4c3..cfd0605054a7b 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -438,6 +438,10 @@ The i.MXRT MCU supports battery backup of the RTC. By connecting a battery of current drawn from the battery is ~20µA, which is rather high. A CR2032 coin cell will last for about one year. +Note: In v1.23.0 the support for subseconds was removed. When reading the RTC, 0 will +be returned as value for subsecond, When setting the RTC time, the subsecond +field is ignored. The RTC itself does not provide a microsecond value. + SD card ------- From 26d91b897ea46a28f3d32cbe094b607124c1657d Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 13 Aug 2024 14:55:26 +0100 Subject: [PATCH 0215/1300] rp2/mphalport: Skip core1_entry if thread disabled. If `MICROPY_PY_THREAD` is set to 0 (ie: a user C module wishes to use core1 exclusively) then the test of `core1_entry` would fail to compile with an "undeclared identifier" error. Fix it by wrapping in `MICROPY_PY_THREAD`. Signed-off-by: Phil Howard --- ports/rp2/mphalport.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index ad51cf8e21f43..aa5415d6c842d 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -232,9 +232,11 @@ static void soft_timer_hardware_callback(unsigned int alarm_num) { // This ISR only runs on core0, but if core1 is running Python code then it // may be blocked in WFE so wake it up as well. Unfortunately this also sets // the event flag on core0, so a subsequent WFE on this core will not suspend + #if MICROPY_PY_THREAD if (core1_entry != NULL) { __sev(); } + #endif } void soft_timer_init(void) { From d420b4e478267cf8c6c0136982bdeeedaccf3920 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 20 Feb 2024 21:46:57 +0100 Subject: [PATCH 0216/1300] rp2/main: Set the default clock frequency at boot. As a side effect, the peripheral clock will be set to 48Mhz and both UART and I2C will not be affected by CPu speed changed using `machine.freq()`. With the change the UART baud rate range is 50 to 3_000_000. Signed-off-by: robert-hh --- ports/rp2/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 1ce1d2bf31d65..fa5495bf4100d 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -80,6 +80,9 @@ int main(int argc, char **argv) { pendsv_init(); soft_timer_init(); + // Set the MCU frequency and as a side effect the peripheral clock to 48 MHz. + set_sys_clock_khz(125000, false); + #if MICROPY_HW_ENABLE_UART_REPL bi_decl(bi_program_feature("UART REPL")) setup_default_uart(); @@ -231,6 +234,10 @@ int main(int argc, char **argv) { gc_sweep_all(); mp_deinit(); + #if MICROPY_HW_ENABLE_UART_REPL + setup_default_uart(); + mp_uart_init(); + #endif } return 0; From 7270b871c248a4a091d310ec59eca4138a3e96e3 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 22 Feb 2024 09:03:06 +0100 Subject: [PATCH 0217/1300] rp2/modmachine: Set the peripheral frequency with machine.freq(). By default, the peripheral clock for UART and SPI is set to 48 MHz and will not be affected by the MCU clock change. This can be changed by a second argument to `machine.freq(freq, peripheral_freq)`. The second argument must be either 48 MHz or identical with the first argument. Note that UART and SPI baud rates may have to be re-configured after changing the MCU clock. Signed-off-by: robert-hh --- docs/rp2/quickref.rst | 17 +++++++++++++++-- ports/rp2/modmachine.c | 14 ++++++++++++++ ports/rp2/mpconfigport.h | 1 + 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/docs/rp2/quickref.rst b/docs/rp2/quickref.rst index ecf3f31861c22..9b82ea5dc6749 100644 --- a/docs/rp2/quickref.rst +++ b/docs/rp2/quickref.rst @@ -31,12 +31,25 @@ The MicroPython REPL is accessed via the USB serial port. Tab-completion is usef find out what methods an object has. Paste mode (ctrl-E) is useful to paste a large slab of Python code into the REPL. -The :mod:`machine` module:: +The :mod:`machine` module: + +machine.freq() allows to change the MCU frequency and control the peripheral +frequency for UART and SPI. Usage:: + + machine.freq(MCU_frequency[, peripheral_frequency=48_000_000]) + +The MCU frequency can be set in a range from less than 48 MHz to about 250MHz. +The default at boot time is 125 MHz. The peripheral frequency must be either +48 MHz or identical to the MCU frequency, with 48 MHz as the default. +If the peripheral frequency is changed, any already existing instance of +UART and SPI will change it's baud rate and may have to be re-configured:: import machine machine.freq() # get the current frequency of the CPU - machine.freq(240000000) # set the CPU frequency to 240 MHz + machine.freq(240000000) # set the CPU frequency to 240 MHz and keep + # the UART frequency at 48MHz + machine.freq(125000000, 125000000) # set the CPU and UART frequency to 125 MHz The :mod:`rp2` module:: diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 229000cc1797c..372c17bcf9d6b 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -96,6 +96,20 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { if (!set_sys_clock_khz(freq / 1000, false)) { mp_raise_ValueError(MP_ERROR_TEXT("cannot change frequency")); } + if (n_args > 1) { + mp_int_t freq_peri = mp_obj_get_int(args[1]); + if (freq_peri != (USB_CLK_KHZ * KHZ)) { + if (freq_peri == freq) { + clock_configure(clk_peri, + 0, + CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, + freq, + freq); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("peripheral freq must be 48_000_000 or the same as the MCU freq")); + } + } + } #if MICROPY_HW_ENABLE_UART_REPL setup_default_uart(); mp_uart_init(); diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 3399212aacaa6..abe25d7009e1e 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -148,6 +148,7 @@ #define MICROPY_PY_MACHINE_UART_SENDBREAK (1) #define MICROPY_PY_MACHINE_WDT (1) #define MICROPY_PY_MACHINE_WDT_INCLUDEFILE "ports/rp2/machine_wdt.c" +#define MICROPY_PY_MACHINE_FREQ_NUM_ARGS_MAX (2) #define MICROPY_PY_ONEWIRE (1) #define MICROPY_VFS (1) #define MICROPY_VFS_LFS2 (1) From ad3829977967f3e2f1a3fdb5d9d71f685702fc28 Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Wed, 14 Aug 2024 10:22:26 +1000 Subject: [PATCH 0218/1300] samd/boards/ADAFRUIT_METRO_M4_EXPRESS: Remove wlan variant. There is no such variant. Signed-off-by: Matt Trentini --- ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/board.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/board.json b/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/board.json index f830ff223f909..4a3158d71334c 100644 --- a/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/board.json +++ b/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/board.json @@ -15,9 +15,6 @@ "metro_m4_express_airlift.jpg" ], "mcu": "samd51", - "variants": { - "wlan": "WLAN without SSL support" - }, "product": "Metro M4 Express Airlift", "thumbnail": "", "url": "https://www.adafruit.com/product/4000", From b704ff66c3e034c36e548eb0b9874871b5f3b5d0 Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Sat, 17 Aug 2024 16:34:51 +1000 Subject: [PATCH 0219/1300] esp32/boards: Remove all IDF3 variants. IDF 3 builds are very old now (it seems like the last successful builds are from 2021), and the current IDF 5 is stable. So remove IDF 3 variants. Signed-off-by: Matt Trentini --- ports/esp32/boards/ESP32_GENERIC/board.json | 1 - ports/esp32/boards/UM_TINYPICO/board.json | 3 --- tools/autobuild/build-boards.sh | 6 ++---- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/ports/esp32/boards/ESP32_GENERIC/board.json b/ports/esp32/boards/ESP32_GENERIC/board.json index 0db38c25d3640..130f6b88c8856 100644 --- a/ports/esp32/boards/ESP32_GENERIC/board.json +++ b/ports/esp32/boards/ESP32_GENERIC/board.json @@ -16,7 +16,6 @@ "thumbnail": "", "url": "https://www.espressif.com/en/products/modules", "variants": { - "IDF3": "Compiled with IDF 3.x", "D2WD": "ESP32 D2WD", "SPIRAM": "Support for SPIRAM / WROVER", "UNICORE": "ESP32 Unicore", diff --git a/ports/esp32/boards/UM_TINYPICO/board.json b/ports/esp32/boards/UM_TINYPICO/board.json index 6dbe762f3afd6..5ec7913c42ec6 100644 --- a/ports/esp32/boards/UM_TINYPICO/board.json +++ b/ports/esp32/boards/UM_TINYPICO/board.json @@ -22,8 +22,5 @@ "product": "TinyPICO", "thumbnail": "", "url": "https://www.tinypico.com/", - "variants": { - "IDF3": "Compiled with IDF 3.x" - }, "vendor": "Unexpected Maker" } diff --git a/tools/autobuild/build-boards.sh b/tools/autobuild/build-boards.sh index 4b8cf315a2f9c..801e7062e5798 100755 --- a/tools/autobuild/build-boards.sh +++ b/tools/autobuild/build-boards.sh @@ -55,10 +55,8 @@ function build_board { $MICROPY_AUTOBUILD_MAKE BOARD=$board BUILD=$build_dir && copy_artefacts $dest_dir $descr $fw_tag $build_dir $@ rm -rf $build_dir - # Query variants from board.json and build them. Ignore the special "IDF3" - # variant for ESP32 boards (this allows the downloads page to still have - # the idf3 files for older releases that used to be explicitly built). - for variant in `cat $board_json | python3 -c "import json,sys; print(' '.join(v for v in json.load(sys.stdin).get('variants', {}).keys() if v != 'IDF3'))"`; do + # Query variants from board.json and build them. + for variant in `cat $board_json | python3 -c "import json,sys; print(' '.join(json.load(sys.stdin).get('variants', {}).keys()))"`; do local variant_build_dir=$build_dir-$variant echo "building variant $descr $board $variant" $MICROPY_AUTOBUILD_MAKE BOARD=$board BOARD_VARIANT=$variant BUILD=$variant_build_dir && copy_artefacts $dest_dir $descr-$variant $fw_tag $variant_build_dir $@ From 5e8d35af08b620f1498a5135d25fe6d8f3e72337 Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Sat, 17 Aug 2024 22:07:21 +1000 Subject: [PATCH 0220/1300] stm32/boards: Add missing images and update product URLs. There are some missing images at MicroPython Downloads. This commit attempts to resolve all the current issues, and add product URLs where missing. Signed-off-by: Matt Trentini --- ports/stm32/boards/B_L475E_IOT01A/board.json | 6 ++++-- ports/stm32/boards/GARATRONIC_PYBSTICK26_F411/board.json | 4 +++- ports/stm32/boards/LEGO_HUB_NO6/board.json | 4 +++- ports/stm32/boards/LEGO_HUB_NO7/board.json | 4 +++- ports/stm32/boards/NUCLEO_F756ZG/board.json | 2 +- ports/stm32/boards/NUCLEO_G0B1RE/board.json | 2 +- ports/stm32/boards/NUCLEO_G474RE/board.json | 2 +- ports/stm32/boards/NUCLEO_H723ZG/board.json | 2 +- ports/stm32/boards/NUCLEO_H743ZI2/board.json | 4 +++- ports/stm32/boards/NUCLEO_L152RE/board.json | 2 +- ports/stm32/boards/NUCLEO_L4A6ZG/board.json | 2 +- ports/stm32/boards/NUCLEO_WL55/board.json | 2 +- ports/stm32/boards/STM32L496GDISC/board.json | 6 ++++-- 13 files changed, 27 insertions(+), 15 deletions(-) diff --git a/ports/stm32/boards/B_L475E_IOT01A/board.json b/ports/stm32/boards/B_L475E_IOT01A/board.json index 91e76bc08e5ab..0841b1c6d92e7 100644 --- a/ports/stm32/boards/B_L475E_IOT01A/board.json +++ b/ports/stm32/boards/B_L475E_IOT01A/board.json @@ -4,10 +4,12 @@ ], "docs": "", "features": [], - "images": [], + "images": [ + "b-l475e-iot01a-discovery.jpg" + ], "mcu": "stm32l4", "product": "B_L475E_IOT01A", "thumbnail": "", - "url": "", + "url": "https://www.st.com/en/evaluation-tools/b-l475e-iot01a.html", "vendor": "ST Microelectronics" } diff --git a/ports/stm32/boards/GARATRONIC_PYBSTICK26_F411/board.json b/ports/stm32/boards/GARATRONIC_PYBSTICK26_F411/board.json index 1df9b6f6586c5..056423d669c9c 100644 --- a/ports/stm32/boards/GARATRONIC_PYBSTICK26_F411/board.json +++ b/ports/stm32/boards/GARATRONIC_PYBSTICK26_F411/board.json @@ -4,7 +4,9 @@ ], "docs": "", "features": [], - "images": [], + "images": [ + "pybstick26_f411.jpg" + ], "mcu": "stm32f4", "product": "GARATRONIC_PYBSTICK26_F411", "thumbnail": "", diff --git a/ports/stm32/boards/LEGO_HUB_NO6/board.json b/ports/stm32/boards/LEGO_HUB_NO6/board.json index 3c602abba1379..0bb878abafff0 100644 --- a/ports/stm32/boards/LEGO_HUB_NO6/board.json +++ b/ports/stm32/boards/LEGO_HUB_NO6/board.json @@ -4,7 +4,9 @@ ], "docs": "", "features": [], - "images": [], + "images": [ + "lego_hub_6.jpg" + ], "mcu": "stm32f4", "product": "Hub No.6", "thumbnail": "", diff --git a/ports/stm32/boards/LEGO_HUB_NO7/board.json b/ports/stm32/boards/LEGO_HUB_NO7/board.json index 1ef02c17a0636..7eb13f18fe12c 100644 --- a/ports/stm32/boards/LEGO_HUB_NO7/board.json +++ b/ports/stm32/boards/LEGO_HUB_NO7/board.json @@ -4,7 +4,9 @@ ], "docs": "", "features": [], - "images": [], + "images": [ + "lego_hub_7.jpg" + ], "mcu": "stm32f4", "product": "Hub No.7", "thumbnail": "", diff --git a/ports/stm32/boards/NUCLEO_F756ZG/board.json b/ports/stm32/boards/NUCLEO_F756ZG/board.json index 416c0b9154e1a..1b98d885694e7 100644 --- a/ports/stm32/boards/NUCLEO_F756ZG/board.json +++ b/ports/stm32/boards/NUCLEO_F756ZG/board.json @@ -10,6 +10,6 @@ "mcu": "stm32f7", "product": "Nucleo F756ZG", "thumbnail": "", - "url": "", + "url": "https://www.st.com/en/evaluation-tools/nucleo-f756zg.html", "vendor": "ST Microelectronics" } diff --git a/ports/stm32/boards/NUCLEO_G0B1RE/board.json b/ports/stm32/boards/NUCLEO_G0B1RE/board.json index f4c6f2f2df3ae..86977e8ee2fcb 100644 --- a/ports/stm32/boards/NUCLEO_G0B1RE/board.json +++ b/ports/stm32/boards/NUCLEO_G0B1RE/board.json @@ -10,6 +10,6 @@ "mcu": "stm32g0", "product": "Nucleo G0B1RE", "thumbnail": "", - "url": "", + "url": "https://www.st.com/en/evaluation-tools/nucleo-g0b1re.html", "vendor": "ST Microelectronics" } diff --git a/ports/stm32/boards/NUCLEO_G474RE/board.json b/ports/stm32/boards/NUCLEO_G474RE/board.json index 3bbb749826443..cfc72e4f1d16b 100644 --- a/ports/stm32/boards/NUCLEO_G474RE/board.json +++ b/ports/stm32/boards/NUCLEO_G474RE/board.json @@ -10,6 +10,6 @@ "mcu": "stm32g4", "product": "Nucleo G474RE", "thumbnail": "", - "url": "", + "url": "https://www.st.com/en/evaluation-tools/nucleo-g474re.html", "vendor": "ST Microelectronics" } diff --git a/ports/stm32/boards/NUCLEO_H723ZG/board.json b/ports/stm32/boards/NUCLEO_H723ZG/board.json index bf7dc0e2fb29e..6aa76008e0514 100644 --- a/ports/stm32/boards/NUCLEO_H723ZG/board.json +++ b/ports/stm32/boards/NUCLEO_H723ZG/board.json @@ -10,6 +10,6 @@ "mcu": "stm32h7", "product": "Nucleo H723ZG", "thumbnail": "", - "url": "", + "url": "https://www.st.com/en/evaluation-tools/nucleo-h723zg.html", "vendor": "ST Microelectronics" } diff --git a/ports/stm32/boards/NUCLEO_H743ZI2/board.json b/ports/stm32/boards/NUCLEO_H743ZI2/board.json index 65f01de56559e..74c1ed6c41e47 100644 --- a/ports/stm32/boards/NUCLEO_H743ZI2/board.json +++ b/ports/stm32/boards/NUCLEO_H743ZI2/board.json @@ -4,7 +4,9 @@ ], "docs": "", "features": [], - "images": [], + "images": [ + "nucleo_h743zi2.jpg" + ], "mcu": "stm32h7", "product": "Nucleo H743ZI2", "thumbnail": "", diff --git a/ports/stm32/boards/NUCLEO_L152RE/board.json b/ports/stm32/boards/NUCLEO_L152RE/board.json index 7b34276937553..abc9fbe58637b 100644 --- a/ports/stm32/boards/NUCLEO_L152RE/board.json +++ b/ports/stm32/boards/NUCLEO_L152RE/board.json @@ -10,6 +10,6 @@ "mcu": "stm32l1", "product": "Nucleo L152RE", "thumbnail": "", - "url": "", + "url": "https://www.st.com/en/evaluation-tools/nucleo-l152re.html", "vendor": "ST Microelectronics" } diff --git a/ports/stm32/boards/NUCLEO_L4A6ZG/board.json b/ports/stm32/boards/NUCLEO_L4A6ZG/board.json index 73b134eac2b35..a3a38f24f50e4 100644 --- a/ports/stm32/boards/NUCLEO_L4A6ZG/board.json +++ b/ports/stm32/boards/NUCLEO_L4A6ZG/board.json @@ -10,6 +10,6 @@ "mcu": "stm32l4", "product": "Nucleo L4A6ZG", "thumbnail": "", - "url": "", + "url": "https://www.st.com/en/evaluation-tools/nucleo-l4a6zg.html", "vendor": "ST Microelectronics" } diff --git a/ports/stm32/boards/NUCLEO_WL55/board.json b/ports/stm32/boards/NUCLEO_WL55/board.json index d756a4cdbbddb..6c12e67b29bda 100644 --- a/ports/stm32/boards/NUCLEO_WL55/board.json +++ b/ports/stm32/boards/NUCLEO_WL55/board.json @@ -10,6 +10,6 @@ "mcu": "stm32wl", "product": "Nucleo WL55", "thumbnail": "", - "url": "", + "url": "https://www.st.com/en/evaluation-tools/nucleo-wl55jc.html", "vendor": "ST Microelectronics" } diff --git a/ports/stm32/boards/STM32L496GDISC/board.json b/ports/stm32/boards/STM32L496GDISC/board.json index b152948c1a861..e1aff372ce4a8 100644 --- a/ports/stm32/boards/STM32L496GDISC/board.json +++ b/ports/stm32/boards/STM32L496GDISC/board.json @@ -4,10 +4,12 @@ ], "docs": "", "features": [], - "images": [], + "images": [ + "stm32l496discovery.jpg" + ], "mcu": "stm32l4", "product": "Discovery L496G", "thumbnail": "", - "url": "", + "url": "https://www.st.com/en/evaluation-tools/32l496gdiscovery.html", "vendor": "ST Microelectronics" } From 185116ea4139e84e7f7afb27d52b14dc03f0c2a8 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 14 Aug 2024 12:04:11 +1000 Subject: [PATCH 0221/1300] stm32/stm32_it: Enable PVD_PVM_IRQHandler for WB and WL MCUs. There is a gap in support for the PVD interrupt on STM32WBxx and STM32WLxx. This has been tested on NUCLEO_WB55 with the example code: from pyb import Pin, ExtInt def callback(line): print(line) PVD = 16 exti = ExtInt(PVD, ExtInt.IRQ_RISING_FALLING, Pin.PULL_DOWN, callback) exti.swint() Before this commit the CPU locks up as soon as the final line is run. After this commit it prints "16". Fixes issue #15548. Signed-off-by: Andrew Leech --- ports/stm32/stm32_it.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index bc1feceb9c2b5..a910a624be953 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -514,7 +514,7 @@ void PVD_IRQHandler(void) { IRQ_EXIT(PVD_IRQn); } -#if defined(STM32L4) +#if defined(STM32L4) || defined(STM32WB) || defined(STM32WL) void PVD_PVM_IRQHandler(void) { IRQ_ENTER(PVD_PVM_IRQn); Handle_EXTI_Irq(EXTI_PVD_OUTPUT); From e2c0e876f52d438fbdc286ab51cee92d5a24218e Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Tue, 9 Aug 2022 00:56:50 +1000 Subject: [PATCH 0222/1300] stm32/rfcore: Allow HSE to be a wakeup source for BLE for the WB55. Signed-off-by: Andrew Leech --- ports/stm32/rfcore.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 05582e887eeb5..2d99a3410baaa 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -539,8 +539,12 @@ void rfcore_init(void) { while (LL_HSEM_1StepLock(HSEM, CFG_HW_PWR_STANDBY_SEMID)) { } - // Select LSE as RF wakeup source + // Set the wakeup source to LSE or fall back to use HSE + #if MICROPY_HW_RTC_USE_LSE RCC->CSR = (RCC->CSR & ~RCC_CSR_RFWKPSEL) | 1 << RCC_CSR_RFWKPSEL_Pos; + #else + RCC->CSR = (RCC->CSR & ~RCC_CSR_RFWKPSEL) | 3 << RCC_CSR_RFWKPSEL_Pos; + #endif // Initialise IPCC and shared memory structures ipcc_init(IRQ_PRI_SDIO); From bd4aaa7333edf53a17c31c96ae731202fec4b0f9 Mon Sep 17 00:00:00 2001 From: nspsck Date: Tue, 26 Sep 2023 20:06:30 +0200 Subject: [PATCH 0223/1300] stm32/octospi: Add OSPI support for STM32H7 MCUs. Added a if-statement to `octospi.c` to detect if the targeted MCU is one of the STM32H7 series. If that is the case, another set of variables are used for the `mp_hal_pin_config_alt_static_speed()` function, as well as for register `OCTOSPI1->CR`. This allows the STM32H723 and STM32H7B3 series MCU to use octo-spi flash like the STM32H573 series MCU. Signed-off-by: nspsck --- ports/stm32/octospi.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/ports/stm32/octospi.c b/ports/stm32/octospi.c index 1d59e2b560eda..407f485664c6b 100644 --- a/ports/stm32/octospi.c +++ b/ports/stm32/octospi.c @@ -45,19 +45,24 @@ void octospi_init(void) { // Configure OCTOSPI pins (allows 1, 2, 4 or 8 line configuration). - mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_CS, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_NCS); - mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_SCK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_CLK); - mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO0); + #if defined(STM32H7) + #define STATIC_AF_OCTOSPI(signal) STATIC_AF_OCTOSPIM_P1_##signal + #else + #define STATIC_AF_OCTOSPI(signal) STATIC_AF_OCTOSPI1_##signal + #endif + mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_CS, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI(NCS)); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_SCK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI(CLK)); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI(IO0)); #if defined(MICROPY_HW_OSPIFLASH_IO1) - mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO1); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI(IO1)); #if defined(MICROPY_HW_OSPIFLASH_IO2) - mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO2); - mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO3); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI(IO2)); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI(IO3)); #if defined(MICROPY_HW_OSPIFLASH_IO4) - mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO4, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO4); - mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO5, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO5); - mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO6); - mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO7, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI1_IO7); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO4, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI(IO4)); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO5, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI(IO5)); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI(IO6)); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_OSPIFLASH_IO7, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_OCTOSPI(IO7)); #endif #endif #endif @@ -69,11 +74,19 @@ void octospi_init(void) { // Configure the OCTOSPI peripheral. + #if defined(STM32H7) + OCTOSPI1->CR = + 3 << OCTOSPI_CR_FTHRES_Pos // 4 bytes must be available to read/write + | 0 << OCTOSPI_CR_FSEL_Pos // FLASH 0 selected + | 0 << OCTOSPI_CR_DQM_Pos // dual-memory mode disabled + ; + #else OCTOSPI1->CR = 3 << OCTOSPI_CR_FTHRES_Pos // 4 bytes must be available to read/write | 0 << OCTOSPI_CR_MSEL_Pos // FLASH 0 selected | 0 << OCTOSPI_CR_DMM_Pos // dual-memory mode disabled ; + #endif OCTOSPI1->DCR1 = (MICROPY_HW_OSPIFLASH_SIZE_BITS_LOG2 - 3 - 1) << OCTOSPI_DCR1_DEVSIZE_Pos From 20a95b249a439b4dc57da0c2beccac35ec45db76 Mon Sep 17 00:00:00 2001 From: nspsck Date: Fri, 17 Nov 2023 12:48:53 +0100 Subject: [PATCH 0224/1300] stm32/system_stm32: Allow selection of OSPI clock source. Added a #if-block to `system_stm32.c` to check whether `MICROPY_HW_RCC_OSPI_CLKSOURCE` is defined. If that is the case, the clock source for the OSPI will be changed to the specified source. Signed-off-by: nspsck --- ports/stm32/system_stm32.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ports/stm32/system_stm32.c b/ports/stm32/system_stm32.c index 2cff36c3a611c..b76a41e97bf78 100644 --- a/ports/stm32/system_stm32.c +++ b/ports/stm32/system_stm32.c @@ -461,6 +461,11 @@ MP_WEAK void SystemClock_Config(void) { PeriphClkInitStruct.QspiClockSelection = MICROPY_HW_RCC_QSPI_CLKSOURCE; #endif + #if defined(MICROPY_HW_RCC_OSPI_CLKSOURCE) + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_OSPI; + PeriphClkInitStruct.OspiClockSelection = MICROPY_HW_RCC_OSPI_CLKSOURCE; + #endif + #if defined(MICROPY_HW_RCC_SPI123_CLKSOURCE) PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_SPI123; PeriphClkInitStruct.Spi123ClockSelection = MICROPY_HW_RCC_SPI123_CLKSOURCE; From 6c3dc0c0b09701b83a957368220d9c8bfe40af0a Mon Sep 17 00:00:00 2001 From: nspsck Date: Tue, 26 Sep 2023 19:44:14 +0200 Subject: [PATCH 0225/1300] stm32/boards/STM32H7B3I_DK: Fix octo-spi pin configuration. The original OSPIFLASH settings in the `mpconfigboard.h` contained some mistakes that prevented the firmware from compiling. These are now corrected and the firmware can be built with support for OSPI flash. Note: external storage in OSPI flash is not yet configured on this board. Signed-off-by: nspsck --- ports/stm32/boards/STM32H7B3I_DK/mpconfigboard.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/stm32/boards/STM32H7B3I_DK/mpconfigboard.h b/ports/stm32/boards/STM32H7B3I_DK/mpconfigboard.h index f742241f7f869..88786e66d8d12 100644 --- a/ports/stm32/boards/STM32H7B3I_DK/mpconfigboard.h +++ b/ports/stm32/boards/STM32H7B3I_DK/mpconfigboard.h @@ -39,20 +39,20 @@ // SMPS configuration #define MICROPY_HW_PWR_SMPS_CONFIG (PWR_DIRECT_SMPS_SUPPLY) -#if 0 +#if !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE // 512MBit external OSPI flash, used for either the filesystem or XIP memory mapped #define MICROPY_HW_OSPIFLASH_SIZE_BITS_LOG2 (29) #define MICROPY_HW_OSPIFLASH_CS (pin_G6) -#define MICROPY_HW_OSPIFLASH_CLK (pin_B2) +#define MICROPY_HW_OSPIFLASH_SCK (pin_B2) #define MICROPY_HW_OSPIFLASH_DQS (pin_C5) -#define MICROPY_HW_OSPIFLASH_IO0 (pin_P8) +#define MICROPY_HW_OSPIFLASH_IO0 (pin_D11) #define MICROPY_HW_OSPIFLASH_IO1 (pin_F9) #define MICROPY_HW_OSPIFLASH_IO2 (pin_F7) #define MICROPY_HW_OSPIFLASH_IO3 (pin_F6) #define MICROPY_HW_OSPIFLASH_IO4 (pin_C1) #define MICROPY_HW_OSPIFLASH_IO5 (pin_H3) -#define MICROPY_HW_OSPIFLASH_IO6 (pin_D6) -#define MICROPY_HW_OSPIFLASH_IO7 (pin_G14) +#define MICROPY_HW_OSPIFLASH_IO6 (pin_G9) +#define MICROPY_HW_OSPIFLASH_IO7 (pin_D7) #endif // UART buses From 0b7f6e1d3d57036aaeb082bdc685310a42dd6cb0 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Sun, 21 Apr 2024 16:10:18 +0200 Subject: [PATCH 0226/1300] py/mkrules.mk: Fix 'make submodules' when building out-of-tree. When MicroPython is used as a submodule and built from the containing project, e.g. for the embed port, `make submodules` fails because it goes looking for the sub-sub-module paths in the outer repository instead of in the micropython repository. Fix this by invoking git inside the micropython submodule. Signed-off-by: Christian Walther --- py/mkrules.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/mkrules.mk b/py/mkrules.mk index 5050935873aff..0dc1cdfe15ac4 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -251,8 +251,8 @@ endif submodules: $(ECHO) "Updating submodules: $(GIT_SUBMODULES)" ifneq ($(GIT_SUBMODULES),) - $(Q)git submodule sync $(addprefix $(TOP)/,$(GIT_SUBMODULES)) - $(Q)git submodule update --init $(addprefix $(TOP)/,$(GIT_SUBMODULES)) + $(Q)cd $(TOP) && git submodule sync $(GIT_SUBMODULES) + $(Q)cd $(TOP) && git submodule update --init $(GIT_SUBMODULES) endif .PHONY: submodules From 260568e081a56488f122db41065e0059ca89c306 Mon Sep 17 00:00:00 2001 From: Rick Sorensen Date: Sun, 9 Jun 2024 21:34:01 -0500 Subject: [PATCH 0227/1300] samd/mcu/samd21: Allow user to enable additional options. Currently for samd21 some features are disable because of limited memory. With the ability to trade firmware and filesystem space, a user may wish to selectively enable some of these features. This change allows them to be enabled in board `mpconfigboard.h` or on the build command line for example. The selectively enable functions are: MICROPY_PY_FRAMEBUF, MICROPY_PY_SELECT, MICROPY_PY_ONEWIRE and MICROPY_PY_ASYNCIO. Signed-off-by: Rick Sorensen --- ports/samd/mcu/samd21/mpconfigmcu.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/samd/mcu/samd21/mpconfigmcu.h b/ports/samd/mcu/samd21/mpconfigmcu.h index fdad3fee4a71b..fbb5b28e2b95a 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.h +++ b/ports/samd/mcu/samd21/mpconfigmcu.h @@ -63,12 +63,20 @@ unsigned long trng_random_u32(int delay); #define MICROPY_PY_BUILTINS_ROUND_INT (SAMD21_EXTRA_FEATURES) #define MICROPY_CAN_OVERRIDE_BUILTINS (SAMD21_EXTRA_FEATURES) #define MICROPY_PY_SYS_STDIO_BUFFER (SAMD21_EXTRA_FEATURES) +#ifndef MICROPY_PY_FRAMEBUF #define MICROPY_PY_FRAMEBUF (SAMD21_EXTRA_FEATURES) +#endif +#ifndef MICROPY_PY_ASYNCIO #define MICROPY_PY_ASYNCIO (SAMD21_EXTRA_FEATURES) +#endif +#ifndef MICROPY_PY_SELECT #define MICROPY_PY_SELECT (SAMD21_EXTRA_FEATURES) +#endif #define MICROPY_PY_ERRNO (SAMD21_EXTRA_FEATURES) #define MICROPY_PY_DEFLATE (SAMD21_EXTRA_FEATURES) +#ifndef MICROPY_PY_ONEWIRE #define MICROPY_PY_ONEWIRE (SAMD21_EXTRA_FEATURES) +#endif #ifndef MICROPY_PY_MACHINE_PIN_BOARD_CPU #define MICROPY_PY_MACHINE_PIN_BOARD_CPU (1) From 706e09dff3d1253bc8b58331b52d04f406919596 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 13 Aug 2024 11:44:57 +0300 Subject: [PATCH 0228/1300] shared/tinyusb: Allow ports to define CDC TX/RX buffer sizes. Signed-off-by: iabdalkader --- shared/tinyusb/tusb_config.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shared/tinyusb/tusb_config.h b/shared/tinyusb/tusb_config.h index 1f8f5e5f6dc56..d0c9f89ec600c 100644 --- a/shared/tinyusb/tusb_config.h +++ b/shared/tinyusb/tusb_config.h @@ -77,8 +77,12 @@ // CDC Configuration #if CFG_TUD_CDC +#ifndef CFG_TUD_CDC_RX_BUFSIZE #define CFG_TUD_CDC_RX_BUFSIZE ((CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED) ? 512 : 256) +#endif +#ifndef CFG_TUD_CDC_TX_BUFSIZE #define CFG_TUD_CDC_TX_BUFSIZE ((CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED) ? 512 : 256) +#endif #define CFG_TUD_CDC_PERSISTENT_TX_BUFF (1) #endif From b82c9ca706a675b53200d8df27ee3903ff521c2d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 24 Jul 2024 17:02:43 +1000 Subject: [PATCH 0229/1300] extmod/modtls_mbedtls: Optimise the DER certificate parsing fix. Small code size and binary size optimisation for the fix merged in 4d6d84983f370e48e81fb05fe31802e0a13fb369. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- extmod/modtls_mbedtls.c | 49 +++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/extmod/modtls_mbedtls.c b/extmod/modtls_mbedtls.c index d3fc26fad2a54..30118e200dee0 100644 --- a/extmod/modtls_mbedtls.c +++ b/extmod/modtls_mbedtls.c @@ -100,11 +100,22 @@ static void mbedtls_debug(void *ctx, int level, const char *file, int line, cons } #endif -#if defined(MBEDTLS_PEM_PARSE_C) -static int mbedtls_is_pem(const byte *data, size_t len) { - return (len >= 10) && (strstr((const char *)data, "-----BEGIN") != NULL); +// Given a string-like object holding PEM or DER formatted ASN.1 data, return a +// pointer to its buffer and the correct length for mbedTLS APIs. +// +// (mbedTLS >= 3.5 rejects DER formatted data with trailing bytes within keylen, +// but PEM must include a terminating NUL byte in the keylen...) +static const unsigned char *asn1_get_data(mp_obj_t obj, size_t *out_len) { + size_t len; + const char *str = mp_obj_str_get_data(obj, &len); + #if defined(MBEDTLS_PEM_PARSE_C) + if (strstr(str, "-----BEGIN ") != NULL) { + ++len; + } + #endif + *out_len = len; + return (const unsigned char *)str; } -#endif static NORETURN void mbedtls_raise_error(int err) { // Handle special cases. @@ -352,15 +363,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(ssl_context_set_ciphers_obj, ssl_context_set_ci static void ssl_context_load_key(mp_obj_ssl_context_t *self, mp_obj_t key_obj, mp_obj_t cert_obj) { size_t key_len; - const byte *key = (const byte *)mp_obj_str_get_data(key_obj, &key_len); - - #if defined(MBEDTLS_PEM_PARSE_C) - // len should include terminating null if the data is PEM encoded - if (mbedtls_is_pem(key, key_len)) { - key_len += 1; - } - #endif - + const unsigned char *key = asn1_get_data(key_obj, &key_len); int ret; #if MBEDTLS_VERSION_NUMBER >= 0x03000000 ret = mbedtls_pk_parse_key(&self->pkey, key, key_len, NULL, 0, mbedtls_ctr_drbg_random, &self->ctr_drbg); @@ -372,15 +375,7 @@ static void ssl_context_load_key(mp_obj_ssl_context_t *self, mp_obj_t key_obj, m } size_t cert_len; - const byte *cert = (const byte *)mp_obj_str_get_data(cert_obj, &cert_len); - - #if defined(MBEDTLS_PEM_PARSE_C) - // len should include terminating null if the data is PEM encoded - if (mbedtls_is_pem(cert, cert_len)) { - cert_len += 1; - } - #endif - + const unsigned char *cert = asn1_get_data(cert_obj, &cert_len); ret = mbedtls_x509_crt_parse(&self->cert, cert, cert_len); if (ret != 0) { mbedtls_raise_error(MBEDTLS_ERR_X509_BAD_INPUT_DATA); // use general error for all cert errors @@ -402,15 +397,7 @@ static MP_DEFINE_CONST_FUN_OBJ_3(ssl_context_load_cert_chain_obj, ssl_context_lo static void ssl_context_load_cadata(mp_obj_ssl_context_t *self, mp_obj_t cadata_obj) { size_t cacert_len; - const byte *cacert = (const byte *)mp_obj_str_get_data(cadata_obj, &cacert_len); - - #if defined(MBEDTLS_PEM_PARSE_C) - // len should include terminating null if the data is PEM encoded - if (mbedtls_is_pem(cacert, cacert_len)) { - cacert_len += 1; - } - #endif - + const unsigned char *cacert = asn1_get_data(cadata_obj, &cacert_len); int ret = mbedtls_x509_crt_parse(&self->cacert, cacert, cacert_len); if (ret != 0) { mbedtls_raise_error(MBEDTLS_ERR_X509_BAD_INPUT_DATA); // use general error for all cert errors From e901ff85573d4cbec1e2656aea2e5603d6bf5bd9 Mon Sep 17 00:00:00 2001 From: Jared Hancock Date: Sat, 17 Aug 2024 23:09:52 -0500 Subject: [PATCH 0230/1300] extmod/network_wiznet5k: Add support for IPv6. This adds support for the WIZNET5K nic to use IPv6 with the LWIP stack. Additionally, if LWIP_IPV6 is disabled, the device is configured to drop all IPv6 packets to reduce load on the MCU. Signed-off-by: Jared Hancock --- extmod/network_wiznet5k.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/extmod/network_wiznet5k.c b/extmod/network_wiznet5k.c index 1eaabe9ebeac3..96f9b6131c4b7 100644 --- a/extmod/network_wiznet5k.c +++ b/extmod/network_wiznet5k.c @@ -61,6 +61,7 @@ #include "lwip/err.h" #include "lwip/dns.h" #include "lwip/dhcp.h" +#include "lwip/ethip6.h" #include "netif/etharp.h" #define TRACE_ETH_TX (0x0002) @@ -297,13 +298,21 @@ static err_t wiznet5k_netif_init(struct netif *netif) { netif->hwaddr_len = sizeof(netif->hwaddr); int ret = WIZCHIP_EXPORT(socket)(0, Sn_MR_MACRAW, 0, 0); if (ret != 0) { - printf("WIZNET fatal error in netifinit: %d\n", ret); + printf("WIZNET fatal error in netif_init: %d\n", ret); return ERR_IF; } // Enable MAC filtering so we only get frames destined for us, to reduce load on lwIP setSn_MR(0, getSn_MR(0) | Sn_MR_MFEN); + #if LWIP_IPV6 + netif->output_ip6 = ethip6_output; + netif->flags |= NETIF_FLAG_MLD6; + #else + // Drop IPv6 packets if firmware does not support it + setSn_MR(0, getSn_MR(0) | Sn_MR_MIP6B); + #endif + return ERR_OK; } @@ -847,6 +856,10 @@ static mp_obj_t wiznet5k_active(size_t n_args, const mp_obj_t *args) { setSHAR(mac); } + #if WIZNET5K_WITH_LWIP_STACK && LWIP_IPV6 + netif_create_ip6_linklocal_address(&self->netif, 1); + #endif + // seems we need a small delay after init mp_hal_delay_ms(250); From a8d1c25a1b32759642f98e994800b39457ec6add Mon Sep 17 00:00:00 2001 From: stijn Date: Wed, 24 Apr 2024 14:17:55 +0200 Subject: [PATCH 0231/1300] unix/coveragecpp: Include all API headers in the C++ code. Make the CI builds compile the public API as C++ to catch accidental introductions of incompatible code. Signed-off-by: stijn --- ports/unix/coveragecpp.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ports/unix/coveragecpp.cpp b/ports/unix/coveragecpp.cpp index 93c1b387fe285..23c3955ae9d0b 100644 --- a/ports/unix/coveragecpp.cpp +++ b/ports/unix/coveragecpp.cpp @@ -1,5 +1,20 @@ extern "C" { -#include "py/obj.h" +// Include the complete public API to verify everything compiles as C++. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include } #if defined(MICROPY_UNIX_COVERAGE) From b095c097e6cc49de6ba51f4d2ba43258f7f69d1b Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Aug 2024 13:54:33 +1000 Subject: [PATCH 0232/1300] tools/pyboard.py: Capture stdout for pts line. The pts line printed by qemu-system-arm goes to stdout, not stderr. Redirect stderr to stdout in case other tools do print to stderr. Signed-off-by: Damien George --- tools/pyboard.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/pyboard.py b/tools/pyboard.py index c422b64ac5516..55cbe8384d580 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -235,9 +235,9 @@ def __init__(self, cmd): preexec_fn=os.setsid, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + stderr=subprocess.STDOUT, ) - pty_line = self.subp.stderr.readline().decode("utf-8") + pty_line = self.subp.stdout.readline().decode("utf-8") m = re.search(r"/dev/pts/[0-9]+", pty_line) if not m: print("Error: unable to find PTY device in startup line:", pty_line) From 86aa61918a67b626809e8d132a80a63daf380b1c Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 19 Aug 2024 12:15:17 +1000 Subject: [PATCH 0233/1300] tests/run-tests.py: Skip additional tests when slice unavailable. Both of these tests require slice to be enabled. Signed-off-by: Damien George --- tests/run-tests.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run-tests.py b/tests/run-tests.py index 83344714c0375..d0c93f74b9d39 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -563,6 +563,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # These tests don't test slice explicitly but rather use it to perform the test misc_slice_tests = ( "builtin_range", + "bytearray1", "class_super", "containment", "errno1", @@ -573,6 +574,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): "memoryview_gc", "object1", "python34", + "string_format_modulo", "struct_endian", ) From 05cad7b56f5d460db26a468a05bfdeabe4a656db Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 19 Aug 2024 10:43:43 +1000 Subject: [PATCH 0234/1300] zephyr: Remove obsolete tinytest test framework. Commit f573e73baeddfb8fde59413e8053b538cf908f9b rewored the zephyr port to build MicroPython as a cmake target, and since that commit the `make-bin-testsuite` helper script no longer works (it requires a Makefile) and hence the tinytest test framework can no longer be run. Instead of fixing this, remove the option to use tinytest. Boards running zephyr can use the standard `tests/run-tests.py` script to run tests in the usual way. Signed-off-by: Damien George --- ports/zephyr/main.c | 14 ---------- ports/zephyr/make-bin-testsuite | 20 --------------- ports/zephyr/mpconfigport_bin_testsuite.h | 31 ----------------------- 3 files changed, 65 deletions(-) delete mode 100755 ports/zephyr/make-bin-testsuite delete mode 100644 ports/zephyr/mpconfigport_bin_testsuite.h diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 2ae5fd22273c1..c4a135aa53e43 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -59,13 +59,6 @@ #include "modmachine.h" #include "modzephyr.h" -#ifdef TEST -#include "shared/upytesthelper/upytesthelper.h" -#include "lib/tinytest/tinytest.c" -#include "shared/upytesthelper/upytesthelper.c" -#include TEST -#endif - static char heap[MICROPY_HEAP_SIZE]; void init_zephyr(void) { @@ -128,13 +121,6 @@ int real_main(void) { init_zephyr(); mp_hal_init(); - #ifdef TEST - static const char *argv[] = {"test"}; - upytest_set_heap(heap, heap + sizeof(heap)); - int r = tinytest_main(1, argv, groups); - printf("status: %d\n", r); - #endif - soft_reset: #if MICROPY_ENABLE_GC gc_init(heap, heap + sizeof(heap)); diff --git a/ports/zephyr/make-bin-testsuite b/ports/zephyr/make-bin-testsuite deleted file mode 100755 index 9a8c329772143..0000000000000 --- a/ports/zephyr/make-bin-testsuite +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# -# This is a wrapper for make to build a binary with builtin testsuite. -# It should be run just like make (i.e. extra vars can be passed on the -# command line, etc.), e.g.: -# -# ./make-bin-testsuite BOARD=qemu_cortex_m3 -# ./make-bin-testsuite BOARD=qemu_cortex_m3 run -# - -(cd ../../tests; ./run-tests.py --write-exp) -(cd ../../tests; ./run-tests.py --list-tests --target=minimal \ - -e async -e intbig -e int_big -e builtin_help -e memstats -e bytes_compare3 -e class_reverse_op \ - -e /set -e frozenset -e complex -e const -e native -e viper \ - -e 'float_divmod\.' -e float_parse_doubleprec -e float/true_value -e float/types \ - | ../tools/tinytest-codegen.py --stdin) > bin-testsuite.c - -make \ - CFLAGS_EXTRA='-DMP_CONFIGFILE="" -DTEST=\"bin-testsuite.c\" -DNO_FORKING' \ - "$@" diff --git a/ports/zephyr/mpconfigport_bin_testsuite.h b/ports/zephyr/mpconfigport_bin_testsuite.h deleted file mode 100644 index 4367067af3eb5..0000000000000 --- a/ports/zephyr/mpconfigport_bin_testsuite.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Linaro Limited - * - * 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 "mpconfigport.h" - -#ifdef TEST -#include "shared/upytesthelper/upytesthelper.h" -#define MP_PLAT_PRINT_STRN(str, len) upytest_output(str, len) -#endif From 9af333f2c62de02da2184821c3a59e4d462365ea Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 19 Aug 2024 12:15:50 +1000 Subject: [PATCH 0235/1300] zephyr: Increase CONFIG_CONSOLE_GETCHAR_BUFSIZE to 258. It needs to be at least this big for `tools/pyboard.py` to work, which is used (among other things) by `tests/run-tests.py`. Signed-off-by: Damien George --- ports/zephyr/prj.conf | 2 +- ports/zephyr/prj_minimal.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/zephyr/prj.conf b/ports/zephyr/prj.conf index 6046690197c25..d3983fcc25102 100644 --- a/ports/zephyr/prj.conf +++ b/ports/zephyr/prj.conf @@ -8,7 +8,7 @@ CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS=y CONFIG_CONSOLE_SUBSYS=y CONFIG_CONSOLE_GETCHAR=y -CONFIG_CONSOLE_GETCHAR_BUFSIZE=128 +CONFIG_CONSOLE_GETCHAR_BUFSIZE=258 CONFIG_CONSOLE_PUTCHAR_BUFSIZE=128 CONFIG_NEWLIB_LIBC=y diff --git a/ports/zephyr/prj_minimal.conf b/ports/zephyr/prj_minimal.conf index 806e26af77841..2c89a02fae263 100644 --- a/ports/zephyr/prj_minimal.conf +++ b/ports/zephyr/prj_minimal.conf @@ -6,7 +6,7 @@ CONFIG_POLL=y CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_CONSOLE_SUBSYS=y CONFIG_CONSOLE_GETCHAR=y -CONFIG_CONSOLE_GETCHAR_BUFSIZE=32 +CONFIG_CONSOLE_GETCHAR_BUFSIZE=258 CONFIG_CONSOLE_PUTCHAR_BUFSIZE=32 # TODO: Disable once https://github.com/zephyrproject-rtos/zephyr/pull/13731 is merged #CONFIG_CONSOLE=n From c8838b5004d32351ffea89b2f224c22c65f9e375 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 19 Aug 2024 12:17:21 +1000 Subject: [PATCH 0236/1300] github/workflows: Add CI to run tests against zephyr with qemu. With this change, the zephyr port is tested against the standard test suite via the following scheme: - the zephyr port is built with the `qemu_cortex_m3` board and the `prj_minimal.conf` configuration - `qemu-system-arm` runs `zephyr.elf` - the zephyr console is redirected to a pts/pty - `tests/run-tests.py` is run in bare-metal mode against the pts/pty device This allows testing the zephyr port as though it were a physical board attached over a serial port. Signed-off-by: Damien George --- .github/workflows/ports_zephyr.yml | 5 +++++ tools/ci.sh | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/.github/workflows/ports_zephyr.yml b/.github/workflows/ports_zephyr.yml index f6f328c9277e4..d075582f30919 100644 --- a/.github/workflows/ports_zephyr.yml +++ b/.github/workflows/ports_zephyr.yml @@ -27,3 +27,8 @@ jobs: run: source tools/ci.sh && ci_zephyr_install - name: Build run: source tools/ci.sh && ci_zephyr_build + - name: Run main test suite + run: source tools/ci.sh && ci_zephyr_run_tests + - name: Print failures + if: failure() + run: tests/run-tests.py --print-failures diff --git a/tools/ci.sh b/tools/ci.sh index 03b6bf59aed4a..7353a3ecdfb7f 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -749,6 +749,11 @@ function ci_zephyr_setup { -w /micropython/ports/zephyr \ zephyrprojectrtos/ci:${ZEPHYR_DOCKER_VERSION} docker ps -a + + # qemu-system-arm is needed to run the test suite. + sudo apt-get update + sudo apt-get install qemu-system-arm + qemu-system-arm --version } function ci_zephyr_install { @@ -763,3 +768,10 @@ function ci_zephyr_build { docker exec zephyr-ci west build -p auto -b mimxrt1050_evk docker exec zephyr-ci west build -p auto -b nucleo_wb55rg # for bluetooth } + +function ci_zephyr_run_tests { + docker exec zephyr-ci west build -p auto -b qemu_cortex_m3 -- -DCONF_FILE=prj_minimal.conf + # Issues with zephyr tests: + # - inf_nan_arith fails pow(-1, nan) test + (cd tests && ./run-tests.py --target minimal --device execpty:"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -monitor null -serial pty -kernel ../ports/zephyr/build/zephyr/zephyr.elf" -d basics float --exclude inf_nan_arith) +} From 9f9c283ef48950487bb566e9717819e8a2a29ac6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Aug 2024 11:17:00 +1000 Subject: [PATCH 0237/1300] shared/runtime/semihosting_arm: Support semihosting on non-Thumb ARM. Signed-off-by: Damien George --- shared/runtime/semihosting_arm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/shared/runtime/semihosting_arm.c b/shared/runtime/semihosting_arm.c index 3ca29d5d752c4..5cfaaa3c6b3c7 100644 --- a/shared/runtime/semihosting_arm.c +++ b/shared/runtime/semihosting_arm.c @@ -40,10 +40,6 @@ #define OPEN_MODE_READ (0) // mode "r" #define OPEN_MODE_WRITE (4) // mode "w" -#ifndef __thumb__ -#error Semihosting is only implemented for ARM microcontrollers. -#endif - static int mp_semihosting_stdout; static uint32_t mp_semihosting_call(uint32_t num, const void *arg) { @@ -61,7 +57,13 @@ static uint32_t mp_semihosting_call(uint32_t num, const void *arg) { register uint32_t num_reg __asm__ ("r0") = num; register const void *args_reg __asm__ ("r1") = arg; __asm__ __volatile__ ( + #if defined(__ARM_ARCH_ISA_ARM) + "svc 0x00123456\n" // invoke semihosting call + #elif defined(__ARM_ARCH_ISA_THUMB) "bkpt 0xAB\n" // invoke semihosting call + #else + #error Unknown architecture + #endif : "+r" (num_reg) // call number and result : "r" (args_reg) // arguments : "memory"); // make sure args aren't optimized away From 70a6791b09f9fad55b99e5996433888cee0a7a64 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Aug 2024 13:54:14 +1000 Subject: [PATCH 0238/1300] shared/runtime/semihosting_arm: Add mp_semihosting_rx_chars. Signed-off-by: Damien George --- shared/runtime/semihosting_arm.c | 15 +++++++++++++++ shared/runtime/semihosting_arm.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/shared/runtime/semihosting_arm.c b/shared/runtime/semihosting_arm.c index 5cfaaa3c6b3c7..d44124faabadf 100644 --- a/shared/runtime/semihosting_arm.c +++ b/shared/runtime/semihosting_arm.c @@ -34,6 +34,7 @@ #define SYS_OPEN 0x01 #define SYS_WRITEC 0x03 #define SYS_WRITE 0x05 +#define SYS_READ 0x06 #define SYS_READC 0x07 // Constants: @@ -91,6 +92,20 @@ int mp_semihosting_rx_char() { return mp_semihosting_call(SYS_READC, NULL); } +// Returns 0 on success. +int mp_semihosting_rx_chars(char *str, size_t len) { + struct { + uint32_t fd; + const char *str; + uint32_t len; + } args = { + .fd = mp_semihosting_stdout, + .str = str, + .len = len, + }; + return mp_semihosting_call(SYS_READ, &args); +} + static void mp_semihosting_tx_char(char c) { mp_semihosting_call(SYS_WRITEC, &c); } diff --git a/shared/runtime/semihosting_arm.h b/shared/runtime/semihosting_arm.h index 7e90f25ac916c..1faaae7fec7d8 100644 --- a/shared/runtime/semihosting_arm.h +++ b/shared/runtime/semihosting_arm.h @@ -38,6 +38,8 @@ Then make sure the debugger is attached and enables semihosting. In OpenOCD thi done with ARM semihosting enable followed by reset. The terminal will need further configuration to work with MicroPython (bash: stty raw -echo). +If mp_semihosting_rx_char() doesn't work then try mp_semihosting_rx_chars(str, 1). + */ #include @@ -45,6 +47,7 @@ configuration to work with MicroPython (bash: stty raw -echo). void mp_semihosting_init(); int mp_semihosting_rx_char(); +int mp_semihosting_rx_chars(char *str, size_t len); uint32_t mp_semihosting_tx_strn(const char *str, size_t len); uint32_t mp_semihosting_tx_strn_cooked(const char *str, size_t len); From 1090f1a60c730cd589f65047e105a22a0445721d Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Aug 2024 11:17:26 +1000 Subject: [PATCH 0239/1300] shared/runtime/semihosting_arm: Add mp_semihosting_exit. Signed-off-by: Damien George --- shared/runtime/semihosting_arm.c | 8 ++++++++ shared/runtime/semihosting_arm.h | 1 + 2 files changed, 9 insertions(+) diff --git a/shared/runtime/semihosting_arm.c b/shared/runtime/semihosting_arm.c index d44124faabadf..f4d168f79bc81 100644 --- a/shared/runtime/semihosting_arm.c +++ b/shared/runtime/semihosting_arm.c @@ -36,6 +36,7 @@ #define SYS_WRITE 0x05 #define SYS_READ 0x06 #define SYS_READC 0x07 +#define SYS_EXIT 0x18 // Constants: #define OPEN_MODE_READ (0) // mode "r" @@ -88,6 +89,13 @@ void mp_semihosting_init() { mp_semihosting_stdout = mp_semihosting_open_console(OPEN_MODE_WRITE); } +void mp_semihosting_exit(int status) { + if (status == 0) { + status = 0x20026; + } + mp_semihosting_call(SYS_EXIT, (void *)(uintptr_t)status); +} + int mp_semihosting_rx_char() { return mp_semihosting_call(SYS_READC, NULL); } diff --git a/shared/runtime/semihosting_arm.h b/shared/runtime/semihosting_arm.h index 1faaae7fec7d8..08fb66578ac14 100644 --- a/shared/runtime/semihosting_arm.h +++ b/shared/runtime/semihosting_arm.h @@ -46,6 +46,7 @@ If mp_semihosting_rx_char() doesn't work then try mp_semihosting_rx_chars(str, 1 #include void mp_semihosting_init(); +void mp_semihosting_exit(int status); int mp_semihosting_rx_char(); int mp_semihosting_rx_chars(char *str, size_t len); uint32_t mp_semihosting_tx_strn(const char *str, size_t len); From e8863e44e51bdd22a684f88779770845f0e7245b Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Aug 2024 11:16:10 +1000 Subject: [PATCH 0240/1300] qemu-arm/Makefile: Make the build directory reflect the board. So multiple boards can be built at once. Signed-off-by: Damien George --- ports/qemu-arm/Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index f521a0c5ad82d..5ff63bf7daacd 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -1,3 +1,8 @@ +BOARD ?= mps2-an385 + +# Make the build directory reflect the board. +BUILD ?= build-$(BOARD) + include ../../py/mkenv.mk -include mpconfigport.mk @@ -11,8 +16,6 @@ MICROPY_ROM_TEXT_COMPRESSION ?= 1 include $(TOP)/py/py.mk include $(TOP)/extmod/extmod.mk -BOARD ?= mps2-an385 - ifeq ($(BOARD),netduino2) CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft CFLAGS += -DQEMU_SOC_STM32 From 8a3842eba7db90af2b29035c5c1ecfd5b140f493 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Aug 2024 11:20:30 +1000 Subject: [PATCH 0241/1300] qemu-arm/uart: Implement uart_rx_chr. Signed-off-by: Damien George --- ports/qemu-arm/uart.c | 57 +++++++++++++++++++++++++++++++++++++++---- ports/qemu-arm/uart.h | 4 +++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/ports/qemu-arm/uart.c b/ports/qemu-arm/uart.c index d7338f9a24230..5ace3d465f147 100644 --- a/ports/qemu-arm/uart.c +++ b/ports/qemu-arm/uart.c @@ -31,14 +31,29 @@ #if defined(QEMU_SOC_STM32) +#define UART_SR_RXNE (1 << 5) +#define UART_CR1_UE (1 << 13) +#define UART_CR1_TE (1 << 3) +#define UART_CR1_RE (1 << 2) + typedef struct _UART_t { volatile uint32_t SR; volatile uint32_t DR; + volatile uint32_t BRR; + volatile uint32_t CR1; } UART_t; #define UART0 ((UART_t *)(0x40011000)) void uart_init(void) { + UART0->CR1 = UART_CR1_UE | UART_CR1_TE | UART_CR1_RE; +} + +int uart_rx_chr(void) { + if (!(UART0->SR & UART_SR_RXNE)) { + return UART_RX_NO_CHAR; + } + return UART0->DR; } void uart_tx_strn(const char *buf, size_t len) { @@ -50,11 +65,15 @@ void uart_tx_strn(const char *buf, size_t len) { #elif defined(QEMU_SOC_NRF51) typedef struct _UART_t { - volatile uint32_t r0[2]; + volatile uint32_t STARTRX; // 0x000 + volatile uint32_t STOPRX; // 0x004 volatile uint32_t STARTTX; // 0x008 - volatile uint32_t r1[(0x500 - 0x008) / 4 - 1]; + volatile uint32_t r0[(0x108 - 0x008) / 4 - 1]; + volatile uint32_t RXDRDY; // 0x108 + volatile uint32_t r1[(0x500 - 0x108) / 4 - 1]; volatile uint32_t ENABLE; // 0x500 - volatile uint32_t r2[(0x51c - 0x500) / 4 - 1]; + volatile uint32_t r2[(0x518 - 0x500) / 4 - 1]; + volatile uint32_t RXD; // 0x518 volatile uint32_t TXD; // 0x51c } UART_t; @@ -62,9 +81,18 @@ typedef struct _UART_t { void uart_init(void) { UART0->ENABLE = 4; + UART0->STARTRX = 1; UART0->STARTTX = 1; } +int uart_rx_chr(void) { + if (!UART0->RXDRDY) { + return UART_RX_NO_CHAR; + } + UART0->RXDRDY = 0; + return UART0->RXD; +} + void uart_tx_strn(const char *buf, size_t len) { for (size_t i = 0; i < len; ++i) { UART0->TXD = buf[i]; @@ -74,6 +102,7 @@ void uart_tx_strn(const char *buf, size_t len) { #elif defined(QEMU_SOC_MPS2) #define UART_STATE_TXFULL (1 << 0) +#define UART_STATE_RXFULL (1 << 1) #define UART_CTRL_TX_EN (1 << 0) #define UART_CTRL_RX_EN (1 << 1) @@ -90,7 +119,14 @@ typedef struct _UART_t { void uart_init(void) { UART0->BAUDDIV = 16; - UART0->CTRL = UART_CTRL_TX_EN; + UART0->CTRL = UART_CTRL_TX_EN | UART_CTRL_RX_EN; +} + +int uart_rx_chr(void) { + if (!(UART0->STATE & UART_STATE_RXFULL)) { + return UART_RX_NO_CHAR; + } + return UART0->DATA; } void uart_tx_strn(const char *buf, size_t len) { @@ -104,7 +140,9 @@ void uart_tx_strn(const char *buf, size_t len) { #elif defined(QEMU_SOC_IMX6) #define UART_UCR1_UARTEN (1 << 0) +#define UART_UCR2_RXEN (1 << 1) #define UART_UCR2_TXEN (1 << 2) +#define UART_UTS1_RXEMPTY (1 << 5) typedef struct _UART_t { volatile uint32_t URXD; // 0x00 @@ -113,13 +151,22 @@ typedef struct _UART_t { volatile uint32_t r1[15]; volatile uint32_t UCR1; // 0x80 volatile uint32_t UCR2; // 0x84 + volatile uint32_t r2[11]; + volatile uint32_t UTS1; // 0xb4 } UART_t; #define UART1 ((UART_t *)(0x02020000)) void uart_init(void) { UART1->UCR1 = UART_UCR1_UARTEN; - UART1->UCR2 = UART_UCR2_TXEN; + UART1->UCR2 = UART_UCR2_TXEN | UART_UCR2_RXEN; +} + +int uart_rx_chr(void) { + if (UART1->UTS1 & UART_UTS1_RXEMPTY) { + return UART_RX_NO_CHAR; + } + return UART1->URXD & 0xff; } void uart_tx_strn(const char *buf, size_t len) { diff --git a/ports/qemu-arm/uart.h b/ports/qemu-arm/uart.h index 33eae05bd76dd..9c62a295d1d04 100644 --- a/ports/qemu-arm/uart.h +++ b/ports/qemu-arm/uart.h @@ -28,7 +28,11 @@ #include +// Returned from uart_rx_chr when there are no chars available. +#define UART_RX_NO_CHAR (-1) + void uart_init(void); +int uart_rx_chr(void); void uart_tx_strn(const char *buf, size_t len); #endif // MICROPY_INCLUDED_QEMU_ARM_UART_H From d9a0fdda9a7b0db55c1115b55bb1b83cd5ce739c Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Aug 2024 11:32:28 +1000 Subject: [PATCH 0242/1300] qemu-arm: Rework to provide a REPL and run tests via a pty serial port. Currently, the qemu-arm (and qemu-riscv) port has two build modes: - a simple test that executes a Python string; and - a full test that uses tinytest to embed all tests within the firmware, then executes that and captures the output. This is very different to all the other ports. A difficulty with using tinytest is that with the large number of tests the firmware overflows its virtual flash size. It's also hard to run tests via .mpy files and with the native emitter. Being different to the other ports also means an extra burden on maintenance. This commit reworks the qemu-arm port so that it has a single build target that creates a standard firmware which has a REPL. When run under qemu-system-arm, the REPL acts like any other bare-metal port, complete with soft reset (use machine.reset() to turn it off and exit qemu-system-arm). This approach gives many benefits: - allows playing with a REPL without hardware; - allows running the test suite as it would on a bare-metal board, by making qemu-system-arm redirect the UART serial of the virtual device to a /dev/pts/xx file, and then running run-tests.py against that serial device; - skipping tests is now done via the logic in `run-tests.py` and no longer needs multiple places to define which tests to skip (`tools/tinytest-codegen.py`, `ports/qemu-arm/tests_profile.txt` and also `tests/run-tests.py`); - allows testing/using mpremote with the qemu-arm port. Eventually the qemu-riscv port would have a similar change. Prior to this commit the test results were: 743 tests ok. (121 skipped) With this commit the test results are: 753 tests performed (22673 individual testcases) 753 tests passed 138 tests skipped More tests are skipped because more are included in the run. But overall more tests pass. Signed-off-by: Damien George --- .github/workflows/ports_qemu-arm.yml | 2 +- ports/qemu-arm/Makefile | 41 +++++++++--- ports/qemu-arm/Makefile.test | 33 ---------- ports/qemu-arm/README.md | 59 ++++++++++++++--- ports/qemu-arm/main.c | 53 ++++++++------- ports/qemu-arm/modmachine.c | 16 +++++ ports/qemu-arm/mpconfigport.h | 13 +--- ports/qemu-arm/mphalport.c | 66 +++++++++++++++++++ ports/qemu-arm/mphalport.h | 5 +- ports/qemu-arm/startup.c | 29 ++------ ports/qemu-arm/tests_profile.txt | 16 ----- ports/qemu-riscv/Makefile | 2 +- .../test_main.c => qemu-riscv/main.c} | 38 ++++++----- tests/run-tests.py | 12 ++-- tools/ci.sh | 6 +- 15 files changed, 237 insertions(+), 154 deletions(-) delete mode 100644 ports/qemu-arm/Makefile.test create mode 100644 ports/qemu-arm/mphalport.c delete mode 100644 ports/qemu-arm/tests_profile.txt rename ports/{qemu-arm/test_main.c => qemu-riscv/main.c} (65%) diff --git a/.github/workflows/ports_qemu-arm.yml b/.github/workflows/ports_qemu-arm.yml index db3cd7871d591..99750b7535657 100644 --- a/.github/workflows/ports_qemu-arm.yml +++ b/.github/workflows/ports_qemu-arm.yml @@ -29,4 +29,4 @@ jobs: run: source tools/ci.sh && ci_qemu_arm_build - name: Print failures if: failure() - run: grep --before-context=100 --text "FAIL" ports/qemu-arm/build/console.out + run: tests/run-tests.py --print-failures diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index 5ff63bf7daacd..cdfc39580b9d8 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -11,14 +11,19 @@ QSTR_DEFS = qstrdefsport.h # MicroPython feature configurations MICROPY_ROM_TEXT_COMPRESSION ?= 1 +FROZEN_MANIFEST ?= "freeze('test-frzmpy')" # include py core make definitions include $(TOP)/py/py.mk include $(TOP)/extmod/extmod.mk +CFLAGS += -DMICROPY_HW_BOARD_NAME='"$(BOARD)"' +QEMU_ARGS += -machine $(BOARD) -nographic -monitor null -semihosting + ifeq ($(BOARD),netduino2) CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft CFLAGS += -DQEMU_SOC_STM32 +CFLAGS += -DMICROPY_HW_MCU_NAME='"STM32"' LDSCRIPT = stm32.ld SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o MPY_CROSS_FLAGS += -march=armv7m @@ -27,8 +32,9 @@ endif ifeq ($(BOARD),microbit) CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft CFLAGS += -DQEMU_SOC_NRF51 +CFLAGS += -DMICROPY_HW_MCU_NAME='"nRF51"' LDSCRIPT = nrf51.ld -QEMU_EXTRA = -global nrf51-soc.flash-size=1048576 -global nrf51-soc.sram-size=262144 +QEMU_ARGS += -global nrf51-soc.flash-size=1048576 -global nrf51-soc.sram-size=262144 SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb1.o MPY_CROSS_FLAGS += -march=armv7m endif @@ -36,6 +42,7 @@ endif ifeq ($(BOARD),mps2-an385) CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft CFLAGS += -DQEMU_SOC_MPS2 +CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-M3"' LDSCRIPT = mps2.ld SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o MPY_CROSS_FLAGS += -march=armv7m @@ -44,11 +51,16 @@ endif ifeq ($(BOARD),sabrelite) CFLAGS += -mcpu=cortex-a9 CFLAGS += -DQEMU_SOC_IMX6 +CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-A9"' LDSCRIPT = imx6.ld -QEMU_EXTRA = -m 128M +QEMU_ARGS += -m 128M SRC_BOARD_O = shared/runtime/gchelper_generic.o # It's really armv7a but closest supported value is armv6. MPY_CROSS_FLAGS += -march=armv6 +# Cortex-A9 should support unaligned-access, but qemu doesn't seem to. +CFLAGS += -mno-unaligned-access +# These don't work on Cortex-A9. +TESTS_EXCLUDE = --exclude '(asmdiv|asmspecialregs).py' endif CROSS_COMPILE ?= arm-none-eabi- @@ -81,16 +93,18 @@ LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) SRC_COMMON_C = \ startup.c \ uart.c \ + mphalport.c \ shared/libc/string0.c \ + shared/readline/readline.c \ + shared/runtime/interrupt_char.c \ + shared/runtime/pyexec.c \ + shared/runtime/semihosting_arm.c \ + shared/runtime/stdout_helpers.c \ shared/runtime/sys_stdio_mphal.c \ SRC_RUN_C = \ main.c \ -SRC_TEST_C = \ - test_main.c \ - lib/tinytest/tinytest.c \ - LIB_SRC_C += $(SRC_LIB_LIBM_C) LIB_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C) @@ -116,10 +130,21 @@ OBJ = $(OBJ_COMMON) $(OBJ_RUN) $(OBJ_TEST) # List of sources for qstr extraction SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C) -all: run +all: $(BUILD)/firmware.elf +.PHONY: repl +repl: $(BUILD)/firmware.elf + $(ECHO) "Use machine.reset() to exit" + qemu-system-arm $(QEMU_ARGS) -serial mon:stdio -kernel $< + +.PHONY: run run: $(BUILD)/firmware.elf - qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< + qemu-system-arm $(QEMU_ARGS) -serial pty -kernel $< + +.PHONY: test +test: $(BUILD)/firmware.elf + $(eval DIRNAME=ports/$(notdir $(CURDIR))) + cd $(TOP)/tests && ./run-tests.py --target qemu-arm --device execpty:"qemu-system-arm $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(TESTS_EXCLUDE) ## `$(LD)` doesn't seem to like `--specs` for some reason, but we can just use `$(CC)` here. $(BUILD)/firmware.elf: $(LDSCRIPT) $(ALL_OBJ_RUN) diff --git a/ports/qemu-arm/Makefile.test b/ports/qemu-arm/Makefile.test deleted file mode 100644 index cb5b0927c8c9a..0000000000000 --- a/ports/qemu-arm/Makefile.test +++ /dev/null @@ -1,33 +0,0 @@ -LIB_SRC_C = shared/upytesthelper/upytesthelper.c - -FROZEN_MANIFEST ?= "freeze('test-frzmpy')" - -include Makefile - -ifeq ($(BOARD),sabrelite) -# These don't work on Cortex-A9. -TESTS_EXCLUDE = inlineasm/asmdiv.py inlineasm/asmspecialregs.py -endif - -CFLAGS += -DTEST - -.PHONY: $(BUILD)/genhdr/tests.h - -TESTS_PROFILE = $(dir $(abspath $(firstword $(MAKEFILE_LIST))))/tests_profile.txt - -$(BUILD)/test_main.o: $(BUILD)/genhdr/tests.h -$(BUILD)/genhdr/tests.h: - (cd $(TOP)/tests; ./run-tests.py --target=qemu-arm --write-exp) - $(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py --profile $(TESTS_PROFILE) $(addprefix --exclude ,$(TESTS_EXCLUDE))) > $@ - -$(BUILD)/lib/tinytest/tinytest.o: CFLAGS += -DNO_FORKING - -$(BUILD)/firmware-test.elf: $(LDSCRIPT) $(ALL_OBJ_TEST) - $(Q)$(LD) $(LDFLAGS) -o $@ $(ALL_OBJ_TEST) $(LIBS) - $(Q)$(SIZE) $@ - -# Note: Using timeout(1) to handle cases where qemu hangs (e.g. this can happen with alignment errors). -test: $(BUILD)/firmware-test.elf - timeout --foreground -k 5s 30s qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< > $(BUILD)/console.out - $(Q)tail -n2 $(BUILD)/console.out - $(Q)tail -n1 $(BUILD)/console.out | grep -q "status: 0" diff --git a/ports/qemu-arm/README.md b/ports/qemu-arm/README.md index f821c4d1e28fb..34d73fd165756 100644 --- a/ports/qemu-arm/README.md +++ b/ports/qemu-arm/README.md @@ -1,3 +1,6 @@ +MicroPython port to qemu-arm +============================ + This is experimental, community-supported port for Cortex-M emulation as provided by QEMU (http://qemu.org). @@ -15,14 +18,52 @@ The purposes of this port are to enable: - no need to use OpenOCD or anything else that might slow down the process in terms of plugging things together, pressing buttons, etc. -This port will only work with the [GNU ARM Embedded Toolchain]( -https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads - and not with CodeSourcery toolchain. You will need to modify -`LDFLAGS` if you want to use CodeSourcery's version of `arm-none-eabi`. -The difference is that CodeSourcery needs `-T generic-m-hosted.ld` while -ARM's version requires `--specs=nano.specs --specs=rdimon.specs` to be -passed to the linker. +Build instructions +------------------ + +First make sure the MicroPython cross-compiler is built (run from this directory): + + $ make -C ../../mpy-cross + +Then build using: + + $ make + +The default qemu-supported board is `mps2-an385`, a Cortex-M3 board. To select a +different board pass the `BOARD` argument to `make`, for example: + + $ make BOARD=sabrelite + +Running +------- + +When the firmware is run it will provide a REPL on the emulated hardware UART. +To access the REPL directly use: + + $ make repl + +This will start `qemu-system-arm` with the UART redirected to stdio. It's also +possible to redirect the UART to a pty device using: + + $ make run + +This will start the emulation and the name of the pty device will be printed to +stdout. This serial device then be accessed via a serial terminal program, +for example `mpremote`: + + $ mpremote connect /dev/pts/1 + +You can disconnect and reconnect to the serial device multiple times. Once you +are finished, stop the `make run` command by pressing Ctrl-C where that command +was started (or execute `machine.reset()` at the REPL). + +The test suite can be run against the firmware by using the UART redirection. +You can either do this automatically using the single command: + + $ make test -To build and run image with builtin testsuite: +Or manually by first starting the emulation with `make run` and then running the +tests against the serial device, for example: - make -f Makefile.test test + $ cd ../../tests + $ ./run-tests.py --target qemu-arm --device /dev/pts/1 diff --git a/ports/qemu-arm/main.c b/ports/qemu-arm/main.c index 025c1f17da04a..042106580407d 100644 --- a/ports/qemu-arm/main.c +++ b/ports/qemu-arm/main.c @@ -25,41 +25,50 @@ */ #include -#include #include "py/compile.h" #include "py/runtime.h" #include "py/stackctrl.h" #include "py/gc.h" #include "py/mperrno.h" +#include "shared/runtime/gchelper.h" +#include "shared/runtime/pyexec.h" -void do_str(const char *src, mp_parse_input_kind_t input_kind) { - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); - qstr source_name = lex->source_name; - mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true); - mp_call_function_0(module_fun); - nlr_pop(); - } else { - // uncaught exception - mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); - } -} +#define HEAP_SIZE (100 * 1024) + +static uint32_t gc_heap[HEAP_SIZE / sizeof(uint32_t)]; int main(int argc, char **argv) { mp_stack_ctrl_init(); mp_stack_set_limit(10240); - uint32_t heap[16 * 1024 / 4]; - gc_init(heap, (char *)heap + 16 * 1024); - mp_init(); - do_str("print('hello world!')", MP_PARSE_SINGLE_INPUT); - mp_deinit(); - return 0; + gc_init(gc_heap, (char *)gc_heap + HEAP_SIZE); + + for (;;) { + mp_init(); + + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } + + mp_printf(&mp_plat_print, "MPY: soft reboot\n"); + + gc_sweep_all(); + mp_deinit(); + } } void gc_collect(void) { + gc_collect_start(); + gc_helper_collect_regs_and_stack(); + gc_collect_end(); } mp_lexer_t *mp_lexer_new_from_file(qstr filename) { @@ -67,6 +76,6 @@ mp_lexer_t *mp_lexer_new_from_file(qstr filename) { } void nlr_jump_fail(void *val) { - printf("uncaught NLR\n"); + mp_printf(&mp_plat_print, "uncaught NLR\n"); exit(1); } diff --git a/ports/qemu-arm/modmachine.c b/ports/qemu-arm/modmachine.c index a897c5670e49b..75872a22c1f40 100644 --- a/ports/qemu-arm/modmachine.c +++ b/ports/qemu-arm/modmachine.c @@ -27,6 +27,22 @@ // This file is never compiled standalone, it's included directly from // extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE. +#include + static void mp_machine_idle(void) { // Do nothing. } + +#if MICROPY_PY_MACHINE_RESET + +static void mp_machine_reset(void) { + // Exit qemu (via semihosting call). + exit(0); +} + +static mp_int_t mp_machine_reset_cause(void) { + // Not implemented. + return 0; +} + +#endif diff --git a/ports/qemu-arm/mpconfigport.h b/ports/qemu-arm/mpconfigport.h index fce379e47ecf4..4059a5926d3cd 100644 --- a/ports/qemu-arm/mpconfigport.h +++ b/ports/qemu-arm/mpconfigport.h @@ -42,22 +42,19 @@ #define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) #define MICROPY_MEM_STATS (1) #define MICROPY_ENABLE_GC (1) -#define MICROPY_KBD_EXCEPTION (0) -#define MICROPY_HELPER_REPL (0) +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_WARNINGS (1) -#define MICROPY_PY_BUILTINS_INPUT (0) -#define MICROPY_PY_BUILTINS_HELP (0) #define MICROPY_PY_IO_IOBASE (0) #define MICROPY_PY_SYS_PLATFORM "qemu-arm" -#define MICROPY_PY_SYS_STDFILES (0) #define MICROPY_PY_SYS_STDIO_BUFFER (0) #define MICROPY_PY_SELECT (0) #define MICROPY_PY_TIME (0) #define MICROPY_PY_ASYNCIO (0) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/qemu-arm/modmachine.c" +#define MICROPY_PY_MACHINE_RESET (1) #define MICROPY_PY_MACHINE_PIN_BASE (1) #define MICROPY_VFS (1) @@ -78,8 +75,4 @@ typedef long mp_off_t; // We need an implementation of the log2 function which is not a macro. #define MP_NEED_LOG2 (1) -#ifdef TEST -#include "shared/upytesthelper/upytesthelper.h" -#undef MP_PLAT_PRINT_STRN -#define MP_PLAT_PRINT_STRN(str, len) upytest_output(str, len) -#endif +#define MP_STATE_PORT MP_STATE_VM diff --git a/ports/qemu-arm/mphalport.c b/ports/qemu-arm/mphalport.c new file mode 100644 index 0000000000000..dbb87b48b801a --- /dev/null +++ b/ports/qemu-arm/mphalport.c @@ -0,0 +1,66 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 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/runtime/semihosting_arm.h" +#include "uart.h" + +// UART is better behaved with redirection under qemu-system-arm, so prefer that for stdio. +#define USE_UART (1) +#define USE_SEMIHOSTING (0) + +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { + // Not implemented. + return 0; +} + +int mp_hal_stdin_rx_chr(void) { + for (;;) { + #if USE_UART + int c = uart_rx_chr(); + if (c >= 0) { + return c; + } + #endif + #if USE_SEMIHOSTING + char str[1]; + int ret = mp_semihosting_rx_chars(str, 1); + if (ret == 0) { + return str[0]; + } + #endif + } +} + +mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { + #if USE_UART + uart_tx_strn(str, len); + #endif + #if USE_SEMIHOSTING + mp_semihosting_tx_strn(str, len); + #endif + return len; +} diff --git a/ports/qemu-arm/mphalport.h b/ports/qemu-arm/mphalport.h index 8a40505ba6b3a..348b45701b89e 100644 --- a/ports/qemu-arm/mphalport.h +++ b/ports/qemu-arm/mphalport.h @@ -24,7 +24,4 @@ * THE SOFTWARE. */ -#include "uart.h" - -#define mp_hal_stdin_rx_chr() (0) -#define mp_hal_stdout_tx_strn_cooked(s, l) uart_tx_strn((s), (l)) +#include "shared/runtime/interrupt_char.h" diff --git a/ports/qemu-arm/startup.c b/ports/qemu-arm/startup.c index a1e89d111c007..118a5b8006f15 100644 --- a/ports/qemu-arm/startup.c +++ b/ports/qemu-arm/startup.c @@ -28,6 +28,7 @@ #include #include +#include "shared/runtime/semihosting_arm.h" #include "uart.h" extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss; @@ -97,6 +98,8 @@ const uint32_t isr_vector[] __attribute__((section(".isr_vector"))) = { #endif void _start(void) { + mp_semihosting_init(); + // Enable the UART uart_init(); @@ -108,21 +111,9 @@ void _start(void) { exit(0); } -__attribute__((naked)) void exit(int status) { +void exit(int status) { // Force qemu to exit using ARM Semihosting - __asm volatile ( - "mov r1, r0\n" - "cmp r1, #0\n" - "bne .notclean\n" - "ldr r1, =0x20026\n" // ADP_Stopped_ApplicationExit, a clean exit - ".notclean:\n" - "movs r0, #0x18\n" // SYS_EXIT - #if defined(__ARM_ARCH_ISA_ARM) - "svc 0x00123456\n" - #elif defined(__ARM_ARCH_ISA_THUMB) - "bkpt 0xab\n" - #endif - ); + mp_semihosting_exit(status); for (;;) { } } @@ -134,13 +125,3 @@ void __assert_func(const char *file, int line, const char *func, const char *exp exit(1); } #endif - -// The following are needed for tinytest - -#include - -int setvbuf(FILE *stream, char *buf, int mode, size_t size) { - return 0; -} - -struct _reent *_impure_ptr; diff --git a/ports/qemu-arm/tests_profile.txt b/ports/qemu-arm/tests_profile.txt deleted file mode 100644 index 101943b7c6ab0..0000000000000 --- a/ports/qemu-arm/tests_profile.txt +++ /dev/null @@ -1,16 +0,0 @@ -# Port-specific test directories. - -test_dirs.update(("inlineasm", "ports/qemu-arm")) - -# Port-specific tests exclusion list. - -exclude_tests.update( - ( - # inline asm FP tests (require Cortex-M4) - "inlineasm/asmfpaddsub.py", - "inlineasm/asmfpcmp.py", - "inlineasm/asmfpldrstr.py", - "inlineasm/asmfpmuldiv.py", - "inlineasm/asmfpsqrt.py", - ) -) diff --git a/ports/qemu-riscv/Makefile b/ports/qemu-riscv/Makefile index 6f15ce52e73c7..473aec882d640 100644 --- a/ports/qemu-riscv/Makefile +++ b/ports/qemu-riscv/Makefile @@ -81,7 +81,7 @@ SRC_COMMON_C = \ shared/runtime/sys_stdio_mphal.c \ SRC_RUN_C = \ - ports/qemu-arm/main.c \ + main.c \ SRC_TEST_C = \ test_main.c \ diff --git a/ports/qemu-arm/test_main.c b/ports/qemu-riscv/main.c similarity index 65% rename from ports/qemu-arm/test_main.c rename to ports/qemu-riscv/main.c index 96984f7cd16ed..025c1f17da04a 100644 --- a/ports/qemu-arm/test_main.c +++ b/ports/qemu-riscv/main.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2014 Ilya Dmitrichenko + * Copyright (c) 2014-2023 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 @@ -32,28 +32,34 @@ #include "py/stackctrl.h" #include "py/gc.h" #include "py/mperrno.h" -#include "shared/runtime/gchelper.h" -#include "lib/tinytest/tinytest.h" -#include "lib/tinytest/tinytest_macros.h" -#define HEAP_SIZE (100 * 1024) - -#include "genhdr/tests.h" +void do_str(const char *src, mp_parse_input_kind_t input_kind) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); + qstr source_name = lex->source_name; + mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true); + mp_call_function_0(module_fun); + nlr_pop(); + } else { + // uncaught exception + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } +} -int main() { +int main(int argc, char **argv) { mp_stack_ctrl_init(); mp_stack_set_limit(10240); - static uint32_t heap[HEAP_SIZE / sizeof(uint32_t)]; - upytest_set_heap(heap, (char *)heap + HEAP_SIZE); - int r = tinytest_main(0, NULL, groups); - printf("status: %d\n", r); - return r; + uint32_t heap[16 * 1024 / 4]; + gc_init(heap, (char *)heap + 16 * 1024); + mp_init(); + do_str("print('hello world!')", MP_PARSE_SINGLE_INPUT); + mp_deinit(); + return 0; } void gc_collect(void) { - gc_collect_start(); - gc_helper_collect_regs_and_stack(); - gc_collect_end(); } mp_lexer_t *mp_lexer_new_from_file(qstr filename) { diff --git a/tests/run-tests.py b/tests/run-tests.py index d0c93f74b9d39..60bfc2599fb47 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -676,7 +676,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): "extmod/time_time_ns.py" ) # RA fsp rtc function doesn't support nano sec info elif args.target == "qemu-arm": - skip_tests.add("misc/print_exception.py") # requires sys stdfiles + skip_tests.add("inlineasm/asmfpaddsub.py") # requires Cortex-M4 + skip_tests.add("inlineasm/asmfpcmp.py") + skip_tests.add("inlineasm/asmfpldrstr.py") + skip_tests.add("inlineasm/asmfpmuldiv.py") + skip_tests.add("inlineasm/asmfpsqrt.py") elif args.target == "qemu-riscv": skip_tests.add("misc/print_exception.py") # requires sys stdfiles elif args.target == "webassembly": @@ -1043,7 +1047,6 @@ def main(): LOCAL_TARGETS = ( "unix", - "qemu-arm", "qemu-riscv", "webassembly", ) @@ -1054,6 +1057,7 @@ def main(): "esp32", "minimal", "nrf", + "qemu-arm", "renesas-ra", "rp2", ) @@ -1141,10 +1145,6 @@ def main(): "ports/unix", ) elif args.target == "qemu-arm": - if not args.write_exp: - raise ValueError("--target=qemu-arm must be used with --write-exp") - # Generate expected output files for qemu run. - # This list should match the test_dirs tuple in tinytest-codegen.py. test_dirs += ( "float", "inlineasm", diff --git a/tools/ci.sh b/tools/ci.sh index 7353a3ecdfb7f..175d7f57622fb 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -258,10 +258,8 @@ function ci_qemu_arm_build { make ${MAKEOPTS} -C ports/qemu-arm submodules make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 make ${MAKEOPTS} -C ports/qemu-arm clean - make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test submodules - make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test test - make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test clean - make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test BOARD=sabrelite test + make ${MAKEOPTS} -C ports/qemu-arm test + make ${MAKEOPTS} -C ports/qemu-arm BOARD=sabrelite test } ######################################################################################## From c8385ef75aea929e7175173d3190dd787f37c8e4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 3 Aug 2024 16:06:52 +1000 Subject: [PATCH 0243/1300] examples/network: Support IPv4 and IPv6 in HTTP client examples. The main changes here are to pass the address family and socket type to `getaddrinfo()`, and then use the result of the address lookup when creating the socket, so it has the correct address family. This allows both IPv4 and IPv6 to work, because the socket is created with the correct AF_INETx type for the address. Also add some more comments to the examples to explain what's going on. Fixes issue #15580. Signed-off-by: Damien George --- examples/network/http_client.py | 32 ++++++++++++++---- examples/network/https_client.py | 34 ++++++++++++++++---- examples/network/https_client_nonblocking.py | 4 +-- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/examples/network/http_client.py b/examples/network/http_client.py index 661c286b70bc4..879cddcd818b0 100644 --- a/examples/network/http_client.py +++ b/examples/network/http_client.py @@ -1,16 +1,35 @@ -import socket +# Very simple HTTP client example: +# - Connects to a server. +# - Sends a HTTP request. +# - Reads the result. +# +# This example works in both MicroPython and CPython. +# +# To implement an HTTP client using less code, use mip to install the requests package: +# https://github.com/micropython/micropython-lib/tree/master/python-ecosys/requests +import socket -def main(use_stream=False): - s = socket.socket() - ai = socket.getaddrinfo("google.com", 80) +# `addr_family` selects IPv4 vs IPv6: 0 means either, or use +# socket.AF_INET or socket.AF_INET6 to select a particular one. +def main(domain, addr_family=0, use_stream=False): + # Lookup the server address, for the given family and socket type. + ai = socket.getaddrinfo(domain, 80, addr_family, socket.SOCK_STREAM) print("Address infos:", ai) - addr = ai[0][-1] + # Select the first address. + ai = ai[0] + + # Create a socket with the server's family, type and proto. + s = socket.socket(ai[0], ai[1], ai[2]) + + # Connect to the server. + addr = ai[-1] print("Connect address:", addr) s.connect(addr) + # Send request and read response. if use_stream: # MicroPython socket objects support stream (aka file) interface # directly, but the line below is needed for CPython. @@ -21,7 +40,8 @@ def main(use_stream=False): s.send(b"GET / HTTP/1.0\r\n\r\n") print(s.recv(4096)) + # Close the socket. s.close() -main() +main("google.com") diff --git a/examples/network/https_client.py b/examples/network/https_client.py index 323971c0ee24d..d20f283d74962 100644 --- a/examples/network/https_client.py +++ b/examples/network/https_client.py @@ -1,20 +1,41 @@ +# Very simple HTTPS client example: +# - Connects to a server. +# - Upgrades the connection to a TLS connection. +# - Sends a HTTP request. +# - Reads the result. +# +# This example works in both MicroPython and CPython. +# +# To implement an HTTPS client using less code, use mip to install the requests package: +# https://github.com/micropython/micropython-lib/tree/master/python-ecosys/requests + import socket import ssl -def main(use_stream=True): - s = socket.socket() - - ai = socket.getaddrinfo("google.com", 443) +# `addr_family` selects IPv4 vs IPv6: 0 means either, or use +# socket.AF_INET or socket.AF_INET6 to select a particular one. +def main(domain, addr_family=0, use_stream=True): + # Lookup the server address, for the given family and socket type. + ai = socket.getaddrinfo(domain, 443, addr_family, socket.SOCK_STREAM) print("Address infos:", ai) - addr = ai[0][-1] + # Select the first address. + ai = ai[0] + + # Create a socket with the server's family, type and proto. + s = socket.socket(ai[0], ai[1], ai[2]) + + # Connect to the server. + addr = ai[-1] print("Connect address:", addr) s.connect(addr) + # Upgrade the socket to a TLS connection. s = ssl.wrap_socket(s) print(s) + # Send request and read response. if use_stream: # Both CPython and MicroPython SSLSocket objects support read() and # write() methods. @@ -26,7 +47,8 @@ def main(use_stream=True): s.send(b"GET / HTTP/1.0\r\n\r\n") print(s.recv(4096)) + # Close the socket. s.close() -main() +main("google.com") diff --git a/examples/network/https_client_nonblocking.py b/examples/network/https_client_nonblocking.py index 6e6b7f37bc7b1..41447e81e701b 100644 --- a/examples/network/https_client_nonblocking.py +++ b/examples/network/https_client_nonblocking.py @@ -37,13 +37,13 @@ def read_nonblocking(poller, sock, n): return data -def main(url): +def main(url, addr_family=0): # Split the given URL into components. proto, _, host, path = url.split(b"/", 3) assert proto == b"https:" # Note: this getaddrinfo() call is blocking! - ai = socket.getaddrinfo(host, 443)[0] + ai = socket.getaddrinfo(host, 443, addr_family, socket.SOCK_STREAM)[0] addr = ai[-1] print("Connect address:", addr) From d75705311ad9406bbb03ae1d510693356cd4f1e3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 3 Aug 2024 16:20:25 +1000 Subject: [PATCH 0244/1300] examples/network: Use SSLContext instead of old ssl.wrap_socket. `ssl.wrap_socket()` is deprecated in CPython, so use `SSLContext` instead, so the example is a good example to copy. Signed-off-by: Damien George --- examples/network/https_client.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/network/https_client.py b/examples/network/https_client.py index d20f283d74962..60ceb83c74674 100644 --- a/examples/network/https_client.py +++ b/examples/network/https_client.py @@ -32,7 +32,10 @@ def main(domain, addr_family=0, use_stream=True): s.connect(addr) # Upgrade the socket to a TLS connection. - s = ssl.wrap_socket(s) + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + s = ctx.wrap_socket(s) print(s) # Send request and read response. From fd03a0587f7303d2b37d69c85a1e079088c3de6c Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 14 Aug 2024 11:43:22 +1000 Subject: [PATCH 0245/1300] examples/network: Support full URLs in HTTP(S) client examples. Not just the domain name. This gives better HTTP 1.0 examples if someone wants to copy them. Signed-off-by: Damien George --- examples/network/http_client.py | 15 ++++++++++----- examples/network/https_client.py | 15 ++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/examples/network/http_client.py b/examples/network/http_client.py index 879cddcd818b0..b16f87a98187a 100644 --- a/examples/network/http_client.py +++ b/examples/network/http_client.py @@ -13,9 +13,13 @@ # `addr_family` selects IPv4 vs IPv6: 0 means either, or use # socket.AF_INET or socket.AF_INET6 to select a particular one. -def main(domain, addr_family=0, use_stream=False): +def main(url, addr_family=0, use_stream=False): + # Split the given URL into components. + proto, _, host, path = url.split(b"/", 3) + assert proto == b"http:" + # Lookup the server address, for the given family and socket type. - ai = socket.getaddrinfo(domain, 80, addr_family, socket.SOCK_STREAM) + ai = socket.getaddrinfo(host, 80, addr_family, socket.SOCK_STREAM) print("Address infos:", ai) # Select the first address. @@ -30,18 +34,19 @@ def main(domain, addr_family=0, use_stream=False): s.connect(addr) # Send request and read response. + request = b"GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n" % (path, host) if use_stream: # MicroPython socket objects support stream (aka file) interface # directly, but the line below is needed for CPython. s = s.makefile("rwb", 0) - s.write(b"GET / HTTP/1.0\r\n\r\n") + s.write(request) print(s.read()) else: - s.send(b"GET / HTTP/1.0\r\n\r\n") + s.send(request) print(s.recv(4096)) # Close the socket. s.close() -main("google.com") +main(b"http://www.google.com/") diff --git a/examples/network/https_client.py b/examples/network/https_client.py index 60ceb83c74674..aaad9b657417b 100644 --- a/examples/network/https_client.py +++ b/examples/network/https_client.py @@ -15,9 +15,13 @@ # `addr_family` selects IPv4 vs IPv6: 0 means either, or use # socket.AF_INET or socket.AF_INET6 to select a particular one. -def main(domain, addr_family=0, use_stream=True): +def main(url, addr_family=0, use_stream=True): + # Split the given URL into components. + proto, _, host, path = url.split(b"/", 3) + assert proto == b"https:" + # Lookup the server address, for the given family and socket type. - ai = socket.getaddrinfo(domain, 443, addr_family, socket.SOCK_STREAM) + ai = socket.getaddrinfo(host, 443, addr_family, socket.SOCK_STREAM) print("Address infos:", ai) # Select the first address. @@ -39,19 +43,20 @@ def main(domain, addr_family=0, use_stream=True): print(s) # Send request and read response. + request = b"GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n" % (path, host) if use_stream: # Both CPython and MicroPython SSLSocket objects support read() and # write() methods. - s.write(b"GET / HTTP/1.0\r\n\r\n") + s.write(request) print(s.read(4096)) else: # MicroPython SSLSocket objects implement only stream interface, not # socket interface - s.send(b"GET / HTTP/1.0\r\n\r\n") + s.send(request) print(s.recv(4096)) # Close the socket. s.close() -main("google.com") +main(b"https://www.google.com/") From 01c046d2a803ead4d320ed533938fcc878a5959e Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 20 Feb 2024 18:52:57 +0100 Subject: [PATCH 0246/1300] rp2/machine_uart: Implement a Python UART IRQ handler. Supported trigger names: IRQ_RXIDLE, IRQ_TXIDLE, IRQ_BREAK - IRQ_RXIDLE: The handler for IRQ_RXIDLE is called reliably 31 UART bit times after the last incoming data. - IRQ_TXIDLE: This IRQ is triggered after at least >5 characters are sent at once. It is triggered when the TX FIFO falls below 4 elements. At that time, up to 5 bytes may still be in the FIFO and output shift register. - IRQ_BREAK: The IRQ triggers if a BREAK state is detected at RX. Properties & side effects: - After a BREAK, a valid character must be received before another break can be detected. - Each break puts a 0xff character into the input buffer. The irq.flags() value is cleared only with a new wanted event. Do not change the flags otherwise. Signed-off-by: robert-hh --- ports/rp2/machine_uart.c | 136 ++++++++++++++++++++++++++++++++++----- ports/rp2/main.c | 1 + ports/rp2/modmachine.h | 1 + ports/rp2/mpconfigport.h | 1 + 4 files changed, 124 insertions(+), 15 deletions(-) diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index d9e97685f1f25..689906003e7dc 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -80,6 +80,10 @@ #define UART_HWCONTROL_CTS (1) #define UART_HWCONTROL_RTS (2) +// OR-ed IRQ flags which are allowed to be used by the user +#define MP_UART_ALLOWED_FLAGS (UART_UARTMIS_RTMIS_BITS | UART_UARTMIS_TXMIS_BITS | UART_UARTMIS_BEMIS_BITS) +#define UART_FIFO_SIZE_RX (32) +#define UART_FIFO_TRIGGER_LEVEL_RX (24) static mutex_t write_mutex_0; static mutex_t write_mutex_1; @@ -111,12 +115,15 @@ typedef struct _machine_uart_obj_t { mutex_t *read_mutex; ringbuf_t write_buffer; mutex_t *write_mutex; + uint16_t mp_irq_trigger; // user IRQ trigger mask + uint16_t mp_irq_flags; // user IRQ active IRQ flags + mp_irq_obj_t *mp_irq_obj; // user IRQ object } machine_uart_obj_t; static machine_uart_obj_t machine_uart_obj[] = { {{&machine_uart_type}, uart0, 0, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, MICROPY_HW_UART0_TX, MICROPY_HW_UART0_RX, MICROPY_HW_UART0_CTS, MICROPY_HW_UART0_RTS, - 0, 0, 0, 0, {NULL, 1, 0, 0}, &read_mutex_0, {NULL, 1, 0, 0}, &write_mutex_0}, + 0, 0, 0, 0, {NULL, 1, 0, 0}, &read_mutex_0, {NULL, 1, 0, 0}, &write_mutex_0, 0, 0, NULL}, {{&machine_uart_type}, uart1, 1, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, MICROPY_HW_UART1_TX, MICROPY_HW_UART1_RX, MICROPY_HW_UART1_CTS, MICROPY_HW_UART1_RTS, 0, 0, 0, 0, {NULL, 1, 0, 0}, &read_mutex_1, {NULL, 1, 0, 0}, &write_mutex_1}, @@ -144,14 +151,15 @@ static inline void read_mutex_unlock(machine_uart_obj_t *u) { mutex_exit(u->read_mutex); } -// take all bytes from the fifo and store them in the buffer -static void uart_drain_rx_fifo(machine_uart_obj_t *self) { +// take at most max_items bytes from the fifo and store them in the buffer +static void uart_drain_rx_fifo(machine_uart_obj_t *self, uint32_t max_items) { if (read_mutex_try_lock(self)) { - while (uart_is_readable(self->uart) && ringbuf_free(&self->read_buffer) > 0) { + while (uart_is_readable(self->uart) && ringbuf_free(&self->read_buffer) > 0 && max_items > 0) { // Get a byte from uart and put into the buffer. Every entry from // the FIFO is accompanied by 4 error bits, that may be used for // error handling. uint16_t c = uart_get_hw(self->uart)->dr; + max_items -= 1; if (c & UART_UARTDR_OE_BITS) { // Overrun Error: We missed at least one byte. Not much we can do here. } @@ -187,15 +195,30 @@ static void uart_fill_tx_fifo(machine_uart_obj_t *self) { } static inline void uart_service_interrupt(machine_uart_obj_t *self) { - if (uart_get_hw(self->uart)->mis & (UART_UARTMIS_RXMIS_BITS | UART_UARTMIS_RTMIS_BITS)) { // rx interrupt? - // clear all interrupt bits but tx - uart_get_hw(self->uart)->icr = UART_UARTICR_BITS & (~UART_UARTICR_TXIC_BITS); - uart_drain_rx_fifo(self); + uint16_t mp_irq_flags = uart_get_hw(self->uart)->mis & (UART_UARTMIS_RXMIS_BITS | UART_UARTMIS_RTMIS_BITS); + if (mp_irq_flags) { // rx interrupt? + // clear all interrupt bits but tx and break + uart_get_hw(self->uart)->icr = UART_UARTICR_BITS & ~(UART_UARTICR_TXIC_BITS | UART_UARTICR_BEIC_BITS); + uart_drain_rx_fifo(self, UART_FIFO_TRIGGER_LEVEL_RX - 1); } if (uart_get_hw(self->uart)->mis & UART_UARTMIS_TXMIS_BITS) { // tx interrupt? - // clear all interrupt bits but rx - uart_get_hw(self->uart)->icr = UART_UARTICR_BITS & ~(UART_UARTICR_RXIC_BITS | UART_UARTICR_RTIC_BITS); - uart_fill_tx_fifo(self); + // clear all interrupt bits but rx and break + uart_get_hw(self->uart)->icr = UART_UARTICR_BITS & ~(UART_UARTICR_RXIC_BITS | UART_UARTICR_RTIC_BITS | UART_UARTICR_BEIC_BITS); + if (ringbuf_avail(&self->write_buffer) == 0) { + mp_irq_flags |= UART_UARTMIS_TXMIS_BITS; + } else { + uart_fill_tx_fifo(self); + } + } + if (uart_get_hw(self->uart)->mis & UART_UARTMIS_BEMIS_BITS) { // break interrupt? + // CLear the event + hw_set_bits(&uart_get_hw(self->uart)->icr, UART_UARTICR_BEIC_BITS); + mp_irq_flags |= UART_UARTMIS_BEMIS_BITS; + } + // Check the flags to see if the user handler should be called + if (self->mp_irq_trigger & mp_irq_flags) { + self->mp_irq_flags = mp_irq_flags; + mp_irq_handler(self->mp_irq_obj); } } @@ -215,14 +238,17 @@ static void uart1_irq_handler(void) { { MP_ROM_QSTR(MP_QSTR_INV_RX), MP_ROM_INT(UART_INVERT_RX) }, \ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HWCONTROL_CTS) }, \ { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HWCONTROL_RTS) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_UARTMIS_RTMIS_BITS) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_TXIDLE), MP_ROM_INT(UART_UARTMIS_TXMIS_BITS) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_BREAK), MP_ROM_INT(UART_UARTMIS_BEMIS_BITS) }, \ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, tx=%d, rx=%d, " - "txbuf=%d, rxbuf=%d, timeout=%u, timeout_char=%u, invert=%s)", + "txbuf=%d, rxbuf=%d, timeout=%u, timeout_char=%u, invert=%s, irq=%d)", self->uart_id, self->baudrate, self->bits, _parity_name[self->parity], self->stop, self->tx, self->rx, self->write_buffer.size - 1, self->read_buffer.size - 1, - self->timeout, self->timeout_char, _invert_name[self->invert]); + self->timeout, self->timeout_char, _invert_name[self->invert], self->mp_irq_trigger); } static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -441,11 +467,19 @@ static void mp_machine_uart_deinit(machine_uart_obj_t *self) { self->baudrate = 0; MP_STATE_PORT(rp2_uart_rx_buffer[self->uart_id]) = NULL; MP_STATE_PORT(rp2_uart_tx_buffer[self->uart_id]) = NULL; + MP_STATE_PORT(rp2_uart_irq_obj)[self->uart_id] = NULL; + self->mp_irq_obj = NULL; + self->mp_irq_trigger = 0; +} + +void machine_uart_deinit_all() { + mp_machine_uart_deinit((machine_uart_obj_t *)&machine_uart_obj[0]); + mp_machine_uart_deinit((machine_uart_obj_t *)&machine_uart_obj[1]); } static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) { // get all bytes from the fifo first - uart_drain_rx_fifo(self); + uart_drain_rx_fifo(self, UART_FIFO_SIZE_RX + 1); return ringbuf_avail(&self->read_buffer); } @@ -460,6 +494,77 @@ static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) { uart_set_break(self->uart, false); } +static void uart_set_irq_level(machine_uart_obj_t *self, uint16_t trigger) { + if (trigger & UART_UARTMIS_BEMIS_BITS) { + // Enable the break Interrupt + hw_set_bits(&uart_get_hw(self->uart)->imsc, UART_UARTIMSC_BEIM_BITS); + } else { + // Disable the break Interrupt + hw_clear_bits(&uart_get_hw(self->uart)->imsc, UART_UARTIMSC_BEIM_BITS); + } + if (trigger & UART_UARTMIS_RTMIS_BITS) { + // Set the RX trigger level to 3/4 FIFO_size + hw_write_masked(&uart_get_hw(self->uart)->ifls, 0b011 << UART_UARTIFLS_RXIFLSEL_LSB, + UART_UARTIFLS_RXIFLSEL_BITS); + } else { + // Set the RX trigger level to 1/8 FIFO_size + hw_write_masked(&uart_get_hw(self->uart)->ifls, 0 << UART_UARTIFLS_RXIFLSEL_LSB, + UART_UARTIFLS_RXIFLSEL_BITS); + } +} + +static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->mp_irq_trigger = new_trigger; + uart_set_irq_level(self, new_trigger); + return 0; +} + +static mp_uint_t uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (info_type == MP_IRQ_INFO_FLAGS) { + return self->mp_irq_flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return self->mp_irq_trigger; + } + return 0; +} + +static const mp_irq_methods_t uart_irq_methods = { + .trigger = uart_irq_trigger, + .info = uart_irq_info, +}; + +static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { + if (self->mp_irq_obj == NULL) { + self->mp_irq_trigger = 0; + self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self)); + MP_STATE_PORT(rp2_uart_irq_obj)[self->uart_id] = self->mp_irq_obj; + } + + if (any_args) { + // Check the handler + mp_obj_t handler = args[MP_IRQ_ARG_INIT_handler].u_obj; + if (handler != mp_const_none && !mp_obj_is_callable(handler)) { + mp_raise_ValueError(MP_ERROR_TEXT("handler must be None or callable")); + } + + // Check the trigger + mp_uint_t trigger = args[MP_IRQ_ARG_INIT_trigger].u_int; + mp_uint_t not_supported = trigger & ~MP_UART_ALLOWED_FLAGS; + if (trigger != 0 && not_supported) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("trigger 0x%04x unsupported"), not_supported); + } + + self->mp_irq_obj->handler = handler; + self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; + self->mp_irq_trigger = trigger; + uart_set_irq_level(self, trigger); + } + + return self->mp_irq_obj; +} + static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_uint_t start = mp_hal_ticks_ms(); @@ -471,7 +576,7 @@ static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t while (ringbuf_avail(&self->read_buffer) == 0) { if (uart_is_readable(self->uart)) { // Force a few incoming bytes to the buffer - uart_drain_rx_fifo(self); + uart_drain_rx_fifo(self, UART_FIFO_SIZE_RX + 1); break; } mp_uint_t elapsed = mp_hal_ticks_ms() - start; @@ -572,3 +677,4 @@ static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uint MP_REGISTER_ROOT_POINTER(void *rp2_uart_rx_buffer[2]); MP_REGISTER_ROOT_POINTER(void *rp2_uart_tx_buffer[2]); +MP_REGISTER_ROOT_POINTER(void *rp2_uart_irq_obj[2]); diff --git a/ports/rp2/main.c b/ports/rp2/main.c index fa5495bf4100d..98417e8da6b0b 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -224,6 +224,7 @@ int main(int argc, char **argv) { #endif machine_pwm_deinit_all(); machine_pin_deinit(); + machine_uart_deinit_all(); #if MICROPY_PY_THREAD mp_thread_deinit(); #endif diff --git a/ports/rp2/modmachine.h b/ports/rp2/modmachine.h index e17ad67b0301d..1ed812afff8f5 100644 --- a/ports/rp2/modmachine.h +++ b/ports/rp2/modmachine.h @@ -8,6 +8,7 @@ void machine_pin_deinit(void); void machine_i2s_init0(void); void machine_i2s_deinit_all(void); void machine_pwm_deinit_all(void); +void machine_uart_deinit_all(void); struct _machine_spi_obj_t *spi_from_mp_obj(mp_obj_t o); diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index abe25d7009e1e..a428721d763da 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -146,6 +146,7 @@ #define MICROPY_PY_MACHINE_UART (1) #define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/rp2/machine_uart.c" #define MICROPY_PY_MACHINE_UART_SENDBREAK (1) +#define MICROPY_PY_MACHINE_UART_IRQ (1) #define MICROPY_PY_MACHINE_WDT (1) #define MICROPY_PY_MACHINE_WDT_INCLUDEFILE "ports/rp2/machine_wdt.c" #define MICROPY_PY_MACHINE_FREQ_NUM_ARGS_MAX (2) From 8e1123b25bca23a3334211546d4164c9c343ec5b Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 22 Feb 2024 13:21:06 +0100 Subject: [PATCH 0247/1300] samd/machine_uart: Implement a Python UART IRQ handler. Supported for all SAMD51 devices and SAMD21 with external flash. For interrupt events, IRQ_RX and IRQ_TXIDLE are provided. IRQ_RX is called for every received byte. This may not be useful for high data rates, but can be used to build a wrapper class providing an IRQ_RXIDLE event or to signal just the first byte of a message. IRQ_TXIDLE is called only when messages are longer than 5 bytes and triggers when still 5 bytes are due to be sent. The SAMD hardware does not support implementing IRQ_RXIDLE. Signed-off-by: robert-hh --- ports/samd/machine_uart.c | 123 ++++++++++++++++++++++++++-- ports/samd/mcu/samd21/mpconfigmcu.h | 1 + ports/samd/mcu/samd51/mpconfigmcu.h | 1 + 3 files changed, 118 insertions(+), 7 deletions(-) diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c index b560e0e8e6924..aa781a17073ae 100644 --- a/ports/samd/machine_uart.c +++ b/ports/samd/machine_uart.c @@ -40,7 +40,16 @@ #define FLOW_CONTROL_RTS (1) #define FLOW_CONTROL_CTS (2) +#define MP_UART_ALLOWED_FLAGS (SERCOM_USART_INTFLAG_RXC | SERCOM_USART_INTFLAG_TXC) + +#if MICROPY_PY_MACHINE_UART_IRQ +#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(SERCOM_USART_INTFLAG_RXC) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_TXIDLE), MP_ROM_INT(SERCOM_USART_INTFLAG_TXC) }, \ + +#else #define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS +#endif typedef struct _machine_uart_obj_t { mp_obj_base_t base; @@ -67,6 +76,11 @@ typedef struct _machine_uart_obj_t { #if MICROPY_HW_UART_TXBUF ringbuf_t write_buffer; #endif + #if MICROPY_PY_MACHINE_UART_IRQ + uint16_t mp_irq_trigger; // user IRQ trigger mask + uint16_t mp_irq_flags; // user IRQ active IRQ flags + mp_irq_obj_t *mp_irq_obj; // user IRQ object + #endif } machine_uart_obj_t; static const char *_parity_name[] = {"None", "", "0", "1"}; // Is defined as 0, 2, 3 @@ -93,23 +107,41 @@ void common_uart_irq_handler(int uart_id) { // Handle IRQ if (self != NULL) { Sercom *uart = sercom_instance[self->id]; + #if MICROPY_PY_MACHINE_UART_IRQ + self->mp_irq_flags = 0; + #endif if (uart->USART.INTFLAG.bit.RXC != 0) { // Now handler the incoming data uart_drain_rx_fifo(self, uart); + #if MICROPY_PY_MACHINE_UART_IRQ + if (ringbuf_avail(&self->read_buffer) > 0) { + self->mp_irq_flags = SERCOM_USART_INTFLAG_RXC; + } + #endif } else if (uart->USART.INTFLAG.bit.DRE != 0) { #if MICROPY_HW_UART_TXBUF // handle the outgoing data if (ringbuf_avail(&self->write_buffer) > 0) { uart->USART.DATA.bit.DATA = ringbuf_get(&self->write_buffer); } else { - // Stop the interrupt if there is no more data + #if MICROPY_PY_MACHINE_UART_IRQ + // Set the TXIDLE flag + self->mp_irq_flags |= SERCOM_USART_INTFLAG_TXC; + #endif + // Stop the DRE interrupt if there is no more data uart->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; } #endif - } else { - // Disable the other interrupts, if set by error - uart->USART.INTENCLR.reg = (uint8_t) ~(SERCOM_USART_INTENCLR_DRE | SERCOM_USART_INTENCLR_RXC); } + // Disable the other interrupts, if set by error + uart->USART.INTENCLR.reg = (uint8_t) ~(SERCOM_USART_INTENCLR_DRE | SERCOM_USART_INTENCLR_RXC); + + #if MICROPY_PY_MACHINE_UART_IRQ + // Check the flags to see if the user handler should be called + if (self->mp_irq_trigger & self->mp_irq_flags) { + mp_irq_handler(self->mp_irq_obj); + } + #endif } } @@ -202,6 +234,9 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ #if MICROPY_HW_UART_RTSCTS ", rts=%q, cts=%q" #endif + #if MICROPY_PY_MACHINE_UART_IRQ + ", irq=%d" + #endif ")", self->id, self->baudrate, self->bits, _parity_name[self->parity], self->stop + 1, self->timeout, self->timeout_char, self->read_buffer.size - 1 @@ -212,6 +247,9 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ , self->rts != 0xff ? pin_find_by_id(self->rts)->name : MP_QSTR_None , self->cts != 0xff ? pin_find_by_id(self->cts)->name : MP_QSTR_None #endif + #if MICROPY_PY_MACHINE_UART_IRQ + , self->mp_irq_trigger + #endif ); } @@ -228,9 +266,7 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - #if MICROPY_HW_UART_TXBUF { MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - #endif #if MICROPY_HW_UART_RTSCTS { MP_QSTR_rts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, @@ -386,6 +422,9 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->rts = 0xff; self->cts = 0xff; #endif + #if MICROPY_PY_MACHINE_UART_IRQ + self->mp_irq_obj = NULL; + #endif self->new = true; MP_STATE_PORT(sercom_table[uart_id]) = self; @@ -445,6 +484,59 @@ static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) { mp_hal_set_pin_mux(self->tx, self->tx_pad_config.alt_fct); } +#if MICROPY_PY_MACHINE_UART_IRQ + +static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->mp_irq_trigger = new_trigger; + return 0; +} + +static mp_uint_t uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (info_type == MP_IRQ_INFO_FLAGS) { + return self->mp_irq_flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return self->mp_irq_trigger; + } + return 0; +} + +static const mp_irq_methods_t uart_irq_methods = { + .trigger = uart_irq_trigger, + .info = uart_irq_info, +}; + +static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { + if (self->mp_irq_obj == NULL) { + self->mp_irq_trigger = 0; + self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self)); + } + + if (any_args) { + // Check the handler + mp_obj_t handler = args[MP_IRQ_ARG_INIT_handler].u_obj; + if (handler != mp_const_none && !mp_obj_is_callable(handler)) { + mp_raise_ValueError(MP_ERROR_TEXT("handler must be None or callable")); + } + + // Check the trigger + mp_uint_t trigger = args[MP_IRQ_ARG_INIT_trigger].u_int; + mp_uint_t not_supported = trigger & ~MP_UART_ALLOWED_FLAGS; + if (trigger != 0 && not_supported) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("trigger 0x%04x unsupported"), not_supported); + } + + self->mp_irq_obj->handler = handler; + self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; + self->mp_irq_trigger = trigger; + } + + return self->mp_irq_obj; +} + +#endif + static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); Sercom *uart = sercom_instance[self->id]; @@ -481,9 +573,17 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ const uint8_t *src = buf_in; Sercom *uart = sercom_instance[self->id]; - #if MICROPY_HW_UART_TXBUF uint64_t t = mp_hal_ticks_ms_64() + self->timeout; + #if MICROPY_HW_UART_TXBUF + #if MICROPY_PY_MACHINE_UART_IRQ + // Prefill the FIFO to get rid of the initial IRQ_TXIDLE event + while (i < size && ringbuf_free(&(self->write_buffer)) > 0) { + ringbuf_put(&(self->write_buffer), *src++); + i++; + } + uart->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; // kick off the IRQ + #endif while (i < size) { // Wait for the first/next character to be sent. while (ringbuf_free(&(self->write_buffer)) == 0) { @@ -506,6 +606,15 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ while (i < size) { while (!(uart->USART.INTFLAG.bit.DRE)) { + if (mp_hal_ticks_ms_64() > t) { // timed out + if (i <= 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } else { + return i; + } + } + MICROPY_EVENT_POLL_HOOK } uart->USART.DATA.bit.DATA = *src++; i++; diff --git a/ports/samd/mcu/samd21/mpconfigmcu.h b/ports/samd/mcu/samd21/mpconfigmcu.h index fbb5b28e2b95a..29965f50f63a8 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.h +++ b/ports/samd/mcu/samd21/mpconfigmcu.h @@ -51,6 +51,7 @@ unsigned long trng_random_u32(int delay); #ifndef MICROPY_HW_UART_RTSCTS #define MICROPY_HW_UART_RTSCTS (SAMD21_EXTRA_FEATURES) #endif +#define MICROPY_PY_MACHINE_UART_IRQ (SAMD21_EXTRA_FEATURES) // selected extensions of the extra features set #define MICROPY_PY_OS_URANDOM (1) diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h index d567f28eb423a..9a7b8528f3573 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.h +++ b/ports/samd/mcu/samd51/mpconfigmcu.h @@ -15,6 +15,7 @@ #define MICROPY_PY_ONEWIRE (1) #define MICROPY_PY_RANDOM_SEED_INIT_FUNC (trng_random_u32()) unsigned long trng_random_u32(void); +#define MICROPY_PY_MACHINE_UART_IRQ (1) // fatfs configuration used in ffconf.h #define MICROPY_FATFS_ENABLE_LFN (1) From b7fa4e2fc87481180c7e36e80f2518856bfa0540 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 24 Sep 2023 21:42:06 +0200 Subject: [PATCH 0248/1300] mimxrt/machine_uart: Implement a Python UART IRQ handler. Supported triggers are: IRQ_RXIDLE and IRQ_TXIDLE. When IRQ_RXIDLE is set, the handler will be called 3 character times after the data in burst stopped. When IRQ_TXIDLE is set, the handler will be called immediately after the data has been sent. This commit requires a change to fsl_lpuart.c, because the existing code does not support under-run appropriately. The irq.flags() value is cleared only at an expected event. Do not change it otherwise. Signed-off-by: robert-hh --- ports/mimxrt/Makefile | 2 +- ports/mimxrt/hal/fsl_lpuart.c | 2013 +++++++++++++++++++++++++++++++++ ports/mimxrt/machine_uart.c | 83 +- ports/mimxrt/mpconfigport.h | 1 + 4 files changed, 2094 insertions(+), 5 deletions(-) create mode 100644 ports/mimxrt/hal/fsl_lpuart.c diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 75cf9caec962c..e98073d33626d 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -132,7 +132,6 @@ SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/fsl_lpi2c.c \ $(MCU_DIR)/drivers/fsl_lpspi.c \ $(MCU_DIR)/drivers/fsl_lpspi_edma.c \ - $(MCU_DIR)/drivers/fsl_lpuart.c \ $(MCU_DIR)/drivers/fsl_pit.c \ $(MCU_DIR)/drivers/fsl_pwm.c \ $(MCU_DIR)/drivers/fsl_sai.c \ @@ -191,6 +190,7 @@ SRC_C += \ eth.c \ fatfs_port.c \ flash.c \ + hal/fsl_lpuart.c \ hal/pwm_backport.c \ help.c \ led.c \ diff --git a/ports/mimxrt/hal/fsl_lpuart.c b/ports/mimxrt/hal/fsl_lpuart.c new file mode 100644 index 0000000000000..54651c3efbcd4 --- /dev/null +++ b/ports/mimxrt/hal/fsl_lpuart.c @@ -0,0 +1,2013 @@ +/* + * Copyright (c) 2015-2016, Freescale Semiconductor, Inc. + * Copyright 2016-2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_lpuart.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.lpuart" +#endif + +/* LPUART transfer state. */ +enum +{ + kLPUART_TxIdle, /*!< TX idle. */ + kLPUART_TxBusy, /*!< TX busy. */ + kLPUART_RxIdle, /*!< RX idle. */ + kLPUART_RxBusy /*!< RX busy. */ +}; + +/******************************************************************************* + * Prototypes + ******************************************************************************/ +/*! + * @brief Check whether the RX ring buffer is full. + * + * @userData handle LPUART handle pointer. + * @retval true RX ring buffer is full. + * @retval false RX ring buffer is not full. + */ +static bool LPUART_TransferIsRxRingBufferFull(LPUART_Type *base, lpuart_handle_t *handle); + +/*! + * @brief Write to TX register using non-blocking method. + * + * This function writes data to the TX register directly, upper layer must make + * sure the TX register is empty or TX FIFO has empty room before calling this function. + * + * @note This function does not check whether all the data has been sent out to bus, + * so before disable TX, check kLPUART_TransmissionCompleteFlag to ensure the TX is + * finished. + * + * @param base LPUART peripheral base address. + * @param data Start address of the data to write. + * @param length Size of the buffer to be sent. + */ +static void LPUART_WriteNonBlocking(LPUART_Type *base, const uint8_t *data, size_t length); + +/*! + * @brief Read RX register using non-blocking method. + * + * This function reads data from the TX register directly, upper layer must make + * sure the RX register is full or TX FIFO has data before calling this function. + * + * @param base LPUART peripheral base address. + * @param data Start address of the buffer to store the received data. + * @param length Size of the buffer. + */ +static void LPUART_ReadNonBlocking(LPUART_Type *base, uint8_t *data, size_t length); + +/******************************************************************************* + * Variables + ******************************************************************************/ +/* Array of LPUART peripheral base address. */ +static LPUART_Type *const s_lpuartBases[] = LPUART_BASE_PTRS; +/* Array of LPUART handle. */ +void *s_lpuartHandle[ARRAY_SIZE(s_lpuartBases)]; +/* Array of LPUART IRQ number. */ +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +static const IRQn_Type s_lpuartRxIRQ[] = LPUART_RX_IRQS; +const IRQn_Type s_lpuartTxIRQ[] = LPUART_TX_IRQS; +#else +const IRQn_Type s_lpuartIRQ[] = LPUART_RX_TX_IRQS; +#endif +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) +/* Array of LPUART clock name. */ +static const clock_ip_name_t s_lpuartClock[] = LPUART_CLOCKS; + +#if defined(LPUART_PERIPH_CLOCKS) +/* Array of LPUART functional clock name. */ +static const clock_ip_name_t s_lpuartPeriphClocks[] = LPUART_PERIPH_CLOCKS; +#endif + +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +/* LPUART ISR for transactional APIs. */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +lpuart_isr_t s_lpuartIsr = (lpuart_isr_t)DefaultISR; +#else +lpuart_isr_t s_lpuartIsr; +#endif + +/******************************************************************************* + * Code + ******************************************************************************/ +/*! + * brief Get the LPUART instance from peripheral base address. + * + * param base LPUART peripheral base address. + * return LPUART instance. + */ +uint32_t LPUART_GetInstance(LPUART_Type *base) { + uint32_t instance; + + /* Find the instance index from base address mappings. */ + for (instance = 0U; instance < ARRAY_SIZE(s_lpuartBases); instance++) { + if (s_lpuartBases[instance] == base) { + break; + } + } + + assert(instance < ARRAY_SIZE(s_lpuartBases)); + + return instance; +} + +/*! + * brief Get the length of received data in RX ring buffer. + * + * userData handle LPUART handle pointer. + * return Length of received data in RX ring buffer. + */ +size_t LPUART_TransferGetRxRingBufferLength(LPUART_Type *base, lpuart_handle_t *handle) { + assert(NULL != handle); + + size_t size; + size_t tmpRxRingBufferSize = handle->rxRingBufferSize; + uint16_t tmpRxRingBufferTail = handle->rxRingBufferTail; + uint16_t tmpRxRingBufferHead = handle->rxRingBufferHead; + + if (tmpRxRingBufferTail > tmpRxRingBufferHead) { + size = ((size_t)tmpRxRingBufferHead + tmpRxRingBufferSize - (size_t)tmpRxRingBufferTail); + } else { + size = ((size_t)tmpRxRingBufferHead - (size_t)tmpRxRingBufferTail); + } + + return size; +} + +static bool LPUART_TransferIsRxRingBufferFull(LPUART_Type *base, lpuart_handle_t *handle) { + assert(NULL != handle); + + bool full; + + if (LPUART_TransferGetRxRingBufferLength(base, handle) == (handle->rxRingBufferSize - 1U)) { + full = true; + } else { + full = false; + } + return full; +} + +static void LPUART_WriteNonBlocking(LPUART_Type *base, const uint8_t *data, size_t length) { + assert(NULL != data); + + size_t i; + + /* The Non Blocking write data API assume user have ensured there is enough space in + peripheral to write. */ + for (i = 0; i < length; i++) { + base->DATA = data[i]; + } +} + +static void LPUART_ReadNonBlocking(LPUART_Type *base, uint8_t *data, size_t length) { + assert(NULL != data); + + size_t i; + #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT + uint32_t ctrl = base->CTRL; + bool isSevenDataBits = (((ctrl & LPUART_CTRL_M7_MASK) != 0U) || + (((ctrl & LPUART_CTRL_M_MASK) == 0U) && ((ctrl & LPUART_CTRL_PE_MASK) != 0U))); + #endif + + /* The Non Blocking read data API assume user have ensured there is enough space in + peripheral to write. */ + for (i = 0; i < length; i++) { + #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT + if (isSevenDataBits) { + data[i] = (uint8_t)(base->DATA & 0x7FU); + } else { + data[i] = (uint8_t)base->DATA; + } + #else + data[i] = (uint8_t)(base->DATA); + #endif + } +} + +/*! + * brief Initializes an LPUART instance with the user configuration structure and the peripheral clock. + * + * This function configures the LPUART module with user-defined settings. Call the LPUART_GetDefaultConfig() function + * to configure the configuration structure and get the default configuration. + * The example below shows how to use this API to configure the LPUART. + * code + * lpuart_config_t lpuartConfig; + * lpuartConfig.baudRate_Bps = 115200U; + * lpuartConfig.parityMode = kLPUART_ParityDisabled; + * lpuartConfig.dataBitsCount = kLPUART_EightDataBits; + * lpuartConfig.isMsb = false; + * lpuartConfig.stopBitCount = kLPUART_OneStopBit; + * lpuartConfig.txFifoWatermark = 0; + * lpuartConfig.rxFifoWatermark = 1; + * LPUART_Init(LPUART1, &lpuartConfig, 20000000U); + * endcode + * + * param base LPUART peripheral base address. + * param config Pointer to a user-defined configuration structure. + * param srcClock_Hz LPUART clock source frequency in HZ. + * retval kStatus_LPUART_BaudrateNotSupport Baudrate is not support in current clock source. + * retval kStatus_Success LPUART initialize succeed + */ +status_t LPUART_Init(LPUART_Type *base, const lpuart_config_t *config, uint32_t srcClock_Hz) { + assert(NULL != config); + assert(0U < config->baudRate_Bps); + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + assert((uint8_t)FSL_FEATURE_LPUART_FIFO_SIZEn(base) >= config->txFifoWatermark); + assert((uint8_t)FSL_FEATURE_LPUART_FIFO_SIZEn(base) >= config->rxFifoWatermark); + #endif + + status_t status = kStatus_Success; + uint32_t temp; + uint16_t sbr, sbrTemp; + uint8_t osr, osrTemp; + uint32_t tempDiff, calculatedBaud, baudDiff; + + /* This LPUART instantiation uses a slightly different baud rate calculation + * The idea is to use the best OSR (over-sampling rate) possible + * Note, OSR is typically hard-set to 16 in other LPUART instantiations + * loop to find the best OSR value possible, one that generates minimum baudDiff + * iterate through the rest of the supported values of OSR */ + + baudDiff = config->baudRate_Bps; + osr = 0U; + sbr = 0U; + for (osrTemp = 4U; osrTemp <= 32U; osrTemp++) { + /* calculate the temporary sbr value */ + sbrTemp = (uint16_t)((srcClock_Hz * 10U / (config->baudRate_Bps * (uint32_t)osrTemp) + 5U) / 10U); + /*set sbrTemp to 1 if the sourceClockInHz can not satisfy the desired baud rate*/ + if (sbrTemp == 0U) { + sbrTemp = 1U; + } + /* Calculate the baud rate based on the temporary OSR and SBR values */ + calculatedBaud = (srcClock_Hz / ((uint32_t)osrTemp * (uint32_t)sbrTemp)); + tempDiff = calculatedBaud > config->baudRate_Bps ? (calculatedBaud - config->baudRate_Bps) : + (config->baudRate_Bps - calculatedBaud); + + if (tempDiff <= baudDiff) { + baudDiff = tempDiff; + osr = osrTemp; /* update and store the best OSR value calculated */ + sbr = sbrTemp; /* update store the best SBR value calculated */ + } + } + + /* Check to see if actual baud rate is within 3% of desired baud rate + * based on the best calculate OSR value */ + if (baudDiff > ((config->baudRate_Bps / 100U) * 3U)) { + /* Unacceptable baud rate difference of more than 3%*/ + status = kStatus_LPUART_BaudrateNotSupport; + } else { + #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + + uint32_t instance = LPUART_GetInstance(base); + + /* Enable lpuart clock */ + (void)CLOCK_EnableClock(s_lpuartClock[instance]); + #if defined(LPUART_PERIPH_CLOCKS) + (void)CLOCK_EnableClock(s_lpuartPeriphClocks[instance]); + #endif + + #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + + #if defined(FSL_FEATURE_LPUART_HAS_GLOBAL) && FSL_FEATURE_LPUART_HAS_GLOBAL + /*Reset all internal logic and registers, except the Global Register */ + LPUART_SoftwareReset(base); + #else + /* Disable LPUART TX RX before setting. */ + base->CTRL &= ~(LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK); + #endif + + temp = base->BAUD; + + /* Acceptable baud rate, check if OSR is between 4x and 7x oversampling. + * If so, then "BOTHEDGE" sampling must be turned on */ + if ((osr > 3U) && (osr < 8U)) { + temp |= LPUART_BAUD_BOTHEDGE_MASK; + } + + /* program the osr value (bit value is one less than actual value) */ + temp &= ~LPUART_BAUD_OSR_MASK; + temp |= LPUART_BAUD_OSR((uint32_t)osr - 1UL); + + /* write the sbr value to the BAUD registers */ + temp &= ~LPUART_BAUD_SBR_MASK; + base->BAUD = temp | LPUART_BAUD_SBR(sbr); + + /* Set bit count and parity mode. */ + base->BAUD &= ~LPUART_BAUD_M10_MASK; + + temp = base->CTRL & ~(LPUART_CTRL_PE_MASK | LPUART_CTRL_PT_MASK | LPUART_CTRL_M_MASK | LPUART_CTRL_ILT_MASK | + LPUART_CTRL_IDLECFG_MASK); + + temp |= (uint8_t)config->parityMode | LPUART_CTRL_IDLECFG(config->rxIdleConfig) | + LPUART_CTRL_ILT(config->rxIdleType); + + #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT + if (kLPUART_SevenDataBits == config->dataBitsCount) { + if (kLPUART_ParityDisabled != config->parityMode) { + temp &= ~LPUART_CTRL_M7_MASK; /* Seven data bits and one parity bit */ + } else { + temp |= LPUART_CTRL_M7_MASK; + } + } else + #endif + { + if (kLPUART_ParityDisabled != config->parityMode) { + temp |= LPUART_CTRL_M_MASK; /* Eight data bits and one parity bit */ + } + } + + base->CTRL = temp; + + #if defined(FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT) && FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT + /* set stop bit per char */ + temp = base->BAUD & ~LPUART_BAUD_SBNS_MASK; + base->BAUD = temp | LPUART_BAUD_SBNS((uint8_t)config->stopBitCount); + #endif + + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + /* Set tx/rx WATER watermark + Note: + Take care of the RX FIFO, RX interrupt request only assert when received bytes + equal or more than RX water mark, there is potential issue if RX water + mark larger than 1. + For example, if RX FIFO water mark is 2, upper layer needs 5 bytes and + 5 bytes are received. the last byte will be saved in FIFO but not trigger + RX interrupt because the water mark is 2. + */ + base->WATER = (((uint32_t)(config->rxFifoWatermark) << 16U) | config->txFifoWatermark); + + /* Enable tx/rx FIFO */ + base->FIFO |= (LPUART_FIFO_TXFE_MASK | LPUART_FIFO_RXFE_MASK); + + /* Flush FIFO */ + base->FIFO |= (LPUART_FIFO_TXFLUSH_MASK | LPUART_FIFO_RXFLUSH_MASK); + #endif + + /* Clear all status flags */ + temp = (LPUART_STAT_RXEDGIF_MASK | LPUART_STAT_IDLE_MASK | LPUART_STAT_OR_MASK | LPUART_STAT_NF_MASK | + LPUART_STAT_FE_MASK | LPUART_STAT_PF_MASK); + + #if defined(FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT) && FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT + temp |= LPUART_STAT_LBKDIF_MASK; + #endif + + #if defined(FSL_FEATURE_LPUART_HAS_ADDRESS_MATCHING) && FSL_FEATURE_LPUART_HAS_ADDRESS_MATCHING + temp |= (LPUART_STAT_MA1F_MASK | LPUART_STAT_MA2F_MASK); + #endif + + #if defined(FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT) && FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT + /* Set the CTS configuration/TX CTS source. */ + base->MODIR |= LPUART_MODIR_TXCTSC(config->txCtsConfig) | LPUART_MODIR_TXCTSSRC(config->txCtsSource); + if (true == config->enableRxRTS) { + /* Enable the receiver RTS(request-to-send) function. */ + base->MODIR |= LPUART_MODIR_RXRTSE_MASK; + } + if (true == config->enableTxCTS) { + /* Enable the CTS(clear-to-send) function. */ + base->MODIR |= LPUART_MODIR_TXCTSE_MASK; + } + #endif + + /* Set data bits order. */ + if (true == config->isMsb) { + temp |= LPUART_STAT_MSBF_MASK; + } else { + temp &= ~LPUART_STAT_MSBF_MASK; + } + + base->STAT |= temp; + + /* Enable TX/RX base on configure structure. */ + temp = base->CTRL; + if (true == config->enableTx) { + temp |= LPUART_CTRL_TE_MASK; + } + + if (true == config->enableRx) { + temp |= LPUART_CTRL_RE_MASK; + } + + base->CTRL = temp; + } + + return status; +} +/*! + * brief Deinitializes a LPUART instance. + * + * This function waits for transmit to complete, disables TX and RX, and disables the LPUART clock. + * + * param base LPUART peripheral base address. + */ +void LPUART_Deinit(LPUART_Type *base) { + uint32_t temp; + + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + /* Wait tx FIFO send out*/ + while (0U != ((base->WATER & LPUART_WATER_TXCOUNT_MASK) >> LPUART_WATER_TXWATER_SHIFT)) { + } + #endif + /* Wait last char shift out */ + while (0U == (base->STAT & LPUART_STAT_TC_MASK)) { + } + + /* Clear all status flags */ + temp = (LPUART_STAT_RXEDGIF_MASK | LPUART_STAT_IDLE_MASK | LPUART_STAT_OR_MASK | LPUART_STAT_NF_MASK | + LPUART_STAT_FE_MASK | LPUART_STAT_PF_MASK); + + #if defined(FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT) && FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT + temp |= LPUART_STAT_LBKDIF_MASK; + #endif + + #if defined(FSL_FEATURE_LPUART_HAS_ADDRESS_MATCHING) && FSL_FEATURE_LPUART_HAS_ADDRESS_MATCHING + temp |= (LPUART_STAT_MA1F_MASK | LPUART_STAT_MA2F_MASK); + #endif + + base->STAT |= temp; + + /* Disable the module. */ + base->CTRL = 0U; + + #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + uint32_t instance = LPUART_GetInstance(base); + + /* Disable lpuart clock */ + (void)CLOCK_DisableClock(s_lpuartClock[instance]); + + #if defined(LPUART_PERIPH_CLOCKS) + (void)CLOCK_DisableClock(s_lpuartPeriphClocks[instance]); + #endif + + #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ +} + +/*! + * brief Gets the default configuration structure. + * + * This function initializes the LPUART configuration structure to a default value. The default + * values are: + * lpuartConfig->baudRate_Bps = 115200U; + * lpuartConfig->parityMode = kLPUART_ParityDisabled; + * lpuartConfig->dataBitsCount = kLPUART_EightDataBits; + * lpuartConfig->isMsb = false; + * lpuartConfig->stopBitCount = kLPUART_OneStopBit; + * lpuartConfig->txFifoWatermark = 0; + * lpuartConfig->rxFifoWatermark = 1; + * lpuartConfig->rxIdleType = kLPUART_IdleTypeStartBit; + * lpuartConfig->rxIdleConfig = kLPUART_IdleCharacter1; + * lpuartConfig->enableTx = false; + * lpuartConfig->enableRx = false; + * + * param config Pointer to a configuration structure. + */ +void LPUART_GetDefaultConfig(lpuart_config_t *config) { + assert(NULL != config); + + /* Initializes the configure structure to zero. */ + (void)memset(config, 0, sizeof(*config)); + + config->baudRate_Bps = 115200U; + config->parityMode = kLPUART_ParityDisabled; + config->dataBitsCount = kLPUART_EightDataBits; + config->isMsb = false; + #if defined(FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT) && FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT + config->stopBitCount = kLPUART_OneStopBit; + #endif + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + config->txFifoWatermark = 0U; + config->rxFifoWatermark = 0U; + #endif + #if defined(FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT) && FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT + config->enableRxRTS = false; + config->enableTxCTS = false; + config->txCtsConfig = kLPUART_CtsSampleAtStart; + config->txCtsSource = kLPUART_CtsSourcePin; + #endif + config->rxIdleType = kLPUART_IdleTypeStartBit; + config->rxIdleConfig = kLPUART_IdleCharacter1; + config->enableTx = false; + config->enableRx = false; +} + +/*! + * brief Sets the LPUART instance baudrate. + * + * This function configures the LPUART module baudrate. This function is used to update + * the LPUART module baudrate after the LPUART module is initialized by the LPUART_Init. + * code + * LPUART_SetBaudRate(LPUART1, 115200U, 20000000U); + * endcode + * + * param base LPUART peripheral base address. + * param baudRate_Bps LPUART baudrate to be set. + * param srcClock_Hz LPUART clock source frequency in HZ. + * retval kStatus_LPUART_BaudrateNotSupport Baudrate is not supported in the current clock source. + * retval kStatus_Success Set baudrate succeeded. + */ +status_t LPUART_SetBaudRate(LPUART_Type *base, uint32_t baudRate_Bps, uint32_t srcClock_Hz) { + assert(0U < baudRate_Bps); + + status_t status = kStatus_Success; + uint32_t temp, oldCtrl; + uint16_t sbr, sbrTemp; + uint8_t osr, osrTemp; + uint32_t tempDiff, calculatedBaud, baudDiff; + + /* This LPUART instantiation uses a slightly different baud rate calculation + * The idea is to use the best OSR (over-sampling rate) possible + * Note, OSR is typically hard-set to 16 in other LPUART instantiations + * loop to find the best OSR value possible, one that generates minimum baudDiff + * iterate through the rest of the supported values of OSR */ + + baudDiff = baudRate_Bps; + osr = 0U; + sbr = 0U; + for (osrTemp = 4U; osrTemp <= 32U; osrTemp++) { + /* calculate the temporary sbr value */ + sbrTemp = (uint16_t)((srcClock_Hz * 10U / (baudRate_Bps * (uint32_t)osrTemp) + 5U) / 10U); + /*set sbrTemp to 1 if the sourceClockInHz can not satisfy the desired baud rate*/ + if (sbrTemp == 0U) { + sbrTemp = 1U; + } + /* Calculate the baud rate based on the temporary OSR and SBR values */ + calculatedBaud = srcClock_Hz / ((uint32_t)osrTemp * (uint32_t)sbrTemp); + + tempDiff = calculatedBaud > baudRate_Bps ? (calculatedBaud - baudRate_Bps) : (baudRate_Bps - calculatedBaud); + + if (tempDiff <= baudDiff) { + baudDiff = tempDiff; + osr = osrTemp; /* update and store the best OSR value calculated */ + sbr = sbrTemp; /* update store the best SBR value calculated */ + } + } + + /* Check to see if actual baud rate is within 3% of desired baud rate + * based on the best calculate OSR value */ + if (baudDiff < (uint32_t)((baudRate_Bps / 100U) * 3U)) { + /* Store CTRL before disable Tx and Rx */ + oldCtrl = base->CTRL; + + /* Disable LPUART TX RX before setting. */ + base->CTRL &= ~(LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK); + + temp = base->BAUD; + + /* Acceptable baud rate, check if OSR is between 4x and 7x oversampling. + * If so, then "BOTHEDGE" sampling must be turned on */ + if ((osr > 3U) && (osr < 8U)) { + temp |= LPUART_BAUD_BOTHEDGE_MASK; + } + + /* program the osr value (bit value is one less than actual value) */ + temp &= ~LPUART_BAUD_OSR_MASK; + temp |= LPUART_BAUD_OSR((uint32_t)osr - 1UL); + + /* write the sbr value to the BAUD registers */ + temp &= ~LPUART_BAUD_SBR_MASK; + base->BAUD = temp | LPUART_BAUD_SBR(sbr); + + /* Restore CTRL. */ + base->CTRL = oldCtrl; + } else { + /* Unacceptable baud rate difference of more than 3%*/ + status = kStatus_LPUART_BaudrateNotSupport; + } + + return status; +} + +/*! + * brief Enable 9-bit data mode for LPUART. + * + * This function set the 9-bit mode for LPUART module. The 9th bit is not used for parity thus can be modified by user. + * + * param base LPUART peripheral base address. + * param enable true to enable, false to disable. + */ +void LPUART_Enable9bitMode(LPUART_Type *base, bool enable) { + assert(base != NULL); + + uint32_t temp = 0U; + + if (enable) { + /* Set LPUART_CTRL_M for 9-bit mode, clear LPUART_CTRL_PE to disable parity. */ + temp = base->CTRL & ~((uint32_t)LPUART_CTRL_PE_MASK | (uint32_t)LPUART_CTRL_M_MASK); + temp |= (uint32_t)LPUART_CTRL_M_MASK; + base->CTRL = temp; + } else { + /* Clear LPUART_CTRL_M. */ + base->CTRL &= ~(uint32_t)LPUART_CTRL_M_MASK; + } + #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT + /* Clear LPUART_CTRL_M7 to disable 7-bit mode. */ + base->CTRL &= ~(uint32_t)LPUART_CTRL_M7_MASK; + #endif + #if defined(FSL_FEATURE_LPUART_HAS_10BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_10BIT_DATA_SUPPORT + /* Clear LPUART_BAUD_M10 to disable 10-bit mode. */ + base->BAUD &= ~(uint32_t)LPUART_BAUD_M10_MASK; + #endif +} + +/*! + * brief Transmit an address frame in 9-bit data mode. + * + * param base LPUART peripheral base address. + * param address LPUART slave address. + */ +void LPUART_SendAddress(LPUART_Type *base, uint8_t address) { + assert(base != NULL); + + uint32_t temp = base->DATA & 0xFFFFFC00UL; + temp |= ((uint32_t)address | (1UL << LPUART_DATA_R8T8_SHIFT)); + base->DATA = temp; +} + +/*! + * brief Enables LPUART interrupts according to a provided mask. + * + * This function enables the LPUART interrupts according to a provided mask. The mask + * is a logical OR of enumeration members. See the ref _lpuart_interrupt_enable. + * This examples shows how to enable TX empty interrupt and RX full interrupt: + * code + * LPUART_EnableInterrupts(LPUART1,kLPUART_TxDataRegEmptyInterruptEnable | kLPUART_RxDataRegFullInterruptEnable); + * endcode + * + * param base LPUART peripheral base address. + * param mask The interrupts to enable. Logical OR of ref _uart_interrupt_enable. + */ +void LPUART_EnableInterrupts(LPUART_Type *base, uint32_t mask) { + /* Only consider the real interrupt enable bits. */ + mask &= (uint32_t)kLPUART_AllInterruptEnable; + + /* Check int enable bits in base->BAUD */ + uint32_t tempReg = base->BAUD; + #if defined(FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT) && FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT + tempReg |= ((mask << 8U) & LPUART_BAUD_LBKDIE_MASK); + /* Clear bit 7 from mask */ + mask &= ~(uint32_t)kLPUART_LinBreakInterruptEnable; + #endif + tempReg |= ((mask << 8U) & LPUART_BAUD_RXEDGIE_MASK); + /* Clear bit 6 from mask */ + mask &= ~(uint32_t)kLPUART_RxActiveEdgeInterruptEnable; + base->BAUD = tempReg; + + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + /* Check int enable bits in base->FIFO */ + base->FIFO = (base->FIFO & ~(LPUART_FIFO_TXOF_MASK | LPUART_FIFO_RXUF_MASK)) | + (mask & (LPUART_FIFO_TXOFE_MASK | LPUART_FIFO_RXUFE_MASK)); + /* Clear bit 9 and bit 8 from mask */ + mask &= ~((uint32_t)kLPUART_TxFifoOverflowInterruptEnable | (uint32_t)kLPUART_RxFifoUnderflowInterruptEnable); + #endif + + /* Check int enable bits in base->CTRL */ + base->CTRL |= mask; +} + +/*! + * brief Disables LPUART interrupts according to a provided mask. + * + * This function disables the LPUART interrupts according to a provided mask. The mask + * is a logical OR of enumeration members. See ref _lpuart_interrupt_enable. + * This example shows how to disable the TX empty interrupt and RX full interrupt: + * code + * LPUART_DisableInterrupts(LPUART1,kLPUART_TxDataRegEmptyInterruptEnable | kLPUART_RxDataRegFullInterruptEnable); + * endcode + * + * param base LPUART peripheral base address. + * param mask The interrupts to disable. Logical OR of ref _lpuart_interrupt_enable. + */ +void LPUART_DisableInterrupts(LPUART_Type *base, uint32_t mask) { + /* Only consider the real interrupt enable bits. */ + mask &= (uint32_t)kLPUART_AllInterruptEnable; + /* Check int enable bits in base->BAUD */ + uint32_t tempReg = base->BAUD; + #if defined(FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT) && FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT + tempReg &= ~((mask << 8U) & LPUART_BAUD_LBKDIE_MASK); + /* Clear bit 7 from mask */ + mask &= ~(uint32_t)kLPUART_LinBreakInterruptEnable; + #endif + tempReg &= ~((mask << 8U) & LPUART_BAUD_RXEDGIE_MASK); + /* Clear bit 6 from mask */ + mask &= ~(uint32_t)kLPUART_RxActiveEdgeInterruptEnable; + base->BAUD = tempReg; + + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + /* Check int enable bits in base->FIFO */ + base->FIFO = (base->FIFO & ~(LPUART_FIFO_TXOF_MASK | LPUART_FIFO_RXUF_MASK)) & + ~(mask & (LPUART_FIFO_TXOFE_MASK | LPUART_FIFO_RXUFE_MASK)); + /* Clear bit 9 and bit 8 from mask */ + mask &= ~((uint32_t)kLPUART_TxFifoOverflowInterruptEnable | (uint32_t)kLPUART_RxFifoUnderflowInterruptEnable); + #endif + + /* Check int enable bits in base->CTRL */ + base->CTRL &= ~mask; +} + +/*! + * brief Gets enabled LPUART interrupts. + * + * This function gets the enabled LPUART interrupts. The enabled interrupts are returned + * as the logical OR value of the enumerators ref _lpuart_interrupt_enable. To check + * a specific interrupt enable status, compare the return value with enumerators + * in ref _lpuart_interrupt_enable. + * For example, to check whether the TX empty interrupt is enabled: + * code + * uint32_t enabledInterrupts = LPUART_GetEnabledInterrupts(LPUART1); + * + * if (kLPUART_TxDataRegEmptyInterruptEnable & enabledInterrupts) + * { + * ... + * } + * endcode + * + * param base LPUART peripheral base address. + * return LPUART interrupt flags which are logical OR of the enumerators in ref _lpuart_interrupt_enable. + */ +uint32_t LPUART_GetEnabledInterrupts(LPUART_Type *base) { + /* Check int enable bits in base->CTRL */ + uint32_t temp = (uint32_t)(base->CTRL & (uint32_t)kLPUART_AllInterruptEnable); + + /* Check int enable bits in base->BAUD */ + temp = (temp & ~(uint32_t)kLPUART_RxActiveEdgeInterruptEnable) | ((base->BAUD & LPUART_BAUD_RXEDGIE_MASK) >> 8U); + #if defined(FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT) && FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT + temp = (temp & ~(uint32_t)kLPUART_LinBreakInterruptEnable) | ((base->BAUD & LPUART_BAUD_LBKDIE_MASK) >> 8U); + #endif + + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + /* Check int enable bits in base->FIFO */ + temp = + (temp & ~((uint32_t)kLPUART_TxFifoOverflowInterruptEnable | (uint32_t)kLPUART_RxFifoUnderflowInterruptEnable)) | + (base->FIFO & (LPUART_FIFO_TXOFE_MASK | LPUART_FIFO_RXUFE_MASK)); + #endif + + return temp; +} + +/*! + * brief Gets LPUART status flags. + * + * This function gets all LPUART status flags. The flags are returned as the logical + * OR value of the enumerators ref _lpuart_flags. To check for a specific status, + * compare the return value with enumerators in the ref _lpuart_flags. + * For example, to check whether the TX is empty: + * code + * if (kLPUART_TxDataRegEmptyFlag & LPUART_GetStatusFlags(LPUART1)) + * { + * ... + * } + * endcode + * + * param base LPUART peripheral base address. + * return LPUART status flags which are ORed by the enumerators in the _lpuart_flags. + */ +uint32_t LPUART_GetStatusFlags(LPUART_Type *base) { + uint32_t temp; + temp = base->STAT; + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + temp |= (base->FIFO & + (LPUART_FIFO_TXEMPT_MASK | LPUART_FIFO_RXEMPT_MASK | LPUART_FIFO_TXOF_MASK | LPUART_FIFO_RXUF_MASK)) >> + 16U; + #endif + /* Only keeps the status bits */ + temp &= (uint32_t)kLPUART_AllFlags; + return temp; +} + +/*! + * brief Clears status flags with a provided mask. + * + * This function clears LPUART status flags with a provided mask. Automatically cleared flags + * can't be cleared by this function. + * Flags that can only cleared or set by hardware are: + * kLPUART_TxDataRegEmptyFlag, kLPUART_TransmissionCompleteFlag, kLPUART_RxDataRegFullFlag, + * kLPUART_RxActiveFlag, kLPUART_NoiseErrorInRxDataRegFlag, kLPUART_ParityErrorInRxDataRegFlag, + * kLPUART_TxFifoEmptyFlag,kLPUART_RxFifoEmptyFlag + * Note: This API should be called when the Tx/Rx is idle, otherwise it takes no effects. + * + * param base LPUART peripheral base address. + * param mask the status flags to be cleared. The user can use the enumerators in the + * _lpuart_status_flag_t to do the OR operation and get the mask. + * return 0 succeed, others failed. + * retval kStatus_LPUART_FlagCannotClearManually The flag can't be cleared by this function but + * it is cleared automatically by hardware. + * retval kStatus_Success Status in the mask are cleared. + */ +status_t LPUART_ClearStatusFlags(LPUART_Type *base, uint32_t mask) { + uint32_t temp; + status_t status; + + /* Only deal with the clearable flags */ + mask &= (uint32_t)kLPUART_AllClearFlags; + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + /* Status bits in FIFO register */ + if ((mask & ((uint32_t)kLPUART_TxFifoOverflowFlag | (uint32_t)kLPUART_RxFifoUnderflowFlag)) != 0U) { + /* Get the FIFO register value and mask the rx/tx FIFO flush bits and the status bits that can be W1C in case + they are written 1 accidentally. */ + temp = (uint32_t)base->FIFO; + temp &= (uint32_t)( + ~(LPUART_FIFO_TXFLUSH_MASK | LPUART_FIFO_RXFLUSH_MASK | LPUART_FIFO_TXOF_MASK | LPUART_FIFO_RXUF_MASK)); + temp |= (mask << 16U) & (LPUART_FIFO_TXOF_MASK | LPUART_FIFO_RXUF_MASK); + base->FIFO = temp; + } + #endif + /* Status bits in STAT register */ + /* First get the STAT register value and mask all the bits that not represent status, then OR with the status bit + * that is to be W1C */ + temp = (base->STAT & 0x3E000000UL) | mask; + base->STAT = temp; + /* If some flags still pending. */ + if (0U != (mask & LPUART_GetStatusFlags(base))) { + status = kStatus_LPUART_FlagCannotClearManually; + } else { + status = kStatus_Success; + } + + return status; +} + +/*! + * brief Writes to the transmitter register using a blocking method. + * + * This function polls the transmitter register, first waits for the register to be empty or TX FIFO to have room, + * and writes data to the transmitter buffer, then waits for the data to be sent out to bus. + * + * param base LPUART peripheral base address. + * param data Start address of the data to write. + * param length Size of the data to write. + * retval kStatus_LPUART_Timeout Transmission timed out and was aborted. + * retval kStatus_Success Successfully wrote all data. + */ +status_t LPUART_WriteBlocking(LPUART_Type *base, const uint8_t *data, size_t length) { + assert(NULL != data); + + const uint8_t *dataAddress = data; + size_t transferSize = length; + + #if UART_RETRY_TIMES + uint32_t waitTimes; + #endif + + while (0U != transferSize) { + #if UART_RETRY_TIMES + waitTimes = UART_RETRY_TIMES; + while ((0U == (base->STAT & LPUART_STAT_TDRE_MASK)) && (0U != --waitTimes)) + #else + while (0U == (base->STAT & LPUART_STAT_TDRE_MASK)) + #endif + { + } + #if UART_RETRY_TIMES + if (0U == waitTimes) { + return kStatus_LPUART_Timeout; + } + #endif + base->DATA = *(dataAddress); + dataAddress++; + transferSize--; + } + /* Ensure all the data in the transmit buffer are sent out to bus. */ + #if UART_RETRY_TIMES + waitTimes = UART_RETRY_TIMES; + while ((0U == (base->STAT & LPUART_STAT_TC_MASK)) && (0U != --waitTimes)) + #else + while (0U == (base->STAT & LPUART_STAT_TC_MASK)) + #endif + { + } + #if UART_RETRY_TIMES + if (0U == waitTimes) { + return kStatus_LPUART_Timeout; + } + #endif + return kStatus_Success; +} + +/*! + * brief Reads the receiver data register using a blocking method. + * + * This function polls the receiver register, waits for the receiver register full or receiver FIFO + * has data, and reads data from the TX register. + * + * param base LPUART peripheral base address. + * param data Start address of the buffer to store the received data. + * param length Size of the buffer. + * retval kStatus_LPUART_RxHardwareOverrun Receiver overrun happened while receiving data. + * retval kStatus_LPUART_NoiseError Noise error happened while receiving data. + * retval kStatus_LPUART_FramingError Framing error happened while receiving data. + * retval kStatus_LPUART_ParityError Parity error happened while receiving data. + * retval kStatus_LPUART_Timeout Transmission timed out and was aborted. + * retval kStatus_Success Successfully received all data. + */ +status_t LPUART_ReadBlocking(LPUART_Type *base, uint8_t *data, size_t length) { + assert(NULL != data); + + status_t status = kStatus_Success; + uint32_t statusFlag; + uint8_t *dataAddress = data; + + #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT + uint32_t ctrl = base->CTRL; + bool isSevenDataBits = (((ctrl & LPUART_CTRL_M7_MASK) != 0U) || + (((ctrl & LPUART_CTRL_M_MASK) == 0U) && ((ctrl & LPUART_CTRL_PE_MASK) != 0U))); + #endif + + #if UART_RETRY_TIMES + uint32_t waitTimes; + #endif + + while (0U != (length--)) { + #if UART_RETRY_TIMES + waitTimes = UART_RETRY_TIMES; + #endif + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + while (0U == ((base->WATER & LPUART_WATER_RXCOUNT_MASK) >> LPUART_WATER_RXCOUNT_SHIFT)) + #else + while (0U == (base->STAT & LPUART_STAT_RDRF_MASK)) + #endif + { + #if UART_RETRY_TIMES + if (0U == --waitTimes) { + status = kStatus_LPUART_Timeout; + break; + } + #endif + statusFlag = LPUART_GetStatusFlags(base); + + if (0U != (statusFlag & (uint32_t)kLPUART_RxOverrunFlag)) { + status = ((kStatus_Success == LPUART_ClearStatusFlags(base, (uint32_t)kLPUART_RxOverrunFlag)) ? + (kStatus_LPUART_RxHardwareOverrun) : + (kStatus_LPUART_FlagCannotClearManually)); + /* Other error flags(FE, NF, and PF) are prevented from setting once OR is set, no need to check other + * error flags*/ + break; + } + + if (0U != (statusFlag & (uint32_t)kLPUART_ParityErrorFlag)) { + status = ((kStatus_Success == LPUART_ClearStatusFlags(base, (uint32_t)kLPUART_ParityErrorFlag)) ? + (kStatus_LPUART_ParityError) : + (kStatus_LPUART_FlagCannotClearManually)); + } + + if (0U != (statusFlag & (uint32_t)kLPUART_FramingErrorFlag)) { + status = ((kStatus_Success == LPUART_ClearStatusFlags(base, (uint32_t)kLPUART_FramingErrorFlag)) ? + (kStatus_LPUART_FramingError) : + (kStatus_LPUART_FlagCannotClearManually)); + } + + if (0U != (statusFlag & (uint32_t)kLPUART_NoiseErrorFlag)) { + status = ((kStatus_Success == LPUART_ClearStatusFlags(base, (uint32_t)kLPUART_NoiseErrorFlag)) ? + (kStatus_LPUART_NoiseError) : + (kStatus_LPUART_FlagCannotClearManually)); + } + if (kStatus_Success != status) { + break; + } + } + + if (kStatus_Success == status) { + #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT + if (isSevenDataBits) { + *(dataAddress) = (uint8_t)(base->DATA & 0x7FU); + dataAddress++; + } else { + *(dataAddress) = (uint8_t)base->DATA; + dataAddress++; + } + #else + *(dataAddress) = (uint8_t)base->DATA; + dataAddress++; + #endif + } else { + break; + } + } + + return status; +} + +/*! + * brief Initializes the LPUART handle. + * + * This function initializes the LPUART handle, which can be used for other LPUART + * transactional APIs. Usually, for a specified LPUART instance, + * call this API once to get the initialized handle. + * + * The LPUART driver supports the "background" receiving, which means that user can set up + * an RX ring buffer optionally. Data received is stored into the ring buffer even when the + * user doesn't call the LPUART_TransferReceiveNonBlocking() API. If there is already data received + * in the ring buffer, the user can get the received data from the ring buffer directly. + * The ring buffer is disabled if passing NULL as p ringBuffer. + * + * param base LPUART peripheral base address. + * param handle LPUART handle pointer. + * param callback Callback function. + * param userData User data. + */ +void LPUART_TransferCreateHandle(LPUART_Type *base, + lpuart_handle_t *handle, + lpuart_transfer_callback_t callback, + void *userData) { + assert(NULL != handle); + + uint32_t instance; + + #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT + uint32_t ctrl = base->CTRL; + bool isSevenDataBits = (((ctrl & LPUART_CTRL_M7_MASK) != 0U) || + (((ctrl & LPUART_CTRL_M_MASK) == 0U) && ((ctrl & LPUART_CTRL_PE_MASK) != 0U))); + #endif + + /* Zero the handle. */ + (void)memset(handle, 0, sizeof(lpuart_handle_t)); + + /* Set the TX/RX state. */ + handle->rxState = (uint8_t)kLPUART_RxIdle; + handle->txState = (uint8_t)kLPUART_TxIdle; + + /* Set the callback and user data. */ + handle->callback = callback; + handle->userData = userData; + + #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT + /* Initial seven data bits flag */ + handle->isSevenDataBits = isSevenDataBits; + #endif + + /* Get instance from peripheral base address. */ + instance = LPUART_GetInstance(base); + + /* Save the handle in global variables to support the double weak mechanism. */ + s_lpuartHandle[instance] = handle; + + s_lpuartIsr = LPUART_TransferHandleIRQ; + +/* Enable interrupt in NVIC. */ + #if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ + (void)EnableIRQ(s_lpuartRxIRQ[instance]); + (void)EnableIRQ(s_lpuartTxIRQ[instance]); + #else + (void)EnableIRQ(s_lpuartIRQ[instance]); + #endif +} + +/*! + * brief Sets up the RX ring buffer. + * + * This function sets up the RX ring buffer to a specific UART handle. + * + * When the RX ring buffer is used, data received is stored into the ring buffer even when + * the user doesn't call the UART_TransferReceiveNonBlocking() API. If there is already data received + * in the ring buffer, the user can get the received data from the ring buffer directly. + * + * note When using RX ring buffer, one byte is reserved for internal use. In other + * words, if p ringBufferSize is 32, then only 31 bytes are used for saving data. + * + * param base LPUART peripheral base address. + * param handle LPUART handle pointer. + * param ringBuffer Start address of ring buffer for background receiving. Pass NULL to disable the ring buffer. + * param ringBufferSize size of the ring buffer. + */ +void LPUART_TransferStartRingBuffer(LPUART_Type *base, + lpuart_handle_t *handle, + uint8_t *ringBuffer, + size_t ringBufferSize) { + assert(NULL != handle); + assert(NULL != ringBuffer); + + /* Setup the ring buffer address */ + handle->rxRingBuffer = ringBuffer; + handle->rxRingBufferSize = ringBufferSize; + handle->rxRingBufferHead = 0U; + handle->rxRingBufferTail = 0U; + + /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. */ + uint32_t irqMask = DisableGlobalIRQ(); + /* Enable the interrupt to accept the data when user need the ring buffer. */ + base->CTRL |= (uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ORIE_MASK); + EnableGlobalIRQ(irqMask); +} + +/*! + * brief Aborts the background transfer and uninstalls the ring buffer. + * + * This function aborts the background transfer and uninstalls the ring buffer. + * + * param base LPUART peripheral base address. + * param handle LPUART handle pointer. + */ +void LPUART_TransferStopRingBuffer(LPUART_Type *base, lpuart_handle_t *handle) { + assert(NULL != handle); + + if (handle->rxState == (uint8_t)kLPUART_RxIdle) { + /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. + */ + uint32_t irqMask = DisableGlobalIRQ(); + base->CTRL &= ~(uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ORIE_MASK); + EnableGlobalIRQ(irqMask); + } + + handle->rxRingBuffer = NULL; + handle->rxRingBufferSize = 0U; + handle->rxRingBufferHead = 0U; + handle->rxRingBufferTail = 0U; +} + +/*! + * brief Transmits a buffer of data using the interrupt method. + * + * This function send data using an interrupt method. This is a non-blocking function, which + * returns directly without waiting for all data written to the transmitter register. When + * all data is written to the TX register in the ISR, the LPUART driver calls the callback + * function and passes the ref kStatus_LPUART_TxIdle as status parameter. + * + * note The kStatus_LPUART_TxIdle is passed to the upper layer when all data are written + * to the TX register. However, there is no check to ensure that all the data sent out. Before disabling the TX, + * check the kLPUART_TransmissionCompleteFlag to ensure that the transmit is finished. + * + * param base LPUART peripheral base address. + * param handle LPUART handle pointer. + * param xfer LPUART transfer structure, see #lpuart_transfer_t. + * retval kStatus_Success Successfully start the data transmission. + * retval kStatus_LPUART_TxBusy Previous transmission still not finished, data not all written to the TX register. + * retval kStatus_InvalidArgument Invalid argument. + */ +status_t LPUART_TransferSendNonBlocking(LPUART_Type *base, lpuart_handle_t *handle, lpuart_transfer_t *xfer) { + assert(NULL != handle); + assert(NULL != xfer); + assert(NULL != xfer->txData); + assert(0U != xfer->dataSize); + + status_t status; + + /* Return error if current TX busy. */ + if ((uint8_t)kLPUART_TxBusy == handle->txState) { + status = kStatus_LPUART_TxBusy; + } else { + handle->txData = xfer->txData; + handle->txDataSize = xfer->dataSize; + handle->txDataSizeAll = xfer->dataSize; + handle->txState = (uint8_t)kLPUART_TxBusy; + + /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. + */ + uint32_t irqMask = DisableGlobalIRQ(); + /* Enable transmitter interrupt. */ + base->CTRL |= (uint32_t)LPUART_CTRL_TIE_MASK; + EnableGlobalIRQ(irqMask); + + status = kStatus_Success; + } + + return status; +} + +/*! + * brief Aborts the interrupt-driven data transmit. + * + * This function aborts the interrupt driven data sending. The user can get the remainBtyes to find out + * how many bytes are not sent out. + * + * param base LPUART peripheral base address. + * param handle LPUART handle pointer. + */ +void LPUART_TransferAbortSend(LPUART_Type *base, lpuart_handle_t *handle) { + assert(NULL != handle); + + /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. */ + uint32_t irqMask = DisableGlobalIRQ(); + base->CTRL &= ~(uint32_t)(LPUART_CTRL_TIE_MASK | LPUART_CTRL_TCIE_MASK); + EnableGlobalIRQ(irqMask); + + handle->txDataSize = 0; + handle->txState = (uint8_t)kLPUART_TxIdle; +} + +/*! + * brief Gets the number of bytes that have been sent out to bus. + * + * This function gets the number of bytes that have been sent out to bus by an interrupt method. + * + * param base LPUART peripheral base address. + * param handle LPUART handle pointer. + * param count Send bytes count. + * retval kStatus_NoTransferInProgress No send in progress. + * retval kStatus_InvalidArgument Parameter is invalid. + * retval kStatus_Success Get successfully through the parameter \p count; + */ +status_t LPUART_TransferGetSendCount(LPUART_Type *base, lpuart_handle_t *handle, uint32_t *count) { + assert(NULL != handle); + assert(NULL != count); + + status_t status = kStatus_Success; + size_t tmptxDataSize = handle->txDataSize; + + if ((uint8_t)kLPUART_TxIdle == handle->txState) { + status = kStatus_NoTransferInProgress; + } else { + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + *count = handle->txDataSizeAll - tmptxDataSize - + ((base->WATER & LPUART_WATER_TXCOUNT_MASK) >> LPUART_WATER_TXCOUNT_SHIFT); + #else + if ((base->STAT & (uint32_t)kLPUART_TxDataRegEmptyFlag) != 0U) { + *count = handle->txDataSizeAll - tmptxDataSize; + } else { + *count = handle->txDataSizeAll - tmptxDataSize - 1U; + } + #endif + } + + return status; +} + +/*! + * brief Receives a buffer of data using the interrupt method. + * + * This function receives data using an interrupt method. This is a non-blocking function + * which returns without waiting to ensure that all data are received. + * If the RX ring buffer is used and not empty, the data in the ring buffer is copied and + * the parameter p receivedBytes shows how many bytes are copied from the ring buffer. + * After copying, if the data in the ring buffer is not enough for read, the receive + * request is saved by the LPUART driver. When the new data arrives, the receive request + * is serviced first. When all data is received, the LPUART driver notifies the upper layer + * through a callback function and passes a status parameter ref kStatus_UART_RxIdle. + * For example, the upper layer needs 10 bytes but there are only 5 bytes in ring buffer. + * The 5 bytes are copied to xfer->data, which returns with the + * parameter p receivedBytes set to 5. For the remaining 5 bytes, the newly arrived data is + * saved from xfer->data[5]. When 5 bytes are received, the LPUART driver notifies the upper layer. + * If the RX ring buffer is not enabled, this function enables the RX and RX interrupt + * to receive data to xfer->data. When all data is received, the upper layer is notified. + * + * param base LPUART peripheral base address. + * param handle LPUART handle pointer. + * param xfer LPUART transfer structure, see #uart_transfer_t. + * param receivedBytes Bytes received from the ring buffer directly. + * retval kStatus_Success Successfully queue the transfer into the transmit queue. + * retval kStatus_LPUART_RxBusy Previous receive request is not finished. + * retval kStatus_InvalidArgument Invalid argument. + */ +status_t LPUART_TransferReceiveNonBlocking(LPUART_Type *base, + lpuart_handle_t *handle, + lpuart_transfer_t *xfer, + size_t *receivedBytes) { + assert(NULL != handle); + assert(NULL != xfer); + assert(NULL != xfer->rxData); + assert(0U != xfer->dataSize); + + uint32_t i; + status_t status; + uint32_t irqMask; + /* How many bytes to copy from ring buffer to user memory. */ + size_t bytesToCopy = 0U; + /* How many bytes to receive. */ + size_t bytesToReceive; + /* How many bytes currently have received. */ + size_t bytesCurrentReceived; + + /* How to get data: + 1. If RX ring buffer is not enabled, then save xfer->data and xfer->dataSize + to lpuart handle, enable interrupt to store received data to xfer->data. When + all data received, trigger callback. + 2. If RX ring buffer is enabled and not empty, get data from ring buffer first. + If there are enough data in ring buffer, copy them to xfer->data and return. + If there are not enough data in ring buffer, copy all of them to xfer->data, + save the xfer->data remained empty space to lpuart handle, receive data + to this empty space and trigger callback when finished. */ + + if ((uint8_t)kLPUART_RxBusy == handle->rxState) { + status = kStatus_LPUART_RxBusy; + } else { + bytesToReceive = xfer->dataSize; + bytesCurrentReceived = 0; + + /* If RX ring buffer is used. */ + if (NULL != handle->rxRingBuffer) { + /* Disable and re-enable the global interrupt to protect the interrupt enable register during + * read-modify-wrte. */ + irqMask = DisableGlobalIRQ(); + /* Disable LPUART RX IRQ, protect ring buffer. */ + base->CTRL &= ~(uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ORIE_MASK); + EnableGlobalIRQ(irqMask); + + /* How many bytes in RX ring buffer currently. */ + bytesToCopy = LPUART_TransferGetRxRingBufferLength(base, handle); + + if (0U != bytesToCopy) { + bytesToCopy = MIN(bytesToReceive, bytesToCopy); + + bytesToReceive -= bytesToCopy; + + /* Copy data from ring buffer to user memory. */ + for (i = 0U; i < bytesToCopy; i++) { + xfer->rxData[bytesCurrentReceived] = handle->rxRingBuffer[handle->rxRingBufferTail]; + bytesCurrentReceived++; + + /* Wrap to 0. Not use modulo (%) because it might be large and slow. */ + if (((uint32_t)handle->rxRingBufferTail + 1U) == handle->rxRingBufferSize) { + handle->rxRingBufferTail = 0U; + } else { + handle->rxRingBufferTail++; + } + } + } + + /* If ring buffer does not have enough data, still need to read more data. */ + if (0U != bytesToReceive) { + /* No data in ring buffer, save the request to LPUART handle. */ + handle->rxData = &xfer->rxData[bytesCurrentReceived]; + handle->rxDataSize = bytesToReceive; + handle->rxDataSizeAll = xfer->dataSize; + handle->rxState = (uint8_t)kLPUART_RxBusy; + } + + /* Disable and re-enable the global interrupt to protect the interrupt enable register during + * read-modify-wrte. */ + irqMask = DisableGlobalIRQ(); + /* Re-enable LPUART RX IRQ. */ + base->CTRL |= (uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ORIE_MASK); + EnableGlobalIRQ(irqMask); + + /* Call user callback since all data are received. */ + if (0U == bytesToReceive) { + if (NULL != handle->callback) { + handle->callback(base, handle, kStatus_LPUART_RxIdle, handle->userData); + } + } + } + /* Ring buffer not used. */ + else { + handle->rxData = &xfer->rxData[bytesCurrentReceived]; + handle->rxDataSize = bytesToReceive; + handle->rxDataSizeAll = bytesToReceive; + handle->rxState = (uint8_t)kLPUART_RxBusy; + + /* Disable and re-enable the global interrupt to protect the interrupt enable register during + * read-modify-wrte. */ + irqMask = DisableGlobalIRQ(); + /* Enable RX interrupt. */ + base->CTRL |= (uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ILIE_MASK | LPUART_CTRL_ORIE_MASK); + EnableGlobalIRQ(irqMask); + } + + /* Return the how many bytes have read. */ + if (NULL != receivedBytes) { + *receivedBytes = bytesCurrentReceived; + } + + status = kStatus_Success; + } + + return status; +} + +/*! + * brief Aborts the interrupt-driven data receiving. + * + * This function aborts the interrupt-driven data receiving. The user can get the remainBytes to find out + * how many bytes not received yet. + * + * param base LPUART peripheral base address. + * param handle LPUART handle pointer. + */ +void LPUART_TransferAbortReceive(LPUART_Type *base, lpuart_handle_t *handle) { + assert(NULL != handle); + + /* Only abort the receive to handle->rxData, the RX ring buffer is still working. */ + if (NULL == handle->rxRingBuffer) { + /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. + */ + uint32_t irqMask = DisableGlobalIRQ(); + /* Disable RX interrupt. */ + base->CTRL &= ~(uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ILIE_MASK | LPUART_CTRL_ORIE_MASK); + EnableGlobalIRQ(irqMask); + } + + handle->rxDataSize = 0U; + handle->rxState = (uint8_t)kLPUART_RxIdle; +} + +/*! + * brief Gets the number of bytes that have been received. + * + * This function gets the number of bytes that have been received. + * + * param base LPUART peripheral base address. + * param handle LPUART handle pointer. + * param count Receive bytes count. + * retval kStatus_NoTransferInProgress No receive in progress. + * retval kStatus_InvalidArgument Parameter is invalid. + * retval kStatus_Success Get successfully through the parameter \p count; + */ +status_t LPUART_TransferGetReceiveCount(LPUART_Type *base, lpuart_handle_t *handle, uint32_t *count) { + assert(NULL != handle); + assert(NULL != count); + + status_t status = kStatus_Success; + size_t tmprxDataSize = handle->rxDataSize; + + if ((uint8_t)kLPUART_RxIdle == handle->rxState) { + status = kStatus_NoTransferInProgress; + } else { + *count = handle->rxDataSizeAll - tmprxDataSize; + } + + return status; +} + +/*! + * brief LPUART IRQ handle function. + * + * This function handles the LPUART transmit and receive IRQ request. + * + * param base LPUART peripheral base address. + * param irqHandle LPUART handle pointer. + */ +void LPUART_TransferHandleIRQ(LPUART_Type *base, void *irqHandle) { + assert(NULL != irqHandle); + + uint8_t count; + uint8_t tempCount; + uint32_t status = LPUART_GetStatusFlags(base); + uint32_t enabledInterrupts = LPUART_GetEnabledInterrupts(base); + uint16_t tpmRxRingBufferHead; + uint32_t tpmData; + uint32_t irqMask; + lpuart_handle_t *handle = (lpuart_handle_t *)irqHandle; + + /* If RX overrun. */ + if ((uint32_t)kLPUART_RxOverrunFlag == ((uint32_t)kLPUART_RxOverrunFlag & status)) { + /* Clear overrun flag, otherwise the RX does not work. */ + base->STAT = ((base->STAT & 0x3FE00000U) | LPUART_STAT_OR_MASK); + + /* Trigger callback. */ + if (NULL != (handle->callback)) { + handle->callback(base, handle, kStatus_LPUART_RxHardwareOverrun, handle->userData); + } + } + /* Receive data register full */ + if ((0U != ((uint32_t)kLPUART_RxDataRegFullFlag & status)) && + (0U != ((uint32_t)kLPUART_RxDataRegFullInterruptEnable & enabledInterrupts))) { + /* Get the size that can be stored into buffer for this interrupt. */ + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + count = ((uint8_t)((base->WATER & LPUART_WATER_RXCOUNT_MASK) >> LPUART_WATER_RXCOUNT_SHIFT)); + #else + count = 1; + #endif + + /* If handle->rxDataSize is not 0, first save data to handle->rxData. */ + while ((0U != handle->rxDataSize) && (0U != count)) { + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + tempCount = (uint8_t)MIN(handle->rxDataSize, count); + #else + tempCount = 1; + #endif + + /* Using non block API to read the data from the registers. */ + LPUART_ReadNonBlocking(base, handle->rxData, tempCount); + handle->rxData = &handle->rxData[tempCount]; + handle->rxDataSize -= tempCount; + count -= tempCount; + + /* If all the data required for upper layer is ready, trigger callback. */ + if (0U == handle->rxDataSize) { + handle->rxState = (uint8_t)kLPUART_RxIdle; + + if (NULL != handle->callback) { + handle->callback(base, handle, kStatus_LPUART_RxIdle, handle->userData); + } + } + } + + /* If use RX ring buffer, receive data to ring buffer. */ + if (NULL != handle->rxRingBuffer) { + while (0U != count--) { + /* If RX ring buffer is full, trigger callback to notify over run. */ + if (LPUART_TransferIsRxRingBufferFull(base, handle)) { + if (NULL != handle->callback) { + handle->callback(base, handle, kStatus_LPUART_RxRingBufferOverrun, handle->userData); + } + } + + /* If ring buffer is still full after callback function, the oldest data is overridden. */ + if (LPUART_TransferIsRxRingBufferFull(base, handle)) { + /* Increase handle->rxRingBufferTail to make room for new data. */ + if (((uint32_t)handle->rxRingBufferTail + 1U) == handle->rxRingBufferSize) { + handle->rxRingBufferTail = 0U; + } else { + handle->rxRingBufferTail++; + } + } + + /* Read data. */ + tpmRxRingBufferHead = handle->rxRingBufferHead; + tpmData = base->DATA; + #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT + if (handle->isSevenDataBits) { + handle->rxRingBuffer[tpmRxRingBufferHead] = (uint8_t)(tpmData & 0x7FU); + } else { + handle->rxRingBuffer[tpmRxRingBufferHead] = (uint8_t)tpmData; + } + #else + handle->rxRingBuffer[tpmRxRingBufferHead] = (uint8_t)tpmData; + #endif + + /* Increase handle->rxRingBufferHead. */ + if (((uint32_t)handle->rxRingBufferHead + 1U) == handle->rxRingBufferSize) { + handle->rxRingBufferHead = 0U; + } else { + handle->rxRingBufferHead++; + } + } + } + /* If no receive request pending, stop RX interrupt. */ + else if (0U == handle->rxDataSize) { + /* Disable and re-enable the global interrupt to protect the interrupt enable register during + * read-modify-wrte. */ + irqMask = DisableGlobalIRQ(); + base->CTRL &= ~(uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ORIE_MASK | LPUART_CTRL_ILIE_MASK); + EnableGlobalIRQ(irqMask); + } else { + } + } + + /* Send data register empty and the interrupt is enabled. */ + if ((0U != ((uint32_t)kLPUART_TxDataRegEmptyFlag & status)) && + (0U != ((uint32_t)kLPUART_TxDataRegEmptyInterruptEnable & enabledInterrupts))) { +/* Get the bytes that available at this moment. */ + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + count = (uint8_t)FSL_FEATURE_LPUART_FIFO_SIZEn(base) - + (uint8_t)((base->WATER & LPUART_WATER_TXCOUNT_MASK) >> LPUART_WATER_TXCOUNT_SHIFT); + #else + count = 1; + #endif + + while ((0U != handle->txDataSize) && (0U != count)) { + #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO + tempCount = (uint8_t)MIN(handle->txDataSize, count); + #else + tempCount = 1; + #endif + + /* Using non block API to write the data to the registers. */ + LPUART_WriteNonBlocking(base, handle->txData, tempCount); + handle->txData = &handle->txData[tempCount]; + handle->txDataSize -= tempCount; + count -= tempCount; + + /* If all the data are written to data register, notify user with the callback, then TX finished. */ + if (0U == handle->txDataSize) { + /* Disable and re-enable the global interrupt to protect the interrupt enable register during + * read-modify-wrte. */ + irqMask = DisableGlobalIRQ(); + /* Disable TX register empty interrupt and enable transmission completion interrupt. */ + base->CTRL = (base->CTRL & ~LPUART_CTRL_TIE_MASK) | LPUART_CTRL_TCIE_MASK; + EnableGlobalIRQ(irqMask); + } + } + } + + /* Transmission complete and the interrupt is enabled. */ + if ((0U != ((uint32_t)kLPUART_TransmissionCompleteFlag & status)) && + (0U != ((uint32_t)kLPUART_TransmissionCompleteInterruptEnable & enabledInterrupts))) { + /* Set txState to idle only when all data has been sent out to bus. */ + handle->txState = (uint8_t)kLPUART_TxIdle; + + /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. + */ + irqMask = DisableGlobalIRQ(); + /* Disable transmission complete interrupt. */ + base->CTRL &= ~(uint32_t)LPUART_CTRL_TCIE_MASK; + EnableGlobalIRQ(irqMask); + + /* Trigger callback. */ + if (NULL != handle->callback) { + handle->callback(base, handle, kStatus_LPUART_TxIdle, handle->userData); + } + } + + /* If IDLE flag is set and the IDLE interrupt is enabled. */ + if ((0U != ((uint32_t)kLPUART_IdleLineFlag & status)) && + (0U != ((uint32_t)kLPUART_IdleLineInterruptEnable & enabledInterrupts))) { + /* Clear IDLE flag.*/ + base->STAT |= LPUART_STAT_IDLE_MASK; + if (NULL != handle->callback) { + handle->callback(base, handle, kStatus_LPUART_IdleLineDetected, handle->userData); + } else { + /* Avoid MISRA 15.7 */ + } + } + +} + +/*! + * brief LPUART Error IRQ handle function. + * + * This function handles the LPUART error IRQ request. + * + * param base LPUART peripheral base address. + * param irqHandle LPUART handle pointer. + */ +void LPUART_TransferHandleErrorIRQ(LPUART_Type *base, void *irqHandle) { + /* To be implemented by User. */ +} +#if defined(FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1) && FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1 +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART0_LPUART1_RX_DriverIRQHandler(void); +void LPUART0_LPUART1_RX_DriverIRQHandler(void) { + /* If handle is registered, treat the transfer function is enabled. */ + if (NULL != s_lpuartHandle[0]) { + s_lpuartIsr(LPUART0, s_lpuartHandle[0]); + } + if (NULL != s_lpuartHandle[1]) { + s_lpuartIsr(LPUART1, s_lpuartHandle[1]); + } + SDK_ISR_EXIT_BARRIER; +} +void LPUART0_LPUART1_TX_DriverIRQHandler(void); +void LPUART0_LPUART1_TX_DriverIRQHandler(void) { + /* If handle is registered, treat the transfer function is enabled. */ + if (NULL != s_lpuartHandle[0]) { + s_lpuartIsr(LPUART0, s_lpuartHandle[0]); + } + if (NULL != s_lpuartHandle[1]) { + s_lpuartIsr(LPUART1, s_lpuartHandle[1]); + } + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART0_LPUART1_DriverIRQHandler(void); +void LPUART0_LPUART1_DriverIRQHandler(void) { + /* If handle is registered, treat the transfer function is enabled. */ + if (NULL != s_lpuartHandle[0]) { + s_lpuartIsr(LPUART0, s_lpuartHandle[0]); + } + if (NULL != s_lpuartHandle[1]) { + s_lpuartIsr(LPUART1, s_lpuartHandle[1]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(LPUART0) +#if !(defined(FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1) && FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART0_TX_DriverIRQHandler(void); +void LPUART0_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART0, s_lpuartHandle[0]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART0_RX_DriverIRQHandler(void); +void LPUART0_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART0, s_lpuartHandle[0]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART0_DriverIRQHandler(void); +void LPUART0_DriverIRQHandler(void) { + s_lpuartIsr(LPUART0, s_lpuartHandle[0]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif +#endif + +#if defined(LPUART1) +#if !(defined(FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1) && FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART1_TX_DriverIRQHandler(void); +void LPUART1_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART1, s_lpuartHandle[1]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART1_RX_DriverIRQHandler(void); +void LPUART1_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART1, s_lpuartHandle[1]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART1_DriverIRQHandler(void); +void LPUART1_DriverIRQHandler(void) { + s_lpuartIsr(LPUART1, s_lpuartHandle[1]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif +#endif + +#if defined(LPUART2) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART2_TX_DriverIRQHandler(void); +void LPUART2_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART2, s_lpuartHandle[2]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART2_RX_DriverIRQHandler(void); +void LPUART2_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART2, s_lpuartHandle[2]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART2_DriverIRQHandler(void); +void LPUART2_DriverIRQHandler(void) { + s_lpuartIsr(LPUART2, s_lpuartHandle[2]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(LPUART3) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART3_TX_DriverIRQHandler(void); +void LPUART3_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART3, s_lpuartHandle[3]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART3_RX_DriverIRQHandler(void); +void LPUART3_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART3, s_lpuartHandle[3]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART3_DriverIRQHandler(void); +void LPUART3_DriverIRQHandler(void) { + s_lpuartIsr(LPUART3, s_lpuartHandle[3]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(LPUART4) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART4_TX_DriverIRQHandler(void); +void LPUART4_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART4, s_lpuartHandle[4]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART4_RX_DriverIRQHandler(void); +void LPUART4_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART4, s_lpuartHandle[4]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART4_DriverIRQHandler(void); +void LPUART4_DriverIRQHandler(void) { + s_lpuartIsr(LPUART4, s_lpuartHandle[4]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(LPUART5) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART5_TX_DriverIRQHandler(void); +void LPUART5_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART5, s_lpuartHandle[5]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART5_RX_DriverIRQHandler(void); +void LPUART5_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART5, s_lpuartHandle[5]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART5_DriverIRQHandler(void); +void LPUART5_DriverIRQHandler(void) { + s_lpuartIsr(LPUART5, s_lpuartHandle[5]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(LPUART6) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART6_TX_DriverIRQHandler(void); +void LPUART6_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART6, s_lpuartHandle[6]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART6_RX_DriverIRQHandler(void); +void LPUART6_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART6, s_lpuartHandle[6]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART6_DriverIRQHandler(void); +void LPUART6_DriverIRQHandler(void) { + s_lpuartIsr(LPUART6, s_lpuartHandle[6]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(LPUART7) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART7_TX_DriverIRQHandler(void); +void LPUART7_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART7, s_lpuartHandle[7]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART7_RX_DriverIRQHandler(void); +void LPUART7_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART7, s_lpuartHandle[7]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART7_DriverIRQHandler(void); +void LPUART7_DriverIRQHandler(void) { + s_lpuartIsr(LPUART7, s_lpuartHandle[7]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(LPUART8) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART8_TX_DriverIRQHandler(void); +void LPUART8_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART8, s_lpuartHandle[8]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART8_RX_DriverIRQHandler(void); +void LPUART8_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART8, s_lpuartHandle[8]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART8_DriverIRQHandler(void); +void LPUART8_DriverIRQHandler(void) { + s_lpuartIsr(LPUART8, s_lpuartHandle[8]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(LPUART9) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART9_TX_DriverIRQHandler(void); +void LPUART9_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART9, s_lpuartHandle[9]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART9_RX_DriverIRQHandler(void); +void LPUART9_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART9, s_lpuartHandle[9]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART9_DriverIRQHandler(void); +void LPUART9_DriverIRQHandler(void) { + s_lpuartIsr(LPUART9, s_lpuartHandle[9]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(LPUART10) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART10_TX_DriverIRQHandler(void); +void LPUART10_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART10, s_lpuartHandle[10]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART10_RX_DriverIRQHandler(void); +void LPUART10_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART10, s_lpuartHandle[10]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART10_DriverIRQHandler(void); +void LPUART10_DriverIRQHandler(void) { + s_lpuartIsr(LPUART10, s_lpuartHandle[10]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(LPUART11) +#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ +void LPUART11_TX_DriverIRQHandler(void); +void LPUART11_TX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART11, s_lpuartHandle[11]); + SDK_ISR_EXIT_BARRIER; +} +void LPUART11_RX_DriverIRQHandler(void); +void LPUART11_RX_DriverIRQHandler(void) { + s_lpuartIsr(LPUART11, s_lpuartHandle[11]); + SDK_ISR_EXIT_BARRIER; +} +#else +void LPUART11_DriverIRQHandler(void); +void LPUART11_DriverIRQHandler(void) { + s_lpuartIsr(LPUART11, s_lpuartHandle[11]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(CM4_0__LPUART) +void M4_0_LPUART_DriverIRQHandler(void); +void M4_0_LPUART_DriverIRQHandler(void) { + s_lpuartIsr(CM4_0__LPUART, s_lpuartHandle[LPUART_GetInstance(CM4_0__LPUART)]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(CM4_1__LPUART) +void M4_1_LPUART_DriverIRQHandler(void); +void M4_1_LPUART_DriverIRQHandler(void) { + s_lpuartIsr(CM4_1__LPUART, s_lpuartHandle[LPUART_GetInstance(CM4_1__LPUART)]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(CM4__LPUART) +void M4_LPUART_DriverIRQHandler(void); +void M4_LPUART_DriverIRQHandler(void) { + s_lpuartIsr(CM4__LPUART, s_lpuartHandle[LPUART_GetInstance(CM4__LPUART)]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(DMA__LPUART0) +void DMA_UART0_INT_DriverIRQHandler(void); +void DMA_UART0_INT_DriverIRQHandler(void) { + s_lpuartIsr(DMA__LPUART0, s_lpuartHandle[LPUART_GetInstance(DMA__LPUART0)]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(DMA__LPUART1) +void DMA_UART1_INT_DriverIRQHandler(void); +void DMA_UART1_INT_DriverIRQHandler(void) { + s_lpuartIsr(DMA__LPUART1, s_lpuartHandle[LPUART_GetInstance(DMA__LPUART1)]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(DMA__LPUART2) +void DMA_UART2_INT_DriverIRQHandler(void); +void DMA_UART2_INT_DriverIRQHandler(void) { + s_lpuartIsr(DMA__LPUART2, s_lpuartHandle[LPUART_GetInstance(DMA__LPUART2)]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(DMA__LPUART3) +void DMA_UART3_INT_DriverIRQHandler(void); +void DMA_UART3_INT_DriverIRQHandler(void) { + s_lpuartIsr(DMA__LPUART3, s_lpuartHandle[LPUART_GetInstance(DMA__LPUART3)]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(DMA__LPUART4) +void DMA_UART4_INT_DriverIRQHandler(void); +void DMA_UART4_INT_DriverIRQHandler(void) { + s_lpuartIsr(DMA__LPUART4, s_lpuartHandle[LPUART_GetInstance(DMA__LPUART4)]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(ADMA__LPUART0) +void ADMA_UART0_INT_DriverIRQHandler(void); +void ADMA_UART0_INT_DriverIRQHandler(void) { + s_lpuartIsr(ADMA__LPUART0, s_lpuartHandle[LPUART_GetInstance(ADMA__LPUART0)]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(ADMA__LPUART1) +void ADMA_UART1_INT_DriverIRQHandler(void); +void ADMA_UART1_INT_DriverIRQHandler(void) { + s_lpuartIsr(ADMA__LPUART1, s_lpuartHandle[LPUART_GetInstance(ADMA__LPUART1)]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(ADMA__LPUART2) +void ADMA_UART2_INT_DriverIRQHandler(void); +void ADMA_UART2_INT_DriverIRQHandler(void) { + s_lpuartIsr(ADMA__LPUART2, s_lpuartHandle[LPUART_GetInstance(ADMA__LPUART2)]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(ADMA__LPUART3) +void ADMA_UART3_INT_DriverIRQHandler(void); +void ADMA_UART3_INT_DriverIRQHandler(void) { + s_lpuartIsr(ADMA__LPUART3, s_lpuartHandle[LPUART_GetInstance(ADMA__LPUART3)]); + SDK_ISR_EXIT_BARRIER; +} +#endif diff --git a/ports/mimxrt/machine_uart.c b/ports/mimxrt/machine_uart.c index 7ae584fdd7454..9f9d6c8fd256f 100644 --- a/ports/mimxrt/machine_uart.c +++ b/ports/mimxrt/machine_uart.c @@ -50,6 +50,10 @@ #define UART_INVERT_RX (2) #define UART_INVERT_MASK (UART_INVERT_TX | UART_INVERT_RX) +#define UART_IRQ_RXIDLE (1) +#define UART_IRQ_TXIDLE (2) +#define MP_UART_ALLOWED_FLAGS (UART_IRQ_RXIDLE | UART_IRQ_TXIDLE) + typedef struct _machine_uart_obj_t { mp_obj_base_t base; struct _lpuart_handle handle; @@ -63,6 +67,9 @@ typedef struct _machine_uart_obj_t { uint8_t *txbuf; uint16_t txbuf_len; bool new; + uint16_t mp_irq_trigger; // user IRQ trigger mask + uint16_t mp_irq_flags; // user IRQ active IRQ flags + mp_irq_obj_t *mp_irq_obj; // user IRQ object } machine_uart_obj_t; typedef struct _iomux_table_t { @@ -137,11 +144,21 @@ bool lpuart_set_iomux_cts(int8_t uart) { void LPUART_UserCallback(LPUART_Type *base, lpuart_handle_t *handle, status_t status, void *userData) { machine_uart_obj_t *self = userData; - if (kStatus_LPUART_TxIdle == status) { + + uint16_t mp_irq_flags = 0; + if (status == kStatus_LPUART_TxIdle) { self->tx_status = kStatus_LPUART_TxIdle; + mp_irq_flags = UART_IRQ_TXIDLE; + } else if (status == kStatus_LPUART_IdleLineDetected) { + mp_irq_flags = UART_IRQ_RXIDLE; + } + // Check the flags to see if the user handler should be called + if (self->mp_irq_trigger & mp_irq_flags) { + self->mp_irq_flags = mp_irq_flags; + mp_irq_handler(self->mp_irq_obj); } - if (kStatus_LPUART_RxRingBufferOverrun == status) { + if (status == kStatus_LPUART_RxRingBufferOverrun) { ; // Ringbuffer full, deassert RTS if flow control is enabled } } @@ -170,16 +187,18 @@ void machine_uart_set_baudrate(mp_obj_t uart_in, uint32_t baudrate) { { MP_ROM_QSTR(MP_QSTR_INV_RX), MP_ROM_INT(UART_INVERT_RX) }, \ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HWCONTROL_CTS) }, \ { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HWCONTROL_RTS) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_TXIDLE), MP_ROM_INT(UART_IRQ_TXIDLE) }, \ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, flow=%s, " - "rxbuf=%d, txbuf=%d, timeout=%u, timeout_char=%u, invert=%s)", + "rxbuf=%d, txbuf=%d, timeout=%u, timeout_char=%u, invert=%s, irq=%d)", self->id, self->config.baudRate_Bps, 8 - self->config.dataBitsCount, _parity_name[self->config.parityMode], self->config.stopBitCount + 1, _flow_name[(self->config.enableTxCTS << 1) | self->config.enableRxRTS], self->handle.rxRingBufferSize, self->txbuf_len, self->timeout, self->timeout_char, - _invert_name[self->invert]); + _invert_name[self->invert], self->mp_irq_trigger); } static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -313,12 +332,17 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, #else LPUART_Init(self->lpuart, &self->config, CLOCK_GetClockRootFreq(kCLOCK_UartClkRoot)); #endif + self->config.rxIdleType = kLPUART_IdleTypeStartBit; + self->config.rxIdleConfig = kLPUART_IdleCharacter4; + LPUART_Init(self->lpuart, &self->config, BOARD_BOOTCLOCKRUN_UART_CLK_ROOT); LPUART_TransferCreateHandle(self->lpuart, &self->handle, LPUART_UserCallback, self); uint8_t *buffer = m_new(uint8_t, rxbuf_len + 1); LPUART_TransferStartRingBuffer(self->lpuart, &self->handle, buffer, rxbuf_len); self->txbuf = m_new(uint8_t, txbuf_len); // Allocate the TX buffer. self->txbuf_len = txbuf_len; + LPUART_EnableInterrupts(self->lpuart, kLPUART_IdleLineInterruptEnable); + // The Uart supports inverting, but not the fsl API, so it has to coded directly // And it has to be done after LPUART_Init. if (self->invert & UART_INVERT_RX) { @@ -356,6 +380,8 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->timeout = 1; self->timeout_char = 1; self->new = true; + self->mp_irq_obj = NULL; + LPUART_GetDefaultConfig(&self->config); // Configure board-specific pin MUX based on the hardware device number. @@ -401,6 +427,55 @@ void machine_uart_deinit_all(void) { } } +static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->mp_irq_trigger = new_trigger; + return 0; +} + +static mp_uint_t uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (info_type == MP_IRQ_INFO_FLAGS) { + return self->mp_irq_flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return self->mp_irq_trigger; + } + return 0; +} + +static const mp_irq_methods_t uart_irq_methods = { + .trigger = uart_irq_trigger, + .info = uart_irq_info, +}; + +static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { + if (self->mp_irq_obj == NULL) { + self->mp_irq_trigger = 0; + self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self)); + } + + if (any_args) { + // Check the handler + mp_obj_t handler = args[MP_IRQ_ARG_INIT_handler].u_obj; + if (handler != mp_const_none && !mp_obj_is_callable(handler)) { + mp_raise_ValueError(MP_ERROR_TEXT("handler must be None or callable")); + } + + // Check the trigger + mp_uint_t trigger = args[MP_IRQ_ARG_INIT_trigger].u_int; + mp_uint_t not_supported = trigger & ~MP_UART_ALLOWED_FLAGS; + if (trigger != 0 && not_supported) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("trigger 0x%04x unsupported"), not_supported); + } + + self->mp_irq_obj->handler = handler; + self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; + self->mp_irq_trigger = trigger; + } + + return self->mp_irq_obj; +} + static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); uint64_t t = ticks_us64() + (uint64_t)self->timeout * 1000; diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index ac918ba4da8d5..5ef6695c1425c 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -114,6 +114,7 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_MACHINE_UART (1) #define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/mimxrt/machine_uart.c" #define MICROPY_PY_MACHINE_UART_SENDBREAK (1) +#define MICROPY_PY_MACHINE_UART_IRQ (1) #define MICROPY_PY_ONEWIRE (1) // fatfs configuration used in ffconf.h From 324c6753479b003d00a6c78a39cb078e73165f30 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 6 Mar 2024 21:05:13 +0100 Subject: [PATCH 0249/1300] renesas-ra/machine_uart: Add the UART.IRQ_RX class constant. The renesas-ra port supports calling a handler to be called on every byte received by UART. For consistency with other ports, the symbol IRQ_RX is added as the trigger name. Side change: Add the received UART data to the REPL input buffer only if it is the REPL UART. Otherwise, every UART would act as REPL input. Signed-off-by: robert-hh --- ports/renesas-ra/machine_uart.c | 1 + ports/renesas-ra/uart.c | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/ports/renesas-ra/machine_uart.c b/ports/renesas-ra/machine_uart.c index 26ed625404374..4a659d107af39 100644 --- a/ports/renesas-ra/machine_uart.c +++ b/ports/renesas-ra/machine_uart.c @@ -41,6 +41,7 @@ #define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \ { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HWCONTROL_RTS) }, \ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HWCONTROL_CTS) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(0x10) }, \ static const char *_parity_name[] = {"None", "ODD", "EVEN"}; diff --git a/ports/renesas-ra/uart.c b/ports/renesas-ra/uart.c index 30707552b3efb..83b3394f7b453 100644 --- a/ports/renesas-ra/uart.c +++ b/ports/renesas-ra/uart.c @@ -71,14 +71,18 @@ static void uart_rx_cb(uint32_t ch, int d) { // even disable the IRQ. This should never happen. return; } - #if MICROPY_KBD_EXCEPTION - if (keyex_cb[ch]) { - (*keyex_cb[ch])(d); - } - #endif + #if defined(MICROPY_HW_UART_REPL) + if (ch == MICROPY_HW_UART_REPL) { + #if MICROPY_KBD_EXCEPTION + if (keyex_cb[ch]) { + (*keyex_cb[ch])(d); + } + #endif - #if MICROPY_HW_ENABLE_UART_REPL - ringbuf_put(&stdin_ringbuf, d); + #if MICROPY_HW_ENABLE_UART_REPL + ringbuf_put(&stdin_ringbuf, d); + #endif + } #endif // Check the flags to see if the user handler should be called From 1027b5f0835d8fba9ccc290f0333c86f7299620a Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 9 Mar 2024 18:24:20 +0100 Subject: [PATCH 0250/1300] cc3200/mods/pybuart: Add the UART.IRQ_RX class constant. As alternative to RX_ANY to match the names used by the other ports. Signed-off-by: robert-hh --- ports/cc3200/mods/pybuart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/cc3200/mods/pybuart.c b/ports/cc3200/mods/pybuart.c index 6ab2371ba708f..eb2b3754f7473 100644 --- a/ports/cc3200/mods/pybuart.c +++ b/ports/cc3200/mods/pybuart.c @@ -590,6 +590,7 @@ static const mp_rom_map_elem_t pyb_uart_locals_dict_table[] = { // class constants { MP_ROM_QSTR(MP_QSTR_RX_ANY), MP_ROM_INT(UART_TRIGGER_RX_ANY) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(UART_TRIGGER_RX_ANY) }, }; static MP_DEFINE_CONST_DICT(pyb_uart_locals_dict, pyb_uart_locals_dict_table); From a04a14163b79753c4c5ee16d80468ce475fd8b23 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 10 Mar 2024 15:14:20 +0100 Subject: [PATCH 0251/1300] esp32/machine_uart: Implement Python UART IRQ with IRQ_RX and IRQ_BREAK. Supported trigger events: IRQ_RX and IRQ_BREAK. Hard IRQ is not supported. Signed-off-by: robert-hh --- ports/esp32/esp32_common.cmake | 1 + ports/esp32/machine_uart.c | 108 ++++++++++++++++++++++++++++++++- ports/esp32/mpconfigport.h | 1 + 3 files changed, 107 insertions(+), 3 deletions(-) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index e928fb439da5e..7c5089639a5f2 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -32,6 +32,7 @@ list(APPEND MICROPY_SOURCE_SHARED ${MICROPY_DIR}/shared/netutils/netutils.c ${MICROPY_DIR}/shared/timeutils/timeutils.c ${MICROPY_DIR}/shared/runtime/interrupt_char.c + ${MICROPY_DIR}/shared/runtime/mpirq.c ${MICROPY_DIR}/shared/runtime/stdout_helpers.c ${MICROPY_DIR}/shared/runtime/sys_stdio_mphal.c ${MICROPY_DIR}/shared/runtime/pyexec.c diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index 50c9a19bef1bb..1a02594132c0c 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -29,6 +29,10 @@ #include "driver/uart.h" #include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "esp_task.h" +#include "shared/runtime/mpirq.h" #include "py/runtime.h" #include "py/stream.h" @@ -49,6 +53,9 @@ #define UART_INV_CTS UART_SIGNAL_CTS_INV #define UART_INV_MASK (UART_INV_TX | UART_INV_RX | UART_INV_RTS | UART_INV_CTS) +#define UART_IRQ_RX (1 << UART_DATA) +#define UART_IRQ_BREAK (1 << UART_BREAK) +#define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_BREAK) typedef struct _machine_uart_obj_t { mp_obj_base_t base; @@ -66,6 +73,11 @@ typedef struct _machine_uart_obj_t { uint16_t timeout; // timeout waiting for first char (in ms) uint16_t timeout_char; // timeout waiting between chars (in ms) uint32_t invert; // lines to invert + TaskHandle_t uart_event_task; + QueueHandle_t uart_queue; + uint16_t mp_irq_trigger; // user IRQ trigger mask + uint16_t mp_irq_flags; // user IRQ active IRQ flags + mp_irq_obj_t *mp_irq_obj; // user IRQ object } machine_uart_obj_t; static const char *_parity_name[] = {"None", "1", "0"}; @@ -80,14 +92,43 @@ static const char *_parity_name[] = {"None", "1", "0"}; { MP_ROM_QSTR(MP_QSTR_INV_CTS), MP_ROM_INT(UART_INV_CTS) }, \ { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HW_FLOWCTRL_RTS) }, \ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HW_FLOWCTRL_CTS) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(UART_IRQ_RX) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_BREAK), MP_ROM_INT(UART_IRQ_BREAK) }, \ + +static void uart_event_task(void *self_in) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + uart_event_t event; + for (;;) { + // Waiting for an UART event. + if (xQueueReceive(self->uart_queue, (void *)&event, (TickType_t)portMAX_DELAY)) { + self->mp_irq_flags = 0; + switch (event.type) { + // Event of UART receiving data + case UART_DATA: + self->mp_irq_flags |= UART_IRQ_RX; + break; + case UART_BREAK: + self->mp_irq_flags |= UART_IRQ_BREAK; + break; + default: + break; + } + // Check the flags to see if the user handler should be called + if (self->mp_irq_trigger & self->mp_irq_flags) { + mp_irq_handler(self->mp_irq_obj); + mp_hal_wake_main_task_from_isr(); + } + } + } +} static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); uint32_t baudrate; check_esp_err(uart_get_baudrate(self->uart_num, &baudrate)); - mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, tx=%d, rx=%d, rts=%d, cts=%d, txbuf=%u, rxbuf=%u, timeout=%u, timeout_char=%u", + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, tx=%d, rx=%d, rts=%d, cts=%d, txbuf=%u, rxbuf=%u, timeout=%u, timeout_char=%u, irq=%d", self->uart_num, baudrate, self->bits, _parity_name[self->parity], - self->stop, self->tx, self->rx, self->rts, self->cts, self->txbuf, self->rxbuf, self->timeout, self->timeout_char); + self->stop, self->tx, self->rx, self->rts, self->cts, self->txbuf, self->rxbuf, self->timeout, self->timeout_char, self->mp_irq_trigger); if (self->invert) { mp_printf(print, ", invert="); uint32_t invert_mask = self->invert; @@ -348,6 +389,7 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->timeout_char = 0; self->invert = 0; self->flowcontrol = 0; + self->uart_event_task = 0; switch (uart_num) { case UART_NUM_0: @@ -378,7 +420,7 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg // Setup check_esp_err(uart_param_config(self->uart_num, &uartcfg)); - check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, 0, NULL, 0)); + check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, 3, &self->uart_queue, 0)); } mp_map_t kw_args; @@ -422,6 +464,66 @@ static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) { check_esp_err(uart_set_baudrate(self->uart_num, baudrate)); } +static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->mp_irq_trigger = new_trigger; + return 0; +} + +static mp_uint_t uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (info_type == MP_IRQ_INFO_FLAGS) { + return self->mp_irq_flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return self->mp_irq_trigger; + } + return 0; +} + +static const mp_irq_methods_t uart_irq_methods = { + .trigger = uart_irq_trigger, + .info = uart_irq_info, +}; + +static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { + if (self->mp_irq_obj == NULL) { + self->mp_irq_trigger = 0; + self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self)); + } + + if (any_args) { + // Check the handler + mp_obj_t handler = args[MP_IRQ_ARG_INIT_handler].u_obj; + if (handler != mp_const_none && !mp_obj_is_callable(handler)) { + mp_raise_ValueError(MP_ERROR_TEXT("handler must be None or callable")); + } + + // Check the trigger + mp_uint_t trigger = args[MP_IRQ_ARG_INIT_trigger].u_int; + mp_uint_t not_supported = trigger & ~MP_UART_ALLOWED_FLAGS; + if (trigger != 0 && not_supported) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("trigger 0x%04x unsupported"), not_supported); + } + + self->mp_irq_obj->handler = handler; + if (args[MP_IRQ_ARG_INIT_hard].u_bool) { + mp_raise_ValueError(MP_ERROR_TEXT("hard IRQ is not supported")); + } + self->mp_irq_obj->ishard = false; + self->mp_irq_trigger = trigger; + // Start a task for handling events + if (handler != mp_const_none && self->uart_event_task == NULL) { + xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self, + ESP_TASKD_EVENT_PRIO, (TaskHandle_t *)&self->uart_event_task, MP_TASK_COREID); + } else if (handler == mp_const_none && self->uart_event_task != NULL) { + vTaskDelete(self->uart_event_task); + self->uart_event_task = NULL; + } + } + + return self->mp_irq_obj; +} + static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 5051afb799ead..a5c6b9c014492 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -155,6 +155,7 @@ #define MICROPY_PY_MACHINE_UART (1) #define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/esp32/machine_uart.c" #define MICROPY_PY_MACHINE_UART_SENDBREAK (1) +#define MICROPY_PY_MACHINE_UART_IRQ (1) #define MICROPY_PY_MACHINE_WDT (1) #define MICROPY_PY_MACHINE_WDT_INCLUDEFILE "ports/esp32/machine_wdt.c" #define MICROPY_PY_NETWORK (1) From 4da5de94bbfa675fb59917827942074307694bde Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 21 Aug 2024 17:35:25 +0200 Subject: [PATCH 0252/1300] nrf/modules/machine/uart: Allow changing the UART baud rate w/o reset. This commit fixes a bug in the existing driver, that the UART baud rate could not be changed without reset or power cycle. It adds as well functionality to UART.deinit(). Signed-off-by: robert-hh --- ports/nrf/modules/machine/uart.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ports/nrf/modules/machine/uart.c b/ports/nrf/modules/machine/uart.c index 90a67bcbc08ad..b4fa0aff1c969 100644 --- a/ports/nrf/modules/machine/uart.c +++ b/ports/nrf/modules/machine/uart.c @@ -62,6 +62,7 @@ typedef struct _machine_uart_buf_t { #define nrfx_uart_tx nrfx_uarte_tx #define nrfx_uart_tx_in_progress nrfx_uarte_tx_in_progress #define nrfx_uart_init nrfx_uarte_init +#define nrfx_uart_uninit nrfx_uarte_uninit #define nrfx_uart_event_t nrfx_uarte_event_t #define NRFX_UART_INSTANCE NRFX_UARTE_INSTANCE @@ -255,7 +256,11 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->buf.rx_ringbuf.iput = 0; // Enable event callback and start asynchronous receive + if (self->initialized) { + nrfx_uart_uninit(self->p_uart); + } nrfx_uart_init(self->p_uart, &config, uart_event_handler); + self->initialized = true; nrfx_uart_rx(self->p_uart, &self->buf.rx_buf[0], 1); #if NRFX_UART_ENABLED @@ -266,7 +271,10 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg } static void mp_machine_uart_deinit(machine_uart_obj_t *self) { - (void)self; + if (self->initialized) { + nrfx_uart_uninit(self->p_uart); + } + self->initialized = false; } // Write a single character on the bus. `data` is an integer to write. From bae809070e0d62cda1b04e97652cf80d513fe86f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 11 Mar 2024 16:27:36 +0100 Subject: [PATCH 0253/1300] nrf/modules/machine/uart: Implement Python UART IRQ for nrf52840 boards. Supported triggers: UART.IRQ_RX and UART.IRQ_TXIDLE. It will probably work on other boards as well, but so far untested. The irq.flags() value is changed only when requested by a triggered event. Do not change it otherwise. Signed-off-by: robert-hh --- ports/nrf/Makefile | 1 + ports/nrf/modules/machine/uart.c | 103 ++++++++++++++++++++++++++++++- ports/nrf/mpconfigport.h | 4 ++ 3 files changed, 106 insertions(+), 2 deletions(-) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index c4150c292cb7c..59e74dce4215a 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -191,6 +191,7 @@ endif SRC_SHARED_C += $(addprefix shared/,\ libc/string0.c \ readline/readline.c \ + runtime/mpirq.c \ runtime/pyexec.c \ runtime/stdout_helpers.c \ runtime/sys_stdio_mphal.c \ diff --git a/ports/nrf/modules/machine/uart.c b/ports/nrf/modules/machine/uart.c index b4fa0aff1c969..8d5a73e095f5e 100644 --- a/ports/nrf/modules/machine/uart.c +++ b/ports/nrf/modules/machine/uart.c @@ -70,6 +70,7 @@ typedef struct _machine_uart_buf_t { #define NRF_UART_HWFC_DISABLED NRF_UARTE_HWFC_DISABLED #define NRF_UART_PARITY_EXCLUDED NRF_UARTE_PARITY_EXCLUDED #define NRFX_UART_EVT_RX_DONE NRFX_UARTE_EVT_RX_DONE +#define NRFX_UART_EVT_TX_DONE NRFX_UARTE_EVT_TX_DONE #define NRFX_UART_EVT_ERROR NRFX_UARTE_EVT_ERROR #define NRF_UART_BAUDRATE_1200 NRF_UARTE_BAUDRATE_1200 @@ -89,18 +90,31 @@ typedef struct _machine_uart_buf_t { #endif +#if MICROPY_PY_MACHINE_UART_IRQ +#define NRFX_UART_IRQ_RX (1 << NRFX_UART_EVT_RX_DONE) +#define NRFX_UART_IRQ_TXIDLE (1 << NRFX_UART_EVT_TX_DONE) +#define MP_UART_ALLOWED_FLAGS (NRFX_UART_IRQ_RX | NRFX_UART_IRQ_TXIDLE) +#endif + typedef struct _machine_uart_obj_t { mp_obj_base_t base; const nrfx_uart_t *p_uart; // Driver instance machine_uart_buf_t buf; uint16_t timeout; // timeout waiting for first char (in ms) uint16_t timeout_char; // timeout waiting between chars (in ms) + uint8_t uart_id; + bool initialized; // static flag. Initialized to False + #if MICROPY_PY_MACHINE_UART_IRQ + uint16_t mp_irq_trigger; // user IRQ trigger mask + uint16_t mp_irq_flags; // user IRQ active IRQ flags + mp_irq_obj_t *mp_irq_obj; // user IRQ object + #endif } machine_uart_obj_t; static const nrfx_uart_t instance0 = NRFX_UART_INSTANCE(0); static machine_uart_obj_t machine_uart_obj[] = { - {{&machine_uart_type}, .p_uart = &instance0} + {{&machine_uart_type}, .p_uart = &instance0, .uart_id = 0} }; void uart_init0(void) { @@ -117,6 +131,9 @@ static int uart_find(mp_obj_t id) { static void uart_event_handler(nrfx_uart_event_t const *p_event, void *p_context) { machine_uart_obj_t *self = p_context; + #if MICROPY_PY_MACHINE_UART_IRQ + uint16_t mp_irq_flags = 0; + #endif if (p_event->type == NRFX_UART_EVT_RX_DONE) { nrfx_uart_rx(self->p_uart, &self->buf.rx_buf[0], 1); int chr = self->buf.rx_buf[0]; @@ -130,10 +147,24 @@ static void uart_event_handler(nrfx_uart_event_t const *p_event, void *p_context { ringbuf_put((ringbuf_t *)&self->buf.rx_ringbuf, chr); } + #if MICROPY_PY_MACHINE_UART_IRQ + mp_irq_flags |= NRFX_UART_IRQ_RX; + #endif } else if (p_event->type == NRFX_UART_EVT_ERROR) { // Perform a read to unlock UART in case of an error nrfx_uart_rx(self->p_uart, &self->buf.rx_buf[0], 1); + } else if (p_event->type == NRFX_UART_EVT_TX_DONE) { + #if MICROPY_PY_MACHINE_UART_IRQ + mp_irq_flags |= NRFX_UART_IRQ_TXIDLE; + #endif + } + #if MICROPY_PY_MACHINE_UART_IRQ + // Check the flags to see if the user handler should be called + if (self->mp_irq_trigger & mp_irq_flags) { + self->mp_irq_flags = mp_irq_flags; + mp_irq_handler(self->mp_irq_obj); } + #endif } bool uart_rx_any(machine_uart_obj_t *self) { @@ -171,8 +202,14 @@ void uart_tx_strn_cooked(machine_uart_obj_t *uart_obj, const char *str, uint len /******************************************************************************/ /* MicroPython bindings */ -// The UART class doesn't have any constants for this port. +#if MICROPY_PY_MACHINE_UART_IRQ +#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(NRFX_UART_IRQ_RX) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_TXIDLE), MP_ROM_INT(NRFX_UART_IRQ_TXIDLE) }, \ + +#else #define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS +#endif static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { mp_printf(print, "UART(0)"); @@ -255,6 +292,12 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->buf.rx_ringbuf.iget = 0; self->buf.rx_ringbuf.iput = 0; + #if MICROPY_PY_MACHINE_UART_IRQ + self->mp_irq_trigger = 0; + self->mp_irq_obj = NULL; + MP_STATE_PORT(nrf_uart_irq_obj)[self->uart_id] = NULL; + #endif + // Enable event callback and start asynchronous receive if (self->initialized) { nrfx_uart_uninit(self->p_uart); @@ -301,6 +344,60 @@ static bool mp_machine_uart_txdone(machine_uart_obj_t *self) { return !nrfx_uart_tx_in_progress(self->p_uart); } +#if MICROPY_PY_MACHINE_UART_IRQ + +static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->mp_irq_trigger = new_trigger; + return 0; +} + +static mp_uint_t uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (info_type == MP_IRQ_INFO_FLAGS) { + return self->mp_irq_flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return self->mp_irq_trigger; + } + return 0; +} + +static const mp_irq_methods_t uart_irq_methods = { + .trigger = uart_irq_trigger, + .info = uart_irq_info, +}; + +static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { + if (self->mp_irq_obj == NULL) { + self->mp_irq_trigger = 0; + self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self)); + MP_STATE_PORT(nrf_uart_irq_obj)[self->uart_id] = self->mp_irq_obj; + } + + if (any_args) { + // Check the handler + mp_obj_t handler = args[MP_IRQ_ARG_INIT_handler].u_obj; + if (handler != mp_const_none && !mp_obj_is_callable(handler)) { + mp_raise_ValueError(MP_ERROR_TEXT("handler must be None or callable")); + } + + // Check the trigger + mp_uint_t trigger = args[MP_IRQ_ARG_INIT_trigger].u_int; + mp_uint_t not_supported = trigger & ~MP_UART_ALLOWED_FLAGS; + if (trigger != 0 && not_supported) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("trigger 0x%04x unsupported"), not_supported); + } + + self->mp_irq_obj->handler = handler; + self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; + self->mp_irq_trigger = trigger; + } + + return self->mp_irq_obj; +} + +#endif + static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { machine_uart_obj_t *self = self_in; byte *buf = buf_in; @@ -383,3 +480,5 @@ static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uint } return MP_STREAM_ERROR; } + +MP_REGISTER_ROOT_POINTER(void *nrf_uart_irq_obj[1]); diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 1c789f779b8e7..7cc8a66d9840c 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -230,6 +230,10 @@ #define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/nrf/modules/machine/uart.c" #define MICROPY_PY_MACHINE_UART_READCHAR_WRITECHAR (1) +#if defined(NRF52840) +#define MICROPY_PY_MACHINE_UART_IRQ (1) +#endif + #ifndef MICROPY_PY_MACHINE_TIMER_NRF #define MICROPY_PY_MACHINE_TIMER_NRF (1) #endif From a86619fb6ffb4c860f4c1cc25643f13c6db92467 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 13 Mar 2024 09:17:50 +0100 Subject: [PATCH 0254/1300] stm32/machine_uart: Add the UART.IRQ_RX event for UART.irq(). Just adding the event symbol. No code change required, and no impact on code execution time when the event is not selected. Tested with STM32F4xx, STM32F7xx and STM32H7xx. Signed-off-by: robert-hh --- ports/stm32/machine_uart.c | 1 + ports/stm32/uart.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 0f139ae83272f..277ae67ef2c54 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -41,6 +41,7 @@ { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HWCONTROL_RTS) }, \ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HWCONTROL_CTS) }, \ { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_FLAG_IDLE) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(UART_FLAG_RXNE) }, \ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h index 956cbb044f6c0..de4b70cdea738 100644 --- a/ports/stm32/uart.h +++ b/ports/stm32/uart.h @@ -52,7 +52,7 @@ typedef enum { #define CHAR_WIDTH_9BIT (1) // OR-ed IRQ flags which are allowed to be used by the user -#define MP_UART_ALLOWED_FLAGS UART_FLAG_IDLE +#define MP_UART_ALLOWED_FLAGS (UART_FLAG_IDLE | UART_FLAG_RXNE) // OR-ed IRQ flags which should not be touched by the user #define MP_UART_RESERVED_FLAGS UART_FLAG_RXNE From ef69d0f2d3a795666c2bf8c7e202604fc5cd5342 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 27 Jun 2024 19:37:54 +0200 Subject: [PATCH 0255/1300] samd/machine_uart: Implement UART.IRQ_RXIDLE based on the softtimer. With the softtimer the minimal delay between the end of a message and the trigger is 2 ms. For baud rates <= 9600 baud it's three character times. Tested with baud rates up tp 115200 baud. The timer used for RXIDLE is running only during UART receive, saving execution cycles when the timer is not needed. The irq.flags() value is changed only with an expected event. Do not change it otherwise. Signed-off-by: robert-hh --- ports/samd/machine_uart.c | 89 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c index aa781a17073ae..b0dc4c5768d13 100644 --- a/ports/samd/machine_uart.c +++ b/ports/samd/machine_uart.c @@ -32,6 +32,7 @@ #include "py/ringbuf.h" #include "samd_soc.h" #include "pin_af.h" +#include "shared/runtime/softtimer.h" #define DEFAULT_UART_BAUDRATE (115200) #define DEFAULT_BUFFER_SIZE (256) @@ -40,17 +41,31 @@ #define FLOW_CONTROL_RTS (1) #define FLOW_CONTROL_CTS (2) -#define MP_UART_ALLOWED_FLAGS (SERCOM_USART_INTFLAG_RXC | SERCOM_USART_INTFLAG_TXC) - #if MICROPY_PY_MACHINE_UART_IRQ +#define UART_IRQ_RXIDLE (4096) +#define RXIDLE_TIMER_MIN (1) +#define MP_UART_ALLOWED_FLAGS (SERCOM_USART_INTFLAG_RXC | SERCOM_USART_INTFLAG_TXC | UART_IRQ_RXIDLE) + #define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \ { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(SERCOM_USART_INTFLAG_RXC) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \ { MP_ROM_QSTR(MP_QSTR_IRQ_TXIDLE), MP_ROM_INT(SERCOM_USART_INTFLAG_TXC) }, \ +enum { + RXIDLE_INACTIVE, + RXIDLE_STANDBY, + RXIDLE_ARMED, + RXIDLE_ALERT, +}; #else #define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS #endif +typedef struct _soft_timer_entry_extended_t { + soft_timer_entry_t base; + void *context; +} soft_timer_entry_extended_t; + typedef struct _machine_uart_obj_t { mp_obj_base_t base; uint8_t id; @@ -80,6 +95,9 @@ typedef struct _machine_uart_obj_t { uint16_t mp_irq_trigger; // user IRQ trigger mask uint16_t mp_irq_flags; // user IRQ active IRQ flags mp_irq_obj_t *mp_irq_obj; // user IRQ object + soft_timer_entry_extended_t rxidle_timer; + uint8_t rxidle_state; + uint16_t rxidle_ms; #endif } machine_uart_obj_t; @@ -108,14 +126,24 @@ void common_uart_irq_handler(int uart_id) { if (self != NULL) { Sercom *uart = sercom_instance[self->id]; #if MICROPY_PY_MACHINE_UART_IRQ - self->mp_irq_flags = 0; + uint16_t mp_irq_flags = 0; #endif if (uart->USART.INTFLAG.bit.RXC != 0) { // Now handler the incoming data uart_drain_rx_fifo(self, uart); #if MICROPY_PY_MACHINE_UART_IRQ if (ringbuf_avail(&self->read_buffer) > 0) { - self->mp_irq_flags = SERCOM_USART_INTFLAG_RXC; + if (self->mp_irq_trigger & UART_IRQ_RXIDLE) { + if (self->rxidle_state != RXIDLE_INACTIVE) { + if (self->rxidle_state == RXIDLE_STANDBY) { + self->rxidle_timer.base.mode = SOFT_TIMER_MODE_PERIODIC; + soft_timer_insert(&self->rxidle_timer.base, self->rxidle_ms); + } + self->rxidle_state = RXIDLE_ALERT; + } + } else { + mp_irq_flags = SERCOM_USART_INTFLAG_RXC; + } } #endif } else if (uart->USART.INTFLAG.bit.DRE != 0) { @@ -126,7 +154,7 @@ void common_uart_irq_handler(int uart_id) { } else { #if MICROPY_PY_MACHINE_UART_IRQ // Set the TXIDLE flag - self->mp_irq_flags |= SERCOM_USART_INTFLAG_TXC; + mp_irq_flags |= SERCOM_USART_INTFLAG_TXC; #endif // Stop the DRE interrupt if there is no more data uart->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; @@ -137,14 +165,34 @@ void common_uart_irq_handler(int uart_id) { uart->USART.INTENCLR.reg = (uint8_t) ~(SERCOM_USART_INTENCLR_DRE | SERCOM_USART_INTENCLR_RXC); #if MICROPY_PY_MACHINE_UART_IRQ - // Check the flags to see if the user handler should be called - if (self->mp_irq_trigger & self->mp_irq_flags) { + // Check the flags to see if the uart user handler should be called + // The handler for RXIDLE is called in the timer callback + if (self->mp_irq_trigger & mp_irq_flags) { + self->mp_irq_flags = mp_irq_flags; mp_irq_handler(self->mp_irq_obj); } #endif } } +#if MICROPY_PY_MACHINE_UART_IRQ +static void uart_soft_timer_callback(soft_timer_entry_t *self) { + machine_uart_obj_t *uart = ((soft_timer_entry_extended_t *)self)->context; + if (uart->rxidle_state == RXIDLE_ALERT) { + // At the first call, just switch the state + uart->rxidle_state = RXIDLE_ARMED; + } else if (uart->rxidle_state == RXIDLE_ARMED) { + // At the second call, run the irq callback and stop the timer + // by setting the mode to SOFT_TIMER_MODE_ONE_SHOT. + // Calling soft_timer_remove() would fail here. + self->mode = SOFT_TIMER_MODE_ONE_SHOT; + uart->rxidle_state = RXIDLE_STANDBY; + uart->mp_irq_flags = UART_IRQ_RXIDLE; + mp_irq_handler(uart->mp_irq_obj); + } +} +#endif + // Configure the Sercom device static void machine_sercom_configure(machine_uart_obj_t *self) { Sercom *uart = sercom_instance[self->id]; @@ -424,6 +472,7 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg #endif #if MICROPY_PY_MACHINE_UART_IRQ self->mp_irq_obj = NULL; + self->rxidle_state = RXIDLE_INACTIVE; #endif self->new = true; MP_STATE_PORT(sercom_table[uart_id]) = self; @@ -486,8 +535,33 @@ static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) { #if MICROPY_PY_MACHINE_UART_IRQ +// Configure the timer used for IRQ_RXIDLE +static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger) { + self->rxidle_state = RXIDLE_INACTIVE; + + if (trigger & UART_IRQ_RXIDLE) { + // The RXIDLE event is always a soft IRQ. + self->mp_irq_obj->ishard = false; + mp_int_t ms = 13000 / self->baudrate + 1; + if (ms < RXIDLE_TIMER_MIN) { + ms = RXIDLE_TIMER_MIN; + } + self->rxidle_ms = ms; + self->rxidle_timer.context = self; + soft_timer_static_init( + &self->rxidle_timer.base, + SOFT_TIMER_MODE_PERIODIC, + ms, + uart_soft_timer_callback + ); + self->rxidle_state = RXIDLE_STANDBY; + } +} + static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + uart_irq_configure_timer(self, new_trigger); self->mp_irq_trigger = new_trigger; return 0; } @@ -526,6 +600,7 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args if (trigger != 0 && not_supported) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("trigger 0x%04x unsupported"), not_supported); } + uart_irq_configure_timer(self, trigger); self->mp_irq_obj->handler = handler; self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; From 7045975d046196e57efb79dc70e6b715155b9bca Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 3 Jul 2024 16:32:35 +0200 Subject: [PATCH 0256/1300] renesas-ra/machine_uart: Implement UART.IRQ_RXIDLE based on softtimer. Allowing to define the trigger UART.IRQ_RXIDLE as well as UART.IRQ_RX. The delay for the IRQ_RXIDLE interrupt is about 3 character times or 1-2 ms, whichever is larger. The irq.flags() value is changed only with an expected event. Do not change it otherwise. Signed-off-by: robert-hh --- ports/renesas-ra/machine_uart.c | 29 ++++++++++++++++++++++++++++- ports/renesas-ra/uart.c | 27 ++++++++++++++++++++++++++- ports/renesas-ra/uart.h | 24 +++++++++++++++++++++++- 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/ports/renesas-ra/machine_uart.c b/ports/renesas-ra/machine_uart.c index 4a659d107af39..b70978ad7a5fe 100644 --- a/ports/renesas-ra/machine_uart.c +++ b/ports/renesas-ra/machine_uart.c @@ -41,7 +41,8 @@ #define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \ { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HWCONTROL_RTS) }, \ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HWCONTROL_CTS) }, \ - { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(0x10) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(UART_IRQ_RX) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \ static const char *_parity_name[] = {"None", "ODD", "EVEN"}; @@ -297,6 +298,8 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg // reference existing UART object self = MP_STATE_PORT(machine_uart_obj_all)[uart_id]; } + self->mp_irq_obj = NULL; + self->rxidle_state = RXIDLE_INACTIVE; // start the peripheral mp_map_t kw_args; @@ -351,6 +354,29 @@ static mp_int_t mp_machine_uart_readchar(machine_uart_obj_t *self) { } } +// Configure the timer used for IRQ_RXIDLE +void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger) { + self->rxidle_state = RXIDLE_INACTIVE; + + if (trigger & UART_IRQ_RXIDLE) { + // The RXIDLE event is always a soft IRQ. + self->mp_irq_obj->ishard = false; + mp_int_t ms = 13000 / self->baudrate + 1; + if (ms < RXIDLE_TIMER_MIN) { + ms = RXIDLE_TIMER_MIN; + } + self->rxidle_ms = ms; + self->rxidle_timer.context = self; + soft_timer_static_init( + &self->rxidle_timer.base, + SOFT_TIMER_MODE_PERIODIC, + ms, + uart_soft_timer_callback + ); + self->rxidle_state = RXIDLE_STANDBY; + } +} + static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { if (self->mp_irq_obj == NULL) { self->mp_irq_trigger = 0; @@ -376,6 +402,7 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args self->mp_irq_obj->handler = handler; self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; self->mp_irq_trigger = trigger; + uart_irq_configure_timer(self, trigger); uart_irq_config(self, true); } diff --git a/ports/renesas-ra/uart.c b/ports/renesas-ra/uart.c index 83b3394f7b453..d17a1fc913fe0 100644 --- a/ports/renesas-ra/uart.c +++ b/ports/renesas-ra/uart.c @@ -85,12 +85,36 @@ static void uart_rx_cb(uint32_t ch, int d) { } #endif + if ((self->mp_irq_trigger & UART_IRQ_RXIDLE) && (self->rxidle_state != RXIDLE_INACTIVE)) { + if (self->rxidle_state == RXIDLE_STANDBY) { + self->rxidle_timer.base.mode = SOFT_TIMER_MODE_PERIODIC; + soft_timer_insert(&self->rxidle_timer.base, self->rxidle_ms); + } + self->rxidle_state = RXIDLE_ALERT; + } // Check the flags to see if the user handler should be called - if (self->mp_irq_trigger) { + if (self->mp_irq_trigger & UART_IRQ_RX) { + self->mp_irq_flags = UART_IRQ_RX; mp_irq_handler(self->mp_irq_obj); } } +void uart_soft_timer_callback(soft_timer_entry_t *self) { + machine_uart_obj_t *uart = ((soft_timer_entry_extended_t *)self)->context; + if (uart->rxidle_state == RXIDLE_ALERT) { + // At the first call, just switch the state + uart->rxidle_state = RXIDLE_ARMED; + } else if (uart->rxidle_state == RXIDLE_ARMED) { + // At the second call, run the irq callback and stop the timer + // by setting the mode to SOFT_TIMER_MODE_ONE_SHOT. + // Calling soft_timer_remove() would fail here. + self->mode = SOFT_TIMER_MODE_ONE_SHOT; + uart->rxidle_state = RXIDLE_STANDBY; + uart->mp_irq_flags = UART_IRQ_RXIDLE; + mp_irq_handler(uart->mp_irq_obj); + } +} + void uart_init0(void) { } @@ -513,6 +537,7 @@ void uart_tx_strn(machine_uart_obj_t *uart_obj, const char *str, uint len) { static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); uart_irq_config(self, false); + uart_irq_configure_timer(self, new_trigger); self->mp_irq_trigger = new_trigger; uart_irq_config(self, true); return 0; diff --git a/ports/renesas-ra/uart.h b/ports/renesas-ra/uart.h index ee8eb321d552c..1f74df412cb43 100644 --- a/ports/renesas-ra/uart.h +++ b/ports/renesas-ra/uart.h @@ -29,6 +29,7 @@ #define MICROPY_INCLUDED_RA_UART_H #include "shared/runtime/mpirq.h" +#include "shared/runtime/softtimer.h" #include "pin.h" typedef enum { @@ -57,12 +58,28 @@ typedef enum { #define UART_HWCONTROL_CTS (1) #define UART_HWCONTROL_RTS (2) +#define UART_IRQ_RX (0x10) +#define UART_IRQ_RXIDLE (0x1000) +#define RXIDLE_TIMER_MIN (1) + // OR-ed IRQ flags which are allowed to be used by the user -#define MP_UART_ALLOWED_FLAGS ((uint32_t)0x00000010) +#define MP_UART_ALLOWED_FLAGS ((uint32_t)(UART_IRQ_RX | UART_IRQ_RXIDLE)) // OR-ed IRQ flags which should not be touched by the user #define MP_UART_RESERVED_FLAGS ((uint16_t)0x0020) +enum { + RXIDLE_INACTIVE, + RXIDLE_STANDBY, + RXIDLE_ARMED, + RXIDLE_ALERT, +}; + +typedef struct _soft_timer_entry_extended_t { + soft_timer_entry_t base; + void *context; +} soft_timer_entry_extended_t; + typedef struct _machine_uart_obj_t { mp_obj_base_t base; machine_uart_t uart_id : 8; @@ -87,6 +104,9 @@ typedef struct _machine_uart_obj_t { uint16_t mp_irq_trigger; // user IRQ trigger mask uint16_t mp_irq_flags; // user IRQ active IRQ flags mp_irq_obj_t *mp_irq_obj; // user IRQ object + soft_timer_entry_extended_t rxidle_timer; + uint8_t rxidle_state; + uint16_t rxidle_ms; } machine_uart_obj_t; extern const mp_irq_methods_t uart_irq_methods; @@ -100,6 +120,8 @@ void uart_irq_config(machine_uart_obj_t *self, bool enable); void uart_set_rxbuf(machine_uart_obj_t *self, size_t len, void *buf); void uart_deinit(machine_uart_obj_t *uart_obj); // void uart_irq_handler(mp_uint_t uart_id); +void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger); +void uart_soft_timer_callback(soft_timer_entry_t *self); void uart_attach_to_repl(machine_uart_obj_t *self, bool attached); uint32_t uart_get_baudrate(machine_uart_obj_t *self); From a38b4f42876cf274d77752f5f8cc559deb87f9fa Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 4 Jul 2024 16:57:29 +0200 Subject: [PATCH 0257/1300] esp32/machine_uart: Implement UART.RX_IDLE based on machine.Timer. The UART.IRQ_IDLE callback is called about two character times after the last byte, or 1 ms, whichever is larger. For the irq, timer 0 is used. machine_timer.c had to be reworked to make it's mechanisms available for machine_uart.c. The irq.flags() value is change only at a requested event. Otherwise keep the state. Signed-off-by: robert-hh --- ports/esp32/machine_timer.c | 49 ++++++++----------- ports/esp32/machine_timer.h | 66 ++++++++++++++++++++++++++ ports/esp32/machine_uart.c | 93 +++++++++++++++++++++++++++++++++++-- 3 files changed, 173 insertions(+), 35 deletions(-) create mode 100644 ports/esp32/machine_timer.h diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index 011f87ba9eb30..278deb10649d4 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -38,6 +38,7 @@ #include "hal/timer_hal.h" #include "hal/timer_ll.h" #include "soc/timer_periph.h" +#include "machine_timer.h" #define TIMER_DIVIDER 8 @@ -46,27 +47,8 @@ #define TIMER_FLAGS 0 -typedef struct _machine_timer_obj_t { - mp_obj_base_t base; - - timer_hal_context_t hal_context; - mp_uint_t group; - mp_uint_t index; - - mp_uint_t repeat; - // ESP32 timers are 64 or 54-bit - uint64_t period; - - mp_obj_t callback; - - intr_handle_t handle; - - struct _machine_timer_obj_t *next; -} machine_timer_obj_t; - const mp_obj_type_t machine_timer_type; -static void machine_timer_disable(machine_timer_obj_t *self); static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); void machine_timer_deinit_all(void) { @@ -91,18 +73,17 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr #endif } -static mp_obj_t machine_timer_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, MP_OBJ_FUN_ARGS_MAX, true); +machine_timer_obj_t *machine_timer_create(mp_uint_t timer) { + + machine_timer_obj_t *self = NULL; #if CONFIG_IDF_TARGET_ESP32C3 - mp_uint_t group = mp_obj_get_int(args[0]) & 1; + mp_uint_t group = timer & 1; mp_uint_t index = 0; #else - mp_uint_t group = (mp_obj_get_int(args[0]) >> 1) & 1; - mp_uint_t index = mp_obj_get_int(args[0]) & 1; + mp_uint_t group = (timer >> 1) & 1; + mp_uint_t index = timer & 1; #endif - machine_timer_obj_t *self = NULL; - // Check whether the timer is already initialized, if so use it for (machine_timer_obj_t *t = MP_STATE_PORT(machine_timer_obj_head); t; t = t->next) { if (t->group == group && t->index == index) { @@ -120,6 +101,14 @@ static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, self->next = MP_STATE_PORT(machine_timer_obj_head); MP_STATE_PORT(machine_timer_obj_head) = self; } + return self; +} + +static mp_obj_t machine_timer_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, MP_OBJ_FUN_ARGS_MAX, true); + + // Create the new timer. + machine_timer_obj_t *self = machine_timer_create(mp_obj_get_int(args[0])); if (n_args > 1 || n_kw > 0) { mp_map_t kw_args; @@ -130,7 +119,7 @@ static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, return self; } -static void machine_timer_disable(machine_timer_obj_t *self) { +void machine_timer_disable(machine_timer_obj_t *self) { if (self->hal_context.dev != NULL) { // Disable the counter and alarm. timer_ll_enable_counter(self->hal_context.dev, self->index, false); @@ -162,7 +151,7 @@ static void machine_timer_isr(void *self_in) { } } -static void machine_timer_enable(machine_timer_obj_t *self) { +void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) { // Initialise the timer. timer_hal_init(&self->hal_context, self->group, self->index); timer_ll_enable_counter(self->hal_context.dev, self->index, false); @@ -176,7 +165,7 @@ static void machine_timer_enable(machine_timer_obj_t *self) { timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index)); ESP_ERROR_CHECK( esp_intr_alloc(timer_group_periph_signals.groups[self->group].timer_irq_id[self->index], - TIMER_FLAGS, machine_timer_isr, self, &self->handle) + TIMER_FLAGS, timer_isr, self, &self->handle) ); timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), true); @@ -234,7 +223,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n self->callback = args[ARG_callback].u_obj; self->handle = NULL; - machine_timer_enable(self); + machine_timer_enable(self, machine_timer_isr); return mp_const_none; } diff --git a/ports/esp32/machine_timer.h b/ports/esp32/machine_timer.h new file mode 100644 index 0000000000000..914bedd86baa9 --- /dev/null +++ b/ports/esp32/machine_timer.h @@ -0,0 +1,66 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2015 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky + * + * 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_ESP32_MACHINE_TIMER_H +#define MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H + +#include "hal/timer_hal.h" +#include "hal/timer_ll.h" +#include "soc/timer_periph.h" + +#define TIMER_DIVIDER 8 + +// TIMER_BASE_CLK is normally 80MHz. TIMER_DIVIDER ought to divide this exactly +#define TIMER_SCALE (APB_CLK_FREQ / TIMER_DIVIDER) + +#define TIMER_FLAGS 0 + +typedef struct _machine_timer_obj_t { + mp_obj_base_t base; + + timer_hal_context_t hal_context; + mp_uint_t group; + mp_uint_t index; + + mp_uint_t repeat; + // ESP32 timers are 64-bit + uint64_t period; + + mp_obj_t callback; + + intr_handle_t handle; + + struct _machine_timer_obj_t *next; +} machine_timer_obj_t; + +machine_timer_obj_t *machine_timer_create(mp_uint_t timer); +void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)); +void machine_timer_disable(machine_timer_obj_t *self); + +#endif // MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index 1a02594132c0c..d260c45c48f80 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -39,6 +39,7 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "uart.h" +#include "machine_timer.h" #if SOC_UART_SUPPORT_XTAL_CLK // Works independently of APB frequency, on ESP32C3, ESP32S3. @@ -54,8 +55,17 @@ #define UART_INV_MASK (UART_INV_TX | UART_INV_RX | UART_INV_RTS | UART_INV_CTS) #define UART_IRQ_RX (1 << UART_DATA) +#define UART_IRQ_RXIDLE (0x1000) #define UART_IRQ_BREAK (1 << UART_BREAK) -#define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_BREAK) +#define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_RXIDLE | UART_IRQ_BREAK) +#define RXIDLE_TIMER_MIN (5000) // 500 us + +enum { + RXIDLE_INACTIVE, + RXIDLE_STANDBY, + RXIDLE_ARMED, + RXIDLE_ALERT, +}; typedef struct _machine_uart_obj_t { mp_obj_base_t base; @@ -78,6 +88,9 @@ typedef struct _machine_uart_obj_t { uint16_t mp_irq_trigger; // user IRQ trigger mask uint16_t mp_irq_flags; // user IRQ active IRQ flags mp_irq_obj_t *mp_irq_obj; // user IRQ object + machine_timer_obj_t *rxidle_timer; + uint8_t rxidle_state; + uint16_t rxidle_period; } machine_uart_obj_t; static const char *_parity_name[] = {"None", "1", "0"}; @@ -93,28 +106,67 @@ static const char *_parity_name[] = {"None", "1", "0"}; { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HW_FLOWCTRL_RTS) }, \ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HW_FLOWCTRL_CTS) }, \ { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(UART_IRQ_RX) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \ { MP_ROM_QSTR(MP_QSTR_IRQ_BREAK), MP_ROM_INT(UART_IRQ_BREAK) }, \ +static void uart_timer_callback(void *self_in) { + machine_timer_obj_t *self = self_in; + + uint32_t intr_status = timer_ll_get_intr_status(self->hal_context.dev); + + if (intr_status & TIMER_LL_EVENT_ALARM(self->index)) { + timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index)); + if (self->repeat) { + timer_ll_enable_alarm(self->hal_context.dev, self->index, true); + } + } + + // The UART object is referred here by the callback field. + machine_uart_obj_t *uart = (machine_uart_obj_t *)self->callback; + if (uart->rxidle_state == RXIDLE_ALERT) { + // At the first call, just switch the state + uart->rxidle_state = RXIDLE_ARMED; + } else if (uart->rxidle_state == RXIDLE_ARMED) { + // At the second call, run the irq callback and stop the timer + uart->rxidle_state = RXIDLE_STANDBY; + uart->mp_irq_flags = UART_IRQ_RXIDLE; + mp_irq_handler(uart->mp_irq_obj); + mp_hal_wake_main_task_from_isr(); + machine_timer_disable(uart->rxidle_timer); + } +} + static void uart_event_task(void *self_in) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); uart_event_t event; for (;;) { // Waiting for an UART event. if (xQueueReceive(self->uart_queue, (void *)&event, (TickType_t)portMAX_DELAY)) { - self->mp_irq_flags = 0; + uint16_t mp_irq_flags = 0; switch (event.type) { // Event of UART receiving data case UART_DATA: - self->mp_irq_flags |= UART_IRQ_RX; + if (self->mp_irq_trigger & UART_IRQ_RXIDLE) { + if (self->rxidle_state != RXIDLE_INACTIVE) { + if (self->rxidle_state == RXIDLE_STANDBY) { + self->rxidle_timer->repeat = true; + self->rxidle_timer->handle = NULL; + machine_timer_enable(self->rxidle_timer, uart_timer_callback); + } + } + self->rxidle_state = RXIDLE_ALERT; + } + mp_irq_flags |= UART_IRQ_RX; break; case UART_BREAK: - self->mp_irq_flags |= UART_IRQ_BREAK; + mp_irq_flags |= UART_IRQ_BREAK; break; default: break; } // Check the flags to see if the user handler should be called - if (self->mp_irq_trigger & self->mp_irq_flags) { + if (self->mp_irq_trigger & mp_irq_flags) { + self->mp_irq_flags = mp_irq_flags; mp_irq_handler(self->mp_irq_obj); mp_hal_wake_main_task_from_isr(); } @@ -390,6 +442,7 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->invert = 0; self->flowcontrol = 0; self->uart_event_task = 0; + self->rxidle_state = RXIDLE_INACTIVE; switch (uart_num) { case UART_NUM_0: @@ -464,8 +517,35 @@ static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) { check_esp_err(uart_set_baudrate(self->uart_num, baudrate)); } +// Configure the timer used for IRQ_RXIDLE +static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger) { + + self->rxidle_state = RXIDLE_INACTIVE; + + if (trigger & UART_IRQ_RXIDLE) { + // The RXIDLE event is always a soft IRQ. + self->mp_irq_obj->ishard = false; + uint32_t baudrate; + uart_get_baudrate(self->uart_num, &baudrate); + mp_int_t period = TIMER_SCALE * 20 / baudrate + 1; + if (period < RXIDLE_TIMER_MIN) { + period = RXIDLE_TIMER_MIN; + } + self->rxidle_period = period; + self->rxidle_timer->period = period; + // The Python callback is not used. So use this + // data field to hold a reference to the UART object. + self->rxidle_timer->callback = self; + self->rxidle_timer->repeat = true; + self->rxidle_timer->handle = NULL; + self->rxidle_state = RXIDLE_STANDBY; + } +} + static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + uart_irq_configure_timer(self, new_trigger); self->mp_irq_trigger = new_trigger; return 0; } @@ -511,6 +591,9 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args } self->mp_irq_obj->ishard = false; self->mp_irq_trigger = trigger; + self->rxidle_timer = machine_timer_create(0); + uart_irq_configure_timer(self, trigger); + // Start a task for handling events if (handler != mp_const_none && self->uart_event_task == NULL) { xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self, From 9bbe61607ac3e97d96294d588afd09d82613c6a9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 6 Mar 2024 13:51:31 +1100 Subject: [PATCH 0258/1300] docs/library/machine.UART: Fix UART.irq docs to match current code. These docs now match the code in `extmod/machine_uart.c`. IRQ trigger support still need to be updated for each port (to be done in a follow-up commit). Signed-off-by: Damien George --- docs/library/machine.UART.rst | 62 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/docs/library/machine.UART.rst b/docs/library/machine.UART.rst index 072bdb7188a3d..6f47eb9abb7c4 100644 --- a/docs/library/machine.UART.rst +++ b/docs/library/machine.UART.rst @@ -152,31 +152,6 @@ Methods Send a break condition on the bus. This drives the bus low for a duration longer than required for a normal transmission of a character. -.. method:: UART.irq(trigger, priority=1, handler=None, wake=machine.IDLE) - - Create a callback to be triggered when data is received on the UART. - - - *trigger* can only be ``UART.RX_ANY`` - - *priority* level of the interrupt. Can take values in the range 1-7. - Higher values represent higher priorities. - - *handler* an optional function to be called when new characters arrive. - - *wake* can only be ``machine.IDLE``. - - .. note:: - - The handler will be called whenever any of the following two conditions are met: - - - 8 new characters have been received. - - At least 1 new character is waiting in the Rx buffer and the Rx line has been - silent for the duration of 1 complete frame. - - This means that when the handler function is called there will be between 1 to 8 - characters waiting. - - Returns an irq object. - - Availability: WiPy. - .. method:: UART.flush() Waits until all data has been sent. In case of a timeout, an exception is raised. The timeout @@ -203,11 +178,42 @@ Methods Availability: rp2, esp32, esp8266, mimxrt, cc3200, stm32, nrf ports, renesas-ra +.. method:: UART.irq(handler=None, trigger=0, hard=False) + + Configure an interrupt handler to be called when a UART event occurs. + + The arguments are: + + - *handler* is an optional function to be called when the interrupt event + triggers. The handler must take exactly one argument which is the + ``UART`` instance. + + - *trigger* configures the event which can generate an interrupt. + Possible values are: + + - ``UART.IRQ_RXIDLE`` interrupt after receiving at least one character + and then the RX line goes idle. + + - *hard* if true a hardware interrupt is used. This reduces the delay + between the pin change and the handler being called. Hard interrupt + handlers may not allocate memory; see :ref:`isr_rules`. + + Returns an irq object. + + Availability: renesas-ra, stm32. + Constants --------- -.. data:: UART.RX_ANY +.. data:: UART.RTS + UART.CTS + + Flow control options. + + Availability: esp32, mimxrt, renesas-ra, rp2, stm32. + +.. data:: UART.IRQ_RXIDLE - IRQ trigger sources + IRQ trigger sources. - Availability: WiPy. + Availability: stm32. From 03b1b6d8e6a968a2aec002466e516952c6683f53 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 11 Mar 2024 12:31:17 +0100 Subject: [PATCH 0259/1300] docs/library/machine.UART: Extend the documentation for UART.irq. For more ports and trigger options, based on the current state of the code. Signed-off-by: robert-hh --- docs/library/machine.UART.rst | 53 +++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/docs/library/machine.UART.rst b/docs/library/machine.UART.rst index 6f47eb9abb7c4..0f3e77ec471a9 100644 --- a/docs/library/machine.UART.rst +++ b/docs/library/machine.UART.rst @@ -188,19 +188,59 @@ Methods triggers. The handler must take exactly one argument which is the ``UART`` instance. - - *trigger* configures the event which can generate an interrupt. - Possible values are: + - *trigger* configures the event(s) which can generate an interrupt. + Possible values are a mask of one or more of the following: - ``UART.IRQ_RXIDLE`` interrupt after receiving at least one character and then the RX line goes idle. + - ``UART.IRQ_RX`` interrupt after each received character. + - ``UART.IRQ_TXIDLE`` interrupt after or while the last character(s) of + a message are or have been sent. + - ``UART.IRQ_BREAK`` interrupt when a break state is detected at RX - *hard* if true a hardware interrupt is used. This reduces the delay - between the pin change and the handler being called. Hard interrupt + between the pin change and the handler being called. Hard interrupt handlers may not allocate memory; see :ref:`isr_rules`. Returns an irq object. - Availability: renesas-ra, stm32. + Due to limitations of the hardware not all trigger events are available on all ports. + + .. table:: Availability of triggers + :align: center + + ============== ========== ====== ========== ========= + Port / Trigger IRQ_RXIDLE IRQ_RX IRQ_TXIDLE IRQ_BREAK + ============== ========== ====== ========== ========= + CC3200 yes + ESP32 yes yes yes + MIMXRT yes yes + NRF yes yes + RENESAS-RA yes yes + RP2 yes yes yes + SAMD yes yes yes + STM32 yes yes + ============== ========== ====== ========== ========= + + + .. note:: + - The ESP32 port does not support the option hard=True. + + - The rp2 port's UART.IRQ_TXIDLE is only triggered when the message + is longer than 5 characters and the trigger happens when still 5 characters + are to be sent. + + - The rp2 port's UART.IRQ_BREAK needs receiving valid characters for triggering + again. + + - The SAMD port's UART.IRQ_TXIDLE is triggered while the last character is sent. + + - On STM32F4xx MCU's, using the trigger UART.IRQ_RXIDLE the handler will be called once + after the first character and then after the end of the message, when the line is + idle. + + + Availability: cc3200, esp32, mimxrt, nrf, renesas-ra, rp2, samd, stm32. Constants --------- @@ -213,7 +253,10 @@ Constants Availability: esp32, mimxrt, renesas-ra, rp2, stm32. .. data:: UART.IRQ_RXIDLE + UART.IRQ_RX + UART.IRQ_TXIDLE + UART.IRQ_BREAK IRQ trigger sources. - Availability: stm32. + Availability: renesas-ra, stm32, esp32, rp2040, mimxrt, samd, cc3200. From b8513e6137faaed4b540b0be7756b30fa06a217a Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 16 Aug 2024 23:07:12 +1000 Subject: [PATCH 0260/1300] tests/extmod: Add test for machine.UART.IRQ_TXIDLE. The test checks whether the message created by the IRQ handler appears about at the end of the data sent by UART. Supported MCUs resp. boards: - RP2040 - Teensy 4.x - Adafruit ItsyBitsy M0 - Adafruit ItsyBitsy M4 - NRF52 (Arduino Nano Connect 33 BLE) Signed-off-by: Damien George --- tests/extmod/machine_uart_irq_txidle.py | 76 +++++++++++++++++++++ tests/extmod/machine_uart_irq_txidle.py.exp | 12 ++++ 2 files changed, 88 insertions(+) create mode 100644 tests/extmod/machine_uart_irq_txidle.py create mode 100644 tests/extmod/machine_uart_irq_txidle.py.exp diff --git a/tests/extmod/machine_uart_irq_txidle.py b/tests/extmod/machine_uart_irq_txidle.py new file mode 100644 index 0000000000000..3e69bb43141aa --- /dev/null +++ b/tests/extmod/machine_uart_irq_txidle.py @@ -0,0 +1,76 @@ +# Test machine.UART.IRQ_TXIDLE firing after a transmission. +# Does not require any external connections. + +try: + from machine import UART + + UART.IRQ_TXIDLE +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +import time, sys + +# Configure pins based on the target. +if "rp2" in sys.platform: + uart_id = 0 + tx_pin = "GPIO0" + rx_pin = "GPIO1" + min_window = 1 +elif "samd" in sys.platform and "ItsyBitsy M0" in sys.implementation._machine: + uart_id = 0 + tx_pin = "D1" + rx_pin = "D0" + # For SAMD delay_ms has to be used for the trailing window, and the + # mininmal time is 11 ms to allow for scheduling. + min_window = 11 +elif "samd" in sys.platform and "ItsyBitsy M4" in sys.implementation._machine: + uart_id = 3 + tx_pin = "D1" + rx_pin = "D0" + min_window = 11 +elif "mimxrt" in sys.platform: + uart_id = 1 + tx_pin = None + min_window = 0 +elif "nrf" in sys.platform: + uart_id = 0 + tx_pin = None + min_window = 0 +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +def irq(u): + print("IRQ_TXIDLE:", u.irq().flags() == u.IRQ_TXIDLE) + + +text = "Hello World" * 20 + +# Test that the IRQ is called after the write has completed. +for bits_per_s in (2400, 9600, 115200): + if tx_pin is None: + uart = UART(uart_id, bits_per_s) + else: + uart = UART(uart_id, bits_per_s, tx=tx_pin, rx=rx_pin) + + uart.irq(irq, uart.IRQ_TXIDLE) + + # The IRQ_TXIDLE shall trigger after the message has been sent. Thus + # the test marks a time window close to the expected of the sending + # and the time at which the IRQ should have been fired. + # It is just a rough estimation of 10 characters before and + # 20 characters after the data's end, unless there is a need to + # wait a minimal time, like for SAMD devices. + + bits_per_char = 10 # 1(startbit) + 8(bits) + 1(stopbit) + 0(parity) + start_time_us = (len(text) - 10) * bits_per_char * 1_000_000 // bits_per_s + window_ms = max(min_window, 20 * bits_per_char * 1_000 // bits_per_s + 1) + + print("write", bits_per_s) + uart.write(text) + time.sleep_us(start_time_us) + print("ready") + time.sleep_ms(window_ms) + print("done") diff --git a/tests/extmod/machine_uart_irq_txidle.py.exp b/tests/extmod/machine_uart_irq_txidle.py.exp new file mode 100644 index 0000000000000..2da51d10faada --- /dev/null +++ b/tests/extmod/machine_uart_irq_txidle.py.exp @@ -0,0 +1,12 @@ +write 2400 +ready +IRQ_TXIDLE: True +done +write 9600 +ready +IRQ_TXIDLE: True +done +write 115200 +ready +IRQ_TXIDLE: True +done From 09d070aa55891587ddbd60fa2b8f926ebb1705cd Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 16 Aug 2024 23:07:34 +1000 Subject: [PATCH 0261/1300] tests/extmod_hardware: Add tests for machine.UART.IRQ_RX/RXIDLE/BREAK. These all require hardware connections, so live in a different directory. Except for the IRQ_BREAK test of ESP32 devices a single UART with loopback is sufficient. General: SAMD21: Due to the limited flash size only SAMD21 devices with external flash support uart.irq(). IRQ_BREAK: ESP32 needs different UART devices for creating and sensing a break. Lacking a second UART the test is skipped for ESP32S2 and ESP32C3. RP2 does not pass the test reliable at 115200 baud, reason to be found. Thus the upper limit is set to 57600 Baud. Coverage: esp32 pass when different UART devices are used. rp2 pass up to 57600 baud IRQ_RX: SAMD21: Being a slow device it needs data to be sent byte-by-byte at 9600 baud, since the IRQ callback is scheduled delayed and then the flags do not match any more. The data matches since it is queued in the FIFO resp. ringbuffer. CC3200: The test cannot be performed since no calls are accepted in the IRQ handler like u.read(). Skipped. Coverage: cc3200 fail due to major differences in the implementation. esp32 pass nrf pass renesas-ra pass samd pass see the notes. stm32 pass IRQ_RXIDLE: STM32: With PyBoard the IRQ is called several times, but only once with the flag IRQ_RXIDLE set. Coverage: esp32 pass mimxrt pass renesas-ra pass rp2 pass samd pass for both SAMD21 and SAMD51 stm32 fail. see notes. Signed-off-by: Damien George Signed-off-by: robert-hh --- .../extmod_hardware/machine_uart_irq_break.py | 62 +++++++++++++++ .../machine_uart_irq_break.py.exp | 15 ++++ tests/extmod_hardware/machine_uart_irq_rx.py | 78 +++++++++++++++++++ .../machine_uart_irq_rx.py.exp | 12 +++ .../machine_uart_irq_rxidle.py | 70 +++++++++++++++++ .../machine_uart_irq_rxidle.py.exp | 12 +++ 6 files changed, 249 insertions(+) create mode 100644 tests/extmod_hardware/machine_uart_irq_break.py create mode 100644 tests/extmod_hardware/machine_uart_irq_break.py.exp create mode 100644 tests/extmod_hardware/machine_uart_irq_rx.py create mode 100644 tests/extmod_hardware/machine_uart_irq_rx.py.exp create mode 100644 tests/extmod_hardware/machine_uart_irq_rxidle.py create mode 100644 tests/extmod_hardware/machine_uart_irq_rxidle.py.exp diff --git a/tests/extmod_hardware/machine_uart_irq_break.py b/tests/extmod_hardware/machine_uart_irq_break.py new file mode 100644 index 0000000000000..82879c1d6e4a8 --- /dev/null +++ b/tests/extmod_hardware/machine_uart_irq_break.py @@ -0,0 +1,62 @@ +# Test machine.UART.IRQ_BREAK firing after a break is received. +# +# IMPORTANT: This test requires hardware connections: the UART TX and RX +# pins must be wired together. + +try: + from machine import UART + + UART.IRQ_BREAK +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +import time, sys + +# Configure pins based on the target. +if "esp32" in sys.platform: + if "ESP32S2" in sys.implementation._machine or "ESP32C3" in sys.implementation._machine: + print("SKIP") + raise SystemExit + # ESP32 needs separate UART instances for the test + recv_uart_id = 1 + recv_tx_pin = 14 + recv_rx_pin = 5 + send_uart_id = 2 + send_tx_pin = 4 + send_rx_pin = 12 +elif "rp2" in sys.platform: + recv_uart_id = 0 + send_uart_id = 0 + recv_tx_pin = "GPIO0" + recv_rx_pin = "GPIO1" +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +def irq(u): + print("IRQ_BREAK:", bool(u.irq().flags() & u.IRQ_BREAK), "data:", u.read(1)) + + +# Test that the IRQ is called for each break received. +for bits_per_s in (2400, 9600, 57600): + recv_uart = UART(recv_uart_id, bits_per_s, tx=recv_tx_pin, rx=recv_rx_pin) + if recv_uart_id != send_uart_id: + send_uart = UART(send_uart_id, bits_per_s, tx=send_tx_pin, rx=send_rx_pin) + else: + send_uart = recv_uart + + recv_uart.irq(irq, recv_uart.IRQ_BREAK) + + print("write", bits_per_s) + for i in range(3): + send_uart.write(str(i)) + send_uart.flush() + time.sleep_ms(10) + send_uart.sendbreak() + time.sleep_ms(10) + if "esp32" in sys.platform: + # On esp32 a read is needed to read in the break byte. + recv_uart.read() + print("done") diff --git a/tests/extmod_hardware/machine_uart_irq_break.py.exp b/tests/extmod_hardware/machine_uart_irq_break.py.exp new file mode 100644 index 0000000000000..ca8afe8f2b6bf --- /dev/null +++ b/tests/extmod_hardware/machine_uart_irq_break.py.exp @@ -0,0 +1,15 @@ +write 2400 +IRQ_BREAK: True data: b'0' +IRQ_BREAK: True data: b'1' +IRQ_BREAK: True data: b'2' +done +write 9600 +IRQ_BREAK: True data: b'0' +IRQ_BREAK: True data: b'1' +IRQ_BREAK: True data: b'2' +done +write 57600 +IRQ_BREAK: True data: b'0' +IRQ_BREAK: True data: b'1' +IRQ_BREAK: True data: b'2' +done diff --git a/tests/extmod_hardware/machine_uart_irq_rx.py b/tests/extmod_hardware/machine_uart_irq_rx.py new file mode 100644 index 0000000000000..bf34900bd0826 --- /dev/null +++ b/tests/extmod_hardware/machine_uart_irq_rx.py @@ -0,0 +1,78 @@ +# Test machine.UART.IRQ_RX firing for each character received. +# +# IMPORTANT: This test requires hardware connections: the UART TX and RX +# pins must be wired together. + +try: + from machine import UART + + UART.IRQ_RX +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +import time, sys + +byte_by_byte = False +# Configure pins based on the target. +if "esp32" in sys.platform: + uart_id = 1 + tx_pin = 4 + rx_pin = 5 +elif "pyboard" in sys.platform: + uart_id = 4 + tx_pin = None # PA0 + rx_pin = None # PA1 +elif "samd" in sys.platform and "ItsyBitsy M0" in sys.implementation._machine: + uart_id = 0 + tx_pin = "D1" + rx_pin = "D0" + byte_by_byte = True +elif "samd" in sys.platform and "ItsyBitsy M4" in sys.implementation._machine: + uart_id = 3 + tx_pin = "D1" + rx_pin = "D0" +elif "nrf" in sys.platform: + uart_id = 0 + tx_pin = None + rx_pin = None +elif "renesas-ra" in sys.platform: + uart_id = 9 + tx_pin = None # P602 @ RA6M2 + rx_pin = None # P601 @ RA6M2 +elif "CC3200" in sys.implementation._machine: + # CC3200 doesn't work because it's too slow and has an allocation error in the handler. + print("SKIP") + raise SystemExit +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +def irq(u): + print("IRQ_RX:", bool(u.irq().flags() & u.IRQ_RX), "data:", u.read(1)) + + +text = "1234" + +# Test that the IRQ is called for each byte received. +# Use slow baudrates so that the IRQ has time to run. +for bits_per_s in (2400, 9600): + if tx_pin is None: + uart = UART(uart_id, bits_per_s) + else: + uart = UART(uart_id, bits_per_s, tx=tx_pin, rx=rx_pin) + + uart.irq(irq, uart.IRQ_RX) + + print("write", bits_per_s) + if byte_by_byte: + # slow devices need data to be sent slow + for c in text: + uart.write(c) + uart.flush() + else: + uart.write(text) + uart.flush() + time.sleep_ms(100) + print("done") diff --git a/tests/extmod_hardware/machine_uart_irq_rx.py.exp b/tests/extmod_hardware/machine_uart_irq_rx.py.exp new file mode 100644 index 0000000000000..945b1e508fa7c --- /dev/null +++ b/tests/extmod_hardware/machine_uart_irq_rx.py.exp @@ -0,0 +1,12 @@ +write 2400 +IRQ_RX: True data: b'1' +IRQ_RX: True data: b'2' +IRQ_RX: True data: b'3' +IRQ_RX: True data: b'4' +done +write 9600 +IRQ_RX: True data: b'1' +IRQ_RX: True data: b'2' +IRQ_RX: True data: b'3' +IRQ_RX: True data: b'4' +done diff --git a/tests/extmod_hardware/machine_uart_irq_rxidle.py b/tests/extmod_hardware/machine_uart_irq_rxidle.py new file mode 100644 index 0000000000000..182ab24ebe0c4 --- /dev/null +++ b/tests/extmod_hardware/machine_uart_irq_rxidle.py @@ -0,0 +1,70 @@ +# Test machine.UART.IRQ_RXIDLE firing after a set of characters are received. +# +# IMPORTANT: This test requires hardware connections: the UART TX and RX +# pins must be wired together. + +try: + from machine import UART + + UART.IRQ_RXIDLE +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +import time, sys + +# Configure pins based on the target. +if "esp32" in sys.platform: + uart_id = 1 + tx_pin = 4 + rx_pin = 5 +elif "mimxrt" in sys.platform: + uart_id = 1 + tx_pin = None +elif "pyboard" in sys.platform: + uart_id = 4 + tx_pin = None # PA0 + rx_pin = None # PA1 +elif "renesas-ra" in sys.platform: + uart_id = 9 + tx_pin = None # P602 @ RA6M2 + rx_pin = None # P601 @ RA6M2 +elif "rp2" in sys.platform: + uart_id = 0 + tx_pin = "GPIO0" + rx_pin = "GPIO1" +elif "samd" in sys.platform and "ItsyBitsy M0" in sys.implementation._machine: + uart_id = 0 + tx_pin = "D1" + rx_pin = "D0" + byte_by_byte = True +elif "samd" in sys.platform and "ItsyBitsy M4" in sys.implementation._machine: + uart_id = 3 + tx_pin = "D1" + rx_pin = "D0" +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +def irq(u): + print("IRQ_RXIDLE:", bool(u.irq().flags() & u.IRQ_RXIDLE), "data:", u.read()) + + +text = "12345678" + +# Test that the IRQ is called for each set of byte received. +for bits_per_s in (2400, 9600, 115200): + if tx_pin is None: + uart = UART(uart_id, bits_per_s) + else: + uart = UART(uart_id, bits_per_s, tx=tx_pin, rx=rx_pin) + + uart.irq(irq, uart.IRQ_RXIDLE) + + print("write", bits_per_s) + uart.write(text) + uart.flush() + print("ready") + time.sleep_ms(100) + print("done") diff --git a/tests/extmod_hardware/machine_uart_irq_rxidle.py.exp b/tests/extmod_hardware/machine_uart_irq_rxidle.py.exp new file mode 100644 index 0000000000000..ce1890a06a4b2 --- /dev/null +++ b/tests/extmod_hardware/machine_uart_irq_rxidle.py.exp @@ -0,0 +1,12 @@ +write 2400 +ready +IRQ_RXIDLE: True data: b'12345678' +done +write 9600 +ready +IRQ_RXIDLE: True data: b'12345678' +done +write 115200 +ready +IRQ_RXIDLE: True data: b'12345678' +done From a89ac9e24a566e45bd3ffa7ef9e61e1d994616ea Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Apr 2024 16:50:26 +1000 Subject: [PATCH 0262/1300] lib/lwip: Update lwIP to STABLE-2_2_0_RELEASE. This updates lwIP from STABLE-2_1_3_RELEASE, which was released in November 2021. The latest STABLE-2_2_0_RELEASE was released in September 2023. Signed-off-by: Damien George --- lib/lwip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lwip b/lib/lwip index 6ca936f6b588c..0a0452b2c39bd 160000 --- a/lib/lwip +++ b/lib/lwip @@ -1 +1 @@ -Subproject commit 6ca936f6b588cee702c638eee75c2436e6cf75de +Subproject commit 0a0452b2c39bdd91e252aef045c115f88f6ca773 From 664dd7b54a220ba6081a8a2d28103cdd1c74b034 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Apr 2024 16:50:52 +1000 Subject: [PATCH 0263/1300] extmod: Update make and cmake scripts to work with latest lwIP. Signed-off-by: Damien George --- extmod/extmod.cmake | 3 +++ extmod/extmod.mk | 3 +++ 2 files changed, 6 insertions(+) diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index 60f1a23ad6b22..69ee0e3ae460d 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -275,11 +275,14 @@ if(MICROPY_PY_LWIP) target_sources(micropy_lib_lwip INTERFACE ${MICROPY_DIR}/shared/netutils/netutils.c ${MICROPY_LIB_LWIP_DIR}/apps/mdns/mdns.c + ${MICROPY_LIB_LWIP_DIR}/apps/mdns/mdns_domain.c + ${MICROPY_LIB_LWIP_DIR}/apps/mdns/mdns_out.c ${MICROPY_LIB_LWIP_DIR}/core/def.c ${MICROPY_LIB_LWIP_DIR}/core/dns.c ${MICROPY_LIB_LWIP_DIR}/core/inet_chksum.c ${MICROPY_LIB_LWIP_DIR}/core/init.c ${MICROPY_LIB_LWIP_DIR}/core/ip.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv4/acd.c ${MICROPY_LIB_LWIP_DIR}/core/ipv4/autoip.c ${MICROPY_LIB_LWIP_DIR}/core/ipv4/dhcp.c ${MICROPY_LIB_LWIP_DIR}/core/ipv4/etharp.c diff --git a/extmod/extmod.mk b/extmod/extmod.mk index c2298bc52130f..3bed5961f87c2 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -332,6 +332,8 @@ $(BUILD)/$(LWIP_DIR)/core/ipv4/dhcp.o: CFLAGS += -Wno-address SRC_THIRDPARTY_C += shared/netutils/netutils.c SRC_THIRDPARTY_C += $(addprefix $(LWIP_DIR)/,\ apps/mdns/mdns.c \ + apps/mdns/mdns_domain.c \ + apps/mdns/mdns_out.c \ core/def.c \ core/dns.c \ core/inet_chksum.c \ @@ -349,6 +351,7 @@ SRC_THIRDPARTY_C += $(addprefix $(LWIP_DIR)/,\ core/tcp_out.c \ core/timeouts.c \ core/udp.c \ + core/ipv4/acd.c \ core/ipv4/autoip.c \ core/ipv4/dhcp.c \ core/ipv4/etharp.c \ From bc952d37fe957adf0165b3403a97d5ada7e8e1a4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 13 Mar 2024 17:09:51 +1100 Subject: [PATCH 0264/1300] extmod/network_ppp_lwip: Add network.PPP via lwIP. This commit adds a new `network.PPP` interface which works on any port that has bare-metal lwIP, eg rp2, stm32, mimxrt. It has been tested on stm32. A board needs to enable `MICROPY_PY_NETWORK_PPP_LWIP` and then it can use it as follows: import network ppp = network.PPP(uart) ppp.connect() while not ppp.isconnected(): pass # use `socket` module as usual, etc ppp.disconnect() Usually the application must first configure the cellular/etc UART link to get it connected and in to PPP mode first (eg ATD*99#), before handing over control to `network.PPP`. The PPP interface automatically configures the UART IRQ callback to call PPP.poll() on incoming data. Signed-off-by: Damien George --- extmod/extmod.cmake | 27 ++++ extmod/extmod.mk | 27 ++++ extmod/modnetwork.c | 4 + extmod/modnetwork.h | 4 + extmod/network_ppp_lwip.c | 326 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 388 insertions(+) create mode 100644 extmod/network_ppp_lwip.c diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index 69ee0e3ae460d..98e8a84608a56 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -49,6 +49,7 @@ set(MICROPY_SOURCE_EXTMOD ${MICROPY_EXTMOD_DIR}/network_cyw43.c ${MICROPY_EXTMOD_DIR}/network_lwip.c ${MICROPY_EXTMOD_DIR}/network_ninaw10.c + ${MICROPY_EXTMOD_DIR}/network_ppp_lwip.c ${MICROPY_EXTMOD_DIR}/network_wiznet5k.c ${MICROPY_EXTMOD_DIR}/os_dupterm.c ${MICROPY_EXTMOD_DIR}/vfs.c @@ -313,6 +314,32 @@ if(MICROPY_PY_LWIP) ${MICROPY_LIB_LWIP_DIR}/core/timeouts.c ${MICROPY_LIB_LWIP_DIR}/core/udp.c ${MICROPY_LIB_LWIP_DIR}/netif/ethernet.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/auth.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/ccp.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/chap-md5.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/chap_ms.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/chap-new.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/demand.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/eap.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/ecp.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/eui64.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/fsm.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/ipcp.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/ipv6cp.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/lcp.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/magic.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/mppe.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/multilink.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/polarssl/md5.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/pppapi.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/ppp.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/pppcrypt.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/pppoe.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/pppol2tp.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/pppos.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/upap.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/utils.c + ${MICROPY_LIB_LWIP_DIR}/netif/ppp/vj.c ) list(APPEND MICROPY_INC_CORE diff --git a/extmod/extmod.mk b/extmod/extmod.mk index 3bed5961f87c2..c132fd89ce887 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -51,6 +51,7 @@ SRC_EXTMOD_C += \ extmod/network_esp_hosted.c \ extmod/network_lwip.c \ extmod/network_ninaw10.c \ + extmod/network_ppp_lwip.c \ extmod/network_wiznet5k.c \ extmod/os_dupterm.c \ extmod/vfs.c \ @@ -370,6 +371,32 @@ SRC_THIRDPARTY_C += $(addprefix $(LWIP_DIR)/,\ core/ipv6/mld6.c \ core/ipv6/nd6.c \ netif/ethernet.c \ + netif/ppp/auth.c \ + netif/ppp/ccp.c \ + netif/ppp/chap-md5.c \ + netif/ppp/chap_ms.c \ + netif/ppp/chap-new.c \ + netif/ppp/demand.c \ + netif/ppp/eap.c \ + netif/ppp/ecp.c \ + netif/ppp/eui64.c \ + netif/ppp/fsm.c \ + netif/ppp/ipcp.c \ + netif/ppp/ipv6cp.c \ + netif/ppp/lcp.c \ + netif/ppp/magic.c \ + netif/ppp/mppe.c \ + netif/ppp/multilink.c \ + netif/ppp/polarssl/md5.c \ + netif/ppp/pppapi.c \ + netif/ppp/ppp.c \ + netif/ppp/pppcrypt.c \ + netif/ppp/pppoe.c \ + netif/ppp/pppol2tp.c \ + netif/ppp/pppos.c \ + netif/ppp/upap.c \ + netif/ppp/utils.c \ + netif/ppp/vj.c \ ) ifeq ($(MICROPY_PY_LWIP_LOOPBACK),1) CFLAGS_EXTMOD += -DLWIP_NETIF_LOOPBACK=1 diff --git a/extmod/modnetwork.c b/extmod/modnetwork.c index f3d7d0faa8e1d..336836b6b8694 100644 --- a/extmod/modnetwork.c +++ b/extmod/modnetwork.c @@ -156,6 +156,10 @@ static const mp_rom_map_elem_t mp_module_network_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&mod_network_ipconfig_obj) }, #endif + #if MICROPY_PY_NETWORK_PPP_LWIP + { MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&mp_network_ppp_lwip_type) }, + #endif + // Defined per port in mpconfigport.h #ifdef MICROPY_PORT_NETWORK_INTERFACES { MP_ROM_QSTR(MP_QSTR_route), MP_ROM_PTR(&network_route_obj) }, diff --git a/extmod/modnetwork.h b/extmod/modnetwork.h index 1a4aa7797e455..7e5a283353724 100644 --- a/extmod/modnetwork.h +++ b/extmod/modnetwork.h @@ -72,6 +72,10 @@ mp_obj_t mod_network_hostname(size_t n_args, const mp_obj_t *args); #include "lwip/init.h" +#if MICROPY_PY_NETWORK_PPP_LWIP +extern const struct _mp_obj_type_t mp_network_ppp_lwip_type; +#endif + struct netif; void mod_network_lwip_init(void); void mod_network_lwip_poll_wrapper(uint32_t ticks_ms); diff --git a/extmod/network_ppp_lwip.c b/extmod/network_ppp_lwip.c new file mode 100644 index 0000000000000..2b77662a24f26 --- /dev/null +++ b/extmod/network_ppp_lwip.c @@ -0,0 +1,326 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 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/runtime.h" +#include "py/mphal.h" +#include "py/stream.h" +#include "extmod/modnetwork.h" + +#if MICROPY_PY_NETWORK_PPP_LWIP + +#include "lwip/dns.h" +#include "netif/ppp/ppp.h" +#include "netif/ppp/pppapi.h" +#include "netif/ppp/pppos.h" + +// Enable this to see the serial data going between the PPP layer. +#define PPP_TRACE_IN_OUT (0) + +typedef enum { + STATE_INACTIVE, + STATE_ACTIVE, + STATE_ERROR, + STATE_CONNECTING, + STATE_CONNECTED, +} network_ppp_state_t; + +typedef struct _network_ppp_obj_t { + mp_obj_base_t base; + network_ppp_state_t state; + int error_code; + mp_obj_t stream; + ppp_pcb *pcb; + struct netif netif; +} 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_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { + network_ppp_obj_t *self = ctx; + switch (err_code) { + case PPPERR_NONE: + self->state = STATE_CONNECTED; + break; + case PPPERR_USER: + if (self->state >= STATE_ERROR) { + // Disable UART IRQ. + mp_obj_t dest[3]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_const_none; + mp_call_method_n_kw(1, 0, dest); + // Indicate that the IRQ is disabled. + self->state = STATE_ACTIVE; + } + // Clean up the PPP PCB. + network_ppp___del__(MP_OBJ_FROM_PTR(self)); + break; + default: + self->state = STATE_ERROR; + self->error_code = err_code; + break; + } +} + +static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_obj_t stream = all_args[0]; + + mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + + network_ppp_obj_t *self = mp_obj_malloc_with_finaliser(network_ppp_obj_t, type); + self->state = STATE_INACTIVE; + self->stream = stream; + self->pcb = NULL; + + return MP_OBJ_FROM_PTR(self); +} + +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 UART stream. + // Force the connection to close, with nocarrier=1. + self->state = STATE_INACTIVE; + ppp_close(self->pcb, 1); + } + // Free PPP PCB and reset state. + self->state = STATE_INACTIVE; + ppp_free(self->pcb); + self->pcb = NULL; + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp___del___obj, network_ppp___del__); + +static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (self->state <= STATE_ERROR) { + return MP_OBJ_NEW_SMALL_INT(-MP_EPERM); + } + + mp_int_t total_len = 0; + for (;;) { + uint8_t buf[256]; + int err; + mp_uint_t len = mp_stream_rw(self->stream, buf, sizeof(buf), &err, 0); + if (len == 0) { + break; + } + #if PPP_TRACE_IN_OUT + mp_printf(&mp_plat_print, "ppp_in(n=%u,data=", len); + for (size_t i = 0; i < len; ++i) { + mp_printf(&mp_plat_print, "%02x:", buf[i]); + } + mp_printf(&mp_plat_print, ")\n"); + #endif + pppos_input(self->pcb, (u8_t *)buf, len); + total_len += len; + } + + return MP_OBJ_NEW_SMALL_INT(total_len); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_poll_obj, 1, 2, network_ppp_poll); + +static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + if (n_args != 1 && kwargs->used != 0) { + mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed")); + } + // network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (kwargs->used != 0) { + for (size_t i = 0; i < kwargs->alloc; i++) { + if (mp_map_slot_is_filled(kwargs, i)) { + switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { + default: + break; + } + } + } + return mp_const_none; + } + + if (n_args != 2) { + mp_raise_TypeError(MP_ERROR_TEXT("can query only one param")); + } + + mp_obj_t val = mp_const_none; + + switch (mp_obj_str_get_qstr(args[1])) { + default: + mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); + } + + return val; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_config_obj, 1, network_ppp_config); + +static mp_obj_t network_ppp_status(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->state == STATE_ERROR) { + return MP_OBJ_NEW_SMALL_INT(-self->error_code); + } else { + return MP_OBJ_NEW_SMALL_INT(self->state); + } +} +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_status_obj, network_ppp_status); + +static u32_t network_ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx) { + network_ppp_obj_t *self = ctx; + #if PPP_TRACE_IN_OUT + mp_printf(&mp_plat_print, "ppp_out(n=%u,data=", len); + for (size_t i = 0; i < len; ++i) { + mp_printf(&mp_plat_print, "%02x:", ((const uint8_t *)data)[i]); + } + mp_printf(&mp_plat_print, ")\n"); + #endif + int err; + // The return value from this output callback is the number of bytes written out. + // If it's less than the requested number of bytes then lwIP will propagate out an error. + return mp_stream_rw(self->stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); +} + +static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + enum { ARG_security, ARG_user, ARG_key }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PPPAUTHTYPE_NONE} }, + { MP_QSTR_user, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + + mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args); + + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (self->state == STATE_INACTIVE) { + self->pcb = pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self); + if (self->pcb == NULL) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed")); + } + self->state = STATE_ACTIVE; + + // Enable UART IRQ to call PPP.poll() when incoming data is ready. + mp_obj_t dest[4]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self)); + dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE); + mp_call_method_n_kw(2, 0, dest); + } + + if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) { + mp_raise_OSError(MP_EALREADY); + } + + switch (parsed_args[ARG_security].u_int) { + case PPPAUTHTYPE_NONE: + case PPPAUTHTYPE_PAP: + case PPPAUTHTYPE_CHAP: + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid auth")); + } + + if (parsed_args[ARG_security].u_int != PPPAUTHTYPE_NONE) { + const char *user_str = mp_obj_str_get_str(parsed_args[ARG_user].u_obj); + const char *key_str = mp_obj_str_get_str(parsed_args[ARG_key].u_obj); + ppp_set_auth(self->pcb, parsed_args[ARG_security].u_int, user_str, key_str); + } + + netif_set_default(self->pcb->netif); + ppp_set_usepeerdns(self->pcb, true); + + if (ppp_connect(self->pcb, 0) != ERR_OK) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_connect failed")); + } + + self->state = STATE_CONNECTING; + + // Do a poll in case there is data waiting on the input stream. + network_ppp_poll(1, args); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_connect_obj, 1, network_ppp_connect); + +static mp_obj_t network_ppp_disconnect(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) { + // Initiate close and wait for PPPERR_USER callback. + ppp_close(self->pcb, 0); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_disconnect_obj, network_ppp_disconnect); + +static mp_obj_t network_ppp_isconnected(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(self->state == STATE_CONNECTED); +} +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_isconnected_obj, network_ppp_isconnected); + +static mp_obj_t network_ppp_ifconfig(size_t n_args, const mp_obj_t *args) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + return mod_network_nic_ifconfig(&self->netif, n_args - 1, args + 1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_ifconfig_obj, 1, 2, network_ppp_ifconfig); + +static mp_obj_t network_ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + return mod_network_nic_ipconfig(&self->netif, n_args - 1, args + 1, kwargs); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_ipconfig_obj, 1, network_ppp_ipconfig); + +static const mp_rom_map_elem_t network_ppp_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&network_ppp___del___obj) }, + { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_ppp_config_obj) }, + { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_ppp_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&network_ppp_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&network_ppp_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_ppp_isconnected_obj) }, + { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_ppp_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_ppp_ipconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&network_ppp_poll_obj) }, + + { MP_ROM_QSTR(MP_QSTR_SEC_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) }, + { MP_ROM_QSTR(MP_QSTR_SEC_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) }, + { MP_ROM_QSTR(MP_QSTR_SEC_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) }, +}; +static MP_DEFINE_CONST_DICT(network_ppp_locals_dict, network_ppp_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_network_ppp_lwip_type, + MP_QSTR_PPP, + MP_TYPE_FLAG_NONE, + make_new, network_ppp_make_new, + locals_dict, &network_ppp_locals_dict + ); + +#endif From a5cc4d46237bf5c905bdddd0a117b8fea35b7f7d Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 28 Mar 2024 09:27:16 +1100 Subject: [PATCH 0265/1300] stm32: Integrate optional network.PPP. Can be enabled by a board by enabling `MICROPY_PY_NETWORK_PPP_LWIP`. Signed-off-by: Damien George --- ports/stm32/lwip_inc/lwipopts.h | 11 ++++++++++- ports/stm32/mpconfigport.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ports/stm32/lwip_inc/lwipopts.h b/ports/stm32/lwip_inc/lwipopts.h index f641cf515add2..b779b558c9ec7 100644 --- a/ports/stm32/lwip_inc/lwipopts.h +++ b/ports/stm32/lwip_inc/lwipopts.h @@ -1,7 +1,7 @@ #ifndef MICROPY_INCLUDED_STM32_LWIP_LWIPOPTS_H #define MICROPY_INCLUDED_STM32_LWIP_LWIPOPTS_H -#include +#include "py/mpconfig.h" // This protection is not needed, instead we execute all lwIP code at PendSV priority #define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) @@ -36,6 +36,12 @@ #define LWIP_MDNS_RESPONDER 1 #define LWIP_IGMP 1 +#if MICROPY_PY_LWIP_PPP +#define PPP_SUPPORT 1 +#define PAP_SUPPORT 1 +#define CHAP_SUPPORT 1 +#endif + #define LWIP_NUM_NETIF_CLIENT_DATA LWIP_MDNS_RESPONDER #define MEMP_NUM_UDP_PCB (4 + LWIP_MDNS_RESPONDER) #define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + LWIP_MDNS_RESPONDER) @@ -77,4 +83,7 @@ extern uint32_t rng_get(void); typedef uint32_t sys_prot_t; +// Needed for PPP. +#define sys_jiffies sys_now + #endif // MICROPY_INCLUDED_STM32_LWIP_LWIPOPTS_H diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 25fc9e11f9d68..c6ba83d1bd293 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -106,6 +106,7 @@ #define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1) #define MICROPY_PY_TIME_TIME_TIME_NS (1) #define MICROPY_PY_TIME_INCLUDEFILE "ports/stm32/modtime.c" +#define MICROPY_PY_LWIP_PPP (MICROPY_PY_NETWORK_PPP_LWIP) #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) #ifndef MICROPY_PY_MACHINE #define MICROPY_PY_MACHINE (1) From c94a3205b044fb27fa703d5c280fb02a094f12e3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 7 Aug 2024 17:47:46 +1000 Subject: [PATCH 0266/1300] stm32/machine_uart: Allow changing only the baudrate. Signed-off-by: Damien George --- ports/stm32/machine_uart.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 277ae67ef2c54..9bd4c5e012527 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -154,6 +154,12 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, { MP_QSTR_read_buf_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, // legacy }; + if (self->is_enabled && n_args == 1 && kw_args->used == 0) { + // Only change the baudrate if that's all that is given. + uart_set_baudrate(self, mp_obj_get_int(pos_args[0])); + return; + } + // parse args struct { mp_arg_val_t baudrate, bits, parity, stop, flow, timeout, timeout_char, rxbuf, read_buf_len; From d8b033776e59dad8cc32d7381e0186b7677440c0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 7 Aug 2024 17:48:13 +1000 Subject: [PATCH 0267/1300] stm32/machine_uart: Return number of bytes written even with timeout. The errcode should be cleared so the caller sees a successful write, even if it's a short write. Signed-off-by: Damien George --- ports/stm32/machine_uart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 9bd4c5e012527..6be6bcdac1a12 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -533,6 +533,7 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ if (*errcode == 0 || *errcode == MP_ETIMEDOUT) { // return number of bytes written, even if there was a timeout + *errcode = 0; return num_tx << self->char_width; } else { return MP_STREAM_ERROR; From a1a16ffd75e1cc5ca977a3d799d5c7de0da5e585 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 7 Aug 2024 17:48:33 +1000 Subject: [PATCH 0268/1300] stm32/uart: Use timeout_char even with CTS enabled. When timeout=0 (non-blocking mode) the UART should still wait for each character to go out. Otherwise non-blocking mode with CTS enabled is useless because it can only write one character at a time. Signed-off-by: Damien George --- ports/stm32/uart.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 886cf1ab340ba..855f44f0f2345 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -1069,26 +1069,20 @@ size_t uart_tx_data(machine_uart_obj_t *self, const void *src_in, size_t num_cha } uint32_t timeout; - if (self->uartx->CR3 & USART_CR3_CTSE) { - // CTS can hold off transmission for an arbitrarily long time. Apply - // the overall timeout rather than the character timeout. - timeout = self->timeout; - } else { - #if defined(STM32G4) - // With using UART FIFO, the timeout should be long enough that FIFO becomes empty. - // Since previous data transfer may be ongoing, the timeout must be multiplied - // timeout_char by FIFO size + 1. - // STM32G4 has 8 words FIFO. - timeout = (8 + 1) * self->timeout_char; - #else - // The timeout specified here is for waiting for the TX data register to - // become empty (ie between chars), as well as for the final char to be - // completely transferred. The default value for timeout_char is long - // enough for 1 char, but we need to double it to wait for the last char - // to be transferred to the data register, and then to be transmitted. - timeout = 2 * self->timeout_char; - #endif - } + #if defined(STM32G4) + // With using UART FIFO, the timeout should be long enough that FIFO becomes empty. + // Since previous data transfer may be ongoing, the timeout must be multiplied + // timeout_char by FIFO size + 1. + // STM32G4 has 8 words FIFO. + timeout = (8 + 1) * self->timeout_char; + #else + // The timeout specified here is for waiting for the TX data register to + // become empty (ie between chars), as well as for the final char to be + // completely transferred. The default value for timeout_char is long + // enough for 1 char, but we need to double it to wait for the last char + // to be transferred to the data register, and then to be transmitted. + timeout = 2 * self->timeout_char; + #endif const uint8_t *src = (const uint8_t *)src_in; size_t num_tx = 0; From aee002dd80ad8829ae247950f784b1f145fb822e Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 20 Aug 2024 15:55:07 +1000 Subject: [PATCH 0269/1300] stm32/lwip_inc: Implement LWIP_PLATFORM_DIAG macro in terms of printf. This allows enabling lwIP debugging output. For example, to enable PPP debugging add the following to `mpconfigboard.h`: #define LWIP_DEBUG 1 #define PPP_DEBUG LWIP_DBG_ON Signed-off-by: Damien George --- ports/stm32/lwip_inc/arch/cc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ports/stm32/lwip_inc/arch/cc.h b/ports/stm32/lwip_inc/arch/cc.h index fc5230ef723ea..a818d6d6a0b61 100644 --- a/ports/stm32/lwip_inc/arch/cc.h +++ b/ports/stm32/lwip_inc/arch/cc.h @@ -2,7 +2,9 @@ #define MICROPY_INCLUDED_STM32_LWIP_ARCH_CC_H #include -#define LWIP_PLATFORM_DIAG(x) +#include + +#define LWIP_PLATFORM_DIAG(x) do { printf x; } while (0) #define LWIP_PLATFORM_ASSERT(x) { assert(1); } #define LWIP_NO_CTYPE_H 1 From 851aa06461fb5b25e52329021ccaa11008c0f77a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Aug 2024 16:59:42 +1000 Subject: [PATCH 0270/1300] rp2: Integrate optional network.PPP. Can be enabled by a board by enabling `MICROPY_PY_NETWORK_PPP_LWIP`. Signed-off-by: Damien George --- ports/rp2/lwip_inc/lwipopts.h | 11 ++++++++++- ports/rp2/mpconfigport.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ports/rp2/lwip_inc/lwipopts.h b/ports/rp2/lwip_inc/lwipopts.h index 5f37ecdac2e9d..f42bea528e0a4 100644 --- a/ports/rp2/lwip_inc/lwipopts.h +++ b/ports/rp2/lwip_inc/lwipopts.h @@ -1,7 +1,7 @@ #ifndef MICROPY_INCLUDED_RP2_LWIP_LWIPOPTS_H #define MICROPY_INCLUDED_RP2_LWIP_LWIPOPTS_H -#include +#include "py/mpconfig.h" // This protection is not needed, instead protect lwIP code with flags #define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) @@ -38,6 +38,12 @@ #define LWIP_MDNS_RESPONDER 1 #define LWIP_IGMP 1 +#if MICROPY_PY_LWIP_PPP +#define PPP_SUPPORT 1 +#define PAP_SUPPORT 1 +#define CHAP_SUPPORT 1 +#endif + #define LWIP_NUM_NETIF_CLIENT_DATA LWIP_MDNS_RESPONDER #define MEMP_NUM_UDP_PCB (4 + LWIP_MDNS_RESPONDER) #define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + LWIP_MDNS_RESPONDER) @@ -57,4 +63,7 @@ extern uint32_t rosc_random_u32(void); typedef uint32_t sys_prot_t; +// Needed for PPP. +#define sys_jiffies sys_now + #endif // MICROPY_INCLUDED_RP2_LWIP_LWIPOPTS_H diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index a428721d763da..49acafe239b9d 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -155,6 +155,7 @@ #define MICROPY_VFS_LFS2 (1) #define MICROPY_VFS_FAT (1) #define MICROPY_SSL_MBEDTLS (1) +#define MICROPY_PY_LWIP_PPP (MICROPY_PY_NETWORK_PPP_LWIP) #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) // Hardware timer alarm index. Available range 0-3. From 35b6a66b0b0ea00ad6b4e83f2dcbeffc349f2b04 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Aug 2024 17:28:48 +1000 Subject: [PATCH 0271/1300] docs/library: Document the network.PPP class. Signed-off-by: Damien George --- docs/library/network.PPP.rst | 98 ++++++++++++++++++++++++++++++++++++ docs/library/network.rst | 1 + 2 files changed, 99 insertions(+) create mode 100644 docs/library/network.PPP.rst diff --git a/docs/library/network.PPP.rst b/docs/library/network.PPP.rst new file mode 100644 index 0000000000000..85f580ce540ed --- /dev/null +++ b/docs/library/network.PPP.rst @@ -0,0 +1,98 @@ +.. currentmodule:: network +.. _network.PPP: + +class PPP -- create network connections over serial PPP +======================================================= + +This class allows you to create a network connection over a serial port using +the PPP protocol. It is only available on selected ports and boards. + +Example usage:: + + import network + + ppp = network.PPP(uart) + ppp.connect() + + while not ppp.isconnected(): + pass + + print(ppp.ipconfig("addr4")) + + # use the socket module as usual, etc + + ppp.disconnect() + +Constructors +------------ + +.. class:: PPP(stream) + + Create a PPP driver object. + + Arguments are: + + - *stream* is any object that supports the stream protocol, but is most commonly a + :class:`machine.UART` instance. This stream object must have an ``irq()`` method + and an ``IRQ_RXIDLE`` constant, for use by `PPP.connect`. + +Methods +------- + +.. method:: PPP.connect(security=SEC_NONE, user=None, key=None) + + Initiate a PPP connection with the given parameters: + + - *security* is the type of security, either ``PPP.SEC_NONE``, ``PPP.SEC_PAP``, + or ``PPP.SEC_CHAP``. + - *user* is an optional user name to use with the security mode. + - *key* is an optional password to use with the security mode. + + When this method is called the underlying stream has its interrupt configured to call + `PPP.poll` via ``stream.irq(ppp.poll, stream.IRQ_RXIDLE)``. This makes sure the + stream is polled, and data passed up the PPP stack, wheverver data becomes available + on the stream. + + The connection proceeds asynchronously, in the background. + +.. method:: PPP.disconnect() + + Terminate the connection. This must be called to cleanly close the PPP connection. + +.. method:: PPP.isconnected() + + Returns ``True`` if the PPP link is connected and up. + Returns ``False`` otherwise. + +.. method:: PPP.status() + + Returns the PPP status. + +.. method:: PPP.config(config_parameters) + + Sets or gets parameters of the PPP interface. There are currently no parameter that + can be set or retrieved. + +.. method:: PPP.ipconfig('param') + PPP.ipconfig(param=value, ...) + + See `AbstractNIC.ipconfig`. + +.. method:: PPP.ifconfig([(ip, subnet, gateway, dns)]) + + See `AbstractNIC.ifconfig`. + +.. method:: PPP.poll() + + Poll the underlying stream for data, and pass it up the PPP stack. + This is called automatically if the stream is a UART with a RXIDLE interrupt, + so it's not usually necessary to call it manually. + +Constants +--------- + +.. data:: PPP.SEC_NONE + PPP.SEC_PAP + PPP.SEC_CHAP + + The type of connection security. diff --git a/docs/library/network.rst b/docs/library/network.rst index 6a436fa85e052..d05d17132dc67 100644 --- a/docs/library/network.rst +++ b/docs/library/network.rst @@ -192,6 +192,7 @@ provide a way to control networking interfaces of various kinds. network.WLANWiPy.rst network.WIZNET5K.rst network.LAN.rst + network.PPP.rst Network functions ================= From 1f5cab9edbd121e2d6338fa14bb7b2572dfe7d90 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Mon, 15 Aug 2022 14:21:47 +1000 Subject: [PATCH 0272/1300] stm32: Add option to put ISR, flash and UART code in RAM. This allows UART RX to function while flash erase/writes operations are under way, preventing lost serial data so long as it fits in the UART RX buffer. This enables (among other things) mpremote to successfully copy files to boards that use a UART REPL. Enable via the following option placed in `mpconfigboard.mk`: MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 Signed-off-by: Andrew Leech --- ports/stm32/Makefile | 18 +++++++--- ports/stm32/boards/PYBD_SF2/f722_qspi.ld | 1 - ports/stm32/boards/PYBD_SF6/f767.ld | 1 - ports/stm32/boards/STM32F769DISC/f769_qspi.ld | 1 + ports/stm32/boards/common_basic.ld | 1 + ports/stm32/boards/common_bl.ld | 1 + ports/stm32/boards/common_blifs.ld | 1 + ports/stm32/boards/common_ifs.ld | 28 ++------------- .../stm32/boards/common_isr_ram/common_isr.ld | 36 +++++++++++++++++++ .../common_isr_ram/common_isr_extratext.ld | 0 .../boards/{ => common_isr_rom}/common_isr.ld | 2 +- .../common_isr_rom/common_isr_extratext.ld | 24 +++++++++++++ ports/stm32/boards/stm32f401xd.ld | 2 +- ports/stm32/boards/stm32f401xe.ld | 2 +- ports/stm32/boards/stm32f405.ld | 2 +- ports/stm32/boards/stm32f411.ld | 2 +- ports/stm32/boards/stm32f412zx.ld | 2 +- ports/stm32/boards/stm32f413xg.ld | 2 +- ports/stm32/boards/stm32f413xh.ld | 2 +- ports/stm32/boards/stm32f427xi.ld | 2 +- ports/stm32/boards/stm32f429.ld | 2 +- ports/stm32/boards/stm32f439.ld | 2 +- ports/stm32/boards/stm32f722.ld | 2 +- ports/stm32/boards/stm32f746.ld | 2 +- ports/stm32/boards/stm32f767.ld | 2 +- ports/stm32/boards/stm32f769.ld | 2 +- ports/stm32/boards/stm32h7b3.ld | 2 +- ports/stm32/main.c | 13 ++++++- 28 files changed, 109 insertions(+), 48 deletions(-) create mode 100644 ports/stm32/boards/common_isr_ram/common_isr.ld create mode 100644 ports/stm32/boards/common_isr_ram/common_isr_extratext.ld rename ports/stm32/boards/{ => common_isr_rom}/common_isr.ld (93%) create mode 100644 ports/stm32/boards/common_isr_rom/common_isr_extratext.ld diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 497e409c66563..806added49e9a 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -143,7 +143,15 @@ CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT CFLAGS += -fsingle-precision-constant endif -LDFLAGS += -nostdlib -L $(LD_DIR) $(addprefix -T,$(LD_FILES)) -Wl,-Map=$(@:.elf=.map) -Wl,--cref +# Configure linker include dir for ram/rom isr support +ifeq ($(MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM),1) +CFLAGS += -DMICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM=1 +LD_ISR_DIR = boards/common_isr_ram +else +LD_ISR_DIR = boards/common_isr_rom +endif + +LDFLAGS += -nostdlib -L $(LD_DIR) -L $(LD_ISR_DIR) $(addprefix -T,$(LD_FILES)) -Wl,-Map=$(@:.elf=.map) -Wl,--cref LDFLAGS += -Wl,--defsym=_estack_reserve=8 LIBS += "$(shell $(CC) $(CFLAGS) -print-libgcc-file-name)" @@ -465,7 +473,8 @@ OBJ += $(GEN_PINS_SRC:.c=.o) # Don't use -O3 with this file because gcc tries to optimise memset in terms of itself. $(BUILD)/shared/libc/string0.o: COPT += -O2 -# We put several files into the first 16K section with the ISRs. +ifneq ($(MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM),1) +# If MBOOT or RAM_IFS is not used we put several files into the first 16K section with the ISRs. # If we compile these using -O0 then it won't fit. So if you really want these # to be compiled with -O0, then edit boards/common.ld (in the .isr_vector section) # and comment out the following lines. @@ -474,6 +483,7 @@ $(filter $(PY_BUILD)/../extmod/vfs_fat_%.o, $(PY_O)): COPT += -Os $(PY_BUILD)/formatfloat.o: COPT += -Os $(PY_BUILD)/parsenum.o: COPT += -Os $(PY_BUILD)/mpprint.o: COPT += -Os +endif all: $(TOP)/lib/stm32lib/README.md all_main $(BUILD)/firmware.hex @@ -557,7 +567,7 @@ TEXT0_ADDR ?= 0x08000000 ifeq ($(TEXT1_ADDR),) # No TEXT1_ADDR given so put all firmware at TEXT0_ADDR location -TEXT0_SECTIONS ?= .isr_vector .text .data .ARM +TEXT0_SECTIONS ?= .isr_vector .isr_extratext .text .data .ARM deploy-stlink: $(BUILD)/firmware.bin $(call RUN_STLINK,$^,$(TEXT0_ADDR)) @@ -574,7 +584,7 @@ $(BUILD)/firmware.dfu: $(BUILD)/firmware.bin else # TEXT0_ADDR and TEXT1_ADDR are specified so split firmware between these locations -TEXT0_SECTIONS ?= .isr_vector +TEXT0_SECTIONS ?= .isr_vector .isr_extratext TEXT1_SECTIONS ?= .text .data .ARM deploy-stlink: $(BUILD)/firmware0.bin $(BUILD)/firmware1.bin diff --git a/ports/stm32/boards/PYBD_SF2/f722_qspi.ld b/ports/stm32/boards/PYBD_SF2/f722_qspi.ld index 6ce4c74c3f34f..70d7f9cc4b8e4 100644 --- a/ports/stm32/boards/PYBD_SF2/f722_qspi.ld +++ b/ports/stm32/boards/PYBD_SF2/f722_qspi.ld @@ -19,7 +19,6 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 32K /* sectors 0,1 */ FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 480K /* sectors 2-7 */ FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 2048K /* external QSPI */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K /* DTCM+SRAM1+SRAM2 */ diff --git a/ports/stm32/boards/PYBD_SF6/f767.ld b/ports/stm32/boards/PYBD_SF6/f767.ld index 167d2c6a1e411..4262a48a9962c 100644 --- a/ports/stm32/boards/PYBD_SF6/f767.ld +++ b/ports/stm32/boards/PYBD_SF6/f767.ld @@ -17,7 +17,6 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 32K /* sector 0, 32K */ FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 2016K /* sectors 1-11 3x32K 1*128K 7*256K */ FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 2048K /* external QSPI */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 512K /* DTCM=128k, SRAM1=368K, SRAM2=16K */ diff --git a/ports/stm32/boards/STM32F769DISC/f769_qspi.ld b/ports/stm32/boards/STM32F769DISC/f769_qspi.ld index b6515b066691c..cde1ef5ca66cf 100644 --- a/ports/stm32/boards/STM32F769DISC/f769_qspi.ld +++ b/ports/stm32/boards/STM32F769DISC/f769_qspi.ld @@ -40,6 +40,7 @@ _heap_end = _sstack; ENTRY(Reset_Handler) +REGION_ALIAS("FLASH_ISR", FLASH_APP); REGION_ALIAS("FLASH_COMMON", FLASH_APP); SECTIONS diff --git a/ports/stm32/boards/common_basic.ld b/ports/stm32/boards/common_basic.ld index 9916a4c2545d9..9c2afc3edca2b 100644 --- a/ports/stm32/boards/common_basic.ld +++ b/ports/stm32/boards/common_basic.ld @@ -12,6 +12,7 @@ ENTRY(Reset_Handler) +REGION_ALIAS("FLASH_ISR", FLASH); REGION_ALIAS("FLASH_COMMON", FLASH); /* define output sections */ diff --git a/ports/stm32/boards/common_bl.ld b/ports/stm32/boards/common_bl.ld index b17fe98743139..e2320be6f9380 100644 --- a/ports/stm32/boards/common_bl.ld +++ b/ports/stm32/boards/common_bl.ld @@ -12,6 +12,7 @@ ENTRY(Reset_Handler) +REGION_ALIAS("FLASH_ISR", FLASH_APP); REGION_ALIAS("FLASH_COMMON", FLASH_APP); /* define output sections */ diff --git a/ports/stm32/boards/common_blifs.ld b/ports/stm32/boards/common_blifs.ld index 51969e1f9067c..ec6caa0a61a5c 100644 --- a/ports/stm32/boards/common_blifs.ld +++ b/ports/stm32/boards/common_blifs.ld @@ -12,6 +12,7 @@ ENTRY(Reset_Handler) +REGION_ALIAS("FLASH_ISR", FLASH_TEXT); REGION_ALIAS("FLASH_COMMON", FLASH_TEXT); /* define output sections */ diff --git a/ports/stm32/boards/common_ifs.ld b/ports/stm32/boards/common_ifs.ld index 33fa948bb7f3d..47b0190c4ec98 100644 --- a/ports/stm32/boards/common_ifs.ld +++ b/ports/stm32/boards/common_ifs.ld @@ -13,36 +13,14 @@ ENTRY(Reset_Handler) +REGION_ALIAS("FLASH_ISR", FLASH_START); REGION_ALIAS("FLASH_COMMON", FLASH_TEXT); /* define output sections */ SECTIONS { - /* The startup code goes first into FLASH */ - .isr_vector : - { - . = ALIGN(4); - KEEP(*(.isr_vector)) /* Startup code */ - - /* This first flash block is 16K and the isr vectors only take up - about 400 bytes. So we pull in a couple of object files to pad it - out. */ - - . = ALIGN(4); - - /* NOTE: If you update the list of files contained in .isr_vector, - then be sure to also update smhal/Makefile where it forcibly - builds each of these files with -Os */ - - */ff.o(.text*) - */vfs_fat_*.o(.text*) - */py/formatfloat.o(.text*) - */py/parsenum.o(.text*) - */py/mpprint.o(.text*) - - . = ALIGN(4); - } >FLASH_ISR - + INCLUDE common_isr.ld + INCLUDE common_isr_extratext.ld INCLUDE common_text.ld INCLUDE common_extratext_data_in_flash.ld INCLUDE common_bss_heap_stack.ld diff --git a/ports/stm32/boards/common_isr_ram/common_isr.ld b/ports/stm32/boards/common_isr_ram/common_isr.ld new file mode 100644 index 0000000000000..7d8f9a64c28ca --- /dev/null +++ b/ports/stm32/boards/common_isr_ram/common_isr.ld @@ -0,0 +1,36 @@ +/* This linker script fragment is intended to be included in SECTIONS. */ + +/* The startup code goes first into FLASH */ +.isr_vector : +{ + . = ALIGN(4); + __isr_vector_ram_start = .; + + KEEP(*(.isr_vector)) /* Startup code */ + + /* These functions need to run from ram to enable uart + reception during flash erase/write operations. + Defining them here ensures they're copied from + flash (in main.c) along with the isr_vector above. + */ + . = ALIGN(4); + *(.text.pendsv_kbd_intr) + *(.text.pendsv_schedule_dispatch) + *(.text.storage_systick_callback) + *(.text.SysTick_Handler) + *(.text.uart_irq_handler) + *(.text.UART*_IRQHandler) + *(.text.USART*_IRQHandler) + *(.text.FLASH_PageErase) + *(.text.FLASH_SectorErase) + *(.text.FLASH_WaitForLastOperation) + *(.text.HAL_FLASHEx_Erase) + *(.text.HAL_GetTick) + + __isr_vector_ram_end = .; + . = ALIGN(4); + +} >RAM AT >FLASH_ISR + +/* Used by the start-up code to initialise data */ +__isr_vector_flash_addr = LOADADDR(.isr_vector); diff --git a/ports/stm32/boards/common_isr_ram/common_isr_extratext.ld b/ports/stm32/boards/common_isr_ram/common_isr_extratext.ld new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ports/stm32/boards/common_isr.ld b/ports/stm32/boards/common_isr_rom/common_isr.ld similarity index 93% rename from ports/stm32/boards/common_isr.ld rename to ports/stm32/boards/common_isr_rom/common_isr.ld index 0f9b8bcaaddfa..8d20acde78fbb 100644 --- a/ports/stm32/boards/common_isr.ld +++ b/ports/stm32/boards/common_isr_rom/common_isr.ld @@ -6,4 +6,4 @@ . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); -} >FLASH_COMMON +} >FLASH_ISR diff --git a/ports/stm32/boards/common_isr_rom/common_isr_extratext.ld b/ports/stm32/boards/common_isr_rom/common_isr_extratext.ld new file mode 100644 index 0000000000000..cacd32043b70d --- /dev/null +++ b/ports/stm32/boards/common_isr_rom/common_isr_extratext.ld @@ -0,0 +1,24 @@ +/* This linker script fragment is intended to be included in SECTIONS when + common_ifs.ld is used with common_isr.ld +. */ + +.isr_extratext : +{ + . = ALIGN(4); + + /* This first flash block is 16K and the isr vectors only take up + about 400 bytes. So we pull in a couple of object files to pad it + out and save flash space in later blocks. + + NOTE: If you update the list of files contained here in .isr_extratext + then be sure to also update stm32/Makefile where it builds each of + these files with -Os to keep this section as compact as possible. + */ + */ff.o(.text*) + */vfs_fat_*.o(.text*) + */py/formatfloat.o(.text*) + */py/parsenum.o(.text*) + */py/mpprint.o(.text*) + + . = ALIGN(4); +} >FLASH_ISR diff --git a/ports/stm32/boards/stm32f401xd.ld b/ports/stm32/boards/stm32f401xd.ld index 33b2912acef72..06a62d79b33fc 100644 --- a/ports/stm32/boards/stm32f401xd.ld +++ b/ports/stm32/boards/stm32f401xd.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 384K /* entire flash */ - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 64K /* sectors 1,2,3,4: 16k+16k+16k+16k(of 64k)=64k */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 256K /* sectors 5,6 are 128K */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 80K diff --git a/ports/stm32/boards/stm32f401xe.ld b/ports/stm32/boards/stm32f401xe.ld index d783cd187542c..54994ab905f56 100644 --- a/ports/stm32/boards/stm32f401xe.ld +++ b/ports/stm32/boards/stm32f401xe.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K /* entire flash */ - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 64K /* sectors 1,2,3,4: 16k+16k+16k+16k(of 64k)=64k */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 384K /* sectors 5,6,7 are 128K */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 80K diff --git a/ports/stm32/boards/stm32f405.ld b/ports/stm32/boards/stm32f405.ld index 6658c1e99124d..792558b6371e0 100644 --- a/ports/stm32/boards/stm32f405.ld +++ b/ports/stm32/boards/stm32f405.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* entire flash */ - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 112K /* sectors 1,2,3,4 are for filesystem */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 5,6,7,8,9,10,11 */ CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K diff --git a/ports/stm32/boards/stm32f411.ld b/ports/stm32/boards/stm32f411.ld index 6e874f66c8cb6..ed78a7fe081b9 100644 --- a/ports/stm32/boards/stm32f411.ld +++ b/ports/stm32/boards/stm32f411.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K /* entire flash */ - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 64K /* sectors 1,2,3,4: 16k+16k+16k+16k(of 64k)=64k */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 384K /* sectors 5,6,7 are 128K */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 112K diff --git a/ports/stm32/boards/stm32f412zx.ld b/ports/stm32/boards/stm32f412zx.ld index c949d827a6c59..b67f1c3e25353 100644 --- a/ports/stm32/boards/stm32f412zx.ld +++ b/ports/stm32/boards/stm32f412zx.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* entire flash */ - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 64K /* sectors 1,2,3,4: 16k+16k+16k+16k(of 64k)=64k */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 5,6,7,8,9,10,11 are 128K */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 240K diff --git a/ports/stm32/boards/stm32f413xg.ld b/ports/stm32/boards/stm32f413xg.ld index cecfcaa881121..95c5ecb5e2f9c 100644 --- a/ports/stm32/boards/stm32f413xg.ld +++ b/ports/stm32/boards/stm32f413xg.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* entire flash */ - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 176K /* sectors 1,2,3 are 16K, 4 is 64K, 5 is 128K (64K used) for filesystem */ FLASH_FS2 (rx) : ORIGIN = 0x08040000, LENGTH = 64K /* sector 6 is 128K (64K used) for filesystem, Total filesystem 240K */ FLASH_TEXT (rx) : ORIGIN = 0x08060000, LENGTH = 640K /* sectors 7,8,9,10,11 are 128K*/ diff --git a/ports/stm32/boards/stm32f413xh.ld b/ports/stm32/boards/stm32f413xh.ld index 0846b5c80fc09..911f496ab342f 100644 --- a/ports/stm32/boards/stm32f413xh.ld +++ b/ports/stm32/boards/stm32f413xh.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1536K /* entire flash */ - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 176K /* sectors 1,2,3 are 16K, 4 is 64K, 5 is 128K (64K used) for filesystem */ FLASH_FS2 (rx) : ORIGIN = 0x08040000, LENGTH = 64K /* sector 6 is 128K (64K used) for filesystem, Total filesystem 240K */ FLASH_TEXT (rx) : ORIGIN = 0x08060000, LENGTH = 1152K /* sectors 7,8,9,10,11,12,13,14,15 are 128K*/ diff --git a/ports/stm32/boards/stm32f427xi.ld b/ports/stm32/boards/stm32f427xi.ld index b405841609f81..d2f6967a5251f 100644 --- a/ports/stm32/boards/stm32f427xi.ld +++ b/ports/stm32/boards/stm32f427xi.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* entire flash */ - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0, 16 KiB */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0, 16 KiB */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 112K /* sectors 1-4: 3*16K+64K */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 5-11 are 128K */ CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K diff --git a/ports/stm32/boards/stm32f429.ld b/ports/stm32/boards/stm32f429.ld index d081c190d318d..38458aff99836 100644 --- a/ports/stm32/boards/stm32f429.ld +++ b/ports/stm32/boards/stm32f429.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* entire flash */ - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0, 16 KiB */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0, 16 KiB */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 112K /* sectors 1-4: 3*16K+64K */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 5-11 are 128K */ CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K diff --git a/ports/stm32/boards/stm32f439.ld b/ports/stm32/boards/stm32f439.ld index 64195368c1b1d..551f4ed18914e 100644 --- a/ports/stm32/boards/stm32f439.ld +++ b/ports/stm32/boards/stm32f439.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* entire flash */ - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 5-11 are 128K */ FLASH_FS (rx) : ORIGIN = 0x08100000, LENGTH = 192K /* sectors 12-15 are 16K, 16 is 64K, 17 is 128K (64K used) */ FLASH_FS2 (rx) : ORIGIN = 0x08140000, LENGTH = 64K /* sector 18 is 128K (64K used) */ diff --git a/ports/stm32/boards/stm32f722.ld b/ports/stm32/boards/stm32f722.ld index 0520f2e95fb27..1e33295bc0d77 100644 --- a/ports/stm32/boards/stm32f722.ld +++ b/ports/stm32/boards/stm32f722.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0, 16K */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0, 16K */ FLASH_FS (r) : ORIGIN = 0x08004000, LENGTH = 112K /* sectors 1-4 3*16KiB 1*64KiB*/ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 384K /* sectors 5-7 3*128KiB = 384K */ DTCM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K /* Used for storage cache */ diff --git a/ports/stm32/boards/stm32f746.ld b/ports/stm32/boards/stm32f746.ld index 854b95463aa89..f6f33ed170aa6 100644 --- a/ports/stm32/boards/stm32f746.ld +++ b/ports/stm32/boards/stm32f746.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 32K /* sector 0, 32K */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 32K /* sector 0, 32K */ FLASH_FS (r) : ORIGIN = 0x08008000, LENGTH = 96K /* sectors 1, 2, 3 (32K each) */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 4-7 1*128Kib 3*256KiB = 896K */ DTCM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K /* Used for storage cache */ diff --git a/ports/stm32/boards/stm32f767.ld b/ports/stm32/boards/stm32f767.ld index 580be50dc9b42..6c027a5840726 100644 --- a/ports/stm32/boards/stm32f767.ld +++ b/ports/stm32/boards/stm32f767.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 32K /* sector 0, 32K */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 32K /* sector 0, 32K */ FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 2016K /* sectors 1-11 3x32K 1*128K 7*256K */ FLASH_FS (r) : ORIGIN = 0x08008000, LENGTH = 96K /* sectors 1, 2, 3 (32K each) */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 4-7 1*128Kib 3*256KiB = 896K */ diff --git a/ports/stm32/boards/stm32f769.ld b/ports/stm32/boards/stm32f769.ld index 9fc73c85969c9..35b1668b733d4 100644 --- a/ports/stm32/boards/stm32f769.ld +++ b/ports/stm32/boards/stm32f769.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 32K /* sector 0, 32K */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 32K /* sector 0, 32K */ FLASH_FS (r) : ORIGIN = 0x08008000, LENGTH = 96K /* sectors 1, 2, 3 (32K each) */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 4-7 1*128Kib 3*256KiB = 896K */ DTCM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K /* Used for storage cache */ diff --git a/ports/stm32/boards/stm32h7b3.ld b/ports/stm32/boards/stm32h7b3.ld index 83a261029fb8b..d9404f3d3d517 100644 --- a/ports/stm32/boards/stm32h7b3.ld +++ b/ports/stm32/boards/stm32h7b3.ld @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* sector 0, 128K */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 128K /* sector 0, 128K */ FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* sector 1, 128K */ FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1792K /* sectors 6*128 + 8*128 */ DTCM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K /* Used for storage cache */ diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 90a62c024427e..df483602dc901 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -303,10 +303,21 @@ void stm32_main(uint32_t reset_mode) { // Low-level MCU initialisation. stm32_system_init(); - #if !defined(STM32F0) && defined(MICROPY_HW_VTOR) + #if !defined(STM32F0) + #if MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM + // Copy IRQ vector table to RAM and point VTOR there + extern uint32_t __isr_vector_flash_addr, __isr_vector_ram_start, __isr_vector_ram_end; + size_t __isr_vector_size = (&__isr_vector_ram_end - &__isr_vector_ram_start) * sizeof(uint32_t); + memcpy(&__isr_vector_ram_start, &__isr_vector_flash_addr, __isr_vector_size); + SCB->VTOR = (uint32_t)&__isr_vector_ram_start; + #else + #if defined(MICROPY_HW_VTOR) // Change IRQ vector table if configured differently SCB->VTOR = MICROPY_HW_VTOR; #endif + #endif + #endif + #if __CORTEX_M != 33 // Enable 8-byte stack alignment for IRQ handlers, in accord with EABI From 9670666623f8cb3df9378aa12329ff41724041fb Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Fri, 23 Aug 2024 12:09:33 +1000 Subject: [PATCH 0273/1300] stm32/boards: Enable RAM_ISR feature on boards with UART REPL. Allows mpremote file transfer to work correctly when mpremote is used over the ST-link USB/UART REPL port. Fixes issue #8386. Signed-off-by: Andrew Leech --- ports/stm32/boards/B_L475E_IOT01A/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_F401RE/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_F411RE/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_F412ZG/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_F413ZH/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_F722ZE/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_F756ZG/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_L152RE/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_L452RE/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_L476RG/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_L4A6ZG/mpconfigboard.mk | 2 ++ ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk | 1 + ports/stm32/boards/OLIMEX_H407/mpconfigboard.mk | 2 ++ ports/stm32/boards/STM32F429DISC/mpconfigboard.mk | 2 ++ ports/stm32/boards/STM32F769DISC/f769_qspi.ld | 7 +++++++ ports/stm32/boards/STM32F7DISC/mpconfigboard.mk | 1 + ports/stm32/boards/STM32L496GDISC/mpconfigboard.mk | 2 ++ 30 files changed, 52 insertions(+) diff --git a/ports/stm32/boards/B_L475E_IOT01A/mpconfigboard.mk b/ports/stm32/boards/B_L475E_IOT01A/mpconfigboard.mk index 137b6be23d01a..2649b09682015 100644 --- a/ports/stm32/boards/B_L475E_IOT01A/mpconfigboard.mk +++ b/ports/stm32/boards/B_L475E_IOT01A/mpconfigboard.mk @@ -5,3 +5,5 @@ CMSIS_MCU = STM32L475xx AF_FILE = boards/stm32l476_af.csv LD_FILES = boards/stm32l476xg.ld boards/common_basic.ld OPENOCD_CONFIG = boards/openocd_stm32l4.cfg + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.mk index bb7142d1b3e20..193ed793f6fc0 100644 --- a/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.mk @@ -6,6 +6,7 @@ LD_FILES = boards/stm32f091xc.ld boards/common_basic.ld # MicroPython settings MICROPY_VFS_FAT = 0 MICROPY_VFS_LFS1 ?= 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 # Don't include default frozen modules because MCU is tight on flash space FROZEN_MANIFEST ?= diff --git a/ports/stm32/boards/NUCLEO_F401RE/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F401RE/mpconfigboard.mk index 4c3022f544357..a9985df302583 100644 --- a/ports/stm32/boards/NUCLEO_F401RE/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F401RE/mpconfigboard.mk @@ -4,3 +4,5 @@ AF_FILE = boards/stm32f401_af.csv LD_FILES = boards/stm32f401xe.ld boards/common_ifs.ld TEXT0_ADDR = 0x08000000 TEXT1_ADDR = 0x08020000 + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_F411RE/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F411RE/mpconfigboard.mk index df9506522574f..d17217e6392d9 100644 --- a/ports/stm32/boards/NUCLEO_F411RE/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F411RE/mpconfigboard.mk @@ -4,3 +4,5 @@ AF_FILE = boards/stm32f411_af.csv LD_FILES = boards/stm32f411.ld boards/common_ifs.ld TEXT0_ADDR = 0x08000000 TEXT1_ADDR = 0x08020000 + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_F412ZG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F412ZG/mpconfigboard.mk index dd671a9f90142..2fbb4ddbe2dd9 100644 --- a/ports/stm32/boards/NUCLEO_F412ZG/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F412ZG/mpconfigboard.mk @@ -4,3 +4,5 @@ AF_FILE = boards/stm32f412_af.csv LD_FILES = boards/stm32f412zx.ld boards/common_ifs.ld TEXT0_ADDR = 0x08000000 TEXT1_ADDR = 0x08020000 + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_F413ZH/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F413ZH/mpconfigboard.mk index 7d8ea6bf6bbb0..b1cbee9a62d2b 100644 --- a/ports/stm32/boards/NUCLEO_F413ZH/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F413ZH/mpconfigboard.mk @@ -4,3 +4,5 @@ AF_FILE = boards/stm32f413_af.csv LD_FILES = boards/stm32f413xh.ld boards/common_ifs.ld TEXT0_ADDR = 0x08000000 TEXT1_ADDR = 0x08060000 + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.mk index e5fc89ab47aa0..f691ad2ffc6ed 100644 --- a/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.mk @@ -9,5 +9,6 @@ TEXT1_ADDR = 0x08020000 MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.mk index b8666755b0345..8a45794f0a3ea 100644 --- a/ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.mk @@ -9,5 +9,6 @@ TEXT1_ADDR = 0x08020000 MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.mk index 3a922acebfbd7..6567f829a3458 100644 --- a/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.mk @@ -4,3 +4,5 @@ AF_FILE = boards/stm32f446_af.csv LD_FILES = boards/stm32f411.ld boards/common_ifs.ld TEXT0_ADDR = 0x08000000 TEXT1_ADDR = 0x08020000 + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_F722ZE/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F722ZE/mpconfigboard.mk index 667c8e55da98c..1d32f0b979efc 100644 --- a/ports/stm32/boards/NUCLEO_F722ZE/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F722ZE/mpconfigboard.mk @@ -4,3 +4,5 @@ AF_FILE = boards/stm32f722_af.csv LD_FILES = boards/stm32f722.ld boards/common_ifs.ld TEXT0_ADDR = 0x08000000 TEXT1_ADDR = 0x08020000 + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk index 20acc63f160e2..81c6b46295800 100644 --- a/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk @@ -9,5 +9,6 @@ TEXT1_ADDR = 0x08020000 MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/NUCLEO_F756ZG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F756ZG/mpconfigboard.mk index ab3eada5b0676..979df77e94375 100644 --- a/ports/stm32/boards/NUCLEO_F756ZG/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F756ZG/mpconfigboard.mk @@ -9,5 +9,6 @@ TEXT1_ADDR = 0x08020000 MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.mk index 22c40981591c9..693429241a59d 100644 --- a/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.mk @@ -10,5 +10,6 @@ TEXT1_ADDR = 0x08020000 MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.mk index abc9b43ef0364..f1c1357108317 100644 --- a/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.mk @@ -11,3 +11,5 @@ endif # LTO reduces final binary size, may be slower to build depending on gcc version and hardware LTO ?= 1 + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.mk index 24a06e08ec436..a72c4e03d3e0e 100644 --- a/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.mk @@ -4,3 +4,5 @@ CMSIS_MCU = STM32G474xx MICROPY_FLOAT_IMPL = single AF_FILE = boards/stm32g474_af.csv LD_FILES = boards/stm32g474.ld boards/common_basic.ld + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.mk index a55a54d1c76d1..e2ce5e3333025 100644 --- a/ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.mk @@ -21,3 +21,4 @@ endif MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk index 6d512ec0eda27..b05da481f65ee 100644 --- a/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk @@ -21,5 +21,6 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 MICROPY_VFS_LFS2 = 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk index cbdf48c52a86d..35e62c470ac5e 100644 --- a/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk @@ -21,5 +21,6 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 MICROPY_VFS_LFS2 = 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/NUCLEO_L152RE/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_L152RE/mpconfigboard.mk index a62a775ac8ddb..c6dfeb85fd966 100644 --- a/ports/stm32/boards/NUCLEO_L152RE/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_L152RE/mpconfigboard.mk @@ -2,3 +2,5 @@ MCU_SERIES = l1 CMSIS_MCU = STM32L152xE AF_FILE = boards/stm32l152_af.csv LD_FILES = boards/stm32l152xe.ld boards/common_basic.ld + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.mk index c3fff81002517..8e816b395b0fe 100644 --- a/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.mk @@ -7,6 +7,7 @@ OPENOCD_CONFIG = boards/openocd_stm32l4.cfg # MicroPython settings MICROPY_VFS_FAT = 0 MICROPY_VFS_LFS1 ?= 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 # Don't include default frozen modules because MCU is tight on flash space FROZEN_MANIFEST ?= diff --git a/ports/stm32/boards/NUCLEO_L452RE/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_L452RE/mpconfigboard.mk index 25ccb45a9427e..3bfeda761fcf8 100644 --- a/ports/stm32/boards/NUCLEO_L452RE/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_L452RE/mpconfigboard.mk @@ -3,3 +3,5 @@ CMSIS_MCU = STM32L452xx AF_FILE = boards/stm32l452_af.csv LD_FILES = boards/stm32l452xe.ld boards/common_basic.ld OPENOCD_CONFIG = boards/openocd_stm32l4.cfg + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_L476RG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_L476RG/mpconfigboard.mk index 10c69461c9309..2a7a6f9ad10c5 100644 --- a/ports/stm32/boards/NUCLEO_L476RG/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_L476RG/mpconfigboard.mk @@ -3,3 +3,5 @@ CMSIS_MCU = STM32L476xx AF_FILE = boards/stm32l476_af.csv LD_FILES = boards/stm32l476xg.ld boards/common_basic.ld OPENOCD_CONFIG = boards/openocd_stm32l4.cfg + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_L4A6ZG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_L4A6ZG/mpconfigboard.mk index 51caeaa3a1f9e..fddf0a6c352e1 100644 --- a/ports/stm32/boards/NUCLEO_L4A6ZG/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_L4A6ZG/mpconfigboard.mk @@ -3,3 +3,5 @@ CMSIS_MCU = STM32L4A6xx AF_FILE = boards/stm32l496_af.csv LD_FILES = boards/stm32l496xg.ld boards/common_basic.ld OPENOCD_CONFIG = boards/openocd_stm32l4.cfg + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk index 2e6ce1fe8f77e..4beaf9cf6cbc4 100644 --- a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk @@ -17,3 +17,4 @@ endif MICROPY_PY_BLUETOOTH = 1 MICROPY_BLUETOOTH_NIMBLE = 1 MICROPY_VFS_LFS2 = 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk index ced2e7619b954..f137fccebb80c 100644 --- a/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk @@ -8,6 +8,7 @@ TEXT0_ADDR = 0x08000000 # MicroPython settings MICROPY_VFS_FAT = 0 MICROPY_VFS_LFS2 = 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 # Board-specific manifest (doesn't include default modules, adds LoRa driver). FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/OLIMEX_H407/mpconfigboard.mk b/ports/stm32/boards/OLIMEX_H407/mpconfigboard.mk index b154dcfbacdc4..5c74443c4c419 100644 --- a/ports/stm32/boards/OLIMEX_H407/mpconfigboard.mk +++ b/ports/stm32/boards/OLIMEX_H407/mpconfigboard.mk @@ -4,3 +4,5 @@ AF_FILE = boards/stm32f405_af.csv LD_FILES = boards/stm32f405.ld boards/common_ifs.ld TEXT0_ADDR = 0x08000000 TEXT1_ADDR = 0x08020000 + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/STM32F429DISC/mpconfigboard.mk b/ports/stm32/boards/STM32F429DISC/mpconfigboard.mk index d19a35c316293..0cff5fd2ada69 100644 --- a/ports/stm32/boards/STM32F429DISC/mpconfigboard.mk +++ b/ports/stm32/boards/STM32F429DISC/mpconfigboard.mk @@ -4,3 +4,5 @@ AF_FILE = boards/stm32f429_af.csv LD_FILES = boards/stm32f429.ld boards/common_ifs.ld TEXT0_ADDR = 0x08000000 TEXT1_ADDR = 0x08020000 + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 diff --git a/ports/stm32/boards/STM32F769DISC/f769_qspi.ld b/ports/stm32/boards/STM32F769DISC/f769_qspi.ld index cde1ef5ca66cf..817422bee022e 100644 --- a/ports/stm32/boards/STM32F769DISC/f769_qspi.ld +++ b/ports/stm32/boards/STM32F769DISC/f769_qspi.ld @@ -17,6 +17,7 @@ MEMORY { + FLASH_FS (r) : ORIGIN = 0x08008000, LENGTH = 96K /* sectors 1, 2, 3 (32K each) */ FLASH_APP (rx) : ORIGIN = 0x08020000, LENGTH = 1920K /* sectors 4-11 1*128K 7*256K */ FLASH_QSPI (rx) : ORIGIN = 0x90000000, LENGTH = 64M /* external QSPI flash in XIP mode */ DTCM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K /* Used for storage cache */ @@ -38,6 +39,12 @@ _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(DTCM); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + ENTRY(Reset_Handler) REGION_ALIAS("FLASH_ISR", FLASH_APP); diff --git a/ports/stm32/boards/STM32F7DISC/mpconfigboard.mk b/ports/stm32/boards/STM32F7DISC/mpconfigboard.mk index 20acc63f160e2..81c6b46295800 100644 --- a/ports/stm32/boards/STM32F7DISC/mpconfigboard.mk +++ b/ports/stm32/boards/STM32F7DISC/mpconfigboard.mk @@ -9,5 +9,6 @@ TEXT1_ADDR = 0x08020000 MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/STM32L496GDISC/mpconfigboard.mk b/ports/stm32/boards/STM32L496GDISC/mpconfigboard.mk index a85635e306d91..b64229418f9af 100644 --- a/ports/stm32/boards/STM32L496GDISC/mpconfigboard.mk +++ b/ports/stm32/boards/STM32L496GDISC/mpconfigboard.mk @@ -3,3 +3,5 @@ CMSIS_MCU = STM32L496xx AF_FILE = boards/stm32l496_af.csv LD_FILES = boards/stm32l496xg.ld boards/common_basic.ld OPENOCD_CONFIG = boards/openocd_stm32l4.cfg + +MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 From 80c5e76483bd8e4d6730d04eabf1ee480ed36362 Mon Sep 17 00:00:00 2001 From: Amirreza Hamzavi Date: Sun, 28 Apr 2024 15:56:57 +0330 Subject: [PATCH 0274/1300] py/objint: Make byteorder argument optional in int.to_bytes() method. This was made optional in CPython 3.11. Signed-off-by: Amirreza Hamzavi --- py/objint.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/py/objint.c b/py/objint.c index 467a4714ef197..14de8bfee8ad2 100644 --- a/py/objint.c +++ b/py/objint.c @@ -422,14 +422,13 @@ static MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, MP_ROM_PTR(&int_from_ static mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { // TODO: Support signed (currently behaves as if signed=(val < 0)) - (void)n_args; bool overflow; mp_int_t dlen = mp_obj_get_int(args[1]); if (dlen < 0) { mp_raise_ValueError(NULL); } - bool big_endian = args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little); + bool big_endian = n_args < 3 || args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little); vstr_t vstr; vstr_init_len(&vstr, dlen); @@ -469,7 +468,7 @@ static mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { return mp_obj_new_bytes_from_vstr(&vstr); } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 3, 4, int_to_bytes); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 2, 4, int_to_bytes); static const mp_rom_map_elem_t int_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_from_bytes), MP_ROM_PTR(&int_from_bytes_obj) }, From 0b432b330688647d2c6a5ecaef5e1f5ab703595d Mon Sep 17 00:00:00 2001 From: Amirreza Hamzavi Date: Tue, 30 Apr 2024 19:10:25 +0330 Subject: [PATCH 0275/1300] py/objint: Make length argument optional in int.to_bytes() method. This was made optional in CPython 3.11. Signed-off-by: Amirreza Hamzavi --- py/objint.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/objint.c b/py/objint.c index 14de8bfee8ad2..a6aed8b8a40eb 100644 --- a/py/objint.c +++ b/py/objint.c @@ -424,7 +424,7 @@ static mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { // TODO: Support signed (currently behaves as if signed=(val < 0)) bool overflow; - mp_int_t dlen = mp_obj_get_int(args[1]); + mp_int_t dlen = n_args < 2 ? 1 : mp_obj_get_int(args[1]); if (dlen < 0) { mp_raise_ValueError(NULL); } @@ -468,7 +468,7 @@ static mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { return mp_obj_new_bytes_from_vstr(&vstr); } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 2, 4, int_to_bytes); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 1, 4, int_to_bytes); static const mp_rom_map_elem_t int_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_from_bytes), MP_ROM_PTR(&int_from_bytes_obj) }, From cb7e99098ee14209db264fd241776e25da6b4c7a Mon Sep 17 00:00:00 2001 From: Amirreza Hamzavi Date: Sun, 28 Apr 2024 16:46:58 +0330 Subject: [PATCH 0276/1300] py/objint: Make byteorder argument optional in int.from_bytes() method. This was made optional in CPython 3.11. Signed-off-by: Amirreza Hamzavi --- py/objint.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/py/objint.c b/py/objint.c index a6aed8b8a40eb..773e180343aeb 100644 --- a/py/objint.c +++ b/py/objint.c @@ -390,7 +390,6 @@ mp_obj_t mp_obj_int_binary_op_extra_cases(mp_binary_op_t op, mp_obj_t lhs_in, mp // this is a classmethod static mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) { // TODO: Support signed param (assumes signed=False at the moment) - (void)n_args; // get the buffer info mp_buffer_info_t bufinfo; @@ -398,7 +397,8 @@ static mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) { const byte *buf = (const byte *)bufinfo.buf; int delta = 1; - if (args[2] == MP_OBJ_NEW_QSTR(MP_QSTR_little)) { + bool big_endian = n_args < 3 || args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little); + if (!big_endian) { buf += bufinfo.len - 1; delta = -1; } @@ -409,7 +409,7 @@ static mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) { #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE if (value > (MP_SMALL_INT_MAX >> 8)) { // Result will overflow a small-int so construct a big-int - return mp_obj_int_from_bytes_impl(args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little), bufinfo.len, bufinfo.buf); + return mp_obj_int_from_bytes_impl(big_endian, bufinfo.len, bufinfo.buf); } #endif value = (value << 8) | *buf; @@ -417,7 +417,7 @@ static mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) { return mp_obj_new_int_from_uint(value); } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_from_bytes_fun_obj, 3, 4, int_from_bytes); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_from_bytes_fun_obj, 2, 4, int_from_bytes); static MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, MP_ROM_PTR(&int_from_bytes_fun_obj)); static mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { From 1897fe6227a578d351494e3db23b732b1c84584c Mon Sep 17 00:00:00 2001 From: Amirreza Hamzavi Date: Tue, 30 Apr 2024 19:32:26 +0330 Subject: [PATCH 0277/1300] tests/basics: Add tests for optional args to int.to_bytes/from_bytes. Signed-off-by: Amirreza Hamzavi --- tests/basics/int_bytes_optional_args_cp311.py | 9 +++++++++ tests/basics/int_bytes_optional_args_cp311.py.exp | 6 ++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/basics/int_bytes_optional_args_cp311.py create mode 100644 tests/basics/int_bytes_optional_args_cp311.py.exp diff --git a/tests/basics/int_bytes_optional_args_cp311.py b/tests/basics/int_bytes_optional_args_cp311.py new file mode 100644 index 0000000000000..07fdcdd282110 --- /dev/null +++ b/tests/basics/int_bytes_optional_args_cp311.py @@ -0,0 +1,9 @@ +# Check optional byteorder argument (CPython 3.11+) +print((10).to_bytes(1)) +print((100).to_bytes(10)) +print(int.from_bytes(b"\0\0\0\0\0\0\0\0\0\x01")) +print(int.from_bytes(b"\x01\0")) + +# Check optional length argument (CPython 3.11+) +print((10).to_bytes()) +print((100).to_bytes()) diff --git a/tests/basics/int_bytes_optional_args_cp311.py.exp b/tests/basics/int_bytes_optional_args_cp311.py.exp new file mode 100644 index 0000000000000..6dffa2577fc0b --- /dev/null +++ b/tests/basics/int_bytes_optional_args_cp311.py.exp @@ -0,0 +1,6 @@ +b'\n' +b'\x00\x00\x00\x00\x00\x00\x00\x00\x00d' +1 +256 +b'\n' +b'd' From 6ad6297ef7524ad74ad7618e666dd35fc5e92cfe Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 27 Aug 2024 15:49:43 +1000 Subject: [PATCH 0278/1300] esp32: Fix ESP32-C3 USB serial/jtag peripheral pre-IDF 5.1. Regression in 0a11832cd in IDF 5.0.x where macro CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED is not defined. With this patch, ESP32-S3 still USB Serial/JTAG incorrectly (now on all ESP-IDF versions). Closes #15701 This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/machine_pin.c | 2 +- ports/esp32/machine_pin.h | 2 +- ports/esp32/main.c | 2 +- ports/esp32/mpconfigport.h | 5 +++++ ports/esp32/mphalport.c | 6 +++--- ports/esp32/uart.h | 2 +- ports/esp32/usb.c | 4 ++-- ports/esp32/usb_serial_jtag.c | 4 ++-- 8 files changed, 16 insertions(+), 11 deletions(-) diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index 17c01fc7b38e6..149715cf60138 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -159,7 +159,7 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ } } - #if CONFIG_IDF_TARGET_ESP32C3 && CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED + #if CONFIG_IDF_TARGET_ESP32C3 && MICROPY_HW_ESP_USB_SERIAL_JTAG if (index == 18 || index == 19) { CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE); } diff --git a/ports/esp32/machine_pin.h b/ports/esp32/machine_pin.h index b46998725326c..1d64e7f413e4f 100644 --- a/ports/esp32/machine_pin.h +++ b/ports/esp32/machine_pin.h @@ -87,7 +87,7 @@ #define MICROPY_HW_ENABLE_GPIO11 (1) #define MICROPY_HW_ENABLE_GPIO12 (1) #define MICROPY_HW_ENABLE_GPIO13 (1) -#if !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED +#if !MICROPY_HW_ESP_USB_SERIAL_JTAG #define MICROPY_HW_ENABLE_GPIO18 (1) #define MICROPY_HW_ENABLE_GPIO19 (1) #endif diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 330ba64b467c7..dedc5421e34e0 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -99,7 +99,7 @@ void mp_task(void *pvParameter) { #if MICROPY_PY_THREAD mp_thread_init(pxTaskGetStackStart(NULL), MICROPY_TASK_STACK_SIZE / sizeof(uintptr_t)); #endif - #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED + #if MICROPY_HW_ESP_USB_SERIAL_JTAG usb_serial_jtag_init(); #elif CONFIG_USB_OTG_SUPPORTED usb_init(); diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index a5c6b9c014492..25fa4706214cf 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -260,6 +260,11 @@ typedef long mp_off_t; // board specifics #define MICROPY_PY_SYS_PLATFORM "esp32" +// Enable stdio over USB Serial/JTAG peripheral +#ifndef MICROPY_HW_ESP_USB_SERIAL_JTAG +#define MICROPY_HW_ESP_USB_SERIAL_JTAG (SOC_USB_SERIAL_JTAG_SUPPORTED) +#endif + // ESP32-S3 extended IO for 47 & 48 #ifndef MICROPY_HW_ESP32S3_EXTENDED_IO #define MICROPY_HW_ESP32S3_EXTENDED_IO (1) diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index cc0e2ee7b4999..e85aa6260c068 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -104,7 +104,7 @@ void check_esp_err_(esp_err_t code, const char *func, const int line, const char uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { uintptr_t ret = 0; - #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED + #if MICROPY_HW_ESP_USB_SERIAL_JTAG usb_serial_jtag_poll_rx(); #endif if ((poll_flags & MP_STREAM_POLL_RD) && stdin_ringbuf.iget != stdin_ringbuf.iput) { @@ -118,7 +118,7 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { int mp_hal_stdin_rx_chr(void) { for (;;) { - #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED + #if MICROPY_HW_ESP_USB_SERIAL_JTAG usb_serial_jtag_poll_rx(); #endif int c = ringbuf_get(&stdin_ringbuf); @@ -143,7 +143,7 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { if (release_gil) { MP_THREAD_GIL_EXIT(); } - #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED + #if MICROPY_HW_ESP_USB_SERIAL_JTAG usb_serial_jtag_tx_strn(str, len); did_write = true; #elif CONFIG_USB_OTG_SUPPORTED diff --git a/ports/esp32/uart.h b/ports/esp32/uart.h index 13c5e88307287..0a0985d1b4e28 100644 --- a/ports/esp32/uart.h +++ b/ports/esp32/uart.h @@ -30,7 +30,7 @@ // Whether to enable the REPL on a UART. #ifndef MICROPY_HW_ENABLE_UART_REPL -#define MICROPY_HW_ENABLE_UART_REPL (!CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED) +#define MICROPY_HW_ENABLE_UART_REPL (!CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !MICROPY_HW_ESP_USB_SERIAL_JTAG) #endif #if MICROPY_HW_ENABLE_UART_REPL diff --git a/ports/esp32/usb.c b/ports/esp32/usb.c index 5a0e6b8a92091..471f2c2e0e460 100644 --- a/ports/esp32/usb.c +++ b/ports/esp32/usb.c @@ -28,7 +28,7 @@ #include "py/mphal.h" #include "usb.h" -#if CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED +#if CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !MICROPY_HW_ESP_USB_SERIAL_JTAG #include "esp_timer.h" #ifndef NO_QSTR @@ -100,4 +100,4 @@ void usb_tx_strn(const char *str, size_t len) { } } -#endif // CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED +#endif // CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !MICROPY_HW_ESP_USB_SERIAL_JTAG diff --git a/ports/esp32/usb_serial_jtag.c b/ports/esp32/usb_serial_jtag.c index f1148ab197ebb..32eb806e72959 100644 --- a/ports/esp32/usb_serial_jtag.c +++ b/ports/esp32/usb_serial_jtag.c @@ -28,7 +28,7 @@ #include "py/mphal.h" #include "usb_serial_jtag.h" -#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED +#if MICROPY_HW_ESP_USB_SERIAL_JTAG #include "hal/usb_serial_jtag_ll.h" #include "esp_intr_alloc.h" @@ -117,4 +117,4 @@ void usb_serial_jtag_tx_strn(const char *str, size_t len) { } } -#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED +#endif // MICROPY_HW_ESP_USB_SERIAL_JTAG From 5e692d0460a2af8225534bf7c70f56007641e96e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 27 Aug 2024 16:11:32 +1000 Subject: [PATCH 0279/1300] esp32: Add MICROPY_HW_USB_CDC macro for native USB-CDC serial. This fixes issue of ESP32-S3 switching its config over to USB serial/JTAG instead of native USB. The the existing logic was hard to follow, adding this config macro makes it easier to see which USB is in use and to have board definitions that enable/disable different USB levels. This commit also drops (nominal) support for manually setting CONFIG_ESP_CONSOLE_USB_CDC in sdkconfig. No included board configs use this and it didn't seem to work (if secondary console was set to the default USB Serial/JTAG then there is no serial output on any port, and if secondary console was set to None then linking fails.) Can be re-added if there's a use case for it. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/main.c | 2 +- ports/esp32/mpconfigport.h | 11 ++++++++++- ports/esp32/mphalport.c | 2 +- ports/esp32/uart.h | 2 +- ports/esp32/usb.c | 4 ++-- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index dedc5421e34e0..ca5a0e3c2097c 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -101,7 +101,7 @@ void mp_task(void *pvParameter) { #endif #if MICROPY_HW_ESP_USB_SERIAL_JTAG usb_serial_jtag_init(); - #elif CONFIG_USB_OTG_SUPPORTED + #elif MICROPY_HW_USB_CDC usb_init(); #endif #if MICROPY_HW_ENABLE_UART_REPL diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 25fa4706214cf..747c55e92dcb7 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -260,9 +260,18 @@ typedef long mp_off_t; // board specifics #define MICROPY_PY_SYS_PLATFORM "esp32" +// Enable stdio over native USB peripheral CDC via TinyUSB +#ifndef MICROPY_HW_USB_CDC +#define MICROPY_HW_USB_CDC (SOC_USB_OTG_SUPPORTED) +#endif + // Enable stdio over USB Serial/JTAG peripheral #ifndef MICROPY_HW_ESP_USB_SERIAL_JTAG -#define MICROPY_HW_ESP_USB_SERIAL_JTAG (SOC_USB_SERIAL_JTAG_SUPPORTED) +#define MICROPY_HW_ESP_USB_SERIAL_JTAG (SOC_USB_SERIAL_JTAG_SUPPORTED && !MICROPY_HW_USB_CDC) +#endif + +#if MICROPY_HW_USB_CDC && MICROPY_HW_ESP_USB_SERIAL_JTAG +#error "Invalid build config: Can't enable both native USB and USB Serial/JTAG peripheral" #endif // ESP32-S3 extended IO for 47 & 48 diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index e85aa6260c068..7d0154cc05315 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -146,7 +146,7 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { #if MICROPY_HW_ESP_USB_SERIAL_JTAG usb_serial_jtag_tx_strn(str, len); did_write = true; - #elif CONFIG_USB_OTG_SUPPORTED + #elif MICROPY_HW_USB_CDC usb_tx_strn(str, len); did_write = true; #endif diff --git a/ports/esp32/uart.h b/ports/esp32/uart.h index 0a0985d1b4e28..fe08e26899aaa 100644 --- a/ports/esp32/uart.h +++ b/ports/esp32/uart.h @@ -30,7 +30,7 @@ // Whether to enable the REPL on a UART. #ifndef MICROPY_HW_ENABLE_UART_REPL -#define MICROPY_HW_ENABLE_UART_REPL (!CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !MICROPY_HW_ESP_USB_SERIAL_JTAG) +#define MICROPY_HW_ENABLE_UART_REPL (!MICROPY_HW_USB_CDC && !MICROPY_HW_ESP_USB_SERIAL_JTAG) #endif #if MICROPY_HW_ENABLE_UART_REPL diff --git a/ports/esp32/usb.c b/ports/esp32/usb.c index 471f2c2e0e460..4207e77df210b 100644 --- a/ports/esp32/usb.c +++ b/ports/esp32/usb.c @@ -28,7 +28,7 @@ #include "py/mphal.h" #include "usb.h" -#if CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !MICROPY_HW_ESP_USB_SERIAL_JTAG +#if MICROPY_HW_USB_CDC #include "esp_timer.h" #ifndef NO_QSTR @@ -100,4 +100,4 @@ void usb_tx_strn(const char *str, size_t len) { } } -#endif // CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !MICROPY_HW_ESP_USB_SERIAL_JTAG +#endif // MICROPY_HW_USB_CDC From a6c35aeee873953bd9016cf63feb1a6772bdd6af Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 4 Sep 2024 11:06:21 +1000 Subject: [PATCH 0280/1300] esp32: Fix ARDUINO_NANO_ESP32 build configuration. Regression introduced by 5e692d04 now at MICROPY_HW_USB_CDC is set. The ARDUINO_NANO_ESP32 specifically builds shared/tinyusb/mp_usb_cdc.c for the 1200bps reset behaviour. However MicroPython esp32 doesn't yet use the rest of the shared/tinyusb functionality. Signed-off-by: Angus Gratton --- ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h index 873838352e198..8c2aa88e9f2ec 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h @@ -19,6 +19,7 @@ #define MICROPY_HW_SPI2_SCK (18) #define MICROPY_HW_ENABLE_USBDEV (1) +#define MICROPY_EXCLUDE_SHARED_TINYUSB_USBD_CDC (1) #define MICROPY_HW_USB_EXTERNAL_TINYUSB (1) #define MICROPY_HW_USB_CDC_1200BPS_TOUCH (1) #define MICROPY_SCHEDULER_STATIC_NODES (1) From fee9d66e3a7308bd9edffb2624b52f4e04ecc4f3 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 3 Sep 2024 15:39:36 +1000 Subject: [PATCH 0281/1300] esp32: Disable hardware stack protection on ESP32-C3. Workaround for what appears to be an upstream issue: https://github.com/espressif/esp-idf/issues/14456 This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb | 3 +++ ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board | 3 +++ 2 files changed, 6 insertions(+) diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb b/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb index 44838c5349cc4..70e5f1f0d195a 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb +++ b/ports/esp32/boards/ESP32_GENERIC_C3/sdkconfig.c3usb @@ -1 +1,4 @@ CONFIG_ESP32C3_REV_MIN_3=y + +# Workaround for https://github.com/espressif/esp-idf/issues/14456 +CONFIG_ESP_SYSTEM_HW_STACK_GUARD=n diff --git a/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board b/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board index 44838c5349cc4..70e5f1f0d195a 100644 --- a/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board +++ b/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board @@ -1 +1,4 @@ CONFIG_ESP32C3_REV_MIN_3=y + +# Workaround for https://github.com/espressif/esp-idf/issues/14456 +CONFIG_ESP_SYSTEM_HW_STACK_GUARD=n From 838c490eb4403885e96cbe3ded6f73a082c9399f Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 16 Aug 2024 14:27:15 +1000 Subject: [PATCH 0282/1300] tests/net_inet: Update micropython.org certificate for SSL tests. The Let's Encrypt root certificate has changed so needs updating in this test. Signed-off-by: Damien George --- tests/net_inet/mpycert.der | Bin 1306 -> 1290 bytes tests/net_inet/ssl_cert.py | 71 +++++++++++------------ tests/net_inet/test_sslcontext_client.py | 9 ++- 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/tests/net_inet/mpycert.der b/tests/net_inet/mpycert.der index 2d66ea723ea4f983874b3ffacdf612bd90e150d7..0b0eabc9bc8135de25785cb8574ab78e03947aa9 100644 GIT binary patch delta 950 zcmV;n14;av3W^FKFoFdJFoFW^paTK{0s;{LihVv4)-N#cBZj>x2WvF=ksxIqG%zzU zGcYhPFfcG$7Y#BuFf%bSGBY(fH91-^Gm)Yu4Gb^}1_M#??uFT>t!m!&tE*=<6KP}_X{U0stZDQQ%`wn9Sw)s0s{d60i%KV zFoE@x0s>AH#m`2h>h#0FdTf=%EwEmaFDB!;lVk!`6cYvmRUIG%3@{2X2nGgcfeZlx z0h74`Dt`?I2`Yw2hW8Bt0Sg5H1A+np08ZkGT?zvnkT}rwANg@y_`{E~H;8&~uq7Yu zzPH$h29D_ogw1llf%E*z&r?Y4z4mEB)TI$VHOLdPk{tzK>MqpS_({X9^_iBO?8T zQTRbT947O}D z2u;^G?MN*y1!%5-UGGfX*dRe)Xz{yR&EpQR3^9kBL*7Y4;m8C((L5W6MKEcosd5f^ z5*#tuJ|kD%r%#~p9D&hyA8*#uxu~-P7Jq>`OP^J)(rVK% z4E#9vV1VClHOh6NROBzbZWqZKr8l*rn!4=tz;#AXcc|!DR*_~cZr&+@)~1&QUSvhS zEY9j=LPW5`Pp$B@+yly)n+bxIf(>JeX89R%_|>_I5aNlL2{rkm9KMosBXsGV9~aj< YN}7aN5W@4P;x+7kJ4MbgMpA3Qz)StIBme*a delta 966 zcmV;%13CPP3YrQaFoFdZFoFX9paTK{0s;{Lkt+yF&kPu+Q})&_C8u9nksxIqFfchV zG%zqQFfcG$7Y#BsFgYO;^@2P? zsZMV7JHsy9jli&6^I16O?ayiOS_>P#z?38wa6t82X|CzIso!nEW2L4Ee9ieHiC3U+K%u)mO?NOnN58D+20|5X5qk;hl zFoFRDlK}!w6cjGA7q?he%dSufK;|E>or@I0#*0s$}$1`8_&0R#bp-xC1=0Wb{)2`Yw2hW8Bt0Sg5H1A+np0ENmr$4MUfq7$1J%;D$G~TCu$_zlUxsDu4eWP8$Wo^%$4%UaZ5VxN7h& zygM_w?n~ax{N%5M3x+?yRYNxRXyG;&YL1!F|D`~uG`GOeV>vZBb#C&ycF28VR> z7tRa<*}Z}LpSNh#24%B?J$F<1j(G#E3NZ(DDnr;+mvY1@4)?@ePROoCgfQ3gg9Du|C{1WG)@NQGX`_?4^_`0^3(`_UraNykS7l8yf!{b&g/dev/null # The certificate is from Let's Encrypt: -# 1 s:/C=US/O=Let's Encrypt/CN=R3 -# i:/C=US/O=Internet Security Research Group/CN=ISRG Root X1 -# Validity -# Not Before: Sep 4 00:00:00 2020 GMT -# Not After : Sep 15 16:00:00 2025 GMT +# 1 s:C=US, O=Let's Encrypt, CN=R11 +# i:C=US, O=Internet Security Research Group, CN=ISRG Root X1 +# a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256 +# v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT # Copy PEM content to a file (certmpy.pem) and convert to DER e.g. # $ openssl x509 -in certmpy.pem -out certmpy.der -outform DER # Then convert to hex format, eg using binascii.hexlify(data). ca_cert_chain = binascii.unhexlify( - b"30820516308202fea003020102021100912b084acf0c18a753f6d62e25a75f5a300d06092a864886" + b"30820506308202eea0030201020211008a7d3e13d62f30ef2386bd29076b34f8300d06092a864886" b"f70d01010b0500304f310b300906035504061302555331293027060355040a1320496e7465726e65" b"742053656375726974792052657365617263682047726f7570311530130603550403130c49535247" - b"20526f6f74205831301e170d3230303930343030303030305a170d3235303931353136303030305a" - b"3032310b300906035504061302555331163014060355040a130d4c6574277320456e637279707431" - b"0b300906035504031302523330820122300d06092a864886f70d01010105000382010f003082010a" - b"0282010100bb021528ccf6a094d30f12ec8d5592c3f882f199a67a4288a75d26aab52bb9c54cb1af" - b"8e6bf975c8a3d70f4794145535578c9ea8a23919f5823c42a94e6ef53bc32edb8dc0b05cf35938e7" - b"edcf69f05a0b1bbec094242587fa3771b313e71cace19befdbe43b45524596a9c153ce34c852eeb5" - b"aeed8fde6070e2a554abb66d0e97a540346b2bd3bc66eb66347cfa6b8b8f572999f830175dba726f" - b"fb81c5add286583d17c7e709bbf12bf786dcc1da715dd446e3ccad25c188bc60677566b3f118f7a2" - b"5ce653ff3a88b647a5ff1318ea9809773f9d53f9cf01e5f5a6701714af63a4ff99b3939ddc53a706" - b"fe48851da169ae2575bb13cc5203f5ed51a18bdb150203010001a382010830820104300e0603551d" - b"0f0101ff040403020186301d0603551d250416301406082b0601050507030206082b060105050703" - b"0130120603551d130101ff040830060101ff020100301d0603551d0e04160414142eb317b75856cb" - b"ae500940e61faf9d8b14c2c6301f0603551d2304183016801479b459e67bb6e5e40173800888c81a" - b"58f6e99b6e303206082b0601050507010104263024302206082b060105050730028616687474703a" - b"2f2f78312e692e6c656e63722e6f72672f30270603551d1f0420301e301ca01aa018861668747470" - b"3a2f2f78312e632e6c656e63722e6f72672f30220603551d20041b30193008060667810c01020130" - b"0d060b2b0601040182df13010101300d06092a864886f70d01010b0500038202010085ca4e473ea3" - b"f7854485bcd56778b29863ad754d1e963d336572542d81a0eac3edf820bf5fccb77000b76e3bf65e" - b"94dee4209fa6ef8bb203e7a2b5163c91ceb4ed3902e77c258a47e6656e3f46f4d9f0ce942bee54ce" - b"12bc8c274bb8c1982fa2afcd71914a08b7c8b8237b042d08f908573e83d904330a472178098227c3" - b"2ac89bb9ce5cf264c8c0be79c04f8e6d440c5e92bb2ef78b10e1e81d4429db5920ed63b921f81226" - b"949357a01d6504c10a22ae100d4397a1181f7ee0e08637b55ab1bd30bf876e2b2aff214e1b05c3f5" - b"1897f05eacc3a5b86af02ebc3b33b9ee4bdeccfce4af840b863fc0554336f668e136176a8e99d1ff" - b"a540a734b7c0d063393539756ef2ba76c89302e9a94b6c17ce0c02d9bd81fb9fb768d40665b3823d" - b"7753f88e7903ad0a3107752a43d8559772c4290ef7c45d4ec8ae468430d7f2855f18a179bbe75e70" - b"8b07e18693c3b98fdc6171252aafdfed255052688b92dce5d6b5e3da7dd0876c842131ae82f5fbb9" - b"abc889173de14ce5380ef6bd2bbd968114ebd5db3d20a77e59d3e2f858f95bb848cdfe5c4f1629fe" - b"1e5523afc811b08dea7c9390172ffdaca20947463ff0e9b0b7ff284d6832d6675e1e69a393b8f59d" - b"8b2f0bd25243a66f3257654d3281df3853855d7e5d6629eab8dde495b5cdb5561242cdc44ec62538" - b"44506decce005518fee94964d44eca979cb45bc073a8abb847c2" + b"20526f6f74205831301e170d3234303331333030303030305a170d3237303331323233353935395a" + b"3033310b300906035504061302555331163014060355040a130d4c6574277320456e637279707431" + b"0c300a0603550403130352313130820122300d06092a864886f70d01010105000382010f00308201" + b"0a0282010100ba87bc5c1b0039cbca0acdd46710f9013ca54ea561cb26ca52fb1501b7b928f5281e" + b"ed27b324183967090c08ece03ab03b770ebdf3e53954410c4eae41d69974de51dbef7bff58bda8b7" + b"13f6de31d5f272c9726a0b8374959c4600641499f3b1d922d9cda892aa1c267a3ffeef58057b0895" + b"81db710f8efbe33109bb09be504d5f8f91763d5a9d9e83f2e9c466b3e106664348188065a037189a" + b"9b843297b1b2bdc4f815009d2788fbe26317966c9b27674bc4db285e69c279f0495ce02450e1c4bc" + b"a105ac7b406d00b4c2413fa758b82fc55c9ba5bb099ef1feebb08539fda80aef45c478eb652ac2cf" + b"5f3cdee35c4d1bf70b272baa0b4277534f796a1d87d90203010001a381f83081f5300e0603551d0f" + b"0101ff040403020186301d0603551d250416301406082b0601050507030206082b06010505070301" + b"30120603551d130101ff040830060101ff020100301d0603551d0e04160414c5cf46a4eaf4c3c07a" + b"6c95c42db05e922f26e3b9301f0603551d2304183016801479b459e67bb6e5e40173800888c81a58" + b"f6e99b6e303206082b0601050507010104263024302206082b060105050730028616687474703a2f" + b"2f78312e692e6c656e63722e6f72672f30130603551d20040c300a3008060667810c010201302706" + b"03551d1f0420301e301ca01aa0188616687474703a2f2f78312e632e6c656e63722e6f72672f300d" + b"06092a864886f70d01010b050003820201004ee2895d0a031c9038d0f51ff9715cf8c38fb237887a" + b"6fb0251fedbeb7d886068ee90984cd72bf81f3fccacf5348edbdf66942d4a5113e35c813b2921d05" + b"5fea2ed4d8f849c3adf599969cef26d8e1b4240b48204dfcd354b4a9c621c8e1361bff77642917b9" + b"f04bef5deacd79d0bf90bfbe23b290da4aa9483174a9440be1e2f62d8371a4757bd294c10519461c" + b"b98ff3c47448252a0de5f5db43e2db939bb919b41f2fdf6a0e8f31d3630fbb29dcdd662c3fb01b67" + b"51f8413ce44db9acb8a49c6663f5ab85231dcc53b6ab71aedcc50171da36ee0a182a32fd09317c8f" + b"f673e79c9cb54a156a77825acfda8d45fe1f2a6405303e73c2c60cb9d63b634aab4603fe99c04640" + b"276063df503a0747d8154a9fea471f995a08620cb66c33084dd738ed482d2e0568ae805def4cdcd8" + b"20415f68f1bb5acde30eb00c31879b43de4943e1c8043fd13c1b87453069a8a9720e79121c31d83e" + b"2357dda74fa0f01c81d1771f6fd6d2b9a8b3031681394b9f55aed26ae4b3bfeaa5d59f4ba3c9d63b" + b"72f34af654ab0cfc38f76080df6e35ca75a154e42fbc6e17c91aa537b5a29abaecf4c075464f77a8" + b"e8595691662d6ede2981d6a697055e6445be2cceea644244b0c34fadf0b4dc03ca999b098295820d" + b"638a66f91972f8d5b98910e289980935f9a21cbe92732374e99d1fd73b4a9a845810c2f3a7e235ec" + b"7e3b45ce3046526bc0c0" ) diff --git a/tests/net_inet/test_sslcontext_client.py b/tests/net_inet/test_sslcontext_client.py index 860b053d5b481..30ec0ac7c83fc 100644 --- a/tests/net_inet/test_sslcontext_client.py +++ b/tests/net_inet/test_sslcontext_client.py @@ -5,11 +5,10 @@ # This certificate was obtained from micropython.org using openssl: # $ openssl s_client -showcerts -connect micropython.org:443 /dev/null # The certificate is from Let's Encrypt: -# 1 s:/C=US/O=Let's Encrypt/CN=R3 -# i:/C=US/O=Internet Security Research Group/CN=ISRG Root X1 -# Validity -# Not Before: Sep 4 00:00:00 2020 GMT -# Not After : Sep 15 16:00:00 2025 GMT +# 1 s:C=US, O=Let's Encrypt, CN=R11 +# i:C=US, O=Internet Security Research Group, CN=ISRG Root X1 +# a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256 +# v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT # Copy PEM content to a file (certmpy.pem) and convert to DER e.g. # $ openssl x509 -in certmpy.pem -out certmpy.der -outform DER # Then convert to hex format, eg using binascii.hexlify(data). From 6be1dbc784ba1f845a4f7525bb2ecd6b61a79405 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 17 Aug 2024 00:10:40 +1000 Subject: [PATCH 0283/1300] tests/run-tests.py: Automatically detect native arch and mpy-cross flag. Now that some ports support multiple architectures (eg esp32 has both Xtensa and RISC-V CPUs) it's no longer possible to set mpy-cross flags based on the target, eg `./run-tests.py --target esp32`. Instead this commit makes it so the `-march=xxx` argument to mpy-cross is detected automatically via evaluation of `sys.implementation._mpy`. Signed-off-by: Damien George --- tests/feature_check/target_info.py | 22 ++++++++++++++++++++++ tests/feature_check/target_info.py.exp | 0 tests/run-tests.py | 24 +++++++----------------- 3 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 tests/feature_check/target_info.py create mode 100644 tests/feature_check/target_info.py.exp diff --git a/tests/feature_check/target_info.py b/tests/feature_check/target_info.py new file mode 100644 index 0000000000000..df89496708aa9 --- /dev/null +++ b/tests/feature_check/target_info.py @@ -0,0 +1,22 @@ +# Retrieve the native architecture of the target. +# See https://docs.micropython.org/en/latest/reference/mpyfiles.html#versioning-and-compatibility-of-mpy-files +# for more details. + +import sys + +sys_mpy = getattr(sys.implementation, "_mpy", 0) +arch = [ + None, + "x86", + "x64", + "armv6", + "armv6m", + "armv7m", + "armv7em", + "armv7emsp", + "armv7emdp", + "xtensa", + "xtensawin", + "rv32imc", +][sys_mpy >> 10] +print(arch) diff --git a/tests/feature_check/target_info.py.exp b/tests/feature_check/target_info.py.exp new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/run-tests.py b/tests/run-tests.py index 60bfc2599fb47..999812bd5db9f 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -1065,11 +1065,6 @@ def main(): pyb = None elif args.target in LOCAL_TARGETS: pyb = None - if not args.mpy_cross_flags: - if args.target == "unix": - args.mpy_cross_flags = "-march=host" - elif args.target == "qemu-arm": - args.mpy_cross_flags = "-march=armv7m" if args.target == "webassembly": pyb = PyboardNodeRunner() elif args.target in EXTERNAL_TARGETS: @@ -1077,24 +1072,19 @@ def main(): sys.path.append(base_path("../tools")) import pyboard - if not args.mpy_cross_flags: - if args.target == "esp8266": - args.mpy_cross_flags = "-march=xtensa" - elif args.target == "esp32": - args.mpy_cross_flags = "-march=xtensawin" - elif args.target == "rp2": - args.mpy_cross_flags = "-march=armv6m" - elif args.target == "pyboard": - args.mpy_cross_flags = "-march=armv7emsp" - else: - args.mpy_cross_flags = "-march=armv7m" - pyb = pyboard.Pyboard(args.device, args.baudrate, args.user, args.password) pyboard.Pyboard.run_script_on_remote_target = run_script_on_remote_target pyb.enter_raw_repl() else: raise ValueError("target must be one of %s" % ", ".join(LOCAL_TARGETS + EXTERNAL_TARGETS)) + # Automatically detect the native architecture for mpy-cross if not given. + if not (args.list_tests or args.write_exp) and not args.mpy_cross_flags: + output = run_feature_check(pyb, args, "target_info.py") + arch = str(output, "ascii").strip() + if arch != "None": + args.mpy_cross_flags = "-march=" + arch + if args.run_failures and (any(args.files) or args.test_dirs is not None): raise ValueError( "--run-failures cannot be used together with files or --test-dirs arguments" From 9396572eee25c40f2e9b641c9aa9563fd1f1c1b4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 29 Aug 2024 13:51:12 +1000 Subject: [PATCH 0284/1300] tools/mpy-tool.py: Support freezing rv32imc native code. Signed-off-by: Damien George --- tools/mpy-tool.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 5007bbbaca013..cdceae3746638 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -1080,6 +1080,7 @@ def __init__( MP_NATIVE_ARCH_X64, MP_NATIVE_ARCH_XTENSA, MP_NATIVE_ARCH_XTENSAWIN, + MP_NATIVE_ARCH_RV32IMC, ): self.fun_data_attributes = '__attribute__((section(".text,\\"ax\\",@progbits # ")))' else: From dc9ecd58601b2b716399cde2b24de6c4786ed77e Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Aug 2024 19:42:31 +1000 Subject: [PATCH 0285/1300] qemu-arm: Factor board config to mk fragments. Signed-off-by: Damien George --- ports/qemu-arm/Makefile | 61 ++++++----------------------- ports/qemu-arm/README.md | 19 ++++++++- ports/qemu-arm/boards/MICROBIT.mk | 13 ++++++ ports/qemu-arm/boards/MPS2_AN385.mk | 12 ++++++ ports/qemu-arm/boards/NETDUINO2.mk | 12 ++++++ ports/qemu-arm/boards/SABRELITE.mk | 19 +++++++++ tools/ci.sh | 2 +- 7 files changed, 86 insertions(+), 52 deletions(-) create mode 100644 ports/qemu-arm/boards/MICROBIT.mk create mode 100644 ports/qemu-arm/boards/MPS2_AN385.mk create mode 100644 ports/qemu-arm/boards/NETDUINO2.mk create mode 100644 ports/qemu-arm/boards/SABRELITE.mk diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index cdfc39580b9d8..db9cdc015a4ac 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -1,4 +1,4 @@ -BOARD ?= mps2-an385 +BOARD ?= MPS2_AN385 # Make the build directory reflect the board. BUILD ?= build-$(BOARD) @@ -6,6 +6,9 @@ BUILD ?= build-$(BOARD) include ../../py/mkenv.mk -include mpconfigport.mk +# Include board specific .mk file. +include boards/$(BOARD).mk + # qstr definitions (must come before including py.mk) QSTR_DEFS = qstrdefsport.h @@ -17,58 +20,16 @@ FROZEN_MANIFEST ?= "freeze('test-frzmpy')" include $(TOP)/py/py.mk include $(TOP)/extmod/extmod.mk -CFLAGS += -DMICROPY_HW_BOARD_NAME='"$(BOARD)"' -QEMU_ARGS += -machine $(BOARD) -nographic -monitor null -semihosting - -ifeq ($(BOARD),netduino2) -CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft -CFLAGS += -DQEMU_SOC_STM32 -CFLAGS += -DMICROPY_HW_MCU_NAME='"STM32"' -LDSCRIPT = stm32.ld -SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o -MPY_CROSS_FLAGS += -march=armv7m -endif - -ifeq ($(BOARD),microbit) -CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft -CFLAGS += -DQEMU_SOC_NRF51 -CFLAGS += -DMICROPY_HW_MCU_NAME='"nRF51"' -LDSCRIPT = nrf51.ld -QEMU_ARGS += -global nrf51-soc.flash-size=1048576 -global nrf51-soc.sram-size=262144 -SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb1.o -MPY_CROSS_FLAGS += -march=armv7m -endif - -ifeq ($(BOARD),mps2-an385) -CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft -CFLAGS += -DQEMU_SOC_MPS2 -CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-M3"' -LDSCRIPT = mps2.ld -SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o -MPY_CROSS_FLAGS += -march=armv7m -endif - -ifeq ($(BOARD),sabrelite) -CFLAGS += -mcpu=cortex-a9 -CFLAGS += -DQEMU_SOC_IMX6 -CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-A9"' -LDSCRIPT = imx6.ld -QEMU_ARGS += -m 128M -SRC_BOARD_O = shared/runtime/gchelper_generic.o -# It's really armv7a but closest supported value is armv6. -MPY_CROSS_FLAGS += -march=armv6 -# Cortex-A9 should support unaligned-access, but qemu doesn't seem to. -CFLAGS += -mno-unaligned-access -# These don't work on Cortex-A9. -TESTS_EXCLUDE = --exclude '(asmdiv|asmspecialregs).py' -endif - CROSS_COMPILE ?= arm-none-eabi- +QEMU_SYSTEM = qemu-system-$(QEMU_ARCH) +QEMU_ARGS += -machine $(QEMU_MACHINE) -nographic -monitor null -semihosting + INC += -I. INC += -I$(TOP) INC += -I$(BUILD) +CFLAGS += -DMICROPY_HW_BOARD_NAME='"$(QEMU_MACHINE)"' CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Werror -std=gnu99 $(COPT) \ -ffunction-sections -fdata-sections CFLAGS += $(CFLAGS_EXTRA) @@ -135,16 +96,16 @@ all: $(BUILD)/firmware.elf .PHONY: repl repl: $(BUILD)/firmware.elf $(ECHO) "Use machine.reset() to exit" - qemu-system-arm $(QEMU_ARGS) -serial mon:stdio -kernel $< + $(QEMU_SYSTEM) $(QEMU_ARGS) -serial mon:stdio -kernel $< .PHONY: run run: $(BUILD)/firmware.elf - qemu-system-arm $(QEMU_ARGS) -serial pty -kernel $< + $(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel $< .PHONY: test test: $(BUILD)/firmware.elf $(eval DIRNAME=ports/$(notdir $(CURDIR))) - cd $(TOP)/tests && ./run-tests.py --target qemu-arm --device execpty:"qemu-system-arm $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(TESTS_EXCLUDE) + cd $(TOP)/tests && ./run-tests.py --target qemu-arm --device execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_ARGS) $(RUN_TESTS_EXTRA) ## `$(LD)` doesn't seem to like `--specs` for some reason, but we can just use `$(CC)` here. $(BUILD)/firmware.elf: $(LDSCRIPT) $(ALL_OBJ_RUN) diff --git a/ports/qemu-arm/README.md b/ports/qemu-arm/README.md index 34d73fd165756..bb49571df4179 100644 --- a/ports/qemu-arm/README.md +++ b/ports/qemu-arm/README.md @@ -32,7 +32,16 @@ Then build using: The default qemu-supported board is `mps2-an385`, a Cortex-M3 board. To select a different board pass the `BOARD` argument to `make`, for example: - $ make BOARD=sabrelite + $ make BOARD=SABRELITE + +Available boards are: + +| Name for `BOARD=` | Corresponding qemu board | +| ----------------- | ------------------------ | +| `MICROBIT` | `microbit` | +| `MPS2_AN385` | `mps2-an385` | +| `NETDUINO2` | `netduino2` | +| `SABRELITE` | `sabrelite` | Running ------- @@ -67,3 +76,11 @@ tests against the serial device, for example: $ cd ../../tests $ ./run-tests.py --target qemu-arm --device /dev/pts/1 + +Extra make options +------------------ + +The following options can be specified on the `make` command line: +- `CFLAGS_EXTRA`: pass in extra flags for the compiler. +- `RUN_TESTS_EXTRA`: pass in extra flags for `run-tests.py` when invoked via + `make test`. diff --git a/ports/qemu-arm/boards/MICROBIT.mk b/ports/qemu-arm/boards/MICROBIT.mk new file mode 100644 index 0000000000000..90edb11aff1a8 --- /dev/null +++ b/ports/qemu-arm/boards/MICROBIT.mk @@ -0,0 +1,13 @@ +QEMU_ARCH = arm +QEMU_MACHINE = microbit +QEMU_ARGS += -global nrf51-soc.flash-size=1048576 -global nrf51-soc.sram-size=262144 + +CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft +CFLAGS += -DQEMU_SOC_NRF51 +CFLAGS += -DMICROPY_HW_MCU_NAME='"nRF51"' + +LDSCRIPT = nrf51.ld + +SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb1.o + +MPY_CROSS_FLAGS += -march=armv7m diff --git a/ports/qemu-arm/boards/MPS2_AN385.mk b/ports/qemu-arm/boards/MPS2_AN385.mk new file mode 100644 index 0000000000000..f155be03d29ed --- /dev/null +++ b/ports/qemu-arm/boards/MPS2_AN385.mk @@ -0,0 +1,12 @@ +QEMU_ARCH = arm +QEMU_MACHINE = mps2-an385 + +CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft +CFLAGS += -DQEMU_SOC_MPS2 +CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-M3"' + +LDSCRIPT = mps2.ld + +SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o + +MPY_CROSS_FLAGS += -march=armv7m diff --git a/ports/qemu-arm/boards/NETDUINO2.mk b/ports/qemu-arm/boards/NETDUINO2.mk new file mode 100644 index 0000000000000..3067f094c60f9 --- /dev/null +++ b/ports/qemu-arm/boards/NETDUINO2.mk @@ -0,0 +1,12 @@ +QEMU_ARCH = arm +QEMU_MACHINE = netduino2 + +CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft +CFLAGS += -DQEMU_SOC_STM32 +CFLAGS += -DMICROPY_HW_MCU_NAME='"STM32"' + +LDSCRIPT = stm32.ld + +SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o + +MPY_CROSS_FLAGS += -march=armv7m diff --git a/ports/qemu-arm/boards/SABRELITE.mk b/ports/qemu-arm/boards/SABRELITE.mk new file mode 100644 index 0000000000000..aef96f552744b --- /dev/null +++ b/ports/qemu-arm/boards/SABRELITE.mk @@ -0,0 +1,19 @@ +QEMU_ARCH = arm +QEMU_MACHINE = sabrelite +QEMU_ARGS += -m 128M + +CFLAGS += -mcpu=cortex-a9 +CFLAGS += -DQEMU_SOC_IMX6 +CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-A9"' +# Cortex-A9 should support unaligned-access, but qemu doesn't seem to. +CFLAGS += -mno-unaligned-access + +LDSCRIPT = imx6.ld + +SRC_BOARD_O = shared/runtime/gchelper_generic.o + +# It's really armv7a but closest supported value is armv6. +MPY_CROSS_FLAGS += -march=armv6 + +# These tests don't work on Cortex-A9, so exclude them. +RUN_TESTS_ARGS = --exclude '(asmdiv|asmspecialregs).py' diff --git a/tools/ci.sh b/tools/ci.sh index 175d7f57622fb..abfb965b543aa 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -259,7 +259,7 @@ function ci_qemu_arm_build { make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 make ${MAKEOPTS} -C ports/qemu-arm clean make ${MAKEOPTS} -C ports/qemu-arm test - make ${MAKEOPTS} -C ports/qemu-arm BOARD=sabrelite test + make ${MAKEOPTS} -C ports/qemu-arm BOARD=SABRELITE test } ######################################################################################## From f769b4329b979b49dfe755b059969794a931d0d5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 29 Aug 2024 11:36:59 +1000 Subject: [PATCH 0286/1300] qemu-arm/Makefile: Clean up SRC and OBJ variables. Signed-off-by: Damien George --- ports/qemu-arm/Makefile | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index db9cdc015a4ac..d748aa049bbb9 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -51,7 +51,8 @@ endif LDFLAGS= -T $(LDSCRIPT) --gc-sections -Map=$(@:.elf=.map) LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) -SRC_COMMON_C = \ +SRC_C = \ + main.c \ startup.c \ uart.c \ mphalport.c \ @@ -63,33 +64,16 @@ SRC_COMMON_C = \ shared/runtime/stdout_helpers.c \ shared/runtime/sys_stdio_mphal.c \ -SRC_RUN_C = \ - main.c \ - LIB_SRC_C += $(SRC_LIB_LIBM_C) LIB_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C) -OBJ_COMMON = -OBJ_COMMON += $(PY_O) -OBJ_COMMON += $(addprefix $(BUILD)/, $(SRC_COMMON_C:.c=.o)) -OBJ_COMMON += $(addprefix $(BUILD)/, $(SRC_BOARD_O)) -OBJ_COMMON += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) - -OBJ_RUN = -OBJ_RUN += $(addprefix $(BUILD)/, $(SRC_RUN_C:.c=.o)) - -ALL_OBJ_RUN = $(OBJ_COMMON) $(OBJ_RUN) - -OBJ_TEST = -OBJ_TEST += $(addprefix $(BUILD)/, $(SRC_TEST_C:.c=.o)) - -ALL_OBJ_TEST = $(OBJ_COMMON) $(OBJ_TEST) - -# All object files, needed to get dependencies correct -OBJ = $(OBJ_COMMON) $(OBJ_RUN) $(OBJ_TEST) +OBJ += $(PY_O) +OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_BOARD_O)) +OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) # List of sources for qstr extraction -SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C) +SRC_QSTR += $(SRC_C) $(LIB_SRC_C) all: $(BUILD)/firmware.elf @@ -108,8 +92,8 @@ test: $(BUILD)/firmware.elf cd $(TOP)/tests && ./run-tests.py --target qemu-arm --device execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_ARGS) $(RUN_TESTS_EXTRA) ## `$(LD)` doesn't seem to like `--specs` for some reason, but we can just use `$(CC)` here. -$(BUILD)/firmware.elf: $(LDSCRIPT) $(ALL_OBJ_RUN) - $(Q)$(LD) $(LDFLAGS) -o $@ $(ALL_OBJ_RUN) $(LIBS) +$(BUILD)/firmware.elf: $(LDSCRIPT) $(OBJ) + $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(Q)$(SIZE) $@ include $(TOP)/py/mkrules.mk From 0426934969d06aa649ba903f5408cb331b5b9c2d Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 29 Aug 2024 13:51:39 +1000 Subject: [PATCH 0287/1300] qemu-arm: Merge RISC-V 32-bit support into qemu-arm port. Currently both the qemu-arm and qemu-riscv ports share a lot of code and functionality. This commit merges the qemu-riscv port into the qemu-arm port. The only real differences between the two are the toolchains used to build the code, and the initialisation/startup framework. Everything else is pretty much the same, so this brings the following benefits: - less code duplication - less burden on maintenance - generalised qemu port, could in the future support other architectures A new board `VIRT_RV32` has been added to the qemu-arm port which is the existing RISC-V board from the qemu-riscv port. To build it: $ make BOARD=VIRT_RV32 repl To cleanly separate the code for the different architectures, startup code has been moved to ports/qemu-arm/mcu//. Signed-off-by: Damien George --- .github/workflows/ports_qemu-arm.yml | 18 ++- .github/workflows/ports_qemu-riscv.yml | 33 ----- ports/qemu-arm/Makefile | 105 ++++++++++++-- ports/qemu-arm/README.md | 42 ++++-- ports/qemu-arm/boards/MICROBIT.mk | 2 +- ports/qemu-arm/boards/MPS2_AN385.mk | 2 +- ports/qemu-arm/boards/NETDUINO2.mk | 2 +- ports/qemu-arm/boards/SABRELITE.mk | 2 +- ports/qemu-arm/boards/VIRT_RV32.mk | 14 ++ ports/qemu-arm/{ => mcu/arm}/imx6.ld | 0 ports/qemu-arm/{ => mcu/arm}/mps2.ld | 0 ports/qemu-arm/{ => mcu/arm}/nrf51.ld | 0 ports/qemu-arm/{ => mcu/arm}/startup.c | 0 ports/qemu-arm/{ => mcu/arm}/stm32.ld | 0 .../mcu/rv32}/entrypoint.s | 0 .../mcu/rv32}/interrupts.c | 0 .../mcu/rv32}/startup.c | 0 .../{qemu-riscv => qemu-arm/mcu/rv32}/virt.ld | 0 ports/qemu-arm/mpconfigport.h | 6 + ports/qemu-arm/mphalport.h | 1 + ports/qemu-arm/uart.c | 35 ++++- ports/qemu-riscv/Makefile | 128 ------------------ ports/qemu-riscv/Makefile.test | 31 ----- ports/qemu-riscv/README.md | 29 ---- ports/qemu-riscv/main.c | 72 ---------- ports/qemu-riscv/mpconfigport.h | 77 ----------- ports/qemu-riscv/mphalport.h | 32 ----- ports/qemu-riscv/qstrdefsport.h | 2 - ports/qemu-riscv/test_main.c | 66 --------- ports/qemu-riscv/tests_profile.txt | 7 - ports/qemu-riscv/uart.c | 45 ------ ports/qemu-riscv/uart.h | 33 ----- tools/ci.sh | 30 ++-- 33 files changed, 212 insertions(+), 602 deletions(-) delete mode 100644 .github/workflows/ports_qemu-riscv.yml create mode 100644 ports/qemu-arm/boards/VIRT_RV32.mk rename ports/qemu-arm/{ => mcu/arm}/imx6.ld (100%) rename ports/qemu-arm/{ => mcu/arm}/mps2.ld (100%) rename ports/qemu-arm/{ => mcu/arm}/nrf51.ld (100%) rename ports/qemu-arm/{ => mcu/arm}/startup.c (100%) rename ports/qemu-arm/{ => mcu/arm}/stm32.ld (100%) rename ports/{qemu-riscv => qemu-arm/mcu/rv32}/entrypoint.s (100%) rename ports/{qemu-riscv => qemu-arm/mcu/rv32}/interrupts.c (100%) rename ports/{qemu-riscv => qemu-arm/mcu/rv32}/startup.c (100%) rename ports/{qemu-riscv => qemu-arm/mcu/rv32}/virt.ld (100%) delete mode 100644 ports/qemu-riscv/Makefile delete mode 100644 ports/qemu-riscv/Makefile.test delete mode 100644 ports/qemu-riscv/README.md delete mode 100644 ports/qemu-riscv/main.c delete mode 100644 ports/qemu-riscv/mpconfigport.h delete mode 100644 ports/qemu-riscv/mphalport.h delete mode 100644 ports/qemu-riscv/qstrdefsport.h delete mode 100644 ports/qemu-riscv/test_main.c delete mode 100644 ports/qemu-riscv/tests_profile.txt delete mode 100644 ports/qemu-riscv/uart.c delete mode 100644 ports/qemu-riscv/uart.h diff --git a/.github/workflows/ports_qemu-arm.yml b/.github/workflows/ports_qemu-arm.yml index 99750b7535657..147d1b4b94231 100644 --- a/.github/workflows/ports_qemu-arm.yml +++ b/.github/workflows/ports_qemu-arm.yml @@ -19,14 +19,26 @@ concurrency: cancel-in-progress: true jobs: - build_and_test: + build_and_test_arm: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install packages - run: source tools/ci.sh && ci_qemu_arm_setup + run: source tools/ci.sh && ci_qemu_setup_arm - name: Build and run test suite - run: source tools/ci.sh && ci_qemu_arm_build + run: source tools/ci.sh && ci_qemu_build_arm + - name: Print failures + if: failure() + run: tests/run-tests.py --print-failures + + build_and_test_rv32: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install packages + run: source tools/ci.sh && ci_qemu_setup_rv32 + - name: Build and run test suite + run: source tools/ci.sh && ci_qemu_build_rv32 - name: Print failures if: failure() run: tests/run-tests.py --print-failures diff --git a/.github/workflows/ports_qemu-riscv.yml b/.github/workflows/ports_qemu-riscv.yml deleted file mode 100644 index 39ba06adb6cbd..0000000000000 --- a/.github/workflows/ports_qemu-riscv.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: qemu-riscv port - -on: - push: - pull_request: - paths: - - '.github/workflows/*.yml' - - 'tools/**' - - 'py/**' - - 'extmod/**' - - 'shared/**' - - 'lib/**' - - 'drivers/**' - - 'ports/qemu-arm/main.c' - - 'ports/qemu-riscv/**' - - 'tests/**' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - build_and_test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install packages - run: source tools/ci.sh && ci_qemu_riscv_setup - - name: Build and run test suite - run: source tools/ci.sh && ci_qemu_riscv_build - - name: Print failures - if: failure() - run: grep --before-context=100 --text "FAIL" ports/qemu-riscv/build/console.out diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index d748aa049bbb9..605b6b572c411 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -1,3 +1,6 @@ +################################################################################ +# Initial setup of Makefile environment + BOARD ?= MPS2_AN385 # Make the build directory reflect the board. @@ -14,16 +17,79 @@ QSTR_DEFS = qstrdefsport.h # MicroPython feature configurations MICROPY_ROM_TEXT_COMPRESSION ?= 1 + +ifeq ($(QEMU_ARCH),arm) FROZEN_MANIFEST ?= "freeze('test-frzmpy')" +endif +ifeq ($(QEMU_ARCH),riscv32) +FROZEN_MANIFEST ?= "freeze('test-frzmpy', ('frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" +endif # include py core make definitions include $(TOP)/py/py.mk include $(TOP)/extmod/extmod.mk +################################################################################ +# ARM specific settings + +ifeq ($(QEMU_ARCH),arm) + CROSS_COMPILE ?= arm-none-eabi- +LDFLAGS += -nostdlib +LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) + +SRC_C += \ + mcu/arm/startup.c \ + shared/runtime/semihosting_arm.c \ + +endif + +################################################################################ +# RISC-V 32-bit specific settings + +ifeq ($(QEMU_ARCH),riscv32) + +CROSS_COMPILE ?= riscv64-unknown-elf- + +GCC_VERSION = $(word 1, $(subst ., , $(shell $(CC) -dumpversion))) + +RV32_ABI = ilp32 + +QEMU_ARGS += -bios none + +# GCC 10 and lower do not recognise the Zicsr extension in the architecture name. +ifeq ($(shell test $(GCC_VERSION) -le 10; echo $$?),0) +RV32_ARCH ?= rv32imac +else +# Recent GCC versions explicitly require to declare extensions. +RV32_ARCH ?= rv32imac_zicsr +endif + +AFLAGS += -mabi=$(RV32_ABI) -march=$(RV32_ARCH) +CFLAGS += $(AFLAGS) +LDFLAGS += -mabi=$(RV32_ABI) -march=$(RV32_ARCH) -Wl,-EL + +SRC_C += \ + mcu/rv32/interrupts.c \ + mcu/rv32/startup.c \ + +SRC_BOARD_O += mcu/rv32/entrypoint.o + +endif + +################################################################################ +# Project specific settings and compiler/linker flags + QEMU_SYSTEM = qemu-system-$(QEMU_ARCH) QEMU_ARGS += -machine $(QEMU_MACHINE) -nographic -monitor null -semihosting +QEMU_ARGS += $(QEMU_EXTRA) + +# Specifying QEMU_DEBUG=1 will block qemu until a debugger is connected. +ifeq ($(QEMU_DEBUG),1) +QEMU_DEBUG_ARGS ?= -s +QEMU_ARGS += -S $(QEMU_DEBUG_ARGS) $(QEMU_DEBUG_EXTRA) +endif INC += -I. INC += -I$(TOP) @@ -34,6 +100,8 @@ CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -We -ffunction-sections -fdata-sections CFLAGS += $(CFLAGS_EXTRA) +LDFLAGS += -T $(LDSCRIPT) -Wl,--gc-sections -Wl,-Map=$(@:.elf=.map) + # Debugging/Optimization ifeq ($(DEBUG), 1) CFLAGS += -g @@ -42,25 +110,31 @@ else COPT += -Os -DNDEBUG endif -## With CoudeSourcery it's actually a little different, you just need `-T generic-m-hosted.ld`. -## Although for some reason `$(LD)` will not find that linker script, it works with `$(CC)`. -## It turns out that this is specific to CoudeSourcery, and ARM version of GCC ships something -## else instead and according to the following files, this is what we need to pass to `$(CC). -## - gcc-arm-none-eabi-4_8-2014q1/share/gcc-arm-none-eabi/samples/src/makefile.conf -## - gcc-arm-none-eabi-4_8-2014q1/share/gcc-arm-none-eabi/samples/src/qemu/Makefile -LDFLAGS= -T $(LDSCRIPT) --gc-sections -Map=$(@:.elf=.map) -LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) +# If Picolibc is available then select it explicitly. Ubuntu 22.04 ships its +# bare metal RISC-V toolchain with Picolibc rather than Newlib, and the default +# is "nosys" so a value must be provided. To avoid having per-distro +# workarounds, always select Picolibc if available. +PICOLIBC_SPECS = $(shell $(CC) --print-file-name=picolibc.specs) +ifeq ($(PICOLIBC_SPECS),picolibc.specs) +# Picolibc was not found. +else +$(info picolibc used $(PICOLIBC_SPECS)) +SPECS_FRAGMENT = --specs=$(PICOLIBC_SPECS) +CFLAGS += $(SPECS_FRAGMENT) +LDFLAGS += $(SPECS_FRAGMENT) +endif -SRC_C = \ +################################################################################ +# Source files and libraries + +SRC_C += \ main.c \ - startup.c \ uart.c \ mphalport.c \ shared/libc/string0.c \ shared/readline/readline.c \ shared/runtime/interrupt_char.c \ shared/runtime/pyexec.c \ - shared/runtime/semihosting_arm.c \ shared/runtime/stdout_helpers.c \ shared/runtime/sys_stdio_mphal.c \ @@ -75,6 +149,9 @@ OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) # List of sources for qstr extraction SRC_QSTR += $(SRC_C) $(LIB_SRC_C) +################################################################################ +# Main targets + all: $(BUILD)/firmware.elf .PHONY: repl @@ -91,9 +168,11 @@ test: $(BUILD)/firmware.elf $(eval DIRNAME=ports/$(notdir $(CURDIR))) cd $(TOP)/tests && ./run-tests.py --target qemu-arm --device execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_ARGS) $(RUN_TESTS_EXTRA) -## `$(LD)` doesn't seem to like `--specs` for some reason, but we can just use `$(CC)` here. $(BUILD)/firmware.elf: $(LDSCRIPT) $(OBJ) - $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + $(Q)$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(Q)$(SIZE) $@ +################################################################################ +# Remaining make rules + include $(TOP)/py/mkrules.mk diff --git a/ports/qemu-arm/README.md b/ports/qemu-arm/README.md index bb49571df4179..2e543e77ecc19 100644 --- a/ports/qemu-arm/README.md +++ b/ports/qemu-arm/README.md @@ -1,8 +1,8 @@ MicroPython port to qemu-arm ============================ -This is experimental, community-supported port for Cortex-M emulation as -provided by QEMU (http://qemu.org). +This is experimental, community-supported port for Cortex-M and RISC-V RV32IMC +emulation as provided by QEMU (http://qemu.org). The purposes of this port are to enable: @@ -18,6 +18,25 @@ The purposes of this port are to enable: - no need to use OpenOCD or anything else that might slow down the process in terms of plugging things together, pressing buttons, etc. +Dependencies +------------ + +### ARM + +For ARM-based boards the build requires a bare-metal ARM toolchain, such as +`arm-none-eabi-gcc`. + +### RISC-V + +For RISC-V-based boards the build requires a bare metal RISC-V toolchain with GCC 10 +or later, either with multilib support or 32 bits specific (M, C, and Zicsr +extensions must be supported, along with ilp32 ABI). Both newlib and picolibc are +supported, with the latter having precedence if found. + +Most pre-built toolchains should work out of the box, either coming from your +Linux distribution's package manager, or independently packaged ones like +[xPack](https://xpack.github.io/dev-tools/riscv-none-elf-gcc/). + Build instructions ------------------ @@ -36,12 +55,13 @@ different board pass the `BOARD` argument to `make`, for example: Available boards are: -| Name for `BOARD=` | Corresponding qemu board | -| ----------------- | ------------------------ | -| `MICROBIT` | `microbit` | -| `MPS2_AN385` | `mps2-an385` | -| `NETDUINO2` | `netduino2` | -| `SABRELITE` | `sabrelite` | +| Name for `BOARD=` | Architecture | Corresponding qemu board | +| ----------------- | ------------ | ------------------------ | +| `MICROBIT` | `arm` | `microbit` | +| `MPS2_AN385` | `arm` | `mps2-an385` | +| `NETDUINO2` | `arm` | `netduino2` | +| `SABRELITE` | `arm` | `sabrelite` | +| `VIRT_RV32` | `riscv32` | `virt` | Running ------- @@ -84,3 +104,9 @@ The following options can be specified on the `make` command line: - `CFLAGS_EXTRA`: pass in extra flags for the compiler. - `RUN_TESTS_EXTRA`: pass in extra flags for `run-tests.py` when invoked via `make test`. +- `QEMU_DEBUG=1`: when running qemu (via `repl`, `run` or `test` target), qemu + will block until a debugger is connected. By default it waits for a gdb connection + on TCP port 1234. +- `QEMU_DEBUG_ARGS`: defaults to `-s` (gdb on TCP port 1234), but can be overridden + with different qemu gdb arguments. +- `QEMU_DEBUG_EXTRA`: extra options to pass to qemu when `QEMU_DEBUG=1` is used. diff --git a/ports/qemu-arm/boards/MICROBIT.mk b/ports/qemu-arm/boards/MICROBIT.mk index 90edb11aff1a8..02eb0576c3438 100644 --- a/ports/qemu-arm/boards/MICROBIT.mk +++ b/ports/qemu-arm/boards/MICROBIT.mk @@ -6,7 +6,7 @@ CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft CFLAGS += -DQEMU_SOC_NRF51 CFLAGS += -DMICROPY_HW_MCU_NAME='"nRF51"' -LDSCRIPT = nrf51.ld +LDSCRIPT = mcu/arm/nrf51.ld SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb1.o diff --git a/ports/qemu-arm/boards/MPS2_AN385.mk b/ports/qemu-arm/boards/MPS2_AN385.mk index f155be03d29ed..182d076eb35d4 100644 --- a/ports/qemu-arm/boards/MPS2_AN385.mk +++ b/ports/qemu-arm/boards/MPS2_AN385.mk @@ -5,7 +5,7 @@ CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft CFLAGS += -DQEMU_SOC_MPS2 CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-M3"' -LDSCRIPT = mps2.ld +LDSCRIPT = mcu/arm/mps2.ld SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o diff --git a/ports/qemu-arm/boards/NETDUINO2.mk b/ports/qemu-arm/boards/NETDUINO2.mk index 3067f094c60f9..ffa781f339977 100644 --- a/ports/qemu-arm/boards/NETDUINO2.mk +++ b/ports/qemu-arm/boards/NETDUINO2.mk @@ -5,7 +5,7 @@ CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft CFLAGS += -DQEMU_SOC_STM32 CFLAGS += -DMICROPY_HW_MCU_NAME='"STM32"' -LDSCRIPT = stm32.ld +LDSCRIPT = mcu/arm/stm32.ld SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o diff --git a/ports/qemu-arm/boards/SABRELITE.mk b/ports/qemu-arm/boards/SABRELITE.mk index aef96f552744b..f1945c10fc2aa 100644 --- a/ports/qemu-arm/boards/SABRELITE.mk +++ b/ports/qemu-arm/boards/SABRELITE.mk @@ -8,7 +8,7 @@ CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-A9"' # Cortex-A9 should support unaligned-access, but qemu doesn't seem to. CFLAGS += -mno-unaligned-access -LDSCRIPT = imx6.ld +LDSCRIPT = mcu/arm/imx6.ld SRC_BOARD_O = shared/runtime/gchelper_generic.o diff --git a/ports/qemu-arm/boards/VIRT_RV32.mk b/ports/qemu-arm/boards/VIRT_RV32.mk new file mode 100644 index 0000000000000..ec3535eed3b06 --- /dev/null +++ b/ports/qemu-arm/boards/VIRT_RV32.mk @@ -0,0 +1,14 @@ +QEMU_ARCH = riscv32 +QEMU_MACHINE = virt + +CFLAGS += -DQEMU_SOC_VIRT +CFLAGS += -DMICROPY_HW_MCU_NAME='"$(RV32_ARCH)"' + +LDSCRIPT = mcu/rv32/virt.ld + +SRC_BOARD_O += shared/runtime/gchelper_native.o shared/runtime/gchelper_rv32i.o + +MPY_CROSS_FLAGS += -march=rv32imc + +# These Thumb tests don't run on RV32, so exclude them. +RUN_TESTS_ARGS = --exclude 'inlineasm|qemu-arm/asm_test' diff --git a/ports/qemu-arm/imx6.ld b/ports/qemu-arm/mcu/arm/imx6.ld similarity index 100% rename from ports/qemu-arm/imx6.ld rename to ports/qemu-arm/mcu/arm/imx6.ld diff --git a/ports/qemu-arm/mps2.ld b/ports/qemu-arm/mcu/arm/mps2.ld similarity index 100% rename from ports/qemu-arm/mps2.ld rename to ports/qemu-arm/mcu/arm/mps2.ld diff --git a/ports/qemu-arm/nrf51.ld b/ports/qemu-arm/mcu/arm/nrf51.ld similarity index 100% rename from ports/qemu-arm/nrf51.ld rename to ports/qemu-arm/mcu/arm/nrf51.ld diff --git a/ports/qemu-arm/startup.c b/ports/qemu-arm/mcu/arm/startup.c similarity index 100% rename from ports/qemu-arm/startup.c rename to ports/qemu-arm/mcu/arm/startup.c diff --git a/ports/qemu-arm/stm32.ld b/ports/qemu-arm/mcu/arm/stm32.ld similarity index 100% rename from ports/qemu-arm/stm32.ld rename to ports/qemu-arm/mcu/arm/stm32.ld diff --git a/ports/qemu-riscv/entrypoint.s b/ports/qemu-arm/mcu/rv32/entrypoint.s similarity index 100% rename from ports/qemu-riscv/entrypoint.s rename to ports/qemu-arm/mcu/rv32/entrypoint.s diff --git a/ports/qemu-riscv/interrupts.c b/ports/qemu-arm/mcu/rv32/interrupts.c similarity index 100% rename from ports/qemu-riscv/interrupts.c rename to ports/qemu-arm/mcu/rv32/interrupts.c diff --git a/ports/qemu-riscv/startup.c b/ports/qemu-arm/mcu/rv32/startup.c similarity index 100% rename from ports/qemu-riscv/startup.c rename to ports/qemu-arm/mcu/rv32/startup.c diff --git a/ports/qemu-riscv/virt.ld b/ports/qemu-arm/mcu/rv32/virt.ld similarity index 100% rename from ports/qemu-riscv/virt.ld rename to ports/qemu-arm/mcu/rv32/virt.ld diff --git a/ports/qemu-arm/mpconfigport.h b/ports/qemu-arm/mpconfigport.h index 4059a5926d3cd..fe12fa57801d5 100644 --- a/ports/qemu-arm/mpconfigport.h +++ b/ports/qemu-arm/mpconfigport.h @@ -37,6 +37,8 @@ #define MICROPY_EMIT_THUMB (1) #define MICROPY_EMIT_INLINE_THUMB (1) #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) +#elif defined(__riscv) +#define MICROPY_EMIT_RV32 (1) #endif #define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) @@ -47,7 +49,11 @@ #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_WARNINGS (1) #define MICROPY_PY_IO_IOBASE (0) +#if defined(__ARM_ARCH) #define MICROPY_PY_SYS_PLATFORM "qemu-arm" +#elif defined(__riscv) +#define MICROPY_PY_SYS_PLATFORM "qemu-riscv32" +#endif #define MICROPY_PY_SYS_STDIO_BUFFER (0) #define MICROPY_PY_SELECT (0) #define MICROPY_PY_TIME (0) diff --git a/ports/qemu-arm/mphalport.h b/ports/qemu-arm/mphalport.h index 348b45701b89e..9355107495373 100644 --- a/ports/qemu-arm/mphalport.h +++ b/ports/qemu-arm/mphalport.h @@ -24,4 +24,5 @@ * THE SOFTWARE. */ +#include #include "shared/runtime/interrupt_char.h" diff --git a/ports/qemu-arm/uart.c b/ports/qemu-arm/uart.c index 5ace3d465f147..fb9376d1fb71d 100644 --- a/ports/qemu-arm/uart.c +++ b/ports/qemu-arm/uart.c @@ -3,7 +3,8 @@ * * The MIT License (MIT) * - * Copyright (c) 2018-2021 Damien P. George + * Copyright (c) 2018-2024 Damien P. George + * Copyright (c) 2023 Alessandro Gatti * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -175,4 +176,36 @@ void uart_tx_strn(const char *buf, size_t len) { } } +#elif defined(QEMU_SOC_VIRT) + +// Line status register bits. +#define UART_LSR_THRE (0x20) +#define UART_LSR_DR (0x01) + +typedef struct _UART_t { + volatile uint8_t DR; + volatile uint8_t r0[4]; + volatile uint8_t LSR; +} UART_t; + +#define UART0 ((UART_t *)(0x10000000)) + +void uart_init(void) { +} + +int uart_rx_chr(void) { + if (UART0->LSR & UART_LSR_DR) { + return UART0->DR; + } + return UART_RX_NO_CHAR; +} + +void uart_tx_strn(const char *buffer, size_t length) { + for (size_t index = 0; index < length; index++) { + while (!(UART0->LSR & UART_LSR_THRE)) { + } + UART0->DR = buffer[index]; + } +} + #endif diff --git a/ports/qemu-riscv/Makefile b/ports/qemu-riscv/Makefile deleted file mode 100644 index 473aec882d640..0000000000000 --- a/ports/qemu-riscv/Makefile +++ /dev/null @@ -1,128 +0,0 @@ -include ../../py/mkenv.mk --include mpconfigport.mk - -# qstr definitions (must come before including py.mk) -QSTR_DEFS = qstrdefsport.h - -# MicroPython feature configurations -MICROPY_ROM_TEXT_COMPRESSION ?= 1 - -# include py core make definitions -include $(TOP)/py/py.mk -include $(TOP)/extmod/extmod.mk - -BOARD ?= virt - -CROSS_COMPILE ?= riscv64-unknown-elf- - -GCC_VERSION = $(word 1, $(subst ., , $(shell $(CC) -dumpversion))) - -# If Picolibc is available then select it explicitly. Ubuntu 22.04 ships its -# bare metal RISC-V toolchain with Picolibc rather than Newlib, and the default -# is "nosys" so a value must be provided. To avoid having per-distro -# workarounds, always select Picolibc if available. -PICOLIBC_SPECS = $(shell $(CC) --print-file-name=picolibc.specs) -ifeq ($(PICOLIBC_SPECS),picolibc.specs) -# Picolibc was not found. -SPECS_FRAGMENT = -else -SPECS_FRAGMENT = --specs=$(PICOLIBC_SPECS) -CFLAGS += $(SPECS_FRAGMENT) -endif - -ifeq ($(BOARD),virt) -ABI = ilp32 -# GCC 10 and lower do not recognise the Zicsr extension in the -# architecture name. "Make" unfortunately does not provide any simple way -# to perform numeric comparisons, so to keep things simple we assume that -# GCC is at least version 10 for the time being. -ifeq ($(GCC_VERSION),10) -ARCH ?= rv32imac -else -# Recent GCC versions explicitly require to declare extensions. -ARCH ?= rv32imac_zicsr -ARCH_LD ?= rv32imac_zicsr -endif -AFLAGS = -mabi=$(ABI) -march=$(ARCH) -CFLAGS += $(AFLAGS) -CFLAGS += -DQEMU_SOC_VIRT -ARCH_LD ?= $(ARCH) -LDSCRIPT = virt.ld -LDFLAGS += -mabi=$(ABI) -march=$(ARCH_LD) -T $(LDSCRIPT) -Wl,-EL -SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_rv32i.o -SRC_BOARD_O += entrypoint.o -endif - -INC += -I. -INC += -I$(TOP) -INC += -I$(BUILD) - -CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Werror -std=gnu99 $(COPT) \ - -ffunction-sections -fdata-sections -CFLAGS += $(CFLAGS_EXTRA) - -LD = $(CC) - -# Debugging/Optimization -ifeq ($(DEBUG), 1) -CFLAGS += -g -COPT = -O0 -else -COPT += -Os -DNDEBUG -endif - -LDFLAGS += $(SPECS_FRAGMENT) -Wl,--gc-sections -Wl,-Map=$(@:.elf=.map) - -SRC_COMMON_C = \ - interrupts.c \ - startup.c \ - uart.c \ - shared/libc/string0.c \ - shared/runtime/sys_stdio_mphal.c \ - -SRC_RUN_C = \ - main.c \ - -SRC_TEST_C = \ - test_main.c \ - lib/tinytest/tinytest.c \ - -LIB_SRC_C += $(SRC_LIB_LIBM_C) -LIB_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C) - -OBJ_COMMON = -OBJ_COMMON += $(PY_O) -OBJ_COMMON += $(addprefix $(BUILD)/, $(SRC_COMMON_C:.c=.o)) -OBJ_COMMON += $(addprefix $(BUILD)/, $(SRC_BOARD_O)) -OBJ_COMMON += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) - -OBJ_RUN = -OBJ_RUN += $(addprefix $(BUILD)/, $(SRC_RUN_C:.c=.o)) - -ALL_OBJ_RUN = $(OBJ_COMMON) $(OBJ_RUN) - -OBJ_TEST = -OBJ_TEST += $(addprefix $(BUILD)/, $(SRC_TEST_C:.c=.o)) - -ALL_OBJ_TEST = $(OBJ_COMMON) $(OBJ_TEST) - -# All object files, needed to get dependencies correct -OBJ = $(OBJ_COMMON) $(OBJ_RUN) $(OBJ_TEST) - -# List of sources for qstr extraction -SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C) - -all: run - -# `make debug` will block QEMU until a debugger is connected to port 1234. -debug: $(BUILD)/firmware.elf - qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -serial mon:stdio -S -s -kernel $< - -run: $(BUILD)/firmware.elf - qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< - -$(BUILD)/firmware.elf: $(LDSCRIPT) $(ALL_OBJ_RUN) - $(Q)$(LD) $(LDFLAGS) -o $@ $(ALL_OBJ_RUN) $(LIBS) - $(Q)$(SIZE) $@ - -include $(TOP)/py/mkrules.mk diff --git a/ports/qemu-riscv/Makefile.test b/ports/qemu-riscv/Makefile.test deleted file mode 100644 index df64a8d7cc920..0000000000000 --- a/ports/qemu-riscv/Makefile.test +++ /dev/null @@ -1,31 +0,0 @@ -LIB_SRC_C = shared/upytesthelper/upytesthelper.c - -include Makefile - -CFLAGS += -DTEST - -.PHONY: $(BUILD)/genhdr/tests.h - -TESTS_PROFILE = $(dir $(abspath $(firstword $(MAKEFILE_LIST))))/tests_profile.txt - -$(BUILD)/test_main.o: $(BUILD)/genhdr/tests.h -$(BUILD)/genhdr/tests.h: - (cd $(TOP)/tests; ./run-tests.py --target=qemu-riscv --write-exp) - $(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py --profile $(TESTS_PROFILE) $(addprefix --exclude ,$(TESTS_EXCLUDE))) > $@ - -$(BUILD)/lib/tinytest/tinytest.o: CFLAGS += -DNO_FORKING - -$(BUILD)/firmware-test.elf: $(LDSCRIPT) $(ALL_OBJ_TEST) - $(Q)$(LD) $(LDFLAGS) -o $@ $(ALL_OBJ_TEST) $(LIBS) - $(Q)$(SIZE) $@ - -# Note: Using timeout(1) to handle cases where qemu hangs (e.g. this can happen with alignment errors). -test: $(BUILD)/firmware-test.elf - timeout --foreground -k 5s 60s qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< > $(BUILD)/console.out - $(Q)tail -n2 $(BUILD)/console.out - $(Q)tail -n1 $(BUILD)/console.out | grep -q "status: 0" - -# `make debugtest` will block QEMU until a debugger is connected to port 1234. - -debugtest: $(BUILD)/firmware-test.elf - qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -serial mon:stdio -S -s -kernel $< diff --git a/ports/qemu-riscv/README.md b/ports/qemu-riscv/README.md deleted file mode 100644 index 50b1a65374d2b..0000000000000 --- a/ports/qemu-riscv/README.md +++ /dev/null @@ -1,29 +0,0 @@ -This is an experimental, community-supported port for RISC-V RV32IMC emulation -as provided by QEMU (http://qemu.org). - -The purposes of this port are to enable: - -1. Continuous integration - - run tests against architecture-specific parts of code base -2. Experimentation - - simulation & prototyping of anything that has architecture-specific - code - - exploring instruction set in terms of optimising some part of - MicroPython or a module -3. Streamlined debugging - - no need for JTAG or even an MCU chip itself - - no need to use OpenOCD or anything else that might slow down the - process in terms of plugging things together, pressing buttons, etc. - -This port requires a bare metal RISC-V toolchain with GCC 10 or later, either -with multilib support or 32 bits specific (M, C, and Zicsr extensions must be -supported, along with ilp32 ABI). Both newlib and picolibc are supported, -with the latter having precedence if found. - -Most pre-built toolchains should work out of the box, either coming from your -Linux distribution's package manager, or independently packaged ones like -[xPack](https://xpack.github.io/dev-tools/riscv-none-elf-gcc/). - -To build and run the image with builtin testsuite: - - make -f Makefile.test test diff --git a/ports/qemu-riscv/main.c b/ports/qemu-riscv/main.c deleted file mode 100644 index 025c1f17da04a..0000000000000 --- a/ports/qemu-riscv/main.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2014-2023 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 -#include - -#include "py/compile.h" -#include "py/runtime.h" -#include "py/stackctrl.h" -#include "py/gc.h" -#include "py/mperrno.h" - -void do_str(const char *src, mp_parse_input_kind_t input_kind) { - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); - qstr source_name = lex->source_name; - mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true); - mp_call_function_0(module_fun); - nlr_pop(); - } else { - // uncaught exception - mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); - } -} - -int main(int argc, char **argv) { - mp_stack_ctrl_init(); - mp_stack_set_limit(10240); - uint32_t heap[16 * 1024 / 4]; - gc_init(heap, (char *)heap + 16 * 1024); - mp_init(); - do_str("print('hello world!')", MP_PARSE_SINGLE_INPUT); - mp_deinit(); - return 0; -} - -void gc_collect(void) { -} - -mp_lexer_t *mp_lexer_new_from_file(qstr filename) { - mp_raise_OSError(MP_ENOENT); -} - -void nlr_jump_fail(void *val) { - printf("uncaught NLR\n"); - exit(1); -} diff --git a/ports/qemu-riscv/mpconfigport.h b/ports/qemu-riscv/mpconfigport.h deleted file mode 100644 index 38bc70e986adb..0000000000000 --- a/ports/qemu-riscv/mpconfigport.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2014-2024 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 - -// options to control how MicroPython is built - -#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) - -#define MICROPY_EMIT_RV32 (1) -#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) -#define MICROPY_MEM_STATS (1) -#define MICROPY_ENABLE_GC (1) -#define MICROPY_KBD_EXCEPTION (0) -#define MICROPY_HELPER_REPL (0) -#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) -#define MICROPY_WARNINGS (1) -#define MICROPY_PY_BUILTINS_INPUT (0) -#define MICROPY_PY_BUILTINS_HELP (0) -#define MICROPY_PY_IO_IOBASE (0) -#define MICROPY_PY_SYS_PLATFORM "qemu-riscv32" -#define MICROPY_PY_SYS_STDFILES (0) -#define MICROPY_PY_SYS_STDIO_BUFFER (0) -#define MICROPY_PY_SELECT (0) -#define MICROPY_PY_TIME (0) -#define MICROPY_PY_ASYNCIO (0) -#define MICROPY_PY_MACHINE (1) -#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/qemu-arm/modmachine.c" -#define MICROPY_PY_MACHINE_PIN_BASE (1) -#define MICROPY_VFS (1) - -// type definitions for the specific machine - -#define MP_SSIZE_MAX (0x7fffffff) - -#define UINT_FMT "%lu" -#define INT_FMT "%ld" - -typedef int32_t mp_int_t; // must be pointer size -typedef uint32_t mp_uint_t; // must be pointer size -typedef long mp_off_t; - -// We need an implementation of the log2 function which is not a macro. -#define MP_NEED_LOG2 (1) - -// We need to provide a declaration/definition of alloca() -#include - -#ifdef TEST -#include "shared/upytesthelper/upytesthelper.h" -#undef MP_PLAT_PRINT_STRN -#define MP_PLAT_PRINT_STRN(str, len) upytest_output(str, len) -#endif diff --git a/ports/qemu-riscv/mphalport.h b/ports/qemu-riscv/mphalport.h deleted file mode 100644 index 0cbdfc039144b..0000000000000 --- a/ports/qemu-riscv/mphalport.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2016-2018 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 - -#include "uart.h" - -#define mp_hal_stdin_rx_chr() (0) -#define mp_hal_stdout_tx_strn_cooked(s, l) uart_tx_strn((s), (l)) diff --git a/ports/qemu-riscv/qstrdefsport.h b/ports/qemu-riscv/qstrdefsport.h deleted file mode 100644 index 00d3e2ae3c555..0000000000000 --- a/ports/qemu-riscv/qstrdefsport.h +++ /dev/null @@ -1,2 +0,0 @@ -// qstrs specific to this port -// *FORMAT-OFF* diff --git a/ports/qemu-riscv/test_main.c b/ports/qemu-riscv/test_main.c deleted file mode 100644 index ca796766aca5a..0000000000000 --- a/ports/qemu-riscv/test_main.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2014 Ilya Dmitrichenko - * - * 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 -#include - -#include "py/compile.h" -#include "py/runtime.h" -#include "py/stackctrl.h" -#include "py/gc.h" -#include "py/mperrno.h" -#include "shared/runtime/gchelper.h" -#include "lib/tinytest/tinytest.h" -#include "lib/tinytest/tinytest_macros.h" - -#define HEAP_SIZE (200 * 1024) - -#include "genhdr/tests.h" - -int main() { - mp_stack_ctrl_init(); - mp_stack_set_limit(10240); - static uint32_t heap[HEAP_SIZE / sizeof(uint32_t)]; - upytest_set_heap(heap, (char *)heap + HEAP_SIZE); - int r = tinytest_main(0, NULL, groups); - printf("status: %d\n", r); - return r; -} - -void gc_collect(void) { - gc_collect_start(); - gc_helper_collect_regs_and_stack(); - gc_collect_end(); -} - -mp_lexer_t *mp_lexer_new_from_file(qstr filename) { - mp_raise_OSError(MP_ENOENT); -} - -void nlr_jump_fail(void *val) { - printf("uncaught NLR\n"); - exit(1); -} diff --git a/ports/qemu-riscv/tests_profile.txt b/ports/qemu-riscv/tests_profile.txt deleted file mode 100644 index 3d16e971be01e..0000000000000 --- a/ports/qemu-riscv/tests_profile.txt +++ /dev/null @@ -1,7 +0,0 @@ -# Port-specific test directories. - -test_dirs.update(()) - -# Port-specific tests exclusion list. - -exclude_tests.update(()) diff --git a/ports/qemu-riscv/uart.c b/ports/qemu-riscv/uart.c deleted file mode 100644 index a266d6e6fdc8b..0000000000000 --- a/ports/qemu-riscv/uart.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2023 Alessandro Gatti - * - * 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 -#include - -#include "uart.h" - -#if defined(QEMU_SOC_VIRT) - -volatile unsigned char *uart_buffer = (volatile unsigned char *)0x10000000; - -void uart_init(void) { -} - -void uart_tx_strn(const char *buffer, size_t length) { - for (size_t index = 0; index < length; index++) { - *uart_buffer = buffer[index]; - } -} - -#endif // QEMU_SOC_VIRT diff --git a/ports/qemu-riscv/uart.h b/ports/qemu-riscv/uart.h deleted file mode 100644 index fdaae424e19d1..0000000000000 --- a/ports/qemu-riscv/uart.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 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. - */ - -#ifndef MICROPY_INCLUDED_QEMU_RISCV_UART_H -#define MICROPY_INCLUDED_QEMU_RISCV_UART_H - -void uart_init(void); -void uart_tx_strn(const char *buf, size_t len); - -#endif // MICROPY_INCLUDED_QEMU_RISCV_UART_H diff --git a/tools/ci.sh b/tools/ci.sh index abfb965b543aa..70fdb487eeba2 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -246,14 +246,21 @@ function ci_powerpc_build { ######################################################################################## # ports/qemu-arm -function ci_qemu_arm_setup { +function ci_qemu_setup_arm { ci_gcc_arm_setup sudo apt-get update sudo apt-get install qemu-system qemu-system-arm --version } -function ci_qemu_arm_build { +function ci_qemu_setup_rv32 { + ci_gcc_riscv_setup + sudo apt-get update + sudo apt-get install qemu-system + qemu-system-riscv32 --version +} + +function ci_qemu_build_arm { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/qemu-arm submodules make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 @@ -262,23 +269,10 @@ function ci_qemu_arm_build { make ${MAKEOPTS} -C ports/qemu-arm BOARD=SABRELITE test } -######################################################################################## -# ports/qemu-riscv - -function ci_qemu_riscv_setup { - ci_gcc_riscv_setup - sudo apt-get update - sudo apt-get install qemu-system - qemu-system-riscv32 --version -} - -function ci_qemu_riscv_build { +function ci_qemu_build_rv32 { make ${MAKEOPTS} -C mpy-cross - make ${MAKEOPTS} -C ports/qemu-riscv submodules - make ${MAKEOPTS} -C ports/qemu-riscv - make ${MAKEOPTS} -C ports/qemu-riscv clean - make ${MAKEOPTS} -C ports/qemu-riscv -f Makefile.test submodules - make ${MAKEOPTS} -C ports/qemu-riscv -f Makefile.test test + make ${MAKEOPTS} -C ports/qemu-arm BOARD=VIRT_RV32 submodules + make ${MAKEOPTS} -C ports/qemu-arm BOARD=VIRT_RV32 test } ######################################################################################## From 3ea1ce63da9bd17c7d395682387e58b4a7759167 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 6 Sep 2024 11:04:13 +1000 Subject: [PATCH 0288/1300] all: Remove remaining qemu-riscv references. Signed-off-by: Damien George --- README.md | 1 - tests/run-tests.py | 9 --------- 2 files changed, 10 deletions(-) diff --git a/README.md b/README.md index 02e0b3abb357b..a4086c68a6e9c 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,6 @@ In addition, the following ports are provided in this repository: - [pic16bit](ports/pic16bit) -- Microchip PIC 16-bit. - [powerpc](ports/powerpc) -- IBM PowerPC (including Microwatt) - [qemu-arm](ports/qemu-arm) -- QEMU-based Arm emulated target (for testing) - - [qemu-riscv](ports/qemu-riscv) -- QEMU-based RISC-V emulated target (for testing) - [renesas-ra](ports/renesas-ra) -- Renesas RA family. - [rp2](ports/rp2) -- Raspberry Pi RP2040 (including Pico and Pico W). - [samd](ports/samd) -- Microchip (formerly Atmel) SAMD21 and SAMD51. diff --git a/tests/run-tests.py b/tests/run-tests.py index 999812bd5db9f..b80a78ed6a059 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -681,8 +681,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("inlineasm/asmfpldrstr.py") skip_tests.add("inlineasm/asmfpmuldiv.py") skip_tests.add("inlineasm/asmfpsqrt.py") - elif args.target == "qemu-riscv": - skip_tests.add("misc/print_exception.py") # requires sys stdfiles elif args.target == "webassembly": skip_tests.add("basics/string_format_modulo.py") # can't print nulls to stdout skip_tests.add("basics/string_strip.py") # can't print nulls to stdout @@ -1047,7 +1045,6 @@ def main(): LOCAL_TARGETS = ( "unix", - "qemu-riscv", "webassembly", ) EXTERNAL_TARGETS = ( @@ -1140,12 +1137,6 @@ def main(): "inlineasm", "ports/qemu-arm", ) - elif args.target == "qemu-riscv": - if not args.write_exp: - raise ValueError("--target=qemu-riscv must be used with --write-exp") - # Generate expected output files for qemu run. - # This list should match the test_dirs tuple in tinytest-codegen.py. - test_dirs += ("float",) elif args.target == "webassembly": test_dirs += ("float", "ports/webassembly") else: From 659113825d10bf2ae71dd215a8597451e505982d Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 4 Sep 2024 17:06:25 +1000 Subject: [PATCH 0289/1300] qemu: Rename qemu-arm port to qemu. Because this port now supports multiple architectures. Signed-off-by: Damien George --- .../{ports_qemu-arm.yml => ports_qemu.yml} | 4 ++-- README.md | 2 +- ports/{qemu-arm => qemu}/Makefile | 2 +- ports/{qemu-arm => qemu}/README.md | 6 +++--- ports/{qemu-arm => qemu}/boards/MICROBIT.mk | 0 ports/{qemu-arm => qemu}/boards/MPS2_AN385.mk | 0 ports/{qemu-arm => qemu}/boards/NETDUINO2.mk | 0 ports/{qemu-arm => qemu}/boards/SABRELITE.mk | 0 ports/{qemu-arm => qemu}/boards/VIRT_RV32.mk | 2 +- ports/{qemu-arm => qemu}/main.c | 0 ports/{qemu-arm => qemu}/mcu/arm/imx6.ld | 0 ports/{qemu-arm => qemu}/mcu/arm/mps2.ld | 0 ports/{qemu-arm => qemu}/mcu/arm/nrf51.ld | 0 ports/{qemu-arm => qemu}/mcu/arm/startup.c | 0 ports/{qemu-arm => qemu}/mcu/arm/stm32.ld | 0 ports/{qemu-arm => qemu}/mcu/rv32/entrypoint.s | 0 ports/{qemu-arm => qemu}/mcu/rv32/interrupts.c | 0 ports/{qemu-arm => qemu}/mcu/rv32/startup.c | 0 ports/{qemu-arm => qemu}/mcu/rv32/virt.ld | 0 ports/{qemu-arm => qemu}/modmachine.c | 0 ports/{qemu-arm => qemu}/mpconfigport.h | 8 ++------ ports/{qemu-arm => qemu}/mphalport.c | 0 ports/{qemu-arm => qemu}/mphalport.h | 0 ports/{qemu-arm => qemu}/qstrdefsport.h | 0 .../{qemu-arm => qemu}/test-frzmpy/frozen_asm.py | 0 .../test-frzmpy/frozen_const.py | 0 .../test-frzmpy/frozen_viper.py | 0 .../test-frzmpy/native_frozen_align.py | 0 ports/{qemu-arm => qemu}/uart.c | 0 ports/{qemu-arm => qemu}/uart.h | 0 tests/float/float_format_ints_power10.py | 2 +- tests/ports/{qemu-arm => qemu}/asm_test.py | 0 tests/ports/{qemu-arm => qemu}/asm_test.py.exp | 0 tests/ports/{qemu-arm => qemu}/native_test.py | 0 .../ports/{qemu-arm => qemu}/native_test.py.exp | 0 tests/ports/{qemu-arm => qemu}/viper_test.py | 0 tests/ports/{qemu-arm => qemu}/viper_test.py.exp | 0 tests/run-tests.py | 8 ++++---- tools/ci.sh | 16 ++++++++-------- 39 files changed, 23 insertions(+), 27 deletions(-) rename .github/workflows/{ports_qemu-arm.yml => ports_qemu.yml} (95%) rename ports/{qemu-arm => qemu}/Makefile (96%) rename ports/{qemu-arm => qemu}/README.md (97%) rename ports/{qemu-arm => qemu}/boards/MICROBIT.mk (100%) rename ports/{qemu-arm => qemu}/boards/MPS2_AN385.mk (100%) rename ports/{qemu-arm => qemu}/boards/NETDUINO2.mk (100%) rename ports/{qemu-arm => qemu}/boards/SABRELITE.mk (100%) rename ports/{qemu-arm => qemu}/boards/VIRT_RV32.mk (84%) rename ports/{qemu-arm => qemu}/main.c (100%) rename ports/{qemu-arm => qemu}/mcu/arm/imx6.ld (100%) rename ports/{qemu-arm => qemu}/mcu/arm/mps2.ld (100%) rename ports/{qemu-arm => qemu}/mcu/arm/nrf51.ld (100%) rename ports/{qemu-arm => qemu}/mcu/arm/startup.c (100%) rename ports/{qemu-arm => qemu}/mcu/arm/stm32.ld (100%) rename ports/{qemu-arm => qemu}/mcu/rv32/entrypoint.s (100%) rename ports/{qemu-arm => qemu}/mcu/rv32/interrupts.c (100%) rename ports/{qemu-arm => qemu}/mcu/rv32/startup.c (100%) rename ports/{qemu-arm => qemu}/mcu/rv32/virt.ld (100%) rename ports/{qemu-arm => qemu}/modmachine.c (100%) rename ports/{qemu-arm => qemu}/mpconfigport.h (92%) rename ports/{qemu-arm => qemu}/mphalport.c (100%) rename ports/{qemu-arm => qemu}/mphalport.h (100%) rename ports/{qemu-arm => qemu}/qstrdefsport.h (100%) rename ports/{qemu-arm => qemu}/test-frzmpy/frozen_asm.py (100%) rename ports/{qemu-arm => qemu}/test-frzmpy/frozen_const.py (100%) rename ports/{qemu-arm => qemu}/test-frzmpy/frozen_viper.py (100%) rename ports/{qemu-arm => qemu}/test-frzmpy/native_frozen_align.py (100%) rename ports/{qemu-arm => qemu}/uart.c (100%) rename ports/{qemu-arm => qemu}/uart.h (100%) rename tests/ports/{qemu-arm => qemu}/asm_test.py (100%) rename tests/ports/{qemu-arm => qemu}/asm_test.py.exp (100%) rename tests/ports/{qemu-arm => qemu}/native_test.py (100%) rename tests/ports/{qemu-arm => qemu}/native_test.py.exp (100%) rename tests/ports/{qemu-arm => qemu}/viper_test.py (100%) rename tests/ports/{qemu-arm => qemu}/viper_test.py.exp (100%) diff --git a/.github/workflows/ports_qemu-arm.yml b/.github/workflows/ports_qemu.yml similarity index 95% rename from .github/workflows/ports_qemu-arm.yml rename to .github/workflows/ports_qemu.yml index 147d1b4b94231..57192c43936e5 100644 --- a/.github/workflows/ports_qemu-arm.yml +++ b/.github/workflows/ports_qemu.yml @@ -1,4 +1,4 @@ -name: qemu-arm port +name: qemu port on: push: @@ -11,7 +11,7 @@ on: - 'shared/**' - 'lib/**' - 'drivers/**' - - 'ports/qemu-arm/**' + - 'ports/qemu/**' - 'tests/**' concurrency: diff --git a/README.md b/README.md index a4086c68a6e9c..c71fa4b1ddf48 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ In addition, the following ports are provided in this repository: - [nrf](ports/nrf) -- Nordic Semiconductor nRF51 and nRF52. - [pic16bit](ports/pic16bit) -- Microchip PIC 16-bit. - [powerpc](ports/powerpc) -- IBM PowerPC (including Microwatt) - - [qemu-arm](ports/qemu-arm) -- QEMU-based Arm emulated target (for testing) + - [qemu](ports/qemu) -- QEMU-based emulated target (for testing) - [renesas-ra](ports/renesas-ra) -- Renesas RA family. - [rp2](ports/rp2) -- Raspberry Pi RP2040 (including Pico and Pico W). - [samd](ports/samd) -- Microchip (formerly Atmel) SAMD21 and SAMD51. diff --git a/ports/qemu-arm/Makefile b/ports/qemu/Makefile similarity index 96% rename from ports/qemu-arm/Makefile rename to ports/qemu/Makefile index 605b6b572c411..4d73c025d7dc9 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu/Makefile @@ -166,7 +166,7 @@ run: $(BUILD)/firmware.elf .PHONY: test test: $(BUILD)/firmware.elf $(eval DIRNAME=ports/$(notdir $(CURDIR))) - cd $(TOP)/tests && ./run-tests.py --target qemu-arm --device execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_ARGS) $(RUN_TESTS_EXTRA) + cd $(TOP)/tests && ./run-tests.py --target qemu --device execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_ARGS) $(RUN_TESTS_EXTRA) $(BUILD)/firmware.elf: $(LDSCRIPT) $(OBJ) $(Q)$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) diff --git a/ports/qemu-arm/README.md b/ports/qemu/README.md similarity index 97% rename from ports/qemu-arm/README.md rename to ports/qemu/README.md index 2e543e77ecc19..d9108311bef92 100644 --- a/ports/qemu-arm/README.md +++ b/ports/qemu/README.md @@ -1,5 +1,5 @@ -MicroPython port to qemu-arm -============================ +MicroPython port to qemu +======================== This is experimental, community-supported port for Cortex-M and RISC-V RV32IMC emulation as provided by QEMU (http://qemu.org). @@ -95,7 +95,7 @@ Or manually by first starting the emulation with `make run` and then running the tests against the serial device, for example: $ cd ../../tests - $ ./run-tests.py --target qemu-arm --device /dev/pts/1 + $ ./run-tests.py --target qemu --device /dev/pts/1 Extra make options ------------------ diff --git a/ports/qemu-arm/boards/MICROBIT.mk b/ports/qemu/boards/MICROBIT.mk similarity index 100% rename from ports/qemu-arm/boards/MICROBIT.mk rename to ports/qemu/boards/MICROBIT.mk diff --git a/ports/qemu-arm/boards/MPS2_AN385.mk b/ports/qemu/boards/MPS2_AN385.mk similarity index 100% rename from ports/qemu-arm/boards/MPS2_AN385.mk rename to ports/qemu/boards/MPS2_AN385.mk diff --git a/ports/qemu-arm/boards/NETDUINO2.mk b/ports/qemu/boards/NETDUINO2.mk similarity index 100% rename from ports/qemu-arm/boards/NETDUINO2.mk rename to ports/qemu/boards/NETDUINO2.mk diff --git a/ports/qemu-arm/boards/SABRELITE.mk b/ports/qemu/boards/SABRELITE.mk similarity index 100% rename from ports/qemu-arm/boards/SABRELITE.mk rename to ports/qemu/boards/SABRELITE.mk diff --git a/ports/qemu-arm/boards/VIRT_RV32.mk b/ports/qemu/boards/VIRT_RV32.mk similarity index 84% rename from ports/qemu-arm/boards/VIRT_RV32.mk rename to ports/qemu/boards/VIRT_RV32.mk index ec3535eed3b06..a61b659fa6d40 100644 --- a/ports/qemu-arm/boards/VIRT_RV32.mk +++ b/ports/qemu/boards/VIRT_RV32.mk @@ -11,4 +11,4 @@ SRC_BOARD_O += shared/runtime/gchelper_native.o shared/runtime/gchelper_rv32i.o MPY_CROSS_FLAGS += -march=rv32imc # These Thumb tests don't run on RV32, so exclude them. -RUN_TESTS_ARGS = --exclude 'inlineasm|qemu-arm/asm_test' +RUN_TESTS_ARGS = --exclude 'inlineasm|qemu/asm_test' diff --git a/ports/qemu-arm/main.c b/ports/qemu/main.c similarity index 100% rename from ports/qemu-arm/main.c rename to ports/qemu/main.c diff --git a/ports/qemu-arm/mcu/arm/imx6.ld b/ports/qemu/mcu/arm/imx6.ld similarity index 100% rename from ports/qemu-arm/mcu/arm/imx6.ld rename to ports/qemu/mcu/arm/imx6.ld diff --git a/ports/qemu-arm/mcu/arm/mps2.ld b/ports/qemu/mcu/arm/mps2.ld similarity index 100% rename from ports/qemu-arm/mcu/arm/mps2.ld rename to ports/qemu/mcu/arm/mps2.ld diff --git a/ports/qemu-arm/mcu/arm/nrf51.ld b/ports/qemu/mcu/arm/nrf51.ld similarity index 100% rename from ports/qemu-arm/mcu/arm/nrf51.ld rename to ports/qemu/mcu/arm/nrf51.ld diff --git a/ports/qemu-arm/mcu/arm/startup.c b/ports/qemu/mcu/arm/startup.c similarity index 100% rename from ports/qemu-arm/mcu/arm/startup.c rename to ports/qemu/mcu/arm/startup.c diff --git a/ports/qemu-arm/mcu/arm/stm32.ld b/ports/qemu/mcu/arm/stm32.ld similarity index 100% rename from ports/qemu-arm/mcu/arm/stm32.ld rename to ports/qemu/mcu/arm/stm32.ld diff --git a/ports/qemu-arm/mcu/rv32/entrypoint.s b/ports/qemu/mcu/rv32/entrypoint.s similarity index 100% rename from ports/qemu-arm/mcu/rv32/entrypoint.s rename to ports/qemu/mcu/rv32/entrypoint.s diff --git a/ports/qemu-arm/mcu/rv32/interrupts.c b/ports/qemu/mcu/rv32/interrupts.c similarity index 100% rename from ports/qemu-arm/mcu/rv32/interrupts.c rename to ports/qemu/mcu/rv32/interrupts.c diff --git a/ports/qemu-arm/mcu/rv32/startup.c b/ports/qemu/mcu/rv32/startup.c similarity index 100% rename from ports/qemu-arm/mcu/rv32/startup.c rename to ports/qemu/mcu/rv32/startup.c diff --git a/ports/qemu-arm/mcu/rv32/virt.ld b/ports/qemu/mcu/rv32/virt.ld similarity index 100% rename from ports/qemu-arm/mcu/rv32/virt.ld rename to ports/qemu/mcu/rv32/virt.ld diff --git a/ports/qemu-arm/modmachine.c b/ports/qemu/modmachine.c similarity index 100% rename from ports/qemu-arm/modmachine.c rename to ports/qemu/modmachine.c diff --git a/ports/qemu-arm/mpconfigport.h b/ports/qemu/mpconfigport.h similarity index 92% rename from ports/qemu-arm/mpconfigport.h rename to ports/qemu/mpconfigport.h index fe12fa57801d5..f3dd8c37508d3 100644 --- a/ports/qemu-arm/mpconfigport.h +++ b/ports/qemu/mpconfigport.h @@ -49,17 +49,13 @@ #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_WARNINGS (1) #define MICROPY_PY_IO_IOBASE (0) -#if defined(__ARM_ARCH) -#define MICROPY_PY_SYS_PLATFORM "qemu-arm" -#elif defined(__riscv) -#define MICROPY_PY_SYS_PLATFORM "qemu-riscv32" -#endif +#define MICROPY_PY_SYS_PLATFORM "qemu" #define MICROPY_PY_SYS_STDIO_BUFFER (0) #define MICROPY_PY_SELECT (0) #define MICROPY_PY_TIME (0) #define MICROPY_PY_ASYNCIO (0) #define MICROPY_PY_MACHINE (1) -#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/qemu-arm/modmachine.c" +#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/qemu/modmachine.c" #define MICROPY_PY_MACHINE_RESET (1) #define MICROPY_PY_MACHINE_PIN_BASE (1) #define MICROPY_VFS (1) diff --git a/ports/qemu-arm/mphalport.c b/ports/qemu/mphalport.c similarity index 100% rename from ports/qemu-arm/mphalport.c rename to ports/qemu/mphalport.c diff --git a/ports/qemu-arm/mphalport.h b/ports/qemu/mphalport.h similarity index 100% rename from ports/qemu-arm/mphalport.h rename to ports/qemu/mphalport.h diff --git a/ports/qemu-arm/qstrdefsport.h b/ports/qemu/qstrdefsport.h similarity index 100% rename from ports/qemu-arm/qstrdefsport.h rename to ports/qemu/qstrdefsport.h diff --git a/ports/qemu-arm/test-frzmpy/frozen_asm.py b/ports/qemu/test-frzmpy/frozen_asm.py similarity index 100% rename from ports/qemu-arm/test-frzmpy/frozen_asm.py rename to ports/qemu/test-frzmpy/frozen_asm.py diff --git a/ports/qemu-arm/test-frzmpy/frozen_const.py b/ports/qemu/test-frzmpy/frozen_const.py similarity index 100% rename from ports/qemu-arm/test-frzmpy/frozen_const.py rename to ports/qemu/test-frzmpy/frozen_const.py diff --git a/ports/qemu-arm/test-frzmpy/frozen_viper.py b/ports/qemu/test-frzmpy/frozen_viper.py similarity index 100% rename from ports/qemu-arm/test-frzmpy/frozen_viper.py rename to ports/qemu/test-frzmpy/frozen_viper.py diff --git a/ports/qemu-arm/test-frzmpy/native_frozen_align.py b/ports/qemu/test-frzmpy/native_frozen_align.py similarity index 100% rename from ports/qemu-arm/test-frzmpy/native_frozen_align.py rename to ports/qemu/test-frzmpy/native_frozen_align.py diff --git a/ports/qemu-arm/uart.c b/ports/qemu/uart.c similarity index 100% rename from ports/qemu-arm/uart.c rename to ports/qemu/uart.c diff --git a/ports/qemu-arm/uart.h b/ports/qemu/uart.h similarity index 100% rename from ports/qemu-arm/uart.h rename to ports/qemu/uart.h diff --git a/tests/float/float_format_ints_power10.py b/tests/float/float_format_ints_power10.py index 98900c135b2e4..7ea200f18ef06 100644 --- a/tests/float/float_format_ints_power10.py +++ b/tests/float/float_format_ints_power10.py @@ -3,6 +3,6 @@ # Check that powers of 10 (that fit in float32) format correctly. for i in range(31): - # It works to 12 digits on all platforms *except* qemu-arm, where + # It works to 12 digits on all platforms *except* qemu, where # 10^11 comes out as 10000000820 or something. print(i, "{:.7g}".format(float("1e" + str(i)))) diff --git a/tests/ports/qemu-arm/asm_test.py b/tests/ports/qemu/asm_test.py similarity index 100% rename from tests/ports/qemu-arm/asm_test.py rename to tests/ports/qemu/asm_test.py diff --git a/tests/ports/qemu-arm/asm_test.py.exp b/tests/ports/qemu/asm_test.py.exp similarity index 100% rename from tests/ports/qemu-arm/asm_test.py.exp rename to tests/ports/qemu/asm_test.py.exp diff --git a/tests/ports/qemu-arm/native_test.py b/tests/ports/qemu/native_test.py similarity index 100% rename from tests/ports/qemu-arm/native_test.py rename to tests/ports/qemu/native_test.py diff --git a/tests/ports/qemu-arm/native_test.py.exp b/tests/ports/qemu/native_test.py.exp similarity index 100% rename from tests/ports/qemu-arm/native_test.py.exp rename to tests/ports/qemu/native_test.py.exp diff --git a/tests/ports/qemu-arm/viper_test.py b/tests/ports/qemu/viper_test.py similarity index 100% rename from tests/ports/qemu-arm/viper_test.py rename to tests/ports/qemu/viper_test.py diff --git a/tests/ports/qemu-arm/viper_test.py.exp b/tests/ports/qemu/viper_test.py.exp similarity index 100% rename from tests/ports/qemu-arm/viper_test.py.exp rename to tests/ports/qemu/viper_test.py.exp diff --git a/tests/run-tests.py b/tests/run-tests.py index b80a78ed6a059..43a9e38d327ab 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -675,7 +675,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add( "extmod/time_time_ns.py" ) # RA fsp rtc function doesn't support nano sec info - elif args.target == "qemu-arm": + elif args.target == "qemu": skip_tests.add("inlineasm/asmfpaddsub.py") # requires Cortex-M4 skip_tests.add("inlineasm/asmfpcmp.py") skip_tests.add("inlineasm/asmfpldrstr.py") @@ -1054,7 +1054,7 @@ def main(): "esp32", "minimal", "nrf", - "qemu-arm", + "qemu", "renesas-ra", "rp2", ) @@ -1131,11 +1131,11 @@ def main(): "cmdline", "ports/unix", ) - elif args.target == "qemu-arm": + elif args.target == "qemu": test_dirs += ( "float", "inlineasm", - "ports/qemu-arm", + "ports/qemu", ) elif args.target == "webassembly": test_dirs += ("float", "ports/webassembly") diff --git a/tools/ci.sh b/tools/ci.sh index 70fdb487eeba2..46aee1a3a274b 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -244,7 +244,7 @@ function ci_powerpc_build { } ######################################################################################## -# ports/qemu-arm +# ports/qemu function ci_qemu_setup_arm { ci_gcc_arm_setup @@ -262,17 +262,17 @@ function ci_qemu_setup_rv32 { function ci_qemu_build_arm { make ${MAKEOPTS} -C mpy-cross - make ${MAKEOPTS} -C ports/qemu-arm submodules - make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 - make ${MAKEOPTS} -C ports/qemu-arm clean - make ${MAKEOPTS} -C ports/qemu-arm test - make ${MAKEOPTS} -C ports/qemu-arm BOARD=SABRELITE test + make ${MAKEOPTS} -C ports/qemu submodules + make ${MAKEOPTS} -C ports/qemu CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 + make ${MAKEOPTS} -C ports/qemu clean + make ${MAKEOPTS} -C ports/qemu test + make ${MAKEOPTS} -C ports/qemu BOARD=SABRELITE test } function ci_qemu_build_rv32 { make ${MAKEOPTS} -C mpy-cross - make ${MAKEOPTS} -C ports/qemu-arm BOARD=VIRT_RV32 submodules - make ${MAKEOPTS} -C ports/qemu-arm BOARD=VIRT_RV32 test + make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV32 submodules + make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV32 test } ######################################################################################## From 455415b1e1199c4364e84cc31905452f7c4ec399 Mon Sep 17 00:00:00 2001 From: timdechant Date: Mon, 26 Aug 2024 10:34:42 -0400 Subject: [PATCH 0290/1300] shared/runtime/sys_stdio_mphal: Fix printed type for stdio streams. The printed type for stdio streams indicates "FileIO", which is a binary IO stream. Stdio is not binary by design, and its printed type should indicate a text stream. "TextIOWrapper" suits that purpose, and is used by VfsPosix files. Signed-off-by: timdechant --- shared/runtime/sys_stdio_mphal.c | 4 ++-- tests/basics/sys_stdio.py | 21 +++++++++++++++++++++ tests/basics/sys_stdio_buffer.py | 21 +++++++++++++++++++++ tests/basics/sys_stdio_buffer.py.exp | 6 ++++++ 4 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/basics/sys_stdio.py create mode 100644 tests/basics/sys_stdio_buffer.py create mode 100644 tests/basics/sys_stdio_buffer.py.exp diff --git a/shared/runtime/sys_stdio_mphal.c b/shared/runtime/sys_stdio_mphal.c index ad045fb3a9537..b82725bfa7bf8 100644 --- a/shared/runtime/sys_stdio_mphal.c +++ b/shared/runtime/sys_stdio_mphal.c @@ -54,7 +54,7 @@ static const sys_stdio_obj_t stdio_buffer_obj; static void stdio_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { sys_stdio_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "", self->fd); + mp_printf(print, "", mp_obj_get_type_str(self_in), self->fd); } static mp_uint_t stdio_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { @@ -122,7 +122,7 @@ static const mp_stream_p_t stdio_obj_stream_p = { MP_DEFINE_CONST_OBJ_TYPE( stdio_obj_type, - MP_QSTR_FileIO, + MP_QSTR_TextIOWrapper, MP_TYPE_FLAG_ITER_IS_STREAM, print, stdio_obj_print, protocol, &stdio_obj_stream_p, diff --git a/tests/basics/sys_stdio.py b/tests/basics/sys_stdio.py new file mode 100644 index 0000000000000..8d746b8b4b257 --- /dev/null +++ b/tests/basics/sys_stdio.py @@ -0,0 +1,21 @@ +# Test sys.std* objects. + +import sys + +try: + sys.stdout + sys.stdin + sys.stderr +except AttributeError: + print("SKIP") + raise SystemExit + +# CPython is more verbose; no need to match exactly + +print('TextIOWrapper' in str(sys.stdout)) +print('TextIOWrapper' in str(sys.stderr)) +print('TextIOWrapper' in str(sys.stdin)) + +print('TextIOWrapper' in str(type(sys.stdout))) +print('TextIOWrapper' in str(type(sys.stderr))) +print('TextIOWrapper' in str(type(sys.stdin))) diff --git a/tests/basics/sys_stdio_buffer.py b/tests/basics/sys_stdio_buffer.py new file mode 100644 index 0000000000000..ae354ec7fbec1 --- /dev/null +++ b/tests/basics/sys_stdio_buffer.py @@ -0,0 +1,21 @@ +# Test sys.std*.buffer objects. + +import sys + +try: + sys.stdout.buffer + sys.stdin.buffer + sys.stderr.buffer +except AttributeError: + print("SKIP") + raise SystemExit + +# CPython is more verbose; no need to match exactly + +print('FileIO' in str(sys.stdout.buffer)) +print('FileIO' in str(sys.stderr.buffer)) +print('FileIO' in str(sys.stdin.buffer)) + +print('FileIO' in str(type(sys.stdout.buffer))) +print('FileIO' in str(type(sys.stderr.buffer))) +print('FileIO' in str(type(sys.stdin.buffer))) diff --git a/tests/basics/sys_stdio_buffer.py.exp b/tests/basics/sys_stdio_buffer.py.exp new file mode 100644 index 0000000000000..681c7ec9e8402 --- /dev/null +++ b/tests/basics/sys_stdio_buffer.py.exp @@ -0,0 +1,6 @@ +True +True +True +True +True +True From d04974d8d0531f2f868c2d698ec8c44016a0c115 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 27 Aug 2024 11:21:27 +1000 Subject: [PATCH 0291/1300] unix: Expand the build steps in the README. - Present the default build dependencies in one place at the top, and make a separate section about building standalone. - Add steps for the "minimal" variant as well. - Document that building standalone requires autoconf and libtool. - Allow MICROPY_STANDALONE to be set as an environment variable. Fixes issue #11313. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/unix/README.md | 117 ++++++++++++++++++++++++++++--------- ports/unix/mpconfigport.mk | 2 +- 2 files changed, 90 insertions(+), 29 deletions(-) diff --git a/ports/unix/README.md b/ports/unix/README.md index e15fd93b2312d..6481522639c3a 100644 --- a/ports/unix/README.md +++ b/ports/unix/README.md @@ -1,17 +1,59 @@ -The Unix version ----------------- +MicroPython Unix port +===================== -The "unix" port requires a standard Unix-like environment with gcc and GNU -make. This includes Linux, BSD, macOS, and Windows Subsystem for Linux. The -x86 and x64 architectures are supported (i.e. x86 32- and 64-bit), as well as -ARM and MIPS. Making a full-featured port to another architecture requires +The "unix" port runs in standard Unix-like environments including Linux, BSD, +macOS, and Windows Subsystem for Linux. + +The x86 and x64 architectures are supported (i.e. x86 32- and 64-bit), as well +as ARM and MIPS. Extending the unix port to another architecture requires writing some assembly code for the exception handling and garbage collection. Alternatively, a fallback implementation based on setjmp/longjmp can be used. -To build (see section below for required dependencies): +Building +-------- + +### Dependencies + +To build the unix port locally then you will need: + +* git command line executable, unless you downloaded a source .tar.xz file from + https://micropython.org/download/ +* gcc (or clang for macOS) toolchain +* GNU Make +* Python 3.x + +To build the default "standard" variant and configuration, then you will also +need: + +* `pkg-config` tool +* `libffi` library and headers + +On Debian/Ubuntu/Mint and related Linux distros, you can install all these +dependencies with a command like: + +``` +# apt install build-essential git python3 pkg-config libbfi-dev +``` + +(See below for steps to build either a standalone or minimal MicroPython +executable that doesn't require system `libffi` or `pkg-config`.) + +### Default build steps + +To set up the environment for building (not needed every time), starting from +the top-level MicroPython directory: $ cd ports/unix + $ make -C ../../mpy-cross $ make submodules + +The `mpy-cross` step builds the [MicroPython +cross-compiler](https://github.com/micropython/micropython/?tab=readme-ov-file#the-micropython-cross-compiler-mpy-cross). +The `make submodules` step can be skipped if you didn't clone the MicroPython +source from git. + +Next, to build the actual executable (still in the `ports/unix` directory): + $ make Then to give it a try: @@ -45,40 +87,59 @@ Browse available modules at [Package management](https://docs.micropython.org/en/latest/reference/packages.html) for more information about `mip`. -External dependencies ---------------------- - -The `libffi` library and `pkg-config` tool are required. On Debian/Ubuntu/Mint -derivative Linux distros, install `build-essential`(includes toolchain and -make), `libffi-dev`, and `pkg-config` packages. +### Minimal Variant -Other dependencies can be built together with MicroPython. This may -be required to enable extra features or capabilities, and in recent -versions of MicroPython, these may be enabled by default. To build -these additional dependencies, in the unix port directory first execute: +The "standard" variant of MicroPython is the default. It enables most features, +including external modules interfaced using `libffi`. To instead build the +"minimal" variant, which disables almost all optional features and modules: + $ cd ports/unix $ make submodules + $ make VARIANT=minimal + +The executable will be built at `build-minimal/micropython`. + +Additional variants can be found in the `variants` sub-directory of the port, +although these are mostly of interest to MicroPython maintainers. + +### Standalone build + +By default, the "standard" variant uses `pkg-config` to link to the system's +shared `libffi` library. + +It is possible to instead build a standalone MicroPython where `libffi` is built +from source and linked statically into the `micropython` executable. This is +mostly useful for embedded or cross-compiled applications. + +Building standalone requires `autoconf` and `libtool` to also be installed. + +To build standalone: + + $ export MICROPY_STANDALONE=1 + $ make submodules # fetches libffi submodule + $ make deplibs # build just the external libraries + $ make # build MicroPython itself + +`make deplibs` causes all supported external libraries (currently only `libffi`) +to be built inside the build directory, so it needs to run again only after +`make clean`. -This will fetch all the relevant git submodules (sub repositories) that -the port needs. Use the same command to get the latest versions of -submodules as they are updated from time to time. After that execute: +If you intend to build MicroPython with additional options (like +cross-compiling), the same set of options should be passed to both `make +deplibs` and `make`. - $ make deplibs +### Other dependencies -This will build all available dependencies (regardless whether they are used -or not). If you intend to build MicroPython with additional options -(like cross-compiling), the same set of options should be passed to `make -deplibs`. To actually enable/disable use of dependencies, edit the +To actually enable/disable use of dependencies, edit the `ports/unix/mpconfigport.mk` file, which has inline descriptions of the options. For example, to build the SSL module, `MICROPY_PY_SSL` should be set to 1. -Debug Symbols -------------- +### Debug Symbols By default, builds are stripped of symbols and debug information to save size. -To build a debuggable version of the Unix port, there are two options +To build a debuggable version of the Unix port, there are two options: 1. Run `make [other arguments] DEBUG=1`. Note setting `DEBUG` also reduces the optimisation level, so it's not a good option for builds that also want the diff --git a/ports/unix/mpconfigport.mk b/ports/unix/mpconfigport.mk index c8ade0b7e277c..1557c5461f6ed 100644 --- a/ports/unix/mpconfigport.mk +++ b/ports/unix/mpconfigport.mk @@ -37,7 +37,7 @@ MICROPY_PY_JNI = 0 # Avoid using system libraries, use copies bundled with MicroPython # as submodules (currently affects only libffi). -MICROPY_STANDALONE = 0 +MICROPY_STANDALONE ?= 0 MICROPY_ROM_TEXT_COMPRESSION = 1 From e370999e378bf5040ef6348e2a0720bc3ec4f773 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 28 Aug 2024 15:39:38 +1000 Subject: [PATCH 0292/1300] unix: Add a description of COPT in the README. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/unix/README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ports/unix/README.md b/ports/unix/README.md index 6481522639c3a..953c83ef73d4c 100644 --- a/ports/unix/README.md +++ b/ports/unix/README.md @@ -142,8 +142,16 @@ By default, builds are stripped of symbols and debug information to save size. To build a debuggable version of the Unix port, there are two options: 1. Run `make [other arguments] DEBUG=1`. Note setting `DEBUG` also reduces the - optimisation level, so it's not a good option for builds that also want the - best performance. + optimisation level and enables assertions, so it's not a good option for + builds that also want the best performance. 2. Run `make [other arguments] STRIP=`. Note that the value of `STRIP` is empty. This will skip the build step that strips symbols and debug information, but changes nothing else in the build configuration. + +### Optimisation Level + +The default compiler optimisation level is -Os, or -Og if `DEBUG=1` is set. + +Setting the variable `COPT` will explicitly set the optimisation level. For +example `make [other arguments] COPT=-O0 DEBUG=1` will build a binary with no +optimisations, assertions enabled, and debug symbols. From a831c788f76c0742f85b8a35a5860255229b98dc Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 28 Aug 2024 15:05:47 +0200 Subject: [PATCH 0293/1300] tools/mpy_ld.py: Ignore R_XTENSA_ASM_EXPAND relocation entries. As reported in #14430 the Xtensa compiler can add R_XTENSA_ASM_EXPAND relocation relaxation entries in object files, and they were not supported by mpy_ld. This commit adds handling for that entry, doing nothing with it, as it is only of real use for an optimising linker. Signed-off-by: Alessandro Gatti --- tools/mpy_ld.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 7046b6a8556f0..45aadadd51314 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -69,6 +69,7 @@ R_386_GOTOFF = 9 R_386_GOTPC = 10 R_ARM_THM_CALL = 10 +R_XTENSA_ASM_EXPAND = 11 R_XTENSA_DIFF32 = 19 R_XTENSA_SLOT0_OP = 20 R_ARM_BASE_PREL = 25 # aka R_ARM_GOTPC @@ -562,9 +563,14 @@ def do_relocation_text(env, text_addr, r): reloc = addr - r_offset reloc_type = "xtensa_l32r" - elif env.arch.name == "EM_XTENSA" and r_info_type in (R_XTENSA_DIFF32, R_XTENSA_PDIFF32): + elif env.arch.name == "EM_XTENSA" and r_info_type in ( + R_XTENSA_DIFF32, + R_XTENSA_PDIFF32, + R_XTENSA_ASM_EXPAND, + ): if s.section.name.startswith(".text"): - # it looks like R_XTENSA_[P]DIFF32 into .text is already correctly relocated + # it looks like R_XTENSA_[P]DIFF32 into .text is already correctly relocated, + # and expand relaxations cannot occur in non-executable sections. return assert 0 From 65244d291acda865aef60ec3cd7291c2211caeb2 Mon Sep 17 00:00:00 2001 From: cajt Date: Wed, 28 Aug 2024 22:19:21 +0200 Subject: [PATCH 0294/1300] extmod/modlwip: Fix compile error for lwIP with SLIP support. Fixes a compile error if STM32 port is compiled with: make BOARD=(..) MICROPY_PY_LWIP=1 MICROPY_PY_LWIP_SLIP=1 `sio_send()` and `sio_tryread()` now use `mp_get_stream`. Signed-off-by: Carl Treudler --- extmod/modlwip.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index b07c1d8159c2f..21c6f7264befa 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -127,15 +127,15 @@ sio_fd_t sio_open(u8_t dvnum) { } void sio_send(u8_t c, sio_fd_t fd) { - mp_obj_type_t *type = mp_obj_get_type(MP_STATE_VM(lwip_slip_stream)); + const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(lwip_slip_stream)); int error; - type->stream_p->write(MP_STATE_VM(lwip_slip_stream), &c, 1, &error); + stream_p->write(MP_STATE_VM(lwip_slip_stream), &c, 1, &error); } u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len) { - mp_obj_type_t *type = mp_obj_get_type(MP_STATE_VM(lwip_slip_stream)); + const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(lwip_slip_stream)); int error; - mp_uint_t out_sz = type->stream_p->read(MP_STATE_VM(lwip_slip_stream), data, len, &error); + mp_uint_t out_sz = stream_p->read(MP_STATE_VM(lwip_slip_stream), data, len, &error); if (out_sz == MP_STREAM_ERROR) { if (mp_is_nonblocking_error(error)) { return 0; From 3294606e2319dd64226434f20acc15b8869ddf55 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 29 Aug 2024 20:53:23 +0200 Subject: [PATCH 0295/1300] extmod/libmetal: Fix libmetal rules for mkdir dependencies. Dependency on auto-generated libmetal should be an order only prerequisite. Signed-off-by: iabdalkader --- extmod/libmetal/libmetal.mk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/libmetal/libmetal.mk b/extmod/libmetal/libmetal.mk index 7cda12a31333d..5475654aded76 100644 --- a/extmod/libmetal/libmetal.mk +++ b/extmod/libmetal/libmetal.mk @@ -4,13 +4,13 @@ # This replicates the basic functionality of the pre-processor, including adding a "micropython" # platform that is almost identical to the built-in "generic" platform with a few small changes # provided by the files in extmod/libmetal. -$(BUILD)/openamp: $(BUILD) +$(BUILD)/openamp: | $(BUILD) $(MKDIR) -p $@ -$(BUILD)/openamp/metal: $(BUILD)/openamp +$(BUILD)/openamp/metal: | $(BUILD)/openamp $(MKDIR) -p $@ -$(BUILD)/openamp/metal/config.h: $(BUILD)/openamp/metal $(TOP)/$(LIBMETAL_DIR)/lib/config.h +$(BUILD)/openamp/metal/config.h: $(TOP)/$(LIBMETAL_DIR)/lib/config.h | $(BUILD)/openamp/metal @$(ECHO) "GEN $@" @for file in $(TOP)/$(LIBMETAL_DIR)/lib/*.c $(TOP)/$(LIBMETAL_DIR)/lib/*.h; do $(SED) -e "s/@PROJECT_SYSTEM@/micropython/g" -e "s/@PROJECT_PROCESSOR@/arm/g" $$file > $(BUILD)/openamp/metal/$$(basename $$file); done $(MKDIR) -p $(BUILD)/openamp/metal/processor/arm From ed86fdbdf6e2d3eb9a118d3476ce4c370ec48b06 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 21 Apr 2024 12:45:36 +0200 Subject: [PATCH 0296/1300] samd/mphalport: Fix an execution order bug in mp_hal_ticks_us_64(). The upper 32 bit of the 64 bit ticks register was taken before disabling the interrupts. That may have caused a wrong return values. Besides that, the function may cause trouble when called in an IRQ context, because it unconditionally enables IRQ. Signed-off-by: robert-hh --- ports/samd/mphalport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/samd/mphalport.c b/ports/samd/mphalport.c index 80904804c9980..3a33fdd6b56b7 100644 --- a/ports/samd/mphalport.c +++ b/ports/samd/mphalport.c @@ -94,10 +94,10 @@ void mp_hal_delay_us(mp_uint_t us) { } uint64_t mp_hal_ticks_us_64(void) { - uint32_t us64_upper = ticks_us64_upper; uint32_t us64_lower; uint8_t intflag; __disable_irq(); + uint32_t us64_upper = ticks_us64_upper; #if defined(MCU_SAMD21) us64_lower = REG_TC4_COUNT32_COUNT; intflag = TC4->COUNT32.INTFLAG.reg; From 1a6279ba37678893db9f2756fceaa5c817cae888 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 17 Aug 2024 17:07:44 +0200 Subject: [PATCH 0297/1300] samd/mphalport: Simplify mp_hal_delay_ms(). Do NOT use `mp_hal_delay_us()` for short delays. This was initially done to make short delays precise, but it does not allow for scheduling. Leave using `mp_hal_delay_us()` to user code if needed. Signed-off-by: robert-hh --- docs/samd/quickref.rst | 2 ++ ports/samd/mphalport.c | 10 +++------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst index 60c57b3a47a31..25b5a8fc8ab2b 100644 --- a/docs/samd/quickref.rst +++ b/docs/samd/quickref.rst @@ -65,6 +65,8 @@ Use the :mod:`time