From 44dc6eb30d9a47cc2f7c0c04f012b0dbdae1acae Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 28 Oct 2024 11:08:24 +1100 Subject: [PATCH 001/546] all: Bump version to 1.25.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 caea0f4f7a3b8..b31da2a37c050 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 24 +#define MICROPY_VERSION_MINOR 25 #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 4601dcb8a1e88e1cb2d644cc8b0c36378d5966b7 Mon Sep 17 00:00:00 2001 From: Steve Holden Date: Thu, 24 Oct 2024 01:35:02 +0100 Subject: [PATCH 002/546] rp2/README: Remove redundant `global` statement from example code. Since `led` is not being rebound inside the `tick` function the standard Python name resolution method will find it. Signed-off-by: Steve Holden --- ports/rp2/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/rp2/README.md b/ports/rp2/README.md index c2f3771cd313f..911d797fe01a2 100644 --- a/ports/rp2/README.md +++ b/ports/rp2/README.md @@ -69,7 +69,6 @@ from machine import Pin, Timer led = Pin(25, Pin.OUT) tim = Timer() def tick(timer): - global led led.toggle() tim.init(freq=2.5, mode=Timer.PERIODIC, callback=tick) From ff70a91581448983d9bdf1251f659ed7bc8189fb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 22 Oct 2024 15:02:32 +1100 Subject: [PATCH 003/546] esp32: Move the linker wrap options out of the project CMakeLists. For in-tree builds, these are effectively equivalent. However for out-of-tree builds it's preferable to have as little as possible in the top-level CMakeLists.txt file (as the out-of-tree build needs its own copy). This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/CMakeLists.txt | 7 ------- ports/esp32/esp32_common.cmake | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt index 83fca88b6972b..9ca9065afd0d4 100644 --- a/ports/esp32/CMakeLists.txt +++ b/ports/esp32/CMakeLists.txt @@ -71,12 +71,5 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake) # Set the location of the main component for the project (one per target). list(APPEND EXTRA_COMPONENT_DIRS main_${IDF_TARGET}) -# Enable the panic handler wrapper -idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) - -# Patch LWIP memory pool allocators (see lwip_patch.c) -idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_malloc" APPEND) -idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_free" APPEND) - # Define the project. project(micropython) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 565f6feec2324..48f067debb76d 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -236,6 +236,13 @@ target_include_directories(${MICROPY_TARGET} PUBLIC target_link_libraries(${MICROPY_TARGET} micropy_extmod_btree) target_link_libraries(${MICROPY_TARGET} usermod) +# Enable the panic handler wrapper +idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) + +# Patch LWIP memory pool allocators (see lwip_patch.c) +idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_malloc" APPEND) +idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_free" APPEND) + # Collect all of the include directories and compile definitions for the IDF components, # including those added by the IDF Component Manager via idf_components.yaml. foreach(comp ${__COMPONENT_NAMES_RESOLVED}) From 043ba45bc36a4bb19e732ae3f68181a05137906b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 22 Oct 2024 16:01:01 +1100 Subject: [PATCH 004/546] esp32: Add some notes about the different CMake files. Signed-off-by: Angus Gratton --- ports/esp32/CMakeLists.txt | 5 ++++- ports/esp32/esp32_common.cmake | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt index 9ca9065afd0d4..d695170fa2b5d 100644 --- a/ports/esp32/CMakeLists.txt +++ b/ports/esp32/CMakeLists.txt @@ -1,5 +1,8 @@ # Top-level cmake file for building MicroPython on ESP32. - +# +# Note for maintainers: Where possible, functionality should be put into +# esp32_common.cmake not this file. This is because this CMakeLists.txt file +# needs to be duplicated for out-of-tree builds, and can easily get out of date. cmake_minimum_required(VERSION 3.12) # Retrieve IDF version diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 48f067debb76d..9d51a03aa856e 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -1,3 +1,9 @@ +# This is the common ESP-IDF "main component" CMakeLists.txt contents for MicroPython. +# +# This file is included directly from a main_${IDF_TARGET}/CMakeLists.txt file +# (or included from an out-of-tree main component CMakeLists.txt for out-of-tree +# builds.) + # Set location of base MicroPython directory. if(NOT MICROPY_DIR) get_filename_component(MICROPY_DIR ${CMAKE_CURRENT_LIST_DIR}/../.. ABSOLUTE) From 9ea8d2a03181681feebaede3add82ec4970e30d7 Mon Sep 17 00:00:00 2001 From: Glenn Moloney Date: Sat, 26 Oct 2024 15:19:54 +1100 Subject: [PATCH 005/546] tools/mpremote: Fix UnboundLocalError in Transport.fs_writefile(). The variable `written` was being used before it was defined in the `fs_writefile()` method of the Transport class. This was causing an `UnboundLocalError` to be raised when the `progress_callback` was not provided. Fixes issue #16084. Signed-off-by: Glenn Moloney --- tools/mpremote/mpremote/transport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mpremote/mpremote/transport.py b/tools/mpremote/mpremote/transport.py index 0b22a61580a03..d845796953a29 100644 --- a/tools/mpremote/mpremote/transport.py +++ b/tools/mpremote/mpremote/transport.py @@ -151,9 +151,9 @@ def fs_writefile(self, dest, data, chunk_size=256, progress_callback=None): while data: chunk = data[:chunk_size] self.exec("w(" + repr(chunk) + ")") - written += len(chunk) data = data[len(chunk) :] if progress_callback: + written += len(chunk) progress_callback(written, src_size) self.exec("f.close()") except TransportExecError as e: From 86c71a0307a509b6c47fbb7ce1ab2d827ff92653 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 1 Oct 2024 21:40:06 +0200 Subject: [PATCH 006/546] esp32/machine_hw_spi: Reject invalid number of bits in constructor. This commit adds an extra bit of parameters validation to the SPI bus constructor on ESP32. Passing 0 as the number of bits would trigger a division by zero error when performing read/write operations on an SPI bus created in such a fashion. Fixes issue #5910. Signed-off-by: Alessandro Gatti --- ports/esp32/machine_hw_spi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c index 4b3d392cbdae2..6eb83fc09c37a 100644 --- a/ports/esp32/machine_hw_spi.c +++ b/ports/esp32/machine_hw_spi.c @@ -196,6 +196,10 @@ static void machine_hw_spi_init_internal(machine_hw_spi_obj_t *self, mp_arg_val_ changed = true; } + if (args[ARG_bits].u_int != -1 && args[ARG_bits].u_int <= 0) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); + } + if (args[ARG_bits].u_int != -1 && args[ARG_bits].u_int != self->bits) { self->bits = args[ARG_bits].u_int; changed = true; From 548babf8a04510d99a49b2d129a9579afc33e182 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Sun, 27 Oct 2024 14:51:37 +1100 Subject: [PATCH 007/546] esp32/machine_pwm: Use IDF functions to calculate resolution correctly. This commit fixes PWM configuration across C3, C6, S2 and S3 chips, which was broken by 6d799378bad4474e77ddb2fa2187ecd6e290e0ba. Without this fix the PWM frequency is limited to a maximum of 2446Hz (on S2 at least). Signed-off-by: Andrew Leech --- ports/esp32/machine_pwm.c | 50 +++++++++++++-------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 71402e1c49e44..61efdfa18548b 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -34,6 +34,7 @@ #include "py/mphal.h" #include "driver/ledc.h" #include "esp_err.h" +#include "esp_clk_tree.h" #include "soc/gpio_sig_map.h" #define PWM_DBG(...) @@ -209,51 +210,32 @@ static void configure_channel(machine_pwm_obj_t *self) { static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) { if (freq != timer->freq_hz) { - // Find the highest bit resolution for the requested frequency - unsigned int i = APB_CLK_FREQ; // 80 MHz - #if SOC_LEDC_SUPPORT_REF_TICK - if (freq < EMPIRIC_FREQ) { - i = REF_CLK_FREQ; // 1 MHz - } - #endif - - int divider = (i + freq / 2) / freq; // rounded - if (divider == 0) { - divider = 1; - } - float f = (float)i / divider; // actual frequency - if (f <= 1.0) { - f = 1.0; - } - i = (unsigned int)roundf((float)i / f); - - unsigned int res = 0; - for (; i > 1; i >>= 1) { - ++res; - } - if (res == 0) { - res = 1; - } else if (res > HIGHEST_PWM_RES) { - // Limit resolution to HIGHEST_PWM_RES to match units of our duty - res = HIGHEST_PWM_RES; - } - - // Configure the new resolution and frequency - timer->duty_resolution = res; + // Configure the new frequency and resolution timer->freq_hz = freq; - #if SOC_LEDC_SUPPORT_XTAL_CLOCK + + #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK + timer->clk_cfg = LEDC_USE_PLL_DIV_CLK; + #elif SOC_LEDC_SUPPORT_APB_CLOCK + timer->clk_cfg = LEDC_USE_APB_CLK; + #elif SOC_LEDC_SUPPORT_XTAL_CLOCK timer->clk_cfg = LEDC_USE_XTAL_CLK; #else - timer->clk_cfg = LEDC_USE_APB_CLK; + #error No supported PWM / LEDC clocks. #endif #if SOC_LEDC_SUPPORT_REF_TICK if (freq < EMPIRIC_FREQ) { timer->clk_cfg = LEDC_USE_REF_TICK; } #endif + uint32_t src_clk_freq = 0; + esp_err_t err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq); + if (err != ESP_OK) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer->clk_cfg); + } + timer->duty_resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer->freq_hz); // Set frequency - esp_err_t err = ledc_timer_config(timer); + err = ledc_timer_config(timer); if (err != ESP_OK) { if (err == ESP_FAIL) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unreachable frequency %d"), freq); From d34b15ac6f03ecad224d02aec9491d6a04605f72 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 12 Sep 2024 13:08:32 +0200 Subject: [PATCH 008/546] pic16bit: Make it build with recent XC16 versions. The PIC16 port didn't catch up with the other ports, so it required a bit of work to make it build with the latest version of XC16. Signed-off-by: Alessandro Gatti --- ports/pic16bit/Makefile | 4 ++-- ports/pic16bit/mpconfigport.h | 4 ---- py/misc.h | 13 +++++++++++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/ports/pic16bit/Makefile b/ports/pic16bit/Makefile index 6d061514f9583..dda48fa3ce5f1 100644 --- a/ports/pic16bit/Makefile +++ b/ports/pic16bit/Makefile @@ -7,7 +7,7 @@ QSTR_DEFS = qstrdefsport.h include $(TOP)/py/py.mk include $(TOP)/extmod/extmod.mk -XCVERSION ?= 1.35 +XCVERSION ?= 2.10 XC16 ?= /opt/microchip/xc16/v$(XCVERSION) CROSS_COMPILE ?= $(XC16)/bin/xc16- @@ -31,7 +31,7 @@ CFLAGS += -O1 -DNDEBUG endif LDFLAGS += --heap=0 -nostdlib -T $(XC16)/support/$(PARTFAMILY)/gld/p$(PART).gld -Map=$@.map --cref -p$(PART) -LIBS += -L$(XC16)/lib -L$(XC16)/lib/$(PARTFAMILY) -lc -lm -lpic30 +LIBS += -L$(XC16)/lib -L$(XC16)/lib/$(PARTFAMILY) -lc99-elf -lm-elf -lc99-pic30-elf SRC_C = \ main.c \ diff --git a/ports/pic16bit/mpconfigport.h b/ports/pic16bit/mpconfigport.h index a2d607fb29325..d80f7edb9e546 100644 --- a/ports/pic16bit/mpconfigport.h +++ b/ports/pic16bit/mpconfigport.h @@ -93,7 +93,3 @@ typedef int mp_off_t; #define MICROPY_MPHALPORT_H "pic16bit_mphal.h" #define MICROPY_HW_BOARD_NAME "dsPICSK" #define MICROPY_HW_MCU_NAME "dsPIC33" - -// XC16 toolchain doesn't seem to define these -typedef int intptr_t; -typedef unsigned int uintptr_t; diff --git a/py/misc.h b/py/misc.h index 96b13c151a6b9..8ac80bb7b3e34 100644 --- a/py/misc.h +++ b/py/misc.h @@ -380,6 +380,18 @@ static inline bool mp_check(bool value) { // 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) { + #ifdef __XC16__ + mp_uint_t mask = MP_OBJ_WORD_MSBIT_HIGH; + mp_uint_t zeroes = 0; + while (mask != 0) { + if (mask & (mp_uint_t)x) { + break; + } + zeroes++; + mask >>= 1; + } + return zeroes; + #else MP_STATIC_ASSERT(sizeof(mp_int_t) == sizeof(long long) || sizeof(mp_int_t) == sizeof(long)); @@ -389,6 +401,7 @@ static inline uint32_t mp_clz_mpi(mp_int_t x) { } else { return mp_clzll((unsigned long long)x); } + #endif } #endif // MICROPY_INCLUDED_PY_MISC_H From 9591b0a53c599baac79e1e895153d6b6944ebef0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 30 Oct 2024 11:18:59 +1100 Subject: [PATCH 009/546] tools/ci.sh: Fix commit msg checking when PR branch HEAD behind master. Fixes the problem noted at https://github.com/micropython/micropython/pull/15547#issuecomment-2434479702 which is that, because default CI HEAD for a PR is a (generated) merge commit into the master branch's current HEAD, then if the PR branch isn't fully rebased then the commit check runs against commits from master as well! Also drops running this check on push, the pull_request event is triggered by default on open and update ("synchronized" event), which probably covers the cases where this check should run. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- .github/workflows/commit_formatting.yml | 4 ++-- tools/ci.sh | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/commit_formatting.yml b/.github/workflows/commit_formatting.yml index 3fdcabc4ca73f..fcbcaa7092ee2 100644 --- a/.github/workflows/commit_formatting.yml +++ b/.github/workflows/commit_formatting.yml @@ -1,6 +1,6 @@ name: Check commit message formatting -on: [push, pull_request] +on: [pull_request] concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - fetch-depth: '100' + fetch-depth: 100 - uses: actions/setup-python@v5 - name: Check commit message formatting run: source tools/ci.sh && ci_commit_formatting_run diff --git a/tools/ci.sh b/tools/ci.sh index 6f0f23a1c4810..0b4b4e11cde42 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -39,12 +39,18 @@ function ci_c_code_formatting_run { # commit formatting function ci_commit_formatting_run { - git remote add upstream https://github.com/micropython/micropython.git - git fetch --depth=100 upstream master + # Default GitHub Actions checkout for a PR is a generated merge commit where + # the parents are the head of base branch (i.e. master) and the head of the + # PR branch, respectively. Use these parents to find the merge-base (i.e. + # where the PR branch head was branched) + # If the common ancestor commit hasn't been found, fetch more. - git merge-base upstream/master HEAD || git fetch --unshallow upstream master - # For a PR, upstream/master..HEAD ends with a merge commit into master, exclude that one. - tools/verifygitlog.py -v upstream/master..HEAD --no-merges + git merge-base HEAD^1 HEAD^2 || git fetch --unshallow origin + + MERGE_BASE=$(git merge-base HEAD^1 HEAD^2) + HEAD=$(git rev-parse HEAD^2) + echo "Checking commits between merge base ${MERGE_BASE} and PR head ${HEAD}..." + tools/verifygitlog.py -v "${MERGE_BASE}..${HEAD}" } ######################################################################################## From 787c424cfc1e07c8881713ee5c1a6e26f6d6bf07 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 31 Oct 2024 10:47:47 +1100 Subject: [PATCH 010/546] tools/ci.sh: Fix reference commit for code size comparison. Previously the code size comparison was between the merge base (i.e. where the PR branched), and the generated merge commit into master. If the PR branch was older than current master, this meant the size comparison could incorrectly include changes already merged on master but missing from the PR branch. This commit changes it to compare the generated merge commit against current master, i.e. the size impact if this PR was to be merged. This commit also disables running the code size check on "push", it now only runs on pull_request events. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- .github/workflows/code_size.yml | 1 - tools/ci.sh | 44 +++++++++++++++++++++------------ 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/.github/workflows/code_size.yml b/.github/workflows/code_size.yml index 163d4455cffec..67261933798cb 100644 --- a/.github/workflows/code_size.yml +++ b/.github/workflows/code_size.yml @@ -1,7 +1,6 @@ name: Check code size on: - push: pull_request: paths: - '.github/workflows/*.yml' diff --git a/tools/ci.sh b/tools/ci.sh index 0b4b4e11cde42..db6f1cce0110f 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -69,25 +69,37 @@ function ci_code_size_build { PORTS_TO_CHECK=bmusxpdv 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 - git remote add upstream https://github.com/micropython/micropython.git - git fetch --depth=100 upstream master - # If the common ancestor commit hasn't been found, fetch more. - git merge-base upstream/master HEAD || git fetch --unshallow upstream master + # Default GitHub pull request sets HEAD to a generated merge commit + # between PR branch (HEAD^2) and base branch (i.e. master) (HEAD^1). + # + # We want to compare this generated commit with the base branch, to see what + # the code size impact would be if we merged this PR. + REFERENCE=$(git rev-parse --short HEAD^1) + COMPARISON=$(git rev-parse --short HEAD) + + echo "Comparing sizes of reference ${REFERENCE} to ${COMPARISON}..." + git log --oneline $REFERENCE..$COMPARISON + + function code_size_build_step { + COMMIT=$1 + OUTFILE=$2 + IGNORE_ERRORS=$3 + + echo "Building ${COMMIT}..." + git checkout --detach $COMMIT + git submodule update --init $SUBMODULES + git show -s + tools/metrics.py clean $PORTS_TO_CHECK + tools/metrics.py build $PORTS_TO_CHECK | tee $OUTFILE || $IGNORE_ERRORS + } + # build reference, save to size0 # ignore any errors with this build, in case master is failing - git checkout `git merge-base --fork-point upstream/master pull_request` - git submodule update --init $SUBMODULES - git show -s - tools/metrics.py clean $PORTS_TO_CHECK - tools/metrics.py build $PORTS_TO_CHECK | tee ~/size0 || true + code_size_build_step $REFERENCE ~/size0 true # build PR/branch, save to size1 - git checkout pull_request - git submodule update --init $SUBMODULES - git log upstream/master..HEAD - tools/metrics.py clean $PORTS_TO_CHECK - tools/metrics.py build $PORTS_TO_CHECK | tee ~/size1 + code_size_build_step $COMPARISON ~/size1 false + + unset -f code_size_build_step } ######################################################################################## From 0e490b7c8f32bb72c516985abe67147b9385dc34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Klus=C3=A1=C4=8Dek?= Date: Tue, 29 Oct 2024 22:38:24 +0100 Subject: [PATCH 011/546] docs/reference/packages: Fix description of --target option in mip. Descripton of mip usage with micropython port suggest using it like this: ./micropython -m mip install --target=third-party pkgname But it should be called without equal sign: ./micropython -m mip install --target third-party pkgname Signed-off-by: honza.klu@gmail.com --- docs/reference/packages.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/packages.rst b/docs/reference/packages.rst index e47c226b24187..86b8efaa1953d 100644 --- a/docs/reference/packages.rst +++ b/docs/reference/packages.rst @@ -69,9 +69,9 @@ On the Unix port, ``mip`` can be used at the REPL as above, and also by using `` $ ./micropython -m mip install pkgname-or-url $ ./micropython -m mip install pkgname-or-url@version -The ``--target=path``, ``--no-mpy``, and ``--index`` arguments can be set:: +The ``--target path``, ``--no-mpy``, and ``--index`` arguments can be set:: - $ ./micropython -m mip install --target=third-party pkgname + $ ./micropython -m mip install --target third-party pkgname $ ./micropython -m mip install --no-mpy pkgname $ ./micropython -m mip install --index https://host/pi pkgname From 7e1098befe178e4b93657a4d49e6a354037beec4 Mon Sep 17 00:00:00 2001 From: Jan Sturm Date: Tue, 29 Oct 2024 19:26:19 +0100 Subject: [PATCH 012/546] py/objdeque: Fix buffer overflow in deque_subscr. In `deque_subscr()`, if `index_val` equals `self->alloc`, the index correction `index_val -= self->alloc` does not execute, leading to an out-of-bounds access in `self->items[index_val]`. The fix in this commit ensures that the index correction is applied whenever `index_val >= self->alloc`, preventing access beyond the allocated buffer size. Signed-off-by: Jan Sturm --- py/objdeque.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/objdeque.c b/py/objdeque.c index 2ad771284df53..22c380a05a926 100644 --- a/py/objdeque.c +++ b/py/objdeque.c @@ -208,7 +208,7 @@ static mp_obj_t deque_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { size_t offset = mp_get_index(self->base.type, deque_len(self), index, false); size_t index_val = self->i_get + offset; - if (index_val > self->alloc) { + if (index_val >= self->alloc) { index_val -= self->alloc; } From c88a9d60a17594bfd72d57b073eeb9150019b52c Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 30 Oct 2024 15:07:58 +1100 Subject: [PATCH 013/546] tests/basics/deque2.py: Add tests for deque subscript-from-end. Signed-off-by: Damien George --- tests/basics/deque2.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/basics/deque2.py b/tests/basics/deque2.py index 3552d5be37abe..ebc0872c7b4e0 100644 --- a/tests/basics/deque2.py +++ b/tests/basics/deque2.py @@ -31,6 +31,16 @@ d[3] = 5 print(d[3]) +# Access the last element via index, when the last element is at various locations +d = deque((), 2) +for i in range(4): + d.append(i) + print(i, d[-1]) + +# Write the last element then access all elements from the end +d[-1] = 4 +print(d[-2], d[-1]) + # Accessing indices out of bounds raises IndexError try: d[4] From f2ac4719891af9d50520828ae5d68ffa0d9f19c5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 30 Oct 2024 12:04:07 +1100 Subject: [PATCH 014/546] tests/run-tests.py: Simplify the way target-specific tests are given. Signed-off-by: Damien George --- tests/run-tests.py | 158 ++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 74 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index cd1d681af1ee5..f144a763c95f5 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -89,6 +89,88 @@ def open(self, path, mode): __import__('__injected_test') """ +# Tests to skip on specific targets. +# These are tests that are difficult to detect that they should not be run on the given target. +target_tests_to_skip = { + "esp8266": ( + "micropython/viper_args.py", # too large + "micropython/viper_binop_arith.py", # too large + "misc/rge_sm.py", # too large + ), + "minimal": ( + "basics/class_inplace_op.py", # all special methods not supported + "basics/subclass_native_init.py", # native subclassing corner cases not support + "misc/rge_sm.py", # too large + "micropython/opt_level.py", # don't assume line numbers are stored + ), + "nrf": ( + "basics/io_buffered_writer.py", + "basics/io_bytesio_cow.py", + "basics/io_bytesio_ext.py", + "basics/io_bytesio_ext2.py", + "basics/io_iobase.py", + "basics/io_stringio1.py", + "basics/io_stringio_base.py", + "basics/io_stringio_with.py", + "basics/io_write_ext.py", + "basics/memoryview1.py", # no item assignment for memoryview + "extmod/random_basic.py", # unimplemented: random.seed + "micropython/opt_level.py", # no support for line numbers + "misc/non_compliant.py", # no item assignment for bytearray + ), + "renesas-ra": ( + "extmod/time_time_ns.py", # RA fsp rtc function doesn't support nano sec info + ), + "rp2": ( + # Skip thread tests that require more that 2 threads. + "thread/stress_heap.py", + "thread/thread_lock2.py", + "thread/thread_lock3.py", + "thread/thread_shared2.py", + ), + "qemu": ( + # Skip tests that require Cortex-M4. + "inlineasm/asmfpaddsub.py", + "inlineasm/asmfpcmp.py", + "inlineasm/asmfpldrstr.py", + "inlineasm/asmfpmuldiv.py", + "inlineasm/asmfpsqrt.py", + ), + "webassembly": ( + "basics/string_format_modulo.py", # can't print nulls to stdout + "basics/string_strip.py", # can't print nulls to stdout + "extmod/asyncio_basic2.py", + "extmod/asyncio_cancel_self.py", + "extmod/asyncio_current_task.py", + "extmod/asyncio_exception.py", + "extmod/asyncio_gather_finished_early.py", + "extmod/asyncio_get_event_loop.py", + "extmod/asyncio_heaplock.py", + "extmod/asyncio_loop_stop.py", + "extmod/asyncio_new_event_loop.py", + "extmod/asyncio_threadsafeflag.py", + "extmod/asyncio_wait_for_fwd.py", + "extmod/binascii_a2b_base64.py", + "extmod/re_stack_overflow.py", + "extmod/time_res.py", + "extmod/vfs_posix.py", + "extmod/vfs_posix_enoent.py", + "extmod/vfs_posix_paths.py", + "extmod/vfs_userfs.py", + "micropython/emg_exc.py", + "micropython/extreme_exc.py", + "micropython/heapalloc_exc_compressed_emg_exc.py", + ), + "wipy": ( + "misc/print_exception.py", # requires error reporting full + ), + "zephyr": ( + # Skip thread tests that require more than 4 threads. + "thread/stress_heap.py", + "thread/thread_lock3.py", + ), +} + def rm_f(fname): if os.path.exists(fname): @@ -629,85 +711,13 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if t.startswith("thread/mutate_"): skip_tests.add(t) - # Skip thread tests that require many threads on targets that don't support multiple threads. - if args.target == "rp2": - skip_tests.add("thread/stress_heap.py") - skip_tests.add("thread/thread_lock2.py") - skip_tests.add("thread/thread_lock3.py") - skip_tests.add("thread/thread_shared2.py") - elif args.target == "zephyr": - skip_tests.add("thread/stress_heap.py") - skip_tests.add("thread/thread_lock3.py") - # Some tests shouldn't be run on pyboard if args.target != "unix": skip_tests.add("basics/exception_chain.py") # warning is not printed skip_tests.add("micropython/meminfo.py") # output is very different to PC output - if args.target == "wipy": - skip_tests.add("misc/print_exception.py") # requires error reporting full - skip_tests.update( - { - "extmod/uctypes_%s.py" % t - for t in "bytearray le native_le ptr_le ptr_native_le sizeof sizeof_native array_assign_le array_assign_native_le".split() - } - ) # requires uctypes - skip_tests.add("extmod/heapq1.py") # heapq not supported by WiPy - skip_tests.add("extmod/random_basic.py") # requires random - skip_tests.add("extmod/random_extra.py") # requires random - elif args.target == "esp8266": - skip_tests.add("micropython/viper_args.py") # too large - skip_tests.add("micropython/viper_binop_arith.py") # too large - skip_tests.add("misc/rge_sm.py") # too large - elif args.target == "minimal": - skip_tests.add("basics/class_inplace_op.py") # all special methods not supported - skip_tests.add( - "basics/subclass_native_init.py" - ) # native subclassing corner cases not support - skip_tests.add("misc/rge_sm.py") # too large - skip_tests.add("micropython/opt_level.py") # don't assume line numbers are stored - elif args.target == "nrf": - skip_tests.add("basics/memoryview1.py") # no item assignment for memoryview - skip_tests.add("extmod/random_basic.py") # unimplemented: random.seed - skip_tests.add("micropython/opt_level.py") # no support for line numbers - skip_tests.add("misc/non_compliant.py") # no item assignment for bytearray - for t in tests: - if t.startswith("basics/io_"): - skip_tests.add(t) - elif args.target == "renesas-ra": - skip_tests.add( - "extmod/time_time_ns.py" - ) # RA fsp rtc function doesn't support nano sec info - 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") - skip_tests.add("inlineasm/asmfpmuldiv.py") - skip_tests.add("inlineasm/asmfpsqrt.py") - 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 - skip_tests.add("extmod/asyncio_basic2.py") - skip_tests.add("extmod/asyncio_cancel_self.py") - skip_tests.add("extmod/asyncio_current_task.py") - skip_tests.add("extmod/asyncio_exception.py") - skip_tests.add("extmod/asyncio_gather_finished_early.py") - skip_tests.add("extmod/asyncio_get_event_loop.py") - skip_tests.add("extmod/asyncio_heaplock.py") - skip_tests.add("extmod/asyncio_loop_stop.py") - skip_tests.add("extmod/asyncio_new_event_loop.py") - skip_tests.add("extmod/asyncio_threadsafeflag.py") - skip_tests.add("extmod/asyncio_wait_for_fwd.py") - skip_tests.add("extmod/binascii_a2b_base64.py") - skip_tests.add("extmod/re_stack_overflow.py") - skip_tests.add("extmod/time_res.py") - skip_tests.add("extmod/vfs_posix.py") - skip_tests.add("extmod/vfs_posix_enoent.py") - skip_tests.add("extmod/vfs_posix_paths.py") - skip_tests.add("extmod/vfs_userfs.py") - skip_tests.add("micropython/emg_exc.py") - skip_tests.add("micropython/extreme_exc.py") - skip_tests.add("micropython/heapalloc_exc_compressed_emg_exc.py") + # Skip target-specific tests. + skip_tests.update(target_tests_to_skip.get(args.target, ())) # Some tests are known to fail on 64-bit machines if pyb is None and platform.architecture()[0] == "64bit": From 8978102f3595ae321484a6be44c1dcf25c8909a9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 18 Aug 2024 16:21:39 +1000 Subject: [PATCH 015/546] tests/run-tests.py: Change --target/--device options to --test-instance. Previously to this commit, running the test suite on a bare-metal board required specifying the target (really platform) and device, eg: $ ./run-tests.py --target pyboard --device /dev/ttyACM1 That's quite a lot to type, and you also need to know what the target platform is, when a lot of the time you either don't care or it doesn't matter. This commit makes it easier to run the tests by replacing both of these options with a single `--test-instance` (`-t` for short) option. That option specifies the executable/port/device to test. Then the target platform is automatically detected. The `--test-instance` can be passed: - "unix" (the default) to use the unix version of MicroPython - "webassembly" to test the webassembly port - anything else is considered a port/device to pass to Pyboard There are also some shortcuts to specify a port/device, following `mpremote`: - a is short for /dev/ttyACM - u is short for /dev/ttyUSB - c is short for COM For example: $ ./run-tests.py -t a1 Note that the default test instance is "unix" and so this commit does not change the standard way to run tests on the unix port, by just doing `./run-tests.py`. As part of this change, the platform (and it's native architecture if it supports importing native .mpy files) is show at the start of the test run. Signed-off-by: Damien George --- docs/develop/gettingstarted.rst | 2 +- docs/develop/writingtests.rst | 2 +- ports/cc3200/tools/smoke.py | 2 +- ports/qemu/Makefile | 2 +- ports/qemu/README.md | 2 +- ports/webassembly/Makefile | 4 +- tests/README.md | 16 +++- tests/feature_check/target_info.py | 3 +- tests/run-tests.py | 148 ++++++++++++++++++----------- tools/ci.sh | 2 +- 10 files changed, 116 insertions(+), 67 deletions(-) diff --git a/docs/develop/gettingstarted.rst b/docs/develop/gettingstarted.rst index c1fd338c54cc0..fed632ea1ac15 100644 --- a/docs/develop/gettingstarted.rst +++ b/docs/develop/gettingstarted.rst @@ -278,7 +278,7 @@ To run a selection of tests on a board/device connected over USB use: .. code-block:: bash $ cd tests - $ ./run-tests.py --target minimal --device /dev/ttyACM0 + $ ./run-tests.py -t /dev/ttyACM0 See also :ref:`writingtests`. diff --git a/docs/develop/writingtests.rst b/docs/develop/writingtests.rst index 9bb5178f55f22..a7d033f17e9b9 100644 --- a/docs/develop/writingtests.rst +++ b/docs/develop/writingtests.rst @@ -60,7 +60,7 @@ Then to run on a board: .. code-block:: bash - $ ./run-tests.py --target minimal --device /dev/ttyACM0 + $ ./run-tests.py -t /dev/ttyACM0 And to run only a certain set of tests (eg a directory): diff --git a/ports/cc3200/tools/smoke.py b/ports/cc3200/tools/smoke.py index 463a485c4d4c8..f7105411b4d7e 100644 --- a/ports/cc3200/tools/smoke.py +++ b/ports/cc3200/tools/smoke.py @@ -6,7 +6,7 @@ """ Execute it like this: -python3 run-tests.py --target wipy --device 192.168.1.1 ../cc3200/tools/smoke.py +python3 run-tests.py -t 192.168.1.1 ../cc3200/tools/smoke.py """ pin_map = [23, 24, 11, 12, 13, 14, 15, 16, 17, 22, 28, 10, 9, 8, 7, 6, 30, 31, 3, 0, 4, 5] diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index 4d73c025d7dc9..62601324d6a92 100644 --- a/ports/qemu/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 --device execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_ARGS) $(RUN_TESTS_EXTRA) + cd $(TOP)/tests && ./run-tests.py -t 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/README.md b/ports/qemu/README.md index d9108311bef92..984faf8704794 100644 --- a/ports/qemu/README.md +++ b/ports/qemu/README.md @@ -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 --device /dev/pts/1 + $ ./run-tests.py -t /dev/pts/1 Extra make options ------------------ diff --git a/ports/webassembly/Makefile b/ports/webassembly/Makefile index 435636531cc26..9a673b757b29c 100644 --- a/ports/webassembly/Makefile +++ b/ports/webassembly/Makefile @@ -148,10 +148,10 @@ repl: $(BUILD)/micropython.mjs min: $(BUILD)/micropython.min.mjs test: $(BUILD)/micropython.mjs $(TOP)/tests/run-tests.py - cd $(TOP)/tests && MICROPY_MICROPYTHON_MJS=../ports/webassembly/$< ./run-tests.py --target webassembly + cd $(TOP)/tests && MICROPY_MICROPYTHON_MJS=../ports/webassembly/$< ./run-tests.py -t webassembly test_min: $(BUILD)/micropython.min.mjs $(TOP)/tests/run-tests.py - cd $(TOP)/tests && MICROPY_MICROPYTHON_MJS=../ports/webassembly/$< ./run-tests.py --target webassembly + cd $(TOP)/tests && MICROPY_MICROPYTHON_MJS=../ports/webassembly/$< ./run-tests.py -t webassembly ################################################################################ # Remaining make rules. diff --git a/tests/README.md b/tests/README.md index 7bd7eb587177b..b39833aa096f4 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,7 +1,19 @@ # MicroPython Test Suite -This directory contains tests for various functionality areas of MicroPython. -To run all stable tests, run "run-tests.py" script in this directory. +This directory contains tests for most parts of MicroPython. + +To run all stable tests, run the "run-tests.py" script in this directory. By default +that will run the test suite against the unix port of MicroPython. + +To run the test suite against a bare-metal target (a board running MicroPython firmware) +use the `-t` option to specify the serial port. This will automatically detect the +target platform and run the appropriate set of tests for that platform. For example: + + $ ./run-tests.py -t /dev/ttyACM0 + +That will run tests on the `/dev/ttyACM0` serial port. You can also use shortcut +device names like `a` for `/dev/ttyACM` and `c` for `COM`. Use +`./run-tests.py --help` to see all of the device possibilites, and other options. Tests of capabilities not supported on all platforms should be written to check for the capability being present. If it is not, the test diff --git a/tests/feature_check/target_info.py b/tests/feature_check/target_info.py index df89496708aa9..f60f3b319192e 100644 --- a/tests/feature_check/target_info.py +++ b/tests/feature_check/target_info.py @@ -4,6 +4,7 @@ import sys +platform = getattr(sys, "platform", "minimal") sys_mpy = getattr(sys.implementation, "_mpy", 0) arch = [ None, @@ -19,4 +20,4 @@ "xtensawin", "rv32imc", ][sys_mpy >> 10] -print(arch) +print(platform, arch) diff --git a/tests/run-tests.py b/tests/run-tests.py index f144a763c95f5..3db058a3a1d88 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -89,9 +89,12 @@ def open(self, path, mode): __import__('__injected_test') """ +# Platforms associated with the unix port, values of `sys.platform`. +PC_PLATFORMS = ("darwin", "linux", "win32") + # Tests to skip on specific targets. # These are tests that are difficult to detect that they should not be run on the given target. -target_tests_to_skip = { +platform_tests_to_skip = { "esp8266": ( "micropython/viper_args.py", # too large "micropython/viper_binop_arith.py", # too large @@ -161,7 +164,7 @@ def open(self, path, mode): "micropython/extreme_exc.py", "micropython/heapalloc_exc_compressed_emg_exc.py", ), - "wipy": ( + "WiPy": ( "misc/print_exception.py", # requires error reporting full ), "zephyr": ( @@ -197,6 +200,53 @@ def convert_regex_escapes(line): return bytes("".join(cs), "utf8") +def get_test_instance(test_instance, baudrate, user, password): + if test_instance.startswith("port:"): + _, port = test_instance.split(":", 1) + elif test_instance == "unix": + return None + elif test_instance == "webassembly": + return PyboardNodeRunner() + elif test_instance.startswith("a") and test_instance[1:].isdigit(): + port = "/dev/ttyACM" + test_instance[1:] + elif test_instance.startswith("u") and test_instance[1:].isdigit(): + port = "/dev/ttyUSB" + test_instance[1:] + elif test_instance.startswith("c") and test_instance[1:].isdigit(): + port = "COM" + test_instance[1:] + else: + # Assume it's a device path. + port = test_instance + + global pyboard + sys.path.append(base_path("../tools")) + import pyboard + + pyb = pyboard.Pyboard(port, baudrate, user, password) + pyboard.Pyboard.run_script_on_remote_target = run_script_on_remote_target + pyb.enter_raw_repl() + return pyb + + +def detect_test_platform(pyb, args): + # Run a script to detect various bits of information about the target test instance. + output = run_feature_check(pyb, args, "target_info.py") + if output.endswith(b"CRASH"): + raise ValueError("cannot detect platform: {}".format(output)) + platform, arch = str(output, "ascii").strip().split() + if arch == "None": + arch = None + + args.platform = platform + args.arch = arch + if arch and not args.mpy_cross_flags: + args.mpy_cross_flags = "-march=" + arch + + print("platform={}".format(platform), end="") + if arch: + print(" arch={}".format(arch), end="") + print() + + def prepare_script_for_target(args, *, script_filename=None, script_text=None, force_plain=False): if force_plain or (not args.via_mpy and args.emit == "bytecode"): if script_filename is not None: @@ -706,18 +756,18 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("extmod/ssl_poll.py") # Skip thread mutation tests on targets that don't have the GIL. - if args.target in ("rp2", "unix"): + if args.platform in PC_PLATFORMS + ("rp2",): for t in tests: if t.startswith("thread/mutate_"): skip_tests.add(t) # Some tests shouldn't be run on pyboard - if args.target != "unix": + if args.platform not in PC_PLATFORMS: skip_tests.add("basics/exception_chain.py") # warning is not printed skip_tests.add("micropython/meminfo.py") # output is very different to PC output - # Skip target-specific tests. - skip_tests.update(target_tests_to_skip.get(args.target, ())) + # Skip platform-specific tests. + skip_tests.update(platform_tests_to_skip.get(args.platform, ())) # Some tests are known to fail on 64-bit machines if pyb is None and platform.architecture()[0] == "64bit": @@ -932,17 +982,38 @@ def main(): formatter_class=argparse.RawDescriptionHelpFormatter, description="""Run and manage tests for MicroPython. +By default the tests are run against the unix port of MicroPython. To run it +against something else, use the -t option. See below for details. + Tests are discovered by scanning test directories for .py files or using the specified test files. If test files nor directories are specified, the script expects to be ran in the tests directory (where this file is located) and the builtin tests suitable for the target platform are ran. + When running tests, run-tests.py compares the MicroPython output of the test with the output produced by running the test through CPython unless a .exp file is found, in which case it is used as comparison. + If a test fails, run-tests.py produces a pair of .out and .exp files in the result directory with the MicroPython output and the expectations, respectively. """, epilog="""\ +The -t option accepts the following for the test instance: +- unix - use the unix port of MicroPython, specified by the MICROPY_MICROPYTHON + environment variable (which defaults to the standard variant of either the unix + or windows ports, depending on the host platform) +- webassembly - use the webassembly port of MicroPython, specified by the + MICROPY_MICROPYTHON_MJS environment variable (which defaults to the standard + variant of the webassembly port) +- port: - connect to and use the given serial port device +- a - connect to and use /dev/ttyACM +- u - connect to and use /dev/ttyUSB +- c - connect to and use COM +- exec: - execute a command and attach to its stdin/stdout +- execpty: - execute a command and attach to the printed /dev/pts/ device +- ... - connect to the given IPv4 address +- anything else specifies a serial port + Options -i and -e can be multiple and processed in the order given. Regex "search" (vs "match") operation is used. An action (include/exclude) of the last matching regex is used: @@ -951,11 +1022,8 @@ def main(): run-tests.py -e async -i async_foo - include all, exclude async, yet still include async_foo """, ) - cmd_parser.add_argument("--target", default="unix", help="the target platform") cmd_parser.add_argument( - "--device", - default="/dev/ttyACM0", - help="the serial device or the IP address of the pyboard", + "-t", "--test-instance", default="unix", help="the MicroPython instance to test" ) cmd_parser.add_argument( "-b", "--baudrate", default=115200, help="the baud rate of the serial device" @@ -1039,43 +1107,11 @@ def main(): sys.exit(0) - LOCAL_TARGETS = ( - "unix", - "webassembly", - ) - EXTERNAL_TARGETS = ( - "pyboard", - "wipy", - "esp8266", - "esp32", - "minimal", - "nrf", - "qemu", - "renesas-ra", - "rp2", - "zephyr", - ) - if args.target in LOCAL_TARGETS: - pyb = None - if args.target == "webassembly": - pyb = PyboardNodeRunner() - elif args.target in EXTERNAL_TARGETS: - global pyboard - sys.path.append(base_path("../tools")) - import pyboard - - 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)) + # Get the test instance to run on. + pyb = get_test_instance(args.test_instance, args.baudrate, args.user, args.password) - # Automatically detect the native architecture for mpy-cross if not given. - if 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 + # Automatically detect the platform. + detect_test_platform(pyb, args) if args.run_failures and (any(args.files) or args.test_dirs is not None): raise ValueError( @@ -1091,7 +1127,7 @@ def main(): tests = [] elif len(args.files) == 0: test_extensions = ("*.py",) - if args.target == "webassembly": + if args.platform == "webassembly": test_extensions += ("*.js", "*.mjs") if args.test_dirs is None: @@ -1101,23 +1137,23 @@ def main(): "misc", "extmod", ) - if args.target == "pyboard": + if args.platform == "pyboard": # run pyboard tests test_dirs += ("float", "stress", "inlineasm", "ports/stm32") - elif args.target in ("renesas-ra"): + elif args.platform == "renesas-ra": test_dirs += ("float", "inlineasm", "ports/renesas-ra") - elif args.target == "rp2": + elif args.platform == "rp2": test_dirs += ("float", "stress", "thread", "ports/rp2") if "arm" in args.mpy_cross_flags: test_dirs += ("inlineasm",) - elif args.target == "esp32": + elif args.platform == "esp32": test_dirs += ("float", "stress", "thread") - elif args.target in ("esp8266", "minimal", "nrf"): + elif args.platform in ("esp8266", "minimal", "nrf"): test_dirs += ("float",) - elif args.target == "wipy": + elif args.platform == "WiPy": # run WiPy tests test_dirs += ("ports/cc3200",) - elif args.target == "unix": + elif args.platform in PC_PLATFORMS: # run PC tests test_dirs += ( "float", @@ -1128,13 +1164,13 @@ def main(): "cmdline", "ports/unix", ) - elif args.target == "qemu": + elif args.platform == "qemu": test_dirs += ( "float", "inlineasm", "ports/qemu", ) - elif args.target == "webassembly": + elif args.platform == "webassembly": test_dirs += ("float", "ports/webassembly") else: # run tests from these directories diff --git a/tools/ci.sh b/tools/ci.sh index db6f1cce0110f..4e6f09b0bb853 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -785,5 +785,5 @@ 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) + (cd tests && ./run-tests.py -t 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 85053adb243800e7168e5736853bea5364770191 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 30 Oct 2024 13:59:31 +1100 Subject: [PATCH 016/546] tests/run-tests.py: Add mimxrt and samd platforms. Signed-off-by: Damien George --- 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 3db058a3a1d88..d0feb4bcd6635 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -1140,6 +1140,8 @@ def main(): if args.platform == "pyboard": # run pyboard tests test_dirs += ("float", "stress", "inlineasm", "ports/stm32") + elif args.platform == "mimxrt": + test_dirs += ("float", "stress", "inlineasm") elif args.platform == "renesas-ra": test_dirs += ("float", "inlineasm", "ports/renesas-ra") elif args.platform == "rp2": @@ -1148,7 +1150,7 @@ def main(): test_dirs += ("inlineasm",) elif args.platform == "esp32": test_dirs += ("float", "stress", "thread") - elif args.platform in ("esp8266", "minimal", "nrf"): + elif args.platform in ("esp8266", "minimal", "samd", "nrf"): test_dirs += ("float",) elif args.platform == "WiPy": # run WiPy tests From 919756cea4da30ab98cfa00016ed5b3847003219 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Oct 2024 12:41:17 +1100 Subject: [PATCH 017/546] extmod/modlwip: Fix IGMP address type when IPv6 is enabled. This was missed in 628abf8f25a7705a2810fffe2ca6ae652c532896. The the bug was that, when IPv6 is enabled, the `sizeof(ip_addr_t)` is much larger than IPv4 size, which is what's needed for IGMP addressing. Fixes issue #16100. Signed-off-by: Damien George --- extmod/modlwip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 21c6f7264befa..0d38bf41b2051 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -1432,7 +1432,7 @@ static mp_obj_t lwip_socket_setsockopt(size_t n_args, const mp_obj_t *args) { case IP_DROP_MEMBERSHIP: { mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); - if (bufinfo.len != sizeof(ip_addr_t) * 2) { + if (bufinfo.len != sizeof(MP_IGMP_IP_ADDR_TYPE) * 2) { mp_raise_ValueError(NULL); } From d278ba5cc53491037a80e5c4fbd3f94d00ebe3d7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Oct 2024 11:32:59 +1100 Subject: [PATCH 018/546] extmod/nimble: Remove asserts of ediv_rand_present and adjust comments. Recent versions of NimBLE (since release 1.6.0) removed this variable; see https://github.com/apache/mynewt-nimble/commit/7cc8c08d67d52b373eeeec6b33b113192cf2e996. We never used it except in an assert, so remove those asserts to make the code compatible with newer NimBLE versions (eg for the esp32 port). Signed-off-by: Damien George --- extmod/nimble/modbluetooth_nimble.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 8d555f1124f9a..6ca0c172677c7 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -1911,24 +1911,21 @@ static int ble_secret_store_read(int obj_type, const union ble_store_key *key, u // (single) // Find the entry for this specific peer. assert(key->sec.idx == 0); - assert(!key->sec.ediv_rand_present); key_data = (const uint8_t *)&key->sec.peer_addr; key_data_len = sizeof(ble_addr_t); } else { // (with index) // Iterate all known peers. - assert(!key->sec.ediv_rand_present); key_data = NULL; key_data_len = 0; } break; } case BLE_STORE_OBJ_TYPE_OUR_SEC: { - // - // Find our secret for this remote device, matching this ediv/rand key. + // + // Find our secret for this remote device. assert(ble_addr_cmp(&key->sec.peer_addr, BLE_ADDR_ANY)); // Must have address. assert(key->sec.idx == 0); - assert(key->sec.ediv_rand_present); key_data = (const uint8_t *)&key->sec.peer_addr; key_data_len = sizeof(ble_addr_t); break; @@ -1958,10 +1955,6 @@ static int ble_secret_store_read(int obj_type, const union ble_store_key *key, u DEBUG_printf("ble_secret_store_read: found secret\n"); - if (obj_type == BLE_STORE_OBJ_TYPE_OUR_SEC) { - // TODO: Verify ediv_rand matches. - } - return 0; } @@ -1970,14 +1963,13 @@ static int ble_secret_store_write(int obj_type, const union ble_store_value *val switch (obj_type) { case BLE_STORE_OBJ_TYPE_PEER_SEC: case BLE_STORE_OBJ_TYPE_OUR_SEC: { - // + // struct ble_store_key_sec key_sec; const struct ble_store_value_sec *value_sec = &val->sec; ble_store_key_from_value_sec(&key_sec, value_sec); assert(ble_addr_cmp(&key_sec.peer_addr, BLE_ADDR_ANY)); // Must have address. - assert(key_sec.ediv_rand_present); if (!mp_bluetooth_gap_on_set_secret(obj_type, (const uint8_t *)&key_sec.peer_addr, sizeof(ble_addr_t), (const uint8_t *)value_sec, sizeof(struct ble_store_value_sec))) { DEBUG_printf("Failed to write key: type=%d\n", obj_type); @@ -2005,9 +1997,7 @@ static int ble_secret_store_delete(int obj_type, const union ble_store_key *key) case BLE_STORE_OBJ_TYPE_PEER_SEC: case BLE_STORE_OBJ_TYPE_OUR_SEC: { // - assert(ble_addr_cmp(&key->sec.peer_addr, BLE_ADDR_ANY)); // Must have address. - // ediv_rand is optional (will not be present for delete). if (!mp_bluetooth_gap_on_set_secret(obj_type, (const uint8_t *)&key->sec.peer_addr, sizeof(ble_addr_t), NULL, 0)) { DEBUG_printf("Failed to delete key: type=%d\n", obj_type); From 275b3afae70ef71c4c17801d4bf56c0921481b6c Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Oct 2024 11:10:46 +1100 Subject: [PATCH 019/546] esp32/network_wlan: Add missing WLAN security constants. These were added to the `network` module but not the `network.WLAN` class, which is the new home for such constants. Also: - Mark the WLAN constants in the `network` module as deprecated, to be removed in MicroPython 2.0. - Move the static assert to the WLAN source code, to be close to where it relates to. Signed-off-by: Damien George --- ports/esp32/modnetwork_globals.h | 3 ++- ports/esp32/network_common.c | 8 -------- ports/esp32/network_wlan.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/ports/esp32/modnetwork_globals.h b/ports/esp32/modnetwork_globals.h index c0cf229421daf..c6092bd142178 100644 --- a/ports/esp32/modnetwork_globals.h +++ b/ports/esp32/modnetwork_globals.h @@ -13,7 +13,8 @@ { 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 +// These WLAN constants are now in the WLAN class and remain here only for backwards compatibility. +#if !MICROPY_PREVIEW_VERSION_2 && MICROPY_PY_NETWORK_WLAN { MP_ROM_QSTR(MP_QSTR_STA_IF), MP_ROM_INT(WIFI_IF_STA)}, { MP_ROM_QSTR(MP_QSTR_AP_IF), MP_ROM_INT(WIFI_IF_AP)}, diff --git a/ports/esp32/network_common.c b/ports/esp32/network_common.c index d6bba32146c5c..fce8a0304c8be 100644 --- a/ports/esp32/network_common.c +++ b/ports/esp32/network_common.c @@ -337,11 +337,3 @@ static mp_obj_t esp_phy_mode(size_t n_args, const mp_obj_t *args) { return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj, 0, 1, esp_phy_mode); - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) -_Static_assert(WIFI_AUTH_MAX == 13, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); -#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 5) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) || ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) -_Static_assert(WIFI_AUTH_MAX == 11, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); -#else -_Static_assert(WIFI_AUTH_MAX == 10, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); -#endif diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index 7e802166c116c..d415c56d32175 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -744,6 +744,13 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_SEC_WPA2_WPA3), MP_ROM_INT(WIFI_AUTH_WPA2_WPA3_PSK) }, { MP_ROM_QSTR(MP_QSTR_SEC_WAPI), MP_ROM_INT(WIFI_AUTH_WAPI_PSK) }, { MP_ROM_QSTR(MP_QSTR_SEC_OWE), MP_ROM_INT(WIFI_AUTH_OWE) }, + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 5) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) || ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) + { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_ENT_192), MP_ROM_INT(WIFI_AUTH_WPA3_ENT_192) }, + #endif + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) + { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_EXT_PSK), MP_ROM_INT(WIFI_AUTH_WPA3_EXT_PSK) }, + { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_EXT_PSK_MIXED_MODE), MP_ROM_INT(WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE) }, + #endif { MP_ROM_QSTR(MP_QSTR_PM_NONE), MP_ROM_INT(WIFI_PS_NONE) }, { MP_ROM_QSTR(MP_QSTR_PM_PERFORMANCE), MP_ROM_INT(WIFI_PS_MIN_MODEM) }, @@ -751,6 +758,14 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { }; static MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) +_Static_assert(WIFI_AUTH_MAX == 13, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); +#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 5) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) || ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) +_Static_assert(WIFI_AUTH_MAX == 11, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); +#else +_Static_assert(WIFI_AUTH_MAX == 10, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); +#endif + MP_DEFINE_CONST_OBJ_TYPE( esp_network_wlan_type, MP_QSTR_WLAN, From 594670e44623e0635ecf45eb34b0a43f27e04101 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 1 Nov 2024 15:57:40 +1100 Subject: [PATCH 020/546] esp32/machine_pwm: Restore PWM support for ESP-IDF v5.0.x and v5.1.x. The cleanup in 548babf8 relies on some functions not available in older ESP-IDF. Temporarily restore them, until we drop support for ESP-IDF <5.2. PWM functionality should end up the same regardless of ESP-IDF version, and also no different from MicroPython V1.23. Signed-off-by: Angus Gratton --- ports/esp32/machine_pwm.c | 52 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 61efdfa18548b..0134fa2cc8c89 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -34,9 +34,12 @@ #include "py/mphal.h" #include "driver/ledc.h" #include "esp_err.h" -#include "esp_clk_tree.h" #include "soc/gpio_sig_map.h" +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) +#include "esp_clk_tree.h" +#endif + #define PWM_DBG(...) // #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n"); @@ -208,7 +211,41 @@ static void configure_channel(machine_pwm_obj_t *self) { } } +// Temporary workaround for ledc_find_suitable_duty_resolution function only being added in IDF V5.2 +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0) +static uint32_t ledc_find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) { + // This implementation is based on the one used in Micropython v1.23 + + // Find the highest bit resolution for the requested frequency + unsigned int freq = src_clk_freq; + + int divider = (freq + timer_freq / 2) / timer_freq; // rounded + if (divider == 0) { + divider = 1; + } + float f = (float)freq / divider; // actual frequency + if (f <= 1.0) { + f = 1.0; + } + freq = (unsigned int)roundf((float)freq / f); + + unsigned int res = 0; + for (; freq > 1; freq >>= 1) { + ++res; + } + if (res == 0) { + res = 1; + } else if (res > HIGHEST_PWM_RES) { + // Limit resolution to HIGHEST_PWM_RES to match units of our duty + res = HIGHEST_PWM_RES; + } + + return res; +} +#endif + static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) { + esp_err_t err; if (freq != timer->freq_hz) { // Configure the new frequency and resolution timer->freq_hz = freq; @@ -228,10 +265,21 @@ static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf } #endif uint32_t src_clk_freq = 0; - esp_err_t err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq); + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) + err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq); if (err != ESP_OK) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer->clk_cfg); } + #else + // Simplified fallback logic for IDF V5.0.x, for targets with APB only. + src_clk_freq = APB_CLK_FREQ; // 80 MHz + #if SOC_LEDC_SUPPORT_REF_TICK + if (timer->clk_cfg == LEDC_USE_REF_TICK) { + src_clk_freq = REF_CLK_FREQ; // 1 MHz + } + #endif // SOC_LEDC_SUPPORT_REF_TICK + #endif // ESP_IDF_VERSION + timer->duty_resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer->freq_hz); // Set frequency From df6b40a87fe3ddcb37cde488ba1a2060625ab737 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 6 Nov 2024 14:27:01 +1100 Subject: [PATCH 021/546] esp32: Workaround native code execution crash on ESP32-S2. Seemingly ESP-IDF incorrectly marks RTC FAST memory region as MALLOC_CAP_EXEC on ESP32-S2 when it isn't. This memory is the lowest priority, so it only is returned if D/IRAM is exhausted. Apply this workaround to treat the allocation as failed if it gives us non-executable RAM back, rather than crashing. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 03dc0807a025c..18ef9d7354798 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -39,6 +39,7 @@ #include "esp_task.h" #include "esp_event.h" #include "esp_log.h" +#include "esp_memory_utils.h" #include "esp_psram.h" #include "py/cstack.h" @@ -237,6 +238,13 @@ void *esp_native_code_commit(void *buf, size_t len, void *reloc) { len = (len + 3) & ~3; size_t len_node = sizeof(native_code_node_t) + len; native_code_node_t *node = heap_caps_malloc(len_node, MALLOC_CAP_EXEC); + #if CONFIG_IDF_TARGET_ESP32S2 + // Workaround for ESP-IDF bug https://github.com/espressif/esp-idf/issues/14835 + if (node != NULL && !esp_ptr_executable(node)) { + free(node); + node = NULL; + } + #endif // CONFIG_IDF_TARGET_ESP32S2 if (node == NULL) { m_malloc_fail(len_node); } From 48f96e96605c020a646adfc2d06ee0db9b87fbe8 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 5 Nov 2024 11:17:32 +1100 Subject: [PATCH 022/546] docs: Specify the recommended network.WLAN.IF_[AP|STA] constants. Removes the deprecated network.[AP|STA]_IF form from the docs. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/esp32/quickref.rst | 11 ++++++----- docs/esp8266/quickref.rst | 8 ++++---- docs/esp8266/tutorial/network_basics.rst | 13 +++++++------ docs/library/espnow.rst | 24 ++++++++++++------------ docs/library/network.WLAN.rst | 8 ++++---- docs/reference/mpremote.rst | 2 +- 6 files changed, 34 insertions(+), 32 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 3ab4e8f5ecd96..b9ca0f8225f8f 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -79,11 +79,11 @@ Networking WLAN ^^^^ -The :mod:`network` module:: +The :class:`network.WLAN` class in the :mod:`network` module:: import network - wlan = network.WLAN(network.STA_IF) # create station interface + wlan = network.WLAN(network.WLAN.IF_STA) # create station interface wlan.active(True) # activate the interface wlan.scan() # scan for access points wlan.isconnected() # check if the station is connected to an AP @@ -91,7 +91,7 @@ The :mod:`network` module:: wlan.config('mac') # get the interface's MAC address wlan.ipconfig('addr4') # get the interface's IPv4 addresses - ap = network.WLAN(network.AP_IF) # create access-point interface + ap = network.WLAN(network.WLAN.IF_AP) # create access-point interface ap.config(ssid='ESP-AP') # set the SSID of the access point ap.config(max_clients=10) # set how many clients can connect to the network ap.active(True) # activate the interface @@ -100,7 +100,7 @@ A useful function for connecting to your local WiFi network is:: def do_connect(): import network - wlan = network.WLAN(network.STA_IF) + wlan = network.WLAN(network.WLAN.IF_STA) wlan.active(True) if not wlan.isconnected(): print('connecting to network...') @@ -124,7 +124,8 @@ to reconnect forever). LAN ^^^ -To use the wired interfaces one has to specify the pins and mode :: +To use the wired interfaces via :class:`network.LAN` one has to specify the pins +and mode :: import network diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index b130ce65db2b7..6f02da95d89d2 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -49,11 +49,11 @@ The :mod:`esp` module:: Networking ---------- -The :mod:`network` module:: +The :class:`network.WLAN` class in the :mod:`network` module:: import network - wlan = network.WLAN(network.STA_IF) # create station interface + wlan = network.WLAN(network.WLAN.IF_STA) # create station interface wlan.active(True) # activate the interface wlan.scan() # scan for access points wlan.isconnected() # check if the station is connected to an AP @@ -61,7 +61,7 @@ The :mod:`network` module:: wlan.config('mac') # get the interface's MAC address wlan.ipconfig('addr4') # get the interface's IPv4 addresses - ap = network.WLAN(network.AP_IF) # create access-point interface + ap = network.WLAN(network.WLAN.IF_AP) # create access-point interface ap.active(True) # activate the interface ap.config(ssid='ESP-AP') # set the SSID of the access point @@ -69,7 +69,7 @@ A useful function for connecting to your local WiFi network is:: def do_connect(): import network - wlan = network.WLAN(network.STA_IF) + wlan = network.WLAN(network.WLAN.IF_STA) wlan.active(True) if not wlan.isconnected(): print('connecting to network...') diff --git a/docs/esp8266/tutorial/network_basics.rst b/docs/esp8266/tutorial/network_basics.rst index 9d74a6283ac47..e383c00c6ceed 100644 --- a/docs/esp8266/tutorial/network_basics.rst +++ b/docs/esp8266/tutorial/network_basics.rst @@ -1,14 +1,15 @@ Network basics ============== -The network module is used to configure the WiFi connection. There are two WiFi -interfaces, one for the station (when the ESP8266 connects to a router) and one -for the access point (for other devices to connect to the ESP8266). Create +The :class:`network.WLAN` class in the :mod:`network` module is used to +configure the WiFi connection. There are two WiFi interfaces, one for +the station (when the ESP8266 connects to a router) and one for the +access point (for other devices to connect to the ESP8266). Create instances of these objects using:: >>> import network - >>> sta_if = network.WLAN(network.STA_IF) - >>> ap_if = network.WLAN(network.AP_IF) + >>> sta_if = network.WLAN(network.WLAN.IF_STA) + >>> ap_if = network.WLAN(network.WLAN.IF_AP) You can check if the interfaces are active by:: @@ -57,7 +58,7 @@ connect to your WiFi network:: def do_connect(): import network - sta_if = network.WLAN(network.STA_IF) + sta_if = network.WLAN(network.WLAN.IF_STA) if not sta_if.isconnected(): print('connecting to network...') sta_if.active(True) diff --git a/docs/library/espnow.rst b/docs/library/espnow.rst index f0b592dffc8ab..1bcc9d7623d14 100644 --- a/docs/library/espnow.rst +++ b/docs/library/espnow.rst @@ -56,7 +56,7 @@ A simple example would be: import espnow # A WLAN interface must be active to send()/recv() - sta = network.WLAN(network.STA_IF) # Or network.AP_IF + sta = network.WLAN(network.WLAN.IF_STA) # Or network.WLAN.IF_AP sta.active(True) sta.disconnect() # For ESP8266 @@ -76,7 +76,7 @@ A simple example would be: import espnow # A WLAN interface must be active to send()/recv() - sta = network.WLAN(network.STA_IF) + sta = network.WLAN(network.WLAN.IF_STA) sta.active(True) sta.disconnect() # Because ESP8266 auto-connects to last Access Point @@ -182,14 +182,14 @@ Configuration Sending and Receiving Data -------------------------- -A wifi interface (``network.STA_IF`` or ``network.AP_IF``) must be +A wifi interface (``network.WLAN.IF_STA`` or ``network.WLAN.IF_AP``) must be `active()` before messages can be sent or received, but it is not necessary to connect or configure the WLAN interface. For example:: import network - sta = network.WLAN(network.STA_IF) + sta = network.WLAN(network.WLAN.IF_STA) sta.active(True) sta.disconnect() # For ESP8266 @@ -445,8 +445,8 @@ must first register the sender and use the same encryption keys as the sender - *ifidx*: (ESP32 only) Index of the wifi interface which will be used to send data to this peer. Must be an integer set to - ``network.STA_IF`` (=0) or ``network.AP_IF`` (=1). - (default=0/``network.STA_IF``). See `ESPNow and Wifi Operation`_ + ``network.WLAN.IF_STA`` (=0) or ``network.WLAN.IF_AP`` (=1). + (default=0/``network.WLAN.IF_STA``). See `ESPNow and Wifi Operation`_ below for more information. - *encrypt*: (ESP32 only) If set to ``True`` data exchanged with @@ -588,7 +588,7 @@ api-reference/network/esp_now.html#api-reference>`_. For example:: elif err.args[1] == 'ESP_ERR_ESPNOW_NOT_FOUND': e.add_peer(peer) elif err.args[1] == 'ESP_ERR_ESPNOW_IF': - network.WLAN(network.STA_IF).active(True) + network.WLAN(network.WLAN.IF_STA).active(True) else: raise err @@ -645,7 +645,7 @@ A small async server example:: import asyncio # A WLAN interface must be active to send()/recv() - network.WLAN(network.STA_IF).active(True) + network.WLAN(network.WLAN.IF_STA).active(True) e = aioespnow.AIOESPNow() # Returns AIOESPNow enhanced with async support e.active(True) @@ -747,8 +747,8 @@ ESPNow and Wifi Operation ------------------------- ESPNow messages may be sent and received on any `active()` -`WLAN` interface (``network.STA_IF`` or ``network.AP_IF``), even -if that interface is also connected to a wifi network or configured as an access +`WLAN` interface (``network.WLAN.IF_STA`` or ``network.WLAN.IF_AP``), +even if that interface is also connected to a wifi network or configured as an access point. When an ESP32 or ESP8266 device connects to a Wifi Access Point (see `ESP32 Quickref <../esp32/quickref.html#networking>`__) the following things happen which affect ESPNow communications: @@ -832,8 +832,8 @@ Other issues to take care with when using ESPNow with wifi are: import network, time def wifi_reset(): # Reset wifi to AP_IF off, STA_IF on and disconnected - sta = network.WLAN(network.STA_IF); sta.active(False) - ap = network.WLAN(network.AP_IF); ap.active(False) + sta = network.WLAN(network.WLAN.IF_STA); sta.active(False) + ap = network.WLAN(network.WLAN.IF_AP); ap.active(False) sta.active(True) while not sta.active(): time.sleep(0.1) diff --git a/docs/library/network.WLAN.rst b/docs/library/network.WLAN.rst index c1eb520961fcb..3c401acb420bf 100644 --- a/docs/library/network.WLAN.rst +++ b/docs/library/network.WLAN.rst @@ -8,7 +8,7 @@ This class provides a driver for WiFi network processors. Example usage:: import network # enable station interface and connect to WiFi access point - nic = network.WLAN(network.STA_IF) + nic = network.WLAN(network.WLAN.IF_STA) nic.active(True) nic.connect('your-ssid', 'your-key') # now use sockets as usual @@ -18,8 +18,8 @@ Constructors .. class:: WLAN(interface_id) Create a WLAN network interface object. Supported interfaces are -``network.STA_IF`` (station aka client, connects to upstream WiFi access -points) and ``network.AP_IF`` (access point, allows other WiFi clients to +``network.WLAN.IF_STA`` (station aka client, connects to upstream WiFi access +points) and ``network.WLAN.IF_AP`` (access point, allows other WiFi clients to connect). Availability of the methods below depends on interface type. For example, only STA interface may `WLAN.connect()` to an access point. @@ -75,7 +75,7 @@ Methods Return the current status of the wireless connection. When called with no argument the return value describes the network link status. - The possible statuses are defined as constants: + The possible statuses are defined as constants in the :mod:`network` module: * ``STAT_IDLE`` -- no connection and no activity, * ``STAT_CONNECTING`` -- connecting in progress, diff --git a/docs/reference/mpremote.rst b/docs/reference/mpremote.rst index 4d86f0080b117..b47ec76c98efe 100644 --- a/docs/reference/mpremote.rst +++ b/docs/reference/mpremote.rst @@ -477,7 +477,7 @@ An example ``config.py`` might look like: """,], # Print out nearby WiFi networks. "wl_ipconfig": [ "exec", - "import network; sta_if = network.WLAN(network.STA_IF); print(sta_if.ipconfig('addr4'))", + "import network; sta_if = network.WLAN(network.WLAN.IF_STA); 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. From 285e1d0b80d821ba910249e8804697ec1739bac7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 5 Nov 2024 11:18:37 +1100 Subject: [PATCH 023/546] esp32: Use the recommended network.WLAN.IF_[AP|STA] constants. Removes the deprecated network.[AP|STA]_IF form from the esp32 port This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/README.md | 2 +- ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py | 2 +- ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py | 2 +- ports/esp32/help.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index a04ad0c6eea1b..4eb791389380c 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -195,7 +195,7 @@ quickly call `wlan_connect()` and it just works): ```python def wlan_connect(ssid='MYSSID', password='MYPASS'): import network - wlan = network.WLAN(network.STA_IF) + wlan = network.WLAN(network.WLAN.IF_STA) if not wlan.active() or not wlan.isconnected(): wlan.active(True) print('connecting to:', ssid) 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 bfe02c35765b8..98ea0adcbf86c 100644 --- a/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py @@ -30,7 +30,7 @@ def display_wifi(self): self.text("Scan...", 0, 0, 1) self.show() - sta_if = network.WLAN(network.STA_IF) + sta_if = network.WLAN(network.WLAN.IF_STA) sta_if.active(True) _wifi = sta_if.scan() diff --git a/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py b/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py index 37dc5a340fe46..58120d44adf7e 100644 --- a/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py +++ b/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py @@ -37,7 +37,7 @@ def display_wifi(self): self.text("Scan...", 0, 0, 1) self.show() - sta_if = network.WLAN(network.STA_IF) + sta_if = network.WLAN(network.WLAN.IF_STA) sta_if.active(True) _wifi = sta_if.scan() diff --git a/ports/esp32/help.c b/ports/esp32/help.c index 2351d7dc73943..62639a0b408ce 100644 --- a/ports/esp32/help.c +++ b/ports/esp32/help.c @@ -48,7 +48,7 @@ const char esp32_help_text[] = "Basic WiFi configuration:\n" "\n" "import network\n" - "sta_if = network.WLAN(network.STA_IF); sta_if.active(True)\n" + "sta_if = network.WLAN(network.WLAN.IF_STA); sta_if.active(True)\n" "sta_if.scan() # Scan for available access points\n" "sta_if.connect(\"\", \"\") # Connect to an AP\n" "sta_if.isconnected() # Check for successful connection\n" From f5b81bee6141f925ed32d2d7968b0c90d7b41b61 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 5 Nov 2024 11:19:00 +1100 Subject: [PATCH 024/546] esp8266: Use the recommended network.WLAN.IF_[AP|STA] constants. Removes the deprecated network.[AP|STA]_IF form from the esp8266 port This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp8266/help.c | 4 ++-- ports/esp8266/modules/inisetup.py | 2 +- ports/esp8266/modules/port_diag.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/esp8266/help.c b/ports/esp8266/help.c index a9cb27bad51c8..694cd90b951d2 100644 --- a/ports/esp8266/help.c +++ b/ports/esp8266/help.c @@ -36,12 +36,12 @@ const char esp_help_text[] = "Basic WiFi configuration:\n" "\n" "import network\n" - "sta_if = network.WLAN(network.STA_IF); sta_if.active(True)\n" + "sta_if = network.WLAN(network.WLAN.IF_STA); sta_if.active(True)\n" "sta_if.scan() # Scan for available access points\n" "sta_if.connect(\"\", \"\") # Connect to an AP\n" "sta_if.isconnected() # Check for successful connection\n" "# Change name/password of ESP8266's AP:\n" - "ap_if = network.WLAN(network.AP_IF)\n" + "ap_if = network.WLAN(network.WLAN.IF_AP)\n" "ap_if.config(ssid=\"\", security=network.AUTH_WPA_WPA2_PSK, key=\"\")\n" "\n" "Control commands:\n" diff --git a/ports/esp8266/modules/inisetup.py b/ports/esp8266/modules/inisetup.py index 20bb28e80b064..f97c238520026 100644 --- a/ports/esp8266/modules/inisetup.py +++ b/ports/esp8266/modules/inisetup.py @@ -6,7 +6,7 @@ def wifi(): import binascii - ap_if = network.WLAN(network.AP_IF) + ap_if = network.WLAN(network.WLAN.IF_AP) ssid = b"MicroPython-%s" % binascii.hexlify(ap_if.config("mac")[-3:]) ap_if.config(ssid=ssid, security=network.AUTH_WPA_WPA2_PSK, key=b"micropythoN") diff --git a/ports/esp8266/modules/port_diag.py b/ports/esp8266/modules/port_diag.py index 4eea6a6d90d36..bbd6225a616f8 100644 --- a/ports/esp8266/modules/port_diag.py +++ b/ports/esp8266/modules/port_diag.py @@ -23,8 +23,8 @@ def main(): print(esp.check_fw()) print("\nNetworking:") - print("STA ifconfig:", network.WLAN(network.STA_IF).ifconfig()) - print("AP ifconfig:", network.WLAN(network.AP_IF).ifconfig()) + print("STA ifconfig:", network.WLAN(network.WLAN.IF_STA).ifconfig()) + print("AP ifconfig:", network.WLAN(network.WLAN.IF_AP).ifconfig()) print("Free WiFi driver buffers of type:") for i, comm in enumerate( ("1,2 TX", "4 Mngmt TX(len: 0x41-0x100)", "5 Mngmt TX (len: 0-0x40)", "7", "8 RX") From 5dfbd4371480c175e6f54e57e4e5eb5d579cbd00 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 5 Nov 2024 11:19:19 +1100 Subject: [PATCH 025/546] tests: Use the recommended network.WLAN.IF_[AP|STA] constants. Removes the deprecated network.[AP|STA]_IF form from unit tests. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/multi_espnow/10_simple_data.py | 2 +- tests/multi_espnow/20_send_echo.py | 2 +- tests/multi_espnow/30_lmk_echo.py | 2 +- tests/multi_espnow/40_recv_test.py | 2 +- tests/multi_espnow/50_esp32_rssi_test.py | 2 +- tests/multi_espnow/60_irq_test.py | 2 +- tests/multi_espnow/80_asyncio_client.py | 2 +- tests/multi_espnow/81_asyncio_server.py | 2 +- tests/multi_espnow/90_memory_test.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/multi_espnow/10_simple_data.py b/tests/multi_espnow/10_simple_data.py index 1d218fe98130a..00d69f30d9158 100644 --- a/tests/multi_espnow/10_simple_data.py +++ b/tests/multi_espnow/10_simple_data.py @@ -14,7 +14,7 @@ def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/20_send_echo.py b/tests/multi_espnow/20_send_echo.py index 4a1d1624d8430..4c325bf68c513 100644 --- a/tests/multi_espnow/20_send_echo.py +++ b/tests/multi_espnow/20_send_echo.py @@ -61,7 +61,7 @@ def echo_client(e, peer, msglens): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/30_lmk_echo.py b/tests/multi_espnow/30_lmk_echo.py index ac8908049250d..2a6c77c6331db 100644 --- a/tests/multi_espnow/30_lmk_echo.py +++ b/tests/multi_espnow/30_lmk_echo.py @@ -92,7 +92,7 @@ def echo_client(e, peer, msglens): # Initialise the wifi and espnow hardware and software def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/40_recv_test.py b/tests/multi_espnow/40_recv_test.py index 46f4f78df4894..554db16ac1713 100644 --- a/tests/multi_espnow/40_recv_test.py +++ b/tests/multi_espnow/40_recv_test.py @@ -48,7 +48,7 @@ def client_send(e, peer, msg, sync): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/50_esp32_rssi_test.py b/tests/multi_espnow/50_esp32_rssi_test.py index 6a47b540d35ec..8aded1c099400 100644 --- a/tests/multi_espnow/50_esp32_rssi_test.py +++ b/tests/multi_espnow/50_esp32_rssi_test.py @@ -49,7 +49,7 @@ def client_send(e, peer, msg, sync): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/60_irq_test.py b/tests/multi_espnow/60_irq_test.py index 37fc57ce4ae73..db8b8168690e7 100644 --- a/tests/multi_espnow/60_irq_test.py +++ b/tests/multi_espnow/60_irq_test.py @@ -49,7 +49,7 @@ def client_send(e, peer, msg, sync): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/80_asyncio_client.py b/tests/multi_espnow/80_asyncio_client.py index 4d58a451c1c01..8c34b98a3c4a6 100644 --- a/tests/multi_espnow/80_asyncio_client.py +++ b/tests/multi_espnow/80_asyncio_client.py @@ -50,7 +50,7 @@ def client_send(e, peer, msg, sync): def init(e, sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e.active(True) e.set_pmk(default_pmk) wlans[0].active(sta_active) diff --git a/tests/multi_espnow/81_asyncio_server.py b/tests/multi_espnow/81_asyncio_server.py index df1dbb2ac8aa1..e6002582c085d 100644 --- a/tests/multi_espnow/81_asyncio_server.py +++ b/tests/multi_espnow/81_asyncio_server.py @@ -30,7 +30,7 @@ def client_send(e, peer, msg, sync): def init(e, sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e.active(True) e.set_pmk(default_pmk) wlans[0].active(sta_active) diff --git a/tests/multi_espnow/90_memory_test.py b/tests/multi_espnow/90_memory_test.py index 5e80eb0fdf669..b59ff61b594a8 100644 --- a/tests/multi_espnow/90_memory_test.py +++ b/tests/multi_espnow/90_memory_test.py @@ -65,7 +65,7 @@ def echo_client(e, peer, msglens): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) From 3844733d604d83fbfec5592cee20aab065ebff48 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 2 Nov 2024 13:43:35 -0500 Subject: [PATCH 026/546] tests/cpydiff: Fix test case for modules_json_nonserializable. The test case was producing the following error: Traceback (most recent call last): File "", line 12, in UnicodeError: which did not demonstrate the intended difference (this particular non-json-serializable object DID throw an exception! just not TypeError). The updated test uses a byte string with all ASCII bytes inside, which better illustrates the diference. Signed-off-by: Jeff Epler --- tests/cpydiff/modules_json_nonserializable.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/cpydiff/modules_json_nonserializable.py b/tests/cpydiff/modules_json_nonserializable.py index ffe523786f501..d6a5660cadfdb 100644 --- a/tests/cpydiff/modules_json_nonserializable.py +++ b/tests/cpydiff/modules_json_nonserializable.py @@ -6,10 +6,7 @@ """ import json -a = bytes(x for x in range(256)) try: - z = json.dumps(a) - x = json.loads(z) - print("Should not get here") + print(json.dumps(b"shouldn't be able to serialise bytes")) except TypeError: print("TypeError") From eab2869990b83dc6baa451f8c4983a312539a0be Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 5 Nov 2024 11:18:42 +1100 Subject: [PATCH 027/546] extmod/modlwip: Don't allow writing to a TCP socket that is connecting. This follows the behaviour of unix MicroPython (POSIX sockets) and the esp32 port. Signed-off-by: Damien George --- extmod/modlwip.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 0d38bf41b2051..ce70627c31c60 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -701,7 +701,12 @@ static mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui MICROPY_PY_LWIP_ENTER - u16_t available = tcp_sndbuf(socket->pcb.tcp); + // If the socket is still connecting then don't let data be written to it. + // Otherwise, get the number of available bytes in the output buffer. + u16_t available = 0; + if (socket->state != STATE_CONNECTING) { + available = tcp_sndbuf(socket->pcb.tcp); + } if (available == 0) { // Non-blocking socket @@ -718,7 +723,8 @@ static mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui // If peer fully closed socket, we would have socket->state set to ERR_RST (connection // reset) by error callback. // Avoid sending too small packets, so wait until at least 16 bytes available - while (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16) { + while (socket->state == STATE_CONNECTING + || (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16)) { MICROPY_PY_LWIP_EXIT if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) { *_errno = MP_ETIMEDOUT; @@ -1548,7 +1554,7 @@ static mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ // raw socket is writable ret |= MP_STREAM_POLL_WR; #endif - } else if (socket->pcb.tcp != NULL && tcp_sndbuf(socket->pcb.tcp) > 0) { + } else if (socket->state != STATE_CONNECTING && socket->pcb.tcp != NULL && tcp_sndbuf(socket->pcb.tcp) > 0) { // TCP socket is writable // Note: pcb.tcp==NULL if state<0, and in this case we can't call tcp_sndbuf ret |= MP_STREAM_POLL_WR; From 69023622ee837df051ef7e606c2f0511015d01f7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 5 Nov 2024 11:19:45 +1100 Subject: [PATCH 028/546] tests/net_hosted: Improve and simplify non-block-xfer test. CPython changed its non-blocking socket behaviour recently and this test would not run under CPython anymore. So the following steps were taken to get the test working again and then simplify it: - Run the test against CPython 3.10.10 and capture the output into the .exp file for the test. - Run this test on unix port of MicroPython and verify that the output matches the CPython 3.10.10 output in the new .exp file (it did). From now on take unix MicroPython as the source of truth for this test when modifying it. - Remove all code that was there for CPython compatibility. - Make it print out more useful information during the test run, including names of the OSError errno values. - Add polling of the socket before the send/write/recv/read to verify that the poll gives the correct result in non-blocking mode. Tested on unix MicroPython, ESP32_GENERIC, PYBD_SF2 and RPI_PICO_W boards. Signed-off-by: Damien George --- tests/net_hosted/connect_nonblock_xfer.py | 130 +++++++----------- tests/net_hosted/connect_nonblock_xfer.py.exp | 44 ++++++ 2 files changed, 94 insertions(+), 80 deletions(-) create mode 100644 tests/net_hosted/connect_nonblock_xfer.py.exp diff --git a/tests/net_hosted/connect_nonblock_xfer.py b/tests/net_hosted/connect_nonblock_xfer.py index dc4693cea6a8c..23620908afbc6 100644 --- a/tests/net_hosted/connect_nonblock_xfer.py +++ b/tests/net_hosted/connect_nonblock_xfer.py @@ -1,15 +1,24 @@ # test that socket.connect() on a non-blocking socket raises EINPROGRESS # and that an immediate write/send/read/recv does the right thing -import sys, time, socket, errno, ssl +import errno +import select +import socket +import ssl -isMP = sys.implementation.name == "micropython" +# only mbedTLS supports non-blocking mode +if not hasattr(ssl, "MBEDTLS_VERSION"): + print("SKIP") + raise SystemExit -def dp(e): - # uncomment next line for development and testing, to print the actual exceptions - # print(repr(e)) - pass +# get the name of an errno error code +def errno_name(er): + if er == errno.EAGAIN: + return "EAGAIN" + if er == errno.EINPROGRESS: + return "EINPROGRESS" + return er # do_connect establishes the socket and wraps it if tls is True. @@ -22,112 +31,75 @@ def do_connect(peer_addr, tls, handshake): # print("Connecting to", peer_addr) s.connect(peer_addr) except OSError as er: - print("connect:", er.errno == errno.EINPROGRESS) - if er.errno != errno.EINPROGRESS: - print(" got", er.errno) + print("connect:", errno_name(er.errno)) # wrap with ssl/tls if desired if tls: ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - if hasattr(ssl_context, "check_hostname"): - ssl_context.check_hostname = False - try: s = ssl_context.wrap_socket(s, do_handshake_on_connect=handshake) - print("wrap: True") + print("wrap ok: True") except Exception as e: - dp(e) - print("wrap:", e) - elif handshake: - # just sleep a little bit, this allows any connect() errors to happen - time.sleep(0.2) + print("wrap er:", e) return s +# poll a socket and print out the result +def poll(s): + poller = select.poll() + poller.register(s) + print("poll: ", poller.poll(0)) + + # test runs the test against a specific peer address. -def test(peer_addr, tls=False, handshake=False): - # MicroPython plain sockets have read/write, but CPython's don't - # MicroPython TLS sockets and CPython's have read/write - # hasRW captures this wonderful state of affairs - hasRW = isMP or tls +def test(peer_addr, tls, handshake): + # MicroPython plain and TLS sockets have read/write + hasRW = True - # MicroPython plain sockets and CPython's have send/recv - # MicroPython TLS sockets don't have send/recv, but CPython's do - # hasSR captures this wonderful state of affairs - hasSR = not (isMP and tls) + # MicroPython plain sockets have send/recv + # MicroPython TLS sockets don't have send/recv + hasSR = not tls # connect + send + # non-blocking send should raise EAGAIN if hasSR: s = do_connect(peer_addr, tls, handshake) - # send -> 4 or EAGAIN + poll(s) try: ret = s.send(b"1234") - print("send:", handshake and ret == 4) + print("send ok:", ret) # shouldn't get here except OSError as er: - # - dp(er) - print("send:", er.errno in (errno.EAGAIN, errno.EINPROGRESS)) + print("send er:", errno_name(er.errno)) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("send:", True) # connect + write + # non-blocking write should return None if hasRW: s = do_connect(peer_addr, tls, handshake) - # write -> None - try: - ret = s.write(b"1234") - print("write:", ret in (4, None)) # SSL may accept 4 into buffer - except OSError as er: - dp(er) - print("write:", False) # should not raise - except ValueError as er: # CPython - dp(er) - print("write:", er.args[0] == "Write on closed or unwrapped SSL socket.") + poll(s) + ret = s.write(b"1234") + print("write: ", ret) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("write:", True) + # connect + recv + # non-blocking recv should raise EAGAIN if hasSR: - # connect + recv s = do_connect(peer_addr, tls, handshake) - # recv -> EAGAIN + poll(s) try: - print("recv:", s.recv(10)) + ret = s.recv(10) + print("recv ok:", ret) # shouldn't get here except OSError as er: - dp(er) - print("recv:", er.errno == errno.EAGAIN) + print("recv er:", errno_name(er.errno)) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("recv:", True) # connect + read + # non-blocking read should return None if hasRW: s = do_connect(peer_addr, tls, handshake) - # read -> None - try: - ret = s.read(10) - print("read:", ret is None) - except OSError as er: - dp(er) - print("read:", False) # should not raise - except ValueError as er: # CPython - dp(er) - print("read:", er.args[0] == "Read on closed or unwrapped SSL socket.") + poll(s) + ret = s.read(10) + print("read: ", ret) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("read:", True) if __name__ == "__main__": @@ -136,10 +108,8 @@ def test(peer_addr, tls=False, handshake=False): print("--- Plain sockets to nowhere ---") test(socket.getaddrinfo("192.0.2.1", 80)[0][-1], False, False) print("--- SSL sockets to nowhere ---") - # this test fails with AXTLS because do_handshake=False blocks on first read/write and - # there it times out until the connect is aborted test(socket.getaddrinfo("192.0.2.1", 443)[0][-1], True, False) print("--- Plain sockets ---") - test(socket.getaddrinfo("micropython.org", 80)[0][-1], False, True) + test(socket.getaddrinfo("micropython.org", 80)[0][-1], False, False) print("--- SSL sockets ---") test(socket.getaddrinfo("micropython.org", 443)[0][-1], True, True) diff --git a/tests/net_hosted/connect_nonblock_xfer.py.exp b/tests/net_hosted/connect_nonblock_xfer.py.exp new file mode 100644 index 0000000000000..c5498a03873a0 --- /dev/null +++ b/tests/net_hosted/connect_nonblock_xfer.py.exp @@ -0,0 +1,44 @@ +--- Plain sockets to nowhere --- +connect: EINPROGRESS +poll: [] +send er: EAGAIN +connect: EINPROGRESS +poll: [] +write: None +connect: EINPROGRESS +poll: [] +recv er: EAGAIN +connect: EINPROGRESS +poll: [] +read: None +--- SSL sockets to nowhere --- +connect: EINPROGRESS +wrap ok: True +poll: [] +write: None +connect: EINPROGRESS +wrap ok: True +poll: [] +read: None +--- Plain sockets --- +connect: EINPROGRESS +poll: [] +send er: EAGAIN +connect: EINPROGRESS +poll: [] +write: None +connect: EINPROGRESS +poll: [] +recv er: EAGAIN +connect: EINPROGRESS +poll: [] +read: None +--- SSL sockets --- +connect: EINPROGRESS +wrap ok: True +poll: [(, 4)] +write: 4 +connect: EINPROGRESS +wrap ok: True +poll: [(, 4)] +read: None From 76e6c6345cbaa694ac8f33cfc8b8daaa11fd58ed Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 6 Nov 2024 12:43:04 +1100 Subject: [PATCH 029/546] tools/mpremote: Make sure stdout and stderr output appear in order. mpremote error messages now go to stderr, so make sure stdout is flushed before printing them. Also update the test runner to capture error messages. Signed-off-by: Damien George --- tools/mpremote/mpremote/main.py | 3 +++ tools/mpremote/tests/run-mpremote-tests.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index 8c0a6cd224c32..e6e397020fe85 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -547,7 +547,10 @@ def main(): return 0 except CommandError as e: + # Make sure existing stdout appears before the error message on stderr. + sys.stdout.flush() print(f"{_PROG}: {e}", file=sys.stderr) + sys.stderr.flush() return 1 finally: do_disconnect(state) diff --git a/tools/mpremote/tests/run-mpremote-tests.sh b/tools/mpremote/tests/run-mpremote-tests.sh index 11d82c9bb3801..b25b5fd7d2966 100755 --- a/tools/mpremote/tests/run-mpremote-tests.sh +++ b/tools/mpremote/tests/run-mpremote-tests.sh @@ -16,7 +16,7 @@ for t in $TESTS; do TMP=$(mktemp -d) echo -n "${t}: " # Strip CR and replace the random temp dir with a token. - if env MPREMOTE=${MPREMOTE} TMP="${TMP}" "${t}" | tr -d '\r' | sed "s,${TMP},"'${TMP},g' > "${t}.out"; then + if env MPREMOTE=${MPREMOTE} TMP="${TMP}" "${t}" 2>&1 | tr -d '\r' | sed "s,${TMP},"'${TMP},g' > "${t}.out"; then if diff "${t}.out" "${t}.exp" > /dev/null; then echo "OK" else From 3b6024a699491720ac0ba48b8534d6e540e828a9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 6 Nov 2024 12:44:50 +1100 Subject: [PATCH 030/546] tools/mpremote: Add test for forced copy. Signed-off-by: Damien George --- tools/mpremote/tests/test_filesystem.sh | 5 +++++ tools/mpremote/tests/test_filesystem.sh.exp | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/tools/mpremote/tests/test_filesystem.sh b/tools/mpremote/tests/test_filesystem.sh index b6d5a7febcb84..727efea90eb67 100755 --- a/tools/mpremote/tests/test_filesystem.sh +++ b/tools/mpremote/tests/test_filesystem.sh @@ -66,6 +66,11 @@ $MPREMOTE resume cp "${TMP}/a.py" :aaa $MPREMOTE resume cp "${TMP}/a.py" :bbb/b.py $MPREMOTE resume cat :aaa/a.py bbb/b.py +# Test cp -f (force copy). +echo ----- +$MPREMOTE resume cp -f "${TMP}/a.py" :aaa +$MPREMOTE resume cat :aaa/a.py + echo ----- $MPREMOTE resume rm :b.py c.py $MPREMOTE resume ls diff --git a/tools/mpremote/tests/test_filesystem.sh.exp b/tools/mpremote/tests/test_filesystem.sh.exp index 7220dc41e472c..b252bc7eb0d03 100644 --- a/tools/mpremote/tests/test_filesystem.sh.exp +++ b/tools/mpremote/tests/test_filesystem.sh.exp @@ -38,6 +38,10 @@ print("World") print("Hello") print("World") ----- +cp ${TMP}/a.py :aaa +print("Hello") +print("World") +----- rm :b.py rm :c.py ls : From 4fd5b72a8b71c6b3a074bcc3b9f6385b049c3dcb Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 6 Nov 2024 12:45:06 +1100 Subject: [PATCH 031/546] tools/mpremote: Support trailing slash on dest for non-recursive copy. This fixes a regression in db59e55fe7a0b67d3af868990468e7b8056afe42: prior to that commit `mpremote` supported trailing slashes on the destination of a normal (non-recursive) copy. Add back support for that, with the semantics that a trailing slash requires the destination to be an existing directory. Also add a test for this. Signed-off-by: Damien George --- tools/mpremote/mpremote/commands.py | 11 +++++++++-- tools/mpremote/tests/test_filesystem.sh | 5 +++++ tools/mpremote/tests/test_filesystem.sh.exp | 6 ++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tools/mpremote/mpremote/commands.py b/tools/mpremote/mpremote/commands.py index 2b1acea438084..f86befd080349 100644 --- a/tools/mpremote/mpremote/commands.py +++ b/tools/mpremote/mpremote/commands.py @@ -129,8 +129,15 @@ def _remote_path_basename(a): def do_filesystem_cp(state, src, dest, multiple, check_hash=False): if dest.startswith(":"): - dest_exists = state.transport.fs_exists(dest[1:]) - dest_isdir = dest_exists and state.transport.fs_isdir(dest[1:]) + dest_no_slash = dest.rstrip("/" + os.path.sep + (os.path.altsep or "")) + dest_exists = state.transport.fs_exists(dest_no_slash[1:]) + dest_isdir = dest_exists and state.transport.fs_isdir(dest_no_slash[1:]) + + # A trailing / on dest forces it to be a directory. + if dest != dest_no_slash: + if not dest_isdir: + raise CommandError("cp: destination is not a directory") + dest = dest_no_slash else: dest_exists = os.path.exists(dest) dest_isdir = dest_exists and os.path.isdir(dest) diff --git a/tools/mpremote/tests/test_filesystem.sh b/tools/mpremote/tests/test_filesystem.sh index 727efea90eb67..afeb7c91da8d6 100755 --- a/tools/mpremote/tests/test_filesystem.sh +++ b/tools/mpremote/tests/test_filesystem.sh @@ -71,6 +71,11 @@ echo ----- $MPREMOTE resume cp -f "${TMP}/a.py" :aaa $MPREMOTE resume cat :aaa/a.py +# Test cp where the destination has a trailing /. +echo ----- +$MPREMOTE resume cp "${TMP}/a.py" :aaa/ +$MPREMOTE resume cp "${TMP}/a.py" :aaa/a.py/ || echo "expect error" + echo ----- $MPREMOTE resume rm :b.py c.py $MPREMOTE resume ls diff --git a/tools/mpremote/tests/test_filesystem.sh.exp b/tools/mpremote/tests/test_filesystem.sh.exp index b252bc7eb0d03..82fe7d6bf78c5 100644 --- a/tools/mpremote/tests/test_filesystem.sh.exp +++ b/tools/mpremote/tests/test_filesystem.sh.exp @@ -42,6 +42,12 @@ cp ${TMP}/a.py :aaa print("Hello") print("World") ----- +cp ${TMP}/a.py :aaa/ +Up to date: aaa/a.py +cp ${TMP}/a.py :aaa/a.py/ +mpremote: cp: destination is not a directory +expect error +----- rm :b.py rm :c.py ls : From 161e2bd37df70556ef2a276c53af2c0cdc79af5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Wed, 9 Aug 2023 10:31:29 +0200 Subject: [PATCH 032/546] extmod/network_ppp: Add stream config parameter. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the stream that the PPP object wraps, which is normally only set once via the constructor, accessible and configurable via the `ppp.config()` method. Signed-off-by: Daniël van de Giessen --- docs/library/network.PPP.rst | 7 +++++-- extmod/network_ppp_lwip.c | 11 ++++++++++- ports/esp32/network_ppp.c | 9 +++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/library/network.PPP.rst b/docs/library/network.PPP.rst index 85f580ce540ed..17a8d1c1cd89d 100644 --- a/docs/library/network.PPP.rst +++ b/docs/library/network.PPP.rst @@ -70,8 +70,11 @@ Methods .. method:: PPP.config(config_parameters) - Sets or gets parameters of the PPP interface. There are currently no parameter that - can be set or retrieved. + Sets or gets parameters of the PPP interface. The only parameter that can be + retrieved and set is the underlying stream, using:: + + stream = PPP.config("stream") + PPP.config(stream=stream) .. method:: PPP.ipconfig('param') PPP.ipconfig(param=value, ...) diff --git a/extmod/network_ppp_lwip.c b/extmod/network_ppp_lwip.c index 2b77662a24f26..72d02602c0cc9 100644 --- a/extmod/network_ppp_lwip.c +++ b/extmod/network_ppp_lwip.c @@ -153,12 +153,17 @@ static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t 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]); + 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)) { + case MP_QSTR_stream: { + mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + self->stream = kwargs->table[i].value; + break; + } default: break; } @@ -174,6 +179,10 @@ static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t mp_obj_t val = mp_const_none; switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_stream: { + val = self->stream; + break; + } default: mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); } diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index f2813f430d482..4f49efaf060a6 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -323,6 +323,11 @@ static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs 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)) { + case MP_QSTR_stream: { + mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + self->stream = kwargs->table[i].value; + break; + } default: break; } @@ -338,6 +343,10 @@ static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs mp_obj_t val = mp_const_none; switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_stream: { + val = self->stream; + break; + } case MP_QSTR_ifname: { if (self->pcb != NULL) { struct netif *pppif = ppp_netif(self->pcb); From 77406b4240b0489963782a7f18920ba3c5d11263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Wed, 28 Feb 2024 12:35:37 +0100 Subject: [PATCH 033/546] extmod/network_ppp: Allow stream=None to suspend PPP. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows the stream to be set to `None`, which essentially stops all PPP communication without disconnecting the session. This allows replacing the stream on-the-fly to suspend it, for example to send AT commands to a modem without completely disconnecting and re-establishing the PPP connection: uart = ppp.config('stream') ppp.config(stream=None) uart.write(b'+++') # do some AT commands uart.write(b'ATO\r\n') ppp.config(stream=uart) Any attempted communication by PPP while the stream is not connected will register as simple packet loss to the LwIP stack because we return 0 for any write calls, and protocols like TCP will then automatically handle retrying. Signed-off-by: Daniël van de Giessen --- extmod/network_ppp_lwip.c | 68 ++++++++++++++++++++++++++++----------- ports/esp32/network_ppp.c | 29 +++++++++++++---- 2 files changed, 72 insertions(+), 25 deletions(-) diff --git a/extmod/network_ppp_lwip.c b/extmod/network_ppp_lwip.c index 72d02602c0cc9..8eb90ea4a6529 100644 --- a/extmod/network_ppp_lwip.c +++ b/extmod/network_ppp_lwip.c @@ -60,6 +60,18 @@ const mp_obj_type_t mp_network_ppp_lwip_type; static mp_obj_t network_ppp___del__(mp_obj_t self_in); +static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } + + // 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); +} + static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { network_ppp_obj_t *self = ctx; switch (err_code) { @@ -68,12 +80,9 @@ static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { 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. + network_ppp_stream_uart_irq_disable(self); + // Indicate that we are no longer connected and thus + // only need to free the PPP PCB, not close it. self->state = STATE_ACTIVE; } // Clean up the PPP PCB. @@ -91,7 +100,9 @@ static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, s mp_obj_t stream = all_args[0]; - mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + if (stream != mp_const_none) { + 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; @@ -105,7 +116,7 @@ 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. + // Still connected over the stream. // Force the connection to close, with nocarrier=1. self->state = STATE_INACTIVE; ppp_close(self->pcb, 1); @@ -127,10 +138,11 @@ static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) { } mp_int_t total_len = 0; - for (;;) { + mp_obj_t stream = self->stream; + while (stream != mp_const_none) { uint8_t buf[256]; int err; - mp_uint_t len = mp_stream_rw(self->stream, buf, sizeof(buf), &err, 0); + mp_uint_t len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); if (len == 0) { break; } @@ -149,6 +161,19 @@ static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_poll_obj, 1, 2, network_ppp_poll); +static void network_ppp_stream_uart_irq_enable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } + + // 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); +} + 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")); @@ -160,8 +185,16 @@ static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t if (mp_map_slot_is_filled(kwargs, i)) { switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { case MP_QSTR_stream: { - mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + if (kwargs->table[i].value != mp_const_none) { + mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_disable(self); + } self->stream = kwargs->table[i].value; + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_enable(self); + } break; } default: @@ -210,10 +243,14 @@ static u32_t network_ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t l } mp_printf(&mp_plat_print, ")\n"); #endif + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + return 0; + } 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); + return mp_stream_rw(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) { @@ -236,12 +273,7 @@ static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_ } 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); + network_ppp_stream_uart_irq_enable(self); } if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) { diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index 4f49efaf060a6..5c41cf948c1ed 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -85,7 +85,9 @@ static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { } static mp_obj_t ppp_make_new(mp_obj_t stream) { - mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + if (stream != mp_const_none) { + mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } ppp_if_obj_t *self = mp_obj_malloc_with_finaliser(ppp_if_obj_t, &ppp_if_type); self->stream = stream; @@ -100,8 +102,14 @@ MP_DEFINE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj, ppp_make_new); static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) { ppp_if_obj_t *self = ctx; + + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + return 0; + } + int err; - return mp_stream_rw(self->stream, data, len, &err, MP_STREAM_RW_WRITE); + return mp_stream_rw(stream, data, len, &err, MP_STREAM_RW_WRITE); } static void pppos_client_task(void *self_in) { @@ -110,10 +118,15 @@ static void pppos_client_task(void *self_in) { int len = 0; while (ulTaskNotifyTake(pdTRUE, len <= 0) == 0) { - int err; - len = mp_stream_rw(self->stream, buf, sizeof(buf), &err, 0); - if (len > 0) { - pppos_input_tcpip(self->pcb, (u8_t *)buf, len); + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + len = 0; + } else { + int err; + len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); + if (len > 0) { + pppos_input_tcpip(self->pcb, (u8_t *)buf, len); + } } } @@ -324,7 +337,9 @@ static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs if (mp_map_slot_is_filled(kwargs, i)) { switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { case MP_QSTR_stream: { - mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + if (kwargs->table[i].value != mp_const_none) { + mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } self->stream = kwargs->table[i].value; break; } From 611d8f9ce82ab5e04fb86ab6cfc28d5ed98f33bf Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Nov 2024 12:04:57 +1100 Subject: [PATCH 034/546] esp32/modsocket: Fix getaddrinfo hints to set AI_CANONNAME. Because the `ai_canonname` field is subsequently used. ESP32_GENERIC_S3 (at least) crashes with IDF 5.2.3 without this set. Signed-off-by: Damien George --- ports/esp32/modsocket.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 916eb79bd9265..7ea5e855d325e 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -213,7 +213,7 @@ static int mdns_getaddrinfo(const char *host_str, const char *port_str, #endif // MICROPY_HW_ENABLE_MDNS_QUERIES static void _getaddrinfo_inner(const mp_obj_t host, const mp_obj_t portx, - const struct addrinfo *hints, struct addrinfo **res) { + struct addrinfo *hints, struct addrinfo **res) { int retval = 0; *res = NULL; @@ -235,6 +235,9 @@ static void _getaddrinfo_inner(const mp_obj_t host, const mp_obj_t portx, MP_THREAD_GIL_EXIT(); + // The ai_canonname field is used below, so set the hint. + hints->ai_flags |= AI_CANONNAME; + #if MICROPY_HW_ENABLE_MDNS_QUERIES retval = mdns_getaddrinfo(host_str, port_str, hints, res); #endif @@ -264,7 +267,8 @@ static void _getaddrinfo_inner(const mp_obj_t host, const mp_obj_t portx, static void _socket_getaddrinfo(const mp_obj_t addrtuple, struct addrinfo **resp) { mp_obj_t *elem; mp_obj_get_array_fixed_n(addrtuple, 2, &elem); - _getaddrinfo_inner(elem[0], elem[1], NULL, resp); + struct addrinfo hints = { 0 }; + _getaddrinfo_inner(elem[0], elem[1], &hints, resp); } static mp_obj_t socket_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { From 5dc9eda1953668eb6861be01ca85f147dcf8d406 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 13 Nov 2024 14:21:29 +1100 Subject: [PATCH 035/546] extmod/vfs_blockdev: Support bool return from Python read/write blocks. Commit f4ab9d924790581989f2398fe30bbac5d680577f inadvertently broke some Python block devices, for example esp32 and stm32 SDCard classes. Those classes return a bool from their `readblocks` and `writeblocks` methods instead of an integer errno code. With that change, both `False` and `True` return values are now be interpreted as non-zero and hence the block device call fails. The fix in this commit is to allow a bool and explicitly convert `True` to 0 and `False` to `-MP_EIO`. Signed-off-by: Damien George --- extmod/vfs_blockdev.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/extmod/vfs_blockdev.c b/extmod/vfs_blockdev.c index a7c14b76ee368..d43c96b08f326 100644 --- a/extmod/vfs_blockdev.c +++ b/extmod/vfs_blockdev.c @@ -58,6 +58,13 @@ static int mp_vfs_blockdev_call_rw(mp_obj_t *args, size_t block_num, size_t bloc if (ret == mp_const_none) { return 0; } else { + // Some block devices return a bool indicating success, so + // convert those to an errno integer code. + if (ret == mp_const_true) { + return 0; + } else if (ret == mp_const_false) { + return -MP_EIO; + } // Block device functions are expected to return 0 on success // and negative integer on errors. Check for positive integer // results as some callers (i.e. littlefs) will produce corrupt From 898407defbb6327fde379304846999033bf349c5 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 4 Nov 2024 16:04:56 +0100 Subject: [PATCH 036/546] ports: Make PWM duty_u16 have an upper value of 65535 across all ports. The following ports used 65536 as the upper value (100% duty cycle) and are changed in this commit to use 65535: esp8266, mimxrt, nrf, samd. Tested that output is high at `duty_u16(65535)` and low at `duty_u16(0)`. Also verified that at `duty_u16(32768)` the high and low pulse have the same length. Partially reverts #10850, commits 9c7ad68165bcd224c94ca6d8f172362cf8000d99 and 2ac643c15bec8c88ece0e944ce58f36d02dfd2dd. Signed-off-by: robert-hh --- docs/mimxrt/quickref.rst | 6 +++--- docs/samd/quickref.rst | 4 ++-- ports/esp8266/machine_pwm.c | 6 +++--- ports/mimxrt/hal/pwm_backport.c | 6 +++--- ports/mimxrt/hal/pwm_backport.h | 2 +- ports/nrf/modules/machine/pwm.c | 6 +++--- ports/samd/machine_pwm.c | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index cfd0605054a7b..34e0aa79f139f 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -193,7 +193,7 @@ PWM Constructor - *freq* should be an integer which sets the frequency in Hz for the PWM cycle. The valid frequency range is 15 Hz resp. 18Hz resp. 24Hz up to > 1 MHz. - - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65536``. + - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65535``. The duty cycle of a X channel can only be changed, if the A and B channel of the respective submodule is not used. Otherwise the duty_16 value of the X channel is 32768 (50%). @@ -231,7 +231,7 @@ is created by dividing the pwm_clk signal by an integral factor, according to th f = pwm_clk / (2**n * m) -with n being in the range of 0..7, and m in the range of 2..65536. pmw_clk is 125Mhz +with n being in the range of 0..7, and m in the range of 2..65535. pmw_clk is 125Mhz for MIMXRT1010/1015/1020, 150 MHz for MIMXRT1050/1060/1064 and 160MHz for MIMXRT1170. The lowest frequency is pwm_clk/2**23 (15, 18, 20Hz). The highest frequency with U16 resolution is pwm_clk/2**16 (1907, 2288, 2441 Hz), the highest frequency @@ -255,7 +255,7 @@ Use the :ref:`machine.ADC ` class:: from machine import ADC adc = ADC(Pin('A2')) # create ADC object on ADC pin - adc.read_u16() # read value, 0-65536 across voltage range 0.0v - 3.3v + adc.read_u16() # read value, 0-65535 across voltage range 0.0v - 3.3v The resolution of the ADC is 12 bit with 10 to 11 bit accuracy, irrespective of the value returned by read_u16(). If you need a higher resolution or better accuracy, use diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst index 25b5a8fc8ab2b..d57dc67908398 100644 --- a/docs/samd/quickref.rst +++ b/docs/samd/quickref.rst @@ -215,7 +215,7 @@ PWM Constructor - *freq* should be an integer which sets the frequency in Hz for the PWM cycle. The valid frequency range is 1 Hz to 24 MHz. - - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65536``. + - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65535``. - *duty_ns* sets the pulse width in nanoseconds. The limitation for X channels apply as well. - *invert*\=True|False. Setting a bit inverts the respective output. @@ -246,7 +246,7 @@ Use the :ref:`machine.ADC ` class:: from machine import ADC adc0 = ADC(Pin('A0')) # create ADC object on ADC pin, average=16 - adc0.read_u16() # read value, 0-65536 across voltage range 0.0v - 3.3v + adc0.read_u16() # read value, 0-65535 across voltage range 0.0v - 3.3v adc1 = ADC(Pin('A1'), average=1) # create ADC object on ADC pin, average=1 The resolution of the ADC is 12 bit with 12 bit accuracy, irrespective of the diff --git a/ports/esp8266/machine_pwm.c b/ports/esp8266/machine_pwm.c index 25a2d6898f7c7..0d5ed595428e3 100644 --- a/ports/esp8266/machine_pwm.c +++ b/ports/esp8266/machine_pwm.c @@ -80,7 +80,7 @@ static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, c pwm_set_duty(args[ARG_duty].u_int, self->channel); } if (args[ARG_duty_u16].u_int != -1) { - pwm_set_duty(args[ARG_duty_u16].u_int * 1000 / 65536, self->channel); + pwm_set_duty(args[ARG_duty_u16].u_int * 1000 / 65535, self->channel); } if (args[ARG_duty_ns].u_int != -1) { uint32_t freq = pwm_get_freq(0); @@ -164,13 +164,13 @@ static void mp_machine_pwm_duty_set(machine_pwm_obj_t *self, mp_int_t duty) { static mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { set_active(self, true); - return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel) * 65536 / 1024); + return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel) * 65535 / 1024); } static void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty) { set_active(self, false); self->duty_ns = -1; - pwm_set_duty(duty * 1024 / 65536, self->channel); + pwm_set_duty(duty * 1024 / 65535, self->channel); pwm_start(); } diff --git a/ports/mimxrt/hal/pwm_backport.c b/ports/mimxrt/hal/pwm_backport.c index 7732e0e8167c8..3df826496392d 100644 --- a/ports/mimxrt/hal/pwm_backport.c +++ b/ports/mimxrt/hal/pwm_backport.c @@ -36,7 +36,7 @@ void PWM_UpdatePwmDutycycle_u16( // Setup the PWM dutycycle of channel A or B if (pwmSignal == kPWM_PwmA) { - if (dutyCycle >= 65536) { + if (dutyCycle >= PWM_FULL_SCALE) { base->SM[subModule].VAL2 = 0; base->SM[subModule].VAL3 = pulseCnt; } else { @@ -44,7 +44,7 @@ void PWM_UpdatePwmDutycycle_u16( base->SM[subModule].VAL3 = base->SM[subModule].VAL2 + pwmHighPulse; } } else { - if (dutyCycle >= 65536) { + if (dutyCycle >= PWM_FULL_SCALE) { base->SM[subModule].VAL4 = 0; base->SM[subModule].VAL5 = pulseCnt; } else { @@ -160,7 +160,7 @@ status_t QTMR_SetupPwm_u16(TMR_Type *base, qtmr_channel_selection_t channel, uin if (dutyCycleU16 == 0) { // Clear the output at the next compare reg |= (TMR_CTRL_LENGTH_MASK | TMR_CTRL_OUTMODE(kQTMR_ClearOnCompare)); - } else if (dutyCycleU16 >= 65536) { + } else if (dutyCycleU16 >= PWM_FULL_SCALE) { // Set the output at the next compare reg |= (TMR_CTRL_LENGTH_MASK | TMR_CTRL_OUTMODE(kQTMR_SetOnCompare)); } else { diff --git a/ports/mimxrt/hal/pwm_backport.h b/ports/mimxrt/hal/pwm_backport.h index 04899173e20b3..9c9f6811add28 100644 --- a/ports/mimxrt/hal/pwm_backport.h +++ b/ports/mimxrt/hal/pwm_backport.h @@ -24,7 +24,7 @@ typedef struct _pwm_signal_param_u16 uint16_t deadtimeValue; // The deadtime value; only used if channel pair is operating in complementary mode } pwm_signal_param_u16_t; -#define PWM_FULL_SCALE (65536UL) +#define PWM_FULL_SCALE (65535UL) void PWM_UpdatePwmDutycycle_u16(PWM_Type *base, pwm_submodule_t subModule, pwm_channels_t pwmSignal, uint32_t dutyCycle, uint16_t center); diff --git a/ports/nrf/modules/machine/pwm.c b/ports/nrf/modules/machine/pwm.c index 8145509c76284..13d824e86674f 100644 --- a/ports/nrf/modules/machine/pwm.c +++ b/ports/nrf/modules/machine/pwm.c @@ -285,7 +285,7 @@ static mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) { if (self->p_config->duty_mode[self->channel] == DUTY_PERCENT) { return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel]); } else if (self->p_config->duty_mode[self->channel] == DUTY_U16) { - return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 100 / 65536); + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 100 / 65535); } else { return MP_OBJ_NEW_SMALL_INT(-1); } @@ -301,7 +301,7 @@ static mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { if (self->p_config->duty_mode[self->channel] == DUTY_U16) { return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel]); } else if (self->p_config->duty_mode[self->channel] == DUTY_PERCENT) { - return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 65536 / 100); + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 65535 / 100); } else { return MP_OBJ_NEW_SMALL_INT(-1); } @@ -365,7 +365,7 @@ static void machine_hard_pwm_start(const machine_pwm_obj_t *self) { if (self->p_config->duty_mode[i] == DUTY_PERCENT) { pulse_width = ((period * self->p_config->duty[i]) / 100); } else if (self->p_config->duty_mode[i] == DUTY_U16) { - pulse_width = ((period * self->p_config->duty[i]) / 65536); + pulse_width = ((period * self->p_config->duty[i]) / 65535); } else if (self->p_config->duty_mode[i] == DUTY_NS) { pulse_width = (uint64_t)self->p_config->duty[i] * tick_freq / 1000000000ULL; } diff --git a/ports/samd/machine_pwm.c b/ports/samd/machine_pwm.c index b2a383c21cba1..468e34a1653dd 100644 --- a/ports/samd/machine_pwm.c +++ b/ports/samd/machine_pwm.c @@ -54,7 +54,7 @@ typedef struct _machine_pwm_obj_t { #define PWM_CLK_READY (1) #define PWM_TCC_ENABLED (2) #define PWM_MASTER_CLK (get_peripheral_freq()) -#define PWM_FULL_SCALE (65536) +#define PWM_FULL_SCALE (65535) #define PWM_UPDATE_TIMEOUT (2000) #define VALUE_NOT_SET (-1) From e2532e0f725107becea3cf7e6d14727ab3abec7c Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 6 Nov 2024 20:26:27 +0100 Subject: [PATCH 037/546] mimxrt/machine_pwm: Fix a few inconsistencies with PWM output. Changes in this commit: - When setting PWM parameters of a FLEXPWM AB channel twice within a PWM cycle, the second setting was ignored. Now the second setting persists. - With `duty_u16(0)` a FLEXPWM X channel was set to high impedance. Now it is set to low impedance with value 0 for `invert=False`, and 1 for `invert=True`. - The align parameter requires a duty rate and frequency to be set. Align will now be ignored if freq or duty are missing. Signed-off-by: robert-hh --- ports/mimxrt/hal/pwm_backport.c | 8 ++------ ports/mimxrt/machine_pwm.c | 29 ++++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/ports/mimxrt/hal/pwm_backport.c b/ports/mimxrt/hal/pwm_backport.c index 3df826496392d..7815fd1dff57d 100644 --- a/ports/mimxrt/hal/pwm_backport.c +++ b/ports/mimxrt/hal/pwm_backport.c @@ -110,12 +110,8 @@ void PWM_SetupPwmx_u16(PWM_Type *base, pwm_submodule_t subModule, base->SM[subModule].OCTRL = (base->SM[subModule].OCTRL & ~PWM_OCTRL_POLX_MASK) | PWM_OCTRL_POLX(!invert); - // Switch the output on or off. - if (duty_cycle == 0) { - base->OUTEN &= ~(1U << subModule); - } else { - base->OUTEN |= (1U << subModule); - } + // Enable PWM output + base->OUTEN |= (1U << subModule); } #ifdef FSL_FEATURE_SOC_TMR_COUNT diff --git a/ports/mimxrt/machine_pwm.c b/ports/mimxrt/machine_pwm.c index f9ae5e22edcc3..b68521281ae6c 100644 --- a/ports/mimxrt/machine_pwm.c +++ b/ports/mimxrt/machine_pwm.c @@ -45,6 +45,8 @@ typedef struct _machine_pwm_obj_t { mp_obj_base_t base; PWM_Type *instance; + const machine_pin_obj_t *pwm_pin; + const machine_pin_af_obj_t *pwm_pin_af_obj; bool is_flexpwm; uint8_t complementary; uint8_t module; @@ -255,6 +257,8 @@ static void configure_flexpwm(machine_pwm_obj_t *self) { PWM_SetupFaultDisableMap(self->instance, self->submodule, self->channel2, kPWM_faultchannel_1, 0); } + // clear the load okay bit for the submodules in case there is a pending load + PWM_SetPwmLdok(self->instance, 1 << self->submodule, false); if (self->channel1 != kPWM_PwmX) { // Only for A/B channels // Initialize the channel parameters pwmSignal.pwmChannel = self->channel1; @@ -283,6 +287,17 @@ static void configure_flexpwm(machine_pwm_obj_t *self) { self->instance->SM[self->submodule].CTRL &= ~(PWM_CTRL_DBLEN_MASK | PWM_CTRL_SPLIT_MASK); } } else { + if (self->duty_u16 == 0) { + // For duty_u16 == 0 just set the output to GPIO mode + if (self->invert) { + mp_hal_pin_high(self->pwm_pin); + } else { + mp_hal_pin_low(self->pwm_pin); + } + IOMUXC_SetPinMux(self->pwm_pin->muxRegister, PIN_AF_MODE_ALT5, 0, 0, 0, 0U); + } else { + IOMUXC_SetPinMux(self->pwm_pin->muxRegister, self->pwm_pin_af_obj->af_mode, 0, 0, 0, 0U); + } PWM_SetupPwmx_u16(self->instance, self->submodule, self->freq, self->duty_u16, self->invert, pwmSourceClockInHz); if (self->xor) { @@ -408,12 +423,16 @@ static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, } self->center = center; } else { // Use alignment setting shortcut - if (args[ARG_align].u_int >= 0) { + uint32_t duty = self->duty_u16; + if (duty == VALUE_NOT_SET && self->duty_ns != VALUE_NOT_SET) { + duty = duty_ns_to_duty_u16(self->freq, self->duty_ns); + } + if (args[ARG_align].u_int >= 0 && duty != VALUE_NOT_SET && self->freq != VALUE_NOT_SET) { uint8_t align = args[ARG_align].u_int & 3; // limit to 0..3 if (align == PWM_BEGIN) { - self->center = self->duty_u16 / 2; + self->center = duty / 2; } else if (align == PWM_END) { - self->center = PWM_FULL_SCALE - self->duty_u16 / 2; + self->center = PWM_FULL_SCALE - duty / 2; } else { self->center = 32768; // Default value: mid. } @@ -515,6 +534,8 @@ static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args // Create and populate the PWM object. machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); + self->pwm_pin = pin1; + self->pwm_pin_af_obj = af_obj1; self->is_flexpwm = is_flexpwm; self->instance = af_obj1->instance; self->module = module; @@ -534,6 +555,8 @@ static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args // Initialize the Pin(s). CLOCK_EnableClock(kCLOCK_Iomuxc); // just in case it was not set yet + // Configure PWMX channels to pin output mode to be prepared for duty_u16 == 0. + mp_hal_pin_output(pin1); IOMUXC_SetPinMux(pin1->muxRegister, af_obj1->af_mode, af_obj1->input_register, af_obj1->input_daisy, pin1->configRegister, 0U); IOMUXC_SetPinConfig(pin1->muxRegister, af_obj1->af_mode, af_obj1->input_register, af_obj1->input_daisy, From 5a70850b30e457ee9c6ab12fa735246bc3a1f8aa Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 2 Sep 2024 16:24:46 +0200 Subject: [PATCH 038/546] samd/machine_uart: Add full support for 9-bit data. Prior to this commit, 9-bit UART data could be specified in the constructor and was transmitted, but the 9th bit was set to 0 when sending, and ignored when receiving. This commit completes 9-bit support in that the 9th bit is taken from the data. 9-bit data has to be provided with `uart.write()` and and read with `uart.read()` as two bytes for each transmitted item, low order byte first. The data length supplied with `uart.write()` and requested by `uart.read()` has to be even, which is checked. The size of the UART buffers will be transparently doubled to cater for 9-bit data. Signed-off-by: robert-hh --- ports/samd/machine_uart.c | 84 ++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c index b0dc4c5768d13..6e9a5538647bc 100644 --- a/ports/samd/machine_uart.c +++ b/ports/samd/machine_uart.c @@ -107,10 +107,17 @@ static const char *_parity_name[] = {"None", "", "0", "1"}; // Is defined as 0, // take all bytes from the fifo and store them in the buffer static void uart_drain_rx_fifo(machine_uart_obj_t *self, Sercom *uart) { + uint8_t bits = self->bits; while (uart->USART.INTFLAG.bit.RXC != 0) { - if (ringbuf_free(&self->read_buffer) > 0) { - // get a byte from uart and put into the buffer - ringbuf_put(&(self->read_buffer), uart->USART.DATA.bit.DATA); + if (ringbuf_free(&self->read_buffer) >= (bits <= 8 ? 1 : 2)) { + // get a word from uart and put into the buffer + if (bits <= 8) { + ringbuf_put(&(self->read_buffer), uart->USART.DATA.bit.DATA); + } else { + uint16_t data = uart->USART.DATA.bit.DATA; + ringbuf_put(&(self->read_buffer), data); + ringbuf_put(&(self->read_buffer), data >> 8); + } } else { // if the buffer is full, disable the RX interrupt // allowing RTS to come up. It will be re-enabled by the next read @@ -150,7 +157,12 @@ void common_uart_irq_handler(int uart_id) { #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); + if (self->bits <= 8) { + uart->USART.DATA.bit.DATA = ringbuf_get(&self->write_buffer); + } else { + uart->USART.DATA.bit.DATA = + ringbuf_get(&self->write_buffer) | (ringbuf_get(&self->write_buffer) << 8); + } } else { #if MICROPY_PY_MACHINE_UART_IRQ // Set the TXIDLE flag @@ -274,6 +286,17 @@ void machine_uart_set_baudrate(mp_obj_t self_in, uint32_t baudrate) { 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); + size_t rxbuf_len = self->read_buffer.size - 1; + #if MICROPY_HW_UART_TXBUF + size_t txbuf_len = self->write_buffer.size - 1; + #endif + if (self->bits > 8) { + rxbuf_len /= 2; + #if MICROPY_HW_UART_TXBUF + txbuf_len /= 2; + #endif + } + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, " "timeout=%u, timeout_char=%u, rxbuf=%d" #if MICROPY_HW_UART_TXBUF @@ -287,9 +310,9 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ #endif ")", self->id, self->baudrate, self->bits, _parity_name[self->parity], - self->stop + 1, self->timeout, self->timeout_char, self->read_buffer.size - 1 + self->stop + 1, self->timeout, self->timeout_char, rxbuf_len #if MICROPY_HW_UART_TXBUF - , self->write_buffer.size - 1 + , txbuf_len #endif #if MICROPY_HW_UART_RTSCTS , self->rts != 0xff ? pin_find_by_id(self->rts)->name : MP_QSTR_None @@ -411,6 +434,14 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, } } #endif + + // Double the buffer lengths for 9 bit transfer + if (self->bits > 8) { + rxbuf_len *= 2; + #if MICROPY_HW_UART_TXBUF + txbuf_len *= 2; + #endif + } // Initialise the UART peripheral if any arguments given, or it was not initialised previously. if (n_args > 0 || kw_args->used > 0 || self->new) { self->new = false; @@ -619,7 +650,12 @@ static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t uint64_t timeout_char = self->timeout_char; uint8_t *dest = buf_in; - for (size_t i = 0; i < size; i++) { + // Check that size is even for 9 bit transfers. + if ((self->bits >= 9) && (size & 1)) { + *errcode = MP_EIO; + return MP_STREAM_ERROR; + } + for (size_t i = 0; i < size;) { // Wait for the first/next character while (ringbuf_avail(&self->read_buffer) == 0) { if (mp_hal_ticks_ms_64() > t) { // timed out @@ -633,6 +669,11 @@ static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t MICROPY_EVENT_POLL_HOOK } *dest++ = ringbuf_get(&(self->read_buffer)); + i++; + if (self->bits >= 9 && i < size) { + *dest++ = ringbuf_get(&(self->read_buffer)); + i++; + } t = mp_hal_ticks_ms_64() + timeout_char; // (Re-)Enable RXC interrupt if ((uart->USART.INTENSET.reg & SERCOM_USART_INTENSET_RXC) == 0) { @@ -647,12 +688,19 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ size_t i = 0; const uint8_t *src = buf_in; Sercom *uart = sercom_instance[self->id]; - uint64_t t = mp_hal_ticks_ms_64() + self->timeout; + uint8_t bits = self->bits; + // Check that size is even for 9 bit transfers. + if ((bits >= 9) && (size & 1)) { + *errcode = MP_EIO; + return MP_STREAM_ERROR; + } + #if MICROPY_HW_UART_TXBUF #if MICROPY_PY_MACHINE_UART_IRQ // Prefill the FIFO to get rid of the initial IRQ_TXIDLE event + // Do not care for 9 Bit transfer here since the UART is not yet started. while (i < size && ringbuf_free(&(self->write_buffer)) > 0) { ringbuf_put(&(self->write_buffer), *src++); i++; @@ -672,8 +720,16 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ } MICROPY_EVENT_POLL_HOOK } - ringbuf_put(&(self->write_buffer), *src++); - i++; + if (bits >= 9) { + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + ringbuf_put(&(self->write_buffer), *src++); + ringbuf_put(&(self->write_buffer), *src++); + i += 2; + MICROPY_END_ATOMIC_SECTION(atomic_state); + } else { + ringbuf_put(&(self->write_buffer), *src++); + i += 1; + } uart->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; // kick off the IRQ } @@ -691,7 +747,13 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ } MICROPY_EVENT_POLL_HOOK } - uart->USART.DATA.bit.DATA = *src++; + if (self->bits > 8 && i < (size - 1)) { + uart->USART.DATA.bit.DATA = *(uint16_t *)src; + i++; + src += 2; + } else { + uart->USART.DATA.bit.DATA = *src++; + } i++; } #endif From 4d36ecf8a882e919b27c953c963e3577d9ae46aa Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 27 Sep 2024 09:03:53 +0200 Subject: [PATCH 039/546] samd/boards/SAMD21_XPLAINED_PRO: Add specific deploy instructions. Add instructions to install a bootloader to the board. The SAMD21 XPLAINED PRO board is shipped without a bootloader, which therefore has to be installed once before it can be used with MicroPython. Signed-off-by: robert-hh --- .../boards/SAMD21_XPLAINED_PRO/board.json | 2 +- .../deploy_xplained_pro.md | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 ports/samd/boards/SAMD21_XPLAINED_PRO/deploy_xplained_pro.md diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json b/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json index c59ebc1b7b7be..899e56e905a68 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json @@ -1,6 +1,6 @@ { "deploy": [ - "../deploy.md" + "deploy_xplained_pro.md" ], "docs": "", "features": [ diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/deploy_xplained_pro.md b/ports/samd/boards/SAMD21_XPLAINED_PRO/deploy_xplained_pro.md new file mode 100644 index 0000000000000..d95ab5061a30f --- /dev/null +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/deploy_xplained_pro.md @@ -0,0 +1,24 @@ +The SAMD21 Xplained Pro board is shipped without a bootloader. For use +with MicroPyhton a suitable bootloader has be be installed first. The +following procedure has been found to work and be simple: + +1. Get the bootloader from https://micropython.org/resources/firmware/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex. +2. Connect your board to the debug port. A drive with the name XPLAINED +shall appear. +3. Copy the Intel hex file of the bootloader to that drive. +4. Connect your board to the target port. A drive with the name SAMD21XPL should +appear. If not, push reset twice. Then it should appear. If that does not +happen, the bootloader was not properly installed or is not compatible. +5. Copy the MicroPython firmware, a .uf2 file, to the SAMD21 drive. When the SAMD21 +drive disappears, MicroPython is installed. + +From now on only steps 4 and 5 are needed to update MicroPython. You can use the +usual methods to invoke the bootloader, namely: + +- Push Reset twice. +- Call machine.bootloader(). +- Use the touch 1200 procedure by switching the USB baud rate to 1200 baud and back. + +At the above link above there are as well .uf2 versions of the bootloader +which one can install using steps 5. and 6. above once a .uf2 capable +bootloader is installed. From 85de67f55d2afb5c75388d639e51e3df7640b4a7 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 30 Sep 2024 09:37:13 +0200 Subject: [PATCH 040/546] samd/mboot: Provide a UF2 bootloader for SAMD21 Xplained Pro. A bootloader labelled for the SAMD21 XPLAINED PRO board. The only difference to a generic bootloader are the names and ID of the USB port and the label of the drive that is opened. Signed-off-by: robert-hh --- ...oader-xplained-pro-v3.16.0-15-gaa52b22.hex | 513 ++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 ports/samd/mboot/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex diff --git a/ports/samd/mboot/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex b/ports/samd/mboot/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex new file mode 100644 index 0000000000000..df8d5afd19c2b --- /dev/null +++ b/ports/samd/mboot/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex @@ -0,0 +1,513 @@ +:10000000E02D002069020000650200006702000088 +:1000100000000000000000000000000000000000E0 +:100020000000000000000000000000006502000069 +:100030000000000000000000650200005D020000FA +:100040006502000065020000650200006502000014 +:100050006502000065020000650200006502000004 +:1000600065020000650200006502000065020000F4 +:1000700065020000650200006502000065020000E4 +:1000800065020000650200006502000065020000D4 +:1000900065020000650200006502000065020000C4 +:1000A00065020000650200006502000065020000B4 +:1000B00000000000074B1A7DD207FCD52022FF323A +:1000C0001A83054A4008D8611A801A7DD207FCD5E8 +:1000D0007047C0460040004102A5FFFF70B5802573 +:1000E0000400ED02AC4200D370BD20000134FFF7E4 +:1000F000E1FFFF34F6E7002330B59A4200D130BD6E +:100100009C000D5901330551F7E70000F0B580253B +:10011000104C114E6368114FAB436360002A00D14D +:10012000F0BD1300102A00D91023D21A2680257D95 +:10013000ED07FCD59B009C460023CD58C5500433E9 +:100140006345FAD1C018C9182780237DDB07FCD589 +:10015000E4E7C0460040004144A5FFFF04A5FFFFBF +:10016000802270B5002304000D005200E958E058C9 +:10017000814203D104339342F8D170BD2000FFF7D0 +:1001800099FF402229002000FFF7C0FFF5E700009B +:1001900010230249CA681A42FCD070470008004088 +:1001A0001E2270B52149224C4B6893431C3A1343DD +:1001B0004B60A284FFF7ECFF1E4B1B689B0E3F2B8E +:1001C00000D1203B00251C4A9B0213431B4AE262DC +:1001D000A362A584FFF7DCFF194BA384FFF7D8FFC8 +:1001E0000223A28C1343A384FFF7D2FF154B9D601B +:1001F0005A7852B2002AFBDB134A5A605A7852B23C +:10020000002AFBDBC021114A114B12485360036ADC +:1002100009061B021B0A0B4303620023936007338A +:10022000136030220C4B1A6070BDC0460040004184 +:100230000008004024608000FF01000080BB0A1C11 +:1002400024050000000C00400007030010E000E05F +:10025000E703000000ED00E00000002010B500F012 +:1002600007F910BDFEE7FEE72449254870B5814235 +:100270000AD0244BC41E0022A34203D303331A1A0C +:100280009208920001F058FC1F48204BC11E00222A +:10029000994203D803331A1A92089200002101F000 +:1002A00054FCFF221A4B032193431A4A0C259360F6 +:1002B000022208243026184B18485A62C3788B4310 +:1002C0001343C370C378AB432343C370144B987B71 +:1002D000B0430600202030439873987BA843044322 +:1002E0009C73987B884302439A7380230D4A51681C +:1002F0000B43536000F07AFEFEE7C0466C1D000021 +:1003000000000020D0010020D0010020DC0D0020E2 +:100310000000000000ED00E0FC70004100500041D2 +:100320000048004100400041F7B5134D01906B8833 +:10033000002B18D1082738001A0292B214B2A44632 +:1003400066465400A2B2002E02DA0C4A5440A2B211 +:10035000013880B20028F1D158002A52802201339E +:1003600052009342E7D1019A0B0A53405800285A91 +:100370000902484080B2FEBDD001002021100000DB +:10038000074B1B685843074B1A789523002A00D166 +:100390001233012243439A4200D370470132FAE7F5 +:1003A00000000020D003002070B505001C240020B0 +:1003B0000F260B00E3403340002A09D1002B07D160 +:1003C000002C0BD0002809D1043C231DF1D170BDB5 +:1003D000092B03DD37332B540130F5E73033FAE7CF +:1003E000054B064A1A60BFF34F8F054B054ADA608A +:1003F000BFF34F8FFEE7C046FC7F0020EF6926F079 +:1004000000ED00E00400FA0510B50B4A1368002B5C +:100410000ED10A4911600A490A6801320A60094985 +:100420000868002806D0824204D30B60FFF7D8FF8B +:10043000013B136010BDC046E4030020DC05000052 +:10044000E0030020D8030020054B064A1A60BFF3E2 +:100450004F8F054B054ADA60BFF34F8FFEE7C0466A +:10046000FC7F0020EF6916F000ED00E00400FA05C3 +:10047000012210B5194B1A491A701A4A1368013330 +:1004800013600A68002A0CD017481018834203D161 +:1004900080241648E4050460934201D100230B60D8 +:1004A00010BD1348DBB20278002B10D180210F4B16 +:1004B000C905196011000A39C9B20E4BF02902D9D9 +:1004C0001978494219701B78D2180270E8E79342F4 +:1004D000E6D18022084BD2051A60E1E7D003002064 +:1004E000DC030020D403002018FCFFFF98440041E7 +:1004F000050000200400002094440041074B084AF6 +:100500001B681168994207D2FA21C9005B18136071 +:100510008022044BD2051A607047C046D4030020E5 +:10052000DC03002094440041044B80221900D205D2 +:10053000883198330A601A607047C0460044004111 +:1005400070470000202370B5354A0F20D1690B4356 +:10055000D361012233490B7813430B70324B197866 +:100560008143197006211C78214319702F490C789A +:1005700022430A701A7802401A70602219780A43DE +:100580001A702B4A2B4B53805378DB09FCD1012284 +:10059000294B19780A431A709A78D207FCD41F2085 +:1005A00026490A68520B0240824200D105221C8D66 +:1005B000234D024092012C4022431A850A68920C76 +:1005C00002401F2A00D1023A1F24188D2240A04366 +:1005D000024307201A850A68D20D0240824200D1E8 +:1005E0000322198D02401748120301400A437F215C +:1005F0001A851A7814480A401A70042219780A4396 +:100600000C211A7058621A898A431A811A890B3987 +:100610008A431A8180220021520001F096FA70BDAF +:1006200000040040584400413C440041594400410A +:10063000000C004006400000005000412460800093 +:100640003FF8FFFFFF8FFFFF74060020F7B51C0087 +:10065000314B06005B6A0F0015000193002C05D199 +:10066000443454432D4BE418FFF7CEFE63782278D0 +:10067000934210D2D51ABD4200D93D00002E08D0B9 +:10068000211DC9182A00300001F056FA63785B1961 +:1006900063702800FEBD019BA1786A01D318002970 +:1006A00011D1211D196059681D48890B89035960B2 +:1006B0005968014059601B49551840212879014368 +:1006C00029710121A1701449002551180191164981 +:1006D0005218127AD207DCD55D68A278EDB2257087 +:1006E000022A04D12A001968201D01F025FABD4212 +:1006F00000D93D00002E0CD02A0030006570211D6D +:1007000001F01AFA0122019BFF331A720023A37031 +:10071000BFE76670F6E7C046005000413004002095 +:10072000FF3F00F000510041FF50004110B5002391 +:10073000FFF78CFF10BDF8B505000C0016001F0078 +:10074000002C00D1F8BD210028003B003200FFF74B +:100750007DFF241A2D18F3E7F8B50D001F49560147 +:100760004C6A3419002B1BD063695B005B08636122 +:1007700020616369AA049B0B920C9B031343636182 +:100780006369174A174813406361321802238021B6 +:10079000160013729171327A1A42FCD02800F8BD0B +:1007A000012163695B005B0F03339940A94209D8BB +:1007B000043A531E9A416369D1075B005A080A4301 +:1007C0006261D5E744275743074A0437BF18010041 +:1007D0002A00380001F0B0F93800C9E700500041A4 +:1007E000FF3F00F0FF5000413004002010B500230F +:1007F000FFF7B2FF10BD000010B5054A0B001188CD +:10080000994200D919000022FFF7F0FF10BDC04641 +:100810007407002007B500216B460A00D81D01703F +:10082000FFF7E4FF07BD0000F0B5B94C9BB0FFF740 +:10083000EBFDA38B08211A000A400392B54A0B4234 +:100840002ED080234020A183A372C024093151708F +:1008500090715371B04BB14D5968A40529402143A3 +:100860005960596929400C43AD495C6104311960F4 +:10087000AC49AD4C196159680C4080218902214373 +:1008800059605968890B8903596050710022A74B40 +:100890001A70A64B1878431E9841C0B21BB0F0BD29 +:1008A0001020137A0342F4D09D4D10726D899C483C +:1008B000AC46664680799E4D02909948984BC78811 +:1008C000C0792E80402501909B889548994E0089DB +:1008D0005571B34200D1B0E136DC81246400A342FB +:1008E00000D1BFE115DC822B00D190E106DC803B1A +:1008F000012B00D886E120239371CAE780214900AB +:100900008B42F8D00221FF318B42F4D1FFF782FFF6 +:10091000BFE7B02189008B4200D16DE10EDC0139C7 +:10092000FF398B42E7D0A23B8349FF3B0B42E2D128 +:10093000002308210893099308A8C4E0C021890076 +:100940008B42D8D07D49DFE77D488342DED000DD91 +:1009500096E07C498B4212DC01398B4200DBACE033 +:1009600079498B42E4D06031FF318B42C3D1FFF72C +:1009700051FF80235B423B43DBB2A37289E78821AE +:100980006A4809018B4200D195E080318B42B2D197 +:100990000770FFF73FFFA02303225B00E254C02251 +:1009A0005D4B5E49586C92050840104358644620E0 +:1009B000FF302554654830271864902040002754A4 +:1009C000586B8026084010435863922040002654FC +:1009D0005F481863B02040002554586F084058679E +:1009E000B220400026543C307D3E2654FC388446DC +:1009F0009C44604606680E4016430660A626FF36F5 +:100A0000A5551E005348A0363060C0267600A75575 +:100A10001F0094373E6880200E4016433E60C22679 +:100A20007600A0551E004C4F90363760E0264427D4 +:100A30007600A7550436A555A055C01984469C4498 +:100A4000604606680E4016430660D42084469C44E7 +:100A5000604606680E4016430660F0268020760049 +:100A6000A7550436A555A0551C00E4342068F4337E +:100A7000084010432060186801400A431A6008E7E4 +:100A80003648834200D122E10ADC35498B4200D14D +:100A90003CE7344934480B40834200D136E72AE72B +:100AA00032498B4200D131E731498B4200D022E7F5 +:100AB000002308A80380012105E080235B009F42FA +:100AC00004D112212B48FFF797FEE2E680239B001A +:100AD0009F4202D199212848F5E7019B032B78D149 +:100AE000029B032B00D906E708AC48220021200016 +:100AF00001F02BF8019B6370029B002B3ED1092271 +:100B000004332370A270E37020002178DBE7C04635 +:100B100000500041FF50004174060020FFFFFF8F8E +:100B2000E803002034040020FF3F00F02C040020E4 +:100B30007407002002030000FFFE00000103000014 +:100B40002109000081060000A1030000BC04002070 +:100B5000780400208805002044050020A121000021 +:100B600021200000FFFEFFFF210A000021220000DB +:100B7000A1FE0000981B00004400002004AE3200DB +:100B8000544B23CB23C200252F001B681360AB00FE +:100B9000514AF358D01919680122FFF705FC0135B5 +:100BA0003F18042DF3D100234B4AD3554B4B4C4AED +:100BB0009B799B009D58280000F0CFFF2300013057 +:100BC000400020702A7802330135002A9CD01A7028 +:100BD000F8E7019B0F2B02D13921424873E7019BB3 +:100BE000212B02D1092140486DE7019B222B00D027 +:100BF00081E621213D4866E7072800D07BE6AA214F +:100C00003B4860E7039B08A8022103805BE708429A +:100C100000D070E60F235022034008335B011042DE +:100C200006D0344A9B189B799B06DB0F08A8EBE79C +:100C3000304A9B189B79DB06F7E70F24030023401B +:100C4000204200D157E60140394300D053E6294AFB +:100C500008335B019B18020602D520225A7155E623 +:100C60001022FBE70F2403002340204200D142E67C +:100C70000140394300D03EE65B01020613D51E4910 +:100C80001C4A9A185B182021D879084200D13DE609 +:100C900059711B7A2B4200D138E613004022FF33F2 +:100CA0001A723E3ADAE710201349124A9A185B1872 +:100CB000D979014200D129E658711B7A01180B42FB +:100CC00000D123E61300FF3301221972C6E70B4857 +:100CD000F9E6C046881B000050060020E80300200B +:100CE000D01B000008000020E0000020AC1B00002A +:100CF000F400002000500041FF500041EC000020B3 +:100D000010B5FFF71FFC0022034B1A700223034AA1 +:100D100011780B43137010BD2C04002000500041CB +:100D200010B50C000122FFF761FD200010BD10B5C9 +:100D30000C000122FFF75AFD200010BD70B5040021 +:100D40000D00FFF771FD03000020834204D0022252 +:100D500029002000FFF7EAFC70BD70B505000C000B +:100D6000FFF762FD002807D000230222210028009F +:100D7000FFF7E1FC200070BD0400FBE730B5002365 +:100D80002025934200DB30BD0C78002C03D00131CC +:100D9000C4540133F5E72C00FAE70000F8B58022CF +:100DA0000C00050000212000920000F0CEFE002D76 +:100DB0000CD13E2220004A4900F0BEFEFF235522FE +:100DC0005B00E254474B9218E254F8BD7E2D23D8C5 +:100DD0006E1E3E2E01D9403D2E00002E12D08025E1 +:100DE0003302581C404EFF30ED001A1F591CAA4216 +:100DF00003D2B3420ED08BB223800B000234814267 +:100E0000F3D1E2E7F0230922FF212370601C00F0F8 +:100E10009CFEE4E7354BEFE7822D31D87F2DD4D10E +:100E20002F490B2220002B31FFF7A8FF8027282312 +:100E30002F4EE3727D3D3F03F0683B0020340028D5 +:100E400002D000F08AFE03001A0A237762771A0C98 +:100E50001B0EE3772B0A3100A277A5760B22E376EF +:100E60002000FFF78BFF67224D2301355242ADB2C0 +:100E700022746374227663761036052DDCD1A4E7E4 +:100E80002B00833B012B0CD8194A1B01D318DD68BA +:100E9000280000F062FE29000200200000F04CFE55 +:100EA00093E78023853D2902DB02994200D38CE73A +:100EB000104B114A2360114B20006360FE235B003E +:100EC000E2508023DB00A361802380229B01A3608A +:100ED0000B4B52006561E1602261E3612030DDE788 +:100EE000E01B0000FF01000003040000FFFF000002 +:100EF000881C00005546320A306FB10A57515D9E7A +:100F0000882BED6870B516001D000A682F4B0C0089 +:100F10009A4252D12E4B4A689A424ED1FE2252003A +:100F20002C4B8A589A4248D18B689A0403D52A4A96 +:100F3000C969914241D1DB0712D4802322695B0049 +:100F40009A420DD1E068C3B2002B09D1F822234B9D +:100F50009202C318934203D221002031FFF700F917 +:100F6000002D2BD0A369002B28D029681C4A8B4266 +:100F700006D0934201D8002901D001235B422B60A7 +:100F80006369934219D8072201211A409140DB0876 +:100F9000EB181A7AC8B2114204D1696802430131D0 +:100FA0001A7269606A682B689A4206D3002E04D1CF +:100FB0000C4B1B681E330C4A136070BD002EFCD115 +:100FC000084B1B682D33FF33F5E7C0465546320A00 +:100FD00057515D9E306FB10A882BED6800E0FFFF2E +:100FE00063040000E0030020D8030020F8B5624D40 +:100FF000AB68002B00D0FEE70221604B5A6B8A439E +:101000005A63DA681205FCD55D4A5A630222596BAD +:101010000A435A63DA689205FCD5DA685205FCD4B3 +:1010200002215A6B8A435A63DA681205FCD50822FA +:10103000596B0A435A630222596B0A4351495A6356 +:101040000B68013321D120224F4BFF32188B024312 +:101050001A8358684D4A02435A604D4ADA614D4A34 +:101060001A801A7DD207FCD54B4A1A801A7DD20706 +:10107000FCD54A4A0A604A4A4A4911604A4A1A80DB +:101080001A7DD207FCD5FFF7DFF90023474A13701A +:10109000D379DB09FCD1FFF747FA454B1E68454B76 +:1010A000F218F8239B029A4213D8434B434A19681B +:1010B000434C444B914232D11978434AC90702D478 +:1010C000216891422BD0414B22601B68404A323349 +:1010D0001360FFF765F8BFF35F8F62B6FFF710FE8E +:1010E0004020FFF72DFA80270A230126394DFF0102 +:1010F0002B70FFF799FB384C00280AD02378DAB21E +:10110000002B05D1324B38001A60FFF719FA2E7008 +:1011100026702378002B25D000F0D6FAFCE71B7848 +:10112000DB07DA0F002B0DD00023802223602B4B2E +:10113000D2051A6080239B011A6882F30888AB608D +:101140003047C6E72168264B994201D12260C0E7AB +:1011500021681D4A9142E7D0FA2023604000FFF742 +:101160000FF9E1E72378002BC3D1FF33C046013BE1 +:10117000002BFBD1BDE7C04600ED00E000080040B9 +:10118000040027000040800000400041800004006F +:101190000020400005A5FFFF44A5FFFFFAC7E0D8E7 +:1011A000044080005DFCFFFF06A5FFFF001000402B +:1011B0000420000000E0FFFFB42000007CB0EE87B8 +:1011C000FC7F002038040040EF6926F0E003002097 +:1011D000D80300200400002076070020944400413A +:1011E000EF6916F010B5054C12220021200000F026 +:1011F000ACFCF0232370E63BE37110BDB309002083 +:1012000010B5FFF7EFFF0022014B1A7310BDC04667 +:101210009E010020F7B580270400160000250191EB +:10122000BF00A368AB4200D8F7BD3900A278019B8C +:10123000E068FFF780FA80235B01E81801223300A1 +:10124000E168FFF75FFE0135EBE770B513000600BC +:101250000C00150000200A000121FFF7F7F9002318 +:10126000984206D02B0022001F213000FFF7EEF934 +:101270000123180070BD000010B504220D210248A2 +:10128000FFF7B4FA10BDC0469E01002010B50B4C0C +:101290000B4861792379A27909021943E379120491 +:1012A00011431B060B431A0A037142711A0C1B0EE1 +:1012B0008271C371FFF7E0FF10BDC04694090020A2 +:1012C0009E01002070B504220D00FFF78FFA154C27 +:1012D00085420FD0FFF786FF012304222373124BB0 +:1012E0009A700022DA701A715A719A711A735A73CD +:1012F000FFF7CCFFFFF784FF637A217A1B020B43D1 +:10130000A17AE27A0904120619431143491B0B0A18 +:10131000217263720B0C090EA372E172FFF7B6FF24 +:1013200070BDC0469E010020B309002070B5184C66 +:10133000142205000021200000F007FC154A002DB2 +:101340001FD021000823D07D08313F26527C324037 +:101350001C2A01D0B24205D10F4A0C330A80052263 +:10136000DBB2CA70191C834200D9011CC9B2002D1E +:101370000BD0023B9BB21B0223802000FFF7A2FF91 +:1013800070BD0423D07C211DDFE7013B2370F4E70F +:1013900078090020940900201C0A0000F0B5040020 +:1013A00000252A4B85B0597C03AAD170997C917095 +:1013B000D97C5170197D1170997D02AAD170DB7DA5 +:1013C000937053880193019BAB4205D8FFF718FF38 +:1013D000FFF75CFF05B0F0BDFFF726FA0028F9D053 +:1013E000039B1B4FEE18002C1FD039003000FFF775 +:1013F000D5FC8021042238008900FFF7F7F9154A4F +:101400000135507A137A917A00021843D37A09048D +:1014100008431B06104903435B18190A13725172E3 +:10142000190C1B0E9172D372CDE780212300380076 +:1014300005228900FFF77FF9220039003000074BB1 +:10144000FFF760FDFFF75AF8D9E7C046940900207E +:10145000780700209E01002000FEFFFFC809002041 +:101460007FB500F02FFB444E002205213000FFF72E +:10147000ECFE002849D0737A327AB57A1B021A43FF +:10148000F37A2D041B0615433C4C1D432B0A637253 +:101490002B0CA3722B0E2572E372F07B2F2827D81A +:1014A000192815D8032827D012282FD0002840D07B +:1014B000FFF798FE012305222373314B9A70002217 +:1014C000DA701A715A719A711A7320325A7332E0B3 +:1014D0001A381528ECD800F01BFB282CEBEB2CEB72 +:1014E000EBEBEB3FEB31EBEB39EB3DEBEBEBEB2CD1 +:1014F0005A28DDD1012019E0F37C191C122B00D9E8 +:1015000012211F48C9B2FFF7DDFE7FBD1D4C1E49E9 +:10151000200018220830FFF731FCF37C191C242B23 +:1015200000D924212000C9B2EDE70020FFF7FEFE1C +:10153000EBE7FFF765FEFFF7A9FEE6E78023134818 +:101540009B024360124B08210360DCE70120FFF798 +:1015500025FFDAE70020FAE70C260E49320001A841 +:1015600000F0EAFAB54204D9002326726372A3722E +:10157000E3720C2101A8C6E7940900209E01002017 +:10158000B3090020AC010020721B00008C09002070 +:1015900000003E7F231D000072B6BFF35F8F044B37 +:1015A000044A9A829A830022034B9A607047C0468D +:1015B00000500041FF03000000ED00E0F0B5C5B0B1 +:1015C0000DAF0400FFF7E8FF44220021380000F0CF +:1015D000BCFA982200211EA800F0B7FA02230F21BE +:1015E000BB70A37862780B40A370264B0A4005932A +:1015F000636805AD06930023E16807932248627093 +:101600002B7301221EABFFF77DFC390020001EAAC0 +:10161000FFF700FE280001230D216278FFF79CF8F8 +:10162000002505AE3A003000A178FFF70EFE002835 +:101630001ED0144B01AD0193069B6B60079BAB6002 +:1016400000232B73F37B002BE4D02A2B17D16B469E +:10165000B27D3900DA70F27D20009A705E881EAA91 +:10166000A660FFF7D7FDAB6876029E1BAE60D1E7A0 +:10167000064B9D4201D9FEF7B3FE0135D1E7FEF7D7 +:10168000E3FEC7E753425355FF0F0000F824010063 +:1016900030B5EFF30883054C2360036883F30888B3 +:1016A0004568A847236883F3088830BDC00A002036 +:1016B00007B5010001226846FEF776FE082168465C +:1016C000FFF72EFB07BD0000F8B500237A227F4C00 +:1016D00023607F4B1A70FFF7C3FE7E4D4021280028 +:1016E000FFF72CFB7C4B186000232B54984201D051 +:1016F000FEF704FF0022794B1D60794B1A60784891 +:10170000754B02681F68BA42E5D2744E31680B7897 +:10171000FF2B37D0734D232B00D0B4E06C4B1B78DC +:10172000532B34D12B680132013102603160BA1A77 +:101730009A4200D91A006C4D20682A6000F0FCF92A +:10174000674829680368654ACB18013B0360106845 +:101750004B1EC3181360FF23644D29701940614B61 +:101760001B688B4203D92068591AFFF7F6FAC04666 +:101770007A22574B1A7000225A4B1A60574A136844 +:1017800001331360564A136801331360B7E7522BD5 +:1017900004D129682068FFF7CAFAE9E74F2B03D183 +:1017A0002B6822681370E3E7482B03D12B6822686B +:1017B0001380DDE7572B0AD14D4B22689A4202D1A4 +:1017C0000020FEF7BDFE23682A681A60D0E76F2B61 +:1017D00004D101212068FFF7A3FAC9E7682B05D1DE +:1017E000022123681B882B602800F4E7772B04D1A3 +:1017F000236804211B682B60F6E7472B09D1286872 +:10180000FFF746FF3B4B1B78002BB1D001213A4834 +:10181000E1E7542BACD04E2BAAD0562B02D12A2173 +:101820003648D8E7582B05D12868FEF757FC032126 +:101830003348D0E7592B0DD12A682068314B002A54 +:1018400003D1186003213048C5E719689208FEF7F4 +:101850005DFCF7E75A2B8BD12F6800252668F71916 +:10186000B74209D101212948FFF75AFA2800FFF7AA +:101870001FFF03212648AEE729003078FEF754FD0C +:1018800001360500ECE71A00303AD1B2092904D834 +:101890002B681B0113432B6070E71A00413A052A9D +:1018A00003D82A68373B1201F4E71A00613A052A87 +:1018B00003D82A68573B1201ECE72C2B03D12B6885 +:1018C00023600023E7E7024A1370FAE7BC0A00200E +:1018D000610A0020680A0020B40A0020B80A00202B +:1018E000AC0A0020640A0020C80A0020B00A0020C8 +:1018F0000CED00E0600A00202F1D00003F1D0000DD +:10190000311D0000C40A0020351D0000391D0000F3 +:101910003B1D00000300F7B5473304001A7840214F +:1019200003000020FEF792FE002801D10020FEBD3A +:1019300000232370237901930423E356002BF5DB66 +:101940003F262000019FA51DEB8F37404830C0186F +:101950003A00611D00F0F0F8E88F019BC01980B2D9 +:10196000B34301D1E887E1E70023EB87DFE7F0B578 +:1019700006000C001F0093B001923F25AC4203DC2F +:101980002500002F00D140373B0002AA2B431370E3 +:1019900002AB31002A00581C00F0CEF86B46402103 +:1019A0001A7902A80123FEF7D7FE7619641BE4D149 +:1019B00013B0F0BD030010B547331A78043100238B +:1019C0004830FFF7D4FF10BDF7B50400FFF7A2FFC2 +:1019D000002842D0230048339A88A06C1A80002245 +:1019E00001385A80082865D800F09CF8150523278F +:1019F000252A483F3800314E300000F0AEF805008F +:101A0000200031002A004C3000F096F82900200018 +:101A1000FFF7D0FF21E00123E364FF332365802338 +:101A2000DB006365A0235B00A365254B1421E36500 +:101A3000EDE7FEF7D5FC0021E9E7FEF705FDFAE743 +:101A400020000021FFF7B6FF8023206D9B01984204 +:101A500003D321005431FEF783FBF7BD2100626DF3 +:101A6000206D5831FEF747FBE5E72000656D216DDD +:101A70002A004C30FEF73FFBA900C8E70026636D43 +:101A8000276D0193019BB34201DC5900BFE73D0084 +:101A900000212B001878FEF747FC7B1C0135FF3333 +:101AA00001009D42F5D1230072004C332F00985263 +:101AB0000136E7E701225A80BDE7C046B81C0000A6 +:101AC000882BED6807480622030010B547331A70CB +:101AD000FFF77AFF04480722030047331A70FFF725 +:101AE00073FF10BDCC0A0020540C002010B5E2B0EA +:101AF0000400FFF751FDC42200216846520000F0A7 +:101B000024F847236B441C706846FFF75DFFFBE732 +:101B100002B4714649084900095649008E4402BC86 +:101B20007047C04602B4714649084900095C490043 +:101B30008E4402BC7047C046002310B59A4200D1C3 +:101B400010BDCC5CC4540133F8E703008218934203 +:101B500000D1704719700133F9E70023C25C0133EB +:101B6000002AFBD1581E70474D6963726F63686924 +:101B7000700053414D4432312058706C61696E657C +:101B8000642050726F0000000CA0800040A0800014 +:101B900044A0800048A0800012011002EF02014022 +:101BA000D804022401420102030100000697FF0944 +:101BB00001A101150026FF00750895400901810269 +:101BC00095400901910295010901B102C000000090 +:101BD00000000000681B0000721B0000500600207F +:101BE000EB3C905546322055463220000201010060 +:101BF0000240007E3EF83F000100010000000000AE +:101C0000000000008000294200420053414D443250 +:101C10003158504C000046415431362020203C21A0 +:101C2000646F63747970652068746D6C3E0A3C68FB +:101C3000746D6C3E3C626F64793E3C736372697094 +:101C4000743E0A6C6F636174696F6E2E7265706C9E +:101C5000616365282268747470733A2F2F777777E1 +:101C60002E7078742E696F2F22293B0A3C2F7363E4 +:101C7000726970743E3C2F626F64793E3C2F6874C9 +:101C80006D6C3E0A00000000494E464F5F554632DB +:101C900054585400B81C0000494E44455820202098 +:101CA00048544D001E1C000043555252454E5420CE +:101CB000554632000000000055463220426F6F74D6 +:101CC0006C6F616465722076332E31362E302D3183 +:101CD000352D6761613532623232205346485752A2 +:101CE0004F0D0A4D6F64656C3A2053414D443231BB +:101CF0002058706C61696E65642050726F0D0A42E5 +:101D00006F6172642D49443A2053414D4432314A47 +:101D10003138412D58706C61696E65642D50726F59 +:101D20000D0A000000000800003E800200020006CC +:101D300000580A0D00590A0D005A00230A0D0076BA +:101D4000312E31205B41726475696E6F3A58595A71 +:101D50005D205365702032382032303234203135E6 +:101D60003A35353A34340A0D000000000100000015 +:101D700001C80000050F3900021810050038B60828 +:101D800034A909A0478BFDA0768815B6650001012E +:101D9000001C100500DF60DDD88945C74C9CD2656A +:101DA0009D9E648A9F00000306AA000200000000B6 +:101DB000090299000501008032080B0002020201AD +:101DC00000090400000102020100052400100104C2 +:101DD00024020605240600010524010301070583EA +:101DE000030800FF09040100020A00000007058142 +:101DF0000240000007050202400000090402000240 +:101E0000080650000705840240000007050502404F +:101E10000000090403000203000000092100010082 +:101E20000122210007058603400001070506034043 +:101E300000010904040002FF2A010007058703408E +:101E4000000107050703400001000000090403002A +:101E5000020300000300000000C2010000000800AF +:101E60000A00000000000306AA00080002000400A7 +:101E7000A0001400030057494E55534200000000D3 +:101E80000000000000008400040007002A00440055 +:101E90006500760069006300650049006E0074000B +:101EA0006500720066006100630065004700550030 +:101EB000490044007300000050007B0039003200EC +:101EC0004300450036003400360032002D00390052 +:101ED0004300370037002D0034003600460045002F +:101EE0002D0039003300330042002D003300310053 +:101EF00043004200390043003500420042003300F5 +:101F0000420039007D00000000005342535500009C +:101F1000000000000000000000800202200000001D +:101F200000000000000000000000000000000000B1 +:101F30000000000000000000312E303000000000E2 +:101F40000000000000000000000000000000000091 +:101F50000000000000000000000000000000000081 +:101F60000000000000000000000000000000000071 +:101F70000000000000000000000000000000000061 +:101F80000000000000000000000000000000000051 +:101F90000000000000000000000000000000000041 +:101FA0000000000000000000000000000000000031 +:101FB0000000000000000000000000000000000021 +:101FC0000000000000000000000000000000000011 +:101FD0000000000000000000000000000000000001 +:101FE00000000000000000000000000000000000F1 +:101FF00000000000ED1A0000BD150000B81C000034 +:00000001FF From 4a159d16febeba65b2de52cb05657b9515a9bcae Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 24 Sep 2024 17:09:06 +0200 Subject: [PATCH 041/546] samd/boards/SAMD21_XPLAINED_PRO: Use the SPI flash for the file system. The initial settings did not support it. The change required to add a dedicated handling of the Adesto 1MByte flash of the XPLAINED PRO board, which does not support the sfdp feature. Fixes the ID check of the Adesto/Renesas 1MByte flash. Signed-off-by: robert-hh --- .../SAMD21_XPLAINED_PRO/mpconfigboard.h | 4 ++ .../SAMD21_XPLAINED_PRO/mpconfigboard.mk | 2 + .../samd/boards/SAMD21_XPLAINED_PRO/pins.csv | 4 ++ ports/samd/samd_spiflash.c | 43 ++++++++++++++----- 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h index 064d1ecc0d030..09bad81bb79aa 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h @@ -2,3 +2,7 @@ #define MICROPY_HW_MCU_NAME "SAMD21J18A" #define MICROPY_HW_XOSC32K (1) + +#define MICROPY_HW_SPIFLASH (1) +#define MICROPY_HW_SPIFLASH_ID (5) +#define MICROPY_HW_SPIFLASH_BAUDRATE (12000000) diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk index f95c6549381ba..cc43c22cea588 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk @@ -2,3 +2,5 @@ MCU_SERIES = SAMD21 CMSIS_MCU = SAMD21J18A LD_FILES = boards/samd21x18a.ld sections.ld TEXT0 = 0x2000 + +MICROPY_HW_CODESIZE ?= 248K diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv b/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv index 69d0c136d08ef..e5327e7916bf0 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv @@ -53,3 +53,7 @@ USB_DP,PA25 SWCLK,PA30 SWDIO,PA31 +FLASH_MOSI,PB22 +FLASH_MISO,PB16 +FLASH_SCK,PB23 +FLASH_CS,PA13 diff --git a/ports/samd/samd_spiflash.c b/ports/samd/samd_spiflash.c index cd4f10640dbf1..8ada7e96092c2 100644 --- a/ports/samd/samd_spiflash.c +++ b/ports/samd/samd_spiflash.c @@ -45,6 +45,7 @@ const uint8_t _COMMANDS_32BIT[] = {0x13, 0x12, 0x21}; // READ, PROGRAM_PAGE, ER #define COMMAND_JEDEC_ID (0x9F) #define COMMAND_READ_STATUS (0x05) +#define COMMAND_WRITE_SR1 (0x01) #define COMMAND_WRITE_ENABLE (0x06) #define COMMAND_READ_SFDP (0x5A) #define PAGE_SIZE (256) @@ -88,7 +89,7 @@ static void wait(spiflash_obj_t *self) { mp_hal_pin_write(self->cs, 0); spi_transfer((mp_obj_base_t *)self->spi, 2, msg, msg); mp_hal_pin_write(self->cs, 1); - } while (msg[1] != 0 && timeout-- > 0); + } while ((msg[1] & 1) != 0 && timeout-- > 0); } static void get_id(spiflash_obj_t *self, uint8_t id[3]) { @@ -123,6 +124,17 @@ static void write_enable(spiflash_obj_t *self) { mp_hal_pin_write(self->cs, 1); } +// Write status register 1 +static void write_sr1(spiflash_obj_t *self, uint8_t value) { + uint8_t msg[2]; + msg[0] = COMMAND_WRITE_SR1; + msg[1] = value; + + mp_hal_pin_write(self->cs, 0); + spi_transfer(self->spi, 2, msg, NULL); + mp_hal_pin_write(self->cs, 1); +} + static void get_sfdp(spiflash_obj_t *self, uint32_t addr, uint8_t *buffer, int size) { uint8_t dummy[1]; write_addr(self, COMMAND_READ_SFDP, addr); @@ -155,27 +167,36 @@ static mp_obj_t spiflash_make_new(const mp_obj_type_t *type, size_t n_args, size mp_hal_pin_write(self->cs, 1); wait(self); - // Get the flash size from the device ID (default) uint8_t id[3]; get_id(self, id); + bool read_sfdp = true; + if (id[1] == 0x84 && id[2] == 1) { // Adesto self->size = 512 * 1024; - } else if (id[1] == 0x1f && id[2] == 1) { // Atmel / Renesas + } else if (id[0] == 0x1f && id[1] == 0x45 && id[2] == 1) { // Adesto/Renesas 8 MBit self->size = 1024 * 1024; + read_sfdp = false; + self->sectorsize = 4096; + self->addr_is_32bit = false; + // Globally unlock the sectors, which are locked after power on. + write_enable(self); + write_sr1(self, 0); } else { self->size = 1 << id[2]; } // Get the addr_is_32bit flag and the sector size - uint8_t buffer[128]; - get_sfdp(self, 0, buffer, 16); // get the header - int len = MIN(buffer[11] * 4, sizeof(buffer)); - if (len >= 29) { - int addr = buffer[12] + (buffer[13] << 8) + (buffer[14] << 16); - get_sfdp(self, addr, buffer, len); // Get the JEDEC mandatory table - self->sectorsize = 1 << buffer[28]; - self->addr_is_32bit = ((buffer[2] >> 1) & 0x03) != 0; + if (read_sfdp) { + uint8_t buffer[128]; + get_sfdp(self, 0, buffer, 16); // get the header + int len = MIN(buffer[11] * 4, sizeof(buffer)); + if (len >= 29) { + int addr = buffer[12] + (buffer[13] << 8) + (buffer[14] << 16); + get_sfdp(self, addr, buffer, len); // Get the JEDEC mandatory table + self->sectorsize = 1 << buffer[28]; + self->addr_is_32bit = ((buffer[2] >> 1) & 0x03) != 0; + } } self->commands = self->addr_is_32bit ? _COMMANDS_32BIT : _COMMANDS_24BIT; From ceae0e14946b48f0e4b01669f241e66467bedddc Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 18 Nov 2024 10:56:07 +0100 Subject: [PATCH 042/546] samd/samd_flash: Make flash read/write methods access self parameters. Use `self` (the first argument) instead of the global `samd_flash_obj` when accessing the `flash_base` parameter. This allows there to be multiple flash objects for various types of filesystem. Signed-off-by: robert-hh --- ports/samd/samd_flash.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ports/samd/samd_flash.c b/ports/samd/samd_flash.c index ef0de7b6fb585..f68bdf140f490 100644 --- a/ports/samd/samd_flash.c +++ b/ports/samd/samd_flash.c @@ -103,7 +103,8 @@ static mp_obj_t samd_flash_version(void) { static MP_DEFINE_CONST_FUN_OBJ_0(samd_flash_version_obj, samd_flash_version); static mp_obj_t samd_flash_readblocks(size_t n_args, const mp_obj_t *args) { - uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + samd_flash_obj.flash_base; + samd_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + self->flash_base; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); if (n_args == 4) { @@ -118,7 +119,8 @@ static mp_obj_t samd_flash_readblocks(size_t n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(samd_flash_readblocks_obj, 3, 4, samd_flash_readblocks); static mp_obj_t samd_flash_writeblocks(size_t n_args, const mp_obj_t *args) { - uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + samd_flash_obj.flash_base; + samd_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + self->flash_base; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); if (n_args == 3) { From cbffe61f96d060ed4af2bde7e3043a3664dc147a Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 18 Nov 2024 16:50:03 +0100 Subject: [PATCH 043/546] samd/mboot/README.md: Add information about the bootloader source. Includes the LICENSE file of the source and the specific board files for the Xplained Pro board. Signed-off-by: robert-hh --- ports/samd/mboot/LICENSE | 60 ++++++++++++++++++++++++++++++++++++++ ports/samd/mboot/README.md | 5 ++++ 2 files changed, 65 insertions(+) create mode 100644 ports/samd/mboot/LICENSE create mode 100644 ports/samd/mboot/README.md diff --git a/ports/samd/mboot/LICENSE b/ports/samd/mboot/LICENSE new file mode 100644 index 0000000000000..7fffbab21e0e6 --- /dev/null +++ b/ports/samd/mboot/LICENSE @@ -0,0 +1,60 @@ +The MIT License (MIT) + +Copyright (c) Microsoft + +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. + +Third Party Programs: The software may include third party programs that +Microsoft, not the third party, licenses to you under this agreement. +Notices, if any, for the third party programs are included for your +information only. + + + +Otherwise, where noted: + +/* ---------------------------------------------------------------------------- + * SAM Software Package License + * ---------------------------------------------------------------------------- + * Copyright (c) 2011-2014, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following condition is met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + diff --git a/ports/samd/mboot/README.md b/ports/samd/mboot/README.md new file mode 100644 index 0000000000000..1695f0a891126 --- /dev/null +++ b/ports/samd/mboot/README.md @@ -0,0 +1,5 @@ +The SAMD Xplained Pro bootloader is built using the files from the +repository https://github.com/adafruit/uf2-samdx1. The repository +includes the LICENSE file https://github.com/adafruit/uf2-samdx1/LICENSE +and a README https://github.com/adafruit/uf2-samdx1/README.md with +build instructions. From 0e7c3901b897a1edd53b56b6119f2d4119e88842 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 2 Oct 2024 19:16:39 +1000 Subject: [PATCH 044/546] docs: Add a "Reset and Boot Sequence" reference page. Previously individual ports documented these aspects to varying degrees, but most of the information is common to all ports. In particular, this adds a canonical explanation of `boot.py` and `main.py`. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/esp8266/general.rst | 35 +-- docs/esp8266/quickref.rst | 8 +- docs/library/builtins.rst | 4 + docs/library/machine.RTC.rst | 2 +- docs/library/machine.USBDevice.rst | 12 +- docs/library/machine.rst | 9 +- docs/library/pyb.rst | 13 +- docs/pyboard/tutorial/reset.rst | 2 + docs/reference/index.rst | 1 + docs/reference/mpremote.rst | 6 +- docs/reference/repl.rst | 9 +- docs/reference/reset_boot.rst | 260 ++++++++++++++++++ docs/renesas-ra/tutorial/program_in_flash.rst | 2 + docs/renesas-ra/tutorial/reset.rst | 20 +- docs/wipy/tutorial/reset.rst | 2 + 15 files changed, 311 insertions(+), 74 deletions(-) create mode 100644 docs/reference/reset_boot.rst diff --git a/docs/esp8266/general.rst b/docs/esp8266/general.rst index be0437e752eed..d7211aa5ab745 100644 --- a/docs/esp8266/general.rst +++ b/docs/esp8266/general.rst @@ -74,40 +74,7 @@ as possible after use. Boot process ------------ -On boot, MicroPython EPS8266 port executes ``_boot.py`` script from internal -frozen modules. It mounts filesystem in FlashROM, or if it's not available, -performs first-time setup of the module and creates the filesystem. This -part of the boot process is considered fixed, and not available for customization -for end users (even if you build from source, please refrain from changes to -it; customization of early boot process is available only to advanced users -and developers, who can diagnose themselves any issues arising from -modifying the standard process). - -Once the filesystem is mounted, ``boot.py`` is executed from it. The standard -version of this file is created during first-time module set up and has -commands to start a WebREPL daemon (disabled by default, configurable -with ``webrepl_setup`` module), etc. This -file is customizable by end users (for example, you may want to set some -parameters or add other services which should be run on -a module start-up). But keep in mind that incorrect modifications to boot.py -may still lead to boot loops or lock ups, requiring to reflash a module -from scratch. (In particular, it's recommended that you use either -``webrepl_setup`` module or manual editing to configure WebREPL, but not -both). - -As a final step of boot procedure, ``main.py`` is executed from filesystem, -if exists. This file is a hook to start up a user application each time -on boot (instead of going to REPL). For small test applications, you may -name them directly as ``main.py``, and upload to module, but instead it's -recommended to keep your application(s) in separate files, and have just -the following in ``main.py``:: - - import my_app - my_app.main() - -This will allow to keep the structure of your application clear, as well as -allow to install multiple applications on a board, and switch among them. - +See :doc:`/reference/reset_boot`. Known Issues ------------ diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index 6f02da95d89d2..635f1f834bb60 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -163,10 +163,10 @@ sys.stdin.read() if it's needed to read characters from the UART(0) while it's also used for the REPL (or detach, read, then reattach). When detached the UART(0) can be used for other purposes. -If there are no objects in any of the dupterm slots when the REPL is -started (on hard or soft reset) then UART(0) is automatically attached. -Without this, the only way to recover a board without a REPL would be to -completely erase and reflash (which would install the default boot.py which +If there are no objects in any of the dupterm slots when the REPL is started (on +:doc:`hard or soft reset `) then UART(0) is automatically +attached. Without this, the only way to recover a board without a REPL would be +to completely erase and reflash (which would install the default boot.py which attaches the REPL). To detach the REPL from UART0, use:: diff --git a/docs/library/builtins.rst b/docs/library/builtins.rst index e489375b1f917..88b1fbcfe4a41 100644 --- a/docs/library/builtins.rst +++ b/docs/library/builtins.rst @@ -170,6 +170,10 @@ Exceptions .. exception:: KeyboardInterrupt + |see_cpython| `python:KeyboardInterrupt`. + + See also in the context of :ref:`soft_bricking`. + .. exception:: KeyError .. exception:: MemoryError diff --git a/docs/library/machine.RTC.rst b/docs/library/machine.RTC.rst index fcd78f1c39863..ad0b0759efe5e 100644 --- a/docs/library/machine.RTC.rst +++ b/docs/library/machine.RTC.rst @@ -83,7 +83,7 @@ Methods a `bytes` object. Data written to RTC user memory is persistent across restarts, including - `machine.soft_reset()` and `machine.deepsleep()`. + :ref:`soft_reset` and `machine.deepsleep()`. The maximum length of RTC user memory is 2048 bytes by default on esp32, and 492 bytes on esp8266. diff --git a/docs/library/machine.USBDevice.rst b/docs/library/machine.USBDevice.rst index a47fda2a28d35..5c18c49a75bb5 100644 --- a/docs/library/machine.USBDevice.rst +++ b/docs/library/machine.USBDevice.rst @@ -32,10 +32,10 @@ Managing a runtime USB interface can be tricky, especially if you are communicat with MicroPython over a built-in USB-CDC serial port that's part of the same USB device. -- A MicroPython soft reset will always clear all runtime USB interfaces, which - results in the entire USB device disconnecting from the host. If MicroPython - is also providing a built-in USB-CDC serial port then this will re-appear - after the soft reset. +- A MicroPython :ref:`soft reset ` will always clear all runtime USB + interfaces, which results in the entire USB device disconnecting from the + host. If MicroPython is also providing a built-in USB-CDC serial port then + this will re-appear after the soft reset. This means some functions (like ``mpremote run``) that target the USB-CDC serial port will immediately fail if a runtime USB interface is active, @@ -44,9 +44,9 @@ device. no more runtime USB interface. - To configure a runtime USB device on every boot, it's recommended to place the - configuration code in the ``boot.py`` file on the :ref:`device VFS + configuration code in the :ref:`boot.py` file on the :ref:`device VFS `. On each reset this file is executed before the USB subsystem is - initialised (and before ``main.py``), so it allows the board to come up with the runtime + initialised (and before :ref:`main.py`), so it allows the board to come up with the runtime USB device immediately. - For development or debugging, it may be convenient to connect a hardware diff --git a/docs/library/machine.rst b/docs/library/machine.rst index 7d2eb26a7ea34..76d111f11ef3d 100644 --- a/docs/library/machine.rst +++ b/docs/library/machine.rst @@ -62,14 +62,13 @@ Reset related functions .. function:: reset() - Resets the device in a manner similar to pushing the external RESET - button. + :ref:`Hard resets ` the device in a manner similar to pushing the + external RESET button. .. function:: soft_reset() - Performs a soft reset of the interpreter, deleting all Python objects and - resetting the Python heap. It tries to retain the method by which the user - is connected to the MicroPython REPL (eg serial, USB, Wifi). + Performs a :ref:`soft reset ` of the interpreter, deleting all + Python objects and resetting the Python heap. .. function:: reset_cause() diff --git a/docs/library/pyb.rst b/docs/library/pyb.rst index f169a77f3a6ca..5eb75c22b608d 100644 --- a/docs/library/pyb.rst +++ b/docs/library/pyb.rst @@ -147,10 +147,10 @@ Power related functions (internal oscillator) directly. The higher frequencies use the HSE to drive the PLL (phase locked loop), and then use the output of the PLL. - Note that if you change the frequency while the USB is enabled then - the USB may become unreliable. It is best to change the frequency - in boot.py, before the USB peripheral is started. Also note that sysclk - frequencies below 36MHz do not allow the USB to function correctly. + Note that if you change the frequency while the USB is enabled then the USB + may become unreliable. It is best to change the frequency in :ref:`boot.py`, + before the USB peripheral is started. Also note that sysclk frequencies below + 36MHz do not allow the USB to function correctly. .. function:: wfi() @@ -205,8 +205,9 @@ Miscellaneous functions .. function:: main(filename) - Set the filename of the main script to run after boot.py is finished. If - this function is not called then the default file main.py will be executed. + Set the filename of the main script to run after :ref:`boot.py` is finished. + If this function is not called then the default file :ref:`main.py` will be + executed. It only makes sense to call this function from within boot.py. diff --git a/docs/pyboard/tutorial/reset.rst b/docs/pyboard/tutorial/reset.rst index 59a3cd82ae1a7..ad56995c60dd7 100644 --- a/docs/pyboard/tutorial/reset.rst +++ b/docs/pyboard/tutorial/reset.rst @@ -10,6 +10,8 @@ execution of ``boot.py`` and ``main.py`` and gives default USB settings. If you have problems with the filesystem you can do a factory reset, which restores the filesystem to its original state. +For more information, see :doc:`/reference/reset_boot`. + Safe mode --------- diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 98f6b65737699..1558c0fdfa9b6 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -21,6 +21,7 @@ implementation and the best practices to use them. glossary.rst repl.rst + reset_boot.rst mpremote.rst mpyfiles.rst isr_rules.rst diff --git a/docs/reference/mpremote.rst b/docs/reference/mpremote.rst index b47ec76c98efe..d4c6983595fec 100644 --- a/docs/reference/mpremote.rst +++ b/docs/reference/mpremote.rst @@ -547,9 +547,9 @@ device at ``/dev/ttyACM1``, printing each result. mpremote resume exec "print_state_info()" soft-reset -Connect to the device without triggering a soft reset and execute the -``print_state_info()`` function (e.g. to find out information about the current -program state), then trigger a soft reset. +Connect to the device without triggering a :ref:`soft reset ` and +execute the ``print_state_info()`` function (e.g. to find out information about +the current program state), then trigger a soft reset. .. code-block:: bash diff --git a/docs/reference/repl.rst b/docs/reference/repl.rst index 55f76ee1a03b9..be02ddebde8e2 100644 --- a/docs/reference/repl.rst +++ b/docs/reference/repl.rst @@ -143,10 +143,12 @@ the auto-indent feature, and changes the prompt from ``>>>`` to ``===``. For exa Paste Mode allows blank lines to be pasted. The pasted text is compiled as if it were a file. Pressing Ctrl-D exits paste mode and initiates the compilation. +.. _repl_soft_reset: + Soft reset ---------- -A soft reset will reset the python interpreter, but tries not to reset the +A :ref:`soft_reset` will reset the python interpreter, but tries not to reset the method by which you're connected to the MicroPython board (USB-serial, or Wifi). You can perform a soft reset from the REPL by pressing Ctrl-D, or from your python @@ -182,6 +184,9 @@ variables no longer exist: ['__name__', 'pyb'] >>> +For more information about reset types and the startup process, see +:doc:`/reference/reset_boot`. + The special variable _ (underscore) ----------------------------------- @@ -196,6 +201,8 @@ So you can use the underscore to save the result in a variable. For example: 15 >>> +.. _raw_repl: + Raw mode and raw-paste mode --------------------------- diff --git a/docs/reference/reset_boot.rst b/docs/reference/reset_boot.rst new file mode 100644 index 0000000000000..7c0d5f334a6e8 --- /dev/null +++ b/docs/reference/reset_boot.rst @@ -0,0 +1,260 @@ +Reset and Boot Sequence +======================= + +A device running MicroPython follows a particular boot sequence to start up and +initialise itself after a reset. + +.. _hard_reset: + +Hard reset +---------- + +Booting from hard reset is what happens when a board is first powered up, a cold +boot. This is a complete reset of the MCU hardware. + +The MicroPython port code initialises all essential hardware (including embedded +clocks and power regulators, internal serial UART, etc), and then starts the +MicroPython environment. Existing :doc:`RTC ` +configuration may be retained after a hard reset, but all other hardware state +is cleared. + +The same hard reset boot sequence can be triggered by a number of events such as: + +- Python code executing :func:`machine.reset()`. +- User presses a physical Reset button on the board (where applicable). +- Waking from deep sleep (on most ports). +- MCU hardware watchdog reset. +- MCU hardware brown out detector. + +The details of hardware-specific reset triggers depend on the port and +associated hardware. The :func:`machine.reset_cause()` function can be used to +further determine the cause of a reset. + +.. _soft_reset: + +Soft Reset +---------- + +When MicroPython is already running, it's possible to trigger a soft reset by +:ref:`typing Ctrl-D in the REPL ` or executing +:func:`machine.soft_reset()`. + +A soft reset clears the Python interpreter, frees all Python memory, and starts +the MicroPython environment again. + +State which is cleared by a soft reset includes: + +- All Python variables, objects, imported modules, etc. +- Most peripherals configured using the :doc:`machine module + `. There are very limited exceptions, for example + :doc:`machine.Pin ` modes (i.e. if a pin is input or + output, high or low) are not reset on most ports. More advanced configuration + such as :func:`Pin.irq()` is always reset. +- Bluetooth. +- Network sockets. Open TCP sockets are closed cleanly with respect to the other party. +- Open files. The filesystem is left in a valid state. + +Some system state remains the same after a soft reset, including: + +- Any existing network connections (Ethernet, Wi-Fi, etc) remain active at the + IP Network layer. Querying the :doc:`network interface from code + ` may indicate the network interface is still active with a + configured IP address, etc. +- An active :doc:`REPL ` appears continuous before and after soft reset, + except in some unusual cases: + + * If the :ref:`machine.USBDevice ` class has been used to + create a custom USB interface then any built-in USB serial device will + appear to disconnect and reconnect as the custom USB interface must be + cleared during reset. + * A serial UART REPL will restore its default hardware configuration (baud + rate, etc). + +- CPU clock speed is usually not changed by a soft reset. +- :doc:`RTC ` configuration (i.e. setting of the current + time) is not changed by soft reset. + +.. _boot_sequence: + +Boot Sequence +------------- + +When MicroPython boots following either a hard or soft reset, it follows this +boot sequence in order: + +_boot.py +^^^^^^^^ + +This is an internal script :doc:`frozen into the MicroPython firmware +`. It is provided by MicroPython on many ports to do essential +initialisation. + +For example, on most ports ``_boot.py`` will detect the first boot of a new +device and format the :doc:`internal flash filesystem ` ready for +use. + +Unless you're creating a custom MicroPython build or adding a new port then you +probably don't need to worry about ``_boot.py``. It's best not to change the +contents unless you really know what you're doing. + +.. _boot.py: + +boot.py +^^^^^^^ + +A file named ``boot.py`` can be copied to the board's internal :ref:`filesystem +` using :doc:`mpremote `. + +If ``boot.py`` is found then it is executed. You can add code in ``boot.py`` to +perform custom one-off initialisation (for example, to configure the board's +hardware). + +A common practice is to configure a board's network connection in ``boot.py`` so +that it's always available after reset for use with the :doc:`REPL `, +:doc:`mpremote `, etc. + +.. warning:: boot.py should always exit and not run indefinitely. + + Depending on the port, some hardware initialisation is delayed until after + ``boot.py`` exits. This includes initialising USB on the stm32 port and all + ports which support :ref:`machine.USBDevice `. On these + ports, output printed from ``boot.py`` may not be visible on the built-in USB + serial port until after ``boot.py`` finishes running. + + The purpose of this late initialisation is so that it's possible to + pre-configure particular hardware in ``boot.py``, and then have it start with + the correct configuration. + +.. note:: It is sometimes simpler to not have a ``boot.py`` file and place any + initialisation code at the top of ``main.py`` instead. + +.. _main.py: + +main.py +^^^^^^^ + +Similar to ``boot.py``, a file named ``main.py`` can be copied to the board's +internal :ref:`filesystem `. If found then it is executed next in the +startup process. + +``main.py`` is for any Python code that you want to run each time your device +starts. + +Some tips for ``main.py`` usage: + +- ``main.py`` doesn't have to exit, feel free to put an infinite ``while + True`` loop in there. +- For complex Python applications then you don't need to put all your + code in ``main.py``. ``main.py`` can be a simple entry point that + imports your application and starts execution:: + + import my_app + my_app.main() + + This can help keep the structure of your application clear. It also makes + it easy to install multiple applications on a board and switch among them. +- It's good practice when writing robust apps to wrap code in ``main.py`` with an + exception handler to take appropriate action if the code crashes. For example:: + + import machine, sys + import my_app + try: + my_app.main() + except Exception as e: + print("Fatal error in main:") + sys.print_exception(e) + + # Following a normal Exception or main() exiting, reset the board. + # Following a non-Exception error such as KeyboardInterrupt (Ctrl-C), + # this code will drop to a REPL. Place machine.reset() in a finally + # block to always reset, instead. + machine.reset() + + Otherwise MicroPython will drop to the REPL following any crash or if main + exits (see below). + +- Any global variables that were set in ``boot.py`` will still be set in the + global context of ``main.py``. + +- To fully optimise flash usage and memory consumption, you can copy + :doc:`pre-compiled ` ``main.mpy`` and/or ``boot.mpy`` files to the + filesystem, or even :doc:`freeze ` them into the firmware build + instead. +- ``main.py`` execution is skipped when a soft reset is initiated from :ref:`raw + REPL mode ` (for example, when :doc:`mpremote ` or another + program is interacting directly with MicroPython). + +Interactive Interpreter (REPL) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If ``main.py`` is not found, or if ``main.py`` exits, then :doc:`repl` +will start immediately. + +.. note:: Even if ``main.py`` contains an infinite loop, typing Ctrl-C on the + REPL serial port will inject a `KeyboardInterrupt`. If no exception + handler catches it then ``main.py`` will exit and the REPL will start. + +Any global variables that were set in ``boot.py`` and ``main.py`` will still be +set in the global context of the REPL. + +The REPL continues executing until Python code triggers a hard or soft reset. + +.. _soft_bricking: + +Soft Bricking (failure to boot) +--------------------------------- + +It is rare but possible for MicroPython to become unresponsive during startup, a +state sometimes called "soft bricked". For example: + +- If ``boot.py`` execution gets stuck and the native USB serial port + never initialises. +- If Python code reconfigures the REPL interface, making it inaccessible. + +Rest assured, recovery is possible! + +KeyboardInterrupt +^^^^^^^^^^^^^^^^^ + +In many cases, opening the REPL serial port and typing ``Ctrl-C`` will inject +`KeyboardInterrupt` and may cause the running script to exit and a REPL to +start. From the REPL, you can use :func:`os.remove()` to remove the misbehaving +Python file:: + + import os + os.remove('main.py') + +To confirm which files are still present in the internal filesystem:: + + import os + os.listdir() + +Safe Mode and Factory Reset +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you're unable to easily access the REPL then you may need to perform one of +two processes: + +1. "Safe mode" boot, which skips ``boot.py`` and ``main.py`` and immediately + starts a REPL, allowing you to clean up. This is only supported on some ports. +2. Factory Reset to erase the entire contents of the flash filesystem. This may + also be necessary if the internal flash filesystem has become corrupted + somehow. + +The specific process(es) are different on each port: + +- :doc:`pyboard and stm32 port instructions ` +- :doc:`renesas-ra port instructions ` +- :doc:`wipy port instructions ` + +For ports without specific instructions linked above, the factory reset process +involves erasing the board's entire flash and then flashing MicroPython again +from scratch. Usually this will involve the same tool(s) that were originally +used to install MicroPython. Consult the installation docs for your board, or +ask on the `GitHub Discussions`_ if you're not sure. + +.. warning:: Re-flashing the MicroPython firmware without erasing the entire + flash first will usually not recover from soft bricking, as a + firmware update usually preserves the contents of the filesystem. + +.. _GitHub Discussions: https://github.com/orgs/micropython/discussions diff --git a/docs/renesas-ra/tutorial/program_in_flash.rst b/docs/renesas-ra/tutorial/program_in_flash.rst index 99a7a3839dd46..985ce6c7fe661 100644 --- a/docs/renesas-ra/tutorial/program_in_flash.rst +++ b/docs/renesas-ra/tutorial/program_in_flash.rst @@ -28,6 +28,8 @@ As the factory setting, following 2 files are created in the file system: * boot.py : executed first when the system starts * main.py : executed after boot.py completes +See :doc:`/reference/reset_boot` for more information. + Write a program in the internal file system ------------------------------------------- diff --git a/docs/renesas-ra/tutorial/reset.rst b/docs/renesas-ra/tutorial/reset.rst index de5f4db91b3e0..8799796479560 100644 --- a/docs/renesas-ra/tutorial/reset.rst +++ b/docs/renesas-ra/tutorial/reset.rst @@ -20,6 +20,8 @@ If that isn't working you can perform a hard reset (turn-it-off-and-on-again) by pressing the RESET button. This will end your session, disconnecting whatever program (PuTTY, screen, etc) that you used to connect to the board. +For more details, see :doc:`/reference/reset_boot`. + boot mode --------- @@ -29,7 +31,9 @@ There are 3 boot modes: * safe boot mode * factory filesystem boot mode -boot.py and main.py are executed on "normal boot mode". +boot.py and main.py are executed on "normal boot mode". See :ref:`boot_sequence`. + +The other modes can be used to recover from :ref:`soft_bricking`: boot.py and main.py are *NOT* executed on "safe boot mode". @@ -46,16 +50,4 @@ on the board: You have created the main.py which executes LED1 blinking in the previous part. If you change the boot mode to safe boot mode, the MicroPython starts without -the execution of main.py. Then you can remove the main.py by following -command or change the boot mode to factory file system boot mode.:: - - import os - os.remove('main.py') - -or change the boot mode to factory file system boot mode. - -You can confirm that the initialized file system that there are only boot.py and main.py files.:: - - import os - os.listdir() - +the execution of main.py. diff --git a/docs/wipy/tutorial/reset.rst b/docs/wipy/tutorial/reset.rst index 1715d3e29788d..fc56429b81a3f 100644 --- a/docs/wipy/tutorial/reset.rst +++ b/docs/wipy/tutorial/reset.rst @@ -16,6 +16,8 @@ There are soft resets and hard resets. import machine machine.reset() +For more information, see :doc:`/reference/reset_boot`. + Safe boot --------- From 9361a9f50af1e0b657eed270df6556ec253ddaeb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 3 Oct 2024 09:28:53 +1000 Subject: [PATCH 045/546] docs/rp2: Add a small factory reset page. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/reference/reset_boot.rst | 1 + docs/rp2/tutorial/intro.rst | 1 + docs/rp2/tutorial/reset.rst | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 docs/rp2/tutorial/reset.rst diff --git a/docs/reference/reset_boot.rst b/docs/reference/reset_boot.rst index 7c0d5f334a6e8..f6307c08cdfb3 100644 --- a/docs/reference/reset_boot.rst +++ b/docs/reference/reset_boot.rst @@ -245,6 +245,7 @@ The specific process(es) are different on each port: - :doc:`pyboard and stm32 port instructions ` - :doc:`renesas-ra port instructions ` +- :doc:`rp2 port instructions ` - :doc:`wipy port instructions ` For ports without specific instructions linked above, the factory reset process diff --git a/docs/rp2/tutorial/intro.rst b/docs/rp2/tutorial/intro.rst index 69c3e6b0a54ae..2d2990105dba0 100644 --- a/docs/rp2/tutorial/intro.rst +++ b/docs/rp2/tutorial/intro.rst @@ -8,4 +8,5 @@ Let's get started! .. toctree:: :maxdepth: 1 + reset.rst pio.rst diff --git a/docs/rp2/tutorial/reset.rst b/docs/rp2/tutorial/reset.rst new file mode 100644 index 0000000000000..3c296ec46ee18 --- /dev/null +++ b/docs/rp2/tutorial/reset.rst @@ -0,0 +1,18 @@ +Factory reset +============= + +If something unexpected happens and your RP2xxx-based board no longer boots +MicroPython, then you may have to factory reset it. For more details, see +:ref:`soft_bricking`. + +Factory resetting the MicroPython rp2 port involves fully erasing the flash and +resetting the flash memory, so you will need to re-flash the MicroPython +firmware afterwards and copy any Python files to the filesystem again. + +1. Follow the instructions on the Raspberry Pi website for `resetting flash + memory`_. +2. Copy the MicroPython .uf2 firmware file to your board. If needed, this file + can be found on the `MicroPython downloads page`_. + +.. _resetting flash memory: https://www.raspberrypi.com/documentation/microcontrollers/pico-series.html#resetting-flash-memory +.. _MicroPython downloads page: https://micropython.org/download/?port=rp2 From a23277e3b0400b6b5ca6ce3d88e3a38859754a4e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 3 Oct 2024 09:42:13 +1000 Subject: [PATCH 046/546] docs/esp32: Add a factory reset page. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/esp32/tutorial/index.rst | 1 + docs/esp32/tutorial/intro.rst | 2 ++ docs/esp32/tutorial/reset.rst | 25 +++++++++++++++++++++++++ docs/reference/reset_boot.rst | 1 + 4 files changed, 29 insertions(+) create mode 100644 docs/esp32/tutorial/reset.rst diff --git a/docs/esp32/tutorial/index.rst b/docs/esp32/tutorial/index.rst index c6242d731f180..2435f2ecd2f59 100644 --- a/docs/esp32/tutorial/index.rst +++ b/docs/esp32/tutorial/index.rst @@ -21,3 +21,4 @@ to ``__. intro.rst pwm.rst peripheral_access.rst + reset.rst diff --git a/docs/esp32/tutorial/intro.rst b/docs/esp32/tutorial/intro.rst index be09599871ce6..cf4d0bcbd2f55 100644 --- a/docs/esp32/tutorial/intro.rst +++ b/docs/esp32/tutorial/intro.rst @@ -50,6 +50,8 @@ features, there are daily builds. If your board has SPIRAM support you can use either the standard firmware or the firmware with SPIRAM support, and in the latter case you will have access to more RAM for Python objects. +.. _esp32_flashing: + Deploying the firmware ---------------------- diff --git a/docs/esp32/tutorial/reset.rst b/docs/esp32/tutorial/reset.rst new file mode 100644 index 0000000000000..b3fc6a85bd436 --- /dev/null +++ b/docs/esp32/tutorial/reset.rst @@ -0,0 +1,25 @@ +Factory reset +============= + +If something unexpected happens and your ESP32-based board no longer boots +MicroPython, then you may have to factory reset it. For more details, see +:ref:`soft_bricking`. + +Factory resetting the MicroPython esp32 port involves fully erasing the flash +and resetting the flash memory, so you will need to re-flash the MicroPython +firmware afterwards and copy any Python files to the filesystem again. + +1. You will need the Espressif `esptool`_ installed on your system. This is the + same tool that you may have used to initially install MicroPython on your + board (see :ref:`installation instructions `). +2. Find the serial port name of your board, and then use esptool to erase the + entire flash contents:: + + esptool.py -p PORTNAME erase_flash + +3. Use esptool to flash the MicroPython file to your board again. If needed, + this file and flashing instructions can be found on the `MicroPython + downloads page`_. + +.. _esptool: https://github.com/espressif/esptool +.. _MicroPython downloads page: https://micropython.org/download/?port=esp32 diff --git a/docs/reference/reset_boot.rst b/docs/reference/reset_boot.rst index f6307c08cdfb3..4772d02dd1e32 100644 --- a/docs/reference/reset_boot.rst +++ b/docs/reference/reset_boot.rst @@ -244,6 +244,7 @@ two processes: The specific process(es) are different on each port: - :doc:`pyboard and stm32 port instructions ` +- :doc:`esp32 port instructions ` - :doc:`renesas-ra port instructions ` - :doc:`rp2 port instructions ` - :doc:`wipy port instructions ` From c5d74fe46876767cc43d7d23d949d0f7c3dcd6bd Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 8 Oct 2024 14:41:44 +1100 Subject: [PATCH 047/546] docs/library: Note link between machine.soft_reset() and sys.exit(). This is currently an implementation detail of MicroPython rather than by design. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/library/builtins.rst | 6 ++++++ docs/library/sys.rst | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/library/builtins.rst b/docs/library/builtins.rst index 88b1fbcfe4a41..5956aea7ab20f 100644 --- a/docs/library/builtins.rst +++ b/docs/library/builtins.rst @@ -194,6 +194,12 @@ Exceptions |see_cpython| `python:SystemExit`. + On non-embedded ports (i.e. Windows and Unix), an unhandled ``SystemExit`` + exits the MicroPython process in a similar way to CPython. + + On embedded ports, an unhandled ``SystemExit`` currently causes a + :ref:`soft_reset` of MicroPython. + .. exception:: TypeError |see_cpython| `python:TypeError`. diff --git a/docs/library/sys.rst b/docs/library/sys.rst index 7b34a0e31c6bf..c72214c13108e 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -12,9 +12,12 @@ Functions .. function:: exit(retval=0, /) Terminate current program with a given exit code. Underlyingly, this - function raise as `SystemExit` exception. If an argument is given, its + function raises a `SystemExit` exception. If an argument is given, its value given as an argument to `SystemExit`. + On embedded ports (i.e. all ports but Windows and Unix), an unhandled + `SystemExit` currently causes a :ref:`soft_reset` of MicroPython. + .. function:: atexit(func) Register *func* to be called upon termination. *func* must be a callable From c1b8e65c8e216ce2e90933aef6772de46ad54407 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 5 Nov 2024 12:24:21 +1100 Subject: [PATCH 048/546] docs: Change copyright line to mention "authors and contributors". The docs have been authored by many people now. Instead of singling out individuals in the copyright line, prefer to mention all "MicroPython authors and contributors". Individual contributions can still be discovered via the git history. Signed-off-by: Damien George --- docs/conf.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 32cc2be10e343..e5c6ba98090af 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,6 +36,9 @@ "is_release": micropy_version != "latest", } +# Authors used in various parts of the documentation. +micropy_authors = "MicroPython authors and contributors" + # -- General configuration ------------------------------------------------ @@ -68,7 +71,7 @@ # General information about the project. project = "MicroPython" -copyright = "- The MicroPython Documentation is Copyright © 2014-2024, Damien P. George, Paul Sokolovsky, and contributors" +copyright = "- The MicroPython Documentation is Copyright © 2014-2024, " + micropy_authors # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -244,7 +247,7 @@ master_doc, "MicroPython.tex", "MicroPython Documentation", - "Damien P. George, Paul Sokolovsky, and contributors", + micropy_authors, "manual", ), ] @@ -281,7 +284,7 @@ "index", "micropython", "MicroPython Documentation", - ["Damien P. George, Paul Sokolovsky, and contributors"], + [micropy_authors], 1, ), ] @@ -300,7 +303,7 @@ master_doc, "MicroPython", "MicroPython Documentation", - "Damien P. George, Paul Sokolovsky, and contributors", + micropy_authors, "MicroPython", "One line description of project.", "Miscellaneous", From f562aa1291ad44605f6b5be75b325a40a208ec41 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 13 Nov 2024 18:14:47 +1100 Subject: [PATCH 049/546] extmod/network_cyw43: Fix isconnected() result on AP interface. This function is documented to return True if any stations are connected to the AP. Without this fix it returns True whenever the driver has brought the AP interface up. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- extmod/network_cyw43.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/extmod/network_cyw43.c b/extmod/network_cyw43.c index 3066cac75d3b7..891e945de5118 100644 --- a/extmod/network_cyw43.c +++ b/extmod/network_cyw43.c @@ -311,7 +311,17 @@ static MP_DEFINE_CONST_FUN_OBJ_1(network_cyw43_disconnect_obj, network_cyw43_dis static mp_obj_t network_cyw43_isconnected(mp_obj_t self_in) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_bool(cyw43_tcpip_link_status(self->cyw, self->itf) == 3); + bool result = (cyw43_tcpip_link_status(self->cyw, self->itf) == CYW43_LINK_UP); + + if (result && self->itf == CYW43_ITF_AP) { + // For AP we need to not only know if the link is up, but also if any stations + // have associated. + uint8_t mac_buf[6]; + int num_stas = 1; + cyw43_wifi_ap_get_stas(self->cyw, &num_stas, mac_buf); + result = num_stas > 0; + } + return mp_obj_new_bool(result); } static MP_DEFINE_CONST_FUN_OBJ_1(network_cyw43_isconnected_obj, network_cyw43_isconnected); From af743eaf59196e9133bd3c6de53cae1bb6c9a59a Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Nov 2024 11:26:34 +1100 Subject: [PATCH 050/546] extmod/network_cyw43: Fix uninitialised variable in status('stations'). The `num_stas` was uninitialised and if it happened to take the value 0 then no results were returned. It now has the correct maximum value. Signed-off-by: Damien George --- extmod/network_cyw43.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extmod/network_cyw43.c b/extmod/network_cyw43.c index 891e945de5118..cebc8deb0da91 100644 --- a/extmod/network_cyw43.c +++ b/extmod/network_cyw43.c @@ -361,13 +361,15 @@ static mp_obj_t network_cyw43_status(size_t n_args, const mp_obj_t *args) { if (self->itf != CYW43_ITF_AP) { mp_raise_ValueError(MP_ERROR_TEXT("AP required")); } - int num_stas; - uint8_t macs[32 * 6]; + static const unsigned mac_len = 6; + static const unsigned max_stas = 32; + int num_stas = max_stas; + uint8_t macs[max_stas * mac_len]; cyw43_wifi_ap_get_stas(self->cyw, &num_stas, macs); mp_obj_t list = mp_obj_new_list(num_stas, NULL); for (int i = 0; i < num_stas; ++i) { mp_obj_t tuple[1] = { - mp_obj_new_bytes(&macs[i * 6], 6), + mp_obj_new_bytes(&macs[i * mac_len], mac_len), }; ((mp_obj_list_t *)MP_OBJ_TO_PTR(list))->items[i] = mp_obj_new_tuple(1, tuple); } From 181800eebde2f3dbda2862c175150d884eef728d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 13 Nov 2024 18:04:59 +1100 Subject: [PATCH 051/546] extmod/network_cyw43: Allow configuring active AP interface. Configuring the AP for cyw43 writes to some buffers that are only sent to the modem when the interface is brought up. This means you can't configure the AP after calling active(True), the new settings seem to be accepted but the radio doesn't change. This is different to the WLAN behaviour on other ports. The esp8266 port requires calling active(True) on the AP before configuring, even. Fix this by bouncing the AP interface after a config change, if it's active. Configuring with active(False) still works the same as before. Adds a static variable to track interface active state, rather than relying on the LWIP interface state. This is because the interface state is updated by a driver callback and there's a race: if code calls active(True) and then config(a=b) then the driver doesn't know it's active yet and the changes aren't correctly applied. It is possible this pattern will cause the AP to come up briefly with the default "PICOabcd" SSID before being reconfigured, however (due to the aforementioned race condition) it seems like this may not happen at all before the new config is applied. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- extmod/network_cyw43.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/extmod/network_cyw43.c b/extmod/network_cyw43.c index cebc8deb0da91..02b022cb81327 100644 --- a/extmod/network_cyw43.c +++ b/extmod/network_cyw43.c @@ -60,6 +60,10 @@ typedef struct _network_cyw43_obj_t { static const network_cyw43_obj_t network_cyw43_wl_sta = { { &mp_network_cyw43_type }, &cyw43_state, CYW43_ITF_STA }; static const network_cyw43_obj_t network_cyw43_wl_ap = { { &mp_network_cyw43_type }, &cyw43_state, CYW43_ITF_AP }; +// Avoid race conditions with callbacks by tracking the last up or down request +// we have made for each interface. +static bool if_active[2]; + static void network_cyw43_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(self_in); struct netif *netif = &self->cyw->netif[self->itf]; @@ -122,6 +126,10 @@ static MP_DEFINE_CONST_FUN_OBJ_3(network_cyw43_ioctl_obj, network_cyw43_ioctl); /*******************************************************************************/ // network API +static uint32_t get_country_code(void) { + return CYW43_COUNTRY(mod_network_country_code[0], mod_network_country_code[1], 0); +} + static mp_obj_t network_cyw43_deinit(mp_obj_t self_in) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(self_in); cyw43_deinit(self->cyw); @@ -132,10 +140,11 @@ static MP_DEFINE_CONST_FUN_OBJ_1(network_cyw43_deinit_obj, network_cyw43_deinit) static mp_obj_t network_cyw43_active(size_t n_args, const mp_obj_t *args) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (n_args == 1) { - return mp_obj_new_bool(cyw43_tcpip_link_status(self->cyw, self->itf)); + return mp_obj_new_bool(if_active[self->itf]); } else { - uint32_t country = CYW43_COUNTRY(mod_network_country_code[0], mod_network_country_code[1], 0); - cyw43_wifi_set_up(self->cyw, self->itf, mp_obj_is_true(args[1]), country); + bool value = mp_obj_is_true(args[1]); + cyw43_wifi_set_up(self->cyw, self->itf, value, get_country_code()); + if_active[self->itf] = value; return mp_const_none; } } @@ -457,6 +466,10 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); } + // A number of these options only update buffers in memory, and + // won't do anything until the interface is cycled down and back up + bool cycle_active = false; + 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]; @@ -469,6 +482,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map } case MP_QSTR_channel: { cyw43_wifi_ap_set_channel(self->cyw, mp_obj_get_int(e->value)); + cycle_active = true; break; } case MP_QSTR_ssid: @@ -476,6 +490,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map size_t len; const char *str = mp_obj_str_get_data(e->value, &len); cyw43_wifi_ap_set_ssid(self->cyw, len, (const uint8_t *)str); + cycle_active = true; break; } case MP_QSTR_monitor: { @@ -495,6 +510,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map } case MP_QSTR_security: { cyw43_wifi_ap_set_auth(self->cyw, mp_obj_get_int(e->value)); + cycle_active = true; break; } case MP_QSTR_key: @@ -502,6 +518,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map size_t len; const char *str = mp_obj_str_get_data(e->value, &len); cyw43_wifi_ap_set_password(self->cyw, len, (const uint8_t *)str); + cycle_active = true; break; } case MP_QSTR_pm: { @@ -531,6 +548,13 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map } } + // If the interface is already active, cycle it down and up + if (cycle_active && if_active[self->itf]) { + uint32_t country = get_country_code(); + cyw43_wifi_set_up(self->cyw, self->itf, false, country); + cyw43_wifi_set_up(self->cyw, self->itf, true, country); + } + return mp_const_none; } } From b65e89107c0194bfdaf92720786341a2047f36b6 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 16 Oct 2024 12:03:39 +1100 Subject: [PATCH 052/546] py/py.mk: Add check that any specified USER_C_MODULES folder exists. Signed-off-by: Andrew Leech --- py/py.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/py/py.mk b/py/py.mk index 2e7ffe9905e0c..9592fbb9170ef 100644 --- a/py/py.mk +++ b/py/py.mk @@ -33,6 +33,9 @@ ifneq ($(USER_C_MODULES),) # pre-define USERMOD variables as expanded so that variables are immediate # expanded as they're added to them +# Confirm the provided path exists, show abspath if not to make it clearer to fix. +$(if $(wildcard $(USER_C_MODULES)/.),,$(error USER_C_MODULES doesn't exist: $(abspath $(USER_C_MODULES)))) + # C/C++ files that are included in the QSTR/module build SRC_USERMOD_C := SRC_USERMOD_CXX := From dccd206f4cbbf7c15e1a5721ec5ab830d92ffa8e Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 16 Oct 2024 13:12:19 +1100 Subject: [PATCH 053/546] py/usermod.cmake: Add check that any specified USER_C_MODULES exists. Signed-off-by: Andrew Leech --- py/usermod.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/py/usermod.cmake b/py/usermod.cmake index c814369a4865e..93fce13b3007b 100644 --- a/py/usermod.cmake +++ b/py/usermod.cmake @@ -42,6 +42,12 @@ endfunction() # Include CMake files for user modules. if (USER_C_MODULES) foreach(USER_C_MODULE_PATH ${USER_C_MODULES}) + # Confirm the provided path exists, show abspath if not to make it clearer to fix. + if (NOT EXISTS ${USER_C_MODULE_PATH}) + get_filename_component(USER_C_MODULES_ABS "${USER_C_MODULE_PATH}" ABSOLUTE) + message(FATAL_ERROR "USER_C_MODULES doesn't exist: ${USER_C_MODULES_ABS}") + endif() + message("Including User C Module(s) from ${USER_C_MODULE_PATH}") include(${USER_C_MODULE_PATH}) endforeach() From 78d017fc4e70bf21b7c53f193550d220f8959e38 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 16 Oct 2024 13:12:56 +1100 Subject: [PATCH 054/546] py/usermod.cmake: If USER_C_MODULES is a folder add micropython.cmake. This mirrors how it works when using a Makefile. Signed-off-by: Andrew Leech --- py/usermod.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/py/usermod.cmake b/py/usermod.cmake index 93fce13b3007b..4a8b99ff31b46 100644 --- a/py/usermod.cmake +++ b/py/usermod.cmake @@ -42,6 +42,10 @@ endfunction() # Include CMake files for user modules. if (USER_C_MODULES) foreach(USER_C_MODULE_PATH ${USER_C_MODULES}) + # If a directory is given, append the micropython.cmake to it. + if (IS_DIRECTORY ${USER_C_MODULE_PATH}) + set(USER_C_MODULE_PATH "${USER_C_MODULE_PATH}/micropython.cmake") + endif() # Confirm the provided path exists, show abspath if not to make it clearer to fix. if (NOT EXISTS ${USER_C_MODULE_PATH}) get_filename_component(USER_C_MODULES_ABS "${USER_C_MODULE_PATH}" ABSOLUTE) From 7647c828de4ffb9877163909017bbe909e79aacb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 13 Nov 2024 11:20:03 +1100 Subject: [PATCH 055/546] tests/multi_espnow: Add channel setting test, add some docs. Test currently passes. It was added so it can be used to check for regressions when fixing channel selection for AP mode in a follow-up commit. Also add some docs about how channel setting is observed to work for ESP-NOW. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/library/espnow.rst | 7 ++- docs/library/network.WLAN.rst | 2 +- tests/multi_espnow/70_channel.py | 89 ++++++++++++++++++++++++++++ tests/multi_espnow/70_channel.py.exp | 13 ++++ 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 tests/multi_espnow/70_channel.py create mode 100644 tests/multi_espnow/70_channel.py.exp diff --git a/docs/library/espnow.rst b/docs/library/espnow.rst index 1bcc9d7623d14..379e60486f224 100644 --- a/docs/library/espnow.rst +++ b/docs/library/espnow.rst @@ -441,7 +441,9 @@ must first register the sender and use the same encryption keys as the sender - *channel*: The wifi channel (2.4GHz) to communicate with this peer. Must be an integer from 0 to 14. If channel is set to 0 the current - channel of the wifi device will be used. (default=0) + channel of the wifi device will be used, if channel is set to another + value then this must match the channel currently configured on the + interface (see :func:`WLAN.config`). (default=0) - *ifidx*: (ESP32 only) Index of the wifi interface which will be used to send data to this peer. Must be an integer set to @@ -470,6 +472,9 @@ must first register the sender and use the same encryption keys as the sender registered. - ``OSError(num, "ESP_ERR_ESPNOW_FULL")`` if too many peers are already registered. + - ``OSError(num, "ESP_ERR_ESPNOW_CHAN")`` if a channel value was + set that doesn't match the channel currently configured for this + interface. - ``ValueError()`` on invalid keyword args or values. .. method:: ESPNow.del_peer(mac) diff --git a/docs/library/network.WLAN.rst b/docs/library/network.WLAN.rst index 3c401acb420bf..09fefc5929f07 100644 --- a/docs/library/network.WLAN.rst +++ b/docs/library/network.WLAN.rst @@ -126,7 +126,7 @@ Methods ============= =========== mac MAC address (bytes) ssid WiFi access point name (string) - channel WiFi channel (integer) + channel WiFi channel (integer). Depending on the port this may only be supported on the AP interface. hidden Whether SSID is hidden (boolean) security Security protocol supported (enumeration, see module constants) key Access key (string) diff --git a/tests/multi_espnow/70_channel.py b/tests/multi_espnow/70_channel.py new file mode 100644 index 0000000000000..f3e8b947b123c --- /dev/null +++ b/tests/multi_espnow/70_channel.py @@ -0,0 +1,89 @@ +# Test that ESP-NOW picks up the channel configuration for STA +# mode on ESP32. +# +# Note that setting the channel on a peer in ESP-NOW on modern ESP-IDF only +# checks it against the configured channel, it doesn't ever change the radio +# channel +import sys +import time + +try: + import network + import espnow +except ImportError: + print("SKIP") + raise SystemExit + +# ESP8266 doesn't support config('channel') on the STA interface, +# and the channel parameter to add_peer doesn't appear to set the +# channel either. +if sys.platform == "esp8266": + print("SKIP") + raise SystemExit + + +timeout_ms = 1000 +default_pmk = b"MicroPyth0nRules" + +CHANNEL = 3 +WRONG_CHANNEL = 8 + + +def init_sta(): + sta = network.WLAN(network.WLAN.IF_STA) + e = espnow.ESPNow() + e.active(True) + sta.active(True) + sta.disconnect() # Force AP disconnect for any saved config, important so the channel doesn't change + sta.config(channel=CHANNEL) + e.set_pmk(default_pmk) + return sta, e + + +# Receiver +def instance0(): + sta, e = init_sta() + multitest.globals(PEER=sta.config("mac")) + multitest.next() + print(sta.config("channel")) + while True: + peer, msg = e.recv(timeout_ms) + if peer is None: + print("Timeout") + break + print(msg) + e.active(False) + + +# Sender +def instance1(): + sta, e = init_sta() + multitest.next() + peer = PEER + + # both instances set channel via sta.config(), above + msg = b"sent to right channel 1" + e.add_peer(peer, channel=CHANNEL) + for _ in range(3): + e.send(peer, msg) + e.del_peer(peer) + print(sta.config("channel")) + + sta.config(channel=WRONG_CHANNEL) + msg = b"sent to wrong channel" + e.add_peer(peer, channel=WRONG_CHANNEL) + for _ in range(3): + e.send(peer, msg) + e.del_peer(peer) + print(sta.config("channel")) + + # switching back to the correct channel should also work + sta.config(channel=CHANNEL) + msg = b"sent to right channel 2" + e.add_peer(peer, channel=CHANNEL) + for _ in range(3): + e.send(peer, msg) + e.del_peer(peer) + print(sta.config("channel")) + + e.active(False) diff --git a/tests/multi_espnow/70_channel.py.exp b/tests/multi_espnow/70_channel.py.exp new file mode 100644 index 0000000000000..d8553966032ce --- /dev/null +++ b/tests/multi_espnow/70_channel.py.exp @@ -0,0 +1,13 @@ +--- instance0 --- +3 +b'sent to right channel 1' +b'sent to right channel 1' +b'sent to right channel 1' +b'sent to right channel 2' +b'sent to right channel 2' +b'sent to right channel 2' +Timeout +--- instance1 --- +3 +8 +3 From 951a10e7078413d2dee2758fc2b7cb292e2b50b7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 13 Nov 2024 10:16:25 +1100 Subject: [PATCH 056/546] esp32: Fix setting WLAN channel in AP mode. - Previously the call to esp_wifi_set_channel() would be immediately overridden by calling esp_wifi_config(...) with the previous channel set. - AP interface doesn't seem to need more than esp_wifi_config(...) to work. It will automatically configure 40MHz bandwidth and place the secondary channel using similar logic to what was being explicitly calculated here. - However, calling esp_wifi_set_channel() on the STA interface is necessary if using this interface with ESP-NOW (without connecting to an AP). So the esp_wifi_set_channel() call is kept in for this purpose. Without this, tests/multi_espnow/70_channel.py fails. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/network_wlan.c | 43 ++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index d415c56d32175..6509b4fc6aaed 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -549,21 +549,38 @@ static mp_obj_t network_wlan_config(size_t n_args, const mp_obj_t *args, mp_map_ break; } case MP_QSTR_channel: { - uint8_t primary; - wifi_second_chan_t secondary; - // Get the current value of secondary - esp_exceptions(esp_wifi_get_channel(&primary, &secondary)); - primary = mp_obj_get_int(kwargs->table[i].value); - esp_err_t err = esp_wifi_set_channel(primary, secondary); - if (err == ESP_ERR_INVALID_ARG) { - // May need to swap secondary channel above to below or below to above - secondary = ( - (secondary == WIFI_SECOND_CHAN_ABOVE) - ? WIFI_SECOND_CHAN_BELOW - : (secondary == WIFI_SECOND_CHAN_BELOW) + uint8_t channel = mp_obj_get_int(kwargs->table[i].value); + if (self->if_id == ESP_IF_WIFI_AP) { + cfg.ap.channel = channel; + } else { + // This setting is only used to determine the + // starting channel for a scan, so it can result in + // slightly faster connection times. + cfg.sta.channel = channel; + + // This additional code to directly set the channel + // on the STA interface is only relevant for ESP-NOW + // (when there is no STA connection attempt.) + uint8_t old_primary; + wifi_second_chan_t secondary; + // Get the current value of secondary + esp_exceptions(esp_wifi_get_channel(&old_primary, &secondary)); + esp_err_t err = esp_wifi_set_channel(channel, secondary); + if (err == ESP_ERR_INVALID_ARG) { + // May need to swap secondary channel above to below or below to above + secondary = ( + (secondary == WIFI_SECOND_CHAN_ABOVE) + ? WIFI_SECOND_CHAN_BELOW + : (secondary == WIFI_SECOND_CHAN_BELOW) ? WIFI_SECOND_CHAN_ABOVE : WIFI_SECOND_CHAN_NONE); - esp_exceptions(esp_wifi_set_channel(primary, secondary)); + err = esp_wifi_set_channel(channel, secondary); + } + esp_exceptions(err); + if (channel != old_primary) { + // Workaround the ESP-IDF Wi-Fi stack sometimes taking a moment to change channels + mp_hal_delay_ms(1); + } } break; } From 0e383a31b9560334b0c8d26b378deccc1c6af961 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 13 Nov 2024 17:40:02 +1100 Subject: [PATCH 057/546] tests: Add basic wlan test. Includes adding some ESP8266 port output to the ignored output list for the multitest runner. This test passes on ESP8266 and various ESP32s (including talking to each other). Without the fix in the parent commit, ESP32 AP will fail if the station can report its channel (i.e. channel is wrong). Testing with a CYW43 (RPI_PICO_W) currently fails but I have some fixes to submit so it can pass as well. Signed-off-by: Angus Gratton --- tests/multi_wlan/01_ap_sta.py | 117 ++++++++++++++++++++++++++++++ tests/multi_wlan/01_ap_sta.py.exp | 13 ++++ tests/run-multitests.py | 11 ++- 3 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 tests/multi_wlan/01_ap_sta.py create mode 100644 tests/multi_wlan/01_ap_sta.py.exp diff --git a/tests/multi_wlan/01_ap_sta.py b/tests/multi_wlan/01_ap_sta.py new file mode 100644 index 0000000000000..1a8e80cd33aa3 --- /dev/null +++ b/tests/multi_wlan/01_ap_sta.py @@ -0,0 +1,117 @@ +# Basic Wi-Fi MAC layer test where one device creates an Access Point and the +# other device connects to it as a Station. Also tests channel assignment (where +# possible) and disconnection. + +try: + import network + + network.WLAN +except (ImportError, NameError): + print("SKIP") + raise SystemExit + +import os +import sys +import time + +CHANNEL = 8 + +# Note that on slower Wi-Fi stacks this bumps up against the run-multitests.py +# timeout which expects <10s between lines of output. We work around this by +# logging something half way through the wait_for loop... +CONNECT_TIMEOUT = 15000 + + +def wait_for(test_func): + has_printed = False + start = time.ticks_ms() + while not test_func(): + time.sleep(0.1) + delta = time.ticks_diff(time.ticks_ms(), start) + if not has_printed and delta > CONNECT_TIMEOUT / 2: + print("...") + has_printed = True + elif delta > CONNECT_TIMEOUT: + break + + if not has_printed: + print("...") # keep the output consistent + + return test_func() + + +# AP +def instance0(): + ap = network.WLAN(network.WLAN.IF_AP) + ssid = "MP-test-" + os.urandom(6).hex() + psk = "Secret-" + os.urandom(6).hex() + + # stop any previous activity + network.WLAN(network.WLAN.IF_STA).active(False) + ap.active(False) + + ap.active(True) + ap.config(ssid=ssid, key=psk, channel=CHANNEL, security=network.WLAN.SEC_WPA_WPA2) + + # print("AP setup", ssid, psk) + print("AP started") + + multitest.globals(SSID=ssid, PSK=psk) + multitest.next() + + # Wait for station + if not wait_for(ap.isconnected): + raise RuntimeError("Timed out waiting for station, status ", ap.status()) + + print("AP got station") + time.sleep( + 3 + ) # depending on port, may still need to negotiate DHCP lease for STA to see connection + + print("AP disabling...") + ap.active(False) + + +# STA +def instance1(): + sta = network.WLAN(network.WLAN.IF_STA) + + # stop any previous activity + network.WLAN(network.WLAN.IF_AP).active(False) + sta.active(False) + + multitest.next() + ssid = SSID + psk = PSK + + # print("STA setup", ssid, psk) + + sta.active(True) + sta.connect(ssid, psk) + + print("STA connecting...") + + if not wait_for(sta.isconnected): + raise RuntimeError("Timed out waiting to connect, status ", sta.status()) + + print("STA connected") + + # Print the current channel, if the port support this + try: + print("channel", sta.config("channel")) + except OSError as e: + if "AP" in str(e): + # ESP8266 only supports reading channel on the AP interface, so fake this result + print("channel", CHANNEL) + else: + raise + + print("STA waiting for disconnect...") + + # Expect the AP to disconnect us immediately + if not wait_for(lambda: not sta.isconnected()): + raise RuntimeError("Timed out waiting for AP to disconnect us, status ", sta.status()) + + print("STA disconnected") + + sta.active(False) diff --git a/tests/multi_wlan/01_ap_sta.py.exp b/tests/multi_wlan/01_ap_sta.py.exp new file mode 100644 index 0000000000000..8fc023a39805b --- /dev/null +++ b/tests/multi_wlan/01_ap_sta.py.exp @@ -0,0 +1,13 @@ +--- instance0 --- +AP started +... +AP got station +AP disabling... +--- instance1 --- +STA connecting... +... +STA connected +channel 8 +STA waiting for disconnect... +... +STA disconnected diff --git a/tests/run-multitests.py b/tests/run-multitests.py index 93a6d3844d235..387eec7018bb3 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -105,15 +105,14 @@ def output_metric(data): multitest.flush() """ -# The btstack implementation on Unix generates some spurious output that we -# can't control. Also other platforms may output certain warnings/errors that -# can be safely ignored. +# Some ports generate output we can't control, and that can be safely ignored. IGNORE_OUTPUT_MATCHES = ( - "libusb: error ", # It tries to open devices that it doesn't have access to (libusb prints unconditionally). + "libusb: error ", # unix btstack tries to open devices that it doesn't have access to (libusb prints unconditionally). "hci_transport_h2_libusb.c", # Same issue. We enable LOG_ERROR in btstack. - "USB Path: ", # Hardcoded in btstack's libusb transport. - "hci_number_completed_packet", # Warning from btstack. + "USB Path: ", # Hardcoded in unix btstack's libusb transport. + "hci_number_completed_packet", # Warning from unix btstack. "lld_pdu_get_tx_flush_nb HCI packet count mismatch (", # From ESP-IDF, see https://github.com/espressif/esp-idf/issues/5105 + " ets_task(", # ESP8266 port debug output ) From ed3c75a3af16405508f7bfa66257abd7b53b4a9d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 6 Nov 2024 16:48:56 +1100 Subject: [PATCH 058/546] esp32: Use hardware version for touchpad macro defines. ESP32 has hardware V1 and S2/S3 has V2, and future chips may have different versions. This should still compile to the same binary before and after. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/machine_touchpad.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/ports/esp32/machine_touchpad.c b/ports/esp32/machine_touchpad.c index 7612063e51d4a..a35e7fccda215 100644 --- a/ports/esp32/machine_touchpad.c +++ b/ports/esp32/machine_touchpad.c @@ -29,12 +29,14 @@ #include "modmachine.h" #include "driver/gpio.h" -#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#if SOC_TOUCH_SENSOR_SUPPORTED -#if CONFIG_IDF_TARGET_ESP32 +#if SOC_TOUCH_VERSION_1 // ESP32 only #include "driver/touch_pad.h" -#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#elif SOC_TOUCH_VERSION_2 // All other SoCs with touch, to date #include "driver/touch_sensor.h" +#else +#error "Unknown touch hardware version" #endif typedef struct _mtp_obj_t { @@ -70,6 +72,8 @@ static const mtp_obj_t touchpad_obj[] = { {{&machine_touchpad_type}, GPIO_NUM_12, TOUCH_PAD_NUM12}, {{&machine_touchpad_type}, GPIO_NUM_13, TOUCH_PAD_NUM13}, {{&machine_touchpad_type}, GPIO_NUM_14, TOUCH_PAD_NUM14}, + #else + #error "Please add GPIO mapping for this SoC" #endif }; @@ -92,14 +96,14 @@ static mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ if (!initialized) { touch_pad_init(); touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); - #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #if TOUCH_HW_VER == 2 touch_pad_fsm_start(); #endif initialized = 1; } - #if CONFIG_IDF_TARGET_ESP32 + #if SOC_TOUCH_VERSION_1 esp_err_t err = touch_pad_config(self->touchpad_id, 0); - #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #elif SOC_TOUCH_VERSION_2 esp_err_t err = touch_pad_config(self->touchpad_id); #endif if (err == ESP_OK) { @@ -110,10 +114,10 @@ static mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ static mp_obj_t mtp_config(mp_obj_t self_in, mp_obj_t value_in) { mtp_obj_t *self = self_in; - #if CONFIG_IDF_TARGET_ESP32 + #if SOC_TOUCH_VERSION_1 uint16_t value = mp_obj_get_int(value_in); esp_err_t err = touch_pad_config(self->touchpad_id, value); - #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #elif SOC_TOUCH_VERSION_2 esp_err_t err = touch_pad_config(self->touchpad_id); #endif if (err == ESP_OK) { @@ -125,10 +129,10 @@ MP_DEFINE_CONST_FUN_OBJ_2(mtp_config_obj, mtp_config); static mp_obj_t mtp_read(mp_obj_t self_in) { mtp_obj_t *self = self_in; - #if CONFIG_IDF_TARGET_ESP32 + #if SOC_TOUCH_VERSION_1 uint16_t value; esp_err_t err = touch_pad_read(self->touchpad_id, &value); - #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #elif SOC_TOUCH_VERSION_2 uint32_t value; esp_err_t err = touch_pad_read_raw_data(self->touchpad_id, &value); #endif @@ -155,4 +159,4 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &mtp_locals_dict ); -#endif +#endif // SOC_TOUCH_SENSOR_SUPPORTED From 66e699e8a538ed7c1dc2d65035e5d93643e56667 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 6 Nov 2024 17:14:09 +1100 Subject: [PATCH 059/546] esp32: Fix machine.TouchPad startup on ESP32-S2 and S3. Closes #13178. TouchPad confirmed working on both chips, and fixes the the ESP32-S3 reading constant max value. Was unable to reproduce the bug on ESP32-S2 but this may be due to my test setup, and it still works with the fix. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/machine_touchpad.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/esp32/machine_touchpad.c b/ports/esp32/machine_touchpad.c index a35e7fccda215..48250280badaf 100644 --- a/ports/esp32/machine_touchpad.c +++ b/ports/esp32/machine_touchpad.c @@ -96,9 +96,6 @@ static mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ if (!initialized) { touch_pad_init(); touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); - #if TOUCH_HW_VER == 2 - touch_pad_fsm_start(); - #endif initialized = 1; } #if SOC_TOUCH_VERSION_1 @@ -107,6 +104,10 @@ static mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ esp_err_t err = touch_pad_config(self->touchpad_id); #endif if (err == ESP_OK) { + #if SOC_TOUCH_VERSION_2 + touch_pad_fsm_start(); + #endif + return MP_OBJ_FROM_PTR(self); } mp_raise_ValueError(MP_ERROR_TEXT("Touch pad error")); From 154d1419659795aa6785778e4ddd6f23f09f2861 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 6 Nov 2024 18:06:48 +1100 Subject: [PATCH 060/546] docs,esp32: Update machine.TouchPad docs for ESP32-S2 and ESP32-S3. Signed-off-by: Angus Gratton --- docs/esp32/quickref.rst | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index b9ca0f8225f8f..5cce96d687875 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -751,20 +751,33 @@ APA102 (DotStar) uses a different driver as it has an additional clock pin. Capacitive touch ---------------- -Use the ``TouchPad`` class in the ``machine`` module:: +ESP32, ESP32-S2 and ESP32-S3 support capacitive touch via the ``TouchPad`` class +in the ``machine`` module:: from machine import TouchPad, Pin t = TouchPad(Pin(14)) t.read() # Returns a smaller number when touched -``TouchPad.read`` returns a value relative to the capacitive variation. Small numbers (typically in -the *tens*) are common when a pin is touched, larger numbers (above *one thousand*) when -no touch is present. However the values are *relative* and can vary depending on the board -and surrounding composition so some calibration may be required. - -There are ten capacitive touch-enabled pins that can be used on the ESP32: 0, 2, 4, 12, 13 -14, 15, 27, 32, 33. Trying to assign to any other pins will result in a ``ValueError``. +``TouchPad.read`` returns a value proportional to the capacitance between the +pin and the board's Ground connection. On ESP32 the number becomes smaller when +the pin (or connected touch pad) is touched, on ESP32-S2 and ESP32-S3 the number +becomes larger when the pin is touched. + +In all cases, a touch causes a significant change in the return value. Note the +returned values are *relative* and can vary depending on the board and +surrounding environment so some calibration (i.e. comparison to a baseline or +rolling average) may be required. + +========= ============================================== +Chip Touch-enabled pins +--------- ---------------------------------------------- +ESP32 0, 2, 4, 12, 13, 14, 15, 27, 32, 33 +ESP32-S2 1 to 14 inclusive +ESP32-S3 1 to 14 inclusive +========= ============================================== + +Trying to assign to any other pins will result in a ``ValueError``. Note that TouchPads can be used to wake an ESP32 from sleep:: From e70048cf5934bf3db65ec7a1b6490635716282f5 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 14 Nov 2024 10:15:10 +0000 Subject: [PATCH 061/546] extmod/modframebuf: Fix 0 radius bug in FrameBuffer.ellipse. This fixes a bug in FrameBuffer.ellipse where it goes into an infinite loop if both radii are 0. This fixes the bug with a simple pre-check to see if both radii are 0, and in that case sets a single pixel at the center. This is consistent with the behaviour of the method when called with just one of the radii set to 0, where it will draw a horizontal or vertical line of 1 pixel width. The pixel is set with setpixel_checked so it should handle out-of-bounds drawing correctly. This fix also includes three new tests: one for the default behaviour, one for drawing out-of-bounds, and one for when the sector mask is 0. Fixes issue #16053. Signed-off-by: Corran Webster --- extmod/modframebuf.c | 4 ++ tests/extmod/framebuf_ellipse.py | 15 +++++ tests/extmod/framebuf_ellipse.py.exp | 96 ++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index cd0f50d104427..b718a66cc62fe 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -536,6 +536,10 @@ static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) { } else { mask |= ELLIPSE_MASK_ALL; } + if (args[2] == 0 && args[3] == 0) { + setpixel_checked(self, args[0], args[1], args[4], mask & ELLIPSE_MASK_ALL); + return mp_const_none; + } mp_int_t two_asquare = 2 * args[2] * args[2]; mp_int_t two_bsquare = 2 * args[3] * args[3]; mp_int_t x = args[2]; diff --git a/tests/extmod/framebuf_ellipse.py b/tests/extmod/framebuf_ellipse.py index a4c784aff8762..ec0461e66ca03 100644 --- a/tests/extmod/framebuf_ellipse.py +++ b/tests/extmod/framebuf_ellipse.py @@ -63,3 +63,18 @@ def printbuf(): fbuf.fill(0) fbuf.ellipse(x, y, 6, 12, 0xAA, True) printbuf() + +# Draw an ellipse with both radius 0 +fbuf.fill(0) +fbuf.ellipse(15, 15, 0, 0, 0xFF, True) +printbuf() + +# Draw an ellipse with both radius 0 out of bounds +fbuf.fill(0) +fbuf.ellipse(45, 45, 0, 0, 0xFF, True) +printbuf() + +# Draw an ellipse with radius 0 and all sectors masked out +fbuf.fill(0) +fbuf.ellipse(15, 15, 0, 0, 0xFF, True, 0) +printbuf() diff --git a/tests/extmod/framebuf_ellipse.py.exp b/tests/extmod/framebuf_ellipse.py.exp index ae6ad1ee7e487..96a60c24da1f1 100644 --- a/tests/extmod/framebuf_ellipse.py.exp +++ b/tests/extmod/framebuf_ellipse.py.exp @@ -702,3 +702,99 @@ aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -->8-- +--8<-- +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000ff0000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +-->8-- +--8<-- +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +-->8-- +--8<-- +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +-->8-- From da692d01ac6c09286ee682c0d4a3780777c67a8f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 27 Nov 2024 16:57:02 +0100 Subject: [PATCH 062/546] nrf/drivers/ticker: Reset slow ticker callback count on soft reboot. The micro:bit board (and probably other boards using the music or display module) locked up on soft reboot. Reason was a buffer overflow caused by an index counter, which was not reset on soft_reboot. That's fixed in this commit. Tested with a micro:bit board, performing a series of soft reboots. Signed-off-by: robert-hh --- ports/nrf/drivers/ticker.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/nrf/drivers/ticker.c b/ports/nrf/drivers/ticker.c index c2fa31e7c207b..e9c07c842ecd5 100644 --- a/ports/nrf/drivers/ticker.c +++ b/ports/nrf/drivers/ticker.c @@ -62,6 +62,7 @@ void ticker_init0(void) { #else NRFX_IRQ_PRIORITY_SET(FastTicker_IRQn, 2); #endif + m_num_of_slow_tickers = 0; NRFX_IRQ_PRIORITY_SET(SlowTicker_IRQn, 3); From 3b3b48892f96e2c81e7887f996fd7ff457acbb5d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 20 Nov 2024 16:15:20 +1100 Subject: [PATCH 063/546] py/objfloat: Workaround non-constant NAN definition on Windows MSVC. Recent MSVC versions have changed the definition of NAN to a non-constant expression! This is a bug, C standard says it should be a constant. Good explanation and workaround at: https://stackoverflow.com/a/79199887 This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- py/objfloat.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/py/objfloat.c b/py/objfloat.c index 5c90b1491cebf..0728fce3151fe 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -47,6 +47,13 @@ #define M_PI (3.14159265358979323846) #endif +// Workaround a bug in recent MSVC where NAN is no longer constant. +// (By redefining back to the previous MSVC definition of NAN) +#if defined(_MSC_VER) && _MSC_VER >= 1942 +#undef NAN +#define NAN (-(float)(((float)(1e+300 * 1e+300)) * 0.0F)) +#endif + typedef struct _mp_obj_float_t { mp_obj_base_t base; mp_float_t value; From 8e11e5f1a14ca3395afaab2e42edebe41221eba1 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 20 Nov 2024 16:46:21 +1100 Subject: [PATCH 064/546] tests/misc/sys_settrace_features.py: Add note about CPython 3.12 issue. CPython 3.12 has a documented issue with settrace for opcodes, apparently due to PEP 669. "This behavior will be changed back in 3.13 to be consistent with previous versions." No easy way to make the test pass on CPython 3.12, but at least this helps signal what the problem is to anyone who runs into a failure. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/misc/sys_settrace_features.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/misc/sys_settrace_features.py b/tests/misc/sys_settrace_features.py index 8ca6b382e3c64..6eeb2b900f18c 100644 --- a/tests/misc/sys_settrace_features.py +++ b/tests/misc/sys_settrace_features.py @@ -6,6 +6,10 @@ print("SKIP") raise SystemExit +if sys.version.startswith("3.12"): + # There is a CPython change in settrace that is reverted in 3.13! + print("WARNING: this test will fail when compared to CPython 3.12.x behaviour") + def print_stacktrace(frame, level=0): # Ignore CPython specific helpers. From 8ec067272a2b03c182f4723026e1d94dd6a44835 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 20 Nov 2024 16:28:33 +1100 Subject: [PATCH 065/546] github/workflows: Workaround using CPython 3.12 in MSYS2 builds. Once MSYS2 repository updates past Python 3.12, this commit can be reverted. Explanation: CPython 3.12 can't pass sys_settrace_features test (see parent commit for explanation). MSYS2 mingw-w64-ARCH-python package is currently 3.12.7. MSYS2 doesn't recommend installing old packages from their archive (due to library dependencies), so switch to the GitHub CI setup-python action for now. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- .github/workflows/ports_windows.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ports_windows.yml b/.github/workflows/ports_windows.yml index 91a3192a10a48..84e018ba15d16 100644 --- a/.github/workflows/ports_windows.yml +++ b/.github/workflows/ports_windows.yml @@ -110,6 +110,11 @@ jobs: run: shell: msys2 {0} steps: + - uses: actions/setup-python@v5 + # note: can go back to installing mingw-w64-${{ matrix.env }}-python after + # MSYS2 updates to Python >3.12 (due to settrace compatibility issue) + with: + python-version: '3.11' - uses: msys2/setup-msys2@v2 with: msystem: ${{ matrix.sys }} @@ -118,9 +123,9 @@ jobs: make mingw-w64-${{ matrix.env }}-gcc pkg-config - mingw-w64-${{ matrix.env }}-python3 git diffutils + path-type: inherit # Remove when setup-python is removed - uses: actions/checkout@v4 - name: Build mpy-cross.exe run: make -C mpy-cross -j2 From eb80b04944e01d437efc2fa29189ad1e2bdd997f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 20 Nov 2024 16:22:56 +1100 Subject: [PATCH 066/546] tests/extmod: Workaround CPython warning in asyncio_new_event_loop test. This started failing in CI on the mingw build, after CPython updated to 3.12.7. The test prints two warnings during interpreter shutdown of "Task was destroyed but it is pending!". This didn't happen on other CPython builds, and I think that's because of finalizer order in CPython interpreter shutdown but not certain (the loop finalizer calls loop.close() if not already closed). Adding explicit calls to loop.close() causes the warning to be printed on every run with CPython 3.12.7 on Linux. Next, added the workaround exception handler to swallow this exception as MicroPython doesn't produce an equivalent. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/extmod/asyncio_new_event_loop.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/extmod/asyncio_new_event_loop.py b/tests/extmod/asyncio_new_event_loop.py index bebc3bf70cc57..3f05ffdd551d7 100644 --- a/tests/extmod/asyncio_new_event_loop.py +++ b/tests/extmod/asyncio_new_event_loop.py @@ -12,6 +12,16 @@ asyncio.set_event_loop(asyncio.new_event_loop()) +def exception_handler(loop, context): + # This is a workaround for a difference between CPython and MicroPython: if + # a CPython event loop is closed while there are tasks pending (i.e. not finished) + # on it, then the task will log an error. MicroPython does not log this error. + if context.get("message", "") == "Task was destroyed but it is pending!": + pass + else: + loop.default_exception_handler(context) + + async def task(): for i in range(4): print("task", i) @@ -22,17 +32,21 @@ async def task(): async def main(): print("start") loop.create_task(task()) - await asyncio.sleep(0) + await asyncio.sleep(0) # yields, meaning new task will run once print("stop") loop.stop() # Use default event loop to run some tasks loop = asyncio.get_event_loop() +loop.set_exception_handler(exception_handler) loop.create_task(main()) loop.run_forever() +loop.close() # Create new event loop, old one should not keep running loop = asyncio.new_event_loop() +loop.set_exception_handler(exception_handler) loop.create_task(main()) loop.run_forever() +loop.close() From d49c1e836cc7497f4bd5385491214173af6410ed Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 26 Nov 2024 15:50:13 +1100 Subject: [PATCH 067/546] tools/ci.sh: Remove explicit macOS pkg-config install. Reasons to remove this: - GitHub's macOS runners install this package by default nowadays. - Brew renamed this package to 'pkgconf' so installing the old name on top of the new package name has started failing. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tools/ci.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/ci.sh b/tools/ci.sh index 4e6f09b0bb853..b9f9dec8bc26e 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -651,9 +651,6 @@ function ci_unix_settrace_stackless_run_tests { } function ci_unix_macos_build { - # Install pkg-config to configure libffi paths. - brew install pkg-config - make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix submodules #make ${MAKEOPTS} -C ports/unix deplibs From 6dd976cc5c42ebed8fec50e4e6aec1fb37347080 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 19:34:09 +0000 Subject: [PATCH 068/546] github/workflows: Bump codecov/codecov-action from 4 to 5. Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ports_unix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index 1707fdc9fb37d..64fd5c902a053 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -88,7 +88,7 @@ jobs: (cd ports/unix && gcov -o build-coverage/py ../../py/*.c || true) (cd ports/unix && gcov -o build-coverage/extmod ../../extmod/*.c || true) - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: fail_ci_if_error: true verbose: true From beabef5aac2c578f6b2f87d42bfccfa4ff074416 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 20 Nov 2024 14:32:25 +1100 Subject: [PATCH 069/546] esp32: Add missing network.STAT_CONNECT_FAIL constant. The esp32 port had network.STAT_ASSOC_FAIL for the same purpose, but this is undocumented and different to all other ports. That constant is now deprecated. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/modnetwork_globals.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/esp32/modnetwork_globals.h b/ports/esp32/modnetwork_globals.h index c6092bd142178..2bfc2393d666f 100644 --- a/ports/esp32/modnetwork_globals.h +++ b/ports/esp32/modnetwork_globals.h @@ -82,5 +82,9 @@ #endif { MP_ROM_QSTR(MP_QSTR_STAT_WRONG_PASSWORD), MP_ROM_INT(WIFI_REASON_AUTH_FAIL)}, { MP_ROM_QSTR(MP_QSTR_STAT_BEACON_TIMEOUT), MP_ROM_INT(WIFI_REASON_BEACON_TIMEOUT)}, +#if !MICROPY_PREVIEW_VERSION_2 +// Deprecated, use STAT_CONNECT_FAIL same as other ports { MP_ROM_QSTR(MP_QSTR_STAT_ASSOC_FAIL), MP_ROM_INT(WIFI_REASON_ASSOC_FAIL)}, +#endif +{ MP_ROM_QSTR(MP_QSTR_STAT_CONNECT_FAIL), MP_ROM_INT(WIFI_REASON_ASSOC_FAIL)}, { MP_ROM_QSTR(MP_QSTR_STAT_HANDSHAKE_TIMEOUT), MP_ROM_INT(WIFI_REASON_HANDSHAKE_TIMEOUT)}, From 579e840de6d71503658f3c427c1e210c7cb85e9e Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 20 Nov 2024 19:07:17 +0100 Subject: [PATCH 070/546] extmod/modplatform: Distinguish AArch64 from AArch32. This commit adds a new platform architecture name for Arm CPUs running in 64 bits mode ("aarch64"). The 32 bits name is left as "arm" to maintain compatibility with existing code. Signed-off-by: Alessandro Gatti --- extmod/modplatform.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extmod/modplatform.h b/extmod/modplatform.h index b932551c7ccb2..0c83d7b8c5d11 100644 --- a/extmod/modplatform.h +++ b/extmod/modplatform.h @@ -36,7 +36,11 @@ // See: https://sourceforge.net/p/predef/wiki/Home/ #if defined(__ARM_ARCH) +#if defined(__ARM_ARCH_ISA_A64) +#define MICROPY_PLATFORM_ARCH "aarch64" +#else #define MICROPY_PLATFORM_ARCH "arm" +#endif #elif defined(__x86_64__) || defined(_M_X64) #define MICROPY_PLATFORM_ARCH "x86_64" #elif defined(__i386__) || defined(_M_IX86) From 09cf01d7c1f564fd018bd8fb3e6382fbe8cd1e35 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 20 Nov 2024 19:11:30 +0100 Subject: [PATCH 071/546] extmod/modplatform: Add Clang to the known compilers list. This commit adds support to distinguish between GCC and Clang to report the appropriate compiler version. Usually Clang also disguises itself as GCC for compatibility reasons, but these changes look for Clang-specific definitions first to avoid that problem. Signed-off-by: Alessandro Gatti --- extmod/modplatform.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extmod/modplatform.h b/extmod/modplatform.h index 0c83d7b8c5d11..19246c0fda1cd 100644 --- a/extmod/modplatform.h +++ b/extmod/modplatform.h @@ -53,7 +53,13 @@ #define MICROPY_PLATFORM_ARCH "" #endif -#if defined(__GNUC__) +#if defined(__clang__) +#define MICROPY_PLATFORM_COMPILER \ + "Clang " \ + MP_STRINGIFY(__clang_major__) "." \ + MP_STRINGIFY(__clang_minor__) "." \ + MP_STRINGIFY(__clang_patchlevel__) +#elif defined(__GNUC__) #define MICROPY_PLATFORM_COMPILER \ "GCC " \ MP_STRINGIFY(__GNUC__) "." \ From 3de3821abf7e245566be227c9799b74d6b80aae3 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 20 Nov 2024 19:13:48 +0100 Subject: [PATCH 072/546] extmod/modplatform: Add Android to the recognised platforms list. This commit adds code to distinguish between regular Linux and Android, also adding a specific entry for the platform libc. The reported libc is marked as "bionic" and its version matches the Android platform API version (there are no definitions for a specific bionic version). Signed-off-by: Alessandro Gatti --- extmod/modplatform.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extmod/modplatform.h b/extmod/modplatform.h index 19246c0fda1cd..5c9cbffb2a486 100644 --- a/extmod/modplatform.h +++ b/extmod/modplatform.h @@ -96,12 +96,17 @@ #elif defined(_PICOLIBC__) #define MICROPY_PLATFORM_LIBC_LIB "picolibc" #define MICROPY_PLATFORM_LIBC_VER _PICOLIBC_VERSION +#elif defined(__ANDROID__) +#define MICROPY_PLATFORM_LIBC_LIB "bionic" +#define MICROPY_PLATFORM_LIBC_VER MP_STRINGIFY(__ANDROID_API__) #else #define MICROPY_PLATFORM_LIBC_LIB "" #define MICROPY_PLATFORM_LIBC_VER "" #endif -#if defined(__linux) +#if defined(__ANDROID__) +#define MICROPY_PLATFORM_SYSTEM "Android" +#elif defined(__linux) #define MICROPY_PLATFORM_SYSTEM "Linux" #elif defined(__unix__) #define MICROPY_PLATFORM_SYSTEM "Unix" From 2e796d6c3e33e2d3b1936cd05aa926400ad1d5b7 Mon Sep 17 00:00:00 2001 From: chuangjinglu Date: Mon, 25 Nov 2024 11:06:15 +0800 Subject: [PATCH 073/546] docs,ports: Fix some comments and error messages with doubled-up words. Signed-off-by: chuangjinglu --- docs/library/bluetooth.rst | 2 +- docs/library/machine.TimerWiPy.rst | 2 +- docs/library/pyb.Timer.rst | 2 +- docs/reference/manifest.rst | 2 +- ports/cc3200/FreeRTOS/Source/tasks.c | 2 +- ports/stm32/timer.c | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/library/bluetooth.rst b/docs/library/bluetooth.rst index f8b154dd11448..b09c370abd46d 100644 --- a/docs/library/bluetooth.rst +++ b/docs/library/bluetooth.rst @@ -665,7 +665,7 @@ L2CAP connection-oriented-channels Connect to a listening peer on the specified *psm* with local MTU set to *mtu*. - On successful connection, the the ``_IRQ_L2CAP_CONNECT`` event will be + On successful connection, the ``_IRQ_L2CAP_CONNECT`` event will be raised, allowing the client to obtain the CID and the local and remote (peer) MTU. An unsuccessful connection will raise the ``_IRQ_L2CAP_DISCONNECT`` event diff --git a/docs/library/machine.TimerWiPy.rst b/docs/library/machine.TimerWiPy.rst index f8c8bb29da7fb..54280a5994ad5 100644 --- a/docs/library/machine.TimerWiPy.rst +++ b/docs/library/machine.TimerWiPy.rst @@ -71,7 +71,7 @@ Methods Otherwise, a TimerChannel object is initialized and returned. - The operating mode is is the one configured to the Timer object that was used to + The operating mode is the one configured to the Timer object that was used to create the channel. - ``channel`` if the width of the timer is 16-bit, then must be either ``TIMER.A``, ``TIMER.B``. diff --git a/docs/library/pyb.Timer.rst b/docs/library/pyb.Timer.rst index 1749efce2d508..c644e38d78624 100644 --- a/docs/library/pyb.Timer.rst +++ b/docs/library/pyb.Timer.rst @@ -163,7 +163,7 @@ Methods - ``callback`` - as per TimerChannel.callback() - ``pin`` None (the default) or a Pin object. If specified (and not None) - this will cause the alternate function of the the indicated pin + this will cause the alternate function of the indicated pin to be configured for this timer channel. An error will be raised if the pin doesn't support any alternate functions for this timer channel. diff --git a/docs/reference/manifest.rst b/docs/reference/manifest.rst index 1a80b1259e39f..dc9d6dd75b2b8 100644 --- a/docs/reference/manifest.rst +++ b/docs/reference/manifest.rst @@ -26,7 +26,7 @@ re-flashing the entire firmware. However, it can still be useful to selectively freeze some rarely-changing dependencies (such as third-party libraries). -The way to list the Python files to be be frozen into the firmware is via +The way to list the Python files to be frozen into the firmware is via a "manifest", which is a Python file that will be interpreted by the build process. Typically you would write a manifest file as part of a board definition, but you can also write a stand-alone manifest file and use it with diff --git a/ports/cc3200/FreeRTOS/Source/tasks.c b/ports/cc3200/FreeRTOS/Source/tasks.c index 6c261a65101dc..3d35c667274ca 100644 --- a/ports/cc3200/FreeRTOS/Source/tasks.c +++ b/ports/cc3200/FreeRTOS/Source/tasks.c @@ -1671,7 +1671,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE ) { /* Is it in the suspended list because it is in the Suspended - state, or because is is blocked with no timeout? */ + state, or because it is blocked with no timeout? */ if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) { xReturn = pdTRUE; diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c index d999e57462edb..acfdbd84eb2b6 100644 --- a/ports/stm32/timer.c +++ b/ports/stm32/timer.c @@ -1089,7 +1089,7 @@ static MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit); /// - `callback` - as per TimerChannel.callback() /// /// - `pin` None (the default) or a Pin object. If specified (and not None) -/// this will cause the alternate function of the the indicated pin +/// this will cause the alternate function of the indicated pin /// to be configured for this timer channel. An error will be raised if /// the pin doesn't support any alternate functions for this timer channel. /// @@ -1197,7 +1197,7 @@ static mp_obj_t pyb_timer_channel(size_t n_args, const mp_obj_t *pos_args, mp_ma mp_obj_t pin_obj = args[2].u_obj; if (pin_obj != mp_const_none) { if (!mp_obj_is_type(pin_obj, &pin_type)) { - mp_raise_ValueError(MP_ERROR_TEXT("pin argument needs to be be a Pin type")); + mp_raise_ValueError(MP_ERROR_TEXT("pin argument needs to be a Pin type")); } const machine_pin_obj_t *pin = MP_OBJ_TO_PTR(pin_obj); const pin_af_obj_t *af = pin_find_af(pin, AF_FN_TIM, self->tim_id); From 406bccc75307bd09fbf20150a29c587e9f12771b Mon Sep 17 00:00:00 2001 From: Amirreza Hamzavi Date: Thu, 23 Nov 2023 21:43:47 +0330 Subject: [PATCH 074/546] docs/library/binascii: Add docs for binascii.crc32 method. Signed-off-by: Amirreza Hamzavi --- docs/library/binascii.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/library/binascii.rst b/docs/library/binascii.rst index 6c02f019aaa58..c728c5a8a574c 100644 --- a/docs/library/binascii.rst +++ b/docs/library/binascii.rst @@ -36,3 +36,9 @@ Functions Encode binary data in base64 format, as in `RFC 3548 `_. Returns the encoded data followed by a newline character if newline is true, as a bytes object. + +.. function:: crc32(data, [value]) + + Compute CRC-32, the 32-bit checksum of *data*, starting with an initial CRC + of *value*. The default initial CRC is zero. The algorithm is consistent + with the ZIP file checksum. From a2554f09573e0a856d9abf9e718bac9977a510db Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Nov 2024 16:26:15 +1100 Subject: [PATCH 075/546] tests/run-tests.py: Add support for tests to use unittest. All the existing tests require a .exp file (either manually specified or generated running the test first under CPython) that is used to check the output of running the test under MicroPython. The test passes if the output matches the expected output exactly. This has worked very well for a long time now. But some of the newer hardware tests (eg UART, SPI, PWM) don't really fit this model, for the following main reasons: - Some but not all parts of the test should be skipped on certain hardware targets. With the expected-output approach, skipping tests is either all or nothing. - It's often useful to output diagnostics as part of the test, which should not affect the result of the test (eg the diagnostics change from run to run, like timing values, or from target to target). - Sometimes a test will do a complex check and then print False/True if it passed or not, which obscures the actual test result. To improve upon this, this commit adds support to `run-tests.py` for a test to use `unittest`. It detects this by looking at the end of the output after running the test, looking for the test summary printed by `unittest` (or an error message saying `unittest` was not found). If the test uses `unittest` then it should not have a .exp file, and it's not run under CPython. A `unittest` based test passes or fails based on the summary printed by `unittest`. Note that (as long as `unittest` is installed on the target) the tests are still fully independent and you can still run them without `run-tests.py`: you just run it as usual, eg `mpremote run `. This is very useful when creating and debugging tests. Note also that the standard test suite testing Python semantics (eg everything in `tests/basics/`) will probably never use unittest. Only more advanced tests will, and ones that are not runnable under CPython. Signed-off-by: Damien George --- tests/README.md | 35 ++++++++--- tests/run-tests.py | 141 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 132 insertions(+), 44 deletions(-) diff --git a/tests/README.md b/tests/README.md index b39833aa096f4..28c1b3a0852cc 100644 --- a/tests/README.md +++ b/tests/README.md @@ -15,6 +15,32 @@ That will run tests on the `/dev/ttyACM0` serial port. You can also use shortcu device names like `a` for `/dev/ttyACM` and `c` for `COM`. Use `./run-tests.py --help` to see all of the device possibilites, and other options. +There are three kinds of tests: + +* Tests that use `unittest`: these tests require `unittest` to be installed on the + target (eg via `mpremote mip install unittest`), and are used to test things that are + MicroPython-specific, such as behaviour that is different to CPython, modules that + aren't available in CPython, and hardware tests. These tests are run only under + MicroPython and the test passes if the `unittest` runner prints "OK" at the end of the + run. Other output may be printed, eg for use as diagnostics, and this output does not + affect the result of the test. + +* Tests with a corresponding `.exp` file: similar to the `unittest` tests, these tests + are for features that generally cannot be run under CPython. In this case the test is + run under MicroPython only and the output from MicroPython is compared against the + provided `.exp` file. The test passes if the output matches exactly. + +* Tests without a corresponding `.exp` file (and don't use `unittest`): these tests are + used to test MicroPython behaviour that should precisely match CPython. These tests + are first run under CPython and the output captured, and then run under MicroPython + and the output compared to the CPython output. The test passes if the output matches + exactly. If the output differs then the test fails and the outputs are saved in a + `.exp` and a `.out` file respectively. + +In all three cases above, the test can usually be run directly on the target MicroPython +instance, either using the unix port with `micropython `, or on a board with +`mpremote run `. This is useful for creating and debugging tests. + Tests of capabilities not supported on all platforms should be written to check for the capability being present. If it is not, the test should merely output 'SKIP' followed by the line terminator, and call @@ -27,15 +53,6 @@ condition a test. The run-tests.py script uses small scripts in the feature_check directory to check whether each such feature is present, and skips the relevant tests if not. -Tests are generally verified by running the test both in MicroPython and -in CPython and comparing the outputs. If the output differs the test fails -and the outputs are saved in a .out and a .exp file respectively. -For tests that cannot be run in CPython, for example because they use -the machine module, a .exp file can be provided next to the test's .py -file. A convenient way to generate that is to run the test, let it fail -(because CPython cannot run it) and then copy the .out file (but not -before checking it manually!) - When creating new tests, anything that relies on float support should go in the float/ subdirectory. Anything that relies on import x, where x is not a built-in module, should go in the import/ subdirectory. diff --git a/tests/run-tests.py b/tests/run-tests.py index d0feb4bcd6635..0e491ccd62bf2 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -508,6 +508,10 @@ def run_feature_check(pyb, args, test_file): return run_micropython(pyb, args, test_file_path, test_file_path, is_special=True) +class TestError(Exception): + pass + + class ThreadSafeCounter: def __init__(self, start=0): self._value = start @@ -862,29 +866,10 @@ def run_one_test(test_file): skipped_tests.append(test_name) return - # get expected output - test_file_expected = test_file + ".exp" - if os.path.isfile(test_file_expected): - # expected output given by a file, so read that in - with open(test_file_expected, "rb") as f: - output_expected = f.read() - else: - # run CPython to work out expected output - try: - output_expected = subprocess.check_output( - CPYTHON3_CMD + [test_file_abspath], - cwd=os.path.dirname(test_file), - stderr=subprocess.STDOUT, - ) - except subprocess.CalledProcessError: - output_expected = b"CPYTHON3 CRASH" - - # canonical form for all host platforms is to use \n for end-of-line - output_expected = output_expected.replace(b"\r\n", b"\n") - - # run MicroPython + # Run the test on the MicroPython target. output_mupy = run_micropython(pyb, args, test_file, test_file_abspath) + # Check if the target requested to skip this test. if output_mupy == b"SKIP\n": if pyb is not None and hasattr(pyb, "read_until"): # Running on a target over a serial connection, and the target requested @@ -896,22 +881,96 @@ def run_one_test(test_file): skipped_tests.append(test_name) return - testcase_count.add(len(output_expected.splitlines())) + # Look at the output of the test to see if unittest was used. + uses_unittest = False + output_mupy_lines = output_mupy.splitlines() + if any( + line == b"ImportError: no module named 'unittest'" for line in output_mupy_lines[-3:] + ): + raise TestError( + ( + "error: test {} requires unittest".format(test_file), + "(eg run `mpremote mip install unittest` to install it)", + ) + ) + elif ( + len(output_mupy_lines) > 4 + and output_mupy_lines[-4] == b"-" * 70 + and output_mupy_lines[-2] == b"" + ): + # look for unittest summary + unittest_ran_match = re.match(rb"Ran (\d+) tests$", output_mupy_lines[-3]) + unittest_result_match = re.match( + b"(" + rb"(OK)( \(skipped=(\d+)\))?" + b"|" + rb"(FAILED) \(failures=(\d+), errors=(\d+)\)" + b")$", + output_mupy_lines[-1], + ) + uses_unittest = unittest_ran_match and unittest_result_match + + # Determine the expected output. + if uses_unittest: + # Expected output is result of running unittest. + output_expected = None + else: + test_file_expected = test_file + ".exp" + if os.path.isfile(test_file_expected): + # Expected output given by a file, so read that in. + with open(test_file_expected, "rb") as f: + output_expected = f.read() + else: + # Run CPython to work out expected output. + try: + output_expected = subprocess.check_output( + CPYTHON3_CMD + [test_file_abspath], + cwd=os.path.dirname(test_file), + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError: + output_expected = b"CPYTHON3 CRASH" + + # Canonical form for all host platforms is to use \n for end-of-line. + output_expected = output_expected.replace(b"\r\n", b"\n") + + # Work out if test passed or not. + test_passed = False + extra_info = "" + if uses_unittest: + test_passed = unittest_result_match.group(2) == b"OK" + num_test_cases = int(unittest_ran_match.group(1)) + extra_info = "unittest: {} ran".format(num_test_cases) + if test_passed and unittest_result_match.group(4) is not None: + num_skipped = int(unittest_result_match.group(4)) + num_test_cases -= num_skipped + extra_info += ", {} skipped".format(num_skipped) + elif not test_passed: + num_failures = int(unittest_result_match.group(6)) + num_errors = int(unittest_result_match.group(7)) + extra_info += ", {} failures, {} errors".format(num_failures, num_errors) + extra_info = "(" + extra_info + ")" + testcase_count.add(num_test_cases) + else: + testcase_count.add(len(output_expected.splitlines())) + test_passed = output_expected == output_mupy filename_expected = os.path.join(result_dir, test_basename + ".exp") filename_mupy = os.path.join(result_dir, test_basename + ".out") - if output_expected == output_mupy: - print("pass ", test_file) + # Print test summary, update counters, and save .exp/.out files if needed. + if test_passed: + print("pass ", test_file, extra_info) passed_count.increment() rm_f(filename_expected) rm_f(filename_mupy) else: - with open(filename_expected, "wb") as f: - f.write(output_expected) + print("FAIL ", test_file, extra_info) + if output_expected is not None: + with open(filename_expected, "wb") as f: + f.write(output_expected) with open(filename_mupy, "wb") as f: f.write(output_mupy) - print("FAIL ", test_file) failed_tests.append((test_name, test_file)) test_count.increment() @@ -919,12 +978,17 @@ def run_one_test(test_file): if pyb: num_threads = 1 - if num_threads > 1: - pool = ThreadPool(num_threads) - pool.map(run_one_test, tests) - else: - for test in tests: - run_one_test(test) + try: + if num_threads > 1: + pool = ThreadPool(num_threads) + pool.map(run_one_test, tests) + else: + for test in tests: + run_one_test(test) + except TestError as er: + for line in er.args[0]: + print(line) + sys.exit(1) print( "{} tests performed ({} individual testcases)".format( @@ -1189,8 +1253,15 @@ def main(): tests = args.files if not args.keep_path: - # clear search path to make sure tests use only builtin modules and those in extmod - os.environ["MICROPYPATH"] = ".frozen" + os.pathsep + base_path("../extmod") + # Clear search path to make sure tests use only builtin modules, those in + # extmod, and a path to unittest in case it's needed. + os.environ["MICROPYPATH"] = ( + ".frozen" + + os.pathsep + + base_path("../extmod") + + os.pathsep + + base_path("../lib/micropython-lib/python-stdlib/unittest") + ) try: os.makedirs(args.result_dir, exist_ok=True) From a8422439ab5e86eeaceae09c4655101225fe5fce Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 3 Dec 2024 12:31:50 +1100 Subject: [PATCH 076/546] tests/run-tests.py: Print .out file when there is no .exp file. So that a failing unittest-based test has its entire log printed when using `run-tests.py --print-failures`. Signed-off-by: Damien George --- tests/run-tests.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index 0e491ccd62bf2..6fe45707bb6cd 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -1154,11 +1154,18 @@ def main(): args = cmd_parser.parse_args() if args.print_failures: - for exp in glob(os.path.join(args.result_dir, "*.exp")): - testbase = exp[:-4] + for out in glob(os.path.join(args.result_dir, "*.out")): + testbase = out[:-4] print() print("FAILURE {0}".format(testbase)) - os.system("{0} {1}.exp {1}.out".format(DIFF, testbase)) + if os.path.exists(testbase + ".exp"): + # Show diff of expected and actual output. + os.system("{0} {1}.exp {1}.out".format(DIFF, testbase)) + else: + # No expected output, just show the actual output (eg from a unittest). + with open(out) as f: + for line in f: + print(line, end="") sys.exit(0) From 6dcbdec4de2975a08a50828a0b25e9b3844e04d0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Nov 2024 22:23:48 +1100 Subject: [PATCH 077/546] lib/micropython-lib: Update submodule to latest. This brings in: - umqtt.simple: add optional socket timeout to connect method - aioespnow,webrepl: use recommended network.WLAN.IF_[AP|STA] constants - unittest: allow SkipTest to work within a subTest - unittest: always use "raise" with an argument Signed-off-by: Damien George --- lib/micropython-lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/micropython-lib b/lib/micropython-lib index 68e3e07bc7ab6..e4cf09527bce7 160000 --- a/lib/micropython-lib +++ b/lib/micropython-lib @@ -1 +1 @@ -Subproject commit 68e3e07bc7ab63931cead3854b2a114e9a084248 +Subproject commit e4cf09527bce7569f5db742cf6ae9db68d50c6a9 From 42f37e951b8990576e16bb7f5449fddeee0c0664 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Nov 2024 15:54:26 +1100 Subject: [PATCH 078/546] esp8266/mpconfigport: Enable function attributes. This allows `unittest` to work on esp8266 boards. Signed-off-by: Damien George --- ports/esp8266/mpconfigport.h | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 6504127755e85..83d80a7c963f6 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -23,7 +23,6 @@ #define MICROPY_OPT_MATH_FACTORIAL (0) #define MICROPY_REPL_EMACS_KEYS (0) #define MICROPY_PY_BUILTINS_COMPLEX (0) -#define MICROPY_PY_FUNCTION_ATTRS (0) #define MICROPY_PY_DELATTR_SETATTR (0) #define MICROPY_PY_BUILTINS_STR_CENTER (0) #define MICROPY_PY_BUILTINS_STR_PARTITION (0) From d3d29765863e7b9ed7ea9c68413b80405a75d0e9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 25 Nov 2024 23:57:27 +1100 Subject: [PATCH 079/546] qemu/Makefile: Include unittest in firmware. So that this port can run unittest-based tests. Signed-off-by: Damien George --- ports/qemu/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index 62601324d6a92..237e1e9514c60 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -19,10 +19,10 @@ QSTR_DEFS = qstrdefsport.h MICROPY_ROM_TEXT_COMPRESSION ?= 1 ifeq ($(QEMU_ARCH),arm) -FROZEN_MANIFEST ?= "freeze('test-frzmpy')" +FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy')" endif ifeq ($(QEMU_ARCH),riscv32) -FROZEN_MANIFEST ?= "freeze('test-frzmpy', ('frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" +FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" endif # include py core make definitions From f62df1a2c2d27d602be29474843a19284d807358 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Nov 2024 16:26:39 +1100 Subject: [PATCH 080/546] tests/ports/stm32_hardware: Convert DMA test to use unittest. Signed-off-by: Damien George --- tests/ports/stm32_hardware/dma_alignment.py | 88 +++++++++++-------- .../ports/stm32_hardware/dma_alignment.py.exp | 2 - 2 files changed, 52 insertions(+), 38 deletions(-) delete mode 100644 tests/ports/stm32_hardware/dma_alignment.py.exp diff --git a/tests/ports/stm32_hardware/dma_alignment.py b/tests/ports/stm32_hardware/dma_alignment.py index 1836b25d86d53..1c271d48e725b 100644 --- a/tests/ports/stm32_hardware/dma_alignment.py +++ b/tests/ports/stm32_hardware/dma_alignment.py @@ -1,43 +1,59 @@ -from machine import SPI # Regression test for DMA for DCache coherency bugs with cache line # written originally for https://github.com/micropython/micropython/issues/13471 # IMPORTANT: This test requires SPI2 MISO (pin Y8 on Pyboard D) to be connected to GND +import unittest +from machine import SPI + SPI_NUM = 2 +BAUDRATE = 5_000_000 + + +class Test(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.spi = SPI(SPI_NUM, baudrate=BAUDRATE) + cls.skip_slow_test = False + + def test_variable_offset_fixed_length(self): + buf = bytearray(1024) + for offs in range(0, len(buf)): + v = memoryview(buf)[offs : offs + 128] + self.spi.readinto(v, 0xFF) + ok = all(b == 0x00 for b in v) + if not ok: + print(offs, v.hex()) + self.skip_slow_test = True + self.assertTrue(ok) + + def test_variable_offset_and_lengths(self): + # this takes around 30s to run, so skipped if already failing + if self.skip_slow_test: + self.skipTest("already failing") + + buf = bytearray(1024) + for op_len in range(1, 66): + print(op_len) + wr = b"\xff" * op_len + for offs in range(1, len(buf) - op_len - 1): + # Place some "sentinel" values before and after the DMA buffer + before = offs & 0xFF + after = (~offs) & 0xFF + buf[offs - 1] = before + buf[offs + op_len] = after + v = memoryview(buf)[offs : offs + op_len] + self.spi.write_readinto(wr, v) + ok = ( + all(b == 0x00 for b in v) + and buf[offs - 1] == before + and buf[offs + op_len] == after + ) + if not ok: + print(v.hex()) + print(hex(op_len), hex(offs), hex(buf[offs - 1]), hex(buf[offs + op_len])) + self.assertTrue(ok) + -spi = SPI(SPI_NUM, baudrate=5_000_000) -buf = bytearray(1024) -ok = True - -for offs in range(0, len(buf)): - v = memoryview(buf)[offs : offs + 128] - spi.readinto(v, 0xFF) - if not all(b == 0x00 for b in v): - print(offs, v.hex()) - ok = False - -print("Variable offset fixed length " + ("OK" if ok else "FAIL")) - -# this takes around 30s to run, so skipped if already failing -if ok: - for op_len in range(1, 66): - wr = b"\xFF" * op_len - for offs in range(1, len(buf) - op_len - 1): - # Place some "sentinel" values before and after the DMA buffer - before = offs & 0xFF - after = (~offs) & 0xFF - buf[offs - 1] = before - buf[offs + op_len] = after - v = memoryview(buf)[offs : offs + op_len] - spi.write_readinto(wr, v) - if ( - not all(b == 0x00 for b in v) - or buf[offs - 1] != before - or buf[offs + op_len] != after - ): - print(v.hex()) - print(hex(op_len), hex(offs), hex(buf[offs - 1]), hex(buf[offs + op_len])) - ok = False - - print("Variable offset and lengths " + ("OK" if ok else "FAIL")) +if __name__ == "__main__": + unittest.main() diff --git a/tests/ports/stm32_hardware/dma_alignment.py.exp b/tests/ports/stm32_hardware/dma_alignment.py.exp deleted file mode 100644 index e890e0081c4b7..0000000000000 --- a/tests/ports/stm32_hardware/dma_alignment.py.exp +++ /dev/null @@ -1,2 +0,0 @@ -Variable offset fixed length OK -Variable offset and lengths OK From c7c3ffa45f5cc3463f0b8983c360d950d210f320 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 13 Nov 2024 12:44:10 +1100 Subject: [PATCH 081/546] tests/net_hosted: Convert connect-nonblock-xfer test to use unittest. This allows it to run parts of the test on esp8266 (or any target using axTLS). Signed-off-by: Damien George --- tests/net_hosted/connect_nonblock_xfer.py | 104 +++++++++++------- tests/net_hosted/connect_nonblock_xfer.py.exp | 44 -------- 2 files changed, 62 insertions(+), 86 deletions(-) delete mode 100644 tests/net_hosted/connect_nonblock_xfer.py.exp diff --git a/tests/net_hosted/connect_nonblock_xfer.py b/tests/net_hosted/connect_nonblock_xfer.py index 23620908afbc6..2c8e12473fe54 100644 --- a/tests/net_hosted/connect_nonblock_xfer.py +++ b/tests/net_hosted/connect_nonblock_xfer.py @@ -1,15 +1,14 @@ # test that socket.connect() on a non-blocking socket raises EINPROGRESS # and that an immediate write/send/read/recv does the right thing +import unittest import errno import select import socket import ssl # only mbedTLS supports non-blocking mode -if not hasattr(ssl, "MBEDTLS_VERSION"): - print("SKIP") - raise SystemExit +ssl_supports_nonblocking = hasattr(ssl, "MBEDTLS_VERSION") # get the name of an errno error code @@ -24,34 +23,43 @@ def errno_name(er): # do_connect establishes the socket and wraps it if tls is True. # If handshake is true, the initial connect (and TLS handshake) is # allowed to be performed before returning. -def do_connect(peer_addr, tls, handshake): +def do_connect(self, peer_addr, tls, handshake): s = socket.socket() s.setblocking(False) try: - # print("Connecting to", peer_addr) + print("Connecting to", peer_addr) s.connect(peer_addr) + self.fail() except OSError as er: print("connect:", errno_name(er.errno)) + self.assertEqual(er.errno, errno.EINPROGRESS) + # wrap with ssl/tls if desired if tls: + print("wrap socket") ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - try: - s = ssl_context.wrap_socket(s, do_handshake_on_connect=handshake) - print("wrap ok: True") - except Exception as e: - print("wrap er:", e) + s = ssl_context.wrap_socket(s, do_handshake_on_connect=handshake) + return s -# poll a socket and print out the result -def poll(s): +# poll a socket and check the result +def poll(self, s, expect_writable): poller = select.poll() poller.register(s) - print("poll: ", poller.poll(0)) + result = poller.poll(0) + print("poll:", result) + if expect_writable: + self.assertEqual(len(result), 1) + self.assertEqual(result[0][1], select.POLLOUT) + else: + self.assertEqual(result, []) + +# do_test runs the test against a specific peer address. +def do_test(self, peer_addr, tls, handshake): + print() -# test runs the test against a specific peer address. -def test(peer_addr, tls, handshake): # MicroPython plain and TLS sockets have read/write hasRW = True @@ -62,54 +70,66 @@ def test(peer_addr, tls, handshake): # connect + send # non-blocking send should raise EAGAIN if hasSR: - s = do_connect(peer_addr, tls, handshake) - poll(s) - try: + s = do_connect(self, peer_addr, tls, handshake) + poll(self, s, False) + with self.assertRaises(OSError) as ctx: ret = s.send(b"1234") - print("send ok:", ret) # shouldn't get here - except OSError as er: - print("send er:", errno_name(er.errno)) + print("send error:", errno_name(ctx.exception.errno)) + self.assertEqual(ctx.exception.errno, errno.EAGAIN) s.close() # connect + write # non-blocking write should return None if hasRW: - s = do_connect(peer_addr, tls, handshake) - poll(s) + s = do_connect(self, peer_addr, tls, handshake) + poll(self, s, tls and handshake) ret = s.write(b"1234") - print("write: ", ret) + print("write:", ret) + if tls and handshake: + self.assertEqual(ret, 4) + else: + self.assertIsNone(ret) s.close() # connect + recv # non-blocking recv should raise EAGAIN if hasSR: - s = do_connect(peer_addr, tls, handshake) - poll(s) - try: + s = do_connect(self, peer_addr, tls, handshake) + poll(self, s, False) + with self.assertRaises(OSError) as ctx: ret = s.recv(10) - print("recv ok:", ret) # shouldn't get here - except OSError as er: - print("recv er:", errno_name(er.errno)) + print("recv error:", errno_name(ctx.exception.errno)) + self.assertEqual(ctx.exception.errno, errno.EAGAIN) s.close() # connect + read # non-blocking read should return None if hasRW: - s = do_connect(peer_addr, tls, handshake) - poll(s) + s = do_connect(self, peer_addr, tls, handshake) + poll(self, s, tls and handshake) ret = s.read(10) - print("read: ", ret) + print("read:", ret) + self.assertIsNone(ret) s.close() -if __name__ == "__main__": +class Test(unittest.TestCase): # these tests use a non-existent test IP address, this way the connect takes forever and # we can see EAGAIN/None (https://tools.ietf.org/html/rfc5737) - print("--- Plain sockets to nowhere ---") - test(socket.getaddrinfo("192.0.2.1", 80)[0][-1], False, False) - print("--- SSL sockets to nowhere ---") - test(socket.getaddrinfo("192.0.2.1", 443)[0][-1], True, False) - print("--- Plain sockets ---") - test(socket.getaddrinfo("micropython.org", 80)[0][-1], False, False) - print("--- SSL sockets ---") - test(socket.getaddrinfo("micropython.org", 443)[0][-1], True, True) + def test_plain_sockets_to_nowhere(self): + do_test(self, socket.getaddrinfo("192.0.2.1", 80)[0][-1], False, False) + + @unittest.skipIf(not ssl_supports_nonblocking, "SSL doesn't support non-blocking") + def test_ssl_sockets_to_nowhere(self): + do_test(self, socket.getaddrinfo("192.0.2.1", 443)[0][-1], True, False) + + def test_plain_sockets(self): + do_test(self, socket.getaddrinfo("micropython.org", 80)[0][-1], False, False) + + @unittest.skipIf(not ssl_supports_nonblocking, "SSL doesn't support non-blocking") + def test_ssl_sockets(self): + do_test(self, socket.getaddrinfo("micropython.org", 443)[0][-1], True, True) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/net_hosted/connect_nonblock_xfer.py.exp b/tests/net_hosted/connect_nonblock_xfer.py.exp deleted file mode 100644 index c5498a03873a0..0000000000000 --- a/tests/net_hosted/connect_nonblock_xfer.py.exp +++ /dev/null @@ -1,44 +0,0 @@ ---- Plain sockets to nowhere --- -connect: EINPROGRESS -poll: [] -send er: EAGAIN -connect: EINPROGRESS -poll: [] -write: None -connect: EINPROGRESS -poll: [] -recv er: EAGAIN -connect: EINPROGRESS -poll: [] -read: None ---- SSL sockets to nowhere --- -connect: EINPROGRESS -wrap ok: True -poll: [] -write: None -connect: EINPROGRESS -wrap ok: True -poll: [] -read: None ---- Plain sockets --- -connect: EINPROGRESS -poll: [] -send er: EAGAIN -connect: EINPROGRESS -poll: [] -write: None -connect: EINPROGRESS -poll: [] -recv er: EAGAIN -connect: EINPROGRESS -poll: [] -read: None ---- SSL sockets --- -connect: EINPROGRESS -wrap ok: True -poll: [(, 4)] -write: 4 -connect: EINPROGRESS -wrap ok: True -poll: [(, 4)] -read: None From 2c80d369988a84d85efe0483f9cf469ff27b70f9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 3 Dec 2024 15:12:34 +1100 Subject: [PATCH 082/546] tests/extmod: Convert machine1.py test to use unittest. Signed-off-by: Damien George --- tests/extmod/machine1.py | 55 +++++++++++++++++------------------- tests/extmod/machine1.py.exp | 8 ------ 2 files changed, 26 insertions(+), 37 deletions(-) delete mode 100644 tests/extmod/machine1.py.exp diff --git a/tests/extmod/machine1.py b/tests/extmod/machine1.py index 90e6d17174fb3..2f1c0681f64d5 100644 --- a/tests/extmod/machine1.py +++ b/tests/extmod/machine1.py @@ -8,39 +8,36 @@ print("SKIP") raise SystemExit -print(machine.mem8) +import unittest -try: - machine.mem16[1] -except ValueError: - print("ValueError") -try: - machine.mem16[1] = 1 -except ValueError: - print("ValueError") +class Test(unittest.TestCase): + def test_mem8_print(self): + self.assertEqual(repr(machine.mem8), "<8-bit memory>") -try: - del machine.mem8[0] -except TypeError: - print("TypeError") + def test_alignment(self): + with self.assertRaises(ValueError): + machine.mem16[1] -try: - machine.mem8[0:1] -except TypeError: - print("TypeError") + with self.assertRaises(ValueError): + machine.mem16[1] = 1 -try: - machine.mem8[0:1] = 10 -except TypeError: - print("TypeError") + def test_operations(self): + with self.assertRaises(TypeError): + del machine.mem8[0] -try: - machine.mem8["hello"] -except TypeError: - print("TypeError") + with self.assertRaises(TypeError): + machine.mem8[0:1] -try: - machine.mem8["hello"] = 10 -except TypeError: - print("TypeError") + with self.assertRaises(TypeError): + machine.mem8[0:1] = 10 + + with self.assertRaises(TypeError): + machine.mem8["hello"] + + with self.assertRaises(TypeError): + machine.mem8["hello"] = 10 + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/extmod/machine1.py.exp b/tests/extmod/machine1.py.exp deleted file mode 100644 index 250485969086e..0000000000000 --- a/tests/extmod/machine1.py.exp +++ /dev/null @@ -1,8 +0,0 @@ -<8-bit memory> -ValueError -ValueError -TypeError -TypeError -TypeError -TypeError -TypeError From a118cf9ab0d44a69b4af23e2f42a49c2abafca41 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 30 Nov 2024 08:31:36 +0100 Subject: [PATCH 083/546] extmod/extmod.mk: Fix libmetal build prefix. libmetal source files already have the build directory prefix, because they're auto-generated inside the build directory. When they're added to `SRC_THIRDPARTY_C`, another build directory prefix is added resulting in the object files being generated in a nested build directory. This patch strips the build directory prefix before adding libmetal's source files. Signed-off-by: iabdalkader --- extmod/extmod.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/extmod.mk b/extmod/extmod.mk index 514197d6b9a88..f2ae3ad6923fb 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -616,6 +616,6 @@ $(BUILD)/$(OPENAMP_DIR)/lib/virtio_mmio/virtio_mmio_drv.o: CFLAGS += -Wno-unused # We need to have generated libmetal before compiling OpenAMP. $(addprefix $(BUILD)/, $(SRC_OPENAMP_C:.c=.o)): $(BUILD)/openamp/metal/config.h -SRC_THIRDPARTY_C += $(SRC_LIBMETAL_C) $(SRC_OPENAMP_C) +SRC_THIRDPARTY_C += $(SRC_OPENAMP_C) $(SRC_LIBMETAL_C:$(BUILD)/%=%) endif # MICROPY_PY_OPENAMP From c55202dd6390d13f77f5bd10c504300eadcfd1a7 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 29 Nov 2024 14:31:18 +0100 Subject: [PATCH 084/546] shared/tinyusb: Set MSC max endpoint size based on device speed. Signed-off-by: iabdalkader --- shared/tinyusb/mp_usbd_descriptor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shared/tinyusb/mp_usbd_descriptor.c b/shared/tinyusb/mp_usbd_descriptor.c index be3473b6b9142..d0c8845b68087 100644 --- a/shared/tinyusb/mp_usbd_descriptor.c +++ b/shared/tinyusb/mp_usbd_descriptor.c @@ -34,6 +34,7 @@ #define USBD_CDC_CMD_MAX_SIZE (8) #define USBD_CDC_IN_OUT_MAX_SIZE ((CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED) ? 512 : 64) +#define USBD_MSC_IN_OUT_MAX_SIZE ((CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED) ? 512 : 64) const tusb_desc_device_t mp_usbd_builtin_desc_dev = { .bLength = sizeof(tusb_desc_device_t), @@ -61,7 +62,7 @@ const uint8_t mp_usbd_builtin_desc_cfg[MP_USBD_BUILTIN_DESC_CFG_LEN] = { USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), #endif #if CFG_TUD_MSC - TUD_MSC_DESCRIPTOR(USBD_ITF_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), + TUD_MSC_DESCRIPTOR(USBD_ITF_MSC, USBD_STR_MSC, EPNUM_MSC_OUT, EPNUM_MSC_IN, USBD_MSC_IN_OUT_MAX_SIZE), #endif }; From 67b4e6236bb769d74cf5c625da9baad13806c9e5 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 29 Nov 2024 16:15:21 +0100 Subject: [PATCH 085/546] mimxrt: Switch to shared TinyUSB descriptor. This removes duplicated TinyUSB configuration and port-specific code. Tested on RT1062, CDC+MSC still working. @robert-hh tested CDC with 1011, 1015, 1020 and 1176. Signed-off-by: iabdalkader --- ports/mimxrt/Makefile | 5 +- ports/mimxrt/mpconfigport.h | 8 ++ ports/mimxrt/tusb_port.c | 138 ------------------------- ports/mimxrt/{tusb_config.h => usbd.c} | 27 +++-- 4 files changed, 28 insertions(+), 150 deletions(-) delete mode 100644 ports/mimxrt/tusb_port.c rename ports/mimxrt/{tusb_config.h => usbd.c} (71%) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index e98073d33626d..3a9550cc974d8 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -78,6 +78,7 @@ INC += -I$(TOP)/lib/oofatfs INC += -I$(TOP)/lib/tinyusb/hw INC += -I$(TOP)/lib/tinyusb/hw/bsp/teensy_40 INC += -I$(TOP)/lib/tinyusb/src +INC += -I$(TOP)/shared/tinyusb INC += -I. INC += -Ihal @@ -215,7 +216,7 @@ SRC_C += \ sdio.c \ systick.c \ ticks.c \ - tusb_port.c \ + usbd.c \ SHARED_SRC_C += \ shared/libc/printf.c \ @@ -234,6 +235,7 @@ SHARED_SRC_C += \ shared/timeutils/timeutils.c \ shared/tinyusb/mp_usbd.c \ shared/tinyusb/mp_usbd_cdc.c \ + shared/tinyusb/mp_usbd_descriptor.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)) @@ -343,6 +345,7 @@ CFLAGS += \ -DBOARD_$(BOARD) \ -DBOARD_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) \ -DCFG_TUSB_MCU=OPT_MCU_MIMXRT1XXX \ + -DCFG_TUD_MAX_SPEED=OPT_MODE_HIGH_SPEED \ -DCLOCK_CONFIG_H='' \ -DCPU_$(MCU_SERIES)$(MCU_CORE) \ -DCPU_$(MCU_VARIANT) \ diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 5ef6695c1425c..31444e4980be5 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -188,6 +188,14 @@ extern const struct _mp_obj_type_t mp_network_cyw43_type; #define MP_STATE_PORT MP_STATE_VM // Miscellaneous settings +#ifndef MICROPY_HW_USB_VID +#define MICROPY_HW_USB_VID (0xf055) +#endif + +#ifndef MICROPY_HW_USB_PID +#define MICROPY_HW_USB_PID (0x9802) +#endif + #ifndef MICROPY_EVENT_POLL_HOOK #define MICROPY_EVENT_POLL_HOOK \ do { \ diff --git a/ports/mimxrt/tusb_port.c b/ports/mimxrt/tusb_port.c deleted file mode 100644 index f81200e2caa87..0000000000000 --- a/ports/mimxrt/tusb_port.c +++ /dev/null @@ -1,138 +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" -#include "py/mphal.h" - -#ifndef MICROPY_HW_USB_VID -#define MICROPY_HW_USB_VID (0xf055) -#endif - -#ifndef MICROPY_HW_USB_PID -#define MICROPY_HW_USB_PID (0x9802) -#endif - -#ifndef MICROPY_HW_USB_MANUFACTURER_STRING -#define MICROPY_HW_USB_MANUFACTURER_STRING ("MicroPython") -#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 (512) - -#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_ENDPOINT0_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] = MICROPY_HW_USB_MANUFACTURER_STRING, - [USBD_STR_PRODUCT] = MICROPY_HW_BOARD_NAME, - [USBD_STR_SERIAL] = "00000000000000000000", - [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]; - static const char hexchr[16] = "0123456789ABCDEF"; - - memset(desc_str, 0, sizeof(desc_str)); - - 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; - } - if (index == USBD_STR_SERIAL) { - uint8_t uid[8]; - mp_hal_get_unique_id(uid); - // store it as a hex string - for (len = 0; len < 16; len += 2) { - desc_str[1 + len] = hexchr[uid[len / 2] >> 4]; - desc_str[1 + len + 1] = hexchr[uid[len / 2] & 0x0f]; - } - } else { - 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/mimxrt/tusb_config.h b/ports/mimxrt/usbd.c similarity index 71% rename from ports/mimxrt/tusb_config.h rename to ports/mimxrt/usbd.c index 607f36446f858..47126ddc017a2 100644 --- a/ports/mimxrt/tusb_config.h +++ b/ports/mimxrt/usbd.c @@ -1,7 +1,9 @@ /* + * This file is part of the MicroPython project, http://micropython.org/ + * * The MIT License (MIT) * - * Copyright (c) 2020 Jim Mussared + * 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 @@ -20,18 +22,21 @@ * 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_MIMXRT_TUSB_CONFIG_H -#define MICROPY_INCLUDED_MIMXRT_TUSB_CONFIG_H -#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) +#include "py/mpconfig.h" + +#if MICROPY_HW_ENABLE_USBDEV -#define CFG_TUSB_OS (OPT_OS_NONE) +#include "py/mphal.h" +#include "mp_usbd.h" +#include "string.h" +#include "tusb.h" -#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) +void mp_usbd_port_get_serial_number(char *serial_buf) { + uint8_t uid[8]; + mp_hal_get_unique_id(uid); + mp_usbd_hex_str(serial_buf, uid, sizeof(uid)); +} -#endif // MICROPY_INCLUDED_MIMXRT_TUSB_CONFIG_H +#endif From d5d366beef8d0331ee533b28d2e3024e34123ef1 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 29 Nov 2024 14:51:58 +0100 Subject: [PATCH 086/546] nrf/boards/ARDUINO_NANO_33_BLE_SENSE: Update LED and timer config. Changes: - Enable hardware timer. - Define LED pins. Signed-off-by: iabdalkader --- ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/mpconfigboard.h | 1 + ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/pins.csv | 4 ++++ 2 files changed, 5 insertions(+) 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 a4bb1d899e067..65642bf234226 100644 --- a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/mpconfigboard.h +++ b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/mpconfigboard.h @@ -13,6 +13,7 @@ #define MICROPY_PY_MACHINE_UART (1) #define MICROPY_PY_MACHINE_HW_PWM (1) +#define MICROPY_PY_MACHINE_TIMER_NRF (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) #define MICROPY_PY_MACHINE_I2C (1) #define MICROPY_PY_MACHINE_ADC (1) diff --git a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/pins.csv b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/pins.csv index 972dd9d3bdcb8..52e3df400e0b5 100644 --- a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/pins.csv +++ b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/pins.csv @@ -80,3 +80,7 @@ TX,P35 RX,P42 I2C_SCL,P2 I2C_SDA,P31 +LED_RED,P24 +LED_GREEN,P16 +LED_BLUE,P6 +LED_YELLOW,P13 From b5e80fafb2d5ef69fd80dbc6d7e839ffaa5a0f98 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 29 Nov 2024 16:56:20 +0100 Subject: [PATCH 087/546] drivers/memory/spiflash: Add a config option to soft-reset SPI flash. Add a compile-time config option to soft-reset SPI flash on init. This puts the flash in a known state on reset. Note this option is disabled by default. Signed-off-by: iabdalkader --- drivers/memory/spiflash.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/memory/spiflash.c b/drivers/memory/spiflash.c index e66153636b1fd..773334e167ae3 100644 --- a/drivers/memory/spiflash.c +++ b/drivers/memory/spiflash.c @@ -44,6 +44,8 @@ #define CMD_RD_DEVID (0x9f) #define CMD_CHIP_ERASE (0xc7) #define CMD_C4READ (0xeb) +#define CMD_RSTEN (0x66) +#define CMD_RESET (0x99) // 32 bit addressing commands #define CMD_WRITE_32 (0x12) @@ -180,6 +182,14 @@ void mp_spiflash_init(mp_spiflash_t *self) { // Ensure SPI flash is out of sleep mode mp_spiflash_deepsleep_internal(self, 0); + // Software reset. + #if MICROPY_HW_SPIFLASH_SOFT_RESET + mp_spiflash_write_cmd(self, CMD_RSTEN); + mp_spiflash_write_cmd(self, CMD_RESET); + mp_spiflash_wait_wip0(self); + mp_hal_delay_ms(1); + #endif + #if defined(CHECK_DEVID) // Validate device id uint32_t devid; From 46ca78e23fdf86d9f7d101142d5448529b0ef7be Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 29 Nov 2024 16:57:38 +0100 Subject: [PATCH 088/546] stm32/boards: Update Arduino board configs for SPI reset and bootloader. Update ARDUINO_GIGA, ARDUINO_OPTA, ARDUINO_NICLA_VISION and ARDUINO_PORTENTA_H7 to: - Enable SPI flash soft-reset. - Disable enter bootloader via reset. Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h | 2 ++ ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h | 2 ++ ports/stm32/boards/ARDUINO_OPTA/mpconfigboard.h | 2 ++ ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h | 2 ++ 4 files changed, 8 insertions(+) diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h index d09a074402e7b..2b86ce528e266 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h @@ -29,9 +29,11 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENABLE_TIMER (1) #define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_HW_ENABLE_MMCARD (0) +#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) +#define MICROPY_HW_SPIFLASH_SOFT_RESET (1) #define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) #define MICROPY_BOARD_STARTUP GIGA_board_startup diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h index 796a9fae923b1..7bb75091e8bdf 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h @@ -29,9 +29,11 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENABLE_TIMER (1) #define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_HW_ENABLE_MMCARD (0) +#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) +#define MICROPY_HW_SPIFLASH_SOFT_RESET (1) #define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) #define MICROPY_BOARD_STARTUP NICLAV_board_startup diff --git a/ports/stm32/boards/ARDUINO_OPTA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_OPTA/mpconfigboard.h index 869522e1ee70f..f52c8a26a81d0 100644 --- a/ports/stm32/boards/ARDUINO_OPTA/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_OPTA/mpconfigboard.h @@ -25,9 +25,11 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_HAS_SWITCH (1) #define MICROPY_HW_HAS_FLASH (1) #define MICROPY_HW_ENABLE_TIMER (1) +#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) +#define MICROPY_HW_SPIFLASH_SOFT_RESET (1) #define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) #define MICROPY_BOARD_STARTUP OPTA_board_startup diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h index 860a0f688b2d9..5442f74276178 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h @@ -29,9 +29,11 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENABLE_TIMER (1) #define MICROPY_HW_ENABLE_SDCARD (1) #define MICROPY_HW_ENABLE_MMCARD (0) +#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) +#define MICROPY_HW_SPIFLASH_SOFT_RESET (1) #define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) #define MICROPY_BOARD_STARTUP PORTENTA_board_startup From 47d9988df2c7c6a4603f631b02a3f3a5c7e997f4 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 4 Dec 2024 09:54:13 +0100 Subject: [PATCH 089/546] stm32/boards: Rename SDRAM frequency config option to make units clear. Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h | 2 +- ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h index 2b86ce528e266..99b92a3793f03 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h @@ -233,7 +233,7 @@ extern struct _spi_bdev_t spi_bdev; // Timing configuration for 200MHz/2=100MHz (10ns) #define MICROPY_HW_SDRAM_CLOCK_PERIOD 2 #define MICROPY_HW_SDRAM_CAS_LATENCY 2 -#define MICROPY_HW_SDRAM_FREQUENCY (100000) // 100 MHz +#define MICROPY_HW_SDRAM_FREQUENCY_KHZ (100000) // 100 MHz #define MICROPY_HW_SDRAM_TIMING_TMRD (2) #define MICROPY_HW_SDRAM_TIMING_TXSR (7) #define MICROPY_HW_SDRAM_TIMING_TRAS (5) diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h index 5442f74276178..6b77b16093fb4 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h @@ -247,7 +247,7 @@ extern struct _spi_bdev_t spi_bdev; // Timing configuration for 200MHz/2=100MHz (10ns) #define MICROPY_HW_SDRAM_CLOCK_PERIOD 2 #define MICROPY_HW_SDRAM_CAS_LATENCY 2 -#define MICROPY_HW_SDRAM_FREQUENCY (100000) // 100 MHz +#define MICROPY_HW_SDRAM_FREQUENCY_KHZ (100000) // 100 MHz #define MICROPY_HW_SDRAM_TIMING_TMRD (2) #define MICROPY_HW_SDRAM_TIMING_TXSR (7) #define MICROPY_HW_SDRAM_TIMING_TRAS (5) From 17808e7b749b269b58dcee67df599a0c61d455bd Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 30 Nov 2024 09:56:23 +0100 Subject: [PATCH 090/546] stm32/sdram: Make SDRAM refresh count configurable by a board. Refresh count calculations were using a hard-coded SDRAM frequency and refresh cycles, so change them to values that can be set by a board. And set these options to their existing values on STM32F769DISC and STM32F7DISC boards. Signed-off-by: iabdalkader --- ports/stm32/boards/STM32F769DISC/mpconfigboard.h | 2 ++ ports/stm32/boards/STM32F7DISC/mpconfigboard.h | 2 ++ ports/stm32/sdram.c | 6 +++--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/STM32F769DISC/mpconfigboard.h b/ports/stm32/boards/STM32F769DISC/mpconfigboard.h index f899091d201c0..017de9c068395 100644 --- a/ports/stm32/boards/STM32F769DISC/mpconfigboard.h +++ b/ports/stm32/boards/STM32F769DISC/mpconfigboard.h @@ -141,6 +141,7 @@ extern struct _spi_bdev_t spi_bdev; #define MICROPY_HW_SDRAM_BURST_LENGTH 1 #define MICROPY_HW_SDRAM_CAS_LATENCY 2 +#define MICROPY_HW_SDRAM_FREQUENCY_KHZ (90000) // 90 MHz #define MICROPY_HW_SDRAM_COLUMN_BITS_NUM 8 #define MICROPY_HW_SDRAM_ROW_BITS_NUM 12 #define MICROPY_HW_SDRAM_MEM_BUS_WIDTH 32 @@ -150,6 +151,7 @@ extern struct _spi_bdev_t spi_bdev; #define MICROPY_HW_SDRAM_RBURST (1) #define MICROPY_HW_SDRAM_WRITE_PROTECTION (0) #define MICROPY_HW_SDRAM_AUTOREFRESH_NUM (8) +#define MICROPY_HW_SDRAM_REFRESH_CYCLES 8192 // See pins.csv for CPU pin mapping #define MICROPY_HW_FMC_SDCKE0 (pyb_pin_FMC_SDCKE0) diff --git a/ports/stm32/boards/STM32F7DISC/mpconfigboard.h b/ports/stm32/boards/STM32F7DISC/mpconfigboard.h index cf7061902ee75..d4b21484ad4ba 100644 --- a/ports/stm32/boards/STM32F7DISC/mpconfigboard.h +++ b/ports/stm32/boards/STM32F7DISC/mpconfigboard.h @@ -111,6 +111,7 @@ void STM32F7DISC_board_early_init(void); #define MICROPY_HW_SDRAM_BURST_LENGTH 1 #define MICROPY_HW_SDRAM_CAS_LATENCY 2 +#define MICROPY_HW_SDRAM_FREQUENCY_KHZ (90000) // 90 MHz #define MICROPY_HW_SDRAM_COLUMN_BITS_NUM 8 #define MICROPY_HW_SDRAM_ROW_BITS_NUM 12 #define MICROPY_HW_SDRAM_MEM_BUS_WIDTH 16 @@ -120,6 +121,7 @@ void STM32F7DISC_board_early_init(void); #define MICROPY_HW_SDRAM_RBURST (1) #define MICROPY_HW_SDRAM_WRITE_PROTECTION (0) #define MICROPY_HW_SDRAM_AUTOREFRESH_NUM (8) +#define MICROPY_HW_SDRAM_REFRESH_CYCLES 8192 #define MICROPY_HW_FMC_SDCKE0 (pin_C3) #define MICROPY_HW_FMC_SDNE0 (pin_H3) diff --git a/ports/stm32/sdram.c b/ports/stm32/sdram.c index e99f34d2262a8..7104eb68e8ff3 100644 --- a/ports/stm32/sdram.c +++ b/ports/stm32/sdram.c @@ -245,15 +245,15 @@ static void sdram_init_seq(SDRAM_HandleTypeDef /* Send the command */ HAL_SDRAM_SendCommand(hsdram, command, 0x1000); - /* Step 8: Set the refresh rate counter + /* Step 8: Set the refresh rate counter. + Assuming 90MHz frequency, 8192 refresh cycles and 64ms refresh rate: RefreshRate = 64 ms / 8192 cyc = 7.8125 us/cyc - RefreshCycles = 7.8125 us * 90 MHz = 703 According to the formula on p.1665 of the reference manual, we also need to subtract 20 from the value, so the target refresh rate is 703 - 20 = 683. */ - #define REFRESH_COUNT (MICROPY_HW_SDRAM_REFRESH_RATE * 90000 / 8192 - 20) + #define REFRESH_COUNT (MICROPY_HW_SDRAM_REFRESH_RATE * MICROPY_HW_SDRAM_FREQUENCY_KHZ / MICROPY_HW_SDRAM_REFRESH_CYCLES - 20) HAL_SDRAM_ProgramRefreshRate(hsdram, REFRESH_COUNT); #if defined(STM32F7) || defined(STM32H7) From d42e39d87d37f20d74456222e603d0b6b409a556 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 2 Dec 2024 08:09:25 +0100 Subject: [PATCH 091/546] stm32/spi: Add spi_deinit_all function. SPI objects can remain active after a soft-reboot because they are statically allocated and lack a finalizer to collect and deinitialize them. This commit adds a `spi_deinit_all()` functions for SPI, similar to other peripherals such as UART, DAC, etc. Signed-off-by: iabdalkader --- ports/stm32/spi.c | 9 +++++++++ ports/stm32/spi.h | 1 + 2 files changed, 10 insertions(+) diff --git a/ports/stm32/spi.c b/ports/stm32/spi.c index 607b3fe685a4c..aa459119c146c 100644 --- a/ports/stm32/spi.c +++ b/ports/stm32/spi.c @@ -545,6 +545,15 @@ void spi_deinit(const spi_t *spi_obj) { } } +void spi_deinit_all(void) { + for (int i = 0; i < MP_ARRAY_SIZE(spi_obj); i++) { + const spi_t *spi = &spi_obj[i]; + if (spi->spi != NULL) { + spi_deinit(spi); + } + } +} + static HAL_StatusTypeDef spi_wait_dma_finished(const spi_t *spi, uint32_t t_start, uint32_t timeout) { volatile HAL_SPI_StateTypeDef *state = &spi->spi->State; for (;;) { diff --git a/ports/stm32/spi.h b/ports/stm32/spi.h index a8bc9d2cfdbe3..204038a92f351 100644 --- a/ports/stm32/spi.h +++ b/ports/stm32/spi.h @@ -67,6 +67,7 @@ extern const mp_obj_type_t pyb_spi_type; void spi_init0(void); int spi_init(const spi_t *spi, bool enable_nss_pin); void spi_deinit(const spi_t *spi_obj); +void spi_deinit_all(void); int spi_find_index(mp_obj_t id); void spi_set_params(const spi_t *spi_obj, uint32_t prescale, int32_t baudrate, int32_t polarity, int32_t phase, int32_t bits, int32_t firstbit); From 405aa69887558d561a993d5e9aa47b2ec7e76d9a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 2 Dec 2024 08:09:47 +0100 Subject: [PATCH 092/546] stm32/pyb_i2c: Add pyb_i2c_deinit_all function. I2C objects can remain active after a soft-reboot because they are statically allocated and lack a finalizer to collect and deinitialize them. This commit adds a `pyb_i2c_deinit_all()` function for I2C, similar to other peripherals such as UART, DAC, etc. Signed-off-by: iabdalkader --- ports/stm32/i2c.h | 1 + ports/stm32/pyb_i2c.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/ports/stm32/i2c.h b/ports/stm32/i2c.h index 04a7e928bdbdf..a48076842cbbf 100644 --- a/ports/stm32/i2c.h +++ b/ports/stm32/i2c.h @@ -50,6 +50,7 @@ void i2c_init0(void); int pyb_i2c_init(I2C_HandleTypeDef *i2c); int pyb_i2c_init_freq(const pyb_i2c_obj_t *self, mp_int_t freq); uint32_t pyb_i2c_get_baudrate(I2C_HandleTypeDef *i2c); +void pyb_i2c_deinit_all(void); void i2c_ev_irq_handler(mp_uint_t i2c_id); void i2c_er_irq_handler(mp_uint_t i2c_id); diff --git a/ports/stm32/pyb_i2c.c b/ports/stm32/pyb_i2c.c index 0529d3bd56e40..7e1489010d097 100644 --- a/ports/stm32/pyb_i2c.c +++ b/ports/stm32/pyb_i2c.c @@ -428,6 +428,15 @@ int pyb_i2c_init_freq(const pyb_i2c_obj_t *self, mp_int_t freq) { return pyb_i2c_init(self->i2c); } +void pyb_i2c_deinit_all(void) { + for (int i = 0; i < MP_ARRAY_SIZE(pyb_i2c_obj); i++) { + const pyb_i2c_obj_t *pyb_i2c = &pyb_i2c_obj[i]; + if (pyb_i2c->i2c != NULL) { + i2c_deinit(pyb_i2c->i2c); + } + } +} + static void i2c_reset_after_error(I2C_HandleTypeDef *i2c) { // wait for bus-busy flag to be cleared, with a timeout for (int timeout = 50; timeout > 0; --timeout) { From 89191b00eae258642cd3d6458bc5440341bbc8d1 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 2 Dec 2024 08:18:28 +0100 Subject: [PATCH 093/546] stm32/main: Deinitialize SPI and I2C on soft-reset. Following UART, CAN, Timer, etc. Signed-off-by: iabdalkader --- ports/stm32/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/stm32/main.c b/ports/stm32/main.c index df483602dc901..ea870ef0da7ca 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -678,6 +678,10 @@ void stm32_main(uint32_t reset_mode) { soft_timer_deinit(); timer_deinit(); uart_deinit_all(); + spi_deinit_all(); + #if MICROPY_PY_PYB_LEGACY && MICROPY_HW_ENABLE_HW_I2C + pyb_i2c_deinit_all(); + #endif #if MICROPY_HW_ENABLE_CAN can_deinit_all(); #endif From fd01cdd203b1049fec35a82ba3ddd89c4bd62a67 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 1 Dec 2024 08:55:34 +0100 Subject: [PATCH 094/546] stm32/mpconfigport: Switch FATFS LFN to type 2. LFN type 2 uses the stack to allocate the internal working buffer for LFN, which is thread-safe and saves about 512 bytes of BSS memory (at the expense of needing that much memory on the stack). Signed-off-by: iabdalkader --- ports/stm32/mpconfigport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index c6ba83d1bd293..dc0a767fb07ee 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -157,7 +157,7 @@ #endif // fatfs configuration used in ffconf.h -#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_ENABLE_LFN (2) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #define MICROPY_FATFS_USE_LABEL (1) #define MICROPY_FATFS_RPATH (2) From 309aa268113bd1779433bb36f3a55f81fd129979 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 1 Dec 2024 08:58:02 +0100 Subject: [PATCH 095/546] rp2/mpconfigport: Switch FATFS LFN to type 2. LFN type 2 uses the stack to allocate the internal working buffer for LFN, which is thread-safe and saves about 512 bytes of BSS memory (at the expense of needing that much memory on the stack). Signed-off-by: iabdalkader --- ports/rp2/mpconfigport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 15eeab4f34b07..8f4e846ba13bb 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -178,7 +178,7 @@ #define MICROPY_HW_SOFT_TIMER_ALARM_NUM (2) // fatfs configuration -#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_ENABLE_LFN (2) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #define MICROPY_FATFS_RPATH (2) #if MICROPY_HW_USB_MSC From a2cdf9a95fea33ade5c608885424d6aae21773f5 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 1 Dec 2024 08:58:24 +0100 Subject: [PATCH 096/546] renesas-ra/mpconfigport: Switch FATFS LFN to type 2. LFN type 2 uses the stack to allocate the internal working buffer for LFN, which is thread-safe and saves about 512 bytes of BSS memory (at the expense of needing that much memory on the stack). Signed-off-by: iabdalkader --- ports/renesas-ra/mpconfigport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/renesas-ra/mpconfigport.h b/ports/renesas-ra/mpconfigport.h index 307dac24ce583..bd936061fa406 100644 --- a/ports/renesas-ra/mpconfigport.h +++ b/ports/renesas-ra/mpconfigport.h @@ -167,7 +167,7 @@ #endif // fatfs configuration used in ffconf.h -#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_ENABLE_LFN (2) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #define MICROPY_FATFS_USE_LABEL (1) #define MICROPY_FATFS_RPATH (2) From 2bee8e1b8cdaae0ac387c03e88a573e35256b092 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 1 Dec 2024 08:59:32 +0100 Subject: [PATCH 097/546] mimxrt/mpconfigport: Update FATFS config to align with other ports. Make this port use the same FATFS config as stm32, rp2, renesas-ra. Signed-off-by: iabdalkader --- ports/mimxrt/mpconfigport.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 31444e4980be5..146c86015afd2 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -118,10 +118,12 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_ONEWIRE (1) // fatfs configuration used in ffconf.h -#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_ENABLE_LFN (2) +#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ +#define MICROPY_FATFS_USE_LABEL (1) #define MICROPY_FATFS_RPATH (2) +#define MICROPY_FATFS_MULTI_PARTITION (1) #define MICROPY_FATFS_MAX_SS (4096) -#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #ifndef MICROPY_PY_NETWORK #define MICROPY_PY_NETWORK (1) From b20687d0e71360bf76eccedbb9d2c1f73697693a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 3 Dec 2024 10:02:46 +1100 Subject: [PATCH 098/546] esp32: Fix link failure due to link library order. When a wrapped symbol is provided in its own file, it's possible for the linker to skip that file entirely and not return to it depending on the order of libraries passed on the linker command line. This is because these wrapped symbols create linker cycles (libmain_espXX depends on liblwip but liblwip now also depends on libmain for the wrapped functions in lwip_patch.c, for example.) Linker failure for symbols in lwip_patch.c was reproducible if mDNS was disabled in the board configuration. This commit adds an explicit undefined symbol for each file, to ensure the linker will add the wrapped objects on its first pass. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/esp32_common.cmake | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 9d51a03aa856e..0a6b29983c2f5 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -242,12 +242,19 @@ target_include_directories(${MICROPY_TARGET} PUBLIC target_link_libraries(${MICROPY_TARGET} micropy_extmod_btree) target_link_libraries(${MICROPY_TARGET} usermod) -# Enable the panic handler wrapper -idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) - -# Patch LWIP memory pool allocators (see lwip_patch.c) -idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_malloc" APPEND) -idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_free" APPEND) +# Extra linker options +# (when wrap symbols are in standalone files, --undefined ensures +# the linker doesn't skip that file.) +target_link_options(${MICROPY_TARGET} PUBLIC + # Patch LWIP memory pool allocators (see lwip_patch.c) + -Wl,--undefined=memp_malloc + -Wl,--wrap=memp_malloc + -Wl,--wrap=memp_free + + # Enable the panic handler wrapper + -Wl,--undefined=esp_panic_handler + -Wl,--wrap=esp_panic_handler +) # Collect all of the include directories and compile definitions for the IDF components, # including those added by the IDF Component Manager via idf_components.yaml. From 5564f3042ca850e363e6644b3df0b006785fcf2d Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 28 Aug 2024 09:21:34 +1000 Subject: [PATCH 099/546] esp32: Add basic espressif IDF v5.3 compatibility. Signed-off-by: Andrew Leech Signed-off-by: Damien George --- ports/esp32/README.md | 2 +- ports/esp32/network_wlan.c | 7 ++++++- ports/esp32/usb_serial_jtag.c | 11 +++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 4eb791389380c..9f842a5a52cd6 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, v5.2.2. +Currently MicroPython supports v5.0.4, v5.0.5, v5.1.2, v5.2.0, v5.2.2, v5.3. 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). diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index 6509b4fc6aaed..d52123ee0a3a2 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -768,6 +768,9 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_EXT_PSK), MP_ROM_INT(WIFI_AUTH_WPA3_EXT_PSK) }, { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_EXT_PSK_MIXED_MODE), MP_ROM_INT(WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE) }, #endif + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + { MP_ROM_QSTR(MP_QSTR_SEC_DPP), MP_ROM_INT(WIFI_AUTH_DPP) }, + #endif { MP_ROM_QSTR(MP_QSTR_PM_NONE), MP_ROM_INT(WIFI_PS_NONE) }, { MP_ROM_QSTR(MP_QSTR_PM_PERFORMANCE), MP_ROM_INT(WIFI_PS_MIN_MODEM) }, @@ -775,7 +778,9 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { }; static MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) +_Static_assert(WIFI_AUTH_MAX == 14, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types_generic.h"); +#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) _Static_assert(WIFI_AUTH_MAX == 13, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); #elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 5) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) || ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) _Static_assert(WIFI_AUTH_MAX == 11, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); diff --git a/ports/esp32/usb_serial_jtag.c b/ports/esp32/usb_serial_jtag.c index 32eb806e72959..c4834e56f962d 100644 --- a/ports/esp32/usb_serial_jtag.c +++ b/ports/esp32/usb_serial_jtag.c @@ -35,10 +35,13 @@ #include "soc/periph_defs.h" #include "freertos/portmacro.h" -#define USB_SERIAL_JTAG_BUF_SIZE (64) +// Number of bytes in the input buffer, and number of bytes for output chunking. +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) +#define USB_SERIAL_JTAG_PACKET_SZ_BYTES (64) +#endif static DRAM_ATTR portMUX_TYPE rx_mux = portMUX_INITIALIZER_UNLOCKED; -static uint8_t rx_buf[USB_SERIAL_JTAG_BUF_SIZE]; +static uint8_t rx_buf[USB_SERIAL_JTAG_PACKET_SZ_BYTES]; static volatile bool terminal_connected = false; static void usb_serial_jtag_handle_rx(void) { @@ -48,8 +51,8 @@ static void usb_serial_jtag_handle_rx(void) { portENTER_CRITICAL(&rx_mux); } size_t req_len = ringbuf_free(&stdin_ringbuf); - if (req_len > USB_SERIAL_JTAG_BUF_SIZE) { - req_len = USB_SERIAL_JTAG_BUF_SIZE; + if (req_len > USB_SERIAL_JTAG_PACKET_SZ_BYTES) { + req_len = USB_SERIAL_JTAG_PACKET_SZ_BYTES; } size_t len = usb_serial_jtag_ll_read_rxfifo(rx_buf, req_len); for (size_t i = 0; i < len; ++i) { From 5fb846df67189363904dcbb9c2bc93d0f287ccd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Tue, 3 Dec 2024 11:08:13 +0100 Subject: [PATCH 100/546] esp32: Fix machine_touchpad compiling on IDFv5.3. Signed-off-by: Damien George --- ports/esp32/machine_touchpad.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/ports/esp32/machine_touchpad.c b/ports/esp32/machine_touchpad.c index 48250280badaf..299c489f5a831 100644 --- a/ports/esp32/machine_touchpad.c +++ b/ports/esp32/machine_touchpad.c @@ -31,9 +31,17 @@ #if SOC_TOUCH_SENSOR_SUPPORTED -#if SOC_TOUCH_VERSION_1 // ESP32 only +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) +#if SOC_TOUCH_VERSION_1 +#define SOC_TOUCH_SENSOR_VERSION (1) +#elif SOC_TOUCH_VERSION_2 +#define SOC_TOUCH_SENSOR_VERSION (2) +#endif +#endif + +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 only #include "driver/touch_pad.h" -#elif SOC_TOUCH_VERSION_2 // All other SoCs with touch, to date +#elif SOC_TOUCH_SENSOR_VERSION == 2 // All other SoCs with touch, to date #include "driver/touch_sensor.h" #else #error "Unknown touch hardware version" @@ -98,13 +106,13 @@ static mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); initialized = 1; } - #if SOC_TOUCH_VERSION_1 + #if SOC_TOUCH_SENSOR_VERSION == 1 esp_err_t err = touch_pad_config(self->touchpad_id, 0); - #elif SOC_TOUCH_VERSION_2 + #elif SOC_TOUCH_SENSOR_VERSION == 2 esp_err_t err = touch_pad_config(self->touchpad_id); #endif if (err == ESP_OK) { - #if SOC_TOUCH_VERSION_2 + #if SOC_TOUCH_SENSOR_VERSION == 2 touch_pad_fsm_start(); #endif @@ -115,10 +123,10 @@ static mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ static mp_obj_t mtp_config(mp_obj_t self_in, mp_obj_t value_in) { mtp_obj_t *self = self_in; - #if SOC_TOUCH_VERSION_1 + #if SOC_TOUCH_SENSOR_VERSION == 1 uint16_t value = mp_obj_get_int(value_in); esp_err_t err = touch_pad_config(self->touchpad_id, value); - #elif SOC_TOUCH_VERSION_2 + #elif SOC_TOUCH_SENSOR_VERSION == 2 esp_err_t err = touch_pad_config(self->touchpad_id); #endif if (err == ESP_OK) { @@ -130,10 +138,10 @@ MP_DEFINE_CONST_FUN_OBJ_2(mtp_config_obj, mtp_config); static mp_obj_t mtp_read(mp_obj_t self_in) { mtp_obj_t *self = self_in; - #if SOC_TOUCH_VERSION_1 + #if SOC_TOUCH_SENSOR_VERSION == 1 uint16_t value; esp_err_t err = touch_pad_read(self->touchpad_id, &value); - #elif SOC_TOUCH_VERSION_2 + #elif SOC_TOUCH_SENSOR_VERSION == 2 uint32_t value; esp_err_t err = touch_pad_read_raw_data(self->touchpad_id, &value); #endif From 160a4812cdd846f82f1f0bfb3798ada760d84d22 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 3 Dec 2024 11:00:24 +1100 Subject: [PATCH 101/546] esp32: Pass V=1 or BUILD_VERBOSE through to idf.py when building. Allows verbose build to work the same on esp32 port as other ports. To minimise copy/paste, split the BUILD_VERBOSE section of mkenv.mk out to its own verbose.mk and include this in the port Makefile. Signed-off-by: Angus Gratton --- ports/esp32/Makefile | 6 ++++++ py/mkenv.mk | 17 +---------------- py/verbose.mk | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 py/verbose.mk diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 4ad3cf0267cda..8888180766b79 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -2,6 +2,8 @@ # # This is a simple, convenience wrapper around idf.py (which uses cmake). +include ../../py/verbose.mk + # Select the board to build for: ifdef BOARD_DIR # Custom board path - remove trailing slash and get the final component of @@ -61,6 +63,10 @@ ifdef MICROPY_PREVIEW_VERSION_2 IDFPY_FLAGS += -D MICROPY_PREVIEW_VERSION_2=1 endif +ifeq ($(BUILD_VERBOSE),1) + IDFPY_FLAGS += --verbose +endif + HELP_BUILD_ERROR ?= "See \033[1;31mhttps://github.com/micropython/micropython/wiki/Build-Troubleshooting\033[0m" define RUN_IDF_PY diff --git a/py/mkenv.mk b/py/mkenv.mk index b52dafbc9d0f6..4656d11366583 100644 --- a/py/mkenv.mk +++ b/py/mkenv.mk @@ -12,22 +12,7 @@ endif THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST)) TOP := $(patsubst %/py/mkenv.mk,%,$(THIS_MAKEFILE)) -# Turn on increased build verbosity by defining BUILD_VERBOSE in your main -# Makefile or in your environment. You can also use V=1 on the make command -# line. - -ifeq ("$(origin V)", "command line") -BUILD_VERBOSE=$(V) -endif -ifndef BUILD_VERBOSE -$(info Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.) -BUILD_VERBOSE = 0 -endif -ifeq ($(BUILD_VERBOSE),0) -Q = @ -else -Q = -endif +include $(TOP)/py/verbose.mk # default settings; can be overridden in main Makefile diff --git a/py/verbose.mk b/py/verbose.mk new file mode 100644 index 0000000000000..734623a21e80f --- /dev/null +++ b/py/verbose.mk @@ -0,0 +1,16 @@ +# Turn on increased build verbosity by defining BUILD_VERBOSE in your main +# Makefile or in your environment. You can also use V=1 on the make command +# line. + +ifeq ("$(origin V)", "command line") +BUILD_VERBOSE=$(V) +endif +ifndef BUILD_VERBOSE +$(info Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.) +BUILD_VERBOSE = 0 +endif +ifeq ($(BUILD_VERBOSE),0) +Q = @ +else +Q = +endif From 31a1e2b96decb9b5945461d808f725d40720d3c4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 3 Dec 2024 11:22:49 +1100 Subject: [PATCH 102/546] rp2: Pass V=1 or BUILD_VERBOSE to rp2 build. Similar to esp32. Previously rp2 could build verbose by passing VERBOSE=1, which is picked up by Makefiles generated from CMake. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile index f950bc7b90898..afa21cc7a4383 100644 --- a/ports/rp2/Makefile +++ b/ports/rp2/Makefile @@ -2,6 +2,8 @@ # # This is a simple wrapper around cmake +include ../../py/verbose.mk + # Select the board to build for: ifdef BOARD_DIR # Custom board path - remove trailing slash and get the final component of @@ -28,7 +30,9 @@ else BUILD ?= build-$(BOARD) endif -$(VERBOSE)MAKESILENT = -s +ifeq ($(BUILD_VERBOSE),1) +MAKE_ARGS += VERBOSE=1 # Picked up in Makefile generated by CMake +endif CMAKE_ARGS += -DMICROPY_BOARD=$(BOARD) -DMICROPY_BOARD_DIR="$(abspath $(BOARD_DIR))" @@ -56,7 +60,7 @@ HELP_BUILD_ERROR ?= "See \033[1;31mhttps://github.com/micropython/micropython/wi all: [ -e $(BUILD)/Makefile ] || cmake -S . -B $(BUILD) -DPICO_BUILD_DOCS=0 ${CMAKE_ARGS} - $(MAKE) $(MAKESILENT) -C $(BUILD) || (echo -e $(HELP_BUILD_ERROR); false) + $(MAKE) $(MAKE_ARGS) -C $(BUILD) || (echo -e $(HELP_BUILD_ERROR); false) clean: $(RM) -rf $(BUILD) From 0f7d68043fce3c305ad68cba06e4126628048f3e Mon Sep 17 00:00:00 2001 From: stijn Date: Tue, 3 Dec 2024 13:24:47 +0100 Subject: [PATCH 103/546] py/misc: Fix msvc and C++ compatibility. Use an explicit cast to suppress the implicit conversion which started popping up in recent compiler versions (and wasn't there yet in 07bf3179). Signed-off-by: stijn --- py/misc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/misc.h b/py/misc.h index 8ac80bb7b3e34..2629d0c46cc53 100644 --- a/py/misc.h +++ b/py/misc.h @@ -357,7 +357,7 @@ static inline uint32_t mp_clzll(unsigned long long x) { // Microsoft don't ship _BitScanReverse64 on Win32, so emulate it static inline uint32_t mp_clzll(unsigned long long x) { unsigned long h = x >> 32; - return h ? mp_clzl(h) : (mp_clzl(x) + 32); + return h ? mp_clzl(h) : (mp_clzl((unsigned long)x) + 32); } #endif From 5784714f734caf3713ff880ffe100bbbec59216e Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 18 Nov 2024 22:00:31 +0100 Subject: [PATCH 104/546] shared/runtime/gchelper_generic: Fix AArch32 build on Clang. This commit fixes a compile error happening on Clang when building the generic gchelper code for AArch32. Clang would raise a warning regarding undefined variable access when aliasing a variable to an existing CPU register. The fix is pretty crude but it works - it simply disables the warning in question for the AArch32 gchelper collection function. Care was taken to make sure the code would also compile on GCC without warnings of sorts. Signed-off-by: Alessandro Gatti --- shared/runtime/gchelper_generic.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/shared/runtime/gchelper_generic.c b/shared/runtime/gchelper_generic.c index 0937231374057..45b2e4f7d848a 100644 --- a/shared/runtime/gchelper_generic.c +++ b/shared/runtime/gchelper_generic.c @@ -101,6 +101,10 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { // Fallback implementation, prefer gchelper_thumb1.s or gchelper_thumb2.s static void gc_helper_get_regs(gc_helper_regs_t arr) { + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wuninitialized" + #endif register long r4 asm ("r4"); register long r5 asm ("r5"); register long r6 asm ("r6"); @@ -121,6 +125,9 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { arr[7] = r11; arr[8] = r12; arr[9] = r13; + #ifdef __clang__ + #pragma clang diagnostic pop + #endif } #elif defined(__aarch64__) From 2a8f6047ff5c94bba7ace738a14b572cc8553251 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 18 Nov 2024 22:04:14 +0100 Subject: [PATCH 105/546] py/emitglue: Fix clear cache builtin warning on Clang for AArch32. This commit fixes a warning occurring on Clang when calling `__builtin___clear_cache` with non-void pointers for its start and end memory area locations. The code now uses a char pointer for the end location, and it still builds without warnings on GCC. Signed-off-by: Alessandro Gatti --- py/emitglue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/emitglue.c b/py/emitglue.c index 444e480477e54..27cbb349ef602 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -115,7 +115,7 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons #endif #elif MICROPY_EMIT_ARM #if (defined(__linux__) && defined(__GNUC__)) || __ARM_ARCH == 7 - __builtin___clear_cache((void *)fun_data, (uint8_t *)fun_data + fun_len); + __builtin___clear_cache((void *)fun_data, (char *)fun_data + fun_len); #elif defined(__arm__) // Flush I-cache and D-cache. asm volatile ( From 28b52446669b72f9863902510430bdeeaaa39fa6 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 4 Dec 2024 22:45:28 +0100 Subject: [PATCH 106/546] extmod/modplatform: Distinguish RISC-V 64 from RISC-V 32. This commit lets the platform module report a more accurate architecture name when running on a RISC-V 64 bits platform. Signed-off-by: Alessandro Gatti --- extmod/modplatform.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extmod/modplatform.h b/extmod/modplatform.h index 5c9cbffb2a486..a155f071cba1b 100644 --- a/extmod/modplatform.h +++ b/extmod/modplatform.h @@ -48,7 +48,11 @@ #elif defined(__xtensa__) #define MICROPY_PLATFORM_ARCH "xtensa" #elif defined(__riscv) +#if __riscv_xlen == 64 +#define MICROPY_PLATFORM_ARCH "riscv64" +#else #define MICROPY_PLATFORM_ARCH "riscv" +#endif #else #define MICROPY_PLATFORM_ARCH "" #endif From e8c3f31ba2e5f004dfb972131d5f7d7a961974c2 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 12 Nov 2024 07:16:38 +0100 Subject: [PATCH 107/546] mimxrt/machine_rtc: Deprecate RTC.cancel in MicroPython v2. The current documentation for the `machine.RTC` class contains information about the `RTC.cancel` method for cancelling pending alarms. However only two ports (cc3200 and mimxrt) implement this functionality but under a different name: `RTC.alarm_cancel`. The mimxrt port also implements `RTC.cancel` but it is aliased to `RTC.alarm_cancel` anyway. To maintain naming consistency, this commit updates the documentation to officially define `RTC.alarm_cancel` as the method to call to cancel pending alarms and deprecates mimxrt's `RTC.cancel` implementation. `RTC.cancel` in the mimxrt port is thus scheduled for removal in MicroPython v2. Signed-off-by: Alessandro Gatti --- docs/library/machine.RTC.rst | 5 ++++- ports/mimxrt/machine_rtc.c | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/library/machine.RTC.rst b/docs/library/machine.RTC.rst index ad0b0759efe5e..a457189037b51 100644 --- a/docs/library/machine.RTC.rst +++ b/docs/library/machine.RTC.rst @@ -62,10 +62,13 @@ Methods Get the number of milliseconds left before the alarm expires. -.. method:: RTC.cancel(alarm_id=0) +.. method:: RTC.alarm_cancel(alarm_id=0) Cancel a running alarm. + The mimxrt port also exposes this function as ``RTC.cancel(alarm_id=0)``, but this is + scheduled to be removed in MicroPython 2.0. + .. method:: RTC.irq(*, trigger, handler=None, wake=machine.IDLE) Create an irq object triggered by a real time clock alarm. diff --git a/ports/mimxrt/machine_rtc.c b/ports/mimxrt/machine_rtc.c index 294942cf5a03f..0d6fc54c8b67c 100644 --- a/ports/mimxrt/machine_rtc.c +++ b/ports/mimxrt/machine_rtc.c @@ -394,7 +394,9 @@ static const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_alarm), MP_ROM_PTR(&machine_rtc_alarm_obj) }, { MP_ROM_QSTR(MP_QSTR_alarm_left), MP_ROM_PTR(&machine_rtc_alarm_left_obj) }, { MP_ROM_QSTR(MP_QSTR_alarm_cancel), MP_ROM_PTR(&machine_rtc_alarm_cancel_obj) }, + #if !MICROPY_PREVIEW_VERSION_2 { MP_ROM_QSTR(MP_QSTR_cancel), MP_ROM_PTR(&machine_rtc_alarm_cancel_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_rtc_irq_obj) }, { MP_ROM_QSTR(MP_QSTR_ALARM0), MP_ROM_INT(0) }, }; From fdd606dd5395d9c474775945fa4458a27199653b Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 6 Nov 2024 10:31:28 +1100 Subject: [PATCH 108/546] py/mkrules.mk: Use partial clone for submodules if available. MicroPython relies on a number of submodules for third party and chip vendor libraries. Users need to check these out before building their desired ports and Github Actions CI here needs to clone them all multiple times for every build. Many of these are getting significantly larger over time, slowing down usage and consuming more disk space. Newer versions of git have features to avoid pulling all historic / blob data which can have a significant impact of total data use. This commit uses a standard feature of git to do a partial clone, with automatic fallback to previous behavior on error. Signed-off-by: Andrew Leech --- py/mkrules.mk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/py/mkrules.mk b/py/mkrules.mk index 0dc1cdfe15ac4..875ddee8523b8 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -252,7 +252,11 @@ submodules: $(ECHO) "Updating submodules: $(GIT_SUBMODULES)" ifneq ($(GIT_SUBMODULES),) $(Q)cd $(TOP) && git submodule sync $(GIT_SUBMODULES) - $(Q)cd $(TOP) && git submodule update --init $(GIT_SUBMODULES) + # If available, do blobless partial clones of submodules to save time and space. + # A blobless partial clone lazily fetches data as needed, but has all the metadata available (tags, etc.). + # Fallback to standard submodule update if blobless isn't available (earlier than 2.36.0) + $(Q)cd $(TOP) && git submodule update --init --filter=blob:none $(GIT_SUBMODULES) || \ + git submodule update --init $(GIT_SUBMODULES) endif .PHONY: submodules From 9441ce6e3c8296ca8e6a5e0ddbd94ee5c9f4d7d6 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 5 Sep 2024 02:59:45 +1000 Subject: [PATCH 109/546] esp32: Use capability defines to configure features. This updates esp32 code where appropriate to replace ifdef's based on a list of specific chips with a feature SOC_* definition. This should simplify adding new esp32-* chips in future, deferring chip feature support to the IDF. Signed-off-by: Andrew Leech --- ports/esp32/adc.c | 20 +++++++++----------- ports/esp32/machine_adc.c | 16 ++++++++++++---- ports/esp32/machine_adc_block.c | 9 +++------ ports/esp32/machine_bitstream.c | 2 +- ports/esp32/machine_i2c.c | 4 ++-- ports/esp32/machine_pin.c | 2 +- ports/esp32/modmachine.c | 12 ++++++++---- 7 files changed, 36 insertions(+), 29 deletions(-) diff --git a/ports/esp32/adc.c b/ports/esp32/adc.c index ccf264e5c2ae3..83af6dce0b2a5 100644 --- a/ports/esp32/adc.c +++ b/ports/esp32/adc.c @@ -33,28 +33,26 @@ #define DEFAULT_VREF 1100 void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits) { + if (bits < SOC_ADC_RTC_MIN_BITWIDTH && bits > SOC_ADC_RTC_MAX_BITWIDTH) { + // Invalid value for the current chip, raise exception in the switch below. + bits = -1; + } switch (bits) { - #if CONFIG_IDF_TARGET_ESP32 case 9: - self->width = ADC_WIDTH_BIT_9; + self->width = ADC_BITWIDTH_9; break; case 10: - self->width = ADC_WIDTH_BIT_10; + self->width = ADC_BITWIDTH_10; break; case 11: - self->width = ADC_WIDTH_BIT_11; + self->width = ADC_BITWIDTH_11; break; - #endif - #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32S3 case 12: - self->width = ADC_WIDTH_BIT_12; + self->width = ADC_BITWIDTH_12; break; - #endif - #if CONFIG_IDF_TARGET_ESP32S2 case 13: - self->width = ADC_WIDTH_BIT_13; + self->width = ADC_BITWIDTH_13; break; - #endif default: mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); } diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index dc21b69083c61..be1725c3709a6 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -35,7 +35,7 @@ #define ADCBLOCK1 (&madcblock_obj[0]) #define ADCBLOCK2 (&madcblock_obj[1]) -#if CONFIG_IDF_TARGET_ESP32 +#if SOC_ADC_RTC_MIN_BITWIDTH <= 9 && SOC_ADC_RTC_MAX_BITWIDTH >= 11 #define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS_WIDTH_9_10_11 \ { MP_ROM_QSTR(MP_QSTR_WIDTH_9BIT), MP_ROM_INT(9) }, \ { MP_ROM_QSTR(MP_QSTR_WIDTH_10BIT), MP_ROM_INT(10) }, \ @@ -44,14 +44,14 @@ #define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS_WIDTH_9_10_11 #endif -#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32S3 +#if SOC_ADC_RTC_MIN_BITWIDTH <= 12 && SOC_ADC_RTC_MAX_BITWIDTH >= 12 #define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS_WIDTH_12 \ { MP_ROM_QSTR(MP_QSTR_WIDTH_12BIT), MP_ROM_INT(12) }, #else #define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS_WIDTH_12 #endif -#if CONFIG_IDF_TARGET_ESP32S2 +#if SOC_ADC_RTC_MIN_BITWIDTH <= 13 && SOC_ADC_RTC_MAX_BITWIDTH >= 13 #define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS_WIDTH_13 \ { MP_ROM_QSTR(MP_QSTR_WIDTH_13BIT), MP_ROM_INT(13) }, #else @@ -87,13 +87,21 @@ static const machine_adc_obj_t madc_obj[] = { {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_7, GPIO_NUM_27}, {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_8, GPIO_NUM_25}, {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_9, GPIO_NUM_26}, - #elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 + #elif CONFIG_IDF_TARGET_ESP32C3 {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_0}, {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_1}, {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_2, GPIO_NUM_2}, {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_3, GPIO_NUM_3}, {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_4, GPIO_NUM_4}, {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_0, GPIO_NUM_5}, + #elif CONFIG_IDF_TARGET_ESP32C6 + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_0}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_1}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_2, GPIO_NUM_2}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_3, GPIO_NUM_3}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_4, GPIO_NUM_4}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_5, GPIO_NUM_5}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_6, GPIO_NUM_6}, #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_1}, {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_2}, diff --git a/ports/esp32/machine_adc_block.c b/ports/esp32/machine_adc_block.c index 78c5b2491b8bf..a373603b7e396 100644 --- a/ports/esp32/machine_adc_block.c +++ b/ports/esp32/machine_adc_block.c @@ -32,12 +32,9 @@ #include "driver/adc.h" machine_adc_block_obj_t madcblock_obj[] = { - #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32S3 - {{&machine_adc_block_type}, ADC_UNIT_1, 12, -1, {0}}, - {{&machine_adc_block_type}, ADC_UNIT_2, 12, -1, {0}}, - #elif CONFIG_IDF_TARGET_ESP32S2 - {{&machine_adc_block_type}, ADC_UNIT_1, 13, -1, {0}}, - {{&machine_adc_block_type}, ADC_UNIT_2, 13, -1, {0}}, + {{&machine_adc_block_type}, ADC_UNIT_1, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}}, + #if SOC_ADC_PERIPH_NUM > 1 + {{&machine_adc_block_type}, ADC_UNIT_2, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}}, #endif }; diff --git a/ports/esp32/machine_bitstream.c b/ports/esp32/machine_bitstream.c index b4e58c51f4d19..6296ff06708c1 100644 --- a/ports/esp32/machine_bitstream.c +++ b/ports/esp32/machine_bitstream.c @@ -42,7 +42,7 @@ // This is a translation of the cycle counter implementation in ports/stm32/machine_bitstream.c. static void IRAM_ATTR machine_bitstream_high_low_bitbang(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len) { uint32_t pin_mask, gpio_reg_set, gpio_reg_clear; - #if !CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 + #if SOC_GPIO_PIN_COUNT > 32 if (pin >= 32) { pin_mask = 1 << (pin - 32); gpio_reg_set = GPIO_OUT1_W1TS_REG; diff --git a/ports/esp32/machine_i2c.c b/ports/esp32/machine_i2c.c index e1e1850da57cd..732a62f47f193 100644 --- a/ports/esp32/machine_i2c.c +++ b/ports/esp32/machine_i2c.c @@ -49,9 +49,9 @@ #endif #endif -#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32S3 +#if SOC_I2C_SUPPORT_XTAL #define I2C_SCLK_FREQ XTAL_CLK_FREQ -#elif CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 +#elif SOC_I2C_SUPPORT_APB #define I2C_SCLK_FREQ APB_CLK_FREQ #else #error "unsupported I2C for ESP32 SoC variant" diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index 687dfe1938dae..74202d6f758cd 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -43,7 +43,7 @@ #include "modesp32.h" #include "genhdr/pins.h" -#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 +#if SOC_USB_SERIAL_JTAG_SUPPORTED #include "soc/usb_serial_jtag_reg.h" #endif diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 9b58d77529be2..e775b1d146906 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -45,7 +45,7 @@ #define MICROPY_PY_MACHINE_SDCARD_ENTRY #endif -#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#if SOC_TOUCH_SENSOR_SUPPORTED #define MICROPY_PY_MACHINE_TOUCH_PAD_ENTRY { MP_ROM_QSTR(MP_QSTR_TouchPad), MP_ROM_PTR(&machine_touchpad_type) }, #else #define MICROPY_PY_MACHINE_TOUCH_PAD_ENTRY @@ -147,30 +147,34 @@ static void machine_sleep_helper(wake_type_t wake_type, size_t n_args, const mp_ esp_sleep_enable_timer_wakeup(((uint64_t)expiry) * 1000); } - #if !(CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6) - + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin != -1 && (machine_rtc_config.ext0_wake_types & wake_type)) { esp_sleep_enable_ext0_wakeup(machine_rtc_config.ext0_pin, machine_rtc_config.ext0_level ? 1 : 0); } + #endif + #if SOC_PM_SUPPORT_EXT1_WAKEUP if (machine_rtc_config.ext1_pins != 0) { esp_sleep_enable_ext1_wakeup( machine_rtc_config.ext1_pins, machine_rtc_config.ext1_level ? ESP_EXT1_WAKEUP_ANY_HIGH : ESP_EXT1_WAKEUP_ALL_LOW); } + #endif + #if SOC_TOUCH_SENSOR_SUPPORTED if (machine_rtc_config.wake_on_touch) { if (esp_sleep_enable_touchpad_wakeup() != ESP_OK) { mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("esp_sleep_enable_touchpad_wakeup() failed")); } } + #endif + #if SOC_ULP_SUPPORTED if (machine_rtc_config.wake_on_ulp) { if (esp_sleep_enable_ulp_wakeup() != ESP_OK) { mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("esp_sleep_enable_ulp_wakeup() failed")); } } - #endif switch (wake_type) { From 82e382a399b07e0af64516749c12882fc0804a6d Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 4 Dec 2024 12:30:25 +0100 Subject: [PATCH 110/546] esp32/mpconfigport: Use the appropriate wait-for-interrupt opcode. When threading is disabled, the pending events handling code would wait for an incoming interrupt once there's no more work to do. This bit of code was Xtensa-specific and wouldn't compile on a RISC-V based MCU. This commit provides the RISC-V equivalent to that part of the code, allowing to make threadless MicroPython builds on RISC-V based MCUs. Signed-off-by: Alessandro Gatti --- ports/esp32/mpconfigport.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index e36d12bc056ac..bf1f5a18641db 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -292,12 +292,17 @@ void *esp_native_code_commit(void *, size_t, void *); MP_THREAD_GIL_ENTER(); \ } while (0); #else +#if CONFIG_IDF_TARGET_ARCH_RISCV +#define MICROPY_PY_WAIT_FOR_INTERRUPT asm volatile ("wfi\n") +#else +#define MICROPY_PY_WAIT_FOR_INTERRUPT asm volatile ("waiti 0\n") +#endif #define MICROPY_EVENT_POLL_HOOK \ do { \ extern void mp_handle_pending(bool); \ mp_handle_pending(true); \ MICROPY_PY_SOCKET_EVENTS_HANDLER \ - asm ("waiti 0"); \ + MICROPY_PY_WAIT_FOR_INTERRUPT; \ } while (0); #endif From 6e5d8d009349a7d7932ab0c93a739a576a3594d3 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 1 Nov 2024 16:32:48 +1100 Subject: [PATCH 111/546] esp32: Drop support for ESP-IDF below V5.2.0. Specifically, remove all conditional compilation for these earlier versions and change the idf_component.yml specifiers to require >=5.2.0. Signed-off-by: Angus Gratton --- ports/esp32/README.md | 2 +- ports/esp32/machine_dac.c | 27 -------------- ports/esp32/machine_pwm.c | 43 ---------------------- ports/esp32/machine_uart.c | 4 -- ports/esp32/main_esp32/idf_component.yml | 2 +- ports/esp32/main_esp32c3/idf_component.yml | 2 +- ports/esp32/main_esp32c6/idf_component.yml | 2 +- ports/esp32/main_esp32s2/idf_component.yml | 2 +- ports/esp32/main_esp32s3/idf_component.yml | 2 +- ports/esp32/modesp32.c | 1 - ports/esp32/modmachine.c | 23 +++--------- ports/esp32/modnetwork_globals.h | 6 --- ports/esp32/mpthreadport.c | 2 +- ports/esp32/network_lan.c | 2 - ports/esp32/network_wlan.c | 12 +----- ports/esp32/uart.c | 4 -- 16 files changed, 13 insertions(+), 123 deletions(-) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 9f842a5a52cd6..a8bada7510f4e 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, v5.2.2, v5.3. +Currently MicroPython supports v5.2, v5.2.2, and v5.3. 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). diff --git a/ports/esp32/machine_dac.c b/ports/esp32/machine_dac.c index 3d1d2df80818a..1c5b7c914cafc 100644 --- a/ports/esp32/machine_dac.c +++ b/ports/esp32/machine_dac.c @@ -34,21 +34,13 @@ #if MICROPY_PY_MACHINE_DAC #include "driver/gpio.h" -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) #include "driver/dac_oneshot.h" -#else -#include "driver/dac.h" -#define DAC_CHAN_0 DAC_CHANNEL_1 -#define DAC_CHAN_1 DAC_CHANNEL_2 -#endif typedef struct _mdac_obj_t { mp_obj_base_t base; gpio_num_t gpio_id; dac_channel_t dac_id; - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) dac_oneshot_handle_t dac_oneshot_handle; - #endif } mdac_obj_t; static mdac_obj_t mdac_obj[] = { @@ -77,21 +69,10 @@ static mp_obj_t mdac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n mp_raise_ValueError(MP_ERROR_TEXT("invalid Pin for DAC")); } - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) dac_oneshot_config_t dac_oneshot_config = {.chan_id = self->dac_id}; check_esp_err(dac_oneshot_new_channel(&dac_oneshot_config, (dac_oneshot_handle_t *)&self->dac_oneshot_handle)); check_esp_err(dac_oneshot_output_voltage(self->dac_oneshot_handle, 0)); return MP_OBJ_FROM_PTR(self); - #else - esp_err_t err = dac_output_enable(self->dac_id); - if (err == ESP_OK) { - err = dac_output_voltage(self->dac_id, 0); - } - if (err == ESP_OK) { - return MP_OBJ_FROM_PTR(self); - } - mp_raise_ValueError(MP_ERROR_TEXT("parameter error")); - #endif } static void mdac_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { @@ -106,16 +87,8 @@ static mp_obj_t mdac_write(mp_obj_t self_in, mp_obj_t value_in) { mp_raise_ValueError(MP_ERROR_TEXT("value out of range")); } - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) check_esp_err(dac_oneshot_output_voltage(self->dac_oneshot_handle, value)); return mp_const_none; - #else - esp_err_t err = dac_output_voltage(self->dac_id, value); - if (err == ESP_OK) { - return mp_const_none; - } - mp_raise_ValueError(MP_ERROR_TEXT("parameter error")); - #endif } MP_DEFINE_CONST_FUN_OBJ_2(mdac_write_obj, mdac_write); diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 0134fa2cc8c89..6e3610b156679 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -211,39 +211,6 @@ static void configure_channel(machine_pwm_obj_t *self) { } } -// Temporary workaround for ledc_find_suitable_duty_resolution function only being added in IDF V5.2 -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0) -static uint32_t ledc_find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) { - // This implementation is based on the one used in Micropython v1.23 - - // Find the highest bit resolution for the requested frequency - unsigned int freq = src_clk_freq; - - int divider = (freq + timer_freq / 2) / timer_freq; // rounded - if (divider == 0) { - divider = 1; - } - float f = (float)freq / divider; // actual frequency - if (f <= 1.0) { - f = 1.0; - } - freq = (unsigned int)roundf((float)freq / f); - - unsigned int res = 0; - for (; freq > 1; freq >>= 1) { - ++res; - } - if (res == 0) { - res = 1; - } else if (res > HIGHEST_PWM_RES) { - // Limit resolution to HIGHEST_PWM_RES to match units of our duty - res = HIGHEST_PWM_RES; - } - - return res; -} -#endif - static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) { esp_err_t err; if (freq != timer->freq_hz) { @@ -265,20 +232,10 @@ static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf } #endif uint32_t src_clk_freq = 0; - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq); if (err != ESP_OK) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer->clk_cfg); } - #else - // Simplified fallback logic for IDF V5.0.x, for targets with APB only. - src_clk_freq = APB_CLK_FREQ; // 80 MHz - #if SOC_LEDC_SUPPORT_REF_TICK - if (timer->clk_cfg == LEDC_USE_REF_TICK) { - src_clk_freq = REF_CLK_FREQ; // 1 MHz - } - #endif // SOC_LEDC_SUPPORT_REF_TICK - #endif // ESP_IDF_VERSION timer->duty_resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer->freq_hz); diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index cff942f39e723..73089ef463681 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -399,11 +399,7 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, } self->flowcontrol = args[ARG_flow].u_int; } - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) uint8_t uart_fifo_len = UART_HW_FIFO_LEN(self->uart_num); - #else - uint8_t uart_fifo_len = UART_FIFO_LEN; - #endif check_esp_err(uart_set_hw_flow_ctrl(self->uart_num, self->flowcontrol, uart_fifo_len - uart_fifo_len / 4)); } diff --git a/ports/esp32/main_esp32/idf_component.yml b/ports/esp32/main_esp32/idf_component.yml index 5dabbc00ba735..11f078f69f761 100644 --- a/ports/esp32/main_esp32/idf_component.yml +++ b/ports/esp32/main_esp32/idf_component.yml @@ -2,4 +2,4 @@ dependencies: espressif/mdns: "~1.1.0" idf: - version: ">=5.0.4" + version: ">=5.2.0" diff --git a/ports/esp32/main_esp32c3/idf_component.yml b/ports/esp32/main_esp32c3/idf_component.yml index 5dabbc00ba735..11f078f69f761 100644 --- a/ports/esp32/main_esp32c3/idf_component.yml +++ b/ports/esp32/main_esp32c3/idf_component.yml @@ -2,4 +2,4 @@ dependencies: espressif/mdns: "~1.1.0" idf: - version: ">=5.0.4" + version: ">=5.2.0" diff --git a/ports/esp32/main_esp32c6/idf_component.yml b/ports/esp32/main_esp32c6/idf_component.yml index 5bbab6d8c3bd8..11f078f69f761 100644 --- a/ports/esp32/main_esp32c6/idf_component.yml +++ b/ports/esp32/main_esp32c6/idf_component.yml @@ -2,4 +2,4 @@ dependencies: espressif/mdns: "~1.1.0" idf: - version: ">=5.1.0" + version: ">=5.2.0" diff --git a/ports/esp32/main_esp32s2/idf_component.yml b/ports/esp32/main_esp32s2/idf_component.yml index 05ab2f29a8d31..2ee00b287786b 100644 --- a/ports/esp32/main_esp32s2/idf_component.yml +++ b/ports/esp32/main_esp32s2/idf_component.yml @@ -3,4 +3,4 @@ dependencies: espressif/mdns: "~1.1.0" espressif/esp_tinyusb: "~1.0.0" idf: - version: ">=5.0.4" + version: ">=5.2.0" diff --git a/ports/esp32/main_esp32s3/idf_component.yml b/ports/esp32/main_esp32s3/idf_component.yml index 05ab2f29a8d31..2ee00b287786b 100644 --- a/ports/esp32/main_esp32s3/idf_component.yml +++ b/ports/esp32/main_esp32s3/idf_component.yml @@ -3,4 +3,4 @@ dependencies: espressif/mdns: "~1.1.0" espressif/esp_tinyusb: "~1.0.0" idf: - version: ">=5.0.4" + version: ">=5.2.0" diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 0a73a0faf06ef..164b1918261f4 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -53,7 +53,6 @@ static mp_obj_t esp32_wake_on_touch(const mp_obj_t wake) { if (machine_rtc_config.ext0_pin != -1) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } - // mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("touchpad wakeup not available for this version of ESP-IDF")); machine_rtc_config.wake_on_touch = mp_obj_is_true(wake); return mp_const_none; diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index e775b1d146906..0c1b94d02d940 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -110,24 +110,11 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { mp_raise_ValueError(MP_ERROR_TEXT("frequency must be 20MHz, 40MHz, 80Mhz, 160MHz or 240MHz")); #endif } - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) - esp_pm_config_t pm; - #else - #if CONFIG_IDF_TARGET_ESP32 - esp_pm_config_esp32_t pm; - #elif CONFIG_IDF_TARGET_ESP32C3 - esp_pm_config_esp32c3_t pm; - #elif CONFIG_IDF_TARGET_ESP32C6 - esp_pm_config_esp32c6_t pm; - #elif CONFIG_IDF_TARGET_ESP32S2 - esp_pm_config_esp32s2_t pm; - #elif CONFIG_IDF_TARGET_ESP32S3 - esp_pm_config_esp32s3_t pm; - #endif - #endif - pm.max_freq_mhz = freq; - pm.min_freq_mhz = freq; - pm.light_sleep_enable = false; + esp_pm_config_t pm = { + .max_freq_mhz = freq, + .min_freq_mhz = freq, + .light_sleep_enable = false, + }; esp_err_t ret = esp_pm_configure(&pm); if (ret != ESP_OK) { mp_raise_ValueError(NULL); diff --git a/ports/esp32/modnetwork_globals.h b/ports/esp32/modnetwork_globals.h index 2bfc2393d666f..1aad785ee3d9c 100644 --- a/ports/esp32/modnetwork_globals.h +++ b/ports/esp32/modnetwork_globals.h @@ -33,13 +33,9 @@ { MP_ROM_QSTR(MP_QSTR_AUTH_WPA2_WPA3_PSK), MP_ROM_INT(WIFI_AUTH_WPA2_WPA3_PSK) }, { MP_ROM_QSTR(MP_QSTR_AUTH_WAPI_PSK), MP_ROM_INT(WIFI_AUTH_WAPI_PSK) }, { MP_ROM_QSTR(MP_QSTR_AUTH_OWE), MP_ROM_INT(WIFI_AUTH_OWE) }, -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 5) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) || ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) { MP_ROM_QSTR(MP_QSTR_AUTH_WPA3_ENT_192), MP_ROM_INT(WIFI_AUTH_WPA3_ENT_192) }, -#endif -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) { MP_ROM_QSTR(MP_QSTR_AUTH_WPA3_EXT_PSK), MP_ROM_INT(WIFI_AUTH_WPA3_EXT_PSK) }, { MP_ROM_QSTR(MP_QSTR_AUTH_WPA3_EXT_PSK_MIXED_MODE), MP_ROM_INT(WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE) }, -#endif { MP_ROM_QSTR(MP_QSTR_AUTH_MAX), MP_ROM_INT(WIFI_AUTH_MAX) }, #endif @@ -75,11 +71,9 @@ { MP_ROM_QSTR(MP_QSTR_STAT_GOT_IP), MP_ROM_INT(STAT_GOT_IP)}, // Errors from the ESP-IDF { MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND), MP_ROM_INT(WIFI_REASON_NO_AP_FOUND)}, -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) { MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND_IN_RSSI_THRESHOLD), MP_ROM_INT(WIFI_REASON_NO_AP_FOUND_IN_RSSI_THRESHOLD)}, { MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD), MP_ROM_INT(WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD)}, { MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND_W_COMPATIBLE_SECURITY), MP_ROM_INT(WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY)}, -#endif { MP_ROM_QSTR(MP_QSTR_STAT_WRONG_PASSWORD), MP_ROM_INT(WIFI_REASON_AUTH_FAIL)}, { MP_ROM_QSTR(MP_QSTR_STAT_BEACON_TIMEOUT), MP_ROM_INT(WIFI_REASON_BEACON_TIMEOUT)}, #if !MICROPY_PREVIEW_VERSION_2 diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c index eac1e5ea3668d..daed1bdd0edf2 100644 --- a/ports/esp32/mpthreadport.c +++ b/ports/esp32/mpthreadport.c @@ -41,7 +41,7 @@ #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 +#if !CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP #define FREERTOS_TASK_DELETE_HOOK vTaskPreDeletionHook #else #define FREERTOS_TASK_DELETE_HOOK vPortCleanUpTCB diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c index 416e02e12db85..e1a8c957857c4 100644 --- a/ports/esp32/network_lan.c +++ b/ports/esp32/network_lan.c @@ -30,8 +30,6 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "esp_idf_version.h" - #if MICROPY_PY_NETWORK_LAN #include "esp_eth.h" diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index d52123ee0a3a2..fed81d28cb6a6 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -113,7 +113,6 @@ static void network_wlan_wifi_event_handler(void *event_handler_arg, esp_event_b // AP may not exist, or it may have momentarily dropped out; try to reconnect. message = "no AP found"; break; - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) case WIFI_REASON_NO_AP_FOUND_IN_RSSI_THRESHOLD: // No AP with RSSI within given threshold exists, or it may have momentarily dropped out; try to reconnect. message = "no AP with RSSI within threshold found"; @@ -126,7 +125,6 @@ static void network_wlan_wifi_event_handler(void *event_handler_arg, esp_event_b // No AP with compatible security exists, or it may have momentarily dropped out; try to reconnect. message = "no AP with compatible security found"; break; - #endif case WIFI_REASON_AUTH_FAIL: // Password may be wrong, or it just failed to connect; try to reconnect. message = "authentication failed"; @@ -367,14 +365,12 @@ static mp_obj_t network_wlan_status(size_t n_args, const mp_obj_t *args) { return MP_OBJ_NEW_SMALL_INT(STAT_GOT_IP); } else if (wifi_sta_disconn_reason == WIFI_REASON_NO_AP_FOUND) { return MP_OBJ_NEW_SMALL_INT(WIFI_REASON_NO_AP_FOUND); - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) } else if (wifi_sta_disconn_reason == WIFI_REASON_NO_AP_FOUND_IN_RSSI_THRESHOLD) { return MP_OBJ_NEW_SMALL_INT(WIFI_REASON_NO_AP_FOUND_IN_RSSI_THRESHOLD); } else if (wifi_sta_disconn_reason == WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD) { return MP_OBJ_NEW_SMALL_INT(WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD); } else if (wifi_sta_disconn_reason == WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY) { return MP_OBJ_NEW_SMALL_INT(WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY); - #endif } else if ((wifi_sta_disconn_reason == WIFI_REASON_AUTH_FAIL) || (wifi_sta_disconn_reason == WIFI_REASON_CONNECTION_FAIL)) { // wrong password return MP_OBJ_NEW_SMALL_INT(WIFI_REASON_AUTH_FAIL); @@ -761,13 +757,9 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_SEC_WPA2_WPA3), MP_ROM_INT(WIFI_AUTH_WPA2_WPA3_PSK) }, { MP_ROM_QSTR(MP_QSTR_SEC_WAPI), MP_ROM_INT(WIFI_AUTH_WAPI_PSK) }, { MP_ROM_QSTR(MP_QSTR_SEC_OWE), MP_ROM_INT(WIFI_AUTH_OWE) }, - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 5) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) || ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_ENT_192), MP_ROM_INT(WIFI_AUTH_WPA3_ENT_192) }, - #endif - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_EXT_PSK), MP_ROM_INT(WIFI_AUTH_WPA3_EXT_PSK) }, { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_EXT_PSK_MIXED_MODE), MP_ROM_INT(WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE) }, - #endif #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) { MP_ROM_QSTR(MP_QSTR_SEC_DPP), MP_ROM_INT(WIFI_AUTH_DPP) }, #endif @@ -782,10 +774,8 @@ static MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table); _Static_assert(WIFI_AUTH_MAX == 14, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types_generic.h"); #elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) _Static_assert(WIFI_AUTH_MAX == 13, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); -#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 5) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) || ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) -_Static_assert(WIFI_AUTH_MAX == 11, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); #else -_Static_assert(WIFI_AUTH_MAX == 10, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); +#error "Error in macro logic, all supported versions should be covered." #endif MP_DEFINE_CONST_OBJ_TYPE( diff --git a/ports/esp32/uart.c b/ports/esp32/uart.c index 8336268509ecf..491314e04d106 100644 --- a/ports/esp32/uart.c +++ b/ports/esp32/uart.c @@ -51,11 +51,7 @@ static void uart_irq_handler(void *arg); void uart_stdout_init(void) { uart_hal_context_t repl_hal = REPL_HAL_DEFN(); - #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0) - uart_sclk_t sclk; - #else soc_module_clk_t sclk; - #endif uint32_t sclk_freq; uart_hal_get_sclk(&repl_hal, &sclk); // To restore SCLK after uart_hal_init() resets it From d90aff5e13ff208728ac49f88e723abb8fbff044 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 1 Nov 2024 16:29:50 +1100 Subject: [PATCH 112/546] esp32: Remove IDF-version-specific sdkconfig. This reverts commit 27279e69b4d74b255bd8d8c5dd6668c97c942776 (plus removes some additional references to the SDKCONFIG_IDF_VERSION_SPECIFIC CMake variable.) Relevant sdkconfig options are added into sdkconfig.base now that IDF >=5.2.0 is required. Signed-off-by: Angus Gratton --- ports/esp32/CMakeLists.txt | 10 ---------- .../boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake | 1 - ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake | 1 - .../esp32/boards/ESP32_GENERIC_C3/mpconfigboard.cmake | 1 - .../esp32/boards/ESP32_GENERIC_C6/mpconfigboard.cmake | 1 - .../esp32/boards/ESP32_GENERIC_S2/mpconfigboard.cmake | 1 - .../esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake | 1 - .../boards/LILYGO_TTGO_LORA32/mpconfigboard.cmake | 1 - ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.cmake | 1 - ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.cmake | 1 - ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.cmake | 1 - ports/esp32/boards/M5STACK_ATOM/mpconfigboard.cmake | 1 - ports/esp32/boards/M5STACK_NANOC6/mpconfigboard.cmake | 1 - .../esp32/boards/OLIMEX_ESP32_EVB/mpconfigboard.cmake | 1 - .../esp32/boards/OLIMEX_ESP32_POE/mpconfigboard.cmake | 1 - ports/esp32/boards/SIL_WESP32/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_FEATHERS2/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_NANOS3/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_OMGS3/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_PROS3/mpconfigboard.cmake | 1 - .../esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_TINYC6/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_TINYPICO/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_TINYS2/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake | 1 - ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.cmake | 1 - ports/esp32/boards/sdkconfig.base | 3 ++- ports/esp32/boards/sdkconfig.idf52 | 2 -- 31 files changed, 2 insertions(+), 41 deletions(-) delete mode 100644 ports/esp32/boards/sdkconfig.idf52 diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt index d695170fa2b5d..c3a675eb2b78f 100644 --- a/ports/esp32/CMakeLists.txt +++ b/ports/esp32/CMakeLists.txt @@ -5,10 +5,6 @@ # needs to be duplicated for out-of-tree builds, and can easily get out of date. cmake_minimum_required(VERSION 3.12) -# Retrieve IDF version -include($ENV{IDF_PATH}/tools/cmake/version.cmake) -set(IDF_VERSION "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}.${IDF_VERSION_PATCH}") - # Set the board if it's not already set. if(NOT MICROPY_BOARD) set(MICROPY_BOARD ESP32_GENERIC) @@ -35,12 +31,6 @@ set(SDKCONFIG ${CMAKE_BINARY_DIR}/sdkconfig) # Save the manifest file set from the cmake command line. set(MICROPY_USER_FROZEN_MANIFEST ${MICROPY_FROZEN_MANIFEST}) -# Specific options for IDF v5.2 and later -set(SDKCONFIG_IDF_VERSION_SPECIFIC "") -if (IDF_VERSION VERSION_GREATER_EQUAL "5.2.0") - set(SDKCONFIG_IDF_VERSION_SPECIFIC boards/sdkconfig.idf52) -endif() - # Include board config; this is expected to set (among other options): # - SDKCONFIG_DEFAULTS # - IDF_TARGET diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake index d7db192ca43f5..a3888823421d2 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake @@ -6,7 +6,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz diff --git a/ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake b/ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake index 9d0077a15e8d2..ac60dc8630929 100644 --- a/ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake +++ b/ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake @@ -1,5 +1,4 @@ set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.ble ) diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.cmake b/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.cmake index 8c9d7c27a1174..429366afac979 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.cmake +++ b/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32c3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.ble boards/ESP32_GENERIC_C3/sdkconfig.c3usb ) diff --git a/ports/esp32/boards/ESP32_GENERIC_C6/mpconfigboard.cmake b/ports/esp32/boards/ESP32_GENERIC_C6/mpconfigboard.cmake index 3ab98e9da1af7..3060c1cfd978a 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C6/mpconfigboard.cmake +++ b/ports/esp32/boards/ESP32_GENERIC_C6/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32c6) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.c6 boards/sdkconfig.ble ) diff --git a/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.cmake b/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.cmake index c9bc60d4cb57b..76d185d90d5fc 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.cmake +++ b/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.usb boards/sdkconfig.spiram_sx ) diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake index 2bfdad21df495..9b0df3b3771ef 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake +++ b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.spiram_sx diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.cmake b/ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.cmake index 42c431681ef11..5bbac10e22f10 100644 --- a/ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.cmake +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.cmake @@ -1,6 +1,5 @@ set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.ble ) diff --git a/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.cmake b/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.cmake index 7767e3b8497fb..0ce7f85e5d580 100644 --- a/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.cmake +++ b/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32c3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.ble boards/LOLIN_C3_MINI/sdkconfig.board ) diff --git a/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.cmake b/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.cmake index 8be4a69d72e75..dc9abd7478bca 100644 --- a/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.cmake +++ b/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.spiram_sx boards/sdkconfig.usb ) diff --git a/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.cmake b/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.cmake index 8be4a69d72e75..dc9abd7478bca 100644 --- a/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.cmake +++ b/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.spiram_sx boards/sdkconfig.usb ) diff --git a/ports/esp32/boards/M5STACK_ATOM/mpconfigboard.cmake b/ports/esp32/boards/M5STACK_ATOM/mpconfigboard.cmake index 19b55d1659786..f0355e2b0388b 100644 --- a/ports/esp32/boards/M5STACK_ATOM/mpconfigboard.cmake +++ b/ports/esp32/boards/M5STACK_ATOM/mpconfigboard.cmake @@ -1,6 +1,5 @@ set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.ble boards/sdkconfig.240mhz boards/M5STACK_ATOM/sdkconfig.board diff --git a/ports/esp32/boards/M5STACK_NANOC6/mpconfigboard.cmake b/ports/esp32/boards/M5STACK_NANOC6/mpconfigboard.cmake index 3ab98e9da1af7..3060c1cfd978a 100644 --- a/ports/esp32/boards/M5STACK_NANOC6/mpconfigboard.cmake +++ b/ports/esp32/boards/M5STACK_NANOC6/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32c6) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.c6 boards/sdkconfig.ble ) diff --git a/ports/esp32/boards/OLIMEX_ESP32_EVB/mpconfigboard.cmake b/ports/esp32/boards/OLIMEX_ESP32_EVB/mpconfigboard.cmake index d6369d9ef18a4..3a6323d126d93 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_EVB/mpconfigboard.cmake +++ b/ports/esp32/boards/OLIMEX_ESP32_EVB/mpconfigboard.cmake @@ -1,6 +1,5 @@ set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.ble boards/OLIMEX_ESP32_EVB/sdkconfig.board ) diff --git a/ports/esp32/boards/OLIMEX_ESP32_POE/mpconfigboard.cmake b/ports/esp32/boards/OLIMEX_ESP32_POE/mpconfigboard.cmake index 59d2831b3e0d9..c460b07d5efac 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_POE/mpconfigboard.cmake +++ b/ports/esp32/boards/OLIMEX_ESP32_POE/mpconfigboard.cmake @@ -1,6 +1,5 @@ set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.ble boards/OLIMEX_ESP32_POE/sdkconfig.board ) diff --git a/ports/esp32/boards/SIL_WESP32/mpconfigboard.cmake b/ports/esp32/boards/SIL_WESP32/mpconfigboard.cmake index 9caef1123ccd6..a1460397b818b 100644 --- a/ports/esp32/boards/SIL_WESP32/mpconfigboard.cmake +++ b/ports/esp32/boards/SIL_WESP32/mpconfigboard.cmake @@ -1,6 +1,5 @@ set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.ble boards/sdkconfig.240mhz boards/SIL_WESP32/sdkconfig.board diff --git a/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.cmake b/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.cmake index e3bcba9f509e7..5e570d513bb47 100644 --- a/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.cmake @@ -1,7 +1,6 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.spiram_sx boards/sdkconfig.usb boards/UM_FEATHERS2/sdkconfig.board diff --git a/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.cmake b/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.cmake index fc7f246a7ee0f..c98ef691769b5 100644 --- a/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.cmake @@ -1,7 +1,6 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.spiram_sx boards/sdkconfig.usb boards/UM_FEATHERS2NEO/sdkconfig.board diff --git a/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake b/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake index d7fcc34694853..63d1af1da76ca 100644 --- a/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz diff --git a/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.cmake b/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.cmake index 2114b321b2e73..1d2b887d2ce4e 100644 --- a/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz diff --git a/ports/esp32/boards/UM_NANOS3/mpconfigboard.cmake b/ports/esp32/boards/UM_NANOS3/mpconfigboard.cmake index 9030d837289c3..6c7f34009e432 100644 --- a/ports/esp32/boards/UM_NANOS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_NANOS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz diff --git a/ports/esp32/boards/UM_OMGS3/mpconfigboard.cmake b/ports/esp32/boards/UM_OMGS3/mpconfigboard.cmake index a6a42a262e056..aa82111d75888 100644 --- a/ports/esp32/boards/UM_OMGS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_OMGS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz diff --git a/ports/esp32/boards/UM_PROS3/mpconfigboard.cmake b/ports/esp32/boards/UM_PROS3/mpconfigboard.cmake index 8bd67567ffdad..41a96f26e34c4 100644 --- a/ports/esp32/boards/UM_PROS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_PROS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz diff --git a/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.cmake b/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.cmake index f734411616bdb..8b29cb344e777 100644 --- a/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz diff --git a/ports/esp32/boards/UM_TINYC6/mpconfigboard.cmake b/ports/esp32/boards/UM_TINYC6/mpconfigboard.cmake index e2df716a16b1f..024b1299fa3e5 100644 --- a/ports/esp32/boards/UM_TINYC6/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_TINYC6/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32c6) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.c6 boards/sdkconfig.ble boards/UM_TINYC6/sdkconfig.board diff --git a/ports/esp32/boards/UM_TINYPICO/mpconfigboard.cmake b/ports/esp32/boards/UM_TINYPICO/mpconfigboard.cmake index c85fb9d723b06..58b42b55eb7fb 100644 --- a/ports/esp32/boards/UM_TINYPICO/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_TINYPICO/mpconfigboard.cmake @@ -1,6 +1,5 @@ set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.ble boards/sdkconfig.240mhz boards/sdkconfig.spiram diff --git a/ports/esp32/boards/UM_TINYS2/mpconfigboard.cmake b/ports/esp32/boards/UM_TINYS2/mpconfigboard.cmake index 1c794baeed6a3..70cb4a814ff24 100644 --- a/ports/esp32/boards/UM_TINYS2/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_TINYS2/mpconfigboard.cmake @@ -1,7 +1,6 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.spiram_sx boards/sdkconfig.usb boards/UM_TINYS2/sdkconfig.board diff --git a/ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake b/ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake index 9030d837289c3..6c7f34009e432 100644 --- a/ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz diff --git a/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.cmake b/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.cmake index e788dc965d150..089149c44c54d 100644 --- a/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index c7179b6125a1e..e20835c70c42d 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -49,7 +49,8 @@ CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n # FreeRTOS CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2 CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y -CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=y +CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=n +CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK=y # UDP CONFIG_LWIP_PPP_SUPPORT=y diff --git a/ports/esp32/boards/sdkconfig.idf52 b/ports/esp32/boards/sdkconfig.idf52 deleted file mode 100644 index 49b7317d4d69f..0000000000000 --- a/ports/esp32/boards/sdkconfig.idf52 +++ /dev/null @@ -1,2 +0,0 @@ -CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=n -CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK=y From d4d1d4798ccdc7077f459005727ff32208cf1c5f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 4 Dec 2024 17:11:15 +1100 Subject: [PATCH 113/546] esp32: Simplify thread cleanup. Now we only support the case of !CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP, can simplify the cleanup code. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/mpthreadport.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c index daed1bdd0edf2..f436ebb80a7ad 100644 --- a/ports/esp32/mpthreadport.c +++ b/ports/esp32/mpthreadport.c @@ -41,12 +41,6 @@ #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 !CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP -#define FREERTOS_TASK_DELETE_HOOK vTaskPreDeletionHook -#else -#define FREERTOS_TASK_DELETE_HOOK vPortCleanUpTCB -#endif - // this structure forms a linked list, one node per active thread typedef struct _mp_thread_t { TaskHandle_t id; // system id of thread @@ -76,7 +70,7 @@ void mp_thread_init(void *stack, uint32_t stack_len) { // memory barrier to ensure above data is committed __sync_synchronize(); - // FREERTOS_TASK_DELETE_HOOK needs the thread ready after thread_mutex is ready + // vTaskPreDeletionHook needs the thread ready after thread_mutex is ready thread = &thread_entry0; } @@ -180,9 +174,10 @@ void mp_thread_finish(void) { mp_thread_mutex_unlock(&thread_mutex); } -// This is called from the FreeRTOS idle task and is not within Python context, -// so MP_STATE_THREAD is not valid and it does not have the GIL. -void FREERTOS_TASK_DELETE_HOOK(void *tcb) { +// This is called either from vTaskDelete() or from the FreeRTOS idle task, so +// may not be within Python context. Therefore MP_STATE_THREAD may not be valid +// and it does not have the GIL. +void vTaskPreDeletionHook(void *tcb) { if (thread == NULL) { // threading not yet initialised return; @@ -243,7 +238,7 @@ void mp_thread_deinit(void) { // No tasks left to delete break; } else { - // Call FreeRTOS to delete the task (it will call FREERTOS_TASK_DELETE_HOOK) + // Call FreeRTOS to delete the task (it will call vTaskPreDeletionHook) vTaskDelete(id); } } @@ -251,7 +246,7 @@ void mp_thread_deinit(void) { #else -void FREERTOS_TASK_DELETE_HOOK(void *tcb) { +void vTaskPreDeletionHook(void *tcb) { } #endif // MICROPY_PY_THREAD From 94343e20e590473ff1e31bd31c654befa42307a7 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 10 Dec 2024 14:07:29 +0100 Subject: [PATCH 114/546] stm32/boards/STM32F429DISC: Fix SDRAM configuration. Define SDRAM frequency and refresh cycles. This was missed in commit 17808e7b749b269b58dcee67df599a0c61d455bd. Signed-off-by: iabdalkader --- ports/stm32/boards/STM32F429DISC/mpconfigboard.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/stm32/boards/STM32F429DISC/mpconfigboard.h b/ports/stm32/boards/STM32F429DISC/mpconfigboard.h index 5d9287242b74c..92e8ca411b456 100644 --- a/ports/stm32/boards/STM32F429DISC/mpconfigboard.h +++ b/ports/stm32/boards/STM32F429DISC/mpconfigboard.h @@ -100,6 +100,7 @@ #define MICROPY_HW_SDRAM_BURST_LENGTH 2 #define MICROPY_HW_SDRAM_CAS_LATENCY 3 +#define MICROPY_HW_SDRAM_FREQUENCY_KHZ (90000) // 90 MHz #define MICROPY_HW_SDRAM_COLUMN_BITS_NUM 8 #define MICROPY_HW_SDRAM_ROW_BITS_NUM 12 #define MICROPY_HW_SDRAM_MEM_BUS_WIDTH 16 @@ -109,6 +110,7 @@ #define MICROPY_HW_SDRAM_RBURST (0) #define MICROPY_HW_SDRAM_WRITE_PROTECTION (0) #define MICROPY_HW_SDRAM_AUTOREFRESH_NUM (4) +#define MICROPY_HW_SDRAM_REFRESH_CYCLES 8192 #define MICROPY_HW_FMC_SDCKE1 (pin_B5) #define MICROPY_HW_FMC_SDNE1 (pin_B6) From bdda91fe7414b2ff8d5259a176cc8da8b72a803f Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 4 Nov 2024 16:55:16 +1100 Subject: [PATCH 115/546] tests/extmod_hardware: Add a test for machine.PWM freq and duty. This adds a hardware test for `machine.PWM`. It requires a jumper wire between two pins, uses `machine.PWM` to output on one of them, and `machine.time_pulse_us()` to time the PWM on the other pin (some boards test more than one pair of pins). It times both the high and low duty cycle (and hence the frequency) for a range of PWM frequencies and duty cycles (including full on and full off). Currently supported on: - esp32 (needs a minor hack for initialisation, and some tests still fail) - esp8266 (passes for frequencies 1kHz and less) - mimxrt / Teensy 4.0 (passes) - rp2 (passes) - samd21 (passes for frequencies 2kHz and less) Signed-off-by: Damien George --- tests/extmod_hardware/machine_pwm.py | 166 +++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 tests/extmod_hardware/machine_pwm.py diff --git a/tests/extmod_hardware/machine_pwm.py b/tests/extmod_hardware/machine_pwm.py new file mode 100644 index 0000000000000..014030be5c601 --- /dev/null +++ b/tests/extmod_hardware/machine_pwm.py @@ -0,0 +1,166 @@ +# Test machine.PWM, frequncy and duty cycle (using machine.time_pulse_us). +# +# IMPORTANT: This test requires hardware connections: the PWM-output and pulse-input +# pins must be wired together (see the variable `pwm_pulse_pins`). + +import sys +import time + +try: + from machine import time_pulse_us, Pin, PWM +except ImportError: + print("SKIP") + raise SystemExit + +import unittest + +pwm_freq_limit = 1000000 +freq_margin_per_thousand = 0 +duty_margin_per_thousand = 0 +timing_margin_us = 5 + +# Configure pins based on the target. +if "esp32" in sys.platform: + pwm_pulse_pins = ((4, 5),) + freq_margin_per_thousand = 2 + duty_margin_per_thousand = 1 + timing_margin_us = 20 +elif "esp8266" in sys.platform: + pwm_pulse_pins = ((4, 5),) + pwm_freq_limit = 1_000 + duty_margin_per_thousand = 3 + timing_margin_us = 50 +elif "mimxrt" in sys.platform: + if "Teensy" in sys.implementation._machine: + # Teensy 4.x + pwm_pulse_pins = ( + ("D0", "D1"), # FLEXPWM X and UART 1 + ("D2", "D3"), # FLEXPWM A/B + ("D11", "D12"), # QTMR and MOSI/MISO of SPI 0 + ) + else: + pwm_pulse_pins = (("D0", "D1"),) +elif "rp2" in sys.platform: + pwm_pulse_pins = (("GPIO0", "GPIO1"),) +elif "samd" in sys.platform: + pwm_pulse_pins = (("D0", "D1"),) + if "SAMD21" in sys.implementation._machine: + # MCU is too slow to capture short pulses. + pwm_freq_limit = 2_000 +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +# Test a specific frequency and duty cycle. +def _test_freq_duty(self, pulse_in, pwm, freq, duty_u16): + print("freq={:<5} duty_u16={:<5} :".format(freq, duty_u16), end="") + + # Check configured freq/duty_u16 is within error bound. + freq_error = abs(pwm.freq() - freq) * 1000 // freq + duty_error = abs(pwm.duty_u16() - duty_u16) * 1000 // (duty_u16 or 1) + print(" freq={} freq_er={}".format(pwm.freq(), freq_error), end="") + print(" duty={} duty_er={}".format(pwm.duty_u16(), duty_error), end="") + print(" :", end="") + self.assertLessEqual(freq_error, freq_margin_per_thousand) + self.assertLessEqual(duty_error, duty_margin_per_thousand) + + # Calculate expected timing. + expected_total_us = 1_000_000 // freq + expected_high_us = expected_total_us * duty_u16 // 65535 + expected_low_us = expected_total_us - expected_high_us + expected_us = (expected_low_us, expected_high_us) + timeout = 2 * expected_total_us + + # Wait for output to settle. + time_pulse_us(pulse_in, 0, timeout) + time_pulse_us(pulse_in, 1, timeout) + + if duty_u16 == 0 or duty_u16 == 65535: + # Expect a constant output level. + no_pulse = ( + time_pulse_us(pulse_in, 0, timeout) < 0 and time_pulse_us(pulse_in, 1, timeout) < 0 + ) + self.assertTrue(no_pulse) + if expected_high_us == 0: + # Expect a constant low level. + self.assertEqual(pulse_in(), 0) + else: + # Expect a constant high level. + self.assertEqual(pulse_in(), 1) + else: + # Test timing of low and high pulse. + n_averaging = 10 + for level in (0, 1): + t = 0 + time_pulse_us(pulse_in, level, timeout) + for _ in range(n_averaging): + t += time_pulse_us(pulse_in, level, timeout) + t //= n_averaging + expected = expected_us[level] + print(" level={} timing_er={}".format(level, abs(t - expected)), end="") + self.assertLessEqual(abs(t - expected), timing_margin_us) + + print() + + +# Test a specific frequency with multiple duty cycles. +def _test_freq(self, freq): + print() + self.pwm.freq(freq) + for duty in (0, 10, 25, 50, 75, 90, 100): + duty_u16 = duty * 65535 // 100 + if sys.platform == "esp32": + # TODO why is this bit needed to get it working on esp32? + self.pwm.init(freq=freq, duty_u16=duty_u16) + time.sleep(0.1) + self.pwm.duty_u16(duty_u16) + _test_freq_duty(self, self.pulse_in, self.pwm, freq, duty_u16) + + +# Given a set of pins, this test class will test multiple frequencies and duty cycles. +class TestBase: + @classmethod + def setUpClass(cls): + print("set up pins:", cls.pwm_pin, cls.pulse_pin) + cls.pwm = PWM(cls.pwm_pin) + cls.pulse_in = Pin(cls.pulse_pin, Pin.IN) + + @classmethod + def tearDownClass(cls): + cls.pwm.deinit() + + def test_freq_50(self): + _test_freq(self, 50) + + def test_freq_100(self): + _test_freq(self, 100) + + def test_freq_500(self): + _test_freq(self, 500) + + def test_freq_1000(self): + _test_freq(self, 1000) + + @unittest.skipIf(pwm_freq_limit < 2000, "frequency too high") + def test_freq_2000(self): + _test_freq(self, 2000) + + @unittest.skipIf(pwm_freq_limit < 5000, "frequency too high") + def test_freq_5000(self): + _test_freq(self, 5000) + + @unittest.skipIf(pwm_freq_limit < 10000, "frequency too high") + def test_freq_10000(self): + _test_freq(self, 10000) + + +# Generate test classes, one for each set of pins to test. +for pwm, pulse in pwm_pulse_pins: + cls_name = "Test_{}_{}".format(pwm, pulse) + globals()[cls_name] = type( + cls_name, (TestBase, unittest.TestCase), {"pwm_pin": pwm, "pulse_pin": pulse} + ) + +if __name__ == "__main__": + unittest.main() From 5f2d05d41755990c1e14223475e19c7d204d9db2 Mon Sep 17 00:00:00 2001 From: Sebastian Romero Date: Wed, 4 Dec 2024 16:30:56 +0100 Subject: [PATCH 116/546] esp32: Enable machine.USBDevice to configure USB at runtime. This adds support for `machine.USBDevice` to S2 and S3 boards. Signed-off-by: Sebastian Romero --- ports/esp32/esp32_common.cmake | 1 + ports/esp32/main.c | 4 ++++ ports/esp32/mpconfigport.h | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 0a6b29983c2f5..b6bfcdacc3cb4 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -78,6 +78,7 @@ if(MICROPY_PY_TINYUSB) ${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 ) list(APPEND MICROPY_INC_TINYUSB diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 18ef9d7354798..4f0c27ee078f8 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -183,6 +183,10 @@ void mp_task(void *pvParameter) { mp_thread_deinit(); #endif + #if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE + mp_usbd_deinit(); + #endif + gc_sweep_all(); // Free any native code pointers that point to iRAM. diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index bf1f5a18641db..b5b7d63a56333 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -215,6 +215,10 @@ #else #define MICROPY_HW_USB_VID (CONFIG_TINYUSB_DESC_CUSTOM_VID) #endif + +#ifndef MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE +#define MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE (1) // Support machine.USBDevice +#endif #endif #ifndef MICROPY_HW_USB_PID From 8e4c8096940c83c49c3ac07acc96b0133f65ec8e Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Dec 2024 11:56:03 +1100 Subject: [PATCH 117/546] unix,windows: Force _FILE_OFFSET_BITS=64 to fix 32-bit file ABI. On 64-bit systems this should have no effect. On 32-bit systems it will force 64-bit file sizes and fixes directory listing on certain 32-bit ports. This option should work on pretty much all 32-bit systems these days. Signed-off-by: Damien George --- ports/unix/Makefile | 4 ++++ ports/windows/Makefile | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index f02e6c6355ab6..88fa1af045454 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -48,6 +48,10 @@ CWARN = -Wall -Werror CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EXTRA) +# Force the use of 64-bits for file sizes in C library functions on 32-bit platforms. +# This option has no effect on 64-bit builds. +CFLAGS += -D_FILE_OFFSET_BITS=64 + # Debugging/Optimization ifdef DEBUG COPT ?= -Og diff --git a/ports/windows/Makefile b/ports/windows/Makefile index cf0a927014b1a..115d1a61ef51e 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -42,6 +42,10 @@ INC += -I$(VARIANT_DIR) CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Werror -std=gnu99 -DUNIX -D__USE_MINGW_ANSI_STDIO=1 $(COPT) $(CFLAGS_EXTRA) LDFLAGS += -lm -lbcrypt $(LDFLAGS_EXTRA) +# Force the use of 64-bits for file sizes in C library functions on 32-bit platforms. +# This option has no effect on 64-bit builds. +CFLAGS += -D_FILE_OFFSET_BITS=64 + # Debugging/Optimization ifdef DEBUG CFLAGS += -g From ee8d8b3448f26abafbfe1ed7f8970e02f4f8d232 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Dec 2024 11:58:43 +1100 Subject: [PATCH 118/546] tools/ci.sh: Re-enable vfs_posix tests on unix qemu MIPS CI. These work now that _FILE_OFFSET_BITS=64. Signed-off-by: Damien George --- tools/ci.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/ci.sh b/tools/ci.sh index b9f9dec8bc26e..33559d339160a 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -682,10 +682,8 @@ function ci_unix_qemu_mips_build { } function ci_unix_qemu_mips_run_tests { - # Issues with MIPS tests: - # - (i)listdir does not work, it always returns the empty list (it's an issue with the underlying C call) file ./ports/unix/build-coverage/micropython - (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py --exclude 'vfs_posix.*\.py') + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py) } function ci_unix_qemu_arm_setup { From 46a37a09fdf469958c14a64ebdab974a4ca87a21 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 25 Nov 2024 23:57:50 +1100 Subject: [PATCH 119/546] qemu/mpconfigport: Enable VFS reader, loading .mpy files and io.IOBase. This allows importing from the VFS, and enables the following 7 additional tests: builtin_execfile, io_iobase, json_dump_iobase, import_mpy_invalid, import_mpy_native, import_mpy_native_gc, vfs_userfs. Signed-off-by: Damien George --- ports/qemu/main.c | 4 ---- ports/qemu/mpconfigport.h | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ports/qemu/main.c b/ports/qemu/main.c index 042106580407d..dff55058e16d1 100644 --- a/ports/qemu/main.c +++ b/ports/qemu/main.c @@ -71,10 +71,6 @@ void gc_collect(void) { gc_collect_end(); } -mp_lexer_t *mp_lexer_new_from_file(qstr filename) { - mp_raise_OSError(MP_ENOENT); -} - void nlr_jump_fail(void *val) { mp_printf(&mp_plat_print, "uncaught NLR\n"); exit(1); diff --git a/ports/qemu/mpconfigport.h b/ports/qemu/mpconfigport.h index f3dd8c37508d3..f95b58ba4d465 100644 --- a/ports/qemu/mpconfigport.h +++ b/ports/qemu/mpconfigport.h @@ -42,13 +42,14 @@ #endif #define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) +#define MICROPY_PERSISTENT_CODE_LOAD (1) #define MICROPY_MEM_STATS (1) +#define MICROPY_READER_VFS (1) #define MICROPY_ENABLE_GC (1) #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_IO_IOBASE (0) #define MICROPY_PY_SYS_PLATFORM "qemu" #define MICROPY_PY_SYS_STDIO_BUFFER (0) #define MICROPY_PY_SELECT (0) From bd6edf00bba5dd5f64f54946cce4e396ed841209 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 10 Dec 2024 23:29:30 +1100 Subject: [PATCH 120/546] stm32/pin: Add option to exclude legacy Pin methods and constants. This is enabled by default, but disabled when MICROPY_PREVIEW_VERSION_2 is enabled. The intention is that these methods and constants are deprecated in MicroPython 2.x. Signed-off-by: Damien George --- ports/stm32/mpconfigboard_common.h | 5 ++++ ports/stm32/pin.c | 42 ++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index c158419483345..f9fe1396a370d 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -52,6 +52,11 @@ #define MICROPY_PY_PYB_LEGACY (1) #endif +// Whether to include legacy methods and constants in machine.Pin (which is also pyb.Pin). +#ifndef MICROPY_PY_MACHINE_PIN_LEGACY +#define MICROPY_PY_MACHINE_PIN_LEGACY (!MICROPY_PREVIEW_VERSION_2) +#endif + // Whether machine.bootloader() will enter the bootloader via reset, or direct jump. #ifndef MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (1) diff --git a/ports/stm32/pin.c b/ports/stm32/pin.c index 117a8366dbffe..0859715465130 100644 --- a/ports/stm32/pin.c +++ b/ports/stm32/pin.c @@ -280,6 +280,8 @@ static mp_obj_t pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_ } } +#if MICROPY_PY_MACHINE_PIN_LEGACY + /// \classmethod mapper([fun]) /// Get or set the pin mapper function. static mp_obj_t pin_mapper(size_t n_args, const mp_obj_t *args) { @@ -304,20 +306,6 @@ static mp_obj_t pin_map_dict(size_t n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_map_dict_fun_obj, 1, 2, pin_map_dict); static MP_DEFINE_CONST_CLASSMETHOD_OBJ(pin_map_dict_obj, MP_ROM_PTR(&pin_map_dict_fun_obj)); -/// \classmethod af_list() -/// Returns an array of alternate functions available for this pin. -static mp_obj_t pin_af_list(mp_obj_t self_in) { - machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_t result = mp_obj_new_list(0, NULL); - - const pin_af_obj_t *af = self->af; - for (mp_uint_t i = 0; i < self->num_af; i++, af++) { - mp_obj_list_append(result, MP_OBJ_FROM_PTR(af)); - } - return result; -} -static MP_DEFINE_CONST_FUN_OBJ_1(pin_af_list_obj, pin_af_list); - /// \classmethod debug([state]) /// Get or set the debugging state (`True` or `False` for on or off). static mp_obj_t pin_debug(size_t n_args, const mp_obj_t *args) { @@ -330,6 +318,8 @@ static mp_obj_t pin_debug(size_t n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_debug_fun_obj, 1, 2, pin_debug); static MP_DEFINE_CONST_CLASSMETHOD_OBJ(pin_debug_obj, MP_ROM_PTR(&pin_debug_fun_obj)); +#endif // MICROPY_PY_MACHINE_PIN_LEGACY + // init(mode, pull=None, alt=-1, *, value, alt) static mp_obj_t pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { @@ -442,6 +432,8 @@ static mp_obj_t pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar } static MP_DEFINE_CONST_FUN_OBJ_KW(pin_irq_obj, 1, pin_irq); +#if MICROPY_PY_MACHINE_PIN_LEGACY + /// \method name() /// Get the pin name. static mp_obj_t pin_name(mp_obj_t self_in) { @@ -469,6 +461,20 @@ static mp_obj_t pin_names(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(pin_names_obj, pin_names); +/// \classmethod af_list() +/// Returns an array of alternate functions available for this pin. +static mp_obj_t pin_af_list(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t result = mp_obj_new_list(0, NULL); + + const pin_af_obj_t *af = self->af; + for (mp_uint_t i = 0; i < self->num_af; i++, af++) { + mp_obj_list_append(result, MP_OBJ_FROM_PTR(af)); + } + return result; +} +static MP_DEFINE_CONST_FUN_OBJ_1(pin_af_list_obj, pin_af_list); + /// \method port() /// Get the pin port. static mp_obj_t pin_port(mp_obj_t self_in) { @@ -520,6 +526,8 @@ static mp_obj_t pin_af(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(pin_af_obj, pin_af); +#endif // MICROPY_PY_MACHINE_PIN_LEGACY + static const mp_rom_map_elem_t pin_locals_dict_table[] = { // instance methods { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pin_init_obj) }, @@ -531,6 +539,7 @@ static const mp_rom_map_elem_t pin_locals_dict_table[] = { // Legacy names as used by pyb.Pin { MP_ROM_QSTR(MP_QSTR_low), MP_ROM_PTR(&pin_off_obj) }, { MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&pin_on_obj) }, + #if MICROPY_PY_MACHINE_PIN_LEGACY { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&pin_name_obj) }, { MP_ROM_QSTR(MP_QSTR_names), MP_ROM_PTR(&pin_names_obj) }, { MP_ROM_QSTR(MP_QSTR_af_list), MP_ROM_PTR(&pin_af_list_obj) }, @@ -540,11 +549,14 @@ static const mp_rom_map_elem_t pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_mode), MP_ROM_PTR(&pin_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_pull), MP_ROM_PTR(&pin_pull_obj) }, { MP_ROM_QSTR(MP_QSTR_af), MP_ROM_PTR(&pin_af_obj) }, + #endif + #if MICROPY_PY_MACHINE_PIN_LEGACY // class methods { MP_ROM_QSTR(MP_QSTR_mapper), MP_ROM_PTR(&pin_mapper_obj) }, { MP_ROM_QSTR(MP_QSTR_dict), MP_ROM_PTR(&pin_map_dict_obj) }, { MP_ROM_QSTR(MP_QSTR_debug), MP_ROM_PTR(&pin_debug_obj) }, + #endif // class attributes { MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&pin_board_pins_obj_type) }, @@ -562,12 +574,14 @@ static const mp_rom_map_elem_t pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_MODE_IT_RISING) }, { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_MODE_IT_FALLING) }, + #if MICROPY_PY_MACHINE_PIN_LEGACY // legacy class constants { MP_ROM_QSTR(MP_QSTR_OUT_PP), MP_ROM_INT(GPIO_MODE_OUTPUT_PP) }, { MP_ROM_QSTR(MP_QSTR_OUT_OD), MP_ROM_INT(GPIO_MODE_OUTPUT_OD) }, { MP_ROM_QSTR(MP_QSTR_AF_PP), MP_ROM_INT(GPIO_MODE_AF_PP) }, { MP_ROM_QSTR(MP_QSTR_AF_OD), MP_ROM_INT(GPIO_MODE_AF_OD) }, { MP_ROM_QSTR(MP_QSTR_PULL_NONE), MP_ROM_INT(GPIO_NOPULL) }, + #endif #include "genhdr/pins_af_const.h" }; From 1c2cdf9f6dc649280ecfcc114100b3c2443c9402 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 10 Dec 2024 23:38:38 +1100 Subject: [PATCH 121/546] stm32/pin: Add config option to exclude Pin alternate function. Signed-off-by: Damien George --- ports/stm32/mpconfigboard_common.h | 5 ++++ ports/stm32/pin.c | 40 ++++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index f9fe1396a370d..5c66b804b2f58 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -57,6 +57,11 @@ #define MICROPY_PY_MACHINE_PIN_LEGACY (!MICROPY_PREVIEW_VERSION_2) #endif +// Whether to include support for alternate function selection in machine.Pin (and pyb.Pin). +#ifndef MICROPY_PY_MACHINE_PIN_ALT_SUPPORT +#define MICROPY_PY_MACHINE_PIN_ALT_SUPPORT (1) +#endif + // Whether machine.bootloader() will enter the bootloader via reset, or direct jump. #ifndef MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (1) diff --git a/ports/stm32/pin.c b/ports/stm32/pin.c index 0859715465130..1430c7c64a3e4 100644 --- a/ports/stm32/pin.c +++ b/ports/stm32/pin.c @@ -320,14 +320,29 @@ static MP_DEFINE_CONST_CLASSMETHOD_OBJ(pin_debug_obj, MP_ROM_PTR(&pin_debug_fun_ #endif // MICROPY_PY_MACHINE_PIN_LEGACY -// init(mode, pull=None, alt=-1, *, value, alt) +// init(mode, pull=None, af=-1, *, value, alt) static mp_obj_t pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_mode, + ARG_pull, + #if MICROPY_PY_MACHINE_PIN_ALT_SUPPORT + ARG_af, + #endif + ARG_value, + #if MICROPY_PY_MACHINE_PIN_ALT_SUPPORT + ARG_alt, + #endif + }; static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + #if MICROPY_PY_MACHINE_PIN_ALT_SUPPORT { MP_QSTR_af, MP_ARG_INT, {.u_int = -1}}, // legacy + #endif { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + #if MICROPY_PY_MACHINE_PIN_ALT_SUPPORT { MP_QSTR_alt, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1}}, + #endif }; // parse args @@ -335,35 +350,38 @@ static mp_obj_t pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // get io mode - uint mode = args[0].u_int; + uint mode = args[ARG_mode].u_int; if (!IS_GPIO_MODE(mode)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin mode: %d"), mode); } // get pull mode uint pull = GPIO_NOPULL; - if (args[1].u_obj != mp_const_none) { - pull = mp_obj_get_int(args[1].u_obj); + if (args[ARG_pull].u_obj != mp_const_none) { + pull = mp_obj_get_int(args[ARG_pull].u_obj); } if (!IS_GPIO_PULL(pull)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin pull: %d"), pull); } + mp_int_t af = -1; + #if MICROPY_PY_MACHINE_PIN_ALT_SUPPORT // get af (alternate function); alt-arg overrides af-arg - mp_int_t af = args[4].u_int; + af = args[ARG_alt].u_int; if (af == -1) { - af = args[2].u_int; + af = args[ARG_af].u_int; } if ((mode == GPIO_MODE_AF_PP || mode == GPIO_MODE_AF_OD) && !IS_GPIO_AF(af)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin af: %d"), af); } + #endif // enable the peripheral clock for the port of this pin mp_hal_gpio_clock_enable(self->gpio); // if given, set the pin value before initialising to prevent glitches - if (args[3].u_obj != MP_OBJ_NULL) { - mp_hal_pin_write(self, mp_obj_is_true(args[3].u_obj)); + if (args[ARG_value].u_obj != MP_OBJ_NULL) { + mp_hal_pin_write(self, mp_obj_is_true(args[ARG_value].u_obj)); } // configure the GPIO as requested @@ -566,8 +584,10 @@ static const mp_rom_map_elem_t pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_MODE_INPUT) }, { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_MODE_OUTPUT_PP) }, { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(GPIO_MODE_OUTPUT_OD) }, + #if MICROPY_PY_MACHINE_PIN_ALT_SUPPORT { MP_ROM_QSTR(MP_QSTR_ALT), MP_ROM_INT(GPIO_MODE_AF_PP) }, { MP_ROM_QSTR(MP_QSTR_ALT_OPEN_DRAIN), MP_ROM_INT(GPIO_MODE_AF_OD) }, + #endif { MP_ROM_QSTR(MP_QSTR_ANALOG), MP_ROM_INT(GPIO_MODE_ANALOG) }, { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULLUP) }, { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULLDOWN) }, @@ -578,12 +598,16 @@ static const mp_rom_map_elem_t pin_locals_dict_table[] = { // legacy class constants { MP_ROM_QSTR(MP_QSTR_OUT_PP), MP_ROM_INT(GPIO_MODE_OUTPUT_PP) }, { MP_ROM_QSTR(MP_QSTR_OUT_OD), MP_ROM_INT(GPIO_MODE_OUTPUT_OD) }, + #if MICROPY_PY_MACHINE_PIN_ALT_SUPPORT { MP_ROM_QSTR(MP_QSTR_AF_PP), MP_ROM_INT(GPIO_MODE_AF_PP) }, { MP_ROM_QSTR(MP_QSTR_AF_OD), MP_ROM_INT(GPIO_MODE_AF_OD) }, + #endif { MP_ROM_QSTR(MP_QSTR_PULL_NONE), MP_ROM_INT(GPIO_NOPULL) }, #endif + #if MICROPY_PY_MACHINE_PIN_ALT_SUPPORT #include "genhdr/pins_af_const.h" + #endif }; static MP_DEFINE_CONST_DICT(pin_locals_dict, pin_locals_dict_table); From 3203e950fc23b31f8a702babe4c220a248763804 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 11 Dec 2024 00:04:34 +1100 Subject: [PATCH 122/546] tools/boardgen.py: Provide macro defns for number of cpu/board pins. So a port can use them if needed to exclude the Pin.cpu/Pin.board objects. Signed-off-by: Damien George --- tools/boardgen.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tools/boardgen.py b/tools/boardgen.py index 41a8e9f2e597b..39bedf71cd0c8 100644 --- a/tools/boardgen.py +++ b/tools/boardgen.py @@ -172,6 +172,8 @@ def __init__(self, pin_type, enable_af=False): self._pins = [] self._pin_type = pin_type self._enable_af = enable_af + self._pin_cpu_num_entries = 0 + self._pin_board_num_entries = 0 # Allows a port to define a known cpu pin (without relying on it being in the # csv file). @@ -298,6 +300,9 @@ def print_board_locals_dict(self, out_source): # Don't include hidden pins in Pins.board. continue + # Keep track of the total number of Pin.board entries. + self._pin_board_num_entries += 1 + # We don't use the enable macro for board pins, because they # shouldn't be referenced in pins.csv unless they're # available. @@ -322,6 +327,9 @@ def print_cpu_locals_dict(self, out_source): file=out_source, ) for pin in self.available_pins(exclude_hidden=True): + # Keep track of the total number of Pin.cpu entries. + self._pin_cpu_num_entries += 1 + m = pin.enable_macro() if m: print(" #if {}".format(m), file=out_source) @@ -351,6 +359,20 @@ def board_name_define_prefix(self): # Print the pin_CPUNAME and pin_BOARDNAME macros. def print_defines(self, out_header, cpu=True, board=True): + # Provide #defines for the number of cpu and board pins. + print( + "#define MICROPY_PY_MACHINE_PIN_CPU_NUM_ENTRIES ({})".format( + self._pin_cpu_num_entries + ), + file=out_header, + ) + print( + "#define MICROPY_PY_MACHINE_PIN_BOARD_NUM_ENTRIES ({})".format( + self._pin_board_num_entries + ), + file=out_header, + ) + # Provide #defines for each cpu pin. for pin in self.available_pins(): print(file=out_header) From a2efafcce3a03e2fd972e2521cae2c2bec2e88ff Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 11 Dec 2024 00:05:19 +1100 Subject: [PATCH 123/546] stm32/pin: Exclude Pin.cpu/Pin.board if they contain no entries. Signed-off-by: Damien George --- ports/stm32/pin.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/stm32/pin.c b/ports/stm32/pin.c index 1430c7c64a3e4..7de87f2c7be2f 100644 --- a/ports/stm32/pin.c +++ b/ports/stm32/pin.c @@ -577,8 +577,12 @@ static const mp_rom_map_elem_t pin_locals_dict_table[] = { #endif // class attributes + #if MICROPY_PY_MACHINE_PIN_BOARD_NUM_ENTRIES > 0 { MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&pin_board_pins_obj_type) }, + #endif + #if MICROPY_PY_MACHINE_PIN_CPU_NUM_ENTRIES > 0 { MP_ROM_QSTR(MP_QSTR_cpu), MP_ROM_PTR(&pin_cpu_pins_obj_type) }, + #endif // class constants { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_MODE_INPUT) }, From 23bfa95d34e9b6f836ee67a1564e04f7b86eaa36 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 2 Sep 2024 13:06:30 +1000 Subject: [PATCH 124/546] stm32/extint: Fix EXTI IRQ handlers for H5 MCUs. The existing EXTI IRQ handlers are moved from `stm32_it.c` to `extint.c` to keep them with related code. A macro is defined to make it easier to define the handler function that handles one line, and correct handlers added for STM32H5xx MCUs. Also, to prevent errors in the future, `MP_STATIC_ASSERT( > 0)` is added to each handler function to check that the correct `IRQn` constant is used, which corresponds to the handler function name. Signed-off-by: Damien George --- ports/stm32/extint.c | 91 ++++++++++++++++++++++++++++++++++++++++++ ports/stm32/stm32_it.c | 78 ------------------------------------ 2 files changed, 91 insertions(+), 78 deletions(-) diff --git a/ports/stm32/extint.c b/ports/stm32/extint.c index 2a08810125572..5b5658809bc84 100644 --- a/ports/stm32/extint.c +++ b/ports/stm32/extint.c @@ -241,6 +241,97 @@ static const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = { #endif }; +#define DEFINE_EXTI_IRQ_HANDLER(line) \ + void EXTI##line##_IRQHandler(void) { \ + MP_STATIC_ASSERT(EXTI##line##_IRQn > 0); \ + IRQ_ENTER(EXTI##line##_IRQn); \ + Handle_EXTI_Irq(line); \ + IRQ_EXIT(EXTI##line##_IRQn); \ + } + +#if defined(STM32F0) || defined(STM32L0) || defined(STM32G0) + +void EXTI0_1_IRQHandler(void) { + MP_STATIC_ASSERT(EXTI0_1_IRQn > 0); + IRQ_ENTER(EXTI0_1_IRQn); + Handle_EXTI_Irq(0); + Handle_EXTI_Irq(1); + IRQ_EXIT(EXTI0_1_IRQn); +} + +void EXTI2_3_IRQHandler(void) { + MP_STATIC_ASSERT(EXTI2_3_IRQn > 0); + IRQ_ENTER(EXTI2_3_IRQn); + Handle_EXTI_Irq(2); + Handle_EXTI_Irq(3); + IRQ_EXIT(EXTI2_3_IRQn); +} + +void EXTI4_15_IRQHandler(void) { + MP_STATIC_ASSERT(EXTI4_15_IRQn > 0); + IRQ_ENTER(EXTI4_15_IRQn); + for (int i = 4; i <= 15; ++i) { + Handle_EXTI_Irq(i); + } + IRQ_EXIT(EXTI4_15_IRQn); +} + +#elif defined(STM32F4) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32L1) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) + +DEFINE_EXTI_IRQ_HANDLER(0) +DEFINE_EXTI_IRQ_HANDLER(1) +DEFINE_EXTI_IRQ_HANDLER(2) +DEFINE_EXTI_IRQ_HANDLER(3) +DEFINE_EXTI_IRQ_HANDLER(4) + +void EXTI9_5_IRQHandler(void) { + MP_STATIC_ASSERT(EXTI9_5_IRQn > 0); + IRQ_ENTER(EXTI9_5_IRQn); + Handle_EXTI_Irq(5); + Handle_EXTI_Irq(6); + Handle_EXTI_Irq(7); + Handle_EXTI_Irq(8); + Handle_EXTI_Irq(9); + IRQ_EXIT(EXTI9_5_IRQn); +} + +void EXTI15_10_IRQHandler(void) { + MP_STATIC_ASSERT(EXTI15_10_IRQn > 0); + IRQ_ENTER(EXTI15_10_IRQn); + Handle_EXTI_Irq(10); + Handle_EXTI_Irq(11); + Handle_EXTI_Irq(12); + Handle_EXTI_Irq(13); + Handle_EXTI_Irq(14); + Handle_EXTI_Irq(15); + IRQ_EXIT(EXTI15_10_IRQn); +} + +#elif defined(STM32H5) + +DEFINE_EXTI_IRQ_HANDLER(0) +DEFINE_EXTI_IRQ_HANDLER(1) +DEFINE_EXTI_IRQ_HANDLER(2) +DEFINE_EXTI_IRQ_HANDLER(3) +DEFINE_EXTI_IRQ_HANDLER(4) +DEFINE_EXTI_IRQ_HANDLER(5) +DEFINE_EXTI_IRQ_HANDLER(6) +DEFINE_EXTI_IRQ_HANDLER(7) +DEFINE_EXTI_IRQ_HANDLER(8) +DEFINE_EXTI_IRQ_HANDLER(9) +DEFINE_EXTI_IRQ_HANDLER(10) +DEFINE_EXTI_IRQ_HANDLER(11) +DEFINE_EXTI_IRQ_HANDLER(12) +DEFINE_EXTI_IRQ_HANDLER(13) +DEFINE_EXTI_IRQ_HANDLER(14) +DEFINE_EXTI_IRQ_HANDLER(15) + +#else + +#error Unsupported processor + +#endif + // Set override_callback_obj to true if you want to unconditionally set the // callback function. uint extint_register(mp_obj_t pin_obj, uint32_t mode, uint32_t pull, mp_obj_t callback_obj, bool override_callback_obj) { diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index e05dbe91bc108..2e625a5894dd9 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -452,62 +452,6 @@ void OTG_HS_WKUP_IRQHandler(void) { { }*/ -/** - * @brief These functions handle the EXTI interrupt requests. - * @param None - * @retval None - */ -void EXTI0_IRQHandler(void) { - IRQ_ENTER(EXTI0_IRQn); - Handle_EXTI_Irq(0); - IRQ_EXIT(EXTI0_IRQn); -} - -void EXTI1_IRQHandler(void) { - IRQ_ENTER(EXTI1_IRQn); - Handle_EXTI_Irq(1); - IRQ_EXIT(EXTI1_IRQn); -} - -void EXTI2_IRQHandler(void) { - IRQ_ENTER(EXTI2_IRQn); - Handle_EXTI_Irq(2); - IRQ_EXIT(EXTI2_IRQn); -} - -void EXTI3_IRQHandler(void) { - IRQ_ENTER(EXTI3_IRQn); - Handle_EXTI_Irq(3); - IRQ_EXIT(EXTI3_IRQn); -} - -void EXTI4_IRQHandler(void) { - IRQ_ENTER(EXTI4_IRQn); - Handle_EXTI_Irq(4); - IRQ_EXIT(EXTI4_IRQn); -} - -void EXTI9_5_IRQHandler(void) { - IRQ_ENTER(EXTI9_5_IRQn); - Handle_EXTI_Irq(5); - Handle_EXTI_Irq(6); - Handle_EXTI_Irq(7); - Handle_EXTI_Irq(8); - Handle_EXTI_Irq(9); - IRQ_EXIT(EXTI9_5_IRQn); -} - -void EXTI15_10_IRQHandler(void) { - IRQ_ENTER(EXTI15_10_IRQn); - Handle_EXTI_Irq(10); - Handle_EXTI_Irq(11); - Handle_EXTI_Irq(12); - Handle_EXTI_Irq(13); - Handle_EXTI_Irq(14); - Handle_EXTI_Irq(15); - IRQ_EXIT(EXTI15_10_IRQn); -} - void PVD_IRQHandler(void) { IRQ_ENTER(PVD_IRQn); Handle_EXTI_Irq(EXTI_PVD_OUTPUT); @@ -605,28 +549,6 @@ void RTC_IRQHandler(void) { } #endif -void EXTI0_1_IRQHandler(void) { - IRQ_ENTER(EXTI0_1_IRQn); - Handle_EXTI_Irq(0); - Handle_EXTI_Irq(1); - IRQ_EXIT(EXTI0_1_IRQn); -} - -void EXTI2_3_IRQHandler(void) { - IRQ_ENTER(EXTI2_3_IRQn); - Handle_EXTI_Irq(2); - Handle_EXTI_Irq(3); - IRQ_EXIT(EXTI2_3_IRQn); -} - -void EXTI4_15_IRQHandler(void) { - IRQ_ENTER(EXTI4_15_IRQn); - for (int i = 4; i <= 15; ++i) { - Handle_EXTI_Irq(i); - } - IRQ_EXIT(EXTI4_15_IRQn); -} - void TIM1_BRK_UP_TRG_COM_IRQHandler(void) { IRQ_ENTER(TIM1_BRK_UP_TRG_COM_IRQn); timer_irq_handler(1); From 8970ede7cccee57032b547b8a97539b64e61102a Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 24 Nov 2024 15:06:30 +1100 Subject: [PATCH 125/546] extmod/moductypes: Fix large return values of addressof and INT_MAYBE. So they don't return a negative number for an address (prior to this fix they would return negative addresses for values that were larger than the maximum small-int value). Signed-off-by: Damien George --- extmod/moductypes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extmod/moductypes.c b/extmod/moductypes.c index 00a69a275a0ee..bf45797658fa0 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -602,7 +602,7 @@ static mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) { uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS); if (agg_type == PTR) { byte *p = *(void **)self->addr; - return mp_obj_new_int((mp_int_t)(uintptr_t)p); + return mp_obj_new_int_from_uint((uintptr_t)p); } } MP_FALLTHROUGH @@ -629,7 +629,7 @@ static mp_int_t uctypes_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, static mp_obj_t uctypes_struct_addressof(mp_obj_t buf) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); - return mp_obj_new_int((mp_int_t)(uintptr_t)bufinfo.buf); + return mp_obj_new_int_from_uint((uintptr_t)bufinfo.buf); } MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_addressof_obj, uctypes_struct_addressof); From d10cda66dcff1b760ebc550c7f9352220fe4163f Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 18 Dec 2024 10:15:01 +1100 Subject: [PATCH 126/546] tests/extmod: Add test for uctypes.addressof function. Signed-off-by: Damien George --- tests/extmod/uctypes_addressof.py | 16 ++++++++++++++++ tests/extmod/uctypes_addressof.py.exp | 9 +++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/extmod/uctypes_addressof.py create mode 100644 tests/extmod/uctypes_addressof.py.exp diff --git a/tests/extmod/uctypes_addressof.py b/tests/extmod/uctypes_addressof.py new file mode 100644 index 0000000000000..c83089d0f72af --- /dev/null +++ b/tests/extmod/uctypes_addressof.py @@ -0,0 +1,16 @@ +# Test uctypes.addressof(). + +try: + from sys import maxsize + import uctypes +except ImportError: + print("SKIP") + raise SystemExit + +# Test small addresses. +for i in range(8): + print(uctypes.addressof(uctypes.bytearray_at(1 << i, 8))) + +# Test address that is bigger than the greatest small-int but still within the address range. +large_addr = maxsize + 1 +print(uctypes.addressof(uctypes.bytearray_at(large_addr, 8)) == large_addr) diff --git a/tests/extmod/uctypes_addressof.py.exp b/tests/extmod/uctypes_addressof.py.exp new file mode 100644 index 0000000000000..5b48569d0185d --- /dev/null +++ b/tests/extmod/uctypes_addressof.py.exp @@ -0,0 +1,9 @@ +1 +2 +4 +8 +16 +32 +64 +128 +True From 92a5ea51b456e96332f703aded4be2fbc22982fc Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 10 Dec 2024 15:34:12 +1100 Subject: [PATCH 127/546] ci: Cache Zephyr workspace installation. Can save several minutes downloading the Zephyr docker image and/or cloning repo from GitHub. Cache keyed on the Zephyr version, which AFAIK is the only determinant for the workspace contents. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- .github/workflows/ports_zephyr.yml | 13 +++++++++++++ tools/ci.sh | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ports_zephyr.yml b/.github/workflows/ports_zephyr.yml index 444b0973daff5..ff3e19c7ea136 100644 --- a/.github/workflows/ports_zephyr.yml +++ b/.github/workflows/ports_zephyr.yml @@ -30,9 +30,22 @@ jobs: docker-images: false swap-storage: false - uses: actions/checkout@v4 + - id: versions + name: Read Zephyr version + run: source tools/ci.sh && echo "ZEPHYR=$ZEPHYR_VERSION" | tee "$GITHUB_OUTPUT" + - name: Cached Zephyr Workspace + id: cache_workspace + uses: actions/cache@v4 + with: + # note that the Zephyr CI docker image is 15GB. At time of writing + # GitHub caches are limited to 10GB total for a project. So we only + # cache the "workspace" + path: ./zephyrproject + key: zephyr-workspace-${{ steps.versions.outputs.ZEPHYR }} - name: Install packages run: source tools/ci.sh && ci_zephyr_setup - name: Install Zephyr + if: steps.cache_workspace.outputs.cache-hit != 'true' run: source tools/ci.sh && ci_zephyr_install - name: Build run: source tools/ci.sh && ci_zephyr_build diff --git a/tools/ci.sh b/tools/ci.sh index 33559d339160a..bc013f5324111 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -747,14 +747,24 @@ ZEPHYR_SDK_VERSION=0.16.8 ZEPHYR_VERSION=v3.7.0 function ci_zephyr_setup { - docker pull zephyrprojectrtos/ci:${ZEPHYR_DOCKER_VERSION} + IMAGE=zephyrprojectrtos/ci:${ZEPHYR_DOCKER_VERSION} + + docker pull ${IMAGE} + + # Directories cached by GitHub Actions, mounted + # into the container + ZEPHYRPROJECT_DIR="$(pwd)/zephyrproject" + + mkdir -p "${ZEPHYRPROJECT_DIR}" + docker run --name zephyr-ci -d -it \ -v "$(pwd)":/micropython \ + -v "${ZEPHYRPROJECT_DIR}":/zephyrproject \ -e ZEPHYR_SDK_INSTALL_DIR=/opt/toolchains/zephyr-sdk-${ZEPHYR_SDK_VERSION} \ -e ZEPHYR_TOOLCHAIN_VARIANT=zephyr \ -e ZEPHYR_BASE=/zephyrproject/zephyr \ -w /micropython/ports/zephyr \ - zephyrprojectrtos/ci:${ZEPHYR_DOCKER_VERSION} + ${IMAGE} docker ps -a # qemu-system-arm is needed to run the test suite. From db4b095644e646f0a700076e4d2c51512eed4d30 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 10 Dec 2024 16:54:35 +1100 Subject: [PATCH 128/546] ci: Pull the Zephyr CI docker image from GitHub container reg. This image is 15GB so in theory this may be faster, although in testing the improvement is either non-existent or marginal. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tools/ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci.sh b/tools/ci.sh index bc013f5324111..d4bfe4933690b 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -747,7 +747,7 @@ ZEPHYR_SDK_VERSION=0.16.8 ZEPHYR_VERSION=v3.7.0 function ci_zephyr_setup { - IMAGE=zephyrprojectrtos/ci:${ZEPHYR_DOCKER_VERSION} + IMAGE=ghcr.io/zephyrproject-rtos/ci:${ZEPHYR_DOCKER_VERSION} docker pull ${IMAGE} From a9945fc528c950659793b6fb4979586297ea1497 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 11 Dec 2024 14:59:39 +1100 Subject: [PATCH 129/546] ci: Add caching of ccache for Zephyr. Similar to the ESP32 builds, but needs additional step to pass the ccache directory through to the Zephyr container. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- .github/workflows/ports_zephyr.yml | 4 ++++ tools/ci.sh | 3 +++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/ports_zephyr.yml b/.github/workflows/ports_zephyr.yml index ff3e19c7ea136..eb85af6a36154 100644 --- a/.github/workflows/ports_zephyr.yml +++ b/.github/workflows/ports_zephyr.yml @@ -42,6 +42,10 @@ jobs: # cache the "workspace" path: ./zephyrproject key: zephyr-workspace-${{ steps.versions.outputs.ZEPHYR }} + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: zephyr - name: Install packages run: source tools/ci.sh && ci_zephyr_setup - name: Install Zephyr diff --git a/tools/ci.sh b/tools/ci.sh index d4bfe4933690b..c67aeed0cecdf 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -754,12 +754,15 @@ function ci_zephyr_setup { # Directories cached by GitHub Actions, mounted # into the container ZEPHYRPROJECT_DIR="$(pwd)/zephyrproject" + CCACHE_DIR="$(pwd)/.ccache" mkdir -p "${ZEPHYRPROJECT_DIR}" + mkdir -p "${CCACHE_DIR}" docker run --name zephyr-ci -d -it \ -v "$(pwd)":/micropython \ -v "${ZEPHYRPROJECT_DIR}":/zephyrproject \ + -v "${CCACHE_DIR}":/root/.cache/ccache \ -e ZEPHYR_SDK_INSTALL_DIR=/opt/toolchains/zephyr-sdk-${ZEPHYR_SDK_VERSION} \ -e ZEPHYR_TOOLCHAIN_VARIANT=zephyr \ -e ZEPHYR_BASE=/zephyrproject/zephyr \ From 22804fccf312030104adf48f836dd8667411cad1 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 13 Aug 2024 10:54:36 +1000 Subject: [PATCH 130/546] stm32/boards/WEACT_F411_BLACKPILL: Add WeAct F411 'blackpill' boards. Adds board profile for the WeAct F411 'blackpill' which is a quite popular low cost ST dev board. This board also has optional spiflash so can be purchased in a few different configurations. Builds for v3.1 with no SPI Flash by default. Includes variants for different board versions and spi flash sizes. Signed-off-by: Andrew Leech --- .../boards/WEACT_F411_BLACKPILL/README.md | 55 +++++++++ .../stm32/boards/WEACT_F411_BLACKPILL/bdev.c | 45 +++++++ .../boards/WEACT_F411_BLACKPILL/board.json | 22 ++++ .../WEACT_F411_BLACKPILL/mpconfigboard.h | 112 ++++++++++++++++++ .../WEACT_F411_BLACKPILL/mpconfigboard.mk | 38 ++++++ .../mpconfigvariant_V13.mk | 1 + .../mpconfigvariant_V13_FLASH_4M.mk | 2 + .../mpconfigvariant_V20_FLASH_4M.mk | 2 + .../mpconfigvariant_V31_FLASH_8M.mk | 2 + .../mpconfigvariant_V31_XTAL_8M.mk | 2 + .../boards/WEACT_F411_BLACKPILL/pins.csv | 45 +++++++ .../WEACT_F411_BLACKPILL/stm32f4xx_hal_conf.h | 19 +++ 12 files changed, 345 insertions(+) create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/README.md create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/bdev.c create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/board.json create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.h create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.mk create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V13.mk create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V13_FLASH_4M.mk create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V20_FLASH_4M.mk create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V31_FLASH_8M.mk create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V31_XTAL_8M.mk create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/pins.csv create mode 100644 ports/stm32/boards/WEACT_F411_BLACKPILL/stm32f4xx_hal_conf.h diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/README.md b/ports/stm32/boards/WEACT_F411_BLACKPILL/README.md new file mode 100644 index 0000000000000..9af29e6b1e4ad --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/README.md @@ -0,0 +1,55 @@ +WeAct F411 'blackpill' +====================== + +The WeAct F411 blackpill board comes in a number of versions and variants. +All have a footprint for a SPI flash chip on the back, though the board is +often sold without any flash chip loaded. + +At the time of writing (Sep 2024) v3.1 is the current version. +This version is sold with both 25Mhz HSE crystal (same as previous versions) and also +with a 8Mhz crystal. The end user should be careful to confirm which variant is +purchased and/or read the markings on the crystal to know which variant build to load. + +The previous v2.0 boards had changed the MCU pinout for the spi flash chip so requires +soft-spi support enabled in the variant settings, unlike v3.1 or v1.3 which is +compatible with the hardware spi peripheral. + +The original v1.3 boards did not include a user switch on the top, it only has +"BOOT0" and "NRST" switches to load bootloader and reset the board respectively. + +For more information see: https://github.com/WeActStudio/WeActStudio.MiniSTM32F4x1 + +Note: The pins used by features like spiflash and USB are also broken out to the +gpio headers on the sides of the board. +If these peripherals / features are enabled then these external pins must be avoided to ensure +there are no conflicts. [pins.csv](pins.csv) should be consulted to check all pins assigned +to alternate functions on the board. + +Customising the build +--------------------- + +After purchasing a board without any spiflash chip loaded the user can solder on +their own of any desired size. Most brands of spiflash in SO8 pinout are compatible +however some do have a slightly different protocol so may not work out of the box +with micropython. Brand compatibility is outide the scope of this doc. + +Once a custom spiflash chip has been loaded onto the board micropython should +be built with the flash size specified. After doing so the spiflash chip will +be used for the FAT/LFS main filesystem. + +Examples: + +For a v3.1 / 25Mhz (default version) board with 16MiB flash chip loaded: +``` bash +make -C ports/stm32 BOARD=WEACT_F411_BLACKPILL SPI_FLASH_SIZE_MB=16 +``` + +For a v3.1 / 8Mhz board with 4MiB flash chip loaded: +``` bash +make -C ports/stm32 BOARD=WEACT_F411_BLACKPILL BOARD_VARIANT=V31_XTAL_8M SPI_FLASH_SIZE_MB=4 +``` + +For a v1.3 board with 2MiB flash chip loaded and XTAL manually replaced with 8Mhz: +``` bash +make -C ports/stm32 BOARD=WEACT_F411_BLACKPILL BOARD_VARIANT=V13 SPI_FLASH_SIZE_MB=2 XTAL_FREQ_MHZ=8 +``` diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/bdev.c b/ports/stm32/boards/WEACT_F411_BLACKPILL/bdev.c new file mode 100644 index 0000000000000..fe349e0c8a4ed --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/bdev.c @@ -0,0 +1,45 @@ +#include "storage.h" +#include "spi.h" +#include "py/mpconfig.h" + +#if !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE + +#if WEACT_F411_V20 +// External SPI flash uses SPI interface, but not on all HW spi pins. +static const mp_soft_spi_obj_t spi_bus = { + .delay_half = MICROPY_HW_SOFTSPI_MIN_DELAY, + .polarity = 0, + .phase = 0, + .sck = MICROPY_HW_SPIFLASH_SCK, + .mosi = MICROPY_HW_SPIFLASH_MOSI, + .miso = MICROPY_HW_SPIFLASH_MISO, +}; + +#else // WEACT_F411_V1.3 or WEACT_F411_V3.1 +static const spi_proto_cfg_t spi_bus = { + .spi = &spi_obj[0], // SPI1 + .baudrate = 25000000, + .polarity = 0, + .phase = 0, + .bits = 8, + .firstbit = SPI_FIRSTBIT_MSB, +}; +#endif + +#if MICROPY_HW_SPIFLASH_ENABLE_CACHE +static mp_spiflash_cache_t spi_bdev_cache; +#endif + +const mp_spiflash_config_t spiflash_config = { + .bus_kind = MP_SPIFLASH_BUS_SPI, + .bus.u_spi.cs = MICROPY_HW_SPIFLASH_CS, + .bus.u_spi.data = (void *)&spi_bus, + .bus.u_spi.proto = &spi_proto, + #if MICROPY_HW_SPIFLASH_ENABLE_CACHE + .cache = &spi_bdev_cache, + #endif +}; + +spi_bdev_t spi_bdev; + +#endif diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/board.json b/ports/stm32/boards/WEACT_F411_BLACKPILL/board.json new file mode 100644 index 0000000000000..5b10787216995 --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/board.json @@ -0,0 +1,22 @@ +{ + "deploy": [ + "../PYBV10/deploy.md" + ], + "docs": "", + "features": [], + "images": [ + "WEACTV20_F411.jpg" + ], + "mcu": "stm32f411", + "product": "WeAct F411 'blackpill'. Default variant is v3.1 with no SPI Flash.", + "thumbnail": "", + "url": "https://github.com/WeActStudio/WeActStudio.MiniSTM32F4x1", + "variants": { + "V13": "v1.3 board with no SPI Flash", + "V13_FLASH_4M": "v1.3 board with 4MB SPI Flash", + "V20_FLASH_4M": "v2.0 board with 4MB SPI Flash", + "V31_FLASH_8M": "v3.1 board with 8MB SPI Flash", + "V31_XTAL_8M": "v3.1 board with 8MHz crystal" + }, + "vendor": "WeAct Studio" +} diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.h b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.h new file mode 100644 index 0000000000000..561c1a9f1516e --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.h @@ -0,0 +1,112 @@ +// based off the following two repositories: +// * https://github.com/mcauser/WEACT_F411CEU6 +// * https://github.com/YXZhu/micropython + +#define MICROPY_HW_BOARD_NAME "WEACT_F411_BLACKPILL" +#define MICROPY_HW_MCU_NAME "STM32F411CE" +#define MICROPY_HW_FLASH_FS_LABEL "WEACT_F411_BLACKPILL" + +// some users having issues with FLASH_LATENCY_2, so set to 3 +// from https://forum.micropython.org/viewtopic.php?t=7154 +#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_3 + +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_USB (1) +#define MICROPY_HW_USB_FS (1) + +// Switch +#if WEACT_F411_V13 +#define MICROPY_HW_HAS_SWITCH (0) +#else +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_USRSW_PIN (pyb_pin_SW) +#define MICROPY_HW_USRSW_PULL (GPIO_PULLUP) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_FALLING) +#define MICROPY_HW_USRSW_PRESSED (0) +#endif + +// LEDs +#define MICROPY_HW_LED1 (pyb_pin_LED_BLUE) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) + +// RTC +#define MICROPY_HW_RTC_USE_LSE (1) +#define MICROPY_HW_RTC_USE_US (0) +#define MICROPY_HW_RTC_USE_CALOUT (1) + +// Set PLLM same as HSE in MHZ. +#define MICROPY_HW_CLK_PLLM (MICROPY_HW_HSE_SPEED_MHZ) +// Configure PLL for final CPU freq of 96MHz +#define MICROPY_HW_CLK_PLLN (192) +#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) +#define MICROPY_HW_CLK_PLLQ (4) + +// UART config +#define MICROPY_HW_UART1_TX (pin_A9) +#define MICROPY_HW_UART1_RX (pin_A10) + +#define MICROPY_HW_UART2_TX (pin_A2) +#define MICROPY_HW_UART2_RX (pin_A3) + +#define MICROPY_HW_UART_REPL (PYB_UART_1) +#define MICROPY_HW_UART_REPL_BAUD (115200) + +// I2C bus +#define MICROPY_HW_I2C1_SCL (pin_B6) +#define MICROPY_HW_I2C1_SDA (pin_B7) +#define MICROPY_HW_I2C2_SCL (pin_B10) +#define MICROPY_HW_I2C2_SDA (pin_B9) +#define MICROPY_HW_I2C3_SCL (pin_A8) +#define MICROPY_HW_I2C3_SDA (pin_B8) + +// SPI bus +// SPI 1 is generally used for the SPI flash if enabled below. +#define MICROPY_HW_SPI1_NSS (pin_A4) +#define MICROPY_HW_SPI1_SCK (pin_A5) +#define MICROPY_HW_SPI1_MISO (pin_A6) +#define MICROPY_HW_SPI1_MOSI (pin_A7) + +#define MICROPY_HW_SPI2_NSS (pin_B12) +#define MICROPY_HW_SPI2_SCK (pin_B13) +#define MICROPY_HW_SPI2_MISO (pin_B14) +#define MICROPY_HW_SPI2_MOSI (pin_B15) + +// SPI 3 is not accessible if SPI flash module is used on V2.0 (PB4 conflict) +#define MICROPY_HW_SPI3_NSS (pin_A15) +#define MICROPY_HW_SPI3_SCK (pin_B3) +#define MICROPY_HW_SPI3_MISO (pin_B4) +#define MICROPY_HW_SPI3_MOSI (pin_B5) + +// External SPI Flash configuration + +#if !MICROPY_HW_SPIFLASH_SIZE_BYTES +// Use internal filesystem if spiflash not enabled. +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1) + +#else +// Disable internal filesystem to use spiflash. +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) + +// SPI flash pins +#define MICROPY_HW_SPIFLASH_CS (pyb_pin_FLASH_CS) +#define MICROPY_HW_SPIFLASH_SCK (pyb_pin_FLASH_SCK) +#define MICROPY_HW_SPIFLASH_MOSI (pyb_pin_FLASH_MOSI) +#if WEACT_F411_V13 +#define MICROPY_HW_SPIFLASH_MISO (pyb_pin_FLASH_MISO_V13) +#elif WEACT_F411_V20 +#define MICROPY_HW_SPIFLASH_MISO (pyb_pin_FLASH_MISO_V20) +#else +#define MICROPY_HW_SPIFLASH_MISO (pyb_pin_FLASH_MISO_V31) +#endif + +extern const struct _mp_spiflash_config_t spiflash_config; +extern struct _spi_bdev_t spi_bdev; +#define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) +#define MICROPY_HW_BDEV_SPIFLASH (&spi_bdev) +#define MICROPY_HW_BDEV_SPIFLASH_CONFIG (&spiflash_config) +#define MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES (MICROPY_HW_SPIFLASH_SIZE_BITS / 8) +#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev) // for extended block protocol +#define MICROPY_HW_SPIFLASH_SIZE_BITS (MICROPY_HW_SPIFLASH_SIZE_BYTES * 8) +#endif diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.mk b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.mk new file mode 100644 index 0000000000000..3ae152294d27c --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.mk @@ -0,0 +1,38 @@ +MCU_SERIES = f4 +CMSIS_MCU = STM32F411xE +AF_FILE = boards/stm32f411_af.csv +LD_FILES = boards/stm32f411.ld boards/common_ifs.ld +TEXT0_ADDR = 0x08000000 +TEXT1_ADDR = 0x08020000 + +MICROPY_VFS_LFS2 = 1 + +# Settings for version, spi flash and HSE xtal. +# These are used in variant configs and/or on make command line. +# If provided on make command line the build folder name will be updated to match +# When set in variant they're included after this file so the following ifdef blocks are ignored. + +ifdef BOARD_VERSION +BUILD := $(BUILD)_V$(BOARD_VERSION) +endif + +ifdef SPI_FLASH_SIZE_MB +BUILD := $(BUILD)_FLASH_$(SPI_FLASH_SIZE_MB)M +endif + +ifdef XTAL_FREQ_MHZ +BUILD := $(BUILD)_XTAL_$(XTAL_FREQ_MHZ)M +endif + +# Blackpill v3.1 board by default +BOARD_VERSION ?= 31 + +# No flash chip in default build - use internal flash +SPI_FLASH_SIZE_MB ?= 0 + +# 25Mhz HSE crystal by default. +XTAL_FREQ_MHZ ?= 25 + +CFLAGS += -DWEACT_F411_V$(BOARD_VERSION)=1 +CFLAGS += -DMICROPY_HW_SPIFLASH_SIZE_BYTES="($(SPI_FLASH_SIZE_MB) * 1024 * 1024)" +CFLAGS += -DMICROPY_HW_HSE_SPEED_MHZ="($(XTAL_FREQ_MHZ))" diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V13.mk b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V13.mk new file mode 100644 index 0000000000000..47c94faee4012 --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V13.mk @@ -0,0 +1 @@ +BOARD_VERSION = 13 diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V13_FLASH_4M.mk b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V13_FLASH_4M.mk new file mode 100644 index 0000000000000..ae7aeaaf52d13 --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V13_FLASH_4M.mk @@ -0,0 +1,2 @@ +BOARD_VERSION = 13 +SPI_FLASH_SIZE_MB = 4 diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V20_FLASH_4M.mk b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V20_FLASH_4M.mk new file mode 100644 index 0000000000000..21ce84ceaff85 --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V20_FLASH_4M.mk @@ -0,0 +1,2 @@ +BOARD_VERSION = 20 +SPI_FLASH_SIZE_MB = 4 diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V31_FLASH_8M.mk b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V31_FLASH_8M.mk new file mode 100644 index 0000000000000..87867b1fedf45 --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V31_FLASH_8M.mk @@ -0,0 +1,2 @@ +BOARD_VERSION = 31 +SPI_FLASH_SIZE_MB = 8 diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V31_XTAL_8M.mk b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V31_XTAL_8M.mk new file mode 100644 index 0000000000000..5eb05e49fe926 --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigvariant_V31_XTAL_8M.mk @@ -0,0 +1,2 @@ +BOARD_VERSION = 31 +XTAL_FREQ_MHZ = 8 diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/pins.csv b/ports/stm32/boards/WEACT_F411_BLACKPILL/pins.csv new file mode 100644 index 0000000000000..eabf753de852b --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/pins.csv @@ -0,0 +1,45 @@ +,PA0 +,PA1 +,PA2 +,PA3 +,PA4 +,PA5 +,PA6 +,PA7 +,PA8 +,PA9 +,PA10 +,PA11 +,PA12 +,PA15 +,PB0 +,PB1 +,PB2 +,PB3 +,PB4 +,PB5 +,PB6 +,PB7 +,PB8 +,PB9 +,PB10 +,PB12 +,PB13 +,PB14 +,PB15 +,PC14 +,PC15 +LED_BLUE,PC13 +SW,PA0 +-SWDIO,PA13 +-SWCLK,PA14 +-OSC32_IN,PH0 +-OSC32_OUT,PH1 +-USB_DM,PA11 +-USB_DP,PA12 +-FLASH_CS,PA4 +-FLASH_SCK,PA5 +-FLASH_MOSI,PA7 +-FLASH_MISO_V13,PA6 +-FLASH_MISO_V20,PB4 +-FLASH_MISO_V31,PA6 diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/stm32f4xx_hal_conf.h b/ports/stm32/boards/WEACT_F411_BLACKPILL/stm32f4xx_hal_conf.h new file mode 100644 index 0000000000000..c11d72a213ff6 --- /dev/null +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/stm32f4xx_hal_conf.h @@ -0,0 +1,19 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2024 Andrew Leech + */ +#ifndef MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H + +#include "boards/stm32f4xx_hal_conf_base.h" + +// Oscillator values in Hz +#define HSE_VALUE (MICROPY_HW_HSE_SPEED_MHZ * 1000000) +#define LSE_VALUE (32768) +#define EXTERNAL_CLOCK_VALUE (12288000) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#endif // MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H From 7924b31050271c76ff6cf26c6fda5fe3ba615d08 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 3 Sep 2024 10:41:25 +1000 Subject: [PATCH 131/546] stm32: Generate PLL tables from pre-processed headers. Allows boards to configure their HSE and PLL values in variants. Signed-off-by: Andrew Leech --- ports/stm32/Makefile | 12 +++---- ports/stm32/boards/plli2svalues.py | 55 +++++++++++++++--------------- ports/stm32/boards/pllvalues.py | 30 ++++++++-------- ports/stm32/machine_i2s.c | 2 ++ ports/stm32/powerctrl.c | 17 ++++++++- 5 files changed, 67 insertions(+), 49 deletions(-) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 806added49e9a..a9612f1a5792e 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -640,15 +640,15 @@ $(BUILD)/%_$(BOARD).c $(HEADER_BUILD)/%.h $(HEADER_BUILD)/%_af_const.h $(HEADER_ --output-source $(GEN_PINS_SRC) --output-header $(GEN_PINS_HDR) \ --output-af-const $(GEN_PINS_AF_CONST) --output-af-defs $(GEN_PINS_AF_DEFS) -powerctrl.c: $(GEN_PLLFREQTABLE_HDR) -$(GEN_PLLFREQTABLE_HDR): $(PLLVALUES) | $(HEADER_BUILD) +$(BUILD)/powerctrl.o: $(GEN_PLLFREQTABLE_HDR) +$(GEN_PLLFREQTABLE_HDR): $(PLLVALUES) | $(HEADER_BUILD)/qstr.i.last $(ECHO) "GEN $@" - $(Q)$(PYTHON) $(PLLVALUES) -c -m $(CMSIS_MCU_LOWER) file:$(BOARD_DIR)/stm32$(MCU_SERIES)xx_hal_conf.h > $@ + $(Q)$(PYTHON) $(PLLVALUES) -c -m $(CMSIS_MCU_LOWER) file:$(HEADER_BUILD)/qstr.i.last > $@ -$(TOP)/extmod/machine_i2s.c: $(GEN_PLLI2STABLE_HDR) -$(GEN_PLLI2STABLE_HDR): $(PLLI2SVALUES) | $(HEADER_BUILD) +$(BUILD)/extmod/machine_i2s.o: $(GEN_PLLI2STABLE_HDR) +$(GEN_PLLI2STABLE_HDR): $(PLLI2SVALUES) | $(HEADER_BUILD)/qstr.i.last $(ECHO) "GEN $@" - $(Q)$(PYTHON) $(PLLI2SVALUES) -c -m $(CMSIS_MCU_LOWER) hse:$(BOARD_DIR)/stm32$(MCU_SERIES)xx_hal_conf.h pllm:$(BOARD_DIR)/mpconfigboard.h > $@ + $(Q)$(PYTHON) $(PLLI2SVALUES) -c -m $(CMSIS_MCU_LOWER) file:$(HEADER_BUILD)/qstr.i.last > $@ $(BUILD)/modstm.o: $(GEN_STMCONST_HDR) $(HEADER_BUILD)/modstm_const.h: $(CMSIS_MCU_HDR) make-stmconst.py | $(HEADER_BUILD) diff --git a/ports/stm32/boards/plli2svalues.py b/ports/stm32/boards/plli2svalues.py index 8ff7f0ff8fcdf..436dcee7444a7 100644 --- a/ports/stm32/boards/plli2svalues.py +++ b/ports/stm32/boards/plli2svalues.py @@ -123,22 +123,21 @@ def generate_c_table(plli2s_table, hse, pllm): print("}") -def search_header(filename, re_include, re_define, lookup, val): - regex_include = re.compile(re_include) +def search_header(filename, re_define, lookup): regex_define = re.compile(re_define) + val = None with open(filename) as f: for line in f: line = line.strip() - m = regex_include.match(line) - if m: - # Search included file - search_header(m.group(1), re_include, re_define, lookup, val) - continue m = regex_define.match(line) if m: # found lookup value + found = m.group(3) + if "*" in found or "/" in found: + # process define using multiply or divide to calculate value + found = eval(found) if m.group(1) == lookup: - val[0] = int(m.group(3)) + val = int(found) return val @@ -166,35 +165,37 @@ def main(): break if mcu_series in mcu_support_plli2s: - if len(argv) != 2: + if len(argv) not in (1, 2): print("usage: pllvalues.py [-c] [-m ] ") sys.exit(1) if argv[0].startswith("hse:"): - # extract HSE_VALUE from header file - (hse,) = search_header( - argv[0][len("hse:") :], - r'#include "(boards/[A-Za-z0-9_./]+)"', - r"#define +(HSE_VALUE) +\((\(uint32_t\))?([0-9]+)\)", - "HSE_VALUE", - [None], + hse = int(argv[0][len("hse:") :]) + + if argv[0].startswith("pllm:"): + pllm = int(argv[0][len("pllm:") :]) + + if argv[0].startswith("file:"): + # extract hse value from processed header files + hse = search_header( + argv[0][len("file:") :], + r"static.* (micropy_hw_hse_value) = +\(*(\(uint32_t\))?([0-9 +-/\*]+)\)*;", + "micropy_hw_hse_value", ) if hse is None: - raise ValueError("%s does not contain a definition of HSE_VALUE" % argv[0]) - argv.pop(0) + raise ValueError( + "%s does not contain a definition of micropy_hw_hse_value" % argv[0] + ) - if argv[0].startswith("pllm:"): - # extract MICROPY_HW_CLK_PLLM from header file - (pllm,) = search_header( - argv[0][len("pllm:") :], - r'#include "(boards/[A-Za-z0-9_./]+)"', - r"#define +(MICROPY_HW_CLK_PLLM) +\((\(uint32_t\))?([0-9]+)\)", - "MICROPY_HW_CLK_PLLM", - [None], + # extract pllm value from processed header files + pllm = search_header( + argv[0][len("file:") :], + r"static.* (micropy_hw_clk_pllm) = +\(*(\(uint32_t\))?([0-9 +-/\*]+)\)*;", + "micropy_hw_clk_pllm", ) if pllm is None: raise ValueError( - "%s does not contain a definition of MICROPY_HW_CLK_PLLM" % argv[0] + "%s does not contain a definition of micropy_hw_clk_pllm" % argv[0] ) argv.pop(0) diff --git a/ports/stm32/boards/pllvalues.py b/ports/stm32/boards/pllvalues.py index 2db6b5f257044..5edc1d51fd6bc 100644 --- a/ports/stm32/boards/pllvalues.py +++ b/ports/stm32/boards/pllvalues.py @@ -228,26 +228,26 @@ def print_table(hse, valid_plls): print("found %u valid configurations" % len(valid_plls)) -def search_header_for_hsx_values(filename, vals): - regex_inc = re.compile(r'#include "(boards/[A-Za-z0-9_./]+)"') - regex_def = re.compile(r"#define +(HSE_VALUE|HSI_VALUE) +\((\(uint32_t\))?([0-9]+)\)") +def search_header_for_hsx_values(filename): + hse = hsi = None + regex_def = re.compile( + r"static.* +(micropy_hw_hs[ei]_value) = +\(*(\(uint32_t\))?([0-9 +-/\*]+)\)*;", + ) with open(filename) as f: for line in f: line = line.strip() - m = regex_inc.match(line) - if m: - # Search included file - search_header_for_hsx_values(m.group(1), vals) - continue m = regex_def.match(line) if m: # Found HSE_VALUE or HSI_VALUE - val = int(m.group(3)) // 1000000 - if m.group(1) == "HSE_VALUE": - vals[0] = val + found = m.group(3) + if "*" in found or "/" in found: + found = eval(found) + val = int(found) // 1000000 + if m.group(1) == "micropy_hw_hse_value": + hse = val else: - vals[1] = val - return vals + hsi = val + return hse, hsi def main(): @@ -280,9 +280,9 @@ def main(): if argv[0].startswith("file:"): # extract HSE_VALUE, and optionally HSI_VALUE, from header file - hse, hsi = search_header_for_hsx_values(argv[0][5:], [None, None]) + hse, hsi = search_header_for_hsx_values(argv[0][5:]) if hse is None: - raise ValueError("%s does not contain a definition of HSE_VALUE" % argv[0]) + raise ValueError("%s does not contain a definition of micropy_hw_hse_value" % argv[0]) else: # HSE given directly as an integer hse = int(argv[0]) diff --git a/ports/stm32/machine_i2s.c b/ports/stm32/machine_i2s.c index a2a0a8291008c..d7d4dc14b988b 100644 --- a/ports/stm32/machine_i2s.c +++ b/ports/stm32/machine_i2s.c @@ -33,7 +33,9 @@ #include "py/mphal.h" #include "pin.h" #include "dma.h" +#ifndef NO_QSTR #include "genhdr/plli2stable.h" +#endif // Notes on this port's specific implementation of I2S: // - the DMA callbacks (1/2 complete and complete) are used to implement the asynchronous background operations diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index 9d3f374f93757..eea009e2d782b 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -28,8 +28,23 @@ #include "py/mphal.h" #include "powerctrl.h" #include "rtc.h" -#include "genhdr/pllfreqtable.h" #include "extmod/modbluetooth.h" +#include "py/mpconfig.h" +#ifndef NO_QSTR +#include "genhdr/pllfreqtable.h" +#endif + +// These will be defined / expanded in pre-processor output for use in the +// boards/pllvalues.py script, then generally stripped from final firmware. +#ifdef HSI_VALUE +static uint32_t __attribute__((unused)) micropy_hw_hsi_value = HSI_VALUE; +#endif +#ifdef HSE_VALUE +static uint32_t __attribute__((unused)) micropy_hw_hse_value = HSE_VALUE; +#endif +#ifdef MICROPY_HW_CLK_PLLM +static uint32_t __attribute__((unused)) micropy_hw_clk_pllm = MICROPY_HW_CLK_PLLM; +#endif #if defined(STM32H5) || defined(STM32H7) #define RCC_SR RSR From 3c1722e705002b2182f138a114f4408ae7ebac38 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 19 Dec 2024 00:55:31 +1100 Subject: [PATCH 132/546] stm32: Fix extraction of hse/hsi/pllm values from preprocessed source. The expressions for the `micropy_hw_hse_value` etc variables may contain parenthesis, eg `micropy_hw_hse_value = ((25) * 1000000)`. To handle such a case, simplify the regex and always use `eval(found)` to evaluate the expression. Signed-off-by: Damien George --- ports/stm32/boards/plli2svalues.py | 11 ++++------- ports/stm32/boards/pllvalues.py | 8 +++----- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/ports/stm32/boards/plli2svalues.py b/ports/stm32/boards/plli2svalues.py index 436dcee7444a7..e5ea4e8dfd49b 100644 --- a/ports/stm32/boards/plli2svalues.py +++ b/ports/stm32/boards/plli2svalues.py @@ -132,12 +132,9 @@ def search_header(filename, re_define, lookup): m = regex_define.match(line) if m: # found lookup value - found = m.group(3) - if "*" in found or "/" in found: - # process define using multiply or divide to calculate value - found = eval(found) + found = m.group(2) if m.group(1) == lookup: - val = int(found) + val = eval(found) return val @@ -179,7 +176,7 @@ def main(): # extract hse value from processed header files hse = search_header( argv[0][len("file:") :], - r"static.* (micropy_hw_hse_value) = +\(*(\(uint32_t\))?([0-9 +-/\*]+)\)*;", + r"static.* (micropy_hw_hse_value) = +([0-9 +-/\*()]+);", "micropy_hw_hse_value", ) if hse is None: @@ -190,7 +187,7 @@ def main(): # extract pllm value from processed header files pllm = search_header( argv[0][len("file:") :], - r"static.* (micropy_hw_clk_pllm) = +\(*(\(uint32_t\))?([0-9 +-/\*]+)\)*;", + r"static.* (micropy_hw_clk_pllm) = +([0-9 +-/\*()]+);", "micropy_hw_clk_pllm", ) if pllm is None: diff --git a/ports/stm32/boards/pllvalues.py b/ports/stm32/boards/pllvalues.py index 5edc1d51fd6bc..d8856bfecdbd4 100644 --- a/ports/stm32/boards/pllvalues.py +++ b/ports/stm32/boards/pllvalues.py @@ -231,7 +231,7 @@ def print_table(hse, valid_plls): def search_header_for_hsx_values(filename): hse = hsi = None regex_def = re.compile( - r"static.* +(micropy_hw_hs[ei]_value) = +\(*(\(uint32_t\))?([0-9 +-/\*]+)\)*;", + r"static.* +(micropy_hw_hs[ei]_value) = +([0-9 +-/\*()]+);", ) with open(filename) as f: for line in f: @@ -239,10 +239,8 @@ def search_header_for_hsx_values(filename): m = regex_def.match(line) if m: # Found HSE_VALUE or HSI_VALUE - found = m.group(3) - if "*" in found or "/" in found: - found = eval(found) - val = int(found) // 1000000 + found = m.group(2) + val = eval(found) // 1000000 if m.group(1) == "micropy_hw_hse_value": hse = val else: From e323da729101c9fe626b5b08cafef2dbdbb70441 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 17 Dec 2024 15:15:59 +1100 Subject: [PATCH 133/546] tests/run-tests.py: Set name of injected test module to '__main__'. Running unittest-based tests with --via-mpy is currently broken, because the unittest test needs the module to be named `__main__`, whereas it's actually called `__injected_test`. Fix this by changing the name when the file is opened. Signed-off-by: Damien George --- tests/run-tests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/run-tests.py b/tests/run-tests.py index 6fe45707bb6cd..c72178cd760c2 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -59,10 +59,13 @@ def base_path(*p): os.environ["PYTHONIOENCODING"] = "utf-8" # Code to allow a target MicroPython to import an .mpy from RAM +# Note: the module is named `__injected_test` but it needs to have `__name__` set to +# `__main__` so that the test sees itself as the main module, eg so unittest works. injected_import_hook_code = """\ import sys, os, io, vfs class __File(io.IOBase): def __init__(self): + sys.modules['__injected_test'].__name__ = '__main__' self.off = 0 def ioctl(self, request, arg): return 0 From b5de529ffc17dd02e24c3f2b14e230eefe42dc7a Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 28 Jan 2023 10:12:49 +0100 Subject: [PATCH 134/546] docs: Fix the quickref documentation of rtc.datetime(). Such that it matches the implementation and the documentation of the `machine.RTC` class. Signed-off-by: robert-hh --- docs/esp32/quickref.rst | 4 +++- docs/esp8266/quickref.rst | 4 +++- docs/mimxrt/quickref.rst | 4 +++- docs/pyboard/quickref.rst | 4 +++- docs/renesas-ra/quickref.rst | 5 +++-- docs/rp2/quickref.rst | 3 ++- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 5cce96d687875..c5c2dfb38e570 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -577,7 +577,9 @@ See :ref:`machine.RTC ` :: from machine import RTC rtc = RTC() - rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time + rtc.datetime((2017, 8, 23, 0, 1, 12, 48, 0)) # set a specific date and + # time, eg. 2017/8/23 1:12:48 + # the day-of-week value is ignored rtc.datetime() # get date and time WDT (Watchdog timer) diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index 635f1f834bb60..e17b60f676105 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -284,7 +284,9 @@ See :ref:`machine.RTC ` :: from machine import RTC rtc = RTC() - rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time + rtc.datetime((2017, 8, 23, 0, 1, 12, 48, 0)) # set a specific date and + # time, eg. 2017/8/23 1:12:48 + # the day-of-week value is ignored rtc.datetime() # get date and time # synchronize with ntp diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index 34e0aa79f139f..49d7befc7452b 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -429,7 +429,9 @@ See :ref:`machine.RTC `:: from machine import RTC rtc = RTC() - rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time + rtc.datetime((2017, 8, 23, 0, 1, 12, 48, 0)) # set a specific date and + # time, eg. 2017/8/23 1:12:48 + # the day-of-week value is ignored rtc.datetime() # get date and time rtc.now() # return date and time in CPython format. diff --git a/docs/pyboard/quickref.rst b/docs/pyboard/quickref.rst index 62157bff0a472..52ddc29b19340 100644 --- a/docs/pyboard/quickref.rst +++ b/docs/pyboard/quickref.rst @@ -138,7 +138,9 @@ See :ref:`pyb.RTC ` :: from pyb import RTC rtc = RTC() - rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time + rtc.datetime((2017, 8, 23, 0, 1, 12, 48, 0)) # set a specific date and + # time, eg. 2017/8/23 1:12:48 + # the day-of-week value is ignored rtc.datetime() # get date and time PWM (pulse width modulation) diff --git a/docs/renesas-ra/quickref.rst b/docs/renesas-ra/quickref.rst index ea9a38db15fcb..b5283707fc8f0 100644 --- a/docs/renesas-ra/quickref.rst +++ b/docs/renesas-ra/quickref.rst @@ -206,8 +206,9 @@ See :ref:`machine.RTC ` :: from machine import RTC rtc = RTC() - rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time - # time, eg 2017/8/23 1:12:48 + rtc.datetime((2017, 8, 23, 0, 1, 12, 48, 0)) # set a specific date and + # time, eg. 2017/8/23 1:12:48 + # the day-of-week value is ignored rtc.datetime() # get date and time Following functions are not supported at the present:: diff --git a/docs/rp2/quickref.rst b/docs/rp2/quickref.rst index 9b82ea5dc6749..6be3180500746 100644 --- a/docs/rp2/quickref.rst +++ b/docs/rp2/quickref.rst @@ -310,8 +310,9 @@ See :ref:`machine.RTC ` :: from machine import RTC rtc = RTC() - rtc.datetime((2017, 8, 23, 2, 12, 48, 0, 0)) # set a specific date and + rtc.datetime((2017, 8, 23, 0, 1, 12, 48, 0)) # set a specific date and # time, eg. 2017/8/23 1:12:48 + # the day-of-week value is ignored rtc.datetime() # get date and time WDT (Watchdog timer) From 0302cd65e85cb255670cf6eb32d9e6cee241128c Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 28 Jan 2023 14:57:22 +0100 Subject: [PATCH 135/546] mimxrt/machine_rtc: Drop machine.RTC.now() method. This is not part of the common machine API. It's dropped on the mimxrt port and kept only on the cc3200 port for legacy. Also show the port availability of `RTC.now()` in the documentation. Signed-off-by: robert-hh --- docs/library/machine.RTC.rst | 2 ++ ports/mimxrt/machine_rtc.c | 20 -------------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/docs/library/machine.RTC.rst b/docs/library/machine.RTC.rst index a457189037b51..ed93c5fff85f1 100644 --- a/docs/library/machine.RTC.rst +++ b/docs/library/machine.RTC.rst @@ -48,6 +48,8 @@ Methods Get get the current datetime tuple. + Availability: WiPy. + .. method:: RTC.deinit() Resets the RTC to the time of January 1, 2015 and starts running it again. diff --git a/ports/mimxrt/machine_rtc.c b/ports/mimxrt/machine_rtc.c index 0d6fc54c8b67c..91769ea33416b 100644 --- a/ports/mimxrt/machine_rtc.c +++ b/ports/mimxrt/machine_rtc.c @@ -230,25 +230,6 @@ static mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, machine_rtc_datetime); -static mp_obj_t machine_rtc_now(mp_obj_t self_in) { - // Get date and time in CPython order. - snvs_lp_srtc_datetime_t srtc_date; - SNVS_LP_SRTC_GetDatetime(SNVS, &srtc_date); - - mp_obj_t tuple[8] = { - mp_obj_new_int(srtc_date.year), - mp_obj_new_int(srtc_date.month), - mp_obj_new_int(srtc_date.day), - mp_obj_new_int(srtc_date.hour), - mp_obj_new_int(srtc_date.minute), - mp_obj_new_int(srtc_date.second), - mp_obj_new_int(0), - mp_const_none, - }; - return mp_obj_new_tuple(8, tuple); -} -static MP_DEFINE_CONST_FUN_OBJ_1(machine_rtc_now_obj, machine_rtc_now); - static mp_obj_t machine_rtc_init(mp_obj_t self_in, mp_obj_t date) { mp_obj_t args[2] = {self_in, date}; machine_rtc_datetime_helper(2, args); @@ -389,7 +370,6 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(machine_rtc_irq_obj, 1, machine_rtc_irq); static const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_rtc_init_obj) }, { MP_ROM_QSTR(MP_QSTR_datetime), MP_ROM_PTR(&machine_rtc_datetime_obj) }, - { MP_ROM_QSTR(MP_QSTR_now), MP_ROM_PTR(&machine_rtc_now_obj) }, { MP_ROM_QSTR(MP_QSTR_calibration), MP_ROM_PTR(&machine_rtc_calibration_obj) }, { MP_ROM_QSTR(MP_QSTR_alarm), MP_ROM_PTR(&machine_rtc_alarm_obj) }, { MP_ROM_QSTR(MP_QSTR_alarm_left), MP_ROM_PTR(&machine_rtc_alarm_left_obj) }, From f4e45995232ad52aeb4da270e6ca7571695e339b Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 28 Jan 2023 15:19:37 +0100 Subject: [PATCH 136/546] ports: Fix machine.RTC.init() method so argument order matches the docs. This commit makes the argument ordering of `machine.RTC.init()` the same for all the ports that implement arguments to this method: cc3200, esp32, mimxrt and samd. The cc3200 argument ordering is used, which matches the documentation. Also document the availability and the differing semantics for the stm32 and renesas-ra port. Signed-off-by: robert-hh --- docs/library/machine.RTC.rst | 9 ++++++++- ports/esp32/machine_rtc.c | 15 +++++++++++---- ports/mimxrt/machine_rtc.c | 12 ++++++------ ports/samd/machine_rtc.c | 12 ++++++------ 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/docs/library/machine.RTC.rst b/docs/library/machine.RTC.rst index ed93c5fff85f1..e2ddd728bde4c 100644 --- a/docs/library/machine.RTC.rst +++ b/docs/library/machine.RTC.rst @@ -42,7 +42,14 @@ Methods Initialise the RTC. Datetime is a tuple of the form: - ``(year, month, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]])`` + ``(year, month, day, hour, minute, second, microsecond, tzinfo)`` + + All eight arguments must be present. The ``microsecond`` and ``tzinfo`` + values are currently ignored but might be used in the future. + + Availability: CC3200, ESP32, MIMXRT, SAMD. The rtc.init() method on + the stm32 and renesas-ra ports just (re-)starts the RTC and does not + accept arguments. .. method:: RTC.now() diff --git a/ports/esp32/machine_rtc.c b/ports/esp32/machine_rtc.c index 087ba9d69efe7..a2f4bb132d4ea 100644 --- a/ports/esp32/machine_rtc.c +++ b/ports/esp32/machine_rtc.c @@ -101,7 +101,7 @@ static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, s return (mp_obj_t)&machine_rtc_obj; } -static mp_obj_t machine_rtc_datetime_helper(mp_uint_t n_args, const mp_obj_t *args) { +static mp_obj_t machine_rtc_datetime_helper(mp_uint_t n_args, const mp_obj_t *args, int hour_index) { if (n_args == 1) { // Get time @@ -131,7 +131,14 @@ static mp_obj_t machine_rtc_datetime_helper(mp_uint_t n_args, const mp_obj_t *ar mp_obj_get_array_fixed_n(args[1], 8, &items); struct timeval tv = {0}; - tv.tv_sec = timeutils_seconds_since_epoch(mp_obj_get_int(items[0]), mp_obj_get_int(items[1]), mp_obj_get_int(items[2]), mp_obj_get_int(items[4]), mp_obj_get_int(items[5]), mp_obj_get_int(items[6])); + tv.tv_sec = timeutils_seconds_since_epoch( + mp_obj_get_int(items[0]), + mp_obj_get_int(items[1]), + mp_obj_get_int(items[2]), + mp_obj_get_int(items[hour_index]), + mp_obj_get_int(items[hour_index + 1]), + mp_obj_get_int(items[hour_index + 2]) + ); tv.tv_usec = mp_obj_get_int(items[7]); settimeofday(&tv, NULL); @@ -139,13 +146,13 @@ static mp_obj_t machine_rtc_datetime_helper(mp_uint_t n_args, const mp_obj_t *ar } } static mp_obj_t machine_rtc_datetime(size_t n_args, const mp_obj_t *args) { - return machine_rtc_datetime_helper(n_args, args); + return machine_rtc_datetime_helper(n_args, args, 4); } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, machine_rtc_datetime); static mp_obj_t machine_rtc_init(mp_obj_t self_in, mp_obj_t date) { mp_obj_t args[2] = {self_in, date}; - machine_rtc_datetime_helper(2, args); + machine_rtc_datetime_helper(2, args, 3); return mp_const_none; } diff --git a/ports/mimxrt/machine_rtc.c b/ports/mimxrt/machine_rtc.c index 91769ea33416b..5feeb0da096ae 100644 --- a/ports/mimxrt/machine_rtc.c +++ b/ports/mimxrt/machine_rtc.c @@ -185,7 +185,7 @@ static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, s return (mp_obj_t)&machine_rtc_obj; } -static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args) { +static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args, int hour_index) { if (n_args == 1) { // Get date and time. snvs_lp_srtc_datetime_t srtc_date; @@ -214,9 +214,9 @@ static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args) srtc_date.month = mp_obj_get_int(items[1]); srtc_date.day = mp_obj_get_int(items[2]); // Ignore weekday at items[3] - srtc_date.hour = mp_obj_get_int(items[4]); - srtc_date.minute = mp_obj_get_int(items[5]); - srtc_date.second = mp_obj_get_int(items[6]); + srtc_date.hour = mp_obj_get_int(items[hour_index]); + srtc_date.minute = mp_obj_get_int(items[hour_index + 1]); + srtc_date.second = mp_obj_get_int(items[hour_index + 2]); if (SNVS_LP_SRTC_SetDatetime(SNVS, &srtc_date) != kStatus_Success) { mp_raise_ValueError(NULL); } @@ -226,13 +226,13 @@ static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args) } static mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { - return machine_rtc_datetime_helper(n_args, args); + return machine_rtc_datetime_helper(n_args, args, 4); } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, machine_rtc_datetime); static mp_obj_t machine_rtc_init(mp_obj_t self_in, mp_obj_t date) { mp_obj_t args[2] = {self_in, date}; - machine_rtc_datetime_helper(2, args); + machine_rtc_datetime_helper(2, args, 3); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_2(machine_rtc_init_obj, machine_rtc_init); diff --git a/ports/samd/machine_rtc.c b/ports/samd/machine_rtc.c index a906f9176f9fe..74c2266d60926 100644 --- a/ports/samd/machine_rtc.c +++ b/ports/samd/machine_rtc.c @@ -93,7 +93,7 @@ static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, s return (mp_obj_t)&machine_rtc_obj; } -static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args) { +static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args, int hour_index) { // Rtc *rtc = RTC; if (n_args == 1) { // Get date and time. @@ -120,9 +120,9 @@ static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args) RTC_MODE2_CLOCK_YEAR(mp_obj_get_int(items[0]) % 100) | RTC_MODE2_CLOCK_MONTH(mp_obj_get_int(items[1])) | RTC_MODE2_CLOCK_DAY(mp_obj_get_int(items[2])) | - RTC_MODE2_CLOCK_HOUR(mp_obj_get_int(items[4])) | - RTC_MODE2_CLOCK_MINUTE(mp_obj_get_int(items[5])) | - RTC_MODE2_CLOCK_SECOND(mp_obj_get_int(items[6])); + RTC_MODE2_CLOCK_HOUR(mp_obj_get_int(items[hour_index])) | + RTC_MODE2_CLOCK_MINUTE(mp_obj_get_int(items[hour_index + 1])) | + RTC_MODE2_CLOCK_SECOND(mp_obj_get_int(items[hour_index + 2])); RTC->MODE2.CLOCK.reg = date; #if defined(MCU_SAMD21) @@ -138,13 +138,13 @@ static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args) } static mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { - return machine_rtc_datetime_helper(n_args, args); + return machine_rtc_datetime_helper(n_args, args, 4); } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, machine_rtc_datetime); static mp_obj_t machine_rtc_init(mp_obj_t self_in, mp_obj_t date) { mp_obj_t args[2] = {self_in, date}; - machine_rtc_datetime_helper(2, args); + machine_rtc_datetime_helper(2, args, 3); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_2(machine_rtc_init_obj, machine_rtc_init); From 32b98d3e66c46b83dc0030e1fa679923557748bd Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Mon, 21 Oct 2024 15:32:06 +0100 Subject: [PATCH 137/546] lib/pico-sdk: Update to version 2.1.0. Brings in support for Pico 2 W, among other things. Signed-off-by: Peter Harper --- lib/pico-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pico-sdk b/lib/pico-sdk index efe2103f9b284..95ea6acad1311 160000 --- a/lib/pico-sdk +++ b/lib/pico-sdk @@ -1 +1 @@ -Subproject commit efe2103f9b28458a1615ff096054479743ade236 +Subproject commit 95ea6acad131124694cda1c162c52cd30e0aece0 From a93762a7623c235d24a3fe806774078c50067555 Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Tue, 1 Oct 2024 15:35:43 +0100 Subject: [PATCH 138/546] rp2/modmachine: Fix USB sleep on RP2350 MCUs. Signed-off-by: Peter Harper --- ports/rp2/modmachine.c | 2 +- tests/ports/rp2/rp2_lightsleep.py | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index cd73552dd9874..3229aed277b35 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -228,7 +228,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { #if PICO_RP2040 clocks_hw->sleep_en1 |= CLOCKS_SLEEP_EN1_CLK_USB_USBCTRL_BITS; #elif PICO_RP2350 - clocks_hw->sleep_en1 |= CLOCKS_SLEEP_EN1_CLK_SYS_USBCTRL_BITS; + clocks_hw->sleep_en1 |= CLOCKS_SLEEP_EN1_CLK_USB_BITS; #else #error Unknown processor #endif diff --git a/tests/ports/rp2/rp2_lightsleep.py b/tests/ports/rp2/rp2_lightsleep.py index 5ce5696e0e18c..63f31940ad054 100644 --- a/tests/ports/rp2/rp2_lightsleep.py +++ b/tests/ports/rp2/rp2_lightsleep.py @@ -16,12 +16,6 @@ except ImportError: print("SKIP") raise SystemExit - -# RP2350 currently fails this test, needs further investigation. -if "RP2350" in sys.implementation._machine: - print("SKIP") - raise SystemExit - try: led = Pin(Pin.board.LED, Pin.OUT) except AttributeError: From 929d7a75265e7f5ed898560b065fd33af5a18264 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 25 Sep 2024 09:58:07 +0100 Subject: [PATCH 139/546] rp2/CMakeLists.txt: Add components required by bootrom.h. Signed-off-by: Peter Harper --- ports/rp2/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 4baaf7debf6c5..4c85b83440ebe 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -196,6 +196,7 @@ set(MICROPY_SOURCE_QSTR ) set(PICO_SDK_COMPONENTS + boot_bootrom_headers hardware_adc hardware_base hardware_boot_lock @@ -222,6 +223,7 @@ set(PICO_SDK_COMPONENTS pico_base_headers pico_binary_info pico_bootrom + pico_flash pico_multicore pico_platform pico_platform_compiler From 30163e0ae483a3c04e57f8346e903a6bc2e0e2cd Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Wed, 23 Oct 2024 16:41:54 +0100 Subject: [PATCH 140/546] rp2/cyw43_configport: Define cyw43 pins. The cyw43 pins used to be hardcoded and `CYW43_PIN_WL_HOST_WAKE` and `CYW43_PIN_WL_REG_ON` were in the `pico_w.h` board header. This has been changed so the board header just defines the "default version of the pins, e.g. `CYW43_DEFAULT_PIN_WL_HOST_WAKE`, `CYW43_DEFAULT_PIN_WL_REG_ON` etc. Set the pin values in `cyw43_configport.`h so `cyw43-driver` sees them and allow them to be changed at runtime (dynamic) if required. Signed-off-by: Peter Harper --- ports/rp2/cyw43_configport.h | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/ports/rp2/cyw43_configport.h b/ports/rp2/cyw43_configport.h index 4b012ce17e994..4294691c66657 100644 --- a/ports/rp2/cyw43_configport.h +++ b/ports/rp2/cyw43_configport.h @@ -53,6 +53,41 @@ #define CYW43_HOST_NAME mod_network_hostname_data +#if CYW43_PIN_WL_DYNAMIC + +// Dynamic pins can be changed at runtime before initialising the CYW43 + +typedef enum cyw43_pin_index_t { + CYW43_PIN_INDEX_WL_REG_ON, + CYW43_PIN_INDEX_WL_DATA_OUT, + CYW43_PIN_INDEX_WL_DATA_IN, + CYW43_PIN_INDEX_WL_HOST_WAKE, + CYW43_PIN_INDEX_WL_CLOCK, + CYW43_PIN_INDEX_WL_CS, + CYW43_PIN_INDEX_WL_COUNT // last +} cyw43_pin_index_t; + +// Function to retrieve a cyw43 dynamic pin +uint cyw43_get_pin_wl(cyw43_pin_index_t pin_id); + +#define CYW43_PIN_WL_REG_ON cyw43_get_pin_wl(CYW43_PIN_INDEX_WL_REG_ON) +#define CYW43_PIN_WL_DATA_OUT cyw43_get_pin_wl(CYW43_PIN_INDEX_WL_DATA_OUT) +#define CYW43_PIN_WL_DATA_IN cyw43_get_pin_wl(CYW43_PIN_INDEX_WL_DATA_IN) +#define CYW43_PIN_WL_HOST_WAKE cyw43_get_pin_wl(CYW43_PIN_INDEX_WL_HOST_WAKE) +#define CYW43_PIN_WL_CLOCK cyw43_get_pin_wl(CYW43_PIN_INDEX_WL_CLOCK) +#define CYW43_PIN_WL_CS cyw43_get_pin_wl(CYW43_PIN_INDEX_WL_CS) + +#else + +#define CYW43_PIN_WL_REG_ON CYW43_DEFAULT_PIN_WL_REG_ON +#define CYW43_PIN_WL_DATA_OUT CYW43_DEFAULT_PIN_WL_DATA_OUT +#define CYW43_PIN_WL_DATA_IN CYW43_DEFAULT_PIN_WL_DATA_IN +#define CYW43_PIN_WL_HOST_WAKE CYW43_DEFAULT_PIN_WL_HOST_WAKE +#define CYW43_PIN_WL_CLOCK CYW43_DEFAULT_PIN_WL_CLOCK +#define CYW43_PIN_WL_CS CYW43_DEFAULT_PIN_WL_CS + +#endif + #define CYW43_SDPCM_SEND_COMMON_WAIT \ if (get_core_num() == 0) { \ cyw43_yield(); \ From 4a6c2460083607ba05ab8af13a31889767c2ce98 Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Wed, 9 Oct 2024 18:03:55 +0100 Subject: [PATCH 141/546] rp2/mphalport: Add mp_hal_is_pin_reserved() function. As cyw43 pins might be dynamic, add a function that returns if a pin is reserved. This is used by `MICROPY_HW_PIN_RESERVED` to prevent the pin IRQ from being reset across a soft-reset. Signed-off-by: Peter Harper --- ports/rp2/boards/RPI_PICO_W/mpconfigboard.h | 4 +++- ports/rp2/mphalport.c | 8 ++++++++ ports/rp2/mphalport.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ports/rp2/boards/RPI_PICO_W/mpconfigboard.h b/ports/rp2/boards/RPI_PICO_W/mpconfigboard.h index ef812b6301356..45ad6107ba14e 100644 --- a/ports/rp2/boards/RPI_PICO_W/mpconfigboard.h +++ b/ports/rp2/boards/RPI_PICO_W/mpconfigboard.h @@ -20,4 +20,6 @@ #define MICROPY_HW_PIN_EXT_COUNT CYW43_WL_GPIO_COUNT -#define MICROPY_HW_PIN_RESERVED(i) ((i) == CYW43_PIN_WL_HOST_WAKE || (i) == CYW43_PIN_WL_REG_ON) +// If this returns true for a pin then its irq will not be disabled on a soft reboot +int mp_hal_is_pin_reserved(int n); +#define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i) diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index 3f50151620a02..caecb695099d5 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -278,3 +278,11 @@ void mp_wfe_or_timeout(uint32_t timeout_ms) { // Clean up the timer node if it's not already soft_timer_remove(&timer); } + +int mp_hal_is_pin_reserved(int n) { + #if MICROPY_PY_NETWORK_CYW43 + return n == CYW43_PIN_WL_HOST_WAKE; + #else + return false; + #endif +} diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h index da865fb7e85ea..33a1073e1df81 100644 --- a/ports/rp2/mphalport.h +++ b/ports/rp2/mphalport.h @@ -210,5 +210,6 @@ enum { void mp_hal_get_mac(int idx, uint8_t buf[6]); void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest); void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]); +int mp_hal_is_pin_reserved(int n); #endif // MICROPY_INCLUDED_RP2_MPHALPORT_H From 1b611dab039c063165fe58c476c2ab9e97804010 Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Wed, 9 Oct 2024 18:05:11 +0100 Subject: [PATCH 142/546] rp2/boards/RPI_PICO2_W: Add new Pico 2 W board definition. Signed-off-by: Peter Harper --- ports/rp2/boards/RPI_PICO2_W/board.json | 21 +++++++++++++ ports/rp2/boards/RPI_PICO2_W/manifest.py | 6 ++++ .../boards/RPI_PICO2_W/mpconfigboard.cmake | 17 +++++++++++ ports/rp2/boards/RPI_PICO2_W/mpconfigboard.h | 22 ++++++++++++++ .../boards/RPI_PICO2_W/mpconfigvariant.cmake | 1 + ports/rp2/boards/RPI_PICO2_W/pins.csv | 30 +++++++++++++++++++ 6 files changed, 97 insertions(+) create mode 100644 ports/rp2/boards/RPI_PICO2_W/board.json create mode 100644 ports/rp2/boards/RPI_PICO2_W/manifest.py create mode 100644 ports/rp2/boards/RPI_PICO2_W/mpconfigboard.cmake create mode 100644 ports/rp2/boards/RPI_PICO2_W/mpconfigboard.h create mode 100644 ports/rp2/boards/RPI_PICO2_W/mpconfigvariant.cmake create mode 100644 ports/rp2/boards/RPI_PICO2_W/pins.csv diff --git a/ports/rp2/boards/RPI_PICO2_W/board.json b/ports/rp2/boards/RPI_PICO2_W/board.json new file mode 100644 index 0000000000000..f35ea940a2663 --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2_W/board.json @@ -0,0 +1,21 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "BLE", + "Dual-core", + "External Flash", + "USB", + "WiFi" + ], + "images": [ + "rp2-pico2-w.jpg" + ], + "mcu": "rp2350", + "product": "Pico 2 W", + "thumbnail": "", + "url": "https://www.raspberrypi.com/products/raspberry-pi-pico-2/", + "vendor": "Raspberry Pi" +} diff --git a/ports/rp2/boards/RPI_PICO2_W/manifest.py b/ports/rp2/boards/RPI_PICO2_W/manifest.py new file mode 100644 index 0000000000000..4e38f09cdee4f --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2_W/manifest.py @@ -0,0 +1,6 @@ +include("$(PORT_DIR)/boards/manifest.py") + +require("bundle-networking") + +# Bluetooth +require("aioble") diff --git a/ports/rp2/boards/RPI_PICO2_W/mpconfigboard.cmake b/ports/rp2/boards/RPI_PICO2_W/mpconfigboard.cmake new file mode 100644 index 0000000000000..1d9b7fc448a0c --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2_W/mpconfigboard.cmake @@ -0,0 +1,17 @@ +# cmake file for Raspberry Pi Pico 2 W + +set(PICO_BOARD "pico2_w") + +# To change the gpio count for QFN-80 +# set(PICO_NUM_GPIOS 48) + +set(MICROPY_PY_LWIP ON) +set(MICROPY_PY_NETWORK_CYW43 ON) + +# Bluetooth +set(MICROPY_PY_BLUETOOTH ON) +set(MICROPY_BLUETOOTH_BTSTACK ON) +set(MICROPY_PY_BLUETOOTH_CYW43 ON) + +# Board specific version of the frozen manifest +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/rp2/boards/RPI_PICO2_W/mpconfigboard.h b/ports/rp2/boards/RPI_PICO2_W/mpconfigboard.h new file mode 100644 index 0000000000000..fe688c2803192 --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2_W/mpconfigboard.h @@ -0,0 +1,22 @@ +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "Raspberry Pi Pico 2 W" +#define MICROPY_HW_FLASH_STORAGE_BYTES (PICO_FLASH_SIZE_BYTES - 1536 * 1024) + +// Enable networking. +#define MICROPY_PY_NETWORK 1 +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "Pico2W" + +// CYW43 driver configuration. +#define CYW43_USE_SPI (1) +#define CYW43_LWIP (1) +#define CYW43_GPIO (1) +#define CYW43_SPI_PIO (1) + +// For debugging mbedtls - also set +// Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose +// #define MODUSSL_MBEDTLS_DEBUG_LEVEL 1 + +#define MICROPY_HW_PIN_EXT_COUNT CYW43_WL_GPIO_COUNT + +int mp_hal_is_pin_reserved(int n); +#define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i) diff --git a/ports/rp2/boards/RPI_PICO2_W/mpconfigvariant.cmake b/ports/rp2/boards/RPI_PICO2_W/mpconfigvariant.cmake new file mode 100644 index 0000000000000..6fe039ba51bba --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2_W/mpconfigvariant.cmake @@ -0,0 +1 @@ +set(PICO_PLATFORM "rp2350") diff --git a/ports/rp2/boards/RPI_PICO2_W/pins.csv b/ports/rp2/boards/RPI_PICO2_W/pins.csv new file mode 100644 index 0000000000000..8debb6326e070 --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2_W/pins.csv @@ -0,0 +1,30 @@ +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 +GP26,GPIO26 +GP27,GPIO27 +GP28,GPIO28 +WL_GPIO0,EXT_GPIO0 +WL_GPIO1,EXT_GPIO1 +WL_GPIO2,EXT_GPIO2 +LED,EXT_GPIO0 From a70a90ccfc6584f51f39a7c6bf43f19695f7da51 Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Fri, 22 Nov 2024 17:19:26 +0000 Subject: [PATCH 143/546] rp2/boards/RPI_PICO2_W: Add RISCV variant for Pico 2 W. Build with: make BOARD=RPI_PICO2_W BOARD_VARIANT=RISCV Signed-off-by: Peter Harper --- ports/rp2/CMakeLists.txt | 1 - ports/rp2/boards/RPI_PICO2_W/mpconfigvariant_RISCV.cmake | 1 + ports/rp2/mpnetworkport.c | 4 ++++ 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 ports/rp2/boards/RPI_PICO2_W/mpconfigvariant_RISCV.cmake diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 4c85b83440ebe..8a4092529b1f2 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -407,7 +407,6 @@ if (MICROPY_PY_NETWORK_CYW43) target_link_libraries(${MICROPY_TARGET} cyw43_driver_picow - cmsis_core ) target_include_directories(${MICROPY_TARGET} PRIVATE ${MICROPY_DIR}/lib/cyw43-driver/ diff --git a/ports/rp2/boards/RPI_PICO2_W/mpconfigvariant_RISCV.cmake b/ports/rp2/boards/RPI_PICO2_W/mpconfigvariant_RISCV.cmake new file mode 100644 index 0000000000000..65a97fc3350d1 --- /dev/null +++ b/ports/rp2/boards/RPI_PICO2_W/mpconfigvariant_RISCV.cmake @@ -0,0 +1 @@ +set(PICO_PLATFORM "rp2350-riscv") diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c index fcc60b3fe5796..af2cabb3bff0f 100644 --- a/ports/rp2/mpnetworkport.c +++ b/ports/rp2/mpnetworkport.c @@ -44,6 +44,7 @@ static soft_timer_entry_t mp_network_soft_timer; #include "lib/cyw43-driver/src/cyw43_stats.h" #include "hardware/irq.h" +#if !defined(__riscv) #if PICO_RP2040 #include "RP2040.h" // cmsis, for NVIC_SetPriority and PendSV_IRQn #elif PICO_RP2350 @@ -51,6 +52,7 @@ static soft_timer_entry_t mp_network_soft_timer; #else #error Unknown processor #endif +#endif #define CYW43_IRQ_LEVEL GPIO_IRQ_LEVEL_HIGH #define CYW43_SHARED_IRQ_HANDLER_PRIORITY PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY @@ -74,7 +76,9 @@ 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); + #if !defined(__riscv) NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_PENDSV); + #endif } void cyw43_post_poll_hook(void) { From 39538e4c9a54e44ea27bcc374e17774ac2b9796d Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Fri, 15 Nov 2024 13:21:04 -0700 Subject: [PATCH 144/546] rp2/boards/SPARKFUN_PROMICRO: Fix SparkFun Pro Micro RP2040 image. Signed-off-by: Dryw Wade --- ports/rp2/boards/SPARKFUN_PROMICRO/board.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json index 46878a29cd889..b8c8afc914eac 100644 --- a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json +++ b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json @@ -11,7 +11,7 @@ "USB-C" ], "images": [ - "17745-SparkFun_Thing_Plus_-_RP2040-01a.jpg" + "18288-SparkFun_Pro_Micro_-_RP2040-01.jpg" ], "mcu": "rp2040", "product": "Pro Micro RP2040", From 4e76acc88d8673ce4f43698fbb4c2913e63b0135 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 18 Dec 2024 17:23:45 +1100 Subject: [PATCH 145/546] doc: Note that machine.USBDevice is now available on esp32 port. Support was added in commit 5f2d05d, this updates the docs to match. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/library/machine.USBDevice.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/library/machine.USBDevice.rst b/docs/library/machine.USBDevice.rst index 5c18c49a75bb5..5291679ac1258 100644 --- a/docs/library/machine.USBDevice.rst +++ b/docs/library/machine.USBDevice.rst @@ -4,8 +4,9 @@ class USBDevice -- USB Device driver ==================================== -.. note:: ``machine.USBDevice`` is currently only supported on the rp2 and samd - ports. +.. note:: ``machine.USBDevice`` is currently only supported for esp32, rp2 and + samd ports. Native USB support is also required, and not every board + supports native USB. USBDevice provides a low-level Python API for implementing USB device functions using Python code. From a3128f89ccb1d24d0ddaec7164d2fa8a101b8ac3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 15 Nov 2024 13:52:04 +1100 Subject: [PATCH 146/546] tests: Fix all file ioctl's to support only MP_STREAM_CLOSE. A return value of 0 from Python-level `ioctl()` means success, but if that's returned unconditionally it means that the method supports all ioctl calls, which is not true. Returning 0 without doing anything can potentially lead to a crash, eg for MP_STREAM_SEEK which requires returning a value in the passed-in struct pointer. This commit makes it so that all `ioctl()` methods respond only to MP_STREAM_CLOSE, ie they return -1 (indicating error) for all other ioctl calls. Signed-off-by: Damien George --- tests/micropython/builtin_execfile.py | 4 +++- tests/micropython/import_mpy_invalid.py | 4 +++- tests/micropython/import_mpy_native.py | 4 +++- tests/micropython/import_mpy_native_gc.py | 4 +++- tests/perf_bench/core_import_mpy_multi.py | 4 +++- tests/perf_bench/core_import_mpy_single.py | 4 +++- tests/run-natmodtests.py | 4 +++- tests/run-tests.py | 4 +++- 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/tests/micropython/builtin_execfile.py b/tests/micropython/builtin_execfile.py index a905521c662d3..75a867bb94023 100644 --- a/tests/micropython/builtin_execfile.py +++ b/tests/micropython/builtin_execfile.py @@ -16,7 +16,9 @@ def __init__(self, data): self.off = 0 def ioctl(self, request, arg): - return 0 + if request == 4: # MP_STREAM_CLOSE + return 0 + return -1 def readinto(self, buf): buf[:] = memoryview(self.data)[self.off : self.off + len(buf)] diff --git a/tests/micropython/import_mpy_invalid.py b/tests/micropython/import_mpy_invalid.py index 89fdf44839bb1..f928d45c79150 100644 --- a/tests/micropython/import_mpy_invalid.py +++ b/tests/micropython/import_mpy_invalid.py @@ -22,7 +22,9 @@ def readinto(self, buf): return n def ioctl(self, req, arg): - return 0 + if req == 4: # MP_STREAM_CLOSE + return 0 + return -1 class UserFS: diff --git a/tests/micropython/import_mpy_native.py b/tests/micropython/import_mpy_native.py index 8f5de25a0b2c1..ac5e724e8d468 100644 --- a/tests/micropython/import_mpy_native.py +++ b/tests/micropython/import_mpy_native.py @@ -28,7 +28,9 @@ def readinto(self, buf): return n def ioctl(self, req, arg): - return 0 + if req == 4: # MP_STREAM_CLOSE + return 0 + return -1 class UserFS: diff --git a/tests/micropython/import_mpy_native_gc.py b/tests/micropython/import_mpy_native_gc.py index bdeb612b4928e..851eb3922953b 100644 --- a/tests/micropython/import_mpy_native_gc.py +++ b/tests/micropython/import_mpy_native_gc.py @@ -22,7 +22,9 @@ def readinto(self, buf): return n def ioctl(self, req, arg): - return 0 + if req == 4: # MP_STREAM_CLOSE + return 0 + return -1 class UserFS: diff --git a/tests/perf_bench/core_import_mpy_multi.py b/tests/perf_bench/core_import_mpy_multi.py index 33437f9da801e..8affa157fa0c5 100644 --- a/tests/perf_bench/core_import_mpy_multi.py +++ b/tests/perf_bench/core_import_mpy_multi.py @@ -31,7 +31,9 @@ def __init__(self): self.off = 0 def ioctl(self, request, arg): - return 0 + if request == 4: # MP_STREAM_CLOSE + return 0 + return -1 def readinto(self, buf): buf[:] = memoryview(file_data)[self.off : self.off + len(buf)] diff --git a/tests/perf_bench/core_import_mpy_single.py b/tests/perf_bench/core_import_mpy_single.py index 18454b8fd5eb2..4d9aa67bf2f0e 100644 --- a/tests/perf_bench/core_import_mpy_single.py +++ b/tests/perf_bench/core_import_mpy_single.py @@ -86,7 +86,9 @@ def __init__(self): self.off = 0 def ioctl(self, request, arg): - return 0 + if request == 4: # MP_STREAM_CLOSE + return 0 + return -1 def readinto(self, buf): buf[:] = memoryview(file_data)[self.off : self.off + len(buf)] diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index f1a2a974690ee..1fe44bec1615a 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -35,7 +35,9 @@ class __File(io.IOBase): def __init__(self): self.off = 0 def ioctl(self, request, arg): - return 0 + if request == 4: # MP_STREAM_CLOSE + return 0 + return -1 def readinto(self, buf): buf[:] = memoryview(__buf)[self.off:self.off + len(buf)] self.off += len(buf) diff --git a/tests/run-tests.py b/tests/run-tests.py index c72178cd760c2..a609a1fcbbebd 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -68,7 +68,9 @@ def __init__(self): sys.modules['__injected_test'].__name__ = '__main__' self.off = 0 def ioctl(self, request, arg): - return 0 + if request == 4: # MP_STREAM_CLOSE + return 0 + return -1 def readinto(self, buf): buf[:] = memoryview(__buf)[self.off:self.off + len(buf)] self.off += len(buf) From 8b6bd43eaba005c24d277fa581c945b40213059d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 15 Nov 2024 11:16:04 +1100 Subject: [PATCH 147/546] extmod/vfs: Guard mutating fs functions with MICROPY_VFS_WRITABLE. Enabled by default. Useful for ports that need the VFS but don't have any writable filesystems. Signed-off-by: Damien George --- extmod/modos.c | 4 +++- extmod/vfs.c | 4 ++++ extmod/vfs.h | 4 ++++ py/mpconfig.h | 5 +++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/extmod/modos.c b/extmod/modos.c index e7f7fc818cf08..69fdc3fac0a06 100644 --- a/extmod/modos.c +++ b/extmod/modos.c @@ -171,13 +171,15 @@ static const mp_rom_map_elem_t os_module_globals_table[] = { { 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) }, + #if MICROPY_VFS_WRITABLE { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mp_vfs_remove_obj) }, // unlink aliases to remove + #endif { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, - { MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mp_vfs_remove_obj) }, // unlink aliases to remove #endif // The following are MicroPython extensions. diff --git a/extmod/vfs.c b/extmod/vfs.c index e545c9af93657..b9c5ab0fc3a8c 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -443,6 +443,8 @@ mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj, 0, 1, mp_vfs_listdir); +#if MICROPY_VFS_WRITABLE + mp_obj_t mp_vfs_mkdir(mp_obj_t path_in) { mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); @@ -479,6 +481,8 @@ mp_obj_t mp_vfs_rmdir(mp_obj_t path_in) { } MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj, mp_vfs_rmdir); +#endif // MICROPY_VFS_WRITABLE + mp_obj_t mp_vfs_stat(mp_obj_t path_in) { mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); diff --git a/extmod/vfs.h b/extmod/vfs.h index f577d3e337c87..626e25a351123 100644 --- a/extmod/vfs.h +++ b/extmod/vfs.h @@ -95,10 +95,12 @@ mp_obj_t mp_vfs_chdir(mp_obj_t path_in); mp_obj_t mp_vfs_getcwd(void); mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args); mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args); +#if MICROPY_VFS_WRITABLE mp_obj_t mp_vfs_mkdir(mp_obj_t path_in); mp_obj_t mp_vfs_remove(mp_obj_t path_in); mp_obj_t mp_vfs_rename(mp_obj_t old_path_in, mp_obj_t new_path_in); mp_obj_t mp_vfs_rmdir(mp_obj_t path_in); +#endif mp_obj_t mp_vfs_stat(mp_obj_t path_in); mp_obj_t mp_vfs_statvfs(mp_obj_t path_in); @@ -111,10 +113,12 @@ MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj); MP_DECLARE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_ilistdir_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj); +#if MICROPY_VFS_WRITABLE MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_mkdir_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_remove_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_vfs_rename_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj); +#endif MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_stat_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj); diff --git a/py/mpconfig.h b/py/mpconfig.h index b31da2a37c050..a330cadc4ef3a 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -991,6 +991,11 @@ typedef double mp_float_t; #define MICROPY_VFS (0) #endif +// Whether to include support for writable filesystems. +#ifndef MICROPY_VFS_WRITABLE +#define MICROPY_VFS_WRITABLE (1) +#endif + // Support for VFS POSIX component, to mount a POSIX filesystem within VFS #ifndef MICROPY_VFS_POSIX #define MICROPY_VFS_POSIX (0) From 136058496f05b4b2ab5e63829b301671dc8b9a59 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 17 Dec 2024 20:06:02 +0100 Subject: [PATCH 148/546] esp32/machine_timer: Restrict timer numbers for ESP32C6 to 0 and 1. The ESP32C6 has only one timer in each of the two groups. Also add a check for valid timer numbers. Addresses issue #16438. Signed-off-by: robert-hh --- ports/esp32/machine_timer.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index 30e64eb334d37..82b432bbacddd 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -66,7 +66,7 @@ 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 + #if SOC_TIMER_GROUP_TIMERS_PER_GROUP == 1 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); @@ -76,7 +76,7 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr machine_timer_obj_t *machine_timer_create(mp_uint_t timer) { machine_timer_obj_t *self = NULL; - #if CONFIG_IDF_TARGET_ESP32C3 + #if SOC_TIMER_GROUP_TIMERS_PER_GROUP == 1 mp_uint_t group = timer & 1; mp_uint_t index = 0; #else @@ -108,7 +108,11 @@ static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_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])); + uint32_t timer_number = mp_obj_get_int(args[0]); + if (timer_number >= SOC_TIMER_GROUP_TOTAL_TIMERS) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid Timer number")); + } + machine_timer_obj_t *self = machine_timer_create(timer_number); if (n_args > 1 || n_kw > 0) { mp_map_t kw_args; From 6760e00817ff6cb449134418411850fc577d0f9c Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sun, 23 Jun 2024 08:18:04 +0200 Subject: [PATCH 149/546] tools/mpy_ld.py: Add native modules support for RV32 code. This commit adds support for RV32IMC native modules, as in embedding native code into a self-contained MPY module and and make its exported functions available to the MicroPython environment. Signed-off-by: Alessandro Gatti --- docs/develop/natmod.rst | 5 +- examples/natmod/README.md | 2 +- examples/natmod/btree/Makefile | 2 +- examples/natmod/deflate/Makefile | 2 +- examples/natmod/features0/Makefile | 3 +- examples/natmod/features1/Makefile | 2 +- examples/natmod/features3/Makefile | 2 +- examples/natmod/features4/Makefile | 2 +- examples/natmod/heapq/Makefile | 2 +- examples/natmod/random/Makefile | 2 +- examples/natmod/re/Makefile | 2 +- py/dynruntime.mk | 16 ++ tools/mpy_ld.py | 399 ++++++++++++++++++++++++++++- 13 files changed, 425 insertions(+), 16 deletions(-) diff --git a/docs/develop/natmod.rst b/docs/develop/natmod.rst index 502ea1c4c6873..ba45e4305c66d 100644 --- a/docs/develop/natmod.rst +++ b/docs/develop/natmod.rst @@ -39,7 +39,8 @@ options for the ``ARCH`` variable, see below): * ``armv7emsp`` (ARM Thumb 2, single precision float, eg Cortex-M4F, Cortex-M7) * ``armv7emdp`` (ARM Thumb 2, double precision float, eg Cortex-M7) * ``xtensa`` (non-windowed, eg ESP8266) -* ``xtensawin`` (windowed with window size 8, eg ESP32) +* ``xtensawin`` (windowed with window size 8, eg ESP32, ESP32S3) +* ``rv32imc`` (RISC-V 32 bits with compressed instructions, eg ESP32C3, ESP32C6) When compiling and linking the native .mpy file the architecture must be chosen and the corresponding file can only be imported on that architecture. For more @@ -172,7 +173,7 @@ The file ``Makefile`` contains: # Source files (.c or .py) SRC = factorial.c - # Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin) + # Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin, rv32imc) ARCH = x64 # Include to get the rules for compiling and linking the module diff --git a/examples/natmod/README.md b/examples/natmod/README.md index f72d1c049a011..ca6b887d034d4 100644 --- a/examples/natmod/README.md +++ b/examples/natmod/README.md @@ -65,7 +65,7 @@ your system package manager or installed from PyPI in a virtual environment with `pip`. Each example provides a Makefile. You should specify the `ARCH` argument to -make (one of x86, x64, armv6m, armv7m, xtensa, xtensawin): +make (one of x86, x64, armv6m, armv7m, xtensa, xtensawin, rv32imc): ``` $ cd features0 diff --git a/examples/natmod/btree/Makefile b/examples/natmod/btree/Makefile index b5846f90065ee..ff130d61b3793 100644 --- a/examples/natmod/btree/Makefile +++ b/examples/natmod/btree/Makefile @@ -7,7 +7,7 @@ MOD = btree_$(ARCH) # Source files (.c or .py) SRC = btree_c.c -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) ARCH = x64 BTREE_DIR = $(MPY_DIR)/lib/berkeley-db-1.xx diff --git a/examples/natmod/deflate/Makefile b/examples/natmod/deflate/Makefile index 86ef29b6324b3..504130d5723b3 100644 --- a/examples/natmod/deflate/Makefile +++ b/examples/natmod/deflate/Makefile @@ -7,7 +7,7 @@ MOD = deflate_$(ARCH) # Source files (.c or .py) SRC = deflate.c -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) ARCH = x64 include $(MPY_DIR)/py/dynruntime.mk diff --git a/examples/natmod/features0/Makefile b/examples/natmod/features0/Makefile index 57490df90abe7..fb01b8d031aaa 100644 --- a/examples/natmod/features0/Makefile +++ b/examples/natmod/features0/Makefile @@ -7,8 +7,9 @@ MOD = features0 # Source files (.c or .py) SRC = features0.c -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) ARCH = x64 # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk + diff --git a/examples/natmod/features1/Makefile b/examples/natmod/features1/Makefile index 010640daf9746..49040511020eb 100644 --- a/examples/natmod/features1/Makefile +++ b/examples/natmod/features1/Makefile @@ -7,7 +7,7 @@ MOD = features1 # Source files (.c or .py) SRC = features1.c -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) ARCH = x64 # Include to get the rules for compiling and linking the module diff --git a/examples/natmod/features3/Makefile b/examples/natmod/features3/Makefile index 4a5f71b8f255b..3573f41caca82 100644 --- a/examples/natmod/features3/Makefile +++ b/examples/natmod/features3/Makefile @@ -7,7 +7,7 @@ MOD = features3 # Source files (.c or .py) SRC = features3.c -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) ARCH = x64 # Include to get the rules for compiling and linking the module diff --git a/examples/natmod/features4/Makefile b/examples/natmod/features4/Makefile index f76a31a7cc141..34fc3a7ef7bf4 100644 --- a/examples/natmod/features4/Makefile +++ b/examples/natmod/features4/Makefile @@ -7,7 +7,7 @@ MOD = features4 # Source files (.c or .py) SRC = features4.c -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) ARCH = x64 # Include to get the rules for compiling and linking the module diff --git a/examples/natmod/heapq/Makefile b/examples/natmod/heapq/Makefile index af45b472da1ae..61e2fc8fcc0e0 100644 --- a/examples/natmod/heapq/Makefile +++ b/examples/natmod/heapq/Makefile @@ -7,7 +7,7 @@ MOD = heapq_$(ARCH) # Source files (.c or .py) SRC = heapq.c -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) ARCH = x64 include $(MPY_DIR)/py/dynruntime.mk diff --git a/examples/natmod/random/Makefile b/examples/natmod/random/Makefile index 5c50227b15f9d..8abdb66dc8724 100644 --- a/examples/natmod/random/Makefile +++ b/examples/natmod/random/Makefile @@ -7,7 +7,7 @@ MOD = random_$(ARCH) # Source files (.c or .py) SRC = random.c -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) ARCH = x64 include $(MPY_DIR)/py/dynruntime.mk diff --git a/examples/natmod/re/Makefile b/examples/natmod/re/Makefile index 1ba540110650d..56b08b9886878 100644 --- a/examples/natmod/re/Makefile +++ b/examples/natmod/re/Makefile @@ -7,7 +7,7 @@ MOD = re_$(ARCH) # Source files (.c or .py) SRC = re.c -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) ARCH = x64 include $(MPY_DIR)/py/dynruntime.mk diff --git a/py/dynruntime.mk b/py/dynruntime.mk index 62db43ad149ca..71679954eab01 100644 --- a/py/dynruntime.mk +++ b/py/dynruntime.mk @@ -99,6 +99,22 @@ CROSS = xtensa-esp32-elf- CFLAGS += MICROPY_FLOAT_IMPL ?= float +else ifeq ($(ARCH),rv32imc) + +# rv32imc +CROSS = riscv64-unknown-elf- +CFLAGS += -march=rv32imac -mabi=ilp32 -mno-relax +# 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 $(CROSS)gcc --print-file-name=picolibc.specs) +ifneq ($(PICOLIBC_SPECS),picolibc.specs) +CFLAGS += --specs=$(PICOLIBC_SPECS) +endif + +MICROPY_FLOAT_IMPL ?= none + else $(error architecture '$(ARCH)' not supported) endif diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index c77c46d44e0e6..54295208f1a29 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -47,6 +47,7 @@ MP_NATIVE_ARCH_ARMV7EMDP = 8 MP_NATIVE_ARCH_XTENSA = 9 MP_NATIVE_ARCH_XTENSAWIN = 10 +MP_NATIVE_ARCH_RV32IMC = 11 MP_PERSISTENT_OBJ_STR = 5 MP_SCOPE_FLAG_VIPERRELOC = 0x10 MP_SCOPE_FLAG_VIPERRODATA = 0x20 @@ -56,6 +57,7 @@ # ELF constants R_386_32 = 1 +R_RISCV_32 = 1 R_X86_64_64 = 1 R_XTENSA_32 = 1 R_386_PC32 = 2 @@ -70,15 +72,57 @@ R_386_GOTPC = 10 R_ARM_THM_CALL = 10 R_XTENSA_ASM_EXPAND = 11 +R_RISCV_BRANCH = 16 +R_RISCV_JAL = 17 +R_RISCV_CALL = 18 +R_RISCV_CALL_PLT = 19 R_XTENSA_DIFF32 = 19 R_XTENSA_SLOT0_OP = 20 +R_RISCV_GOT_HI20 = 20 +R_RISCV_TLS_GD_HI20 = 22 +R_RISCV_PCREL_HI20 = 23 +R_RISCV_PCREL_LO12_I = 24 +R_RISCV_PCREL_LO12_S = 25 R_ARM_BASE_PREL = 25 # aka R_ARM_GOTPC R_ARM_GOT_BREL = 26 # aka R_ARM_GOT32 R_ARM_THM_JUMP24 = 30 +R_RISCV_HI20 = 26 +R_RISCV_LO12_I = 27 +R_RISCV_LO12_S = 28 +R_RISCV_TPREL_HI20 = 29 +R_RISCV_TPREL_LO12_I = 30 +R_RISCV_TPREL_LO12_S = 31 +R_RISCV_TPREL_ADD = 32 +R_RISCV_ADD8 = 33 +R_RISCV_ADD16 = 34 +R_RISCV_ADD32 = 35 +R_RISCV_ADD64 = 36 +R_RISCV_SUB8 = 37 +R_RISCV_SUB16 = 38 +R_RISCV_SUB32 = 39 +R_RISCV_SUB64 = 40 +R_RISCV_GOT32_PCREL = 41 R_X86_64_GOTPCREL = 9 R_X86_64_REX_GOTPCRELX = 42 R_386_GOT32X = 43 +R_RISCV_ALIGN = 43 +R_RISCV_RVC_BRANCH = 44 +R_RISCV_RVC_JUMP = 45 +R_RISCV_RELAX = 51 +R_RISCV_SUB6 = 52 +R_RISCV_SET6 = 53 +R_RISCV_SET8 = 54 +R_RISCV_SET16 = 55 +R_RISCV_SET32 = 56 +R_RISCV_32_PCREL = 57 +R_RISCV_PLT32 = 59 R_XTENSA_PDIFF32 = 59 +R_RISCV_SET_ULEB128 = 60 +R_RISCV_SUB_ULEB128 = 61 +R_RISCV_TLSDESC_HI20 = 62 +R_RISCC_TLSDESC_LOAD_LO12 = 63 +R_RISCV_TLSDESC_ADD_LO12 = 64 +R_RISCV_TLSDESC_CALL = 65 ################################################################################ # Architecture configuration @@ -130,6 +174,18 @@ def asm_jump_xtensa(entry): return struct.pack("> 8) +def asm_jump_rv32(entry): + # This could be 6 bytes shorter, but the code currently cannot + # support a trampoline with varying length depending on the offset. + + # auipc t6, HI(entry) + # jalr zero, t6, LO(entry) + upper, lower = split_riscv_address(entry) + return struct.pack( + "> 16 & 0xFF +def split_riscv_address(value): + # The address can be represented with just the lowest 12 bits + if value < 0 and value > -2048: + value = 4096 + value + return 0, value + # 2s complement + if value < 0: + value = 0x100000000 + value + upper, lower = (value & 0xFFFFF000), (value & 0xFFF) + if lower & 0x800 != 0: + # Reverse lower part sign extension + upper += 0x1000 + return upper & 0xFFFFFFFF, lower & 0xFFFFFFFF + + def xxd(text): for i in range(0, len(text), 16): print("{:08x}:".format(i), end="") @@ -346,7 +424,7 @@ def build_got_generic(env): for r in sec.reloc: s = r.sym if not ( - s.entry["st_info"]["bind"] == "STB_GLOBAL" + s.entry["st_info"]["bind"] in ("STB_GLOBAL", "STB_WEAK") and r["r_info_type"] in env.arch.arch_got ): continue @@ -487,6 +565,8 @@ def do_relocation_text(env, text_addr, r): # Default relocation type and name for logging reloc_type = "le32" log_name = None + addr = None + value = None if ( env.arch.name == "EM_386" @@ -590,12 +670,46 @@ def do_relocation_text(env, text_addr, r): return assert 0 + elif env.arch.name == "EM_RISCV" and r_info_type in ( + R_RISCV_TLS_GD_HI20, + R_RISCV_TLSDESC_HI20, + R_RISCV_TLSDESC_ADD_LO12, + R_RISCV_TLSDESC_CALL, + ): + # TLS relocations are not supported. + raise LinkError("{}: RISC-V TLS relocation: {}".format(s.filename, s.name)) + + elif env.arch.name == "EM_RISCV" and r_info_type in ( + R_RISCV_TPREL_HI20, + R_RISCV_TPREL_LO12_I, + R_RISCV_TPREL_LO12_S, + R_RISCV_TPREL_ADD, + ): + # ThreadPointer-relative relocations are not supported. + raise LinkError("{}: RISC-V TP-relative relocation: {}".format(s.filename, s.name)) + + elif env.arch.name == "EM_RISCV" and r_info_type in (R_RISCV_SET_ULEB128, R_RISCV_SUB_ULEB128): + # 128-bit value relocations are not supported + raise LinkError("{}: RISC-V ULEB128 relocation: {}".format(s.filename, s.name)) + + elif env.arch.name == "EM_RISCV" and r_info_type in (R_RISCV_RELAX, R_RISCV_ALIGN): + # To keep things simple, no relocations are relaxed and thus no + # size optimisation is performed even if there is the chance, along + # with no offsets to fix up. + return + + elif env.arch.name == "EM_RISCV": + (addr, value) = process_riscv32_relocation(env, text_addr, r) + else: # Unknown/unsupported relocation assert 0, r_info_type # Write relocation - if reloc_type == "le32": + if env.arch.name == "EM_RISCV": + # This case is already handled by `process_riscv_relocation`. + pass + elif reloc_type == "le32": (existing,) = struct.unpack_from(" {:08x}".format(r_offset, log_name, addr)) + if addr is not None: + log(LOG_LEVEL_3, " {:08x} {} -> {:08x}".format(r_offset, log_name, addr)) + else: + log(LOG_LEVEL_3, " {:08x} {} == {:08x}".format(r_offset, log_name, value)) def do_relocation_data(env, text_addr, r): @@ -646,12 +763,16 @@ def do_relocation_data(env, text_addr, r): and r_info_type == R_ARM_ABS32 or env.arch.name == "EM_XTENSA" and r_info_type == R_XTENSA_32 + or env.arch.name == "EM_RISCV" + and r_info_type == R_RISCV_32 ): # Relocation in data.rel.ro to internal/external symbol if env.arch.word_size == 4: struct_type = "> 1) + | ((reloc & 0x20) >> 3) + | ((reloc & 0x18) << 7) + | ((reloc & 0x06) << 2) + ) + & 0xFFFF, + ) + elif reloc_type == "riscv_cj": + # Patch the target of a compressed jump opcode + (existing,) = struct.unpack_from("> 2) + | ((reloc & 0x300) << 1) + | ((reloc & 0x80) >> 1) + | ((reloc & 0x40) << 1) + | ((reloc & 0x20) >> 3) + | ((reloc & 0x10) << 7) + | ((reloc & 0x0E) << 2) + ) + & 0xFFFF, + ) + elif reloc_type == "riscv_call": + # Patch a pair of opcodes forming a call operation + upper, lower = split_riscv_address(reloc) + (existing,) = struct.unpack_from("> 4) + | ((reloc & 0x7E0) << 20) + | ((reloc & 0x1E) << 7) + ) + & 0xFFFFFFFF, + ) + elif reloc_type == "riscv_j": + # Patch a jump/jump with link opcode + (existing,) = struct.unpack_from(" Date: Wed, 2 Oct 2024 23:46:14 +0200 Subject: [PATCH 150/546] examples/natmod/re: Fix build on RV32 with alloca. This fixes compilation of the `re` natmod example when built with Picolibc in the CI environment. Ubuntu 22.04's combination of its bare metal RISC-V toolchain and its version of Picolibc makes the `alloca` symbol more elusive than it should be. This commit makes the `re` natmod try harder to get an `alloca` implementation. Signed-off-by: Alessandro Gatti --- examples/natmod/re/re.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/examples/natmod/re/re.c b/examples/natmod/re/re.c index eb6d13778c387..c0279ee7e816d 100644 --- a/examples/natmod/re/re.c +++ b/examples/natmod/re/re.c @@ -4,7 +4,20 @@ #define MICROPY_PY_RE_MATCH_SPAN_START_END (1) #define MICROPY_PY_RE_SUB (0) // requires vstr interface +#if defined __has_builtin +#if __has_builtin(__builtin_alloca) +#define alloca __builtin_alloca +#endif +#endif + +#if !defined(alloca) +#if defined(_PICOLIBC__) && !defined(HAVE_BUILTIN_ALLOCA) +#define alloca(n) m_malloc(n) +#else #include +#endif +#endif + #include "py/dynruntime.h" #define STACK_LIMIT (2048) From 4bf087b272856482c055a6df66ec008f2ca846d0 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 11 Dec 2024 13:36:32 +0100 Subject: [PATCH 151/546] py/dynruntime.mk: Delete compiled module file on clean. This commit adds the compiled native module file to the list of files to remove when `make clean` is issued in a native module source directory. Signed-off-by: Alessandro Gatti --- py/dynruntime.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/py/dynruntime.mk b/py/dynruntime.mk index 71679954eab01..68d5e2e268f5b 100644 --- a/py/dynruntime.mk +++ b/py/dynruntime.mk @@ -38,6 +38,8 @@ MPY_CROSS_FLAGS += -march=$(ARCH) SRC_O += $(addprefix $(BUILD)/, $(patsubst %.c,%.o,$(filter %.c,$(SRC))) $(patsubst %.S,%.o,$(filter %.S,$(SRC)))) SRC_MPY += $(addprefix $(BUILD)/, $(patsubst %.py,%.mpy,$(filter %.py,$(SRC)))) +CLEAN_EXTRA += $(MOD).mpy + ################################################################################ # Architecture configuration From 7ca6e5eb6815de6f7c2792969b0f66cf306f5b91 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sun, 22 Dec 2024 00:27:03 +0100 Subject: [PATCH 152/546] qemu: Add test_natmod target for RV32 and use as part of CI pipeline. This commit brings the natmod tests in the CI build process for the RV32 platform. Not all example natmods are tested at the moment, as `features` requires soft-float support, and `btree` needs thread-local storage support in `mpy_ld.py` when built with the CI's toolchain. Co-authored-by: Damien George Signed-off-by: Alessandro Gatti --- ports/qemu/Makefile | 6 ++++++ ports/qemu/README.md | 18 +++++++++++++++--- ports/qemu/boards/VIRT_RV32.mk | 2 ++ tools/ci.sh | 13 +++++++++++-- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index 237e1e9514c60..b85ff2896a16e 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -168,6 +168,12 @@ test: $(BUILD)/firmware.elf $(eval DIRNAME=ports/$(notdir $(CURDIR))) cd $(TOP)/tests && ./run-tests.py -t execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_ARGS) $(RUN_TESTS_EXTRA) +.PHONY: test_natmod +test_natmod: $(BUILD)/firmware.elf + $(eval DIRNAME=ports/$(notdir $(CURDIR))) + # "btree" cannot build against Picolibc right now. + cd $(TOP)/tests && ./run-natmodtests.py -p -d execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_NATMODTESTS_ARGS) extmod/{deflate,framebuf,heapq,random_basic,re}*.py + $(BUILD)/firmware.elf: $(LDSCRIPT) $(OBJ) $(Q)$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(Q)$(SIZE) $@ diff --git a/ports/qemu/README.md b/ports/qemu/README.md index 984faf8704794..9274bcc4e7566 100644 --- a/ports/qemu/README.md +++ b/ports/qemu/README.md @@ -71,8 +71,9 @@ 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: +This will start `qemu-system-arm` (or `qemu-system-riscv32`) with the UART +redirected to stdio. It's also possible to redirect the UART to a pty device +using: $ make run @@ -84,7 +85,7 @@ for example `mpremote`: 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). +was started (or execute `import machine; 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: @@ -97,6 +98,17 @@ tests against the serial device, for example: $ cd ../../tests $ ./run-tests.py -t /dev/pts/1 +Selected native modules that come as examples with the MicroPython source tree +can also be tested with this command (this is currently supported only for the +`VIRT_RV32` board): + + $ make test_natmod + +The same remarks about manually running the tests apply for native modules, but +`run-natmodtests.py` should be run instead of `run-tests.py`. In this case you +also have to explicitly pass the architecture you are running native modules to +`run-natmodtests.py` ("--arch rv32imc" for the `VIRT_RV32` board). + Extra make options ------------------ diff --git a/ports/qemu/boards/VIRT_RV32.mk b/ports/qemu/boards/VIRT_RV32.mk index a61b659fa6d40..355a09c3d520e 100644 --- a/ports/qemu/boards/VIRT_RV32.mk +++ b/ports/qemu/boards/VIRT_RV32.mk @@ -12,3 +12,5 @@ MPY_CROSS_FLAGS += -march=rv32imc # These Thumb tests don't run on RV32, so exclude them. RUN_TESTS_ARGS = --exclude 'inlineasm|qemu/asm_test' + +RUN_NATMODTESTS_ARGS = --arch rv32imc diff --git a/tools/ci.sh b/tools/ci.sh index c67aeed0cecdf..8d75d7cecf425 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -273,6 +273,7 @@ function ci_qemu_setup_arm { } function ci_qemu_setup_rv32 { + ci_mpy_format_setup ci_gcc_riscv_setup sudo apt-get update sudo apt-get install qemu-system @@ -292,6 +293,10 @@ function ci_qemu_build_rv32 { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV32 submodules make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV32 test + + # Test building and running native .mpy with rv32imc architecture. + ci_native_mpy_modules_build rv32imc + make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV32 test_natmod } ######################################################################################## @@ -476,10 +481,14 @@ function ci_native_mpy_modules_build { arch=$1 fi make -C examples/natmod/features1 ARCH=$arch - make -C examples/natmod/features2 ARCH=$arch + if [ $arch != rv32imc ]; then + # This requires soft-float support on rv32imc. + make -C examples/natmod/features2 ARCH=$arch + # This requires thread local storage support on rv32imc. + make -C examples/natmod/btree ARCH=$arch + fi make -C examples/natmod/features3 ARCH=$arch make -C examples/natmod/features4 ARCH=$arch - make -C examples/natmod/btree ARCH=$arch make -C examples/natmod/deflate ARCH=$arch make -C examples/natmod/framebuf ARCH=$arch make -C examples/natmod/heapq ARCH=$arch From 4729a895046908414b239f02256ac3334d1b2c15 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 20 Dec 2024 22:43:14 +0100 Subject: [PATCH 153/546] tools/verifygitlog.py: Show invalid commit subjects in quotes. If a commit subject line has any trailing whitespace it won't match the repository validation rules, and the line will show up as part of the relevant error message. However, since there's no quotation marks around the offending text, the trailing whitespace may go unnoticed, and given that the commit message is then discarded when the commit operation is retried this can get fairly annoying. This commit simply modifies the error output for invalid subject lines to add quotation marks around the offending text, so trailing whitespace is much easier to see. Signed-off-by: Alessandro Gatti --- tools/verifygitlog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/verifygitlog.py b/tools/verifygitlog.py index ad9385e7ac540..67215d5c5d066 100755 --- a/tools/verifygitlog.py +++ b/tools/verifygitlog.py @@ -47,7 +47,7 @@ def git_log(pretty_format, *args): def diagnose_subject_line(subject_line, subject_line_format, err): - err.error("Subject line: " + subject_line) + err.error('Subject line: "' + subject_line + '"') if not subject_line.endswith("."): err.error('* must end with "."') if not re.match(r"^[^!]+: ", subject_line): From 50637ff239cb6a09294f49fe2d2534c2d7ae8b76 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Mar 2022 10:52:35 +1100 Subject: [PATCH 154/546] extmod/vfs_rom: Add VfsRom filesystem object. This commit defines a new ROMFS filesystem for storing read-only files that can be memory mapped, and a new VfsRom driver. Files opened from this filesystem support the buffer protocol. This allows naturally getting the memory-mapped address of the file using: - memoryview(file) - uctypes.addressof(file) Furthermore, if these files are .mpy files then their content can be referenced in-place when importing. Such imports take up a lot less RAM than importing from a normal filesystem. This is essentially dynamically frozen .mpy files, building on the revamped v6 .mpy file format. Signed-off-by: Damien George --- extmod/extmod.cmake | 2 + extmod/extmod.mk | 2 + extmod/modvfs.c | 4 + extmod/vfs_rom.c | 399 ++++++++++++++++++++++++++++++++++++++++++ extmod/vfs_rom.h | 47 +++++ extmod/vfs_rom_file.c | 180 +++++++++++++++++++ py/mpconfig.h | 5 + 7 files changed, 639 insertions(+) create mode 100644 extmod/vfs_rom.c create mode 100644 extmod/vfs_rom.h create mode 100644 extmod/vfs_rom_file.c diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index 98e8a84608a56..154931a664477 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -58,6 +58,8 @@ set(MICROPY_SOURCE_EXTMOD ${MICROPY_EXTMOD_DIR}/vfs_fat_diskio.c ${MICROPY_EXTMOD_DIR}/vfs_fat_file.c ${MICROPY_EXTMOD_DIR}/vfs_lfs.c + ${MICROPY_EXTMOD_DIR}/vfs_rom.c + ${MICROPY_EXTMOD_DIR}/vfs_rom_file.c ${MICROPY_EXTMOD_DIR}/vfs_posix.c ${MICROPY_EXTMOD_DIR}/vfs_posix_file.c ${MICROPY_EXTMOD_DIR}/vfs_reader.c diff --git a/extmod/extmod.mk b/extmod/extmod.mk index f2ae3ad6923fb..c8bc74d268e77 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -61,6 +61,8 @@ SRC_EXTMOD_C += \ extmod/vfs_fat_diskio.c \ extmod/vfs_fat_file.c \ extmod/vfs_lfs.c \ + extmod/vfs_rom.c \ + extmod/vfs_rom_file.c \ extmod/vfs_posix.c \ extmod/vfs_posix_file.c \ extmod/vfs_reader.c \ diff --git a/extmod/modvfs.c b/extmod/modvfs.c index 32dc0e5d08b0d..df422365b75ef 100644 --- a/extmod/modvfs.c +++ b/extmod/modvfs.c @@ -32,6 +32,7 @@ #include "extmod/vfs_fat.h" #include "extmod/vfs_lfs.h" #include "extmod/vfs_posix.h" +#include "extmod/vfs_rom.h" #if !MICROPY_VFS #error "MICROPY_PY_VFS requires MICROPY_VFS" @@ -51,6 +52,9 @@ static const mp_rom_map_elem_t vfs_module_globals_table[] = { #if MICROPY_VFS_LFS2 { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, #endif + #if MICROPY_VFS_ROM + { MP_ROM_QSTR(MP_QSTR_VfsRom), MP_ROM_PTR(&mp_type_vfs_rom) }, + #endif #if MICROPY_VFS_POSIX { MP_ROM_QSTR(MP_QSTR_VfsPosix), MP_ROM_PTR(&mp_type_vfs_posix) }, #endif diff --git a/extmod/vfs_rom.c b/extmod/vfs_rom.c new file mode 100644 index 0000000000000..b8b6f8e40d12b --- /dev/null +++ b/extmod/vfs_rom.c @@ -0,0 +1,399 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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. + */ + +// ROMFS filesystem format +// ======================= +// +// ROMFS is a flexible and extensible filesystem format designed to represent a +// directory hierarchy with files, where those files are read-only and their data +// can be memory mapped. +// +// Concepts: +// - varuint: An unsigned integer that is encoded in a variable number of bytes. It is +// stored big-endian with the high bit of the byte set if there are following bytes. +// - record: A variable sized element with a type. It is stored as two varuint's and then +// a payload. The first varuint is the record kind and the second varuint is the +// payload length (which may be zero bytes long). +// +// A ROMFS filesystem is a record with record kind 0x14a6b1, chosen so the encoded value +// is 0xd2-0xcd-0x31 which is "RM1" with the first two bytes having their high bit set. +// If the ROMFS record's payload is non-empty then it contains records. +// +// Record types: +// - 0 = unused, can be used to detect corruption of the filesystem. +// - 1 = padding/comments, can contain any data in their payload. +// - 2 = verbatim data, used to store file data. +// - 3 = indirect data, pointer to offset within the ROMFS payload. +// - 4 = a directory: payload contains a varuint which is the length of the directory +// name in bytes, then the name, then optional nested records for the contents +// of the directory (including optional metadata). +// - 5 = a file: payload contains a varuint which is the length of the filename in bytes +// then the name, then optional nested records. +// +// Remarks: +// - A varuint can be padded if needed by prepending with one or more 0x80 bytes. This +// padding does not change any semantics. +// - The size of the ROMFS record (including kind and length and payload) must be a +// multiple of 2 (because it's not possible to add a padding record of one byte). +// - File data can be optionally aligned using padding records and/or indirect data +// records. +// - There is no limit to the size of directory/file names or file data. +// +// Unknown record types must be skipped over. They may in the future add optional +// features, while still retaining backwards compatibility. Such features may be: +// - Alignment requirements of the ROMFS record. +// - Timestamps on directories/files. +// - A precomputed hash of a file, or other metadata. +// - An optimised lookup table indexing the directory hierarchy. + +#include + +#include "py/bc.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "extmod/vfs.h" +#include "extmod/vfs_rom.h" + +#if MICROPY_VFS_ROM + +#define ROMFS_SIZE_MIN (4) +#define ROMFS_HEADER_BYTE0 (0x80 | 'R') +#define ROMFS_HEADER_BYTE1 (0x80 | 'M') +#define ROMFS_HEADER_BYTE2 (0x00 | '1') + +// Values for `record_kind_t`. +#define ROMFS_RECORD_KIND_UNUSED (0) +#define ROMFS_RECORD_KIND_PADDING (1) +#define ROMFS_RECORD_KIND_DATA_VERBATIM (2) +#define ROMFS_RECORD_KIND_DATA_POINTER (3) +#define ROMFS_RECORD_KIND_DIRECTORY (4) +#define ROMFS_RECORD_KIND_FILE (5) + +typedef mp_uint_t record_kind_t; + +struct _mp_obj_vfs_rom_t { + mp_obj_base_t base; + mp_obj_t memory; + const uint8_t *filesystem; + const uint8_t *filesystem_end; +}; + +static record_kind_t extract_record(const uint8_t **fs, const uint8_t **fs_next) { + record_kind_t record_kind = mp_decode_uint(fs); + mp_uint_t record_len = mp_decode_uint(fs); + *fs_next = *fs + record_len; + return record_kind; +} + +static void extract_data(mp_obj_vfs_rom_t *self, const uint8_t *fs, const uint8_t *fs_top, size_t *size_out, const uint8_t **data_out) { + *size_out = 0; + *data_out = NULL; + while (fs < fs_top) { + const uint8_t *fs_next; + record_kind_t record_kind = extract_record(&fs, &fs_next); + if (record_kind == ROMFS_RECORD_KIND_DATA_VERBATIM) { + // Verbatim data. + *size_out = fs_next - fs; + *data_out = fs; + break; + } else if (record_kind == ROMFS_RECORD_KIND_DATA_POINTER) { + // Pointer to data. + *size_out = mp_decode_uint(&fs); + *data_out = self->filesystem + mp_decode_uint(&fs); + break; + } else { + // Skip this record. + fs = fs_next; + } + } +} + +// Searches for `path` in the filesystem. +// `path` must be null-terminated. +mp_import_stat_t mp_vfs_rom_search_filesystem(mp_obj_vfs_rom_t *self, const char *path, size_t *size_out, const uint8_t **data_out) { + const uint8_t *fs = self->filesystem; + const uint8_t *fs_top = self->filesystem_end; + size_t path_len = strlen(path); + if (*path == '/') { + // An optional slash at the start of the path enters the top-level filesystem. + ++path; + --path_len; + } + while (path_len > 0 && fs < fs_top) { + const uint8_t *fs_next; + record_kind_t record_kind = extract_record(&fs, &fs_next); + if (record_kind == ROMFS_RECORD_KIND_DIRECTORY || record_kind == ROMFS_RECORD_KIND_FILE) { + // A directory or file record. + mp_uint_t name_len = mp_decode_uint(&fs); + if ((name_len == path_len + || (name_len < path_len && path[name_len] == '/')) + && memcmp(path, fs, name_len) == 0) { + // Name matches, so enter this record. + fs += name_len; + fs_top = fs_next; + path += name_len; + path_len -= name_len; + if (record_kind == ROMFS_RECORD_KIND_DIRECTORY) { + // Continue searching in this directory. + if (*path == '/') { + ++path; + --path_len; + } + } else { + // Return this file. + if (path_len != 0) { + return MP_IMPORT_STAT_NO_EXIST; + } + if (size_out != NULL) { + extract_data(self, fs, fs_top, size_out, data_out); + } + return MP_IMPORT_STAT_FILE; + } + } else { + // Skip this directory/file record. + fs = fs_next; + } + } else { + // Skip this record. + fs = fs_next; + } + } + if (path_len == 0) { + if (size_out != NULL) { + *size_out = fs_top - fs; + *data_out = fs; + } + return MP_IMPORT_STAT_DIR; + } + return MP_IMPORT_STAT_NO_EXIST; +} + +static mp_obj_t vfs_rom_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_obj_vfs_rom_t *self = m_new_obj(mp_obj_vfs_rom_t); + self->base.type = type; + self->memory = args[0]; + + mp_buffer_info_t bufinfo; + if (mp_get_buffer(self->memory, &bufinfo, MP_BUFFER_READ)) { + if (bufinfo.len < ROMFS_SIZE_MIN) { + mp_raise_OSError(MP_ENODEV); + } + self->filesystem = bufinfo.buf; + } else { + self->filesystem = (const uint8_t *)(uintptr_t)mp_obj_get_int_truncated(self->memory); + } + + // Verify it is a ROMFS. + if (!(self->filesystem[0] == ROMFS_HEADER_BYTE0 + && self->filesystem[1] == ROMFS_HEADER_BYTE1 + && self->filesystem[2] == ROMFS_HEADER_BYTE2)) { + mp_raise_OSError(MP_ENODEV); + } + + // The ROMFS is a record itself, so enter into it and compute its limit. + extract_record(&self->filesystem, &self->filesystem_end); + + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t vfs_rom_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) { + (void)self_in; + (void)readonly; + if (mp_obj_is_true(mkfs)) { + mp_raise_OSError(MP_EPERM); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(vfs_rom_mount_obj, vfs_rom_mount); + +// mp_vfs_rom_file_open is implemented in vfs_rom_file.c. +static MP_DEFINE_CONST_FUN_OBJ_3(vfs_rom_open_obj, mp_vfs_rom_file_open); + +static mp_obj_t vfs_rom_chdir(mp_obj_t self_in, mp_obj_t path_in) { + mp_obj_vfs_rom_t *self = MP_OBJ_TO_PTR(self_in); + const char *path = mp_vfs_rom_get_path_str(self, path_in); + if (path[0] == '/' && path[1] == '\0') { + // Allow chdir to the root of the filesystem. + } else { + // Don't allow chdir to any subdirectory (not currently implemented). + mp_raise_OSError(MP_EOPNOTSUPP); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(vfs_rom_chdir_obj, vfs_rom_chdir); + +typedef struct _vfs_rom_ilistdir_it_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_obj_vfs_rom_t *vfs_rom; + bool is_str; + const uint8_t *index; + const uint8_t *index_top; +} vfs_rom_ilistdir_it_t; + +static mp_obj_t vfs_rom_ilistdir_it_iternext(mp_obj_t self_in) { + vfs_rom_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in); + + while (self->index < self->index_top) { + const uint8_t *index_next; + record_kind_t record_kind = extract_record(&self->index, &index_next); + uint32_t type; + mp_uint_t name_len; + size_t data_len; + if (record_kind == ROMFS_RECORD_KIND_DIRECTORY || record_kind == ROMFS_RECORD_KIND_FILE) { + // A directory or file record. + name_len = mp_decode_uint(&self->index); + if (record_kind == ROMFS_RECORD_KIND_DIRECTORY) { + // A directory. + type = MP_S_IFDIR; + data_len = index_next - self->index - name_len; + } else { + // A file. + type = MP_S_IFREG; + const uint8_t *data_value; + extract_data(self->vfs_rom, self->index + name_len, index_next, &data_len, &data_value); + } + } else { + // Skip this record. + self->index = index_next; + continue; + } + + const uint8_t *name_str = self->index; + self->index = index_next; + + // Make 4-tuple with info about this entry: (name, attr, inode, size) + 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((const char *)name_str, name_len); + } else { + t->items[0] = mp_obj_new_bytes(name_str, name_len); + } + + t->items[1] = MP_OBJ_NEW_SMALL_INT(type); + t->items[2] = MP_OBJ_NEW_SMALL_INT(0); + t->items[3] = mp_obj_new_int(data_len); + + return MP_OBJ_FROM_PTR(t); + } + + return MP_OBJ_STOP_ITERATION; +} + +static mp_obj_t vfs_rom_ilistdir(mp_obj_t self_in, mp_obj_t path_in) { + mp_obj_vfs_rom_t *self = MP_OBJ_TO_PTR(self_in); + vfs_rom_ilistdir_it_t *iter = m_new_obj(vfs_rom_ilistdir_it_t); + iter->base.type = &mp_type_polymorph_iter; + iter->iternext = vfs_rom_ilistdir_it_iternext; + iter->vfs_rom = self; + iter->is_str = mp_obj_get_type(path_in) == &mp_type_str; + const char *path = mp_vfs_rom_get_path_str(self, path_in); + size_t size; + if (mp_vfs_rom_search_filesystem(self, path, &size, &iter->index) != MP_IMPORT_STAT_DIR) { + mp_raise_OSError(MP_ENOENT); + } + iter->index_top = iter->index + size; + return MP_OBJ_FROM_PTR(iter); +} +static MP_DEFINE_CONST_FUN_OBJ_2(vfs_rom_ilistdir_obj, vfs_rom_ilistdir); + +static mp_obj_t vfs_rom_stat(mp_obj_t self_in, mp_obj_t path_in) { + mp_obj_vfs_rom_t *self = MP_OBJ_TO_PTR(self_in); + const char *path = mp_vfs_rom_get_path_str(self, path_in); + size_t file_size; + const uint8_t *file_data; + mp_import_stat_t stat = mp_vfs_rom_search_filesystem(self, path, &file_size, &file_data); + if (stat == MP_IMPORT_STAT_NO_EXIST) { + mp_raise_OSError(MP_ENOENT); + } + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); + t->items[0] = MP_OBJ_NEW_SMALL_INT(stat == MP_IMPORT_STAT_FILE ? MP_S_IFREG : MP_S_IFDIR); // st_mode + t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino + t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev + t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink + t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid + t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid + t->items[6] = MP_OBJ_NEW_SMALL_INT(file_size); // st_size + t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // st_atime + t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // st_mtime + t->items[9] = MP_OBJ_NEW_SMALL_INT(0); // st_ctime + return MP_OBJ_FROM_PTR(t); +} +static MP_DEFINE_CONST_FUN_OBJ_2(vfs_rom_stat_obj, vfs_rom_stat); + +static mp_obj_t vfs_rom_statvfs(mp_obj_t self_in, mp_obj_t path_in) { + mp_obj_vfs_rom_t *self = MP_OBJ_TO_PTR(self_in); + (void)path_in; + size_t filesystem_len = self->filesystem_end - self->filesystem; + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); + t->items[0] = MP_OBJ_NEW_SMALL_INT(1); // f_bsize + t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // f_frsize + t->items[2] = mp_obj_new_int_from_uint(filesystem_len); // f_blocks + t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // f_bfree + t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // f_bavail + t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // f_files + t->items[6] = MP_OBJ_NEW_SMALL_INT(0); // f_ffree + t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // f_favail + t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // f_flags + t->items[9] = MP_OBJ_NEW_SMALL_INT(32767); // f_namemax + return MP_OBJ_FROM_PTR(t); +} +static MP_DEFINE_CONST_FUN_OBJ_2(vfs_rom_statvfs_obj, vfs_rom_statvfs); + +static const mp_rom_map_elem_t vfs_rom_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_rom_mount_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_identity_obj) }, + { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&vfs_rom_open_obj) }, + + { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&vfs_rom_chdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&vfs_rom_ilistdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&vfs_rom_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&vfs_rom_statvfs_obj) }, +}; +static MP_DEFINE_CONST_DICT(vfs_rom_locals_dict, vfs_rom_locals_dict_table); + +static mp_import_stat_t mp_vfs_rom_import_stat(void *self_in, const char *path) { + mp_obj_vfs_rom_t *self = MP_OBJ_TO_PTR(self_in); + return mp_vfs_rom_search_filesystem(self, path, NULL, NULL); +} + +static const mp_vfs_proto_t vfs_rom_proto = { + .import_stat = mp_vfs_rom_import_stat, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_vfs_rom, + MP_QSTR_VfsRom, + MP_TYPE_FLAG_NONE, + make_new, vfs_rom_make_new, + protocol, &vfs_rom_proto, + locals_dict, &vfs_rom_locals_dict + ); + +#endif // MICROPY_VFS_ROM diff --git a/extmod/vfs_rom.h b/extmod/vfs_rom.h new file mode 100644 index 0000000000000..d8e2b911ba0ca --- /dev/null +++ b/extmod/vfs_rom.h @@ -0,0 +1,47 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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_EXTMOD_VFS_ROM_H +#define MICROPY_INCLUDED_EXTMOD_VFS_ROM_H + +#include "py/builtin.h" +#include "py/obj.h" + +#if MICROPY_VFS_ROM + +typedef struct _mp_obj_vfs_rom_t mp_obj_vfs_rom_t; + +extern const mp_obj_type_t mp_type_vfs_rom; + +static inline const char *mp_vfs_rom_get_path_str(mp_obj_vfs_rom_t *self, mp_obj_t path) { + return mp_obj_str_get_str(path); +} + +mp_import_stat_t mp_vfs_rom_search_filesystem(mp_obj_vfs_rom_t *self, const char *path, size_t *size_out, const uint8_t **data_out); +mp_obj_t mp_vfs_rom_file_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in); + +#endif // MICROPY_VFS_ROM + +#endif // MICROPY_INCLUDED_EXTMOD_VFS_ROM_H diff --git a/extmod/vfs_rom_file.c b/extmod/vfs_rom_file.c new file mode 100644 index 0000000000000..57aca8c5dc7d3 --- /dev/null +++ b/extmod/vfs_rom_file.c @@ -0,0 +1,180 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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 "py/reader.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "extmod/vfs_rom.h" + +#if MICROPY_VFS_ROM + +typedef struct _mp_obj_vfs_rom_file_t { + mp_obj_base_t base; + size_t file_size; + size_t file_offset; + const uint8_t *file_data; +} mp_obj_vfs_rom_file_t; + +static const mp_obj_type_t mp_type_vfs_rom_fileio; +static const mp_obj_type_t mp_type_vfs_rom_textio; + +mp_obj_t mp_vfs_rom_file_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) { + mp_obj_vfs_rom_t *self = MP_OBJ_TO_PTR(self_in); + + const char *mode_s = mp_obj_str_get_str(mode_in); + const mp_obj_type_t *type = &mp_type_vfs_rom_textio; + while (*mode_s) { + switch (*mode_s++) { + case 'r': + break; + case 'w': + case 'a': + case '+': + mp_raise_OSError(MP_EROFS); + case 'b': + type = &mp_type_vfs_rom_fileio; + break; + case 't': + type = &mp_type_vfs_rom_textio; + break; + } + } + + mp_obj_vfs_rom_file_t *o = m_new_obj(mp_obj_vfs_rom_file_t); + o->base.type = type; + o->file_offset = 0; + + const char *path = mp_vfs_rom_get_path_str(self, path_in); + mp_import_stat_t stat = mp_vfs_rom_search_filesystem(self, path, &o->file_size, &o->file_data); + if (stat == MP_IMPORT_STAT_NO_EXIST) { + mp_raise_OSError(MP_ENOENT); + } else if (stat == MP_IMPORT_STAT_DIR) { + mp_raise_OSError(MP_EISDIR); + } + + return MP_OBJ_FROM_PTR(o); +} + +static mp_int_t vfs_rom_file_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + mp_obj_vfs_rom_file_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)self->file_data; + bufinfo->len = self->file_size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Can't write to a ROM file. + return 1; + } +} + +static mp_uint_t vfs_rom_file_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { + mp_obj_vfs_rom_file_t *self = MP_OBJ_TO_PTR(o_in); + size_t remain = self->file_size - self->file_offset; + if (size > remain) { + size = remain; + } + memcpy(buf, self->file_data + self->file_offset, size); + self->file_offset += size; + return size; +} + +static mp_uint_t vfs_rom_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { + mp_obj_vfs_rom_file_t *self = MP_OBJ_TO_PTR(o_in); + + switch (request) { + case MP_STREAM_SEEK: { + struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)arg; + if (s->whence == 0) { // SEEK_SET + self->file_offset = (size_t)s->offset; + } else if (s->whence == 1) { // SEEK_CUR + self->file_offset += s->offset; + } else { // SEEK_END + self->file_offset = self->file_size + s->offset; + } + if (self->file_offset > self->file_size) { + if (s->offset < 0) { + // Seek to before the start of the file. + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; + } + self->file_offset = self->file_size; + } + s->offset = self->file_offset; + return 0; + } + case MP_STREAM_CLOSE: + return 0; + default: + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; + } +} + +static const mp_rom_map_elem_t vfs_rom_rawfile_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) }, + { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) }, + { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&mp_stream___exit___obj) }, +}; +static MP_DEFINE_CONST_DICT(vfs_rom_rawfile_locals_dict, vfs_rom_rawfile_locals_dict_table); + +static const mp_stream_p_t vfs_rom_fileio_stream_p = { + .read = vfs_rom_file_read, + .ioctl = vfs_rom_file_ioctl, +}; + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_vfs_rom_fileio, + MP_QSTR_FileIO, + MP_TYPE_FLAG_ITER_IS_STREAM, + buffer, vfs_rom_file_get_buffer, + protocol, &vfs_rom_fileio_stream_p, + locals_dict, &vfs_rom_rawfile_locals_dict + ); + +static const mp_stream_p_t vfs_rom_textio_stream_p = { + .read = vfs_rom_file_read, + .ioctl = vfs_rom_file_ioctl, + .is_text = true, +}; + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_vfs_rom_textio, + MP_QSTR_TextIOWrapper, + MP_TYPE_FLAG_ITER_IS_STREAM, + protocol, &vfs_rom_textio_stream_p, + locals_dict, &vfs_rom_rawfile_locals_dict + ); + +#endif // MICROPY_VFS_ROM diff --git a/py/mpconfig.h b/py/mpconfig.h index a330cadc4ef3a..8598eaa5bb639 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1016,6 +1016,11 @@ typedef double mp_float_t; #define MICROPY_VFS_LFS2 (0) #endif +// Support for ROMFS. +#ifndef MICROPY_VFS_ROM +#define MICROPY_VFS_ROM (0) +#endif + /*****************************************************************************/ /* Fine control over Python builtins, classes, modules, etc */ From a1c32101ac3bc70ed2774f461c69548ce8dae23d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Mar 2022 10:40:15 +1100 Subject: [PATCH 155/546] py/qstr: Add qstr_from_strn_static() helper function. Allows an interned string to reference static/ROM data, instead of allocating it on the GC heap. Signed-off-by: Damien George --- py/qstr.c | 24 ++++++++++++++++++++++-- py/qstr.h | 3 +++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/py/qstr.c b/py/qstr.c index fea7f44fe13bf..7902646a92dc5 100644 --- a/py/qstr.c +++ b/py/qstr.c @@ -309,7 +309,7 @@ qstr qstr_from_str(const char *str) { return qstr_from_strn(str, strlen(str)); } -qstr qstr_from_strn(const char *str, size_t len) { +static qstr qstr_from_strn_helper(const char *str, size_t len, bool data_is_static) { QSTR_ENTER(); qstr q = qstr_find_strn(str, len); if (q == 0) { @@ -321,6 +321,12 @@ qstr qstr_from_strn(const char *str, size_t len) { mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("name too long")); } + if (data_is_static) { + // Given string data will be forever available so use it directly. + assert(str[len] == '\0'); + goto add; + } + // compute number of bytes needed to intern this string size_t n_bytes = len + 1; @@ -364,12 +370,26 @@ qstr qstr_from_strn(const char *str, size_t len) { // store the interned strings' data memcpy(q_ptr, str, len); q_ptr[len] = '\0'; - q = qstr_add(len, q_ptr); + str = q_ptr; + + add: + q = qstr_add(len, str); } QSTR_EXIT(); return q; } +qstr qstr_from_strn(const char *str, size_t len) { + return qstr_from_strn_helper(str, len, false); +} + +#if MICROPY_VFS_ROM +// Create a new qstr that can forever reference the given string data. +qstr qstr_from_strn_static(const char *str, size_t len) { + return qstr_from_strn_helper(str, len, true); +} +#endif + mp_uint_t qstr_hash(qstr q) { const qstr_pool_t *pool = find_qstr(&q); #if MICROPY_QSTR_BYTES_IN_HASH diff --git a/py/qstr.h b/py/qstr.h index 58ce285fd2c8a..0cde6062eb1a5 100644 --- a/py/qstr.h +++ b/py/qstr.h @@ -101,6 +101,9 @@ qstr qstr_find_strn(const char *str, size_t str_len); // returns MP_QSTRnull if qstr qstr_from_str(const char *str); qstr qstr_from_strn(const char *str, size_t len); +#if MICROPY_VFS_ROM +qstr qstr_from_strn_static(const char *str, size_t len); +#endif mp_uint_t qstr_hash(qstr q); const char *qstr_str(qstr q); From f870e8d2d406e809b4494e6d029fee0269b1ea05 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Mar 2022 10:54:05 +1100 Subject: [PATCH 156/546] py/reader: Provide mp_reader_try_read_rom() function. This allows accessing data directly in ROM if the reader supports it. Signed-off-by: Damien George --- py/reader.c | 15 ++++++++++++++- py/reader.h | 9 +++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/py/reader.c b/py/reader.c index 151e04cac2c71..8feb6d75275f1 100644 --- a/py/reader.c +++ b/py/reader.c @@ -50,7 +50,7 @@ static mp_uint_t mp_reader_mem_readbyte(void *data) { static void mp_reader_mem_close(void *data) { mp_reader_mem_t *reader = (mp_reader_mem_t *)data; - if (reader->free_len > 0) { + if (reader->free_len > 0 && reader->free_len != MP_READER_IS_ROM) { m_del(char, (char *)reader->beg, reader->free_len); } m_del_obj(mp_reader_mem_t, reader); @@ -67,6 +67,19 @@ void mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t reader->close = mp_reader_mem_close; } +const uint8_t *mp_reader_try_read_rom(mp_reader_t *reader, size_t len) { + if (reader->readbyte != mp_reader_mem_readbyte) { + return NULL; + } + mp_reader_mem_t *m = reader->data; + if (m->free_len != MP_READER_IS_ROM) { + return NULL; + } + const uint8_t *data = m->cur; + m->cur += len; + return data; +} + #if MICROPY_READER_POSIX #include diff --git a/py/reader.h b/py/reader.h index 5cb1e67966c61..301c70ab3f0d4 100644 --- a/py/reader.h +++ b/py/reader.h @@ -28,6 +28,10 @@ #include "py/obj.h" +// Pass to the `free_len` argument to `mp_reader_new_mem` to indicate that the data is in ROM. +// This means that the data is addressable and will remain valid at least until a soft reset. +#define MP_READER_IS_ROM ((size_t)-1) + // the readbyte function must return the next byte in the input stream // it must return MP_READER_EOF if end of stream // it can be called again after returning MP_READER_EOF, and in that case must return MP_READER_EOF @@ -43,4 +47,9 @@ void mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t void mp_reader_new_file(mp_reader_t *reader, qstr filename); void mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd); +// Try to efficiently read the given number of bytes from a ROM-based reader. +// Returns a valid, non-NULL pointer to the requseted data if the reader points to ROM. +// Returns NULL if the reader does not point to ROM. +const uint8_t *mp_reader_try_read_rom(mp_reader_t *reader, size_t len); + #endif // MICROPY_INCLUDED_PY_READER_H From 5e9dd4b6a2ba58bc61ffc0d0a34a03c0382fcc72 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 18 Dec 2024 11:30:33 +1100 Subject: [PATCH 157/546] extmod/vfs_reader: Add support for opening a memory-mappable file. If the file can be memory mapped (because it responds to the buffer protocol) then return a memory-reader that directly references the ROM data of the file. Signed-off-by: Damien George --- extmod/vfs_reader.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/extmod/vfs_reader.c b/extmod/vfs_reader.c index 80d0fa6344aa1..de5c4e03d3c13 100644 --- a/extmod/vfs_reader.c +++ b/extmod/vfs_reader.c @@ -85,6 +85,17 @@ void mp_reader_new_file(mp_reader_t *reader, qstr filename) { const mp_stream_p_t *stream_p = mp_get_stream(file); int errcode = 0; + + #if MICROPY_VFS_ROM + // Check if the stream can be memory mapped. + mp_buffer_info_t bufinfo; + if (mp_get_buffer(file, &bufinfo, MP_BUFFER_READ)) { + mp_reader_new_mem(reader, bufinfo.buf, bufinfo.len, MP_READER_IS_ROM); + return; + } + #endif + + // Determine how big the input buffer should be, if the stream requests a certain size or not. mp_uint_t bufsize = stream_p->ioctl(file, MP_STREAM_GET_BUFFER_SIZE, 0, &errcode); if (bufsize == MP_STREAM_ERROR || bufsize == 0) { // bufsize == 0 is included here to support mpremote v1.21 and older where mount file ioctl @@ -94,6 +105,7 @@ void mp_reader_new_file(mp_reader_t *reader, qstr filename) { bufsize = MIN(MICROPY_READER_VFS_MAX_BUFFER_SIZE, MAX(MICROPY_READER_VFS_MIN_BUFFER_SIZE, bufsize)); } + // Create the reader. mp_reader_vfs_t *rf = m_new_obj_var(mp_reader_vfs_t, buf, byte, bufsize); rf->file = file; rf->bufsize = bufsize; From 97518064cd5fadf6e57e9b989958c9f36fe94e47 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Mar 2022 10:54:05 +1100 Subject: [PATCH 158/546] py/persistentcode: Add support for loading .mpy files from a ROM reader. This adds an optimisation for loading .mpy files from a reader that points to ROM. In such a case qstr, str and bytes data, along with bytecode, are all referenced in-place in ROM. Signed-off-by: Damien George --- py/persistentcode.c | 70 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/py/persistentcode.c b/py/persistentcode.c index be93eaa5b4564..0843d1a2c52e6 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -181,6 +181,15 @@ static qstr load_qstr(mp_reader_t *reader) { return len >> 1; } len >>= 1; + + #if MICROPY_VFS_ROM + // If possible, create the qstr from the memory-mapped string data. + const uint8_t *memmap = mp_reader_try_read_rom(reader, len + 1); + if (memmap != NULL) { + return qstr_from_strn_static((const char *)memmap, len); + } + #endif + char *str = m_new(char, len); read_bytes(reader, (byte *)str, len); read_byte(reader); // read and discard null terminator @@ -189,6 +198,24 @@ static qstr load_qstr(mp_reader_t *reader) { return qst; } +#if MICROPY_VFS_ROM +// Create a str/bytes object that can forever reference the given data. +static mp_obj_t mp_obj_new_str_static(const mp_obj_type_t *type, const byte *data, size_t len) { + if (type == &mp_type_str) { + qstr q = qstr_find_strn((const char *)data, len); + if (q != MP_QSTRnull) { + return MP_OBJ_NEW_QSTR(q); + } + } + assert(data[len] == '\0'); + mp_obj_str_t *o = mp_obj_malloc(mp_obj_str_t, type); + o->len = len; + o->hash = qstr_compute_hash(data, len); + o->data = data; + return MP_OBJ_FROM_PTR(o); +} +#endif + static mp_obj_t load_obj(mp_reader_t *reader) { byte obj_type = read_byte(reader); #if MICROPY_EMIT_MACHINE_CODE @@ -206,6 +233,8 @@ static mp_obj_t load_obj(mp_reader_t *reader) { return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj); } else { size_t len = read_uint(reader); + + // Handle empty bytes object, and tuple objects. if (len == 0 && obj_type == MP_PERSISTENT_OBJ_BYTES) { read_byte(reader); // skip null terminator return mp_const_empty_bytes; @@ -216,11 +245,31 @@ static mp_obj_t load_obj(mp_reader_t *reader) { } return MP_OBJ_FROM_PTR(tuple); } + + // Read in the object's data, either from ROM or into RAM. + const uint8_t *memmap = NULL; vstr_t vstr; - vstr_init_len(&vstr, len); - read_bytes(reader, (byte *)vstr.buf, len); + #if MICROPY_VFS_ROM + memmap = mp_reader_try_read_rom(reader, len); + vstr.buf = (void *)memmap; + vstr.len = len; + #endif + if (memmap == NULL) { + // Data could not be memory-mapped, so allocate it in RAM and read it in. + vstr_init_len(&vstr, len); + read_bytes(reader, (byte *)vstr.buf, len); + } + + // Create and return the object. if (obj_type == MP_PERSISTENT_OBJ_STR || obj_type == MP_PERSISTENT_OBJ_BYTES) { - read_byte(reader); // skip null terminator + read_byte(reader); // skip null terminator (it needs to be there for ROM str objects) + #if MICROPY_VFS_ROM + if (memmap != NULL) { + // Create a str/bytes that references the memory-mapped data. + const mp_obj_type_t *t = obj_type == MP_PERSISTENT_OBJ_STR ? &mp_type_str : &mp_type_bytes; + return mp_obj_new_str_static(t, memmap, len); + } + #endif if (obj_type == MP_PERSISTENT_OBJ_STR) { return mp_obj_new_str_from_utf8_vstr(&vstr); } else { @@ -257,10 +306,17 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co #endif if (kind == MP_CODE_BYTECODE) { - // Allocate memory for the bytecode - fun_data = m_new(uint8_t, fun_data_len); - // Load bytecode - read_bytes(reader, fun_data, fun_data_len); + #if MICROPY_VFS_ROM + // Try to reference memory-mapped data for the bytecode. + fun_data = (uint8_t *)mp_reader_try_read_rom(reader, fun_data_len); + #endif + + if (fun_data == NULL) { + // Allocate memory for the bytecode. + fun_data = m_new(uint8_t, fun_data_len); + // Load bytecode. + read_bytes(reader, fun_data, fun_data_len); + } #if MICROPY_EMIT_MACHINE_CODE } else { From d9378c9287f87c0214dc4a33206dfd047f798240 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Mar 2022 10:56:27 +1100 Subject: [PATCH 159/546] unix: Enable VfsRom on standard and coverage variants. Signed-off-by: Damien George --- ports/unix/variants/mpconfigvariant_common.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/unix/variants/mpconfigvariant_common.h b/ports/unix/variants/mpconfigvariant_common.h index cea0397414325..093477af0aded 100644 --- a/ports/unix/variants/mpconfigvariant_common.h +++ b/ports/unix/variants/mpconfigvariant_common.h @@ -121,3 +121,5 @@ #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_PIN_BASE (1) + +#define MICROPY_VFS_ROM (1) From def9a37994897341ee9250b39851f9cf53a748de Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 25 Nov 2024 23:58:01 +1100 Subject: [PATCH 160/546] qemu/mpconfigport: Enable VfsRom. Signed-off-by: Damien George --- ports/qemu/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/qemu/mpconfigport.h b/ports/qemu/mpconfigport.h index f95b58ba4d465..d3e3c0a3f37ac 100644 --- a/ports/qemu/mpconfigport.h +++ b/ports/qemu/mpconfigport.h @@ -60,6 +60,7 @@ #define MICROPY_PY_MACHINE_RESET (1) #define MICROPY_PY_MACHINE_PIN_BASE (1) #define MICROPY_VFS (1) +#define MICROPY_VFS_ROM (1) // type definitions for the specific machine From c73204128e16cfa62abd42b07981bd8e5adeb321 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 18 Nov 2024 10:35:31 +1100 Subject: [PATCH 161/546] tests/extmod: Add VfsRom test. Provides full coverage of the VfsRom driver. Signed-off-by: Damien George --- tests/extmod/vfs_rom.py | 412 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 412 insertions(+) create mode 100644 tests/extmod/vfs_rom.py diff --git a/tests/extmod/vfs_rom.py b/tests/extmod/vfs_rom.py new file mode 100644 index 0000000000000..0382c84c55894 --- /dev/null +++ b/tests/extmod/vfs_rom.py @@ -0,0 +1,412 @@ +# Test VfsRom filesystem. + +try: + import sys, struct, os, uctypes, vfs + + vfs.VfsRom +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +try: + import select +except ImportError: + select = None + +import unittest + +IFDIR = 0x4000 +IFREG = 0x8000 + +SEEK_SET = 0 +SEEK_CUR = 1 +SEEK_END = 2 + +# An mpy file with four constant objects: str, bytes, long-int, float. +test_mpy = ( + # header + b"M\x06\x00\x1f" # mpy file header + b"\x06" # n_qstr + b"\x05" # n_obj + # qstrs + b"\x0etest.py\x00" # qstr0 = "test.py" + b"\x0f" # qstr1 = "" + b"\x0estr_obj\x00" # qstr2 = "str_obj" + b"\x12bytes_obj\x00" # qstr3 = "bytes_obj" + b"\x0eint_obj\x00" # qstr4 = "int_obj" + b"\x12float_obj\x00" # qstr5 = "float_obj" + # objects + b"\x05\x14this is a str object\x00" + b"\x06\x16this is a bytes object\x00" + b"\x07\x0a1234567890" # long-int object + b"\x08\x041.23" # float object + b"\x05\x07str_obj\x00" # str object of existing qstr + # bytecode + b"\x81\x28" # 21 bytes, no children, bytecode + b"\x00\x02" # prelude + b"\x01" # simple name () + b"\x23\x00" # LOAD_CONST_OBJ(0) + b"\x16\x02" # STORE_NAME(str_obj) + b"\x23\x01" # LOAD_CONST_OBJ(1) + b"\x16\x03" # STORE_NAME(bytes_obj) + b"\x23\x02" # LOAD_CONST_OBJ(2) + b"\x16\x04" # STORE_NAME(int_obj) + b"\x23\x03" # LOAD_CONST_OBJ(3) + b"\x16\x05" # STORE_NAME(float_obj) + b"\x51" # LOAD_CONST_NONE + b"\x63" # RETURN_VALUE +) + + +class VfsRomWriter: + ROMFS_HEADER = b"\xd2\xcd\x31" + + ROMFS_RECORD_KIND_UNUSED = 0 + ROMFS_RECORD_KIND_PADDING = 1 + ROMFS_RECORD_KIND_DATA_VERBATIM = 2 + ROMFS_RECORD_KIND_DATA_POINTER = 3 + ROMFS_RECORD_KIND_DIRECTORY = 4 + ROMFS_RECORD_KIND_FILE = 5 + + def __init__(self): + self._dir_stack = [(None, bytearray())] + + def _encode_uint(self, value): + encoded = [value & 0x7F] + value >>= 7 + while value != 0: + encoded.insert(0, 0x80 | (value & 0x7F)) + value >>= 7 + return bytes(encoded) + + def _pack(self, kind, payload): + return self._encode_uint(kind) + self._encode_uint(len(payload)) + payload + + def _extend(self, data): + buf = self._dir_stack[-1][1] + buf.extend(data) + return len(buf) + + def finalise(self): + _, data = self._dir_stack.pop() + encoded_kind = VfsRomWriter.ROMFS_HEADER + encoded_len = self._encode_uint(len(data)) + if (len(encoded_kind) + len(encoded_len) + len(data)) % 2 == 1: + encoded_len = b"\x80" + encoded_len + data = encoded_kind + encoded_len + data + return data + + def opendir(self, dirname): + self._dir_stack.append((dirname, bytearray())) + + def closedir(self): + dirname, dirdata = self._dir_stack.pop() + dirdata = self._encode_uint(len(dirname)) + bytes(dirname, "ascii") + dirdata + self._extend(self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DIRECTORY, dirdata)) + + def mkdata(self, data): + assert len(self._dir_stack) == 1 + return self._extend(self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DATA_VERBATIM, data)) - len( + data + ) + + def mkfile(self, filename, filedata, extra_payload=b""): + filename = bytes(filename, "ascii") + payload = self._encode_uint(len(filename)) + payload += filename + payload += extra_payload + if isinstance(filedata, tuple): + sub_payload = self._encode_uint(filedata[0]) + sub_payload += self._encode_uint(filedata[1]) + payload += self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DATA_POINTER, sub_payload) + else: + payload += self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DATA_VERBATIM, filedata) + self._dir_stack[-1][1].extend(self._pack(VfsRomWriter.ROMFS_RECORD_KIND_FILE, payload)) + + +def _make_romfs(fs, files, data_map): + for filename, contents in files: + if isinstance(contents, tuple): + fs.opendir(filename) + _make_romfs(fs, contents, data_map) + fs.closedir() + elif isinstance(contents, int): + fs.mkfile(filename, data_map[contents]) + else: + fs.mkfile(filename, contents) + + +def make_romfs(files, data=None): + fs = VfsRomWriter() + data_map = {} + if data: + for k, v in data.items(): + data_map[k] = len(v), fs.mkdata(v) + _make_romfs(fs, files, data_map) + return fs.finalise() + + +# A class to test if a value is within a range, needed because MicroPython's range +# doesn't support arbitrary objects. +class Range: + def __init__(self, lower, upper): + self._lower = lower + self._upper = upper + + def __repr__(self): + return "Range({}, {})".format(self._lower, self._upper) + + def __contains__(self, value): + return self._lower <= value < self._upper + + +class TestBase(unittest.TestCase): + @classmethod + def setUpClass(cls): + fs_inner = make_romfs((("test_inner.txt", b"contents_inner"), ("c.py", b""))) + cls.romfs = make_romfs( + ( + ("fs.romfs", 0), + ("test.txt", b"contents"), + ( + "dir", + ( + ("a.py", b"x = 1"), + ("b.py", b"x = 2"), + ("test.mpy", test_mpy), + ), + ), + ), + {0: fs_inner}, + ) + cls.romfs_ilistdir = [ + ("fs.romfs", IFREG, 0, 46), + ("test.txt", IFREG, 0, 8), + ("dir", IFDIR, 0, 198), + ] + cls.romfs_listdir = [x[0] for x in cls.romfs_ilistdir] + cls.romfs_listdir_dir = ["a.py", "b.py", "test.mpy"] + cls.romfs_listdir_bytes = [bytes(x, "ascii") for x in cls.romfs_listdir] + cls.romfs_addr = uctypes.addressof(cls.romfs) + cls.romfs_addr_range = Range(cls.romfs_addr, cls.romfs_addr + len(cls.romfs)) + + +class TestEdgeCases(unittest.TestCase): + def test_empty_romfs(self): + empty_romfs = make_romfs(()) + self.assertEqual(empty_romfs, bytes([0x80 | ord("R"), 0x80 | ord("M"), ord("1"), 0])) + fs = vfs.VfsRom(empty_romfs) + self.assertIsInstance(fs, vfs.VfsRom) + fs.mount(True, False) + self.assertEqual(list(fs.ilistdir("")), []) + self.assertEqual(fs.stat(""), (IFDIR, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + self.assertEqual(fs.statvfs(""), (1, 0, 0, 0, 0, 0, 0, 0, 0, 32767)) + + def test_error(self): + for bad_romfs in (b"", b"xxx", b"not a romfs"): + with self.assertRaises(OSError) as ctx: + vfs.VfsRom(bad_romfs) + self.assertEqual(ctx.exception.errno, errno.ENODEV) + + def test_unknown_record(self): + fs = VfsRomWriter() + fs._extend(fs._pack(VfsRomWriter.ROMFS_RECORD_KIND_PADDING, b"payload")) + fs.mkfile( + "test", + b"contents", + extra_payload=fs._pack(VfsRomWriter.ROMFS_RECORD_KIND_PADDING, b"pad"), + ) + romfs = fs.finalise() + fs = vfs.VfsRom(romfs) + self.assertEqual(list(fs.ilistdir("")), [("test", IFREG, 0, 8)]) + with fs.open("test", "rb") as f: + self.assertEqual(f.read(), b"contents") + + +class TestStandalone(TestBase): + def test_constructor(self): + self.assertIsInstance(vfs.VfsRom(self.romfs), vfs.VfsRom) + self.assertIsInstance(vfs.VfsRom(self.romfs_addr), vfs.VfsRom) + + def test_mount(self): + vfs.VfsRom(self.romfs).mount(True, False) + with self.assertRaises(OSError): + vfs.VfsRom(self.romfs).mount(True, True) + + def test_ilistdir(self): + fs = vfs.VfsRom(self.romfs) + self.assertEqual(list(fs.ilistdir("")), self.romfs_ilistdir) + self.assertEqual(list(fs.ilistdir("/")), self.romfs_ilistdir) + with self.assertRaises(OSError): + fs.ilistdir("does not exist") + + def test_stat(self): + fs = vfs.VfsRom(self.romfs) + self.assertEqual(fs.stat(""), (IFDIR, 0, 0, 0, 0, 0, 289, 0, 0, 0)) + self.assertEqual(fs.stat("/"), (IFDIR, 0, 0, 0, 0, 0, 289, 0, 0, 0)) + self.assertEqual(fs.stat("/test.txt"), (IFREG, 0, 0, 0, 0, 0, 8, 0, 0, 0)) + self.assertEqual(fs.stat("/dir"), (IFDIR, 0, 0, 0, 0, 0, 198, 0, 0, 0)) + with self.assertRaises(OSError): + fs.stat("/does-not-exist") + + def test_statvfs(self): + fs = vfs.VfsRom(self.romfs) + self.assertEqual(fs.statvfs(""), (1, 0, 289, 0, 0, 0, 0, 0, 0, 32767)) + + def test_open(self): + fs = vfs.VfsRom(self.romfs) + + with fs.open("/test.txt", "") as f: + self.assertEqual(f.read(), "contents") + with fs.open("/test.txt", "rt") as f: + self.assertEqual(f.read(), "contents") + with fs.open("/test.txt", "rb") as f: + self.assertEqual(f.read(), b"contents") + + with self.assertRaises(OSError) as ctx: + fs.open("/file-does-not-exist", "") + self.assertEqual(ctx.exception.errno, errno.ENOENT) + + with self.assertRaises(OSError) as ctx: + fs.open("/dir", "rb") + self.assertEqual(ctx.exception.errno, errno.EISDIR) + + with self.assertRaises(OSError): + fs.open("/test.txt", "w") + with self.assertRaises(OSError): + fs.open("/test.txt", "a") + with self.assertRaises(OSError): + fs.open("/test.txt", "+") + + def test_file_seek(self): + fs = vfs.VfsRom(self.romfs) + with fs.open("/test.txt", "") as f: + self.assertEqual(f.seek(0, SEEK_SET), 0) + self.assertEqual(f.seek(3, SEEK_SET), 3) + self.assertEqual(f.read(), "tents") + self.assertEqual(f.seek(0, SEEK_SET), 0) + self.assertEqual(f.seek(100, SEEK_CUR), 8) + self.assertEqual(f.seek(-1, SEEK_END), 7) + self.assertEqual(f.read(), "s") + self.assertEqual(f.seek(1, SEEK_END), 8) + with self.assertRaises(OSError): + f.seek(-1, SEEK_SET) + f.seek(0, SEEK_SET) + with self.assertRaises(OSError): + f.seek(-1, SEEK_CUR) + with self.assertRaises(OSError): + f.seek(-100, SEEK_END) + + @unittest.skipIf(select is None, "no select module") + def test_file_ioctl_invalid(self): + fs = vfs.VfsRom(self.romfs) + with fs.open("/test.txt", "") as f: + p = select.poll() + p.register(f) + with self.assertRaises(OSError): + p.poll(0) + + def test_memory_mapping(self): + fs = vfs.VfsRom(self.romfs) + with fs.open("/test.txt", "rb") as f: + addr = uctypes.addressof(f) + data = memoryview(f) + self.assertIn(addr, self.romfs_addr_range) + self.assertIn(addr + len(data), self.romfs_addr_range) + self.assertEqual(bytes(data), b"contents") + + +class TestMounted(TestBase): + def setUp(self): + self.orig_sys_path = list(sys.path) + self.orig_cwd = os.getcwd() + vfs.mount(vfs.VfsRom(self.romfs), "/test_rom") + + def tearDown(self): + vfs.umount("/test_rom") + os.chdir(self.orig_cwd) + sys.path = self.orig_sys_path + + def test_listdir(self): + self.assertEqual(os.listdir("/test_rom"), self.romfs_listdir) + self.assertEqual(os.listdir("/test_rom/dir"), self.romfs_listdir_dir) + self.assertEqual(os.listdir(b"/test_rom"), self.romfs_listdir_bytes) + + def test_chdir(self): + os.chdir("/test_rom") + self.assertEqual(os.listdir(), self.romfs_listdir) + + os.chdir("/test_rom/") + self.assertEqual(os.listdir(), self.romfs_listdir) + + # chdir within the romfs is not implemented. + with self.assertRaises(OSError): + os.chdir("/test_rom/dir") + + def test_stat(self): + self.assertEqual(os.stat("/test_rom"), (IFDIR, 0, 0, 0, 0, 0, 289, 0, 0, 0)) + self.assertEqual(os.stat("/test_rom/"), (IFDIR, 0, 0, 0, 0, 0, 289, 0, 0, 0)) + self.assertEqual(os.stat("/test_rom/test.txt"), (IFREG, 0, 0, 0, 0, 0, 8, 0, 0, 0)) + self.assertEqual(os.stat("/test_rom/dir"), (IFDIR, 0, 0, 0, 0, 0, 198, 0, 0, 0)) + with self.assertRaises(OSError): + os.stat("/test_rom/does-not-exist") + + def test_open(self): + with open("/test_rom/test.txt") as f: + self.assertEqual(f.read(), "contents") + with open("/test_rom/test.txt", "b") as f: + self.assertEqual(f.read(), b"contents") + + with self.assertRaises(OSError) as ctx: + open("/test_rom/file-does-not-exist") + self.assertEqual(ctx.exception.errno, errno.ENOENT) + + with self.assertRaises(OSError) as ctx: + open("/test_rom/dir") + self.assertEqual(ctx.exception.errno, errno.EISDIR) + + def test_import_py(self): + sys.path.append("/test_rom/dir") + a = __import__("a") + b = __import__("b") + self.assertEqual(a.__file__, "/test_rom/dir/a.py") + self.assertEqual(a.x, 1) + self.assertEqual(b.__file__, "/test_rom/dir/b.py") + self.assertEqual(b.x, 2) + + def test_import_mpy(self): + sys.path.append("/test_rom/dir") + test = __import__("test") + self.assertEqual(test.__file__, "/test_rom/dir/test.mpy") + self.assertEqual(test.str_obj, "this is a str object") + self.assertEqual(test.bytes_obj, b"this is a bytes object") + self.assertEqual(test.int_obj, 1234567890) + self.assertEqual(test.float_obj, 1.23) + self.assertIn(uctypes.addressof(test.str_obj), self.romfs_addr_range) + self.assertIn(uctypes.addressof(test.bytes_obj), self.romfs_addr_range) + + def test_romfs_inner(self): + with open("/test_rom/fs.romfs", "rb") as f: + romfs_inner = vfs.VfsRom(memoryview(f)) + + vfs.mount(romfs_inner, "/test_rom2") + + self.assertEqual(os.listdir("/test_rom2"), ["test_inner.txt", "c.py"]) + + sys.path.append("/test_rom2") + self.assertEqual(__import__("c").__file__, "/test_rom2/c.py") + + with open("/test_rom2/test_inner.txt") as f: + self.assertEqual(f.read(), "contents_inner") + + with open("/test_rom2/test_inner.txt", "rb") as f: + addr = uctypes.addressof(f) + data = memoryview(f) + self.assertIn(addr, self.romfs_addr_range) + self.assertIn(addr + len(data), self.romfs_addr_range) + + vfs.umount("/test_rom2") + + +if __name__ == "__main__": + unittest.main() From a5270c84cfdab6136d0dd4e63d41007f9a37b108 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 20 Aug 2024 07:21:55 +0200 Subject: [PATCH 162/546] tests/inlineasm: Make room for RV32IMC inline asm tests. Thumb/Thumb2 tests are now into their own subdirectory, as RV32IMC-specific tests will be added as part of the RV32 inline assembler support. Signed-off-by: Alessandro Gatti --- ports/qemu/boards/SABRELITE.mk | 2 +- ports/qemu/boards/VIRT_RV32.mk | 2 +- .../{frozen_asm.py => frozen_asm_thumb.py} | 0 tests/inlineasm/{ => thumb}/asmargs.py | 0 tests/inlineasm/{ => thumb}/asmargs.py.exp | 0 tests/inlineasm/{ => thumb}/asmbcc.py | 0 tests/inlineasm/{ => thumb}/asmbcc.py.exp | 0 tests/inlineasm/{ => thumb}/asmbitops.py | 0 tests/inlineasm/{ => thumb}/asmbitops.py.exp | 0 tests/inlineasm/{ => thumb}/asmblbx.py | 0 tests/inlineasm/{ => thumb}/asmblbx.py.exp | 0 tests/inlineasm/{ => thumb}/asmconst.py | 0 tests/inlineasm/{ => thumb}/asmconst.py.exp | 0 tests/inlineasm/{ => thumb}/asmdata.py | 0 tests/inlineasm/{ => thumb}/asmdata.py.exp | 0 tests/inlineasm/{ => thumb}/asmdiv.py | 0 tests/inlineasm/{ => thumb}/asmdiv.py.exp | 0 tests/inlineasm/{ => thumb}/asmfpaddsub.py | 0 .../inlineasm/{ => thumb}/asmfpaddsub.py.exp | 0 tests/inlineasm/{ => thumb}/asmfpcmp.py | 0 tests/inlineasm/{ => thumb}/asmfpcmp.py.exp | 0 tests/inlineasm/{ => thumb}/asmfpldrstr.py | 0 .../inlineasm/{ => thumb}/asmfpldrstr.py.exp | 0 tests/inlineasm/{ => thumb}/asmfpmuldiv.py | 0 .../inlineasm/{ => thumb}/asmfpmuldiv.py.exp | 0 tests/inlineasm/{ => thumb}/asmfpsqrt.py | 0 tests/inlineasm/{ => thumb}/asmfpsqrt.py.exp | 0 tests/inlineasm/{ => thumb}/asmit.py | 0 tests/inlineasm/{ => thumb}/asmit.py.exp | 0 tests/inlineasm/{ => thumb}/asmpushpop.py | 0 tests/inlineasm/{ => thumb}/asmpushpop.py.exp | 0 tests/inlineasm/{ => thumb}/asmrettype.py | 0 tests/inlineasm/{ => thumb}/asmrettype.py.exp | 0 tests/inlineasm/{ => thumb}/asmshift.py | 0 tests/inlineasm/{ => thumb}/asmshift.py.exp | 0 tests/inlineasm/{ => thumb}/asmspecialregs.py | 0 .../{ => thumb}/asmspecialregs.py.exp | 0 tests/inlineasm/{ => thumb}/asmsum.py | 0 tests/inlineasm/{ => thumb}/asmsum.py.exp | 0 tests/ports/qemu/asm_test.py | 2 +- tests/run-tests.py | 42 +++++++++---------- 41 files changed, 24 insertions(+), 24 deletions(-) rename ports/qemu/test-frzmpy/{frozen_asm.py => frozen_asm_thumb.py} (100%) rename tests/inlineasm/{ => thumb}/asmargs.py (100%) rename tests/inlineasm/{ => thumb}/asmargs.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmbcc.py (100%) rename tests/inlineasm/{ => thumb}/asmbcc.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmbitops.py (100%) rename tests/inlineasm/{ => thumb}/asmbitops.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmblbx.py (100%) rename tests/inlineasm/{ => thumb}/asmblbx.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmconst.py (100%) rename tests/inlineasm/{ => thumb}/asmconst.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmdata.py (100%) rename tests/inlineasm/{ => thumb}/asmdata.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmdiv.py (100%) rename tests/inlineasm/{ => thumb}/asmdiv.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmfpaddsub.py (100%) rename tests/inlineasm/{ => thumb}/asmfpaddsub.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmfpcmp.py (100%) rename tests/inlineasm/{ => thumb}/asmfpcmp.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmfpldrstr.py (100%) rename tests/inlineasm/{ => thumb}/asmfpldrstr.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmfpmuldiv.py (100%) rename tests/inlineasm/{ => thumb}/asmfpmuldiv.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmfpsqrt.py (100%) rename tests/inlineasm/{ => thumb}/asmfpsqrt.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmit.py (100%) rename tests/inlineasm/{ => thumb}/asmit.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmpushpop.py (100%) rename tests/inlineasm/{ => thumb}/asmpushpop.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmrettype.py (100%) rename tests/inlineasm/{ => thumb}/asmrettype.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmshift.py (100%) rename tests/inlineasm/{ => thumb}/asmshift.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmspecialregs.py (100%) rename tests/inlineasm/{ => thumb}/asmspecialregs.py.exp (100%) rename tests/inlineasm/{ => thumb}/asmsum.py (100%) rename tests/inlineasm/{ => thumb}/asmsum.py.exp (100%) diff --git a/ports/qemu/boards/SABRELITE.mk b/ports/qemu/boards/SABRELITE.mk index f1945c10fc2aa..839b3d6ac4fcf 100644 --- a/ports/qemu/boards/SABRELITE.mk +++ b/ports/qemu/boards/SABRELITE.mk @@ -16,4 +16,4 @@ SRC_BOARD_O = shared/runtime/gchelper_generic.o MPY_CROSS_FLAGS += -march=armv6 # These tests don't work on Cortex-A9, so exclude them. -RUN_TESTS_ARGS = --exclude '(asmdiv|asmspecialregs).py' +RUN_TESTS_ARGS = --exclude 'inlineasm/thumb/(asmdiv|asmspecialregs).py' diff --git a/ports/qemu/boards/VIRT_RV32.mk b/ports/qemu/boards/VIRT_RV32.mk index 355a09c3d520e..e166165a1994f 100644 --- a/ports/qemu/boards/VIRT_RV32.mk +++ b/ports/qemu/boards/VIRT_RV32.mk @@ -11,6 +11,6 @@ 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/asm_test' +RUN_TESTS_ARGS = --exclude 'inlineasm/thumb|qemu/asm_test' RUN_NATMODTESTS_ARGS = --arch rv32imc diff --git a/ports/qemu/test-frzmpy/frozen_asm.py b/ports/qemu/test-frzmpy/frozen_asm_thumb.py similarity index 100% rename from ports/qemu/test-frzmpy/frozen_asm.py rename to ports/qemu/test-frzmpy/frozen_asm_thumb.py diff --git a/tests/inlineasm/asmargs.py b/tests/inlineasm/thumb/asmargs.py similarity index 100% rename from tests/inlineasm/asmargs.py rename to tests/inlineasm/thumb/asmargs.py diff --git a/tests/inlineasm/asmargs.py.exp b/tests/inlineasm/thumb/asmargs.py.exp similarity index 100% rename from tests/inlineasm/asmargs.py.exp rename to tests/inlineasm/thumb/asmargs.py.exp diff --git a/tests/inlineasm/asmbcc.py b/tests/inlineasm/thumb/asmbcc.py similarity index 100% rename from tests/inlineasm/asmbcc.py rename to tests/inlineasm/thumb/asmbcc.py diff --git a/tests/inlineasm/asmbcc.py.exp b/tests/inlineasm/thumb/asmbcc.py.exp similarity index 100% rename from tests/inlineasm/asmbcc.py.exp rename to tests/inlineasm/thumb/asmbcc.py.exp diff --git a/tests/inlineasm/asmbitops.py b/tests/inlineasm/thumb/asmbitops.py similarity index 100% rename from tests/inlineasm/asmbitops.py rename to tests/inlineasm/thumb/asmbitops.py diff --git a/tests/inlineasm/asmbitops.py.exp b/tests/inlineasm/thumb/asmbitops.py.exp similarity index 100% rename from tests/inlineasm/asmbitops.py.exp rename to tests/inlineasm/thumb/asmbitops.py.exp diff --git a/tests/inlineasm/asmblbx.py b/tests/inlineasm/thumb/asmblbx.py similarity index 100% rename from tests/inlineasm/asmblbx.py rename to tests/inlineasm/thumb/asmblbx.py diff --git a/tests/inlineasm/asmblbx.py.exp b/tests/inlineasm/thumb/asmblbx.py.exp similarity index 100% rename from tests/inlineasm/asmblbx.py.exp rename to tests/inlineasm/thumb/asmblbx.py.exp diff --git a/tests/inlineasm/asmconst.py b/tests/inlineasm/thumb/asmconst.py similarity index 100% rename from tests/inlineasm/asmconst.py rename to tests/inlineasm/thumb/asmconst.py diff --git a/tests/inlineasm/asmconst.py.exp b/tests/inlineasm/thumb/asmconst.py.exp similarity index 100% rename from tests/inlineasm/asmconst.py.exp rename to tests/inlineasm/thumb/asmconst.py.exp diff --git a/tests/inlineasm/asmdata.py b/tests/inlineasm/thumb/asmdata.py similarity index 100% rename from tests/inlineasm/asmdata.py rename to tests/inlineasm/thumb/asmdata.py diff --git a/tests/inlineasm/asmdata.py.exp b/tests/inlineasm/thumb/asmdata.py.exp similarity index 100% rename from tests/inlineasm/asmdata.py.exp rename to tests/inlineasm/thumb/asmdata.py.exp diff --git a/tests/inlineasm/asmdiv.py b/tests/inlineasm/thumb/asmdiv.py similarity index 100% rename from tests/inlineasm/asmdiv.py rename to tests/inlineasm/thumb/asmdiv.py diff --git a/tests/inlineasm/asmdiv.py.exp b/tests/inlineasm/thumb/asmdiv.py.exp similarity index 100% rename from tests/inlineasm/asmdiv.py.exp rename to tests/inlineasm/thumb/asmdiv.py.exp diff --git a/tests/inlineasm/asmfpaddsub.py b/tests/inlineasm/thumb/asmfpaddsub.py similarity index 100% rename from tests/inlineasm/asmfpaddsub.py rename to tests/inlineasm/thumb/asmfpaddsub.py diff --git a/tests/inlineasm/asmfpaddsub.py.exp b/tests/inlineasm/thumb/asmfpaddsub.py.exp similarity index 100% rename from tests/inlineasm/asmfpaddsub.py.exp rename to tests/inlineasm/thumb/asmfpaddsub.py.exp diff --git a/tests/inlineasm/asmfpcmp.py b/tests/inlineasm/thumb/asmfpcmp.py similarity index 100% rename from tests/inlineasm/asmfpcmp.py rename to tests/inlineasm/thumb/asmfpcmp.py diff --git a/tests/inlineasm/asmfpcmp.py.exp b/tests/inlineasm/thumb/asmfpcmp.py.exp similarity index 100% rename from tests/inlineasm/asmfpcmp.py.exp rename to tests/inlineasm/thumb/asmfpcmp.py.exp diff --git a/tests/inlineasm/asmfpldrstr.py b/tests/inlineasm/thumb/asmfpldrstr.py similarity index 100% rename from tests/inlineasm/asmfpldrstr.py rename to tests/inlineasm/thumb/asmfpldrstr.py diff --git a/tests/inlineasm/asmfpldrstr.py.exp b/tests/inlineasm/thumb/asmfpldrstr.py.exp similarity index 100% rename from tests/inlineasm/asmfpldrstr.py.exp rename to tests/inlineasm/thumb/asmfpldrstr.py.exp diff --git a/tests/inlineasm/asmfpmuldiv.py b/tests/inlineasm/thumb/asmfpmuldiv.py similarity index 100% rename from tests/inlineasm/asmfpmuldiv.py rename to tests/inlineasm/thumb/asmfpmuldiv.py diff --git a/tests/inlineasm/asmfpmuldiv.py.exp b/tests/inlineasm/thumb/asmfpmuldiv.py.exp similarity index 100% rename from tests/inlineasm/asmfpmuldiv.py.exp rename to tests/inlineasm/thumb/asmfpmuldiv.py.exp diff --git a/tests/inlineasm/asmfpsqrt.py b/tests/inlineasm/thumb/asmfpsqrt.py similarity index 100% rename from tests/inlineasm/asmfpsqrt.py rename to tests/inlineasm/thumb/asmfpsqrt.py diff --git a/tests/inlineasm/asmfpsqrt.py.exp b/tests/inlineasm/thumb/asmfpsqrt.py.exp similarity index 100% rename from tests/inlineasm/asmfpsqrt.py.exp rename to tests/inlineasm/thumb/asmfpsqrt.py.exp diff --git a/tests/inlineasm/asmit.py b/tests/inlineasm/thumb/asmit.py similarity index 100% rename from tests/inlineasm/asmit.py rename to tests/inlineasm/thumb/asmit.py diff --git a/tests/inlineasm/asmit.py.exp b/tests/inlineasm/thumb/asmit.py.exp similarity index 100% rename from tests/inlineasm/asmit.py.exp rename to tests/inlineasm/thumb/asmit.py.exp diff --git a/tests/inlineasm/asmpushpop.py b/tests/inlineasm/thumb/asmpushpop.py similarity index 100% rename from tests/inlineasm/asmpushpop.py rename to tests/inlineasm/thumb/asmpushpop.py diff --git a/tests/inlineasm/asmpushpop.py.exp b/tests/inlineasm/thumb/asmpushpop.py.exp similarity index 100% rename from tests/inlineasm/asmpushpop.py.exp rename to tests/inlineasm/thumb/asmpushpop.py.exp diff --git a/tests/inlineasm/asmrettype.py b/tests/inlineasm/thumb/asmrettype.py similarity index 100% rename from tests/inlineasm/asmrettype.py rename to tests/inlineasm/thumb/asmrettype.py diff --git a/tests/inlineasm/asmrettype.py.exp b/tests/inlineasm/thumb/asmrettype.py.exp similarity index 100% rename from tests/inlineasm/asmrettype.py.exp rename to tests/inlineasm/thumb/asmrettype.py.exp diff --git a/tests/inlineasm/asmshift.py b/tests/inlineasm/thumb/asmshift.py similarity index 100% rename from tests/inlineasm/asmshift.py rename to tests/inlineasm/thumb/asmshift.py diff --git a/tests/inlineasm/asmshift.py.exp b/tests/inlineasm/thumb/asmshift.py.exp similarity index 100% rename from tests/inlineasm/asmshift.py.exp rename to tests/inlineasm/thumb/asmshift.py.exp diff --git a/tests/inlineasm/asmspecialregs.py b/tests/inlineasm/thumb/asmspecialregs.py similarity index 100% rename from tests/inlineasm/asmspecialregs.py rename to tests/inlineasm/thumb/asmspecialregs.py diff --git a/tests/inlineasm/asmspecialregs.py.exp b/tests/inlineasm/thumb/asmspecialregs.py.exp similarity index 100% rename from tests/inlineasm/asmspecialregs.py.exp rename to tests/inlineasm/thumb/asmspecialregs.py.exp diff --git a/tests/inlineasm/asmsum.py b/tests/inlineasm/thumb/asmsum.py similarity index 100% rename from tests/inlineasm/asmsum.py rename to tests/inlineasm/thumb/asmsum.py diff --git a/tests/inlineasm/asmsum.py.exp b/tests/inlineasm/thumb/asmsum.py.exp similarity index 100% rename from tests/inlineasm/asmsum.py.exp rename to tests/inlineasm/thumb/asmsum.py.exp diff --git a/tests/ports/qemu/asm_test.py b/tests/ports/qemu/asm_test.py index 26c7efe4273d5..57238c62945b8 100644 --- a/tests/ports/qemu/asm_test.py +++ b/tests/ports/qemu/asm_test.py @@ -1,4 +1,4 @@ -import frozen_asm +import frozen_asm_thumb as frozen_asm print(frozen_asm.asm_add(1, 2)) print(frozen_asm.asm_add1(3)) diff --git a/tests/run-tests.py b/tests/run-tests.py index a609a1fcbbebd..f00510f2da2d7 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -138,11 +138,11 @@ def open(self, path, mode): ), "qemu": ( # Skip tests that require Cortex-M4. - "inlineasm/asmfpaddsub.py", - "inlineasm/asmfpcmp.py", - "inlineasm/asmfpldrstr.py", - "inlineasm/asmfpmuldiv.py", - "inlineasm/asmfpsqrt.py", + "inlineasm/thumb/asmfpaddsub.py", + "inlineasm/thumb/asmfpcmp.py", + "inlineasm/thumb/asmfpldrstr.py", + "inlineasm/thumb/asmfpmuldiv.py", + "inlineasm/thumb/asmfpsqrt.py", ), "webassembly": ( "basics/string_format_modulo.py", # can't print nulls to stdout @@ -664,17 +664,17 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # Check if @micropython.asm_thumb supports Thumb2 instructions, and skip such tests if it doesn't output = run_feature_check(pyb, args, "inlineasm_thumb2.py") if output != b"thumb2\n": - skip_tests.add("inlineasm/asmbcc.py") - skip_tests.add("inlineasm/asmbitops.py") - skip_tests.add("inlineasm/asmconst.py") - skip_tests.add("inlineasm/asmdiv.py") - skip_tests.add("inlineasm/asmfpaddsub.py") - skip_tests.add("inlineasm/asmfpcmp.py") - skip_tests.add("inlineasm/asmfpldrstr.py") - skip_tests.add("inlineasm/asmfpmuldiv.py") - skip_tests.add("inlineasm/asmfpsqrt.py") - skip_tests.add("inlineasm/asmit.py") - skip_tests.add("inlineasm/asmspecialregs.py") + skip_tests.add("inlineasm/thumb/asmbcc.py") + skip_tests.add("inlineasm/thumb/asmbitops.py") + skip_tests.add("inlineasm/thumb/asmconst.py") + skip_tests.add("inlineasm/thumb/asmdiv.py") + skip_tests.add("inlineasm/thumb/asmfpaddsub.py") + skip_tests.add("inlineasm/thumb/asmfpcmp.py") + skip_tests.add("inlineasm/thumb/asmfpldrstr.py") + skip_tests.add("inlineasm/thumb/asmfpmuldiv.py") + skip_tests.add("inlineasm/thumb/asmfpsqrt.py") + skip_tests.add("inlineasm/thumb/asmit.py") + skip_tests.add("inlineasm/thumb/asmspecialregs.py") # Check if emacs repl is supported, and skip such tests if it's not t = run_feature_check(pyb, args, "repl_emacs_check.py") @@ -1215,15 +1215,15 @@ def main(): ) if args.platform == "pyboard": # run pyboard tests - test_dirs += ("float", "stress", "inlineasm", "ports/stm32") + test_dirs += ("float", "stress", "inlineasm/thumb", "ports/stm32") elif args.platform == "mimxrt": - test_dirs += ("float", "stress", "inlineasm") + test_dirs += ("float", "stress", "inlineasm/thumb") elif args.platform == "renesas-ra": - test_dirs += ("float", "inlineasm", "ports/renesas-ra") + test_dirs += ("float", "inlineasm/thumb", "ports/renesas-ra") elif args.platform == "rp2": test_dirs += ("float", "stress", "thread", "ports/rp2") if "arm" in args.mpy_cross_flags: - test_dirs += ("inlineasm",) + test_dirs += ("inlineasm/thumb",) elif args.platform == "esp32": test_dirs += ("float", "stress", "thread") elif args.platform in ("esp8266", "minimal", "samd", "nrf"): @@ -1245,7 +1245,7 @@ def main(): elif args.platform == "qemu": test_dirs += ( "float", - "inlineasm", + "inlineasm/thumb", "ports/qemu", ) elif args.platform == "webassembly": From 3044233ea3726e9d8727d8f6a76f32c48e6fae5e Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 22 Aug 2024 00:48:55 +0200 Subject: [PATCH 163/546] py/misc: Add a popcount(uint32_t) implementation. This makes the existing popcount(uint32_t) implementation found in the RV32 emitter available to the rest of the codebase. This version of popcount will use intrinsic or builtin implementations if they are available, falling back to a generic implementation if that is not the case. Signed-off-by: Alessandro Gatti --- py/asmrv32.c | 24 ++---------------------- py/misc.h | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/py/asmrv32.c b/py/asmrv32.c index 3f3395842efb9..6aa3ec16541f0 100644 --- a/py/asmrv32.c +++ b/py/asmrv32.c @@ -29,6 +29,7 @@ #include #include "py/emit.h" +#include "py/misc.h" #include "py/mpconfig.h" // wrapper around everything in this file @@ -43,27 +44,6 @@ #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_S0 #define AVAILABLE_REGISTERS_COUNT 32 @@ -249,7 +229,7 @@ static void adjust_stack(asm_rv32_t *state, mp_int_t stack_size) { // 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); + 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. diff --git a/py/misc.h b/py/misc.h index 2629d0c46cc53..e05fbe61a9d6d 100644 --- a/py/misc.h +++ b/py/misc.h @@ -370,12 +370,29 @@ static inline uint32_t mp_ctz(uint32_t x) { static inline bool mp_check(bool value) { return value; } + +static inline uint32_t mp_popcount(uint32_t x) { + return __popcnt(x); +} #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) +#if defined __has_builtin +#if __has_builtin(__builtin_popcount) +#define mp_popcount(x) __builtin_popcount(x) +#endif +#endif +#if !defined(mp_popcount) +static inline uint32_t mp_popcount(uint32_t x) { + x = x - ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + return x * 0x01010101; +} +#endif #endif // mp_int_t can be larger than long, i.e. Windows 64-bit, nan-box variants From 268acb714dd79fa5eeeb82c1fca022bc4ea126b7 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sun, 25 Aug 2024 16:28:35 +0200 Subject: [PATCH 164/546] py/emitinlinerv32: Add inline assembler support for RV32. This commit adds support for writing inline assembler functions when targeting a RV32IMC processor. Given that this takes up a bit of rodata space due to its large instruction decoding table and its extensive error messages, it is enabled by default only on offline targets such as mpy-cross and the qemu port. Signed-off-by: Alessandro Gatti --- mpy-cross/mpconfigport.h | 1 + ports/qemu/Makefile | 4 +- ports/qemu/boards/MICROBIT.mk | 3 + ports/qemu/boards/MPS2_AN385.mk | 3 + ports/qemu/boards/NETDUINO2.mk | 3 + ports/qemu/boards/SABRELITE.mk | 2 +- ports/qemu/boards/VIRT_RV32.mk | 2 +- ports/qemu/mpconfigport.h | 1 + ports/qemu/test-frzmpy/frozen_asm_rv32.py | 31 + py/asmrv32.c | 34 +- py/asmrv32.h | 302 +++++++- py/compile.c | 6 + py/emit.h | 3 + py/emitinlinerv32.c | 818 ++++++++++++++++++++++ py/mpconfig.h | 7 +- py/py.cmake | 1 + py/py.mk | 1 + tests/inlineasm/rv32/asmargs.py | 44 ++ tests/inlineasm/rv32/asmargs.py.exp | 5 + tests/inlineasm/rv32/asmarith.py | 79 +++ tests/inlineasm/rv32/asmarith.py.exp | 14 + tests/inlineasm/rv32/asmbranch.py | 161 +++++ tests/inlineasm/rv32/asmbranch.py.exp | 32 + tests/inlineasm/rv32/asmconst.py | 49 ++ tests/inlineasm/rv32/asmconst.py.exp | 5 + tests/inlineasm/rv32/asmcsr.py | 65 ++ tests/inlineasm/rv32/asmcsr.py.exp | 2 + tests/inlineasm/rv32/asmdata.py | 33 + tests/inlineasm/rv32/asmdata.py.exp | 10 + tests/inlineasm/rv32/asmdivmul.py | 63 ++ tests/inlineasm/rv32/asmdivmul.py.exp | 18 + tests/inlineasm/rv32/asmjump.py | 115 +++ tests/inlineasm/rv32/asmjump.py.exp | 6 + tests/inlineasm/rv32/asmloadstore.py | 86 +++ tests/inlineasm/rv32/asmloadstore.py.exp | 3 + tests/inlineasm/rv32/asmrettype.py | 33 + tests/inlineasm/rv32/asmrettype.py.exp | 4 + tests/inlineasm/rv32/asmsanity.py | 204 ++++++ tests/inlineasm/rv32/asmsanity.py.exp | 162 +++++ tests/inlineasm/rv32/asmshift.py | 121 ++++ tests/inlineasm/rv32/asmshift.py.exp | 15 + tests/inlineasm/rv32/asmstack.py | 65 ++ tests/inlineasm/rv32/asmstack.py.exp | 3 + tests/inlineasm/rv32/asmsum.py | 59 ++ tests/inlineasm/rv32/asmsum.py.exp | 3 + tests/ports/qemu/asm_test.py | 8 +- tests/run-tests.py | 5 +- 47 files changed, 2649 insertions(+), 45 deletions(-) create mode 100644 ports/qemu/test-frzmpy/frozen_asm_rv32.py create mode 100644 py/emitinlinerv32.c create mode 100644 tests/inlineasm/rv32/asmargs.py create mode 100644 tests/inlineasm/rv32/asmargs.py.exp create mode 100644 tests/inlineasm/rv32/asmarith.py create mode 100644 tests/inlineasm/rv32/asmarith.py.exp create mode 100644 tests/inlineasm/rv32/asmbranch.py create mode 100644 tests/inlineasm/rv32/asmbranch.py.exp create mode 100644 tests/inlineasm/rv32/asmconst.py create mode 100644 tests/inlineasm/rv32/asmconst.py.exp create mode 100644 tests/inlineasm/rv32/asmcsr.py create mode 100644 tests/inlineasm/rv32/asmcsr.py.exp create mode 100644 tests/inlineasm/rv32/asmdata.py create mode 100644 tests/inlineasm/rv32/asmdata.py.exp create mode 100644 tests/inlineasm/rv32/asmdivmul.py create mode 100644 tests/inlineasm/rv32/asmdivmul.py.exp create mode 100644 tests/inlineasm/rv32/asmjump.py create mode 100644 tests/inlineasm/rv32/asmjump.py.exp create mode 100644 tests/inlineasm/rv32/asmloadstore.py create mode 100644 tests/inlineasm/rv32/asmloadstore.py.exp create mode 100644 tests/inlineasm/rv32/asmrettype.py create mode 100644 tests/inlineasm/rv32/asmrettype.py.exp create mode 100644 tests/inlineasm/rv32/asmsanity.py create mode 100644 tests/inlineasm/rv32/asmsanity.py.exp create mode 100644 tests/inlineasm/rv32/asmshift.py create mode 100644 tests/inlineasm/rv32/asmshift.py.exp create mode 100644 tests/inlineasm/rv32/asmstack.py create mode 100644 tests/inlineasm/rv32/asmstack.py.exp create mode 100644 tests/inlineasm/rv32/asmsum.py create mode 100644 tests/inlineasm/rv32/asmsum.py.exp diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index 8723472eb710c..d3805f0307356 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -47,6 +47,7 @@ #define MICROPY_EMIT_INLINE_XTENSA (1) #define MICROPY_EMIT_XTENSAWIN (1) #define MICROPY_EMIT_RV32 (1) +#define MICROPY_EMIT_INLINE_RV32 (1) #define MICROPY_EMIT_NATIVE_DEBUG (1) #define MICROPY_EMIT_NATIVE_DEBUG_PRINTER (&mp_stdout_print) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index b85ff2896a16e..ea0bef814866d 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -19,10 +19,10 @@ QSTR_DEFS = qstrdefsport.h MICROPY_ROM_TEXT_COMPRESSION ?= 1 ifeq ($(QEMU_ARCH),arm) -FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy')" +FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_asm_thumb.py', 'frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" endif ifeq ($(QEMU_ARCH),riscv32) -FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" +FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_asm_rv32.py', 'frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" endif # include py core make definitions diff --git a/ports/qemu/boards/MICROBIT.mk b/ports/qemu/boards/MICROBIT.mk index 02eb0576c3438..d6cfd7e2243d8 100644 --- a/ports/qemu/boards/MICROBIT.mk +++ b/ports/qemu/boards/MICROBIT.mk @@ -11,3 +11,6 @@ LDSCRIPT = mcu/arm/nrf51.ld SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb1.o MPY_CROSS_FLAGS += -march=armv7m + +# These RV32 tests don't run on Thumb, so exclude them. +RUN_TESTS_ARGS = --exclude 'inlineasm/rv32' diff --git a/ports/qemu/boards/MPS2_AN385.mk b/ports/qemu/boards/MPS2_AN385.mk index 182d076eb35d4..8d5c27df57c23 100644 --- a/ports/qemu/boards/MPS2_AN385.mk +++ b/ports/qemu/boards/MPS2_AN385.mk @@ -10,3 +10,6 @@ LDSCRIPT = mcu/arm/mps2.ld SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o MPY_CROSS_FLAGS += -march=armv7m + +# These RV32 tests don't run on Thumb, so exclude them. +RUN_TESTS_ARGS = --exclude 'inlineasm/rv32' diff --git a/ports/qemu/boards/NETDUINO2.mk b/ports/qemu/boards/NETDUINO2.mk index ffa781f339977..f88ea32e7e58f 100644 --- a/ports/qemu/boards/NETDUINO2.mk +++ b/ports/qemu/boards/NETDUINO2.mk @@ -10,3 +10,6 @@ LDSCRIPT = mcu/arm/stm32.ld SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o MPY_CROSS_FLAGS += -march=armv7m + +# These RV32 tests don't run on Thumb, so exclude them. +RUN_TESTS_ARGS = --exclude 'inlineasm/rv32' diff --git a/ports/qemu/boards/SABRELITE.mk b/ports/qemu/boards/SABRELITE.mk index 839b3d6ac4fcf..d3f4e14d48022 100644 --- a/ports/qemu/boards/SABRELITE.mk +++ b/ports/qemu/boards/SABRELITE.mk @@ -16,4 +16,4 @@ SRC_BOARD_O = shared/runtime/gchelper_generic.o MPY_CROSS_FLAGS += -march=armv6 # These tests don't work on Cortex-A9, so exclude them. -RUN_TESTS_ARGS = --exclude 'inlineasm/thumb/(asmdiv|asmspecialregs).py' +RUN_TESTS_ARGS = --exclude 'inlineasm/rv32|inlineasm/thumb/(asmdiv|asmspecialregs).py' diff --git a/ports/qemu/boards/VIRT_RV32.mk b/ports/qemu/boards/VIRT_RV32.mk index e166165a1994f..80dd5d56fdc58 100644 --- a/ports/qemu/boards/VIRT_RV32.mk +++ b/ports/qemu/boards/VIRT_RV32.mk @@ -11,6 +11,6 @@ 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/thumb|qemu/asm_test' +RUN_TESTS_ARGS = --exclude 'inlineasm/thumb' RUN_NATMODTESTS_ARGS = --arch rv32imc diff --git a/ports/qemu/mpconfigport.h b/ports/qemu/mpconfigport.h index d3e3c0a3f37ac..8f55d284f93da 100644 --- a/ports/qemu/mpconfigport.h +++ b/ports/qemu/mpconfigport.h @@ -39,6 +39,7 @@ #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) #elif defined(__riscv) #define MICROPY_EMIT_RV32 (1) +#define MICROPY_EMIT_INLINE_RV32 (1) #endif #define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) diff --git a/ports/qemu/test-frzmpy/frozen_asm_rv32.py b/ports/qemu/test-frzmpy/frozen_asm_rv32.py new file mode 100644 index 0000000000000..ce1bd428c0376 --- /dev/null +++ b/ports/qemu/test-frzmpy/frozen_asm_rv32.py @@ -0,0 +1,31 @@ +# Test freezing inline-asm code. + +# ruff: noqa: F821 - @asm_rv32 decorator adds names to function scope + +import micropython + + +@micropython.asm_rv32 +def asm_add(a0, a1): + add(a0, a0, a1) + + +@micropython.asm_rv32 +def asm_add1(a0) -> object: + slli(a0, a0, 1) + addi(a0, a0, 3) + + +@micropython.asm_rv32 +def asm_cast_bool(a0) -> bool: + pass + + +@micropython.asm_rv32 +def asm_shift_int(a0) -> int: + slli(a0, a0, 29) + + +@micropython.asm_rv32 +def asm_shift_uint(a0) -> uint: + slli(a0, a0, 29) diff --git a/py/asmrv32.c b/py/asmrv32.c index 6aa3ec16541f0..c24d05a1384d4 100644 --- a/py/asmrv32.c +++ b/py/asmrv32.c @@ -45,12 +45,6 @@ #endif #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) \ @@ -106,7 +100,6 @@ static void split_immediate(mp_int_t immediate, mp_uint_t *upper, mp_uint_t *low // Turn the lower half from unsigned to signed. if ((*lower & 0x800) != 0) { *upper += 0x1000; - *lower -= 0x1000; } } @@ -180,7 +173,7 @@ void asm_rv32_emit_optimised_load_immediate(asm_rv32_t *state, mp_uint_t rd, mp_ 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++) { + for (mp_uint_t register_index = 0; register_index < RV32_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 @@ -192,7 +185,7 @@ static void emit_registers_store(asm_rv32_t *state, mp_uint_t registers_mask) { 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++) { + for (mp_uint_t register_index = 0; register_index < RV32_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 @@ -262,7 +255,7 @@ static bool calculate_displacement_for_label(asm_rv32_t *state, mp_uint_t label, 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 << INTERNAL_TEMPORARY); + (1U << REG_LOCAL_2) | (1U << REG_LOCAL_3); state->locals_count = locals; emit_function_prologue(state, state->saved_registers_mask); } @@ -281,10 +274,11 @@ 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_UNSIGNED(offset, 6)) { + if (RV32_IS_IN_C_REGISTER_WINDOW(REG_FUN_TABLE) && RV32_IS_IN_C_REGISTER_WINDOW(INTERNAL_TEMPORARY) && FIT_UNSIGNED(offset, 6)) { + state->saved_registers_mask |= (1U << INTERNAL_TEMPORARY); // 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_clw(state, RV32_MAP_IN_C_REGISTER_WINDOW(INTERNAL_TEMPORARY), RV32_MAP_IN_C_REGISTER_WINDOW(REG_FUN_TABLE), offset); asm_rv32_opcode_cjalr(state, INTERNAL_TEMPORARY); return; } @@ -341,9 +335,9 @@ void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_ ptrdiff_t displacement = 0; bool can_emit_short_jump = calculate_displacement_for_label(state, label, &displacement); - if (can_emit_short_jump && FIT_SIGNED(displacement, 8) && IS_IN_C_REGISTER_WINDOW(rs)) { + if (can_emit_short_jump && FIT_SIGNED(displacement, 8) && RV32_IS_IN_C_REGISTER_WINDOW(rs)) { // c.bnez rs', displacement - asm_rv32_opcode_cbnez(state, MAP_IN_C_REGISTER_WINDOW(rs), displacement); + asm_rv32_opcode_cbnez(state, RV32_MAP_IN_C_REGISTER_WINDOW(rs), displacement); return; } @@ -364,8 +358,8 @@ void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_ // jalr zero, temporary, LO(displacement) ; PC + 8 // ... ; PC + 12 - if (can_emit_short_jump && IS_IN_C_REGISTER_WINDOW(rs)) { - asm_rv32_opcode_cbeqz(state, MAP_IN_C_REGISTER_WINDOW(rs), 10); + if (can_emit_short_jump && RV32_IS_IN_C_REGISTER_WINDOW(rs)) { + asm_rv32_opcode_cbeqz(state, RV32_MAP_IN_C_REGISTER_WINDOW(rs), 10); // Compensate for the C.BEQZ opcode. displacement -= ASM_HALFWORD_SIZE; } else { @@ -438,9 +432,9 @@ 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_UNSIGNED(offset, 10) && offset != 0 && IS_IN_C_REGISTER_WINDOW(rd)) { + if (FIT_UNSIGNED(offset, 10) && offset != 0 && RV32_IS_IN_C_REGISTER_WINDOW(rd)) { // c.addi4spn rd', offset - asm_rv32_opcode_caddi4spn(state, MAP_IN_C_REGISTER_WINDOW(rd), offset); + asm_rv32_opcode_caddi4spn(state, RV32_MAP_IN_C_REGISTER_WINDOW(rd), offset); return; } @@ -459,9 +453,9 @@ 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 (scaled_offset >= 0 && IS_IN_C_REGISTER_WINDOW(rd) && IS_IN_C_REGISTER_WINDOW(rs) && FIT_UNSIGNED(scaled_offset, 6)) { + if (scaled_offset >= 0 && RV32_IS_IN_C_REGISTER_WINDOW(rd) && RV32_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); + asm_rv32_opcode_clw(state, RV32_MAP_IN_C_REGISTER_WINDOW(rd), RV32_MAP_IN_C_REGISTER_WINDOW(rs), scaled_offset); return; } diff --git a/py/asmrv32.h b/py/asmrv32.h index 775cf1ffc9630..584e3ffd22436 100644 --- a/py/asmrv32.h +++ b/py/asmrv32.h @@ -74,9 +74,6 @@ #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) @@ -85,6 +82,9 @@ #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_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_T3 (ASM_RV32_REG_X28) #define ASM_RV32_REG_T4 (ASM_RV32_REG_X29) #define ASM_RV32_REG_T5 (ASM_RV32_REG_X30) @@ -103,6 +103,12 @@ #define ASM_RV32_REG_S10 (ASM_RV32_REG_X26) #define ASM_RV32_REG_S11 (ASM_RV32_REG_X27) +#define RV32_AVAILABLE_REGISTERS_COUNT 32 +#define RV32_MAP_IN_C_REGISTER_WINDOW(register_number) \ + ((register_number) - ASM_RV32_REG_X8) +#define RV32_IS_IN_C_REGISTER_WINDOW(register_number) \ + (((register_number) >= ASM_RV32_REG_X8) && ((register_number) <= ASM_RV32_REG_X15)) + typedef struct _asm_rv32_t { // Opaque emitter state. mp_asm_base_t base; @@ -127,6 +133,10 @@ void asm_rv32_end_pass(asm_rv32_t *state); ((imm & 0x1E) << 7) | ((rs1 & 0x1F) << 15) | ((rs2 & 0x1F) << 20) | \ ((imm & 0x7E0) << 20) | ((imm & 0x1000) << 19)) +#define RV32_ENCODE_TYPE_CSRI(op, ft3, rd, csr, imm) \ + ((op & 0x7F) | ((rd & 0x1F) << 7) | ((ft3 & 0x07) << 12) | \ + ((csr & 0xFFF) << 20) | ((imm & 0x1F) << 15)) + #define RV32_ENCODE_TYPE_I(op, ft3, rd, rs, imm) \ ((op & 0x7F) | ((rd & 0x1F) << 7) | ((ft3 & 0x07) << 12) | \ ((rs & 0x1F) << 15) | ((imm & 0xFFF) << 20)) @@ -143,13 +153,16 @@ void asm_rv32_end_pass(asm_rv32_t *state); ((op & 0x7F) | ((imm & 0x1F) << 7) | ((ft3 & 0x07) << 12) | \ ((rs1 & 0x1F) << 15) | ((rs2 & 0x1F) << 20) | ((imm & 0xFE0) << 20)) +#define RV32_ENCODE_TYPE_CA(op, ft6, ft2, rd, rs) \ + ((op & 0x03) | ((ft6 & 0x3F) << 10) | ((ft2 & 0x03) << 5) | \ + ((rd & 0x03) << 7) | ((rs & 0x03) << 2)) + #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 & 0x03) | ((ft3 & 0x07) << 13) | ((rs & 0x07) << 7) | \ - (((imm) & 0x100) << 4) | (((imm) & 0xC0) >> 1) | (((imm) & 0x20) >> 3) | \ - (((imm) & 0x18) << 7) | (((imm) & 0x06) << 2)) + (((imm) & 0xE0) << 5) | (((imm) & 0x1F) << 2)) #define RV32_ENCODE_TYPE_CI(op, ft3, rd, imm) \ ((op & 0x03) | ((ft3 & 0x07) << 13) | ((rd & 0x1F) << 7) | \ @@ -174,6 +187,11 @@ void asm_rv32_end_pass(asm_rv32_t *state); #define RV32_ENCODE_TYPE_CR(op, ft4, rs1, rs2) \ ((op & 0x03) | ((rs2 & 0x1F) << 2) | ((rs1 & 0x1F) << 7) | ((ft4 & 0x0F) << 12)) +#define RV32_ENCODE_TYPE_CS(op, ft3, rd, rs, imm) \ + ((op & 0x03) | ((ft3 & 0x07) << 13) | ((rd & 0x07) << 2) | \ + ((rs & 0x07) << 7) | ((imm & 0x40) >> 1) | ((imm & 0x38) << 7) | \ + ((imm & 0x04) << 4)) + #define RV32_ENCODE_TYPE_CSS(op, ft3, rs, imm) \ ((op & 0x03) | ((ft3 & 0x07) << 13) | ((rs & 0x1F) << 2) | ((imm) & 0x3F) << 7) @@ -198,6 +216,12 @@ static inline void asm_rv32_opcode_and(asm_rv32_t *state, mp_uint_t rd, mp_uint_ asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x07, 0x00, rd, rs1, rs2)); } +// ANDI RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_andi(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { + // I: ............ ..... 111 ..... 0010011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x13, 0x07, rd, rs, immediate)); +} + // AUIPC RD, offset static inline void asm_rv32_opcode_auipc(asm_rv32_t *state, mp_uint_t rd, mp_int_t offset) { // U: .................... ..... 0010111 @@ -210,6 +234,30 @@ static inline void asm_rv32_opcode_beq(asm_rv32_t *state, mp_uint_t rs1, mp_uint asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_B(0x63, 0x00, rs1, rs2, offset)); } +// BGE RS1, RS2, OFFSET +static inline void asm_rv32_opcode_bge(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_int_t offset) { + // B: . ...... ..... ..... 101 .... . 1100011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_B(0x63, 0x05, rs1, rs2, offset)); +} + +// BGEU RS1, RS2, OFFSET +static inline void asm_rv32_opcode_bgeu(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_int_t offset) { + // B: . ...... ..... ..... 111 .... . 1100011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_B(0x63, 0x07, rs1, rs2, offset)); +} + +// BLT RS1, RS2, OFFSET +static inline void asm_rv32_opcode_blt(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_int_t offset) { + // B: . ...... ..... ..... 100 .... . 1100011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_B(0x63, 0x04, rs1, rs2, offset)); +} + +// BLTU RS1, RS2, OFFSET +static inline void asm_rv32_opcode_bltu(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_int_t offset) { + // B: . ...... ..... ..... 110 .... . 1100011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_B(0x63, 0x06, 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 @@ -234,16 +282,39 @@ static inline void asm_rv32_opcode_caddi4spn(asm_rv32_t *state, mp_uint_t rd, mp asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CIW(0x00, 0x00, rd, immediate)); } +// C.AND RD', RS' +static inline void asm_rv32_opcode_cand(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) { + // CA: 100011 ... 11 ... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CA(0x01, 0x23, 0x03, rd, rs)); +} + +// C.ANDI RD', IMMEDIATE +static inline void asm_rv32_opcode_candi(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { + // CB: 100 . 10 ... ..... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CB(0x01, 0x04, rd, + (((immediate & 0x20) << 2) | (immediate & 0x1F) | 0x40))); +} + // 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(0x01, 0x06, rs, offset)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CB(0x01, 0x06, rs, + (((offset & 0x100) >> 1) | ((offset & 0xC0) >> 3) | ((offset & 0x20) >> 5) | + ((offset & 0x18) << 2) | (offset & 0x06)))); } // 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(0x01, 0x07, rs, offset)); + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CB(0x01, 0x07, rs, + (((offset & 0x100) >> 1) | ((offset & 0xC0) >> 3) | ((offset & 0x20) >> 5) | + ((offset & 0x18) << 2) | (offset & 0x06)))); +} + +// C.EBREAK +static inline void asm_rv32_opcode_cebreak(asm_rv32_t *state) { + // CA: 100 1 00000 00000 10 + asm_rv32_emit_halfword_opcode(state, 0x9002); } // C.J OFFSET @@ -252,6 +323,12 @@ static inline void asm_rv32_opcode_cj(asm_rv32_t *state, mp_int_t offset) { asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CJ(0x01, 0x05, offset)); } +// C.JAL OFFSET +static inline void asm_rv32_opcode_cjal(asm_rv32_t *state, mp_int_t offset) { + // CJ: 001 ........... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CJ(0x01, 0x01, offset)); +} + // C.JALR RS static inline void asm_rv32_opcode_cjalr(asm_rv32_t *state, mp_uint_t rs) { // CR: 1001 ..... 00000 10 @@ -294,31 +371,159 @@ static inline void asm_rv32_opcode_cmv(asm_rv32_t *state, mp_uint_t rd, mp_uint_ asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CR(0x02, 0x08, rd, rs)); } +// C.NOP +static inline void asm_rv32_opcode_cnop(asm_rv32_t *state) { + // CI: 000 . 00000 ..... 01 + asm_rv32_emit_halfword_opcode(state, 0x0001); +} + +// C.OR RD', RS' +static inline void asm_rv32_opcode_cor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) { + // CA: 100011 ... 10 ... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CA(0x01, 0x23, 0x02, rd, rs)); +} + +// C.SLLI RD, IMMEDIATE +static inline void asm_rv32_opcode_cslli(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { + // CI: 000 . ..... ..... 10 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CI(0x02, 0x00, rd, immediate)); +} + +// C.SRAI RD, IMMEDIATE +static inline void asm_rv32_opcode_csrai(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { + // CB: 100 . 01 ... ..... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CB(0x01, 0x04, rd, + (((immediate & 0x20) << 2) | (immediate & 0x1F) | 0x20))); +} + +// C.SRLI RD, IMMEDIATE +static inline void asm_rv32_opcode_csrli(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate) { + // CB: 100 . 00 ... ..... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CB(0x01, 0x04, rd, + (((immediate & 0x20) << 2) | (immediate & 0x1F)))); +} + +// C.SUB RD', RS' +static inline void asm_rv32_opcode_csub(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) { + // CA: 100011 ... 00 ... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CA(0x01, 0x23, 0x00, rd, rs)); +} + +// C.SW RS1', OFFSET(RS2') +static inline void asm_rv32_opcode_csw(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_int_t offset) { + // CS: 110 ... ... .. ... 00 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CL(0x00, 0x06, rs1, rs2, offset)); +} + // 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(0x02, 0x06, rs, ((offset & 0xC0) >> 6) | (offset & 0x3C))); } -// JALR RD, RS, offset +// C.XOR RD', RS' +static inline void asm_rv32_opcode_cxor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) { + // CA: 100011 ... 01 ... 01 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CA(0x01, 0x23, 0x01, rd, rs)); +} + +// CSRRC RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_csrrc(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { + // I: ............ ..... 011 ..... 1110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x73, 0x03, rd, rs, immediate)); +} + +// CSRRS RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_csrrs(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { + // I: ............ ..... 010 ..... 1110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x73, 0x02, rd, rs, immediate)); +} + +// CSRRW RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_csrrw(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { + // I: ............ ..... 001 ..... 1110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x73, 0x01, rd, rs, immediate)); +} + +// CSRRCI RD, CSR, IMMEDIATE +static inline void asm_rv32_opcode_csrrci(asm_rv32_t *state, mp_uint_t rd, mp_uint_t csr, mp_int_t immediate) { + // CSRI: ............ ..... 111 ..... 1110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_CSRI(0x73, 0x07, rd, csr, immediate)); +} + +// CSRRSI RD, CSR, IMMEDIATE +static inline void asm_rv32_opcode_csrrsi(asm_rv32_t *state, mp_uint_t rd, mp_uint_t csr, mp_int_t immediate) { + // CSRI: ............ ..... 110 ..... 1110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_CSRI(0x73, 0x06, rd, csr, immediate)); +} + +// CSRRWI RD, CSR, IMMEDIATE +static inline void asm_rv32_opcode_csrrwi(asm_rv32_t *state, mp_uint_t rd, mp_uint_t csr, mp_int_t immediate) { + // CSRI: ............ ..... 101 ..... 1110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_CSRI(0x73, 0x05, rd, csr, immediate)); +} + +// DIV RD, RS1, RS2 +static inline void asm_rv32_opcode_div(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000001 ..... ..... 100 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x04, 0x01, rd, rs1, rs2)); +} + +// DIVU RD, RS1, RS2 +static inline void asm_rv32_opcode_divu(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000001 ..... ..... 101 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x05, 0x01, rd, rs1, rs2)); +} + +// EBREAK +static inline void asm_rv32_opcode_ebreak(asm_rv32_t *state) { + // I: 000000000001 00000 000 00000 1110011 + asm_rv32_emit_word_opcode(state, 0x100073); +} + +// ECALL +static inline void asm_rv32_opcode_ecall(asm_rv32_t *state) { + // I: 000000000000 00000 000 00000 1110011 + asm_rv32_emit_word_opcode(state, 0x73); +} + +// JAL RD, OFFSET +static inline void asm_rv32_opcode_jal(asm_rv32_t *state, mp_uint_t rd, mp_int_t offset) { + // J: ......................... 1101111 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_J(0x6F, rd, offset)); +} + +// 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(0x67, 0x00, rd, rs, offset)); } +// LB RD, OFFSET(RS) +static inline void asm_rv32_opcode_lb(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { + // I: ............ ..... 000 ..... 0000011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x03, 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(0x03, 0x04, rd, rs, offset)); } +// LH RD, OFFSET(RS) +static inline void asm_rv32_opcode_lh(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { + // I: ............ ..... 001 ..... 0000011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x03, 0x01, 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(0x03, 0x05, rd, rs, offset)); } -// LUI RD, immediate +// 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(0x37, rd, immediate)); @@ -336,12 +541,48 @@ static inline void asm_rv32_opcode_mul(asm_rv32_t *state, mp_uint_t rd, mp_uint_ asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x00, 0x01, rd, rs1, rs2)); } +// MULH RD, RS1, RS2 +static inline void asm_rv32_opcode_mulh(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000001 ..... ..... 001 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x01, 0x01, rd, rs1, rs2)); +} + +// MULHSU RD, RS1, RS2 +static inline void asm_rv32_opcode_mulhsu(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000001 ..... ..... 010 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x02, 0x01, rd, rs1, rs2)); +} + +// MULHU RD, RS1, RS2 +static inline void asm_rv32_opcode_mulhu(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000001 ..... ..... 011 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x03, 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(0x33, 0x06, 0x00, rd, rs1, rs2)); } +// ORI RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_ori(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { + // I: ............ ..... 110 ..... 0010011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x13, 0x06, rd, rs, immediate)); +} + +// REM RD, RS1, RS2 +static inline void asm_rv32_opcode_rem(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000001 ..... ..... 110 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x06, 0x01, rd, rs1, rs2)); +} + +// REMU RD, RS1, RS2 +static inline void asm_rv32_opcode_remu(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0000001 ..... ..... 111 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x07, 0x01, 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 @@ -349,23 +590,29 @@ static inline void asm_rv32_opcode_sll(asm_rv32_t *state, mp_uint_t rd, mp_uint_ } // 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) { +static inline void asm_rv32_opcode_slli(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { // I: 0000000..... ..... 001 ..... 0010011 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(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(0x33, 0x02, 0x00, rd, rs1, rs2)); } +// SLTI RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_slti(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { + // I: ............ ..... 010 ..... 0010011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x13, 0x02, rd, rs, immediate)); +} + +// SLTIU RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_sltiu(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { + // I: ............ ..... 011 ..... 0010011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x13, 0x03, rd, rs, immediate)); +} + // 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 @@ -378,6 +625,24 @@ static inline void asm_rv32_opcode_sra(asm_rv32_t *state, mp_uint_t rd, mp_uint_ asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x05, 0x20, rd, rs1, rs2)); } +// SRAI RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_srai(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { + // I: 0100000..... ..... 101 ..... 0010011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x13, 0x05, rd, rs, ((immediate & 0x1F) | 0x400))); +} + +// 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(0x33, 0x05, 0x00, rd, rs1, rs2)); +} + +// SRLI RD, RS, IMMEDIATE +static inline void asm_rv32_opcode_srli(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t immediate) { + // I: 0000000..... ..... 101 ..... 0010011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x13, 0x05, rd, rs, immediate & 0x1F)); +} + // 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 @@ -435,6 +700,8 @@ void asm_rv32_meta_comparison_ne(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2 void asm_rv32_meta_comparison_lt(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd, bool unsigned_comparison); 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); +void asm_rv32_emit_optimised_load_immediate(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate); + #ifdef GENERIC_ASM_API void asm_rv32_emit_call_ind(asm_rv32_t *state, mp_uint_t index); @@ -444,10 +711,9 @@ void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_ 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_local(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); diff --git a/py/compile.c b/py/compile.c index d2af6aaf2cb6b..60f06d7773e34 100644 --- a/py/compile.c +++ b/py/compile.c @@ -147,6 +147,7 @@ static const emit_inline_asm_method_table_t *emit_asm_table[] = { &emit_inline_thumb_method_table, &emit_inline_xtensa_method_table, NULL, + &emit_inline_rv32_method_table, }; #elif MICROPY_EMIT_INLINE_ASM @@ -157,6 +158,9 @@ static const emit_inline_asm_method_table_t *emit_asm_table[] = { #elif MICROPY_EMIT_INLINE_XTENSA #define ASM_DECORATOR_QSTR MP_QSTR_asm_xtensa #define ASM_EMITTER(f) emit_inline_xtensa_##f +#elif MICROPY_EMIT_INLINE_RV32 +#define ASM_DECORATOR_QSTR MP_QSTR_asm_rv32 +#define ASM_EMITTER(f) emit_inline_rv32_##f #else #error "unknown asm emitter" #endif @@ -855,6 +859,8 @@ static bool compile_built_in_decorator(compiler_t *comp, size_t name_len, mp_par *emit_options = MP_EMIT_OPT_ASM; } else if (attr == MP_QSTR_asm_xtensa) { *emit_options = MP_EMIT_OPT_ASM; + } else if (attr == MP_QSTR_asm_rv32) { + *emit_options = MP_EMIT_OPT_ASM; #else } else if (attr == ASM_DECORATOR_QSTR) { *emit_options = MP_EMIT_OPT_ASM; diff --git a/py/emit.h b/py/emit.h index 623b163490164..033ac9c763b07 100644 --- a/py/emit.h +++ b/py/emit.h @@ -307,12 +307,15 @@ typedef struct _emit_inline_asm_method_table_t { void (*op)(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args); } emit_inline_asm_method_table_t; +extern const emit_inline_asm_method_table_t emit_inline_rv32_method_table; extern const emit_inline_asm_method_table_t emit_inline_thumb_method_table; extern const emit_inline_asm_method_table_t emit_inline_xtensa_method_table; +emit_inline_asm_t *emit_inline_rv32_new(mp_uint_t max_num_labels); emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels); emit_inline_asm_t *emit_inline_xtensa_new(mp_uint_t max_num_labels); +void emit_inline_rv32_free(emit_inline_asm_t *emit); void emit_inline_thumb_free(emit_inline_asm_t *emit); void emit_inline_xtensa_free(emit_inline_asm_t *emit); diff --git a/py/emitinlinerv32.c b/py/emitinlinerv32.c new file mode 100644 index 0000000000000..179e289c67b05 --- /dev/null +++ b/py/emitinlinerv32.c @@ -0,0 +1,818 @@ +/* + * 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 +#include + +#include "py/asmrv32.h" +#include "py/emit.h" +#include "py/misc.h" + +#if MICROPY_EMIT_INLINE_RV32 + +typedef enum { +// define rules with a compile function +#define DEF_RULE(rule, comp, kind, ...) PN_##rule, +#define DEF_RULE_NC(rule, kind, ...) + #include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + PN_const_object, // special node for a constant, generic Python object +// define rules without a compile function +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) PN_##rule, + #include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +} pn_kind_t; + +struct _emit_inline_asm_t { + asm_rv32_t as; + uint16_t pass; + mp_obj_t *error_slot; + mp_uint_t max_num_labels; + qstr *label_lookup; +}; + +static const qstr REGISTERS_QSTR_TABLE[] = { + MP_QSTR_zero, MP_QSTR_ra, MP_QSTR_sp, MP_QSTR_gp, MP_QSTR_tp, MP_QSTR_t0, MP_QSTR_t1, MP_QSTR_t2, + MP_QSTR_s0, MP_QSTR_s1, MP_QSTR_a0, MP_QSTR_a1, MP_QSTR_a2, MP_QSTR_a3, MP_QSTR_a4, MP_QSTR_a5, + MP_QSTR_a6, MP_QSTR_a7, MP_QSTR_s2, MP_QSTR_s3, MP_QSTR_s4, MP_QSTR_s5, MP_QSTR_s6, MP_QSTR_s7, + MP_QSTR_s8, MP_QSTR_s9, MP_QSTR_s10, MP_QSTR_s11, MP_QSTR_t3, MP_QSTR_t4, MP_QSTR_t5, MP_QSTR_t6, + MP_QSTR_x0, MP_QSTR_x1, MP_QSTR_x2, MP_QSTR_x3, MP_QSTR_x4, MP_QSTR_x5, MP_QSTR_x6, MP_QSTR_x7, + MP_QSTR_x8, MP_QSTR_x9, MP_QSTR_x10, MP_QSTR_x11, MP_QSTR_x12, MP_QSTR_x13, MP_QSTR_x14, MP_QSTR_x15, + MP_QSTR_x16, MP_QSTR_x17, MP_QSTR_x18, MP_QSTR_x19, MP_QSTR_x20, MP_QSTR_x21, MP_QSTR_x22, MP_QSTR_x23, + MP_QSTR_x24, MP_QSTR_x25, MP_QSTR_x26, MP_QSTR_x27, MP_QSTR_x28, MP_QSTR_x29, MP_QSTR_x30, MP_QSTR_x31, +}; + +//////////////////////////////////////////////////////////////////////////////// + +static inline void emit_inline_rv32_error_msg(emit_inline_asm_t *emit, mp_rom_error_text_t msg) { + *emit->error_slot = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg); +} + +static inline void emit_inline_rv32_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) { + *emit->error_slot = exc; +} + +emit_inline_asm_t *emit_inline_rv32_new(mp_uint_t max_num_labels) { + emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t); + memset(&emit->as, 0, sizeof(emit->as)); + mp_asm_base_init(&emit->as.base, max_num_labels); + emit->max_num_labels = max_num_labels; + emit->label_lookup = m_new(qstr, max_num_labels); + return emit; +} + +void emit_inline_rv32_free(emit_inline_asm_t *emit) { + m_del(qstr, emit->label_lookup, emit->max_num_labels); + mp_asm_base_deinit(&emit->as.base, false); + m_del_obj(emit_inline_asm_t, emit); +} + +static void emit_inline_rv32_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot) { + emit->pass = pass; + emit->error_slot = error_slot; + if (emit->pass == MP_PASS_CODE_SIZE) { + memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr)); + } + mp_asm_base_start_pass(&emit->as.base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); +} + +static void emit_inline_rv32_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) { + // c.jr ra + asm_rv32_opcode_cjr(&emit->as, ASM_RV32_REG_RA); + asm_rv32_end_pass(&emit->as); +} + +static bool parse_register_node(mp_parse_node_t node, mp_uint_t *register_number, bool compressed) { + assert(register_number != NULL && "Register number pointer is NULL."); + + if (!MP_PARSE_NODE_IS_ID(node)) { + return false; + } + + qstr node_qstr = MP_PARSE_NODE_LEAF_ARG(node); + for (mp_uint_t index = 0; index < MP_ARRAY_SIZE(REGISTERS_QSTR_TABLE); index++) { + if (node_qstr == REGISTERS_QSTR_TABLE[index]) { + mp_uint_t number = index % RV32_AVAILABLE_REGISTERS_COUNT; + if (!compressed || (compressed && RV32_IS_IN_C_REGISTER_WINDOW(number))) { + *register_number = compressed ? RV32_MAP_IN_C_REGISTER_WINDOW(number) : number; + return true; + } + break; + } + } + + return false; +} + +static mp_uint_t lookup_label(emit_inline_asm_t *emit, mp_parse_node_t node, qstr *qstring) { + assert(qstring && "qstring pointer is NULL"); + + *qstring = MP_PARSE_NODE_LEAF_ARG(node); + for (mp_uint_t label = 0; label < emit->max_num_labels; label++) { + if (emit->label_lookup[label] == *qstring) { + return label; + } + } + + return emit->max_num_labels; +} + +static inline ptrdiff_t label_code_offset(emit_inline_asm_t *emit, mp_uint_t label_index) { + return emit->as.base.label_offsets[label_index] - emit->as.base.code_offset; +} + +static mp_uint_t emit_inline_rv32_count_params(emit_inline_asm_t *emit, mp_uint_t parameters_count, mp_parse_node_t *parameter_nodes) { + // TODO: Raise this up to 8? RV32I has 8 A-registers that are meant to + // be used for passing arguments. + + if (parameters_count > 4) { + emit_inline_rv32_error_msg(emit, MP_ERROR_TEXT("can only have up to 4 parameters for RV32 assembly")); + return 0; + } + + mp_uint_t register_index = 0; + for (mp_uint_t index = 0; index < parameters_count; index++) { + bool valid_register = parse_register_node(parameter_nodes[index], ®ister_index, false); + if (!valid_register || (register_index != (ASM_RV32_REG_A0 + index))) { + emit_inline_rv32_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence a0 to a3")); + return 0; + } + } + + return parameters_count; +} + +static bool emit_inline_rv32_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) { + assert(label_num < emit->max_num_labels); + if (emit->pass == MP_PASS_CODE_SIZE) { + for (mp_uint_t index = 0; index < emit->max_num_labels; index++) { + if (emit->label_lookup[index] == label_id) { + return false; + } + } + } + emit->label_lookup[label_num] = label_id; + mp_asm_base_label_assign(&emit->as.base, label_num); + return true; +} + +#define CALL_RRR 0 // Register, Register, Register +#define CALL_RR 1 // Register, Register +#define CALL_RRI 2 // Register, Register, Immediate +#define CALL_RRL 3 // Register, Register, Label +#define CALL_RI 4 // Register, Immediate +#define CALL_L 5 // Label +#define CALL_R 6 // Register +#define CALL_RL 7 // Register, Label +#define CALL_N 8 // No arguments +#define CALL_I 9 // Immediate +#define CALL_RII 10 // Register, Immediate, Immediate +#define CALL_RIR 11 // Register, Immediate(Register) + +#define N 0 // No argument +#define R 1 // Register +#define I 2 // Immediate +#define L 3 // Label +#define C (1 << 2) // Compressed register +#define U (1 << 2) // Unsigned immediate +#define Z (1 << 3) // Non-zero + +typedef void (*call_l_t)(asm_rv32_t *state, mp_uint_t label_index); +typedef void (*call_ri_t)(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate); +typedef void (*call_rri_t)(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_int_t immediate); +typedef void (*call_rii_t)(asm_rv32_t *state, mp_uint_t rd, mp_uint_t immediate1, mp_int_t immediate2); +typedef void (*call_rrr_t)(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2); +typedef void (*call_rr_t)(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs); +typedef void (*call_i_t)(asm_rv32_t *state, mp_int_t immediate); +typedef void (*call_r_t)(asm_rv32_t *state, mp_uint_t rd); +typedef void (*call_n_t)(asm_rv32_t *state); + +typedef struct _opcode_t { + qstr qstring; + void *emitter; + uint32_t calling_convention : 4; + uint32_t argument1_kind : 4; + uint32_t argument1_shift : 5; + uint32_t argument2_kind : 4; + uint32_t argument2_shift : 5; + uint32_t argument3_kind : 4; + uint32_t argument3_shift : 5; + uint32_t argument1_mask; + uint32_t argument2_mask; + uint32_t argument3_mask; +} opcode_t; + +#define opcode_li asm_rv32_emit_optimised_load_immediate + +static void opcode_la(asm_rv32_t *state, mp_uint_t rd, mp_int_t displacement) { + // This cannot be optimised for size, otherwise label addresses would move around. + mp_uint_t upper = (mp_uint_t)displacement & 0xFFFFF000; + mp_uint_t lower = (mp_uint_t)displacement & 0x00000FFF; + if ((lower & 0x800) != 0) { + upper += 0x1000; + } + asm_rv32_opcode_auipc(state, rd, upper); + asm_rv32_opcode_addi(state, rd, rd, lower); +} + +#define RC (R | C) +#define IU (I | U) +#define IZ (I | Z) +#define IUZ (I | U | Z) + +static const opcode_t OPCODES[] = { + { MP_QSTR_add, asm_rv32_opcode_add, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_addi, asm_rv32_opcode_addi, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, + { MP_QSTR_and_, asm_rv32_opcode_and, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_andi, asm_rv32_opcode_andi, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, + { MP_QSTR_auipc, asm_rv32_opcode_auipc, CALL_RI, R, 0, I, 12, N, 0, 0xFFFFFFFF, 0xFFFFF000, 0x00000000 }, + { MP_QSTR_beq, asm_rv32_opcode_beq, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, + { MP_QSTR_bge, asm_rv32_opcode_bge, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, + { MP_QSTR_bgeu, asm_rv32_opcode_bgeu, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, + { MP_QSTR_blt, asm_rv32_opcode_blt, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, + { MP_QSTR_bltu, asm_rv32_opcode_bltu, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, + { MP_QSTR_bne, asm_rv32_opcode_bne, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, + { MP_QSTR_csrrc, asm_rv32_opcode_csrrc, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, + { MP_QSTR_csrrs, asm_rv32_opcode_csrrs, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, + { MP_QSTR_csrrw, asm_rv32_opcode_csrrw, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, + { MP_QSTR_csrrci, asm_rv32_opcode_csrrci, CALL_RII, R, 0, IU, 0, IU, 0, 0xFFFFFFFF, 0x00000FFF, 0x0000001F }, + { MP_QSTR_csrrsi, asm_rv32_opcode_csrrsi, CALL_RII, R, 0, IU, 0, IU, 0, 0xFFFFFFFF, 0x00000FFF, 0x0000001F }, + { MP_QSTR_csrrwi, asm_rv32_opcode_csrrwi, CALL_RII, R, 0, IU, 0, IU, 0, 0xFFFFFFFF, 0x00000FFF, 0x0000001F }, + { MP_QSTR_c_add, asm_rv32_opcode_cadd, CALL_RR, R, 0, R, 0, N, 0, 0xFFFFFFFE, 0xFFFFFFFE, 0x00000000 }, + { MP_QSTR_c_addi, asm_rv32_opcode_caddi, CALL_RI, R, 0, IZ, 0, N, 0, 0xFFFFFFFE, 0x0000003F, 0x00000000 }, + { MP_QSTR_c_addi4spn, asm_rv32_opcode_caddi4spn, CALL_RI, R, 0, IUZ, 0, N, 0, 0x0000FF00, 0x000003FC, 0x00000000 }, + { MP_QSTR_c_and, asm_rv32_opcode_cand, CALL_RR, RC, 0, RC, 0, N, 0, 0x0000FF00, 0x0000FF00, 0x00000000 }, + { MP_QSTR_c_andi, asm_rv32_opcode_candi, CALL_RI, RC, 0, I, 0, N, 0, 0x0000FF00, 0x0000003F, 0x00000000 }, + { MP_QSTR_c_beqz, asm_rv32_opcode_cbeqz, CALL_RL, RC, 0, L, 0, N, 0, 0x0000FF00, 0x000001FE, 0x00000000 }, + { MP_QSTR_c_bnez, asm_rv32_opcode_cbnez, CALL_RL, RC, 0, L, 0, N, 0, 0x0000FF00, 0x000001FE, 0x00000000 }, + { MP_QSTR_c_ebreak, asm_rv32_opcode_cebreak, CALL_N, N, 0, N, 0, N, 0, 0x00000000, 0x00000000, 0x00000000 }, + { MP_QSTR_c_j, asm_rv32_opcode_cj, CALL_L, L, 0, N, 0, N, 0, 0x00000FFE, 0x00000000, 0x00000000 }, + { MP_QSTR_c_jal, asm_rv32_opcode_cjal, CALL_L, L, 0, N, 0, N, 0, 0x00000FFE, 0x00000000, 0x00000000 }, + { MP_QSTR_c_jalr, asm_rv32_opcode_cjalr, CALL_R, R, 0, N, 0, N, 0, 0xFFFFFFFE, 0x00000000, 0x00000000 }, + { MP_QSTR_c_jr, asm_rv32_opcode_cjr, CALL_R, R, 0, N, 0, N, 0, 0xFFFFFFFE, 0x00000000, 0x00000000 }, + { MP_QSTR_c_li, asm_rv32_opcode_cli, CALL_RI, R, 0, I, 0, N, 0, 0xFFFFFFFE, 0x0000003F, 0x00000000 }, + { MP_QSTR_c_lui, asm_rv32_opcode_clui, CALL_RI, R, 0, IUZ, 12, N, 0, 0xFFFFFFFA, 0x0001F800, 0x00000000 }, + { MP_QSTR_c_lw, asm_rv32_opcode_clw, CALL_RIR, RC, 0, I, 0, RC, 0, 0x0000FF00, 0x0000007C, 0x0000FF00 }, + { MP_QSTR_c_lwsp, asm_rv32_opcode_clwsp, CALL_RI, R, 0, I, 0, N, 0, 0xFFFFFFFE, 0x000000FC, 0x00000000 }, + { MP_QSTR_c_mv, asm_rv32_opcode_cmv, CALL_RR, R, 0, R, 0, N, 0, 0xFFFFFFFE, 0xFFFFFFFE, 0x00000000 }, + { MP_QSTR_c_nop, asm_rv32_opcode_cnop, CALL_N, N, 0, N, 0, N, 0, 0x00000000, 0x00000000, 0x00000000 }, + { MP_QSTR_c_or, asm_rv32_opcode_cor, CALL_RR, RC, 0, RC, 0, N, 0, 0x0000FF00, 0x0000FF00, 0x00000000 }, + { MP_QSTR_c_slli, asm_rv32_opcode_cslli, CALL_RI, R, 0, IU, 0, N, 0, 0xFFFFFFFE, 0x0000001F, 0x00000000 }, + { MP_QSTR_c_srai, asm_rv32_opcode_csrai, CALL_RI, RC, 0, IU, 0, N, 0, 0x0000FF00, 0x0000001F, 0x00000000 }, + { MP_QSTR_c_srli, asm_rv32_opcode_csrli, CALL_RI, RC, 0, IU, 0, N, 0, 0x0000FF00, 0x0000001F, 0x00000000 }, + { MP_QSTR_c_sub, asm_rv32_opcode_csub, CALL_RR, RC, 0, RC, 0, N, 0, 0x0000FF00, 0x0000FF00, 0x00000000 }, + { MP_QSTR_c_sw, asm_rv32_opcode_csw, CALL_RIR, RC, 0, I, 0, RC, 0, 0x0000FF00, 0x0000007C, 0x0000FF00 }, + { MP_QSTR_c_swsp, asm_rv32_opcode_cswsp, CALL_RI, R, 0, I, 0, N, 0, 0xFFFFFFFF, 0x000000FC, 0x00000000 }, + { MP_QSTR_c_xor, asm_rv32_opcode_cxor, CALL_RR, RC, 0, RC, 0, N, 0, 0x0000FF00, 0x0000FF00, 0x00000000 }, + { MP_QSTR_div, asm_rv32_opcode_div, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_divu, asm_rv32_opcode_divu, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_ebreak, asm_rv32_opcode_ebreak, CALL_N, N, 0, N, 0, N, 0, 0x00000000, 0x00000000, 0x00000000 }, + { MP_QSTR_ecall, asm_rv32_opcode_ecall, CALL_N, N, 0, N, 0, N, 0, 0x00000000, 0x00000000, 0x00000000 }, + { MP_QSTR_jal, asm_rv32_opcode_jal, CALL_RL, R, 0, L, 0, N, 0, 0xFFFFFFFF, 0x001FFFFE, 0x00000000 }, + { MP_QSTR_jalr, asm_rv32_opcode_jalr, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, + { MP_QSTR_la, opcode_la, CALL_RL, R, 0, L, 0, N, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 }, + { MP_QSTR_lb, asm_rv32_opcode_lb, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, + { MP_QSTR_lbu, asm_rv32_opcode_lbu, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, + { MP_QSTR_lh, asm_rv32_opcode_lh, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, + { MP_QSTR_lhu, asm_rv32_opcode_lhu, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, + { MP_QSTR_li, opcode_li, CALL_RI, R, 0, I, 0, N, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 }, + { MP_QSTR_lui, asm_rv32_opcode_lui, CALL_RI, R, 0, I, 12, N, 0, 0xFFFFFFFF, 0xFFFFF000, 0x00000000 }, + { MP_QSTR_lw, asm_rv32_opcode_lw, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, + { MP_QSTR_mv, asm_rv32_opcode_cmv, CALL_RR, R, 0, R, 0, N, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 }, + { MP_QSTR_mul, asm_rv32_opcode_mul, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_mulh, asm_rv32_opcode_mulh, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_mulhsu, asm_rv32_opcode_mulhsu, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_mulhu, asm_rv32_opcode_mulhu, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_or_, asm_rv32_opcode_or, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_ori, asm_rv32_opcode_ori, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, + { MP_QSTR_rem, asm_rv32_opcode_rem, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_remu, asm_rv32_opcode_remu, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_sb, asm_rv32_opcode_sb, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, + { MP_QSTR_sh, asm_rv32_opcode_sh, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, + { MP_QSTR_sll, asm_rv32_opcode_sll, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_slli, asm_rv32_opcode_slli, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000001F }, + { MP_QSTR_slt, asm_rv32_opcode_slt, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_slti, asm_rv32_opcode_slti, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, + { MP_QSTR_sltiu, asm_rv32_opcode_sltiu, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, + { MP_QSTR_sltu, asm_rv32_opcode_sltu, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_sra, asm_rv32_opcode_sra, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_srai, asm_rv32_opcode_srai, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000001F }, + { MP_QSTR_srl, asm_rv32_opcode_srl, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_srli, asm_rv32_opcode_srli, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000001F }, + { MP_QSTR_sub, asm_rv32_opcode_sub, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_sw, asm_rv32_opcode_sw, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, + { MP_QSTR_xor, asm_rv32_opcode_xor, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { MP_QSTR_xori, asm_rv32_opcode_xori, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, +}; + +#undef RC +#undef IU +#undef IZ +#undef IUZ + +// These two checks assume the bitmasks are contiguous. + +static bool is_in_signed_mask(mp_uint_t mask, mp_uint_t value) { + mp_uint_t leading_zeroes = mp_clz(mask); + if (leading_zeroes == 0 || leading_zeroes > 32) { + return true; + } + mp_uint_t positive_mask = ~(mask & ~(1U << (31 - leading_zeroes))); + if ((value & positive_mask) == 0) { + return true; + } + mp_uint_t negative_mask = ~(mask >> 1); + mp_uint_t trailing_zeroes = mp_ctz(mask); + if (trailing_zeroes > 0) { + mp_uint_t trailing_mask = (1U << trailing_zeroes) - 1; + if ((value & trailing_mask) != 0) { + return false; + } + negative_mask &= ~trailing_mask; + } + return (value & negative_mask) == negative_mask; +} + +static inline bool is_in_unsigned_mask(mp_uint_t mask, mp_uint_t value) { + return (value & ~mask) == 0; +} + +static bool validate_integer(mp_uint_t value, mp_uint_t mask, mp_uint_t flags) { + if (flags & U) { + if (!is_in_unsigned_mask(mask, value)) { + return false; + } + } else { + if (!is_in_signed_mask(mask, value)) { + return false; + } + } + + if ((flags & Z) && (value == 0)) { + return false; + } + + return true; +} + +static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, + const opcode_t *opcode, mp_parse_node_t node, mp_uint_t node_index) { + assert((node_index < 3) && "Invalid argument node number."); + + uint32_t kind = 0; + uint32_t shift = 0; + uint32_t mask = 0; + + switch (node_index) { + case 0: + kind = opcode->argument1_kind; + shift = opcode->argument1_shift; + mask = opcode->argument1_mask; + break; + + case 1: + kind = opcode->argument2_kind; + shift = opcode->argument2_shift; + mask = opcode->argument2_mask; + break; + + case 2: + kind = opcode->argument3_kind; + shift = opcode->argument3_shift; + mask = opcode->argument3_mask; + break; + + default: + break; + } + + switch (kind & 0x03) { + case N: + return true; + + case R: { + mp_uint_t register_index; + if (!parse_register_node(node, ®ister_index, false)) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("opcode '%q' argument %d must be a register"), + opcode_qstr, node_index + 1)); + return false; + } + + if ((mask & (1U << register_index)) == 0) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("opcode '%q' argument %d is an invalid register"), + opcode_qstr, node_index + 1)); + return false; + } + + return true; + } + break; + + case I: { + mp_obj_t object; + if (!mp_parse_node_get_int_maybe(node, &object)) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("opcode '%q' argument %d must be an integer"), + opcode_qstr, node_index + 1)); + return false; + } + + mp_uint_t immediate = mp_obj_get_int_truncated(object) << shift; + if (kind & U) { + if (!is_in_unsigned_mask(mask, immediate)) { + goto out_of_range; + } + } else { + if (!is_in_signed_mask(mask, immediate)) { + goto out_of_range; + } + } + + if ((kind & Z) && (immediate == 0)) { + goto zero_immediate; + } + + return true; + } + break; + + case L: { + if (!MP_PARSE_NODE_IS_ID(node)) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("opcode '%q' argument %d must be a label"), + opcode_qstr, node_index + 1)); + return false; + } + + qstr qstring; + mp_uint_t label_index = lookup_label(emit, node, &qstring); + if (label_index >= emit->max_num_labels && emit->pass == MP_PASS_EMIT) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("opcode '%q' argument %d label '%q' is undefined"), + opcode_qstr, node_index + 1, qstring)); + return false; + } + + mp_uint_t displacement = (mp_uint_t)(label_code_offset(emit, label_index)); + if (kind & U) { + if (!is_in_unsigned_mask(mask, displacement)) { + goto out_of_range; + } + } else { + if (!is_in_signed_mask(mask, displacement)) { + goto out_of_range; + } + } + return true; + } + break; + + default: + assert(!"Unknown argument kind"); + break; + } + + return false; + +out_of_range: + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("opcode '%q' argument %d is out of range"), + opcode_qstr, node_index + 1)); + return false; + +zero_immediate: + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("opcode '%q' argument %d must not be zero"), + opcode_qstr, node_index + 1)); + return false; +} + +static bool parse_register_offset_node(emit_inline_asm_t *emit, qstr opcode_qstr, const opcode_t *opcode_data, mp_parse_node_t node, mp_uint_t node_index, mp_parse_node_t *register_node, mp_parse_node_t *offset_node, bool *negative) { + assert(register_node != NULL && "Register node pointer is NULL."); + assert(offset_node != NULL && "Offset node pointer is NULL."); + assert(negative != NULL && "Negative pointer is NULL."); + + if (!MP_PARSE_NODE_IS_STRUCT_KIND(node, PN_atom_expr_normal) && !MP_PARSE_NODE_IS_STRUCT_KIND(node, PN_factor_2)) { + goto invalid_structure; + } + mp_parse_node_struct_t *node_struct = (mp_parse_node_struct_t *)node; + *negative = false; + if (MP_PARSE_NODE_IS_STRUCT_KIND(node, PN_factor_2)) { + if (MP_PARSE_NODE_IS_TOKEN_KIND(node_struct->nodes[0], MP_TOKEN_OP_MINUS)) { + *negative = true; + } else { + if (!MP_PARSE_NODE_IS_TOKEN_KIND(node_struct->nodes[0], MP_TOKEN_OP_PLUS)) { + goto invalid_structure; + } + } + if (!MP_PARSE_NODE_IS_STRUCT_KIND(node_struct->nodes[1], PN_atom_expr_normal)) { + goto invalid_structure; + } + node_struct = (mp_parse_node_struct_t *)node_struct->nodes[1]; + } + + if (*negative) { + // If the value is negative, RULE_atom_expr_normal's first token will be the + // offset stripped of its negative marker; range check will then fail if the + // default method is used, so a custom check is used instead. + mp_obj_t object; + if (!mp_parse_node_get_int_maybe(node_struct->nodes[0], &object)) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("opcode '%q' argument %d must be an integer"), + opcode_qstr, 2)); + return false; + } + mp_uint_t value = mp_obj_get_int_truncated(object); + value = (~value + 1) & (mp_uint_t)-1; + if (!validate_integer(value << opcode_data->argument2_shift, opcode_data->argument2_mask, opcode_data->argument2_kind)) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("opcode '%q' argument %d is out of range"), + opcode_qstr, 2)); + return false; + } + } else { + if (!validate_argument(emit, opcode_qstr, opcode_data, node_struct->nodes[0], 1)) { + return false; + } + } + + *offset_node = node_struct->nodes[0]; + node_struct = (mp_parse_node_struct_t *)node_struct->nodes[1]; + if (!validate_argument(emit, opcode_qstr, opcode_data, node_struct->nodes[0], 2)) { + return false; + } + *register_node = node_struct->nodes[0]; + return true; + +invalid_structure: + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("opcode '%q' argument %d must be an integer offset to a register"), + opcode_qstr, node_index + 1)); + return false; +} + +static void handle_opcode(emit_inline_asm_t *emit, qstr opcode, const opcode_t *opcode_data, mp_parse_node_t *arguments) { + mp_uint_t rd = 0; + mp_uint_t rs1 = 0; + mp_uint_t rs2 = 0; + + switch (opcode_data->calling_convention) { + case CALL_RRR: { + parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); + parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); + parse_register_node(arguments[2], &rs2, opcode_data->argument3_kind & C); + ((call_rrr_t)opcode_data->emitter)(&emit->as, rd, rs1, rs2); + break; + } + + case CALL_RR: { + parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); + parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); + ((call_rr_t)opcode_data->emitter)(&emit->as, rd, rs1); + break; + } + + case CALL_RRI: { + parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); + parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); + mp_obj_t object; + mp_parse_node_get_int_maybe(arguments[2], &object); + mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument3_shift; + ((call_rri_t)opcode_data->emitter)(&emit->as, rd, rs1, immediate); + break; + } + + case CALL_RI: { + parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); + mp_obj_t object; + mp_parse_node_get_int_maybe(arguments[1], &object); + mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument2_shift; + ((call_ri_t)opcode_data->emitter)(&emit->as, rd, immediate); + break; + } + + case CALL_R: { + parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); + ((call_r_t)opcode_data->emitter)(&emit->as, rd); + break; + } + + case CALL_RRL: { + parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); + parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); + qstr qstring; + mp_uint_t label_index = lookup_label(emit, arguments[2], &qstring); + ptrdiff_t displacement = label_code_offset(emit, label_index); + ((call_rri_t)opcode_data->emitter)(&emit->as, rd, rs1, displacement); + break; + } + + case CALL_RL: { + parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); + qstr qstring; + mp_uint_t label_index = lookup_label(emit, arguments[1], &qstring); + ptrdiff_t displacement = label_code_offset(emit, label_index); + ((call_ri_t)opcode_data->emitter)(&emit->as, rd, displacement); + break; + } + + case CALL_L: { + qstr qstring; + mp_uint_t label_index = lookup_label(emit, arguments[0], &qstring); + ptrdiff_t displacement = label_code_offset(emit, label_index); + ((call_i_t)opcode_data->emitter)(&emit->as, displacement); + break; + } + + case CALL_N: + ((call_n_t)opcode_data->emitter)(&emit->as); + break; + + case CALL_I: { + mp_obj_t object; + mp_parse_node_get_int_maybe(arguments[0], &object); + mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument1_shift; + ((call_i_t)opcode_data->emitter)(&emit->as, immediate); + break; + } + + case CALL_RII: { + parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); + mp_obj_t object; + mp_parse_node_get_int_maybe(arguments[1], &object); + mp_uint_t immediate1 = mp_obj_get_int_truncated(object) << opcode_data->argument2_shift; + mp_parse_node_get_int_maybe(arguments[2], &object); + mp_uint_t immediate2 = mp_obj_get_int_truncated(object) << opcode_data->argument3_shift; + ((call_rii_t)opcode_data->emitter)(&emit->as, rd, immediate1, immediate2); + break; + } + + case CALL_RIR: + assert(!"Should not get here."); + break; + + default: + assert(!"Unhandled call convention."); + break; + } +} + +static bool handle_load_store_opcode_with_offset(emit_inline_asm_t *emit, qstr opcode, const opcode_t *opcode_data, mp_parse_node_t *argument_nodes) { + mp_parse_node_t nodes[3] = {0}; + if (!validate_argument(emit, opcode, opcode_data, argument_nodes[0], 0)) { + return false; + } + nodes[0] = argument_nodes[0]; + bool negative = false; + if (!parse_register_offset_node(emit, opcode, opcode_data, argument_nodes[1], 1, &nodes[1], &nodes[2], &negative)) { + return false; + } + + mp_uint_t rd; + mp_uint_t rs1; + parse_register_node(nodes[0], &rd, opcode_data->argument1_kind & C); + if (!parse_register_node(nodes[1], &rs1, opcode_data->argument3_kind & C)) { + return false; + } + + mp_obj_t object; + mp_parse_node_get_int_maybe(nodes[2], &object); + mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument2_shift; + if (negative) { + immediate = (~immediate + 1) & (mp_uint_t)-1; + } + if (!is_in_signed_mask(opcode_data->argument2_mask, immediate)) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("opcode '%q' argument %d is out of range"), + opcode, 2)); + return false; + } + + ((call_rri_t)opcode_data->emitter)(&emit->as, rd, rs1, immediate); + return true; +} + +static void emit_inline_rv32_opcode(emit_inline_asm_t *emit, qstr opcode, mp_uint_t arguments_count, mp_parse_node_t *argument_nodes) { + const opcode_t *opcode_data = NULL; + for (mp_uint_t index = 0; index < MP_ARRAY_SIZE(OPCODES); index++) { + if (OPCODES[index].qstring == opcode) { + opcode_data = &OPCODES[index]; + break; + } + } + + if (!opcode_data) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("unsupported or unknown RV32 instruction '%q'."), opcode)); + return; + } + + size_t required_arguments = 0; + if (opcode_data->argument1_kind != N) { + required_arguments++; + } + if (opcode_data->argument2_kind != N) { + required_arguments++; + } + if (opcode_data->argument3_kind != N) { + required_arguments++; + } + + if (opcode_data->calling_convention != CALL_RIR) { + if (required_arguments != arguments_count) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("RV32 instruction '%q' requires %d arguments."), opcode, required_arguments)); + return; + } + if (required_arguments >= 1 && !validate_argument(emit, opcode, opcode_data, argument_nodes[0], 0)) { + return; + } + if (required_arguments >= 2 && !validate_argument(emit, opcode, opcode_data, argument_nodes[1], 1)) { + return; + } + if (required_arguments >= 3 && !validate_argument(emit, opcode, opcode_data, argument_nodes[2], 2)) { + return; + } + handle_opcode(emit, opcode, opcode_data, argument_nodes); + return; + } + + assert(required_arguments == 3 && "Invalid arguments count for calling convention."); + assert((opcode_data->argument2_kind & U) == 0 && "Offset must not be unsigned."); + assert((opcode_data->argument2_kind & Z) == 0 && "Offset can be zero."); + + if (arguments_count != 2) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("RV32 instruction '%q' requires %d arguments."), opcode, 2)); + return; + } + + handle_load_store_opcode_with_offset(emit, opcode, opcode_data, argument_nodes); +} + +#undef N +#undef R +#undef I +#undef L +#undef C +#undef U + +const emit_inline_asm_method_table_t emit_inline_rv32_method_table = { + #if MICROPY_DYNAMIC_COMPILER + emit_inline_rv32_new, + emit_inline_rv32_free, + #endif + + emit_inline_rv32_start_pass, + emit_inline_rv32_end_pass, + emit_inline_rv32_count_params, + emit_inline_rv32_label, + emit_inline_rv32_opcode, +}; + +#endif // MICROPY_EMIT_INLINE_RV32 diff --git a/py/mpconfig.h b/py/mpconfig.h index 8598eaa5bb639..e84d258a1220a 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -411,6 +411,11 @@ #define MICROPY_EMIT_RV32 (0) #endif +// Whether to enable the RISC-V RV32 inline assembler +#ifndef MICROPY_EMIT_INLINE_RV32 +#define MICROPY_EMIT_INLINE_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 || MICROPY_EMIT_RV32 || MICROPY_EMIT_NATIVE_DEBUG) @@ -420,7 +425,7 @@ #define MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE (MICROPY_EMIT_XTENSAWIN) // Convenience definition for whether any inline assembler emitter is enabled -#define MICROPY_EMIT_INLINE_ASM (MICROPY_EMIT_INLINE_THUMB || MICROPY_EMIT_INLINE_XTENSA) +#define MICROPY_EMIT_INLINE_ASM (MICROPY_EMIT_INLINE_THUMB || MICROPY_EMIT_INLINE_XTENSA || MICROPY_EMIT_INLINE_RV32) // Convenience definition for whether any native or inline assembler emitter is enabled #define MICROPY_EMIT_MACHINE_CODE (MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM) diff --git a/py/py.cmake b/py/py.cmake index dd94f6a59e31d..0fee74ddcb489 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -24,6 +24,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/emitbc.c ${MICROPY_PY_DIR}/emitcommon.c ${MICROPY_PY_DIR}/emitglue.c + ${MICROPY_PY_DIR}/emitinlinerv32.c ${MICROPY_PY_DIR}/emitinlinethumb.c ${MICROPY_PY_DIR}/emitinlinextensa.c ${MICROPY_PY_DIR}/emitnarm.c diff --git a/py/py.mk b/py/py.mk index 9592fbb9170ef..c0b7e1ac8b90e 100644 --- a/py/py.mk +++ b/py/py.mk @@ -123,6 +123,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ emitnxtensawin.o \ asmrv32.o \ emitnrv32.o \ + emitinlinerv32.o \ emitndebug.o \ formatfloat.o \ parsenumbase.o \ diff --git a/tests/inlineasm/rv32/asmargs.py b/tests/inlineasm/rv32/asmargs.py new file mode 100644 index 0000000000000..78afd511150d6 --- /dev/null +++ b/tests/inlineasm/rv32/asmargs.py @@ -0,0 +1,44 @@ +# test passing arguments + + +@micropython.asm_rv32 +def arg0(): + c_li(a0, 1) + + +print(arg0()) + + +@micropython.asm_rv32 +def arg1(a0): + addi(a0, a0, 1) + + +print(arg1(1)) + + +@micropython.asm_rv32 +def arg2(a0, a1): + add(a0, a0, a1) + + +print(arg2(1, 2)) + + +@micropython.asm_rv32 +def arg3(a0, a1, a2): + add(a0, a0, a1) + add(a0, a0, a2) + + +print(arg3(1, 2, 3)) + + +@micropython.asm_rv32 +def arg4(a0, a1, a2, a3): + add(a0, a0, a1) + add(a0, a0, a2) + add(a0, a0, a3) + + +print(arg4(1, 2, 3, 4)) diff --git a/tests/inlineasm/rv32/asmargs.py.exp b/tests/inlineasm/rv32/asmargs.py.exp new file mode 100644 index 0000000000000..e33a6964f46e8 --- /dev/null +++ b/tests/inlineasm/rv32/asmargs.py.exp @@ -0,0 +1,5 @@ +1 +2 +3 +6 +10 diff --git a/tests/inlineasm/rv32/asmarith.py b/tests/inlineasm/rv32/asmarith.py new file mode 100644 index 0000000000000..8b864c0b3b289 --- /dev/null +++ b/tests/inlineasm/rv32/asmarith.py @@ -0,0 +1,79 @@ +# test arithmetic opcodes + + +@micropython.asm_rv32 +def f1(): + li(a0, 0x100) + li(a1, 1) + add(a0, a0, a1) + addi(a0, a0, 1) + addi(a0, a0, -2) + sub(a0, a0, a1) + c_add(a0, a1) + c_addi(a0, -1) + c_sub(a0, a1) + + +print(hex(f1())) + + +@micropython.asm_rv32 +def f2(): + li(a0, 0x10FF) + li(a1, 1) + and_(a2, a0, a1) + andi(a3, a0, 0x10) + or_(a2, a2, a3) + ori(a2, a2, 8) + li(a1, 0x200) + c_or(a2, a1) + li(a1, 0xF0) + mv(a0, a2) + c_and(a0, a1) + li(a1, 0x101) + xor(a0, a0, a1) + xori(a0, a0, 0x101) + c_xor(a0, a1) + + +print(hex(f2())) + + +@micropython.asm_rv32 +def f3(a0, a1): + slt(a0, a0, a1) + + +print(f3(0xFFFFFFF0, 0xFFFFFFF1)) +print(f3(0x0, 0xFFFFFFF1)) +print(f3(0xFFFFFFF1, 0xFFFFFFF1)) +print(f3(0xFFFFFFF1, 0xFFFFFFF0)) + + +@micropython.asm_rv32 +def f4(a0, a1): + sltu(a0, a0, a1) + + +print(f3(0xFFFFFFF0, 0xFFFFFFF1)) +print(f3(0x0, 0xFFFFFFF1)) +print(f3(0xFFFFFFF1, 0xFFFFFFF1)) +print(f3(0xFFFFFFF1, 0xFFFFFFF0)) + + +@micropython.asm_rv32 +def f5(a0): + slti(a0, a0, -2) + + +print(f5(-1)) +print(f5(-3)) + + +@micropython.asm_rv32 +def f6(a0): + sltiu(a0, a0, -2) + + +print(f6(-1)) +print(f6(-3)) diff --git a/tests/inlineasm/rv32/asmarith.py.exp b/tests/inlineasm/rv32/asmarith.py.exp new file mode 100644 index 0000000000000..7da4dd5c93c40 --- /dev/null +++ b/tests/inlineasm/rv32/asmarith.py.exp @@ -0,0 +1,14 @@ +0xfe +0x111 +1 +0 +0 +0 +1 +0 +0 +0 +0 +1 +0 +1 diff --git a/tests/inlineasm/rv32/asmbranch.py b/tests/inlineasm/rv32/asmbranch.py new file mode 100644 index 0000000000000..d7d059d4067c6 --- /dev/null +++ b/tests/inlineasm/rv32/asmbranch.py @@ -0,0 +1,161 @@ +# test branch instructions + + +@micropython.asm_rv32 +def tbeq(a0): + mv(a1, a0) + + li(a0, 10) + li(a2, 1) + beq(a1, a2, end) + + li(a0, 20) + li(a2, 2) + beq(a1, a2, end) + + li(a0, 30) + li(a2, 3) + beq(a1, a2, end) + + li(a0, 0) + + label(end) + + +print(tbeq(0)) +print(tbeq(1)) +print(tbeq(2)) +print(tbeq(3)) + + +@micropython.asm_rv32 +def tbne(a0): + mv(a1, a0) + + li(a0, 10) + li(a2, 1) + bne(a1, a2, end) + + li(a0, 20) + li(a2, 2) + bne(a1, a2, end) + + li(a0, 30) + li(a2, 3) + bne(a1, a2, end) + + li(a0, 0) + + label(end) + + +print(tbne(0)) +print(tbne(1)) +print(tbne(2)) +print(tbne(3)) + + +@micropython.asm_rv32 +def tbgeu(a0): + mv(a1, a0) + + li(a0, 1) + li(a2, 2) + bgeu(a1, a2, end) + li(a0, 0) + + label(end) + + +print(tbgeu(0)) +print(tbgeu(1)) +print(tbgeu(2)) +print(tbgeu(3)) + + +@micropython.asm_rv32 +def tbltu(a0): + mv(a1, a0) + + li(a0, 1) + li(a2, 2) + bltu(a1, a2, end) + li(a0, 0) + + label(end) + + +print(tbltu(0)) +print(tbltu(1)) +print(tbltu(2)) +print(tbltu(3)) + + +@micropython.asm_rv32 +def tbge(a0): + mv(a1, a0) + + li(a0, 1) + li(a2, -2) + bge(a1, a2, end) + li(a0, 0) + + label(end) + + +print(tbge(-3)) +print(tbge(-2)) +print(tbge(-1)) +print(tbge(0)) + + +@micropython.asm_rv32 +def tblt(a0): + mv(a1, a0) + + li(a0, 1) + li(a2, -2) + blt(a1, a2, end) + li(a0, 0) + + label(end) + + +print(tblt(-3)) +print(tblt(-2)) +print(tblt(-1)) +print(tblt(0)) + + +@micropython.asm_rv32 +def tcbeqz(a0): + mv(a1, a0) + + li(a0, 1) + c_beqz(a1, end) + li(a0, 0) + + label(end) + + +print(tcbeqz(0)) +print(tcbeqz(1)) +print(tcbeqz(2)) +print(tcbeqz(3)) + + +@micropython.asm_rv32 +def tcbnez(a0): + mv(a1, a0) + + li(a0, 1) + c_bnez(a1, end) + li(a0, 0) + + label(end) + + +print(tcbnez(0)) +print(tcbnez(1)) +print(tcbnez(2)) +print(tcbnez(3)) diff --git a/tests/inlineasm/rv32/asmbranch.py.exp b/tests/inlineasm/rv32/asmbranch.py.exp new file mode 100644 index 0000000000000..baae69149538e --- /dev/null +++ b/tests/inlineasm/rv32/asmbranch.py.exp @@ -0,0 +1,32 @@ +0 +10 +20 +30 +10 +20 +10 +10 +0 +0 +1 +1 +1 +1 +0 +0 +0 +1 +1 +1 +1 +0 +0 +0 +1 +0 +0 +0 +0 +1 +1 +1 diff --git a/tests/inlineasm/rv32/asmconst.py b/tests/inlineasm/rv32/asmconst.py new file mode 100644 index 0000000000000..2b6363a43db95 --- /dev/null +++ b/tests/inlineasm/rv32/asmconst.py @@ -0,0 +1,49 @@ +# test constants in assembler + + +@micropython.asm_rv32 +def c1(): + li(a0, 0xFFFFFFFF) + li(a1, 0xF0000000) + sub(a0, a0, a1) + + +print(hex(c1())) + + +@micropython.asm_rv32 +def c2(): + lui(a0, 0x12345) + li(a1, 0x678) + add(a0, a0, a1) + + +print(hex(c2())) + + +@micropython.asm_rv32 +def c3() -> uint: + lui(a0, 0) + addi(a0, a0, 0x7FF) + + +print(hex(c3())) + + +@micropython.asm_rv32 +def c4() -> uint: + lui(a0, 0) + addi(a0, a0, -1) + + +print(hex(c4())) + + +@micropython.asm_rv32 +def c5(): + c_lui(a0, 1) + c_li(a1, 1) + c_add(a0, a1) + + +print(hex(c5())) diff --git a/tests/inlineasm/rv32/asmconst.py.exp b/tests/inlineasm/rv32/asmconst.py.exp new file mode 100644 index 0000000000000..0c713a841486b --- /dev/null +++ b/tests/inlineasm/rv32/asmconst.py.exp @@ -0,0 +1,5 @@ +0xfffffff +0x12345678 +0x7ff +0xffffffff +0x1001 diff --git a/tests/inlineasm/rv32/asmcsr.py b/tests/inlineasm/rv32/asmcsr.py new file mode 100644 index 0000000000000..f27e2aa5e34ec --- /dev/null +++ b/tests/inlineasm/rv32/asmcsr.py @@ -0,0 +1,65 @@ +# test csr instructions + +# CSR 0x340 is `mscratch`. This test suite is only safe to run on a system +# where it is known that there is no other code that can read from or write +# to that register. The qemu port is one such system, as the CSR is only +# accessed when a machine exception occurs, and at that point it doesn't matter +# anymore whether these tests are running or not. + + +@micropython.asm_rv32 +def csr(): + li(a0, 0) + csrrw(zero, zero, 0x340) # All zeroes + csrrs(a1, zero, 0x340) # Read zeroes + c_bnez(a1, end) + addi(a0, a0, 1) + li(a1, 0xA5A5A5A5) + li(a2, 0x5A5A5A5A) + csrrs(a2, a1, 0x340) # Read zeroes, set 0xA5A5A5A5 + c_bnez(a2, end) + addi(a0, a0, 1) + csrrs(a3, zero, 0x340) # Read 0xA5A5A5A5 + bne(a3, a1, end) + addi(a0, a0, 1) + li(a2, 0xF0F0F0F0) + csrrc(zero, a2, 0x340) # Clear upper half + csrrs(a3, zero, 0x340) # Read 0x05050505 + xori(a2, a2, -1) + and_(a2, a1, a2) + bne(a2, a3, end) + addi(a0, a0, 1) + label(end) + + +print(csr()) + + +@micropython.asm_rv32 +def csri(): + li(a0, 0) + csrrwi(zero, 0x340, 15) # Write 0xF + csrrs(a1, zero, 0x340) # Read 0xF + csrrsi(a2, 0x340, 0) # Read + bne(a1, a2, end) + addi(a0, a0, 1) + csrrci(a2, 0x340, 0) # Read + bne(a1, a2, end) + addi(a0, a0, 1) + li(a2, 15) + bne(a1, a2, end) + addi(a0, a0, 1) + csrrci(zero, 0x340, 1) # Clear bit 1 + csrrs(a1, zero, 0x340) # Read 0xE + li(a2, 14) + bne(a1, a2, end) + addi(a0, a0, 1) + csrrsi(zero, 0x340, 1) # Set bit 1 + csrrs(a1, zero, 0x340) # Read 0xF + li(a2, 15) + bne(a1, a2, end) + addi(a0, a0, 1) + label(end) + + +print(csri()) diff --git a/tests/inlineasm/rv32/asmcsr.py.exp b/tests/inlineasm/rv32/asmcsr.py.exp new file mode 100644 index 0000000000000..61c83cba41ce3 --- /dev/null +++ b/tests/inlineasm/rv32/asmcsr.py.exp @@ -0,0 +1,2 @@ +4 +5 diff --git a/tests/inlineasm/rv32/asmdata.py b/tests/inlineasm/rv32/asmdata.py new file mode 100644 index 0000000000000..5e555ef4bf465 --- /dev/null +++ b/tests/inlineasm/rv32/asmdata.py @@ -0,0 +1,33 @@ +# test the "data" directive + + +@micropython.asm_rv32 +def ret_num(a0) -> uint: + slli(a0, a0, 2) + addi(a0, a0, 16) + auipc(a1, 0) + add(a1, a1, a0) + lw(a0, 0(a1)) + jal(zero, HERE) + data(4, 0x12345678, 0x20000000, 0x40000000, 0x7FFFFFFF + 1, (1 << 32) - 2) + label(HERE) + + +for i in range(5): + print(hex(ret_num(i))) + + +@micropython.asm_rv32 +def ret_num_la(a0) -> uint: + slli(a0, a0, 2) + la(a1, DATA) + add(a1, a1, a0) + lw(a0, 0(a1)) + jal(zero, HERE) + label(DATA) + data(4, 0x12345678, 0x20000000, 0x40000000, 0x7FFFFFFF + 1, (1 << 32) - 2) + label(HERE) + + +for i in range(5): + print(hex(ret_num_la(i))) diff --git a/tests/inlineasm/rv32/asmdata.py.exp b/tests/inlineasm/rv32/asmdata.py.exp new file mode 100644 index 0000000000000..79e92bdfa5de5 --- /dev/null +++ b/tests/inlineasm/rv32/asmdata.py.exp @@ -0,0 +1,10 @@ +0x12345678 +0x20000000 +0x40000000 +0x80000000 +0xfffffffe +0x12345678 +0x20000000 +0x40000000 +0x80000000 +0xfffffffe diff --git a/tests/inlineasm/rv32/asmdivmul.py b/tests/inlineasm/rv32/asmdivmul.py new file mode 100644 index 0000000000000..e1120c6f63cef --- /dev/null +++ b/tests/inlineasm/rv32/asmdivmul.py @@ -0,0 +1,63 @@ +@micropython.asm_rv32 +def sdiv(a0, a1): + div(a0, a0, a1) + + +@micropython.asm_rv32 +def udiv(a0, a1): + divu(a0, a0, a1) + + +@micropython.asm_rv32 +def srem(a0, a1): + rem(a0, a0, a1) + + +@micropython.asm_rv32 +def urem(a0, a1): + remu(a0, a0, a1) + + +print(sdiv(1234, 3)) +print(sdiv(-1234, 3)) +print(sdiv(1234, -3)) +print(sdiv(-1234, -3)) + +print(udiv(1234, 3)) +print(udiv(0xFFFFFFFF, 0x7FFFFFFF)) +print(udiv(0xFFFFFFFF, 0xFFFFFFFF)) + +print(srem(1234, 3)) +print(srem(-1234, 3)) +print(srem(1234, -3)) +print(srem(-1234, -3)) + +print(urem(1234, 3)) +print(urem(0xFFFFFFFF, 0x7FFFFFFF)) +print(urem(0xFFFFFFFF, 0xFFFFFFFF)) + + +@micropython.asm_rv32 +def m1(a0, a1): + mul(a0, a0, a1) + + +@micropython.asm_rv32 +def m2(a0, a1): + mulh(a0, a0, a1) + + +@micropython.asm_rv32 +def m3(a0, a1): + mulhu(a0, a0, a1) + + +@micropython.asm_rv32 +def m4(a0, a1): + mulhsu(a0, a0, a1) + + +print(m1(0xFFFFFFFF, 2)) +print(m2(0xFFFFFFFF, 0xFFFFFFF0)) +print(m3(0xFFFFFFFF, 0xFFFFFFF0)) +print(m4(0xFFFFFFFF, 0xFFFFFFF0)) diff --git a/tests/inlineasm/rv32/asmdivmul.py.exp b/tests/inlineasm/rv32/asmdivmul.py.exp new file mode 100644 index 0000000000000..60d28635f792b --- /dev/null +++ b/tests/inlineasm/rv32/asmdivmul.py.exp @@ -0,0 +1,18 @@ +411 +-411 +-411 +411 +411 +2 +1 +1 +-1 +1 +-1 +1 +1 +0 +-2 +0 +-17 +-1 diff --git a/tests/inlineasm/rv32/asmjump.py b/tests/inlineasm/rv32/asmjump.py new file mode 100644 index 0000000000000..fe87d3f968b37 --- /dev/null +++ b/tests/inlineasm/rv32/asmjump.py @@ -0,0 +1,115 @@ +@micropython.asm_rv32 +def f1(): + li(a0, 0) + la(a1, END) + c_jr(a1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + label(END) + + +print(f1()) + + +@micropython.asm_rv32 +def f2(): + addi(sp, sp, -4) + c_swsp(ra, 0) + li(ra, 0) + li(a0, 0) + c_jal(END) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + label(END) + bne(ra, zero, SUCCESS) + c_addi(a0, 2) + label(SUCCESS) + c_lwsp(ra, 0) + addi(sp, sp, 4) + + +print(f2()) + + +@micropython.asm_rv32 +def f3(): + li(a0, 0) + c_j(END) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + label(END) + + +print(f3()) + + +@micropython.asm_rv32 +def f4(): + addi(sp, sp, -4) + c_swsp(ra, 0) + li(ra, 0) + li(a0, 0) + la(a1, END) + c_jalr(a1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + label(END) + bne(ra, zero, SUCCESS) + c_addi(a0, 2) + label(SUCCESS) + c_lwsp(ra, 0) + addi(sp, sp, 4) + + +print(f4()) + + +@micropython.asm_rv32 +def f5(): + li(a0, 0) + li(a1, 0) + jal(a1, END) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + label(END) + bne(a1, zero, SUCCESS) + c_addi(a0, 2) + label(SUCCESS) + + +print(f5()) + + +@micropython.asm_rv32 +def f6(): + li(a0, 0) + la(a1, JUMP) + li(a2, 0) + jalr(a2, a1, 10) + label(JUMP) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + c_addi(a0, 1) + bne(a2, zero, SUCCESS) + c_addi(a0, 2) + label(SUCCESS) + + +print(f6()) diff --git a/tests/inlineasm/rv32/asmjump.py.exp b/tests/inlineasm/rv32/asmjump.py.exp new file mode 100644 index 0000000000000..f7eb44d66e0b1 --- /dev/null +++ b/tests/inlineasm/rv32/asmjump.py.exp @@ -0,0 +1,6 @@ +0 +0 +0 +0 +0 +0 diff --git a/tests/inlineasm/rv32/asmloadstore.py b/tests/inlineasm/rv32/asmloadstore.py new file mode 100644 index 0000000000000..2c49e07b41a5c --- /dev/null +++ b/tests/inlineasm/rv32/asmloadstore.py @@ -0,0 +1,86 @@ +# test load/store opcodes + + +@micropython.asm_rv32 +def l(): + li(a5, 4) + addi(sp, sp, -12) + li(a0, 0x123) + c_swsp(a0, 0) + addi(a1, a0, 0x111) + c_swsp(a1, 4) + addi(a2, a1, 0x111) + c_swsp(a2, 8) + mv(a4, sp) + c_lw(a3, 0(a4)) + bne(a3, a0, END) + addi(a5, a5, -1) + lw(a3, 4(a4)) + bne(a3, a1, END) + addi(a5, a5, -1) + lhu(a3, 8(a4)) + bne(a3, a2, END) + addi(a5, a5, -1) + lbu(a0, 8(a4)) + addi(a0, a0, 0x300) + bne(a0, a2, END) + addi(a5, a5, -1) + label(END) + addi(sp, sp, 12) + mv(a0, a5) + + +print(l()) + + +@micropython.asm_rv32 +def s(): + li(a5, 4) + addi(sp, sp, -12) + c_swsp(zero, 0) + c_swsp(zero, 4) + c_swsp(zero, 8) + li(a0, 0x12345) + mv(a4, sp) + c_sw(a0, 0(a4)) + sh(a0, 4(a4)) + sb(a0, 8(a4)) + li(a1, 0xFFFF) + and_(a1, a0, a1) + andi(a2, a0, 0xFF) + lw(a3, 0(sp)) + bne(a3, a0, END) + addi(a5, a5, -1) + lw(a3, 4(sp)) + bne(a3, a1, END) + addi(a5, a5, -1) + lw(a3, 8(sp)) + bne(a3, a2, END) + addi(a5, a5, -1) + label(END) + addi(sp, sp, 12) + mv(a0, a5) + + +print(s()) + + +@micropython.asm_rv32 +def lu(): + li(a5, 4) + addi(sp, sp, -8) + li(a0, 0xF1234567) + c_swsp(a0, 0) + c_swsp(a0, 4) + lh(a1, 0(sp)) + blt(a1, zero, END) + addi(a5, a5, -1) + lb(a2, 4(sp)) + blt(a2, zero, END) + addi(a5, a5, -1) + label(END) + addi(sp, sp, 8) + mv(a0, a5) + + +print(lu()) diff --git a/tests/inlineasm/rv32/asmloadstore.py.exp b/tests/inlineasm/rv32/asmloadstore.py.exp new file mode 100644 index 0000000000000..4539bbf2d22d5 --- /dev/null +++ b/tests/inlineasm/rv32/asmloadstore.py.exp @@ -0,0 +1,3 @@ +0 +1 +2 diff --git a/tests/inlineasm/rv32/asmrettype.py b/tests/inlineasm/rv32/asmrettype.py new file mode 100644 index 0000000000000..fc7ae61d15225 --- /dev/null +++ b/tests/inlineasm/rv32/asmrettype.py @@ -0,0 +1,33 @@ +# test return type of inline asm + + +@micropython.asm_rv32 +def ret_obj(a0) -> object: + pass + + +ret_obj(print)(1) + + +@micropython.asm_rv32 +def ret_bool(a0) -> bool: + pass + + +print(ret_bool(0), ret_bool(1)) + + +@micropython.asm_rv32 +def ret_int(a0) -> int: + slli(a0, a0, 29) + + +print(ret_int(0), hex(ret_int(1)), hex(ret_int(2)), hex(ret_int(4))) + + +@micropython.asm_rv32 +def ret_uint(a0) -> uint: + slli(a0, a0, 29) + + +print(ret_uint(0), hex(ret_uint(1)), hex(ret_uint(2)), hex(ret_uint(4))) diff --git a/tests/inlineasm/rv32/asmrettype.py.exp b/tests/inlineasm/rv32/asmrettype.py.exp new file mode 100644 index 0000000000000..cbb49d24724ec --- /dev/null +++ b/tests/inlineasm/rv32/asmrettype.py.exp @@ -0,0 +1,4 @@ +1 +False True +0 0x20000000 0x40000000 -0x80000000 +0 0x20000000 0x40000000 0x80000000 diff --git a/tests/inlineasm/rv32/asmsanity.py b/tests/inlineasm/rv32/asmsanity.py new file mode 100644 index 0000000000000..1a16d3504dbfe --- /dev/null +++ b/tests/inlineasm/rv32/asmsanity.py @@ -0,0 +1,204 @@ +TEMPLATE3 = """ +@micropython.asm_rv32 +def f(): + {}({}, {}, {}) +""" + +TEMPLATE2 = """ +@micropython.asm_rv32 +def f(): + {}({}, {}) +""" + +TEMPLATE1 = """ +@micropython.asm_rv32 +def f(): + {}({}) +""" + + +REGISTERS = [ + "zero", + "s0", + "s1", + "s2", + "s3", + "s4", + "s5", + "s6", + "s7", + "s8", + "s9", + "s10", + "s11", + "a0", + "a1", + "a2", + "a3", + "a4", + "a5", + "a6", + "a7", + "tp", + "gp", + "sp", + "ra", + "t0", + "t1", + "t2", + "t3", + "t4", + "t5", + "t6", + "x0", + "x1", + "x2", + "x3", + "x4", + "x5", + "x6", + "x7", + "x8", + "x9", + "x10", + "x11", + "x12", + "x13", + "x14", + "x15", + "x16", + "x17", + "x18", + "x19", + "x20", + "x21", + "x22", + "x23", + "x24", + "x25", + "x26", + "x27", + "x28", + "x29", + "x30", + "x31", +] + + +def harness(opcode, fragment, tag): + try: + exec(fragment) + except SyntaxError: + print(tag, opcode) + + +for opcode in ("slli", "srli", "srai"): + harness(opcode, TEMPLATE3.format(opcode, "a0", "a0", -1), "-") + harness(opcode, TEMPLATE3.format(opcode, "a0", "a0", 33), "+") + +for opcode in ("c_slli", "c_srli", "c_srai"): + harness(opcode, TEMPLATE2.format(opcode, "a0", -1), "-") + harness(opcode, TEMPLATE2.format(opcode, "a0", 33), "+") + +harness("c_slli", TEMPLATE2.format("c_slli", "zero", 0), "0") +harness("c_slli", TEMPLATE2.format("c_slli", "x0", 0), "0") + +for opcode in ("c_srli", "c_srai"): + for register in REGISTERS: + harness(opcode, TEMPLATE2.format(opcode, register, 0), register) + +for opcode in ("c_mv", "c_add"): + harness(opcode, TEMPLATE2.format(opcode, "a0", "zero"), "0l") + harness(opcode, TEMPLATE2.format(opcode, "zero", "a0"), "0r") + harness(opcode, TEMPLATE2.format(opcode, "zero", "zero"), "0b") + +harness("c_jr", TEMPLATE1.format("c_jr", "zero"), "0") + +for opcode in ("addi", "andi", "ori", "slti", "sltiu", "xori"): + harness(opcode, TEMPLATE3.format(opcode, "a0", "a0", 0x7FF), ">=s") + harness(opcode, TEMPLATE3.format(opcode, "a0", "a0", 0x800), ">s") + harness(opcode, TEMPLATE3.format(opcode, "a0", "a0", -2048), "<=s") + harness(opcode, TEMPLATE3.format(opcode, "a0", "a0", -2049), "=s") + harness(opcode, TEMPLATE.format(opcode, 0x800), ">s") + harness(opcode, TEMPLATE.format(opcode, -2048), "<=s") + harness(opcode, TEMPLATE.format(opcode, -2049), "0") +harness("c_addi", TEMPLATE2.format("c_andi", "zero", -512), "<0") +harness("c_addi", TEMPLATE2.format("c_andi", "s0", 0), "s0") +harness("c_addi", TEMPLATE2.format("c_andi", "s0", -100), "s") + +harness("c_andi", TEMPLATE2.format("c_andi", "zero", 0), "00") +harness("c_andi", TEMPLATE2.format("c_andi", "zero", 512), ">0") +harness("c_andi", TEMPLATE2.format("c_andi", "zero", -512), "<0") +harness("c_andi", TEMPLATE2.format("c_andi", "s0", 0), "s0") +harness("c_andi", TEMPLATE2.format("c_andi", "s0", -100), "s") + +C_REGISTERS = ( + "a0", + "a1", + "a2", + "a3", + "a4", + "a5", + "s0", + "s1", + "x8", + "x9", + "x10", + "x11", + "x12", + "x13", + "x14", + "x15", +) + +for opcode in ("c_and", "c_or", "c_xor"): + for source in REGISTERS: + for destination in REGISTERS: + if source in C_REGISTERS and destination in C_REGISTERS: + try: + exec( + """ +@micropython.asm_rv32 +def f(): + {}({}, {}) +""".format(opcode, source, destination) + ) + except SyntaxError: + print(source, destination, opcode) + else: + try: + exec( + """ +@micropython.asm_rv32 +def f(): + {}({}, {}) +""".format(opcode, source, destination) + ) + print(source, destination, opcode) + except SyntaxError: + pass + print(opcode) + +for opcode in ("c_lw", "c_sw"): + TEMPLATE = """ +@micropython.asm_rv32 +def f(): + {}(a0, {}(a0)) +""" + harness(opcode, TEMPLATE.format(opcode, 60), ">=s") + harness(opcode, TEMPLATE.format(opcode, 61), ">s") + harness(opcode, TEMPLATE.format(opcode, -60), "<=s") + harness(opcode, TEMPLATE.format(opcode, -61), "s addi +s andi +s ori +s slti +s sltiu +s xori +s lb +s lbu +s lh +s lhu +s lw +s sb +s sh +s sw +0 c_addi +<0 c_addi +s c_addi +00 c_andi +>0 c_andi +<0 c_andi +s c_andi +c_and +c_or +c_xor +>s c_lw +s c_sw + Date: Fri, 6 Sep 2024 12:43:54 +0200 Subject: [PATCH 165/546] qemu/main: Make GC heap size configurable on a per-arch basis. In certain circumstances depending on the code size, the `deflate_decompress` test fails on both ARM and RV32 with a memory allocation failure error. The issue is mitigated by having a larger GC heap, in this case around 20 KBytes more than the original 100 KBytes default. This commit makes the GC heap size configurable on a per-arch basis, with both ARM and RV32 using the enlarged 120 KBytes heap. Signed-off-by: Alessandro Gatti --- ports/qemu/Makefile | 4 ++++ ports/qemu/README.md | 2 ++ ports/qemu/main.c | 8 +++++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index ea0bef814866d..c8c07965f9cd3 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -19,9 +19,11 @@ QSTR_DEFS = qstrdefsport.h MICROPY_ROM_TEXT_COMPRESSION ?= 1 ifeq ($(QEMU_ARCH),arm) +MICROPY_HEAP_SIZE ?= 122880 FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_asm_thumb.py', 'frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" endif ifeq ($(QEMU_ARCH),riscv32) +MICROPY_HEAP_SIZE ?= 122880 FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_asm_rv32.py', 'frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" endif @@ -29,6 +31,8 @@ endif include $(TOP)/py/py.mk include $(TOP)/extmod/extmod.mk +CFLAGS += -DMICROPY_HEAP_SIZE=$(MICROPY_HEAP_SIZE) + ################################################################################ # ARM specific settings diff --git a/ports/qemu/README.md b/ports/qemu/README.md index 9274bcc4e7566..70edf97f58e37 100644 --- a/ports/qemu/README.md +++ b/ports/qemu/README.md @@ -122,3 +122,5 @@ The following options can be specified on the `make` command line: - `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. +- `MICROPY_HEAP_SIZE`: pass in an optional value (in bytes) for overriding the GC + heap size used by the port. diff --git a/ports/qemu/main.c b/ports/qemu/main.c index dff55058e16d1..75c6fe4cdc0d8 100644 --- a/ports/qemu/main.c +++ b/ports/qemu/main.c @@ -34,14 +34,16 @@ #include "shared/runtime/gchelper.h" #include "shared/runtime/pyexec.h" -#define HEAP_SIZE (100 * 1024) +#if MICROPY_HEAP_SIZE <= 0 +#error MICROPY_HEAP_SIZE must be a positive integer. +#endif -static uint32_t gc_heap[HEAP_SIZE / sizeof(uint32_t)]; +static uint32_t gc_heap[MICROPY_HEAP_SIZE / sizeof(uint32_t)]; int main(int argc, char **argv) { mp_stack_ctrl_init(); mp_stack_set_limit(10240); - gc_init(gc_heap, (char *)gc_heap + HEAP_SIZE); + gc_init(gc_heap, (char *)gc_heap + MICROPY_HEAP_SIZE); for (;;) { mp_init(); From 24482a93ef50dc0df1a704922f465d9c23e143c6 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 20 Nov 2024 10:16:53 +0100 Subject: [PATCH 166/546] rp2/mpconfigport: Enable RV32 inline assembly support. This commit enables by default inline assembly support for the RP2 target when it is operating in RISC-V mode. This brings the feature set when in RISC-V mode to parity with what's available in ARM mode. Signed-off-by: Alessandro Gatti --- ports/rp2/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 8f4e846ba13bb..bc289bcbf97cf 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -87,6 +87,7 @@ #endif #elif PICO_RISCV #define MICROPY_EMIT_RV32 (1) +#define MICROPY_EMIT_INLINE_RV32 (1) #endif // Optimisations From 931a768f5529649c43a65e999ab28f2820002c38 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 20 Nov 2024 11:55:54 +0100 Subject: [PATCH 167/546] tests/run-tests.py: Detect inlineasm support and add tests if needed. This commit implements a method to detect at runtime if inline assembler support is enabled, and if so which platform it targets. This allows clean test runs even on modified version of ARM-based ports where inline assembler support is disabled, running inline assembler tests on ports that have such feature not enabled by default and manually enabled, and allows to always run the correct inlineasm tests for ports that support more than one architecture (esp32, qemu, rp2). Signed-off-by: Alessandro Gatti --- ports/qemu/boards/MICROBIT.mk | 3 -- ports/qemu/boards/MPS2_AN385.mk | 3 -- ports/qemu/boards/NETDUINO2.mk | 3 -- ports/qemu/boards/SABRELITE.mk | 2 +- ports/qemu/boards/VIRT_RV32.mk | 3 -- tests/feature_check/inlineasm_rv32.py | 9 ++++ tests/feature_check/inlineasm_rv32.py.exp | 1 + tests/feature_check/inlineasm_thumb.py | 9 ++++ tests/feature_check/inlineasm_thumb.py.exp | 1 + tests/feature_check/inlineasm_xtensa.py | 9 ++++ tests/feature_check/inlineasm_xtensa.py.exp | 1 + tests/run-tests.py | 60 +++++++++++++-------- 12 files changed, 68 insertions(+), 36 deletions(-) create mode 100644 tests/feature_check/inlineasm_rv32.py create mode 100644 tests/feature_check/inlineasm_rv32.py.exp create mode 100644 tests/feature_check/inlineasm_thumb.py create mode 100644 tests/feature_check/inlineasm_thumb.py.exp create mode 100644 tests/feature_check/inlineasm_xtensa.py create mode 100644 tests/feature_check/inlineasm_xtensa.py.exp diff --git a/ports/qemu/boards/MICROBIT.mk b/ports/qemu/boards/MICROBIT.mk index d6cfd7e2243d8..02eb0576c3438 100644 --- a/ports/qemu/boards/MICROBIT.mk +++ b/ports/qemu/boards/MICROBIT.mk @@ -11,6 +11,3 @@ LDSCRIPT = mcu/arm/nrf51.ld SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb1.o MPY_CROSS_FLAGS += -march=armv7m - -# These RV32 tests don't run on Thumb, so exclude them. -RUN_TESTS_ARGS = --exclude 'inlineasm/rv32' diff --git a/ports/qemu/boards/MPS2_AN385.mk b/ports/qemu/boards/MPS2_AN385.mk index 8d5c27df57c23..182d076eb35d4 100644 --- a/ports/qemu/boards/MPS2_AN385.mk +++ b/ports/qemu/boards/MPS2_AN385.mk @@ -10,6 +10,3 @@ LDSCRIPT = mcu/arm/mps2.ld SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o MPY_CROSS_FLAGS += -march=armv7m - -# These RV32 tests don't run on Thumb, so exclude them. -RUN_TESTS_ARGS = --exclude 'inlineasm/rv32' diff --git a/ports/qemu/boards/NETDUINO2.mk b/ports/qemu/boards/NETDUINO2.mk index f88ea32e7e58f..ffa781f339977 100644 --- a/ports/qemu/boards/NETDUINO2.mk +++ b/ports/qemu/boards/NETDUINO2.mk @@ -10,6 +10,3 @@ LDSCRIPT = mcu/arm/stm32.ld SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o MPY_CROSS_FLAGS += -march=armv7m - -# These RV32 tests don't run on Thumb, so exclude them. -RUN_TESTS_ARGS = --exclude 'inlineasm/rv32' diff --git a/ports/qemu/boards/SABRELITE.mk b/ports/qemu/boards/SABRELITE.mk index d3f4e14d48022..839b3d6ac4fcf 100644 --- a/ports/qemu/boards/SABRELITE.mk +++ b/ports/qemu/boards/SABRELITE.mk @@ -16,4 +16,4 @@ SRC_BOARD_O = shared/runtime/gchelper_generic.o MPY_CROSS_FLAGS += -march=armv6 # These tests don't work on Cortex-A9, so exclude them. -RUN_TESTS_ARGS = --exclude 'inlineasm/rv32|inlineasm/thumb/(asmdiv|asmspecialregs).py' +RUN_TESTS_ARGS = --exclude 'inlineasm/thumb/(asmdiv|asmspecialregs).py' diff --git a/ports/qemu/boards/VIRT_RV32.mk b/ports/qemu/boards/VIRT_RV32.mk index 80dd5d56fdc58..dd926480052e1 100644 --- a/ports/qemu/boards/VIRT_RV32.mk +++ b/ports/qemu/boards/VIRT_RV32.mk @@ -10,7 +10,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/thumb' - RUN_NATMODTESTS_ARGS = --arch rv32imc diff --git a/tests/feature_check/inlineasm_rv32.py b/tests/feature_check/inlineasm_rv32.py new file mode 100644 index 0000000000000..21dd103b6c3fe --- /dev/null +++ b/tests/feature_check/inlineasm_rv32.py @@ -0,0 +1,9 @@ +# check if RISC-V 32 inline asm is supported + + +@micropython.asm_rv32 +def f(): + add(a0, a0, a0) + + +print("rv32") diff --git a/tests/feature_check/inlineasm_rv32.py.exp b/tests/feature_check/inlineasm_rv32.py.exp new file mode 100644 index 0000000000000..5eecf09c22403 --- /dev/null +++ b/tests/feature_check/inlineasm_rv32.py.exp @@ -0,0 +1 @@ +rv32 diff --git a/tests/feature_check/inlineasm_thumb.py b/tests/feature_check/inlineasm_thumb.py new file mode 100644 index 0000000000000..321eab0e2f87a --- /dev/null +++ b/tests/feature_check/inlineasm_thumb.py @@ -0,0 +1,9 @@ +# check if Thumb inline asm is supported + + +@micropython.asm_thumb +def f(): + nop() + + +print("thumb") diff --git a/tests/feature_check/inlineasm_thumb.py.exp b/tests/feature_check/inlineasm_thumb.py.exp new file mode 100644 index 0000000000000..bb48e1a2f03c2 --- /dev/null +++ b/tests/feature_check/inlineasm_thumb.py.exp @@ -0,0 +1 @@ +thumb diff --git a/tests/feature_check/inlineasm_xtensa.py b/tests/feature_check/inlineasm_xtensa.py new file mode 100644 index 0000000000000..2a24d39973c8c --- /dev/null +++ b/tests/feature_check/inlineasm_xtensa.py @@ -0,0 +1,9 @@ +# check if Xtensa inline asm is supported + + +@micropython.asm_xtensa +def f(): + ret_n() + + +print("xtensa") diff --git a/tests/feature_check/inlineasm_xtensa.py.exp b/tests/feature_check/inlineasm_xtensa.py.exp new file mode 100644 index 0000000000000..036142c5097b5 --- /dev/null +++ b/tests/feature_check/inlineasm_xtensa.py.exp @@ -0,0 +1 @@ +xtensa diff --git a/tests/run-tests.py b/tests/run-tests.py index db5ebe34cc3d5..3dc0df029e74a 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -232,6 +232,14 @@ def get_test_instance(test_instance, baudrate, user, password): return pyb +def detect_inline_asm_arch(pyb, args): + for arch in ("rv32", "thumb", "xtensa"): + output = run_feature_check(pyb, args, "inlineasm_{}.py".format(arch)) + if output.strip() == arch.encode(): + return arch + return None + + def detect_test_platform(pyb, args): # Run a script to detect various bits of information about the target test instance. output = run_feature_check(pyb, args, "target_info.py") @@ -240,15 +248,19 @@ def detect_test_platform(pyb, args): platform, arch = str(output, "ascii").strip().split() if arch == "None": arch = None + inlineasm_arch = detect_inline_asm_arch(pyb, args) args.platform = platform args.arch = arch if arch and not args.mpy_cross_flags: args.mpy_cross_flags = "-march=" + arch + args.inlineasm_arch = inlineasm_arch print("platform={}".format(platform), end="") if arch: print(" arch={}".format(arch), end="") + if inlineasm_arch: + print(" inlineasm={}".format(inlineasm_arch), end="") print() @@ -601,6 +613,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_io_module = False skip_fstring = False skip_endian = False + skip_inlineasm = False has_complex = True has_coverage = False @@ -661,20 +674,21 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if output != b"a=1\n": skip_fstring = True - # Check if @micropython.asm_thumb supports Thumb2 instructions, and skip such tests if it doesn't - output = run_feature_check(pyb, args, "inlineasm_thumb2.py") - if output != b"thumb2\n": - skip_tests.add("inlineasm/thumb/asmbcc.py") - skip_tests.add("inlineasm/thumb/asmbitops.py") - skip_tests.add("inlineasm/thumb/asmconst.py") - skip_tests.add("inlineasm/thumb/asmdiv.py") - skip_tests.add("inlineasm/thumb/asmfpaddsub.py") - skip_tests.add("inlineasm/thumb/asmfpcmp.py") - skip_tests.add("inlineasm/thumb/asmfpldrstr.py") - skip_tests.add("inlineasm/thumb/asmfpmuldiv.py") - skip_tests.add("inlineasm/thumb/asmfpsqrt.py") - skip_tests.add("inlineasm/thumb/asmit.py") - skip_tests.add("inlineasm/thumb/asmspecialregs.py") + if args.inlineasm_arch == "thumb": + # Check if @micropython.asm_thumb supports Thumb2 instructions, and skip such tests if it doesn't + output = run_feature_check(pyb, args, "inlineasm_thumb2.py") + if output != b"thumb2\n": + skip_tests.add("inlineasm/thumb/asmbcc.py") + skip_tests.add("inlineasm/thumb/asmbitops.py") + skip_tests.add("inlineasm/thumb/asmconst.py") + skip_tests.add("inlineasm/thumb/asmdiv.py") + skip_tests.add("inlineasm/thumb/asmfpaddsub.py") + skip_tests.add("inlineasm/thumb/asmfpcmp.py") + skip_tests.add("inlineasm/thumb/asmfpldrstr.py") + skip_tests.add("inlineasm/thumb/asmfpmuldiv.py") + skip_tests.add("inlineasm/thumb/asmfpsqrt.py") + skip_tests.add("inlineasm/thumb/asmit.py") + skip_tests.add("inlineasm/thumb/asmspecialregs.py") # Check if emacs repl is supported, and skip such tests if it's not t = run_feature_check(pyb, args, "repl_emacs_check.py") @@ -699,6 +713,8 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): ) skip_endian = upy_byteorder != cpy_byteorder + skip_inlineasm = args.inlineasm_arch is None + # These tests don't test slice explicitly but rather use it to perform the test misc_slice_tests = ( "builtin_range", @@ -852,6 +868,7 @@ def run_one_test(test_file): is_const = test_name.startswith("const") is_io_module = test_name.startswith("io_") is_fstring = test_name.startswith("string_fstring") + is_inlineasm = test_name.startswith("asm") skip_it = test_file in skip_tests skip_it |= skip_native and is_native @@ -865,6 +882,7 @@ def run_one_test(test_file): skip_it |= skip_revops and "reverse_op" in test_name skip_it |= skip_io_module and is_io_module skip_it |= skip_fstring and is_fstring + skip_it |= skip_inlineasm and is_inlineasm if skip_it: print("skip ", test_file) @@ -1213,17 +1231,17 @@ def main(): "misc", "extmod", ) + if args.inlineasm_arch is not None: + test_dirs += ("inlineasm/{}".format(args.inlineasm_arch),) if args.platform == "pyboard": # run pyboard tests - test_dirs += ("float", "stress", "inlineasm/thumb", "ports/stm32") + test_dirs += ("float", "stress", "ports/stm32") elif args.platform == "mimxrt": - test_dirs += ("float", "stress", "inlineasm/thumb") + test_dirs += ("float", "stress") elif args.platform == "renesas-ra": - test_dirs += ("float", "inlineasm/thumb", "ports/renesas-ra") + test_dirs += ("float", "ports/renesas-ra") elif args.platform == "rp2": test_dirs += ("float", "stress", "thread", "ports/rp2") - if "arm" in args.mpy_cross_flags: - test_dirs += ("inlineasm/thumb",) elif args.platform == "esp32": test_dirs += ("float", "stress", "thread") elif args.platform in ("esp8266", "minimal", "samd", "nrf"): @@ -1247,10 +1265,6 @@ def main(): "float", "ports/qemu", ) - if args.arch == "rv32imc": - test_dirs += ("inlineasm/rv32",) - else: - test_dirs += ("inlineasm/thumb",) elif args.platform == "webassembly": test_dirs += ("float", "ports/webassembly") else: From 61e2931f864129524e0707c7487f804cfe59e84e Mon Sep 17 00:00:00 2001 From: Victor Rajewski Date: Wed, 4 Dec 2024 10:48:29 +1100 Subject: [PATCH 168/546] stm32/mboot: Add mboot version string. Adds a configurable version string to a known location at the end of mboot flash section. Also stores the options mboot was built with, eg usb and which filesystems are supported. A board can override the defaults, or disable the version string entirely by setting MBOOT_VERSION_ALLOCATED_BYTES=0. Signed-off-by: Victor Rajewski --- ports/stm32/mboot/Makefile | 20 +++++++++- ports/stm32/mboot/README.md | 16 ++++++++ ports/stm32/mboot/fwupdate.py | 22 ++++++++++ ports/stm32/mboot/stm32_sections.ld | 6 +++ ports/stm32/mboot/version.c | 62 +++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 ports/stm32/mboot/version.c diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 07053b3293907..87bced1aee7ce 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -36,6 +36,12 @@ include $(BOARD_DIR)/mpconfigboard.mk # A board can set MBOOT_TEXT0_ADDR to a custom location where mboot should reside. MBOOT_TEXT0_ADDR ?= 0x08000000 +# The string in MBOOT_VERSION (default defined in version.c if not defined by a +# board) will be stored in the final MBOOT_VERSION_ALLOCATED_BYTES bytes of mboot flash. +# A board can change the size of this region by defining MBOOT_VERSION_ALLOCATED_BYTES. +MBOOT_VERSION_ALLOCATED_BYTES ?= 64 +MBOOT_VERSION_INCLUDE_OPTIONS ?= 1 # if set to 1, this will append build options to version string (see version.c) + USBDEV_DIR=usbdev DFU=$(TOP)/tools/dfu.py PYDFU ?= $(TOP)/tools/pydfu.py @@ -78,9 +84,14 @@ CFLAGS += -DBUILDING_MBOOT=$(BUILDING_MBOOT) CFLAGS += -DMICROPY_HW_STM32WB_FLASH_SYNCRONISATION=0 CFLAGS += -DUSBD_ENABLE_VENDOR_DEVICE_REQUESTS=1 CFLAGS += -DBOOTLOADER_DFU_USB_VID=$(BOOTLOADER_DFU_USB_VID) -DBOOTLOADER_DFU_USB_PID=$(BOOTLOADER_DFU_USB_PID) +ifdef MBOOT_VERSION +CFLAGS += -DMBOOT_VERSION=\"$(MBOOT_VERSION)\" +endif +CFLAGS += -DMBOOT_VERSION_ALLOCATED_BYTES=$(MBOOT_VERSION_ALLOCATED_BYTES) -DMBOOT_VERSION_INCLUDE_OPTIONS=$(MBOOT_VERSION_INCLUDE_OPTIONS) MBOOT_LD_FILES ?= stm32_memory.ld stm32_sections.ld LDFLAGS += -nostdlib -L . $(addprefix -T,$(MBOOT_LD_FILES)) -Map=$(@:.elf=.map) --cref +LDFLAGS += --defsym mboot_version_len=$(MBOOT_VERSION_ALLOCATED_BYTES) LIBS += $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) # Remove uncalled code from the final image. @@ -121,6 +132,7 @@ SRC_C += \ vfs_fat.c \ vfs_lfs.c \ vfs_raw.c \ + version.c \ drivers/bus/softspi.c \ drivers/bus/softqspi.c \ drivers/memory/spiflash.c \ @@ -206,7 +218,7 @@ deploy-stlink: $(BUILD)/firmware.dfu $(BUILD)/firmware.dfu: $(BUILD)/firmware.elf $(ECHO) "Create $@" - $(Q)$(OBJCOPY) -O binary -j .isr_vector -j .text -j .data $^ $(BUILD)/firmware.bin + $(Q)$(OBJCOPY) -O binary -j .isr_vector -j .text -j .data -j .mboot_version_text $^ $(BUILD)/firmware.bin $(Q)$(PYTHON) $(DFU) -b $(MBOOT_TEXT0_ADDR):$(BUILD)/firmware.bin $@ $(BUILD)/firmware.hex: $(BUILD)/firmware.elf @@ -231,8 +243,9 @@ GEN_PINS_SRC = $(BUILD)/pins_$(BOARD).c GEN_PINS_HDR = $(HEADER_BUILD)/pins.h GEN_PINS_AF_CONST = $(HEADER_BUILD)/pins_af_const.h GEN_PINS_AF_DEFS = $(HEADER_BUILD)/pins_af_defs.h +GEN_MPVERSION = $(HEADER_BUILD)/mpversion.h -$(OBJ): $(GEN_QSTRDEFS_GENERATED) $(GEN_ROOT_POINTERS) $(GEN_PINS_AF_DEFS) +$(OBJ): $(GEN_QSTRDEFS_GENERATED) $(GEN_ROOT_POINTERS) $(GEN_PINS_AF_DEFS) $(GEN_MPVERSION) $(HEADER_BUILD): $(MKDIR) -p $(BUILD)/genhdr @@ -250,6 +263,9 @@ $(GEN_PINS_AF_DEFS): $(BOARD_PINS) $(MAKE_PINS) ../$(AF_FILE) $(PREFIX_FILE) | $ --output-af-const $(GEN_PINS_AF_CONST) --output-af-defs $(GEN_PINS_AF_DEFS) \ --mboot-mode +$(GEN_MPVERSION): | $(HEADER_BUILD) + $(PYTHON) ../../../py/makeversionhdr.py $@ + ######################################### vpath %.S . $(TOP) diff --git a/ports/stm32/mboot/README.md b/ports/stm32/mboot/README.md index 221e3a7c3acad..c294041a5194d 100644 --- a/ports/stm32/mboot/README.md +++ b/ports/stm32/mboot/README.md @@ -89,6 +89,17 @@ How to use the beginning of the chunk when the end is reached. Then use a split raw filesystem to inform mboot of this wrapping. + The version and config options that mboot was built with are stored in a + small, fixed section of bytes at the end of the flash region allocated + for mboot. The length of the fixed section defaults to 64 bytes, but can + be overridden by setting MBOOT_VERSION_ALLOCATED_BYTES. If running + low on flash for the mboot build, this can be reduced or even set to 0. + The version string stored defaults to the micropython git version as + generated by makeversionhdr.py. The default version string can be + overridden by setting MBOOT_VERSION in a board's build files. The version + string is appended with options mboot was built with - see version.c for + details. This can be prevented by setting MBOOT_VERSION_INCLUDE_OPTIONS to 0. + 2. Build the board's main application firmware as usual. 3. Build mboot via: @@ -209,6 +220,11 @@ and signed firmware, and can be deployed via USB DFU, or by copying it to the de internal filesystem (if `MBOOT_FSLOAD` is enabled). `firmware.dfu` is still unencrypted and can be directly flashed with jtag etc. +Retrieving the mboot version in micropython +------------------------------------------- +The function `get_mboot_version` in `fwupdate.py` returns the version mboot was built with, +optionally with build options. + Example: Mboot on PYBv1.x ------------------------- diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py index 8578ff4fc9032..b28ba2a782112 100644 --- a/ports/stm32/mboot/fwupdate.py +++ b/ports/stm32/mboot/fwupdate.py @@ -281,3 +281,25 @@ def update_mpy(*args, **kwargs): elems = update_app_elements(*args, **kwargs) if elems: machine.bootloader(elems) + + +def get_mboot_version( + mboot_base=0x0800_0000, # address of start of mboot flash section + mboot_len=0x8000, # length of mboot flash section + mboot_ver_len=64, # length of mboot version section (defined in mboot/Makefile or in board dir) + valid_prefix="mboot-", # prefix that the version string was defined with + include_opts=True, # return the options mboot was built with (set False for just the version) +): + s = "" + for i in range(mboot_ver_len): + c = stm.mem8[mboot_base + mboot_len - mboot_ver_len + i] + if c == 0x00 or c == 0xFF: # have hit padding or empty flash + break + s += chr(c) + if s.startswith(valid_prefix): + if include_opts: + return s + else: + return s.split("+")[0] # optional mboot config info stored after "+" + else: # version hasn't been set, so on the original mboot (i.e. mboot-v1.0.0) + return None diff --git a/ports/stm32/mboot/stm32_sections.ld b/ports/stm32/mboot/stm32_sections.ld index 3302c5f97291d..43511f083971d 100644 --- a/ports/stm32/mboot/stm32_sections.ld +++ b/ports/stm32/mboot/stm32_sections.ld @@ -47,6 +47,12 @@ SECTIONS _edata = .; } >RAM AT> FLASH_BL + /* Final section of mboot flash reserved for mboot version */ + .mboot_version_text (ORIGIN(FLASH_BL) + LENGTH(FLASH_BL) - mboot_version_len) : + { + KEEP(*(.mboot_version)); + } >FLASH_BL + /* Zeroed-out data section */ .bss : { diff --git a/ports/stm32/mboot/version.c b/ports/stm32/mboot/version.c new file mode 100644 index 0000000000000..b9912f61fcb65 --- /dev/null +++ b/ports/stm32/mboot/version.c @@ -0,0 +1,62 @@ +#include "mboot.h" +#include "genhdr/mpversion.h" + +#if defined(MBOOT_VERSION_ALLOCATED_BYTES) && MBOOT_VERSION_ALLOCATED_BYTES > 0 + +#ifndef MBOOT_VERSION +#define MBOOT_VERSION "mboot-" MICROPY_GIT_TAG +#endif + +#if MBOOT_VERSION_INCLUDE_OPTIONS // if this is defined, append a list of build options e.g. fat.lfs2 +#define MBOOT_VERSION_USB MBOOT_VERSION "+usb" // USB is always included + +#if defined(MBOOT_I2C_SCL) +#define MBOOT_VERSION_I2C MBOOT_VERSION_USB ".i2c" +#else +#define MBOOT_VERSION_I2C MBOOT_VERSION_USB +#endif + +#if MBOOT_ADDRESS_SPACE_64BIT +#define MBOOT_VERSION_64BIT MBOOT_VERSION_I2C ".64" +#else +#define MBOOT_VERSION_64BIT MBOOT_VERSION_I2C +#endif + +#if MBOOT_VFS_FAT +#define MBOOT_VERSION_FAT MBOOT_VERSION_64BIT ".fat" +#else +#define MBOOT_VERSION_FAT MBOOT_VERSION_64BIT +#endif + +#if MBOOT_VFS_LFS1 +#define MBOOT_VERSION_LFS1 MBOOT_VERSION_FAT ".lfs1" +#else +#define MBOOT_VERSION_LFS1 MBOOT_VERSION_FAT +#endif + +#if MBOOT_VFS_LFS2 +#define MBOOT_VERSION_LFS2 MBOOT_VERSION_LFS1 ".lfs2" +#else +#define MBOOT_VERSION_LFS2 MBOOT_VERSION_LFS1 +#endif + +#if MBOOT_VFS_RAW +#define MBOOT_VERSION_RAW MBOOT_VERSION_LFS2 ".raw" +#else +#define MBOOT_VERSION_RAW MBOOT_VERSION_LFS2 +#endif + +#define MBOOT_VERSION_FINAL MBOOT_VERSION_RAW + +#else // MBOOT_VERSION_INCLUDE_OPTIONS + +#define MBOOT_VERSION_FINAL MBOOT_VERSION + +#endif // MBOOT_VERSION_INCLUDE_OPTIONS + +// Ensure we don't overrun the allocated space +_Static_assert(sizeof(MBOOT_VERSION_FINAL) <= MBOOT_VERSION_ALLOCATED_BYTES + 1, "mboot version string is too long"); +// Cuts off the null terminator +const char mboot_version[sizeof(MBOOT_VERSION_FINAL) - 1] __attribute__((section(".mboot_version"))) __attribute__ ((__used__)) = MBOOT_VERSION_FINAL; + +#endif From 3dd605e7de0dc8a04aac77c49709e8acf2ea2dd2 Mon Sep 17 00:00:00 2001 From: Yoctopuce Date: Mon, 13 May 2024 12:00:08 +0200 Subject: [PATCH 169/546] shared/timeutils: Add missing mp_uint_t casts. To prevent compiler warnings. Signed-off-by: Yoctopuce --- shared/timeutils/timeutils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/timeutils/timeutils.h b/shared/timeutils/timeutils.h index 82032600f3a9b..874d16e97646d 100644 --- a/shared/timeutils/timeutils.h +++ b/shared/timeutils/timeutils.h @@ -62,7 +62,7 @@ mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, static inline void timeutils_seconds_since_epoch_to_struct_time(uint64_t t, timeutils_struct_time_t *tm) { // TODO this will give incorrect results for dates before 2000/1/1 - timeutils_seconds_since_2000_to_struct_time(t - TIMEUTILS_SECONDS_1970_TO_2000, tm); + timeutils_seconds_since_2000_to_struct_time((mp_uint_t)(t - TIMEUTILS_SECONDS_1970_TO_2000), tm); } // Year is absolute, month/mday are 1-based, hours/minutes/seconds are 0-based. @@ -78,7 +78,7 @@ static inline uint64_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t m } static inline mp_uint_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(uint64_t ns) { - return ns / 1000000000ULL; + return (mp_uint_t)(ns / 1000000000ULL); } static inline uint64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(uint64_t ns) { From b323b427702a26b655183c00e20ef49e3612f9a2 Mon Sep 17 00:00:00 2001 From: Yoctopuce Date: Mon, 13 May 2024 12:04:16 +0200 Subject: [PATCH 170/546] extmod/modsocket: Add missing static in private function definitions. Signed-off-by: Yoctopuce --- extmod/modsocket.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/modsocket.c b/extmod/modsocket.c index eedc17b2ffcb6..dd288b641a0cf 100644 --- a/extmod/modsocket.c +++ b/extmod/modsocket.c @@ -470,7 +470,7 @@ static const mp_rom_map_elem_t socket_locals_dict_table[] = { static MP_DEFINE_CONST_DICT(socket_locals_dict, socket_locals_dict_table); -mp_uint_t socket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { +static mp_uint_t socket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->nic == MP_OBJ_NULL) { return MP_STREAM_ERROR; @@ -482,7 +482,7 @@ mp_uint_t socket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) return ret; } -mp_uint_t socket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { +static mp_uint_t socket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->nic == MP_OBJ_NULL) { return MP_STREAM_ERROR; @@ -494,7 +494,7 @@ mp_uint_t socket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *e return ret; } -mp_uint_t socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { +static mp_uint_t socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); if (request == MP_STREAM_CLOSE) { if (self->nic != MP_OBJ_NULL) { From 61995b53c349d7ac3f3be08a5d6440de6614e0a8 Mon Sep 17 00:00:00 2001 From: Yoctopuce Date: Mon, 13 May 2024 12:11:31 +0200 Subject: [PATCH 171/546] extmod/moddeflate: Add missing size_t cast. To prevent compiler warnings. Signed-off-by: Yoctopuce --- extmod/moddeflate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/moddeflate.c b/extmod/moddeflate.c index c0c3bb26a8b12..4afad01618c4d 100644 --- a/extmod/moddeflate.c +++ b/extmod/moddeflate.c @@ -142,7 +142,7 @@ static bool deflateio_init_read(mp_obj_deflateio_t *self) { } } - size_t window_len = 1 << wbits; + size_t window_len = (size_t)1 << wbits; self->read->window = m_new(uint8_t, window_len); uzlib_uncompress_init(&self->read->decomp, self->read->window, window_len); From 9bb6b50693fe9d813e95af01f1af4d850ebb0b07 Mon Sep 17 00:00:00 2001 From: Yoctopuce Date: Mon, 13 May 2024 12:01:31 +0200 Subject: [PATCH 172/546] py/obj: Cast float literals to 64-bit to prevent overflow warning. Fixes compilation warning C4307: '+': integral constant overflow. Signed-off-by: Yoctopuce --- py/obj.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/py/obj.h b/py/obj.h index d942e1bbf7b28..8a32bfcf0f1e4 100644 --- a/py/obj.h +++ b/py/obj.h @@ -184,12 +184,13 @@ static inline bool mp_obj_is_small_int(mp_const_obj_t o) { #define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 1) | 1)) #if MICROPY_PY_BUILTINS_FLOAT -#define mp_const_float_e MP_ROM_PTR((mp_obj_t)(((0x402df854 & ~3) | 2) + 0x80800000)) -#define mp_const_float_pi MP_ROM_PTR((mp_obj_t)(((0x40490fdb & ~3) | 2) + 0x80800000)) +#define MP_OBJ_NEW_CONST_FLOAT(f) MP_ROM_PTR((mp_obj_t)((((((uint64_t)f) & ~3) | 2) + 0x80800000) & 0xffffffff)) +#define mp_const_float_e MP_OBJ_NEW_CONST_FLOAT(0x402df854) +#define mp_const_float_pi MP_OBJ_NEW_CONST_FLOAT(0x40490fdb) #if MICROPY_PY_MATH_CONSTANTS -#define mp_const_float_tau MP_ROM_PTR((mp_obj_t)(((0x40c90fdb & ~3) | 2) + 0x80800000)) -#define mp_const_float_inf MP_ROM_PTR((mp_obj_t)(((0x7f800000 & ~3) | 2) + 0x80800000)) -#define mp_const_float_nan MP_ROM_PTR((mp_obj_t)(((0xffc00000 & ~3) | 2) + 0x80800000)) +#define mp_const_float_tau MP_OBJ_NEW_CONST_FLOAT(0x40c90fdb) +#define mp_const_float_inf MP_OBJ_NEW_CONST_FLOAT(0x7f800000) +#define mp_const_float_nan MP_OBJ_NEW_CONST_FLOAT(0xffc00000) #endif static inline bool mp_obj_is_float(mp_const_obj_t o) { From 5d12df51fc431ecb159112bf4a40983c2ccba165 Mon Sep 17 00:00:00 2001 From: Yoctopuce Date: Fri, 24 May 2024 11:56:40 +0200 Subject: [PATCH 173/546] py/obj: Make literals unsigned in float get/new functions. Fixes gcc warning when -Wsign-conversion is on. Signed-off-by: Yoctopuce --- py/obj.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/obj.h b/py/obj.h index 8a32bfcf0f1e4..bd31ef11d0928 100644 --- a/py/obj.h +++ b/py/obj.h @@ -203,7 +203,7 @@ static inline mp_float_t mp_obj_float_get(mp_const_obj_t o) { union { mp_float_t f; mp_uint_t u; - } num = {.u = ((mp_uint_t)o - 0x80800000) & ~3}; + } num = {.u = ((mp_uint_t)o - 0x80800000u) & ~3u}; return num.f; } static inline mp_obj_t mp_obj_new_float(mp_float_t f) { @@ -211,7 +211,7 @@ static inline mp_obj_t mp_obj_new_float(mp_float_t f) { mp_float_t f; mp_uint_t u; } num = {.f = f}; - return (mp_obj_t)(((num.u & ~0x3) | 2) + 0x80800000); + return (mp_obj_t)(((num.u & ~0x3u) | 2u) + 0x80800000u); } #endif From 91e30df5f2c9e8c2c84277363a3266d09bc7ae49 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 20 Dec 2024 22:34:42 +1100 Subject: [PATCH 174/546] tests/run-tests.py: Set __main__ module to __injected_test. When using unittest (for example) with injected mpy files, not only does the name of the main test module need to be `__main__`, but also the `__main__` module should correspond to this injected module. Otherwise the unittest test won't be detected. Signed-off-by: Damien George --- 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 3dc0df029e74a..6af70cb836873 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -65,7 +65,9 @@ def base_path(*p): import sys, os, io, vfs class __File(io.IOBase): def __init__(self): - sys.modules['__injected_test'].__name__ = '__main__' + module = sys.modules['__injected_test'] + module.__name__ = '__main__' + sys.modules['__main__'] = module self.off = 0 def ioctl(self, request, arg): if request == 4: # MP_STREAM_CLOSE From 6a90e513dea2267372d8e94f1b83f9260141907d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 27 Dec 2024 16:13:59 +1100 Subject: [PATCH 175/546] py/asmarm: Fix asm_arm_ldrh_reg_reg_offset to emit correct machine code. Prior to this fix, the assembler generated `LDRH Rd, [Rn, #imm]!`, so the second `LDRH` from the same origin would load from the wrong base. Co-authored-by: Alessandro Gatti Signed-off-by: Damien George --- py/asmarm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/asmarm.c b/py/asmarm.c index 6006490701251..634f2db4f6ab7 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -328,7 +328,7 @@ void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn) { void asm_arm_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { // ldrh rd, [rn, #off] - emit_al(as, 0x1f000b0 | (rn << 16) | (rd << 12) | ((byte_offset & 0xf0) << 4) | (byte_offset & 0xf)); + emit_al(as, 0x1d000b0 | (rn << 16) | (rd << 12) | ((byte_offset & 0xf0) << 4) | (byte_offset & 0xf)); } void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn) { From 510e055c71afd60be1aaae4594ec8f97cf8b9fa9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 2 Jan 2025 13:31:18 +1100 Subject: [PATCH 176/546] py/asmarm: Allow function state to be larger than 255. Co-authored-by: Alessandro Gatti Signed-off-by: Damien George --- py/asmarm.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/py/asmarm.c b/py/asmarm.c index 634f2db4f6ab7..b20bdebde5b1a 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -168,13 +168,23 @@ void asm_arm_entry(asm_arm_t *as, int num_locals) { emit_al(as, asm_arm_op_push(as->push_reglist | 1 << ASM_ARM_REG_LR)); if (as->stack_adjust > 0) { - emit_al(as, asm_arm_op_sub_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust)); + if (as->stack_adjust < 0x100) { + emit_al(as, asm_arm_op_sub_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust)); + } else { + asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, as->stack_adjust); + emit_al(as, asm_arm_op_sub_reg(ASM_ARM_REG_SP, ASM_ARM_REG_SP, ASM_ARM_REG_R8)); + } } } void asm_arm_exit(asm_arm_t *as) { if (as->stack_adjust > 0) { - emit_al(as, asm_arm_op_add_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust)); + if (as->stack_adjust < 0x100) { + emit_al(as, asm_arm_op_add_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust)); + } else { + asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, as->stack_adjust); + emit_al(as, asm_arm_op_add_reg(ASM_ARM_REG_SP, ASM_ARM_REG_SP, ASM_ARM_REG_R8)); + } } emit_al(as, asm_arm_op_pop(as->push_reglist | (1 << ASM_ARM_REG_PC))); From 966eb003946cdddb0e9236a70c432890117f70ab Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 2 Jan 2025 14:11:28 +1100 Subject: [PATCH 177/546] tests/run-tests.py: Implement getcwd on __FS hook filesystem. This method is needed by tests like `extmod/vfs_rom.py`. 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 6af70cb836873..1df1b35d23b83 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -84,6 +84,8 @@ def umount(self): pass def chdir(self, path): pass + def getcwd(self): + return "" def stat(self, path): if path == '__injected_test.mpy': return tuple(0 for _ in range(10)) From fca8ea6c3994c5d9ffe088a678d01ec5279d5b6c Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 2 Jan 2025 14:11:57 +1100 Subject: [PATCH 178/546] tests/extmod/vfs_rom.py: Import errno for test. It's needed by the test. This previously passed because the compiler (actually parser) optimises away errno constants. Signed-off-by: Damien George --- tests/extmod/vfs_rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/extmod/vfs_rom.py b/tests/extmod/vfs_rom.py index 0382c84c55894..d416d83870558 100644 --- a/tests/extmod/vfs_rom.py +++ b/tests/extmod/vfs_rom.py @@ -1,7 +1,7 @@ # Test VfsRom filesystem. try: - import sys, struct, os, uctypes, vfs + import errno, sys, struct, os, uctypes, vfs vfs.VfsRom except (ImportError, AttributeError): From bb1e7de5c625e32d10a25fe594ece73f3daadd28 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 2 Jan 2025 15:47:06 +1100 Subject: [PATCH 179/546] qemu/boards: Exclude Thumb2 tests and tests failing with native emitter. The `asmbcc`, `asmbitops`, `asmconst` and `asmit` tests fail to compile with mpy-cross on armv6 architecture (used by SABRELITE), so explicitly exclude them. The `math_domain` and `vfs_rom` tests fail when compiled to native machine code, so also exclude those unconditionally. Signed-off-by: Damien George --- ports/qemu/boards/SABRELITE.mk | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ports/qemu/boards/SABRELITE.mk b/ports/qemu/boards/SABRELITE.mk index 839b3d6ac4fcf..9875667830294 100644 --- a/ports/qemu/boards/SABRELITE.mk +++ b/ports/qemu/boards/SABRELITE.mk @@ -16,4 +16,7 @@ SRC_BOARD_O = shared/runtime/gchelper_generic.o MPY_CROSS_FLAGS += -march=armv6 # These tests don't work on Cortex-A9, so exclude them. -RUN_TESTS_ARGS = --exclude 'inlineasm/thumb/(asmdiv|asmspecialregs).py' +RUN_TESTS_ARGS += --exclude 'inlineasm/thumb/(asmbcc|asmbitops|asmconst|asmdiv|asmit|asmspecialregs).py' + +# These tests fail with via-mpy and the native (armv6) emitter, so exclude them. +RUN_TESTS_ARGS += --exclude 'extmod/vfs_rom.py|float/math_domain.py' From b2a45014612beb0f1a79342d1921d5ea0fb2af2d Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 30 Sep 2024 11:55:23 +1000 Subject: [PATCH 180/546] qemu/Makefile: Add test_full target to run a comprehensive test suite. The tests now include `--via-mpy` and `--via-mpy --emit native`, which will test more cases of the native emitter under both ARM and RISC-V. Signed-off-by: Damien George --- ports/qemu/Makefile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index c8c07965f9cd3..b792ee8ca9c5b 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -128,6 +128,8 @@ CFLAGS += $(SPECS_FRAGMENT) LDFLAGS += $(SPECS_FRAGMENT) endif +RUN_TESTS_FULL_ARGS = -t execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../ports/qemu/$<" $(RUN_TESTS_ARGS) + ################################################################################ # Source files and libraries @@ -169,8 +171,13 @@ run: $(BUILD)/firmware.elf .PHONY: test test: $(BUILD)/firmware.elf - $(eval DIRNAME=ports/$(notdir $(CURDIR))) - cd $(TOP)/tests && ./run-tests.py -t execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_ARGS) $(RUN_TESTS_EXTRA) + cd $(TOP)/tests && ./run-tests.py $(RUN_TESTS_FULL_ARGS) $(RUN_TESTS_EXTRA) + +.PHONY: test_full +test_full: $(BUILD)/firmware.elf + cd $(TOP)/tests && ./run-tests.py $(RUN_TESTS_FULL_ARGS) + cd $(TOP)/tests && ./run-tests.py $(RUN_TESTS_FULL_ARGS) --via-mpy + cd $(TOP)/tests && ./run-tests.py $(RUN_TESTS_FULL_ARGS) --via-mpy --emit native .PHONY: test_natmod test_natmod: $(BUILD)/firmware.elf From 495ce91caefda4623b026520e2b8d8709e274843 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 30 Sep 2024 11:56:40 +1000 Subject: [PATCH 181/546] tools/ci.sh: Run test_full for qemu port CI. Signed-off-by: Damien George --- tools/ci.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/ci.sh b/tools/ci.sh index 8d75d7cecf425..374395abd2ca5 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -285,14 +285,14 @@ function ci_qemu_build_arm { 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 + make ${MAKEOPTS} -C ports/qemu test_full + make ${MAKEOPTS} -C ports/qemu BOARD=SABRELITE test_full } function ci_qemu_build_rv32 { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV32 submodules - make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV32 test + make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV32 test_full # Test building and running native .mpy with rv32imc architecture. ci_native_mpy_modules_build rv32imc From 065d45f9ec7d5597bfcfe075d45abb76197c1319 Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Fri, 8 Nov 2024 12:35:03 -0700 Subject: [PATCH 182/546] rp2/mphalport: Fix mp_hal_pin_low/high() for pin>=32. Fixes issue #16190. Signed-off-by: Dryw Wade --- ports/rp2/mphalport.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h index 33a1073e1df81..956db3ec702fb 100644 --- a/ports/rp2/mphalport.h +++ b/ports/rp2/mphalport.h @@ -182,11 +182,11 @@ static inline void mp_hal_pin_od_high(mp_hal_pin_obj_t pin) { } static inline void mp_hal_pin_low(mp_hal_pin_obj_t pin) { - gpio_clr_mask(1 << pin); + gpio_clr_mask64(UINT64_C(1) << pin); } static inline void mp_hal_pin_high(mp_hal_pin_obj_t pin) { - gpio_set_mask(1 << pin); + gpio_set_mask64(UINT64_C(1) << pin); } enum mp_hal_pin_interrupt_trigger { From 7964a435ea8cafed185e41eeb6dc24b269773650 Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Sat, 9 Nov 2024 15:04:35 -0700 Subject: [PATCH 183/546] rp2/machine_bitstream: Tweak MP_HAL_BITSTREAM_NS_OVERHEAD for RP2350. See https://github.com/micropython/micropython/issues/16190#issuecomment-2466155919 Signed-off-by: Dryw Wade --- ports/rp2/machine_bitstream.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/rp2/machine_bitstream.c b/ports/rp2/machine_bitstream.c index 4ef97f0c86839..8411179f1ae57 100644 --- a/ports/rp2/machine_bitstream.c +++ b/ports/rp2/machine_bitstream.c @@ -32,7 +32,11 @@ #if MICROPY_PY_MACHINE_BITSTREAM +#if PICO_RP2350 +#define MP_HAL_BITSTREAM_NS_OVERHEAD (5) +#else #define MP_HAL_BITSTREAM_NS_OVERHEAD (9) +#endif #if PICO_RISCV From 99ac8193ea299e6d836b076149f561e58226ede6 Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Thu, 14 Nov 2024 15:02:16 -0700 Subject: [PATCH 184/546] rp2/boards/SPARKFUN_PROMICRO_RP2350: Add SparkFun Pro Micro RP2350. Signed-off-by: Dryw Wade --- .../SPARKFUN_PROMICRO_RP2350/board.json | 22 +++++++++++++ .../mpconfigboard.cmake | 4 +++ .../SPARKFUN_PROMICRO_RP2350/mpconfigboard.h | 26 ++++++++++++++++ .../boards/SPARKFUN_PROMICRO_RP2350/pins.csv | 31 +++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json create mode 100644 ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.cmake create mode 100644 ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.h create mode 100644 ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/pins.csv diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json new file mode 100644 index 0000000000000..d2bfa80a348d7 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json @@ -0,0 +1,22 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Dual-core", + "External Flash", + "External RAM", + "JST-SH", + "RGB LED", + "USB-C" + ], + "images": [ + "DEV-24870-Pro-Micro-RP2350-Feature.jpg" + ], + "mcu": "rp2350", + "product": "Pro Micro RP2350", + "thumbnail": "", + "url": "https://www.sparkfun.com/products/24870", + "vendor": "Sparkfun" +} diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.cmake b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.cmake new file mode 100644 index 0000000000000..16f34c68027d7 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.cmake @@ -0,0 +1,4 @@ +# cmake file for SparkFun Pro Micro RP2350 + +set(PICO_BOARD "sparkfun_promicro_rp2350") +set(PICO_PLATFORM "rp2350") diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.h new file mode 100644 index 0000000000000..e01467d71ab05 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/mpconfigboard.h @@ -0,0 +1,26 @@ +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "SparkFun Pro Micro RP2350" +#define MICROPY_HW_FLASH_STORAGE_BYTES (14 * 1024 * 1024) + +#define MICROPY_HW_USB_VID (0x1B4F) +#define MICROPY_HW_USB_PID (0x0039) + +#define MICROPY_HW_UART0_TX (0) +#define MICROPY_HW_UART0_RX (1) +#define MICROPY_HW_UART0_CTS (2) +#define MICROPY_HW_UART0_RTS (3) + +#define MICROPY_HW_UART1_TX (8) +#define MICROPY_HW_UART1_RX (9) +#define MICROPY_HW_UART1_CTS (6) +#define MICROPY_HW_UART1_RTS (7) + +#define MICROPY_HW_I2C0_SDA (16) +#define MICROPY_HW_I2C0_SCL (17) + +#define MICROPY_HW_SPI0_SCK (22) +#define MICROPY_HW_SPI0_MOSI (23) +#define MICROPY_HW_SPI0_MISO (20) + +#define MICROPY_HW_PSRAM_CS_PIN (19) +#define MICROPY_HW_ENABLE_PSRAM (1) diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/pins.csv b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/pins.csv new file mode 100644 index 0000000000000..07e1b094e9152 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/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 +LED,GPIO25 +LED_RGB,GPIO25 +RGB_LED,GPIO25 +NEOPIXEL,GPIO25 From 5a5f0cb462e643a6ca4d5811f803ffad0c2e28d2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 10 Jan 2025 17:11:51 +1100 Subject: [PATCH 185/546] github/workflows: Use Python 3.11 for unix settrace jobs. GitHub Actions has updated ubuntu-latest to 24.04, which now defaults CPython to 3.12, which has a known regression with settrace. Fix that by explicitly using CPython 3.11. Signed-off-by: Damien George --- .github/workflows/ports_unix.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index 64fd5c902a053..cb7abf06aac04 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -173,6 +173,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests. + # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default. + with: + python-version: '3.11' - name: Build run: source tools/ci.sh && ci_unix_settrace_build - name: Run main test suite @@ -185,6 +190,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests. + # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default. + with: + python-version: '3.11' - name: Build run: source tools/ci.sh && ci_unix_settrace_stackless_build - name: Run main test suite From 790986b3eda7702fb48502270d90a9dfc164832f Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 10 Jan 2025 22:27:12 +1100 Subject: [PATCH 186/546] github/workflows: Use ubuntu-22.04 for unix qemu CI. To use an older version of libffi. Signed-off-by: Damien George --- .github/workflows/ports_unix.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index cb7abf06aac04..a095136b4a65e 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -219,7 +219,8 @@ jobs: run: tests/run-tests.py --print-failures qemu_mips: - runs-on: ubuntu-latest + # ubuntu-22.04 is needed for older libffi. + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Install packages @@ -233,7 +234,8 @@ jobs: run: tests/run-tests.py --print-failures qemu_arm: - runs-on: ubuntu-latest + # ubuntu-22.04 is needed for older libffi. + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Install packages @@ -247,7 +249,8 @@ jobs: run: tests/run-tests.py --print-failures qemu_riscv64: - runs-on: ubuntu-latest + # ubuntu-22.04 is needed for older libffi. + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Install packages From 6dbb7ab18f4b4c39baf2028e927177c5b2e1b14e Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Jan 2025 14:34:13 +1100 Subject: [PATCH 187/546] tests/README: Update TLS certificate generation instructions. Fix the command that converts `ec_key.pem` to `ec_key.der`, and increase the certificate validity to 10 years. Signed-off-by: Damien George --- tests/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/README.md b/tests/README.md index 28c1b3a0852cc..891668c8b8737 100644 --- a/tests/README.md +++ b/tests/README.md @@ -222,7 +222,7 @@ need to be re-created by end users. This section is included here for reference A new self-signed RSA key/cert pair can be created with openssl: ``` -$ openssl req -x509 -newkey rsa:2048 -keyout rsa_key.pem -out rsa_cert.pem -days 365 -nodes -subj '/CN=micropython.local/O=MicroPython/C=AU' +$ openssl req -x509 -newkey rsa:2048 -keyout rsa_key.pem -out rsa_cert.pem -days 3650 -nodes -subj '/CN=micropython.local/O=MicroPython/C=AU' ``` In this case CN is: micropython.local @@ -235,6 +235,6 @@ $ openssl x509 -in rsa_cert.pem -out rsa_cert.der -outform DER For elliptic curve tests using key/cert pairs, create a key then a certificate using: ``` $ openssl ecparam -name prime256v1 -genkey -noout -out ec_key.pem -$ openssl x509 -in ec_key.pem -out ec_key.der -outform DER -$ openssl req -new -x509 -key ec_key.pem -out ec_cert.der -outform DER -days 365 -nodes -subj '/CN=micropython.local/O=MicroPython/C=AU' +$ openssl pkey -in ec_key.pem -out ec_key.der -outform DER +$ openssl req -new -x509 -key ec_key.pem -out ec_cert.der -outform DER -days 3650 -nodes -subj '/CN=micropython.local/O=MicroPython/C=AU' ``` From 1b4c969ce0c2ed83bcf366e1b6d977df3d72a9d2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Jan 2025 14:35:54 +1100 Subject: [PATCH 188/546] tests/multi_net: Update TLS test certificates and keys. They expired in early January 2025. Signed-off-by: Damien George --- tests/multi_net/ec_cert.der | Bin 471 -> 470 bytes tests/multi_net/ec_key.der | Bin 121 -> 121 bytes tests/multi_net/rsa_cert.der | Bin 867 -> 867 bytes tests/multi_net/rsa_key.der | Bin 1193 -> 1191 bytes tests/multi_net/sslcontext_getpeercert.py.exp | 2 +- .../sslcontext_verify_callback.py.exp | 2 +- 6 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/multi_net/ec_cert.der b/tests/multi_net/ec_cert.der index a503a39dfe49443dfdecfd79af0eab9f7cc2337d..a13f1c984a6400b7f6c9aef3b23c7f17c5a9c425 100644 GIT binary patch delta 300 zcmcc4e2rP(po#Gk5LYf>W@2Pw5@BBbK`mP-zVF1Xtu-dsLSK44xw+ebi;Y98&EuRc z3p3Ni2~Kiy;=D$t28M2*TrSBJarXUseP>V{LJ zn}}IStn|c^MaBHu$9;av$vo5bx|J?8uTyF}U-4q^6mJIJUO69UiCM-I6!~2WGZzON z1RBV)F^9_Xv52vV2%NhzeV+xx^NkZY0{s4R-Lc&NK+8ZLB(2OMVIbCku7DM+VsZha zoQFGufeVv@x2UZ~;PaN`Knv?v2d{3PKCzK8Y{SoV1&7O?^F&xAGno{GAL~D2zr5#k d{fx-XB7DW{{;4_4Zk57ufz@(zHbgz$1OTp$Y*YXM delta 301 zcmcb{e4Sa}po#G^5LYf>W@2Pw5^)w?ezrgJ-##@5L%*8o*SJ)b*NPf&v2kd%d7QIl zVP={*!AVX|oY%<2z|hddz{J$p)Hn*rH3f2wA>4`mvGtzYzy8Xo*%UP;&dt#pD7; zIZqD;16L-6i2B6C?v~enZEes>_dOHhV)gPw?cx&l9>0fYo4>3To5G~XaQ;Z_c{PuY fW4m1_&yK zNX|V20SBQ(13~}exboMie%5T^o^fqH_-`$4vPi9Of-b+Gi4; bi5j;KbE8XSO8^ax9!x_Rnlq3g4@7ZknF}pC literal 121 zcmV-<0EYiCcLD(c1R$DG+%@_9vL<@B;!Ue6N^g&V-V~d-iN0|&7;OcczVo081_&yK zNX|V20SBQ(13~});(-;eB7fZ5nWl4BUKtT zFflPSFf%hXFfdvd4Kp<`F)=bQGcz?XFp-jDf9?^F=O21pd|LuZnZODyu6IibE#z1s z`nKde31!0ty&(xnjABv~E!X`aBVtlk`>Sx#b)96LjDoRlntv(%?Sq{w4Zf*z=*wE1swL@^; zh0YR31aN#aDagXg2%@)*E{1|3v#$CXf#jCH&W}x7)#_wNS%Xa5GmeYNZ;w@u{w{tc zMO=!epbp6=tf;bOa4Cs0|5X5qf;f6jE0x;t6;lPx2_s?r`dJlRg3^ ze;C_TRaP5zU5RLB0>NjJ0tey0aV+F`0tAIkhC?2`3ZYY#fe zwMflD>q4+n_w{#Z68VRuQ;d7*kG1^4nhD4<|F1G;?NQt-5Whm}VYT0w|}Q9`flSr0q)yf2c89O(is_hBDxF3G;ylM`7gy=8ct78jZarEM&*7270%N!+ C-Y=*C delta 668 zcmV;N0%QH-2IB@JFoFYLFoFU{paTK{0s<5wuk(~YwW*=sF0&t&g6?5ev6!}zBUKtS zFflPTFf=nUGBa8i4Kg(_F)=hSG&3nNd82vUF~b`nsyBcwx1)S%*tF7fz+r5gcg?}?-0W=JLh^VS{n_}5ndgbkjNM#E zeXkTsSPqik{X%6K!NaalOsVJMLA%K#gCLq;Hp8#t=#c(J6S7)!7Q>8tU=dSEbe|0X ze^eMH;6&(TV$iJnzUEGqV6Ytn_D@&do~I zzBDFGklJ|6Hup)hOX8p@CS_01=pxLJV+ZQaTrW_#nJR;9r%0PtlU%ypTOwWppeb1< zX!=G4>wV2{pEqi7s3}RHZsYR$G49>Se_n3zc#@ICJ&P}bGJ_E2Tym)z2%A>9MdM@- zrAVWpJC~*qJq!RvTt*gC42pRvA6fPq?cCpoJYn-VOIm*oMb&h*Zx(Nl4^JbrF;z%P1i=L|5k_i(Ld+c4i{H3Lx8ZCjZR6!qR_Efg@0-J`` CrZBkx diff --git a/tests/multi_net/rsa_key.der b/tests/multi_net/rsa_key.der index c9c535ae639557e4e0023490a8f491a8ebf8985d..658af9bd89923af4381500f78b87cdb212c88301 100644 GIT binary patch literal 1191 zcmV;Y1X%kpf&`-i0RRGm0RaH+5s&8|dR%;40!f*`3N5a8O9?IHSRwkhpl&NF6Z>Ow*yf?Y!(0LY# zX`tqaN3J!rTR)G=>*dvW4ztHlqU(5jY*`M>&u(rA5ZKVs5Vb>a-i6K*M+9(uGbzZz z$_S#jjV^|QA+xUf8G+=MzRr(LTh;1hM_GeR+cS=f$#0KUj{YuwCFSv^Z5Zg!P(iqa zXzDA*VSZN15$Hx;L;gPk0|5X50)hbmLBpst_=Oy|U5u;-Yx1`&a5L^4n)aay#HR>t1#vT0*bMa8NW%yG^KctSGgn0LjRpWj0t0)c@5`$h#`7Dyp&r1oHuim%&fz9TwH_cGOBQ>apf zCXy9~qMYr&Py9+PYug>3Glk3EJ59f&^ayJllI%_dnRr zQKQmgq3OZ}U^RD#H7sLEx0)gDsWylK5i#j>}#l|$@4atQA?uE?0*jB4JV2csvRpjqjQ z%N1zj#^!@i)eNK(clSqCm9>f-JE9ox0)c@5!;F){%syycha&>EVWUWLw0?i^HGfAH z$0b+0^4l@fq-v~=HD69Ml_;!lV1a9?}K@Iiv_OU zTRu=@TQcg>%5A~x;2hY*W9gTuMfjFmPYh|s$*?s1&q0~VaE^=nR!$$2KC8=pHHPo$ zF&LbnnnK3<6+A-LCp=!}1lwt@`DNpN1!WHJdT=b2pllYMi{hlwxoiV9gft`Mzgl+m FrDD&ROFsYr literal 1193 zcmV;a1XlYnf&`@k0RRGm0RaHcHA#8tD#$+6Jr!l6keA;P+b`4E2bpr*i#&OwdE7C> z8zHJUfGxM9d}-LU(sRIJY@2t@!SCGcY}`Wfcp3fK_=%b4iOr1NTt|Jc6iQeQlHdJ8 zWf{T4u24*==i))T$s>axnqM}d^wrK$Gt8E=lU1?~MLOMAX;eyY8>*9M~gtF4&OyIORggw2ogTl5C5%vA;j zf7?)gKYoC()^HO_P(@37TSaQ3@_Nw!v1W}hJqbGm4LYeBKEo{H*;m@(K5Rr9p44n~ z`<|q72dsN@Hp_dFsGC&+0|5X50)hbmJHn%_st#2IC|n(U88#6q^z+{lg8A_8DqM zM9TUR8ky8G9CW)L%&Nd1j=qNlEP$|)U3%`uxu%BvV^-xowxm+&4}4UxaGS#U7Uimh z3Qj$y>E+E`?pGOWI19{1fg*x~LZsS-li6Vjd!SOf$?%fxLE*Ur4}R;11W>?`_D&Wm zR1{Gf)N1K#^D#3p^RU`kI*3;Xt)j&8;P8I(Td0h#3R;SeIhat1+Q=%?RQnRwDTn%C z&W>s?JM{}k2Z!{{b=YJKF!R7o0)c@5>hgq)q8a96ry81^*EVOYrOU6BaXowMHDP@n zUdk|WHeLHi+ENYyvaob_#M4-My~dV5W!@VZf)t5i#E;s;uq5tSZJI zjPERM%2Q*UQwB;t!VjUk_LQ-sM1Qi#cJb6qThdD#7+()3p_k@;6g-;tb&5M@0)c@5 z;ID+9HImxTl$yoIaK;Vb&W-^p_&mUT--XY)t2;BBI}dc-;70)c@5(+c;WulSwl1M#%dkINDRmWD}qh~w7H zC=DYj-@COL?B-UF6u)w>$UJH3iOv+WuVJSqMhgl49CgQiv){ux&BPV05N`nKCB))q z8w%|n=p^fY&yZ?Zz=u@l+`rf2E}qbqJn)P%;0;JiGV98PQ#J@u>?oUL-D$Tq4bB#y z0)c@5ti=jD1%n9^Ie5{)Ol@A;k>S~%AvXZPexb=Rt?|ME^e diff --git a/tests/multi_net/sslcontext_getpeercert.py.exp b/tests/multi_net/sslcontext_getpeercert.py.exp index e7a0ab0b4622c..a0dfaa4f567e5 100644 --- a/tests/multi_net/sslcontext_getpeercert.py.exp +++ b/tests/multi_net/sslcontext_getpeercert.py.exp @@ -1,5 +1,5 @@ --- instance0 --- b'client to server' --- instance1 --- -308201d330820179a00302010202144315a7cd8f69febe2640314e7c97d60a2523ad15300a06082a8648ce3d040302303f311a301806035504030c116d6963726f707974686f6e2e6c6f63616c31143012060355040a0c0b4d6963726f507974686f6e310b3009060355040613024155301e170d3234303131343034353335335a170d3235303131333034353335335a303f311a301806035504030c116d6963726f707974686f6e2e6c6f63616c31143012060355040a0c0b4d6963726f507974686f6e310b30090603550406130241553059301306072a8648ce3d020106082a8648ce3d0301070342000449b7f5fa687cb25a9464c397508149992f445c860bcf7002958eb4337636c6af840cd4c8cf3b96f2384860d8ae3ee3fa135dba051e8605e62bd871689c6af43ca3533051301d0603551d0e0416041441b3ae171d91e330411d8543ba45e0f2d5b2951b301f0603551d2304183016801441b3ae171d91e330411d8543ba45e0f2d5b2951b300f0603551d130101ff040530030101ff300a06082a8648ce3d04030203480030450220587f61c34739d6fab5802a674dcc54443ae9c87da374078c4ee1cd83f4ad1694022100cfc45dcf264888c6ba2c36e78bd27bb67856d7879a052dd7aa7ecf7215f7b992 +308201d230820179a003020102021403abf0266b125f8ec8dab57c34d655e94ae4d9bb300a06082a8648ce3d040302303f311a301806035504030c116d6963726f707974686f6e2e6c6f63616c31143012060355040a0c0b4d6963726f507974686f6e310b3009060355040613024155301e170d3235303131343033333531375a170d3335303131323033333531375a303f311a301806035504030c116d6963726f707974686f6e2e6c6f63616c31143012060355040a0c0b4d6963726f507974686f6e310b30090603550406130241553059301306072a8648ce3d020106082a8648ce3d030107034200042882eb6055f540ddef989ec7ead84281461436745d1b9174a2730f2bc74cf91e1ce62d4ada67129e891ab70e73a34b644b000d8d1e4c43189a3390210f447169a3533051301d0603551d0e0416041410ced497be3800e7b19008504efd0adc39bfe02a301f0603551d2304183016801410ced497be3800e7b19008504efd0adc39bfe02a300f0603551d130101ff040530030101ff300a06082a8648ce3d040302034700304402204b153d2851e7846351383beac1d5b39791810156b0f9672040d3499e14041969022013e32fe207d3bccb7f9859b3140e73074f656c034679135e517b1e9cb05ae5b2 b'server to client' diff --git a/tests/multi_net/sslcontext_verify_callback.py.exp b/tests/multi_net/sslcontext_verify_callback.py.exp index e7a0ab0b4622c..a0dfaa4f567e5 100644 --- a/tests/multi_net/sslcontext_verify_callback.py.exp +++ b/tests/multi_net/sslcontext_verify_callback.py.exp @@ -1,5 +1,5 @@ --- instance0 --- b'client to server' --- instance1 --- -308201d330820179a00302010202144315a7cd8f69febe2640314e7c97d60a2523ad15300a06082a8648ce3d040302303f311a301806035504030c116d6963726f707974686f6e2e6c6f63616c31143012060355040a0c0b4d6963726f507974686f6e310b3009060355040613024155301e170d3234303131343034353335335a170d3235303131333034353335335a303f311a301806035504030c116d6963726f707974686f6e2e6c6f63616c31143012060355040a0c0b4d6963726f507974686f6e310b30090603550406130241553059301306072a8648ce3d020106082a8648ce3d0301070342000449b7f5fa687cb25a9464c397508149992f445c860bcf7002958eb4337636c6af840cd4c8cf3b96f2384860d8ae3ee3fa135dba051e8605e62bd871689c6af43ca3533051301d0603551d0e0416041441b3ae171d91e330411d8543ba45e0f2d5b2951b301f0603551d2304183016801441b3ae171d91e330411d8543ba45e0f2d5b2951b300f0603551d130101ff040530030101ff300a06082a8648ce3d04030203480030450220587f61c34739d6fab5802a674dcc54443ae9c87da374078c4ee1cd83f4ad1694022100cfc45dcf264888c6ba2c36e78bd27bb67856d7879a052dd7aa7ecf7215f7b992 +308201d230820179a003020102021403abf0266b125f8ec8dab57c34d655e94ae4d9bb300a06082a8648ce3d040302303f311a301806035504030c116d6963726f707974686f6e2e6c6f63616c31143012060355040a0c0b4d6963726f507974686f6e310b3009060355040613024155301e170d3235303131343033333531375a170d3335303131323033333531375a303f311a301806035504030c116d6963726f707974686f6e2e6c6f63616c31143012060355040a0c0b4d6963726f507974686f6e310b30090603550406130241553059301306072a8648ce3d020106082a8648ce3d030107034200042882eb6055f540ddef989ec7ead84281461436745d1b9174a2730f2bc74cf91e1ce62d4ada67129e891ab70e73a34b644b000d8d1e4c43189a3390210f447169a3533051301d0603551d0e0416041410ced497be3800e7b19008504efd0adc39bfe02a301f0603551d2304183016801410ced497be3800e7b19008504efd0adc39bfe02a300f0603551d130101ff040530030101ff300a06082a8648ce3d040302034700304402204b153d2851e7846351383beac1d5b39791810156b0f9672040d3499e14041969022013e32fe207d3bccb7f9859b3140e73074f656c034679135e517b1e9cb05ae5b2 b'server to client' From e73cf71a246ee456aac0f4d16167e0856846db6b Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 4 Jan 2025 15:00:28 +0100 Subject: [PATCH 189/546] tests/extmod/re_sub.py: Fix test execution on Python 3.13. This commit fixes a test failure for `extmod/re_sub.py` where the code, whilst being correct, would not make the test pass due to a newer Python version than expected. On Python 3.13, running `tests/extmod/re_sub.py` would yield a deprecation warning about `re.sub` not providing the match count as a keyword parameter. This warning would be embedded in the expected test result and thus the test would always fail. Co-authored-by: stijn Signed-off-by: Alessandro Gatti --- tests/extmod/re_sub.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/extmod/re_sub.py b/tests/extmod/re_sub.py index 2c7c6c10f1a49..ecaa66d83d8a7 100644 --- a/tests/extmod/re_sub.py +++ b/tests/extmod/re_sub.py @@ -10,6 +10,8 @@ print("SKIP") raise SystemExit +import sys + def multiply(m): return str(int(m.group(0)) * 2) @@ -47,7 +49,11 @@ def A(): print(re.sub("a", "b", "c")) # with maximum substitution count specified -print(re.sub("a", "b", "1a2a3a", 2)) +if sys.implementation.name != "micropython": + # On CPython 3.13 and later the substitution count must be a keyword argument. + print(re.sub("a", "b", "1a2a3a", count=2)) +else: + print(re.sub("a", "b", "1a2a3a", 2)) # invalid group try: From 0a9cc9014ac228e5c51a067b7d83a97a539899da Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Wed, 13 Nov 2024 16:42:30 -0700 Subject: [PATCH 190/546] rp2/boards/SPARKFUN_THINGPLUS_RP2350: Add SparkFun Thing Plus RP2350. Signed-off-by: Dryw Wade --- .../SPARKFUN_THINGPLUS_RP2350/board.json | 27 +++++++++++ .../SPARKFUN_THINGPLUS_RP2350/manifest.py | 6 +++ .../mpconfigboard.cmake | 15 ++++++ .../SPARKFUN_THINGPLUS_RP2350/mpconfigboard.h | 48 +++++++++++++++++++ .../boards/SPARKFUN_THINGPLUS_RP2350/pins.csv | 33 +++++++++++++ 5 files changed, 129 insertions(+) create mode 100644 ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/board.json create mode 100644 ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/manifest.py create mode 100644 ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/mpconfigboard.cmake create mode 100644 ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/mpconfigboard.h create mode 100644 ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/pins.csv diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/board.json b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/board.json new file mode 100644 index 0000000000000..09bbefe04dc1e --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/board.json @@ -0,0 +1,27 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "BLE", + "Battery Charging", + "Dual-core", + "External Flash", + "External RAM", + "Feather", + "JST-SH", + "RGB LED", + "USB-C", + "WiFi", + "microSD" + ], + "images": [ + "25134-Thing-Plus-RP2350-Feature.jpg" + ], + "mcu": "rp2350", + "product": "Thing Plus RP2350", + "thumbnail": "", + "url": "https://www.sparkfun.com/products/25134", + "vendor": "Sparkfun" +} diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/manifest.py b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/manifest.py new file mode 100644 index 0000000000000..4e38f09cdee4f --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/manifest.py @@ -0,0 +1,6 @@ +include("$(PORT_DIR)/boards/manifest.py") + +require("bundle-networking") + +# Bluetooth +require("aioble") diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/mpconfigboard.cmake b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/mpconfigboard.cmake new file mode 100644 index 0000000000000..12b51f32a213f --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/mpconfigboard.cmake @@ -0,0 +1,15 @@ +# cmake file for SparkFun Thing Plus RP2350 + +set(PICO_BOARD "sparkfun_thingplus_rp2350") +set(PICO_PLATFORM "rp2350") + +set(MICROPY_PY_LWIP ON) +set(MICROPY_PY_NETWORK_CYW43 ON) + +# Bluetooth +set(MICROPY_PY_BLUETOOTH ON) +set(MICROPY_BLUETOOTH_BTSTACK ON) +set(MICROPY_PY_BLUETOOTH_CYW43 ON) + +# Board specific version of the frozen manifest +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/mpconfigboard.h new file mode 100644 index 0000000000000..f86faa761987b --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/mpconfigboard.h @@ -0,0 +1,48 @@ +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "SparkFun Thing Plus RP2350" +#define MICROPY_HW_FLASH_STORAGE_BYTES (14 * 1024 * 1024) + +// Enable networking. +#define MICROPY_PY_NETWORK 1 +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "ThingPlusRP2350" + +// CYW43 driver configuration. +#define CYW43_USE_SPI (1) +#define CYW43_LWIP (1) +#define CYW43_GPIO (1) +#define CYW43_SPI_PIO (1) + +#define MICROPY_HW_PIN_EXT_COUNT CYW43_WL_GPIO_COUNT + +int mp_hal_is_pin_reserved(int n); +#define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i) + +#define MICROPY_HW_USB_VID (0x1B4F) +#define MICROPY_HW_USB_PID (0x0038) + +#define MICROPY_HW_I2C0_SDA (20) +#define MICROPY_HW_I2C0_SCL (21) + +#define MICROPY_HW_I2C1_SDA (6) +#define MICROPY_HW_I2C1_SCL (7) + +#define MICROPY_HW_SPI0_SCK (2) +#define MICROPY_HW_SPI0_MOSI (3) +#define MICROPY_HW_SPI0_MISO (4) + +#define MICROPY_HW_SPI1_SCK (26) +#define MICROPY_HW_SPI1_MOSI (27) +#define MICROPY_HW_SPI1_MISO (28) + +#define MICROPY_HW_UART0_TX (0) +#define MICROPY_HW_UART0_RX (1) +#define MICROPY_HW_UART0_CTS (18) +#define MICROPY_HW_UART0_RTS (19) + +#define MICROPY_HW_UART1_TX (4) +#define MICROPY_HW_UART1_RX (5) +#define MICROPY_HW_UART1_CTS (6) +#define MICROPY_HW_UART1_RTS (7) + +#define MICROPY_HW_PSRAM_CS_PIN (8) +#define MICROPY_HW_ENABLE_PSRAM (1) diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/pins.csv b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/pins.csv new file mode 100644 index 0000000000000..d11ab42cd5063 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/pins.csv @@ -0,0 +1,33 @@ +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 +GP26,GPIO26 +GP27,GPIO27 +GP28,GPIO28 +WL_GPIO0,EXT_GPIO0 +WL_GPIO1,EXT_GPIO1 +WL_GPIO2,EXT_GPIO2 +LED,EXT_GPIO0 +LED_RGB,GPIO14 +RGB_LED,GPIO14 +NEOPIXEL,GPIO14 From ca71df0081ea634105beebaea063cdca8ef6a9a3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Jan 2025 16:44:23 +1100 Subject: [PATCH 191/546] esp32/boards: Remove remaining "id" entries from board.json. This entry was originally used to override the firmware filenames generated by the build server, but these days all filenames should match the board directory name. So, remove the "id" entry and let the default be used. This is a follow-up to 1a99f74063569df0927e1ada0256059fcdef128c (these three boards were added after that change). Signed-off-by: Damien George --- ports/esp32/boards/ESP32_GENERIC_C6/board.json | 1 - ports/esp32/boards/UM_NANOS3/board.json | 1 - ports/esp32/boards/UM_OMGS3/board.json | 1 - 3 files changed, 3 deletions(-) diff --git a/ports/esp32/boards/ESP32_GENERIC_C6/board.json b/ports/esp32/boards/ESP32_GENERIC_C6/board.json index 963d9515be859..cf644f9038a6b 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C6/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_C6/board.json @@ -7,7 +7,6 @@ "BLE", "WiFi" ], - "id": "esp32c6", "images": [ "esp32c6_devkitmini.jpg" ], diff --git a/ports/esp32/boards/UM_NANOS3/board.json b/ports/esp32/boards/UM_NANOS3/board.json index b213a8ad6bd43..958f9adf514d7 100644 --- a/ports/esp32/boards/UM_NANOS3/board.json +++ b/ports/esp32/boards/UM_NANOS3/board.json @@ -13,7 +13,6 @@ "features_non_filterable": [ "TinyPICO Nano Compatible" ], - "id": "nanos3", "images": [ "unexpectedmaker_nanos3.jpg" ], diff --git a/ports/esp32/boards/UM_OMGS3/board.json b/ports/esp32/boards/UM_OMGS3/board.json index e4cb873914885..7e976ad63034e 100644 --- a/ports/esp32/boards/UM_OMGS3/board.json +++ b/ports/esp32/boards/UM_OMGS3/board.json @@ -13,7 +13,6 @@ "features_non_filterable": [ "I2C BAT Fuel Gauge" ], - "id": "omgs3", "images": [ "unexpectedmaker_omgs3.jpg" ], From d533c9067a3a2709d56304944e5142ff71830c8d Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Jan 2025 17:17:22 +1100 Subject: [PATCH 192/546] tools/autobuild: Don't allow a board to change its ID. All board IDs are now the board directory name. Signed-off-by: Damien George --- tools/autobuild/build-downloads.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/autobuild/build-downloads.py b/tools/autobuild/build-downloads.py index c03d98aa5dea4..d6f6d0324691c 100755 --- a/tools/autobuild/build-downloads.py +++ b/tools/autobuild/build-downloads.py @@ -65,9 +65,8 @@ def main(repo_path, output_path): ) sys.exit(1) - # Use "id" if specified, otherwise default to board dir (e.g. "PYBV11"). - # We allow boards to override ID for the historical build names. - blob["id"] = blob.get("id", os.path.basename(board_dir)) + # The ID of a board is the board directory (e.g. "PYBV11"). + blob["id"] = os.path.basename(board_dir) # Check for duplicate board IDs. if blob["id"] in board_ids: From 0950f65ac48bb8315a9ae51be0d4099432cad548 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 6 Sep 2024 15:50:35 +0200 Subject: [PATCH 193/546] tools/pyboard.py: Wait a bit before accessing the PTY serial port. Some PTY targets, namely `NETDUINO2` and `MICROBIT` under Qemu, take a bit more time to present a REPL than usual. The pyboard tool is a bit too impatient and would bail out before any of those targets had a chance to respond to the raw REPL request. Co-authored-by: Damien George Signed-off-by: Alessandro Gatti --- tools/pyboard.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/pyboard.py b/tools/pyboard.py index 8459bcf655683..dac51e7d6b896 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -244,6 +244,8 @@ def __init__(self, cmd): self.close() sys.exit(1) pty = m.group() + # Compensate for some boards taking a bit longer to start + time.sleep(0.1) # rtscts, dsrdtr params are to workaround pyserial bug: # http://stackoverflow.com/questions/34831131/pyserial-does-not-play-well-with-virtual-port self.serial = serial.Serial(pty, interCharTimeout=1, rtscts=True, dsrdtr=True) From 1232a83555968e04ec8e2ceb0d49435aa18de9f0 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 6 Sep 2024 18:40:34 +0200 Subject: [PATCH 194/546] qemu/mcu/arm: Dump exception cause and registers on machine error. When a CPU exception is raised when emulating a Thumb-capable processor, the default exception handler would simply enter in an endless loop without providing any further information. This commit adds a more complete exception handler that dumps to STDOUT the exception cause and the status of the registers at the moment of the exception. Signed-off-by: Alessandro Gatti --- ports/qemu/Makefile | 1 + ports/qemu/mcu/arm/errorhandler.c | 176 ++++++++++++++++++++++++++++++ ports/qemu/mcu/arm/startup.c | 32 ++++-- 3 files changed, 199 insertions(+), 10 deletions(-) create mode 100644 ports/qemu/mcu/arm/errorhandler.c diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index b792ee8ca9c5b..f555ca50deaf5 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -44,6 +44,7 @@ LDFLAGS += -nostdlib LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) SRC_C += \ + mcu/arm/errorhandler.c \ mcu/arm/startup.c \ shared/runtime/semihosting_arm.c \ diff --git a/ports/qemu/mcu/arm/errorhandler.c b/ports/qemu/mcu/arm/errorhandler.c new file mode 100644 index 0000000000000..a0e7ef930f0b8 --- /dev/null +++ b/ports/qemu/mcu/arm/errorhandler.c @@ -0,0 +1,176 @@ +/* + * 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/runtime.h" + +#if defined(__ARM_ARCH_ISA_THUMB) + +typedef enum _exception_kind_t { + RESET = 1, + NMI = 2, + HARD_FAULT = 3, + MEM_MANAGE = 4, + BUS_FAULT = 5, + USAGE_FAULT = 6, + SV_CALL = 11, + DEBUG_MONITOR = 12, + PENDING_SV = 13, + SYSTEM_TICK = 14, +} exception_kind_t; + +static const char *EXCEPTION_NAMES_TABLE[] = { + "Reserved", + "Reset", + "NMI", + "HardFault", + "MemManage", + "BusFault", + "UsageFault", + "SVCall", + "DebugMonitor", + "PendSV", + "SysTick", + "External interrupt" +}; + +// R0-R15, PSR, Kind +uintptr_t registers_copy[18] = { 0 }; + +__attribute__((naked)) NORETURN void exception_handler(uintptr_t kind) { + // Save registers + __asm volatile ( + "ldr r1, =registers_copy \n" + "str r0, [r1, #68] \n" // Kind + "ldr r0, [sp, #0] \n" // R0 + "str r0, [r1, #0] \n" + "ldr r0, [sp, #4] \n" // R1 + "str r0, [r1, #4] \n" + "ldr r0, [sp, #8] \n" // R2 + "str r0, [r1, #8] \n" + "ldr r0, [sp, #12] \n" // R3 + "str r0, [r1, #12] \n" + "str r4, [r1, #16] \n" + "str r5, [r1, #20] \n" + "str r6, [r1, #24] \n" + "str r7, [r1, #28] \n" + "mov r0, r8 \n" + "str r0, [r1, #32] \n" + "mov r0, r9 \n" + "str r0, [r1, #36] \n" + "mov r0, r10 \n" + "str r0, [r1, #40] \n" + "mov r0, r11 \n" + "str r0, [r1, #44] \n" + "ldr r0, [sp, #16] \n" // R12 + "str r0, [r1, #48] \n" + "mov r0, sp \n" + "sub r0, #32 \n" + "str r0, [r1, #52] \n" + "ldr r0, [sp, #20] \n" // R14 + "str r0, [r1, #56] \n" + "ldr r0, [sp, #24] \n" // R15 + "str r0, [r1, #60] \n" + "ldr r0, [sp, #28] \n" // xPSR + "str r0, [r1, #64] \n" + : + : + : "memory" + ); + + uintptr_t saved_kind = registers_copy[17]; + + switch (saved_kind) { + case RESET: + case NMI: + case HARD_FAULT: + case MEM_MANAGE: + case BUS_FAULT: + case USAGE_FAULT: + case SV_CALL: + case DEBUG_MONITOR: + case PENDING_SV: + case SYSTEM_TICK: + printf(EXCEPTION_NAMES_TABLE[saved_kind]); + break; + default: + if (saved_kind >= 16) { + printf("%s %d", EXCEPTION_NAMES_TABLE[11], saved_kind - 16); + } else { + printf(EXCEPTION_NAMES_TABLE[0]); + } + break; + } + printf(" exception caught:\n"); + printf("R0: %08X R1: %08X R2: %08X R3: %08X\n", registers_copy[0], registers_copy[1], registers_copy[2], registers_copy[3]); + printf("R4: %08X R5: %08X R6: %08X R7: %08X\n", registers_copy[4], registers_copy[5], registers_copy[6], registers_copy[7]); + printf("R8: %08X R9: %08X R10: %08X R11: %08X\n", registers_copy[8], registers_copy[9], registers_copy[10], registers_copy[11]); + printf("R12: %08X R13: %08X R14: %08X R15: %08X\n", registers_copy[12], registers_copy[13], registers_copy[14], registers_copy[15]); + printf("xPSR: %08X\n", registers_copy[16]); + + for (;;) {} +} + +__attribute__((naked)) NORETURN void NMI_Handler(void) { + exception_handler(NMI); +} + +__attribute__((naked)) NORETURN void HardFault_Handler(void) { + exception_handler(HARD_FAULT); +} + +__attribute__((naked)) NORETURN void MemManage_Handler(void) { + exception_handler(MEM_MANAGE); +} + +__attribute__((naked)) NORETURN void BusFault_Handler(void) { + exception_handler(BUS_FAULT); +} + +__attribute__((naked)) NORETURN void UsageFault_Handler(void) { + exception_handler(USAGE_FAULT); +} + +__attribute__((naked)) NORETURN void SVC_Handler(void) { + exception_handler(SV_CALL); +} + +__attribute__((naked)) NORETURN void DebugMon_Handler(void) { + exception_handler(DEBUG_MONITOR); +} + +__attribute__((naked)) NORETURN void PendSV_Handler(void) { + exception_handler(PENDING_SV); +} + +__attribute__((naked)) NORETURN void SysTick_Handler(void) { + exception_handler(SYSTEM_TICK); +} + +#endif diff --git a/ports/qemu/mcu/arm/startup.c b/ports/qemu/mcu/arm/startup.c index 118a5b8006f15..dbb75e3392287 100644 --- a/ports/qemu/mcu/arm/startup.c +++ b/ports/qemu/mcu/arm/startup.c @@ -28,6 +28,8 @@ #include #include +#include "py/runtime.h" + #include "shared/runtime/semihosting_arm.h" #include "uart.h" @@ -50,7 +52,7 @@ __attribute__((naked)) void Reset_Handler(void) { _start(); } -void Default_Handler(void) { +NORETURN void Default_Handler(void) { for (;;) { } } @@ -74,25 +76,35 @@ __attribute__((naked, section(".isr_vector"))) void isr_vector(void) { #elif defined(__ARM_ARCH_ISA_THUMB) +extern void NMI_Handler(void); +extern void HardFault_Handler(void); +extern void MemManage_Handler(void); +extern void BusFault_Handler(void); +extern void UsageFault_Handler(void); +extern void SVC_Handler(void); +extern void DebugMon_Handler(void); +extern void PendSV_Handler(void); +extern void SysTick_Handler(void); + // ARM architecture with Thumb-only ISA. const uint32_t isr_vector[] __attribute__((section(".isr_vector"))) = { (uint32_t)&_estack, (uint32_t)&Reset_Handler, - (uint32_t)&Default_Handler, // NMI_Handler - (uint32_t)&Default_Handler, // HardFault_Handler - (uint32_t)&Default_Handler, // MemManage_Handler - (uint32_t)&Default_Handler, // BusFault_Handler - (uint32_t)&Default_Handler, // UsageFault_Handler + (uint32_t)&NMI_Handler, + (uint32_t)&HardFault_Handler, + (uint32_t)&MemManage_Handler, + (uint32_t)&BusFault_Handler, + (uint32_t)&UsageFault_Handler, 0, 0, 0, 0, - (uint32_t)&Default_Handler, // SVC_Handler - (uint32_t)&Default_Handler, // DebugMon_Handler + (uint32_t)&SVC_Handler, + (uint32_t)&DebugMon_Handler, 0, - (uint32_t)&Default_Handler, // PendSV_Handler - (uint32_t)&Default_Handler, // SysTick_Handler + (uint32_t)&PendSV_Handler, + (uint32_t)&SysTick_Handler, }; #endif From 3225c1bc66a3cf21c3fc48917123c24131bdbf8b Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 6 Sep 2024 20:32:04 +0200 Subject: [PATCH 195/546] qemu: Disable native emitter for the MICROBIT board. The Micro:Bit machine definition in Qemu has soft MMU support enabled, which is currently not compatible with the way MicroPython generates code that needs to call back into non-emitted code. As a stop-gap solution, the native code emitter for the MICROBIT board is turned off. Signed-off-by: Alessandro Gatti --- ports/qemu/Makefile | 4 ++++ ports/qemu/mpconfigport.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index f555ca50deaf5..1b77b1790598a 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -20,8 +20,12 @@ MICROPY_ROM_TEXT_COMPRESSION ?= 1 ifeq ($(QEMU_ARCH),arm) MICROPY_HEAP_SIZE ?= 122880 +ifeq ($(BOARD),MICROBIT) +FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_const.py'))" +else FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_asm_thumb.py', 'frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" endif +endif ifeq ($(QEMU_ARCH),riscv32) MICROPY_HEAP_SIZE ?= 122880 FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_asm_rv32.py', 'frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" diff --git a/ports/qemu/mpconfigport.h b/ports/qemu/mpconfigport.h index 8f55d284f93da..0a25df4d0ac10 100644 --- a/ports/qemu/mpconfigport.h +++ b/ports/qemu/mpconfigport.h @@ -34,8 +34,10 @@ #define MICROPY_EMIT_ARM (1) #define MICROPY_EMIT_INLINE_THUMB (1) #elif defined(__ARM_ARCH_ISA_THUMB) +#if !defined(QEMU_SOC_NRF51) #define MICROPY_EMIT_THUMB (1) #define MICROPY_EMIT_INLINE_THUMB (1) +#endif #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) #elif defined(__riscv) #define MICROPY_EMIT_RV32 (1) From e84c9abfc21f57fe93b4d9a05c1d123e3f333880 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 14 Jan 2025 02:26:16 +0100 Subject: [PATCH 196/546] qemu/Makefile: Increase GC heap size to 140KiB. This commit increases the GC heap size from 120KiB to 140KiB, as it is needed to make the full test suite pass on SABRELITE when ran through the armv6 native emitter. This is needed as the code output by the armv6 native emitter is limited to 4-bytes opcodes and thus takes more space than other ARM emitters. To keep things aligned, the RV32 port also got its heap size increased even though it is not needed on that platform right now. Signed-off-by: Alessandro Gatti --- ports/qemu/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index 1b77b1790598a..bf9addc6f3256 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -19,7 +19,7 @@ QSTR_DEFS = qstrdefsport.h MICROPY_ROM_TEXT_COMPRESSION ?= 1 ifeq ($(QEMU_ARCH),arm) -MICROPY_HEAP_SIZE ?= 122880 +MICROPY_HEAP_SIZE ?= 143360 ifeq ($(BOARD),MICROBIT) FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_const.py'))" else @@ -27,7 +27,7 @@ FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_asm_thum endif endif ifeq ($(QEMU_ARCH),riscv32) -MICROPY_HEAP_SIZE ?= 122880 +MICROPY_HEAP_SIZE ?= 143360 FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_asm_rv32.py', 'frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" endif From 928c71638cf13eb0b47aebb917913820b889403f Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 14 Jan 2025 01:09:40 +0100 Subject: [PATCH 197/546] py/asmarm: Fix locals address loading code generation with large imm. This commit fixes code generation for loading a local's address if its index is greater than 63. The old code blindly encoded the offset into an `ADD Rd, Rn, #imm` opcode, but only the lowest 8 bits would be put into the opcode itself. This commit instead generates a two-opcodes sequence, a constant load into R8, and then an `ADD Rd, Rn, R8` opcode. This fixes `tests/float/math_domain.py` for the qemu/SABRELITE board. Signed-off-by: Alessandro Gatti --- py/asmarm.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/py/asmarm.c b/py/asmarm.c index b20bdebde5b1a..a1c15d0fa602a 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -292,8 +292,15 @@ void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) { } void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num) { - // add rd, sp, #local_num*4 - emit_al(as, asm_arm_op_add_imm(rd, ASM_ARM_REG_SP, local_num << 2)); + if (local_num >= 0x40) { + // mov r8, #local_num*4 + // add rd, sp, r8 + asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, local_num << 2); + emit_al(as, asm_arm_op_add_reg(rd, ASM_ARM_REG_SP, ASM_ARM_REG_R8)); + } else { + // add rd, sp, #local_num*4 + emit_al(as, asm_arm_op_add_imm(rd, ASM_ARM_REG_SP, local_num << 2)); + } } void asm_arm_mov_reg_pcrel(asm_arm_t *as, uint reg_dest, uint label) { From c610199f2d679ae0c8860d149edfece4fc8d49e5 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 14 Jan 2025 01:28:20 +0100 Subject: [PATCH 198/546] py/asmarm: Fix halfword loads with larger offsets. This commit fixes code generation for loading halfwords using an offset greater than 255. The old code blindly encoded the offset into a `LDRH Rd, [Rn, #imm]` opcode, but only the lowest 8 bits would be put into the opcode itself. This commit instead generates a two-opcodes sequence, a constant load into R8, and then `LDRH Rd, [Rn, R8]`. This fixes `tests/extmod/vfs_rom.py` for the qemu/SABRELITE board. Signed-off-by: Alessandro Gatti --- py/asmarm.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/py/asmarm.c b/py/asmarm.c index a1c15d0fa602a..6fa751b32eb7c 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -344,8 +344,15 @@ void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn) { } void asm_arm_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { - // ldrh rd, [rn, #off] - emit_al(as, 0x1d000b0 | (rn << 16) | (rd << 12) | ((byte_offset & 0xf0) << 4) | (byte_offset & 0xf)); + if (byte_offset < 0x100) { + // ldrh rd, [rn, #off] + emit_al(as, 0x1d000b0 | (rn << 16) | (rd << 12) | ((byte_offset & 0xf0) << 4) | (byte_offset & 0xf)); + } else { + // mov r8, #off + // ldrh rd, [rn, r8] + asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, byte_offset); + emit_al(as, 0x19000b0 | (rn << 16) | (rd << 12) | ASM_ARM_REG_R8); + } } void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn) { From a46e84280774a358eb7419de2c07a1ab2868d853 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 14 Jan 2025 01:30:00 +0100 Subject: [PATCH 199/546] qemu/boards/SABRELITE.mk: Remove exception for omitted tests. This commit re-introduces `tests/extmod/vfs_rom.py` and `tests/float/math_domain.py` to the test suite, as the issues that made them fail have now been addressed. Signed-off-by: Alessandro Gatti --- ports/qemu/boards/SABRELITE.mk | 3 --- 1 file changed, 3 deletions(-) diff --git a/ports/qemu/boards/SABRELITE.mk b/ports/qemu/boards/SABRELITE.mk index 9875667830294..80ea08f12cf76 100644 --- a/ports/qemu/boards/SABRELITE.mk +++ b/ports/qemu/boards/SABRELITE.mk @@ -17,6 +17,3 @@ MPY_CROSS_FLAGS += -march=armv6 # These tests don't work on Cortex-A9, so exclude them. RUN_TESTS_ARGS += --exclude 'inlineasm/thumb/(asmbcc|asmbitops|asmconst|asmdiv|asmit|asmspecialregs).py' - -# These tests fail with via-mpy and the native (armv6) emitter, so exclude them. -RUN_TESTS_ARGS += --exclude 'extmod/vfs_rom.py|float/math_domain.py' From a82fc718a74a75d778e485548330414bad85f070 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 14 Jan 2025 09:40:16 +0100 Subject: [PATCH 200/546] stm32/mpconfigboard_common: Add MICROPY_HW_SPI_IS_STATIC macro. A board should make this return true if the specified SPI instances should not be deinitialized on soft-reboot. Signed-off-by: iabdalkader --- ports/stm32/mpconfigboard_common.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 5c66b804b2f58..e1c9c159ecf6f 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -187,6 +187,12 @@ #define MICROPY_HW_SPI_IS_RESERVED(spi_id) (false) #endif +// Function to determine if the given spi_id is static or not. +// Static SPI instances can be accessed by the user but are not deinit'd on soft reset. +#ifndef MICROPY_HW_SPI_IS_STATIC +#define MICROPY_HW_SPI_IS_STATIC(spi_id) (false) +#endif + // Function to determine if the given tim_id is reserved for system use or not. #ifndef MICROPY_HW_TIM_IS_RESERVED #define MICROPY_HW_TIM_IS_RESERVED(tim_id) (false) From 5fa960c71912b0c5dace6ccd60a8dba48dc9e2da Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 19 Dec 2024 10:58:21 +0100 Subject: [PATCH 201/546] stm32/spi: Retain the state of special SPI buses on soft reboot. Reserved and static SPI buses must remain initialized during a soft reboot as they may be used for SPI flash storage or XIP. Signed-off-by: iabdalkader --- ports/stm32/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/spi.c b/ports/stm32/spi.c index aa459119c146c..96dd170652e1e 100644 --- a/ports/stm32/spi.c +++ b/ports/stm32/spi.c @@ -548,7 +548,7 @@ void spi_deinit(const spi_t *spi_obj) { void spi_deinit_all(void) { for (int i = 0; i < MP_ARRAY_SIZE(spi_obj); i++) { const spi_t *spi = &spi_obj[i]; - if (spi->spi != NULL) { + if (spi->spi != NULL && !MICROPY_HW_SPI_IS_RESERVED(i + 1) && !MICROPY_HW_SPI_IS_STATIC(i + 1)) { spi_deinit(spi); } } From b79ceeca8fc006c14fb1be5c9be3962f09269a85 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 19 Dec 2024 11:22:01 +0100 Subject: [PATCH 202/546] stm32/boards: Reserve SPI bus when it's used for external flash storage. Reserve SPI flash bus used for storage or XIP. Signed-off-by: iabdalkader --- ports/stm32/boards/ADAFRUIT_F405_EXPRESS/mpconfigboard.h | 1 + ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h | 1 + ports/stm32/boards/MIKROE_QUAIL/mpconfigboard.h | 1 + ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.h | 3 +++ 4 files changed, 6 insertions(+) diff --git a/ports/stm32/boards/ADAFRUIT_F405_EXPRESS/mpconfigboard.h b/ports/stm32/boards/ADAFRUIT_F405_EXPRESS/mpconfigboard.h index a7bfc37df2994..c573f942a0a13 100644 --- a/ports/stm32/boards/ADAFRUIT_F405_EXPRESS/mpconfigboard.h +++ b/ports/stm32/boards/ADAFRUIT_F405_EXPRESS/mpconfigboard.h @@ -28,6 +28,7 @@ // External SPI Flash config #if !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE +#define MICROPY_HW_SPI_IS_RESERVED(id) (id == 1) // Reserve SPI flash bus. #define MICROPY_HW_SPIFLASH_SIZE_BITS (16 * 1024 * 1024) // 16 Mbit (2 MByte) #define MICROPY_HW_SPIFLASH_CS (MICROPY_HW_SPI1_NSS) diff --git a/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h b/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h index ffc53fa6fc5ec..9878b1533eedb 100644 --- a/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h +++ b/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h @@ -65,6 +65,7 @@ #define MICROPY_HW_SPI2_SCK (pyb_pin_FLASH_SCK) #define MICROPY_HW_SPI2_MISO (pyb_pin_FLASH_MISO) #define MICROPY_HW_SPI2_MOSI (pyb_pin_FLASH_MOSI) +#define MICROPY_HW_SPI_IS_RESERVED(id) (id == 2) // Reserve SPI flash bus. // USB config #define MICROPY_HW_USB_VBUS_DETECT_PIN (pyb_pin_USB_VBUS) diff --git a/ports/stm32/boards/MIKROE_QUAIL/mpconfigboard.h b/ports/stm32/boards/MIKROE_QUAIL/mpconfigboard.h index 4c3fbba052176..d7b21d801f8d0 100644 --- a/ports/stm32/boards/MIKROE_QUAIL/mpconfigboard.h +++ b/ports/stm32/boards/MIKROE_QUAIL/mpconfigboard.h @@ -71,6 +71,7 @@ // External SPI Flash config (Cypress S25FL164K) #if !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE +#define MICROPY_HW_SPI_IS_STATIC(id) (id == 3) // Shared with SPIFLASH. #define MICROPY_HW_SPIFLASH_SIZE_BITS (64 * 1024 * 1024) // 64 Mbit (8 MByte) #define MICROPY_HW_SPIFLASH_CS (pin_A13) diff --git a/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.h b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.h index 561c1a9f1516e..98d6dc6a0b83d 100644 --- a/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.h +++ b/ports/stm32/boards/WEACT_F411_BLACKPILL/mpconfigboard.h @@ -86,6 +86,9 @@ #define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1) #else +// Reserve SPI flash bus. +#define MICROPY_HW_SPI_IS_RESERVED(id) (id == 1) + // Disable internal filesystem to use spiflash. #define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) From 624bd48d2fd90fd9e6cd95b82b8caef4121cb1c7 Mon Sep 17 00:00:00 2001 From: peterhinch Date: Sun, 29 Jan 2023 18:09:51 +0000 Subject: [PATCH 203/546] docs/reference/isr_rules: Describe issue with hard ISRs and globals. Signed-off-by: Damien George --- docs/reference/isr_rules.rst | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/reference/isr_rules.rst b/docs/reference/isr_rules.rst index ea330acad3aaa..14010fb207fdd 100644 --- a/docs/reference/isr_rules.rst +++ b/docs/reference/isr_rules.rst @@ -170,11 +170,17 @@ is partially updated. When the ISR tries to read the object, a crash results. Be on rare, random occasions they can be hard to diagnose. There are ways to circumvent this issue, described in :ref:`Critical Sections ` below. -It is important to be clear about what constitutes the modification of an object. An alteration to a built-in type -such as a dictionary is problematic. Altering the contents of an array or bytearray is not. This is because bytes -or words are written as a single machine code instruction which is not interruptible: in the parlance of real time -programming the write is atomic. A user defined object might instantiate an integer, array or bytearray. It is valid -for both the main loop and the ISR to alter the contents of these. +It is important to be clear about what constitutes the modification of an object. Altering the contents of an array +or bytearray is safe. This is because bytes or words are written as a single machine code instruction which is not +interruptible: in the parlance of real time programming the write is atomic. The same is true of updating a +dictionary item because items are machine words, being integers or pointers to objects. A user defined object might +instantiate an array or bytearray. It is valid for both the main loop and the ISR to alter the contents of these. + +The hazard arises when the structure of an object is altered, notably in the case of dictionaries. Adding or deleting +keys can trigger a rehash. If a hard ISR runs while a rehash is in progress and attempts to access an item, a crash +may occur. Internally globals are implemented as a dictionary. Consequently the main program should create all +necessary globals before starting a process that generates hard interrupts. Application code should also avoid +deleting globals. MicroPython supports integers of arbitrary precision. Values between 2**30 -1 and -2**30 will be stored in a single machine word. Larger values are stored as Python objects. Consequently changes to long integers cannot From f2cd1a3db6d0e9f3cd0025a3167822356f8c1614 Mon Sep 17 00:00:00 2001 From: Glenn Strauss Date: Thu, 25 Jul 2024 03:45:09 -0400 Subject: [PATCH 204/546] lib/mbedtls: Update to mbedtls v3.6.2. Signed-off-by: Glenn Strauss --- extmod/extmod.cmake | 1 + extmod/extmod.mk | 1 + extmod/mbedtls/mbedtls_config_common.h | 1 + lib/mbedtls | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index 154931a664477..532ce83f9075f 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -219,6 +219,7 @@ if(MICROPY_SSL_MBEDTLS) ${MICROPY_LIB_MBEDTLS_DIR}/library/pkcs12.c ${MICROPY_LIB_MBEDTLS_DIR}/library/pkcs5.c ${MICROPY_LIB_MBEDTLS_DIR}/library/pkparse.c + ${MICROPY_LIB_MBEDTLS_DIR}/library/pk_ecc.c ${MICROPY_LIB_MBEDTLS_DIR}/library/pk_wrap.c ${MICROPY_LIB_MBEDTLS_DIR}/library/pkwrite.c ${MICROPY_LIB_MBEDTLS_DIR}/library/platform.c diff --git a/extmod/extmod.mk b/extmod/extmod.mk index c8bc74d268e77..6d54ae2222e74 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -294,6 +294,7 @@ SRC_THIRDPARTY_C += $(addprefix $(MBEDTLS_DIR)/library/,\ pkcs12.c \ pkcs5.c \ pkparse.c \ + pk_ecc.c \ pk_wrap.c \ pkwrite.c \ platform.c \ diff --git a/extmod/mbedtls/mbedtls_config_common.h b/extmod/mbedtls/mbedtls_config_common.h index 4028bdf56d29b..6ea8540af9925 100644 --- a/extmod/mbedtls/mbedtls_config_common.h +++ b/extmod/mbedtls/mbedtls_config_common.h @@ -79,6 +79,7 @@ #define MBEDTLS_OID_C #define MBEDTLS_PKCS5_C #define MBEDTLS_PK_C +#define MBEDTLS_PK_HAVE_ECC_KEYS #define MBEDTLS_PK_PARSE_C #define MBEDTLS_PLATFORM_C #define MBEDTLS_RSA_C diff --git a/lib/mbedtls b/lib/mbedtls index edb8fec988208..107ea89daaefb 160000 --- a/lib/mbedtls +++ b/lib/mbedtls @@ -1 +1 @@ -Subproject commit edb8fec9882084344a314368ac7fd957a187519c +Subproject commit 107ea89daaefb9867ea9121002fbbdf926780e98 From 8e2da5a519c3c68f0e9a04613e9d3e2a48ec44bb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Oct 2024 14:53:37 +1100 Subject: [PATCH 205/546] stm32/boards: Support 'FDCAN' in board pin CSVs. Previously micros with the 'FDCAN' peripheral (as opposed to the older 'CAN' peripheral) needed to rename these pins in the CSVs for the CAN driver to work. The following CSVs in MicroPython still had FDCAN in them: $ rg -t csv -l FDCAN boards boards/stm32h7b3_af.csv boards/stm32h743_af.csv boards/stm32h573_af.csv boards/stm32h723_af.csv boards/stm32g0b1_af.csv Confirmed that this allows CAN to work on NUCLEO_H723ZG board, and that at least one board based on each of the other chips can still compile. Some of these boards could possibly have MICROPY_HW_ENABLE_CAN set and work, now. Signed-off-by: Angus Gratton --- ports/stm32/boards/make-pins.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/stm32/boards/make-pins.py b/ports/stm32/boards/make-pins.py index 8fb442153fad9..1b89fd641542a 100755 --- a/ports/stm32/boards/make-pins.py +++ b/ports/stm32/boards/make-pins.py @@ -122,6 +122,9 @@ def add_af(self, af_idx, af_name, af): if af_ext: af_pin = "EXT" + af_pin + # Special case: FDCAN peripheral is named CAN in MicroPython, same as bxCAN + af_fn = af_fn.replace("FDCAN", "CAN") + af_supported = af_fn in SUPPORTED_AF and af_pin in SUPPORTED_AF[af_fn] self._afs.append(PinAf(af_idx, af_fn, af_unit, af_pin, af_supported, af_name)) From 1d8943ac7b57b89efa5400d966957e6613a7604d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 8 Jan 2025 12:05:45 +1100 Subject: [PATCH 206/546] stm32/pyb_can: Fix CAN-FD BRS baud initialisation. Was initialising using the Classic CAN bs1/bs2 value, incorrectly. Signed-off-by: Angus Gratton --- ports/stm32/pyb_can.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index 563758831cc5e..dc05f965d81a8 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -269,8 +269,8 @@ static mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp // Set BRS bit timings. self->can.Init.DataPrescaler = args[ARG_brs_prescaler].u_int; self->can.Init.DataSyncJumpWidth = args[ARG_brs_sjw].u_int; - self->can.Init.DataTimeSeg1 = args[ARG_bs1].u_int; // DataTimeSeg1 = Propagation_segment + Phase_segment_1 - self->can.Init.DataTimeSeg2 = args[ARG_bs2].u_int; + self->can.Init.DataTimeSeg1 = args[ARG_brs_bs1].u_int; // DataTimeSeg1 = Propagation_segment + Phase_segment_1 + self->can.Init.DataTimeSeg2 = args[ARG_brs_bs2].u_int; #else // Init filter banks for classic CAN. can2_start_bank = args[ARG_num_filter_banks].u_int; From e8d3df51dc036337803fbe047fff77266b3b69f4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 8 Jan 2025 12:07:41 +1100 Subject: [PATCH 207/546] stm32/pyb_can: Make pyb.CAN baud calculation a little more forgiving. Not every baudrate or sample point combination has an exact match, but getting within 1% on sample point and .1% on baud rate should always be good enough. Because the search goes from shorter bit periods (lowest brp) and increases, the first match which meets this criteria should still mostly be the best available. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/library/pyb.CAN.rst | 21 ++++++++++++++------- ports/stm32/pyb_can.c | 12 ++++++++++-- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/docs/library/pyb.CAN.rst b/docs/library/pyb.CAN.rst index 57a85d54b7714..eb21d8223f26e 100644 --- a/docs/library/pyb.CAN.rst +++ b/docs/library/pyb.CAN.rst @@ -67,11 +67,17 @@ Methods :meth:`~CAN.restart()` can be used to leave the bus-off state - *baudrate* if a baudrate other than 0 is provided, this function will try to automatically calculate the CAN nominal bit time (overriding *prescaler*, *bs1* and *bs2*) that satisfies - both the baudrate and the desired *sample_point*. - - *sample_point* given in a percentage of the nominal bit time, the *sample_point* specifies the position - of the bit sample with respect to the whole nominal bit time. The default *sample_point* is 75%. + both the *baudrate* (within .1%) and the desired *sample_point* (to the nearest 1%). For more precise + control over the CAN timing, set the *prescaler*, *bs1* and *bs2* parameters directly. + - *sample_point* specifies the position of the bit sample with respect to the whole nominal bit time, + expressed as an integer percentage of the nominal bit time. The default *sample_point* is 75%. + This parameter is ignored unless *baudrate* is set. - *num_filter_banks* for classic CAN, this is the number of banks that will be assigned to CAN(1), the rest of the 28 are assigned to CAN(2). + + The remaining parameters are only present on boards with CAN FD support, and configure the optional CAN FD + Bit Rate Switch (BRS) feature: + - *brs_prescaler* is the value by which the CAN FD input clock is divided to generate the data bit time quanta. The prescaler can be a value between 1 and 32 inclusive. - *brs_sjw* is the resynchronisation jump width in units of time quanta for data bits; @@ -82,10 +88,11 @@ Methods it can be a value between 1 and 16 inclusive - *brs_baudrate* if a baudrate other than 0 is provided, this function will try to automatically calculate the CAN data bit time (overriding *brs_prescaler*, *brs_bs1* and *brs_bs2*) that satisfies - both the baudrate and the desired *brs_sample_point*. - - *brs_sample_point* given in a percentage of the data bit time, the *brs_sample_point* specifies the position - of the bit sample with respect to the whole data bit time. The default *brs_sample_point* is 75%. - + both the *brs_baudrate* (within .1%) and the desired *brs_sample_point* (to the nearest 1%). For more + precise control over the BRS timing, set the *brs_prescaler*, *brs_bs1* and *brs_bs2* parameters directly. + - *brs_sample_point* specifies the position of the bit sample with respect to the whole nominal bit time, + expressed as an integer percentage of the nominal bit time. The default *brs_sample_point* is 75%. + This parameter is ignored unless *brs_baudrate* is set. The time quanta tq is the basic unit of time for the CAN bus. tq is the CAN prescaler value divided by PCLK1 (the frequency of internal peripheral bus 1); diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index dc05f965d81a8..723ff6f9dfa3c 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -25,6 +25,7 @@ */ #include +#include #include "py/objarray.h" #include "py/runtime.h" @@ -199,12 +200,19 @@ static void pyb_can_get_bit_timing(mp_uint_t baudrate, mp_uint_t sample_point, uint32_t max_brp, uint32_t max_bs1, uint32_t max_bs2, uint32_t min_tseg, mp_int_t *bs1_out, mp_int_t *bs2_out, mp_int_t *prescaler_out) { uint32_t can_kern_clk = pyb_can_get_source_freq(); + mp_uint_t max_baud_error = baudrate / 1000; // Allow .1% deviation + const mp_uint_t MAX_SAMPLE_ERROR = 5; // round to nearest 1%, which is the param resolution + sample_point *= 10; // Calculate CAN bit timing. for (uint32_t brp = 1; brp < max_brp; brp++) { for (uint32_t bs1 = min_tseg; bs1 < max_bs1; bs1++) { for (uint32_t bs2 = min_tseg; bs2 < max_bs2; bs2++) { - if ((baudrate == (can_kern_clk / (brp * (1 + bs1 + bs2)))) && - ((sample_point * 10) == (((1 + bs1) * 1000) / (1 + bs1 + bs2)))) { + mp_int_t calc_baud = can_kern_clk / (brp * (1 + bs1 + bs2)); + mp_int_t calc_sample = ((1 + bs1) * 1000) / (1 + bs1 + bs2); + mp_int_t baud_err = baudrate - calc_baud; + mp_int_t sample_err = sample_point - calc_sample; + if (abs(baud_err) < max_baud_error && + abs(sample_err) < MAX_SAMPLE_ERROR) { *bs1_out = bs1; *bs2_out = bs2; *prescaler_out = brp; From 221a4ecf300d903d6043418a9980aa239dcd2879 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 8 Jan 2025 12:11:34 +1100 Subject: [PATCH 208/546] stm32/pyb_can: Include requested CAN baudrate in matching error. This is redundant for bxCAN, but for CAN-FD with BRS it's otherwise unclear which set of parameters (baudrate & sample_point or brs_baudrate & brs_sample_point) failed to match. This makes finding a valid combination extra annoying. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/stm32/pyb_can.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index 723ff6f9dfa3c..181b3173866c3 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -222,7 +222,7 @@ static void pyb_can_get_bit_timing(mp_uint_t baudrate, mp_uint_t sample_point, } } - mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("couldn't match baudrate and sample point")); + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("couldn't match baudrate %u and sample point %u"), baudrate, sample_point / 10); } // init(mode, prescaler=100, *, sjw=1, bs1=6, bs2=8) From 3a60f32c9db15060a95db69a3d2ca5df33a36f1b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Oct 2024 15:50:39 +1100 Subject: [PATCH 209/546] stm32/can: Fix clearing filters on CAN3 (bxCAN). HAL argument is ignored for CAN1, CAN2 but needed for CAN3. Signed-off-by: Angus Gratton --- ports/stm32/can.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/can.c b/ports/stm32/can.c index 27d29207227ba..490faeba709a2 100644 --- a/ports/stm32/can.c +++ b/ports/stm32/can.c @@ -174,7 +174,7 @@ void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t bank) { filter.FilterActivation = DISABLE; filter.BankNumber = bank; - HAL_CAN_ConfigFilter(NULL, &filter); + HAL_CAN_ConfigFilter(&self->can, &filter); } int can_receive(CAN_HandleTypeDef *can, int fifo, CanRxMsgTypeDef *msg, uint8_t *data, uint32_t timeout_ms) { From 21b7bd9f44a1e9dff2881b8aa1c72f400d3c7c39 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 23 May 2024 17:46:47 +1000 Subject: [PATCH 210/546] stm32/fdcan: Fix extended CAN ID filtering for stm32g4. The memory bank addresses used for these are independent, can (and must) enable both. Also looks like no need to shrink these if FDCAN2 is added, the Reference Manual is a bit unclear but looks like the peripheral's RAM multiplies out for each additional controller. Signed-off-by: Angus Gratton --- ports/stm32/fdcan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/fdcan.c b/ports/stm32/fdcan.c index 26a8860988418..04d8135c081b3 100644 --- a/ports/stm32/fdcan.c +++ b/ports/stm32/fdcan.c @@ -86,8 +86,8 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ init->DataSyncJumpWidth = 1; init->DataTimeSeg1 = 1; init->DataTimeSeg2 = 1; - init->StdFiltersNbr = 28; // /2 ? if FDCAN2 is used !!? - init->ExtFiltersNbr = 0; // Not used + init->StdFiltersNbr = 28; + init->ExtFiltersNbr = 8; init->TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; #elif defined(STM32H7) // The dedicated FDCAN RAM is 2560 32-bit words and shared between the FDCAN instances. From 05eb1d8e1535b310712a532c254e28161694a9b9 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 13 Aug 2024 09:48:11 +1000 Subject: [PATCH 211/546] stm32/boards/ARDUINO_NICLA_VISION: Fix CAN pin assignment. The only STM32H747 pins with CAN function that are also broken out on the board are PB8 and PB9. Signed-off-by: Angus Gratton --- ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h index 7bb75091e8bdf..c845bc91f9712 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h @@ -186,8 +186,8 @@ extern struct _spi_bdev_t spi_bdev; // FDCAN bus #define MICROPY_HW_CAN1_NAME "FDCAN1" -#define MICROPY_HW_CAN1_TX (pin_A10) -#define MICROPY_HW_CAN1_RX (pin_A9) +#define MICROPY_HW_CAN1_TX (pin_B9) +#define MICROPY_HW_CAN1_RX (pin_B8) #define MICROPY_HW_CAN_IS_RESERVED(id) (id != PYB_CAN_1) // LEDs From b6649b922e0ff7fa263b05d560f4c11a3910d1d9 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 16 Jan 2025 13:47:50 +0100 Subject: [PATCH 212/546] stm32/boards: Update Arduino boards to reserve timers and fix USB PID. Reserve timers used for the camera, and fix USB PID because 0x055F is reserved for MicroPython. Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h | 2 +- ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h | 3 ++- ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h index 99b92a3793f03..fdf3128a3606d 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h @@ -30,6 +30,7 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_HW_ENABLE_MMCARD (0) #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) +#define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1) // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) @@ -42,7 +43,6 @@ void GIGA_board_startup(void); #define MICROPY_BOARD_EARLY_INIT GIGA_board_early_init void GIGA_board_early_init(void); -#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) #define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) GIGA_board_enter_bootloader() void GIGA_board_enter_bootloader(void); diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h index c845bc91f9712..505f8b6890169 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h @@ -30,6 +30,7 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_HW_ENABLE_MMCARD (0) #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) +#define MICROPY_HW_TIM_IS_RESERVED(id) (id == 3) // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) @@ -225,7 +226,7 @@ extern struct _spi_bdev_t spi_bdev; #define MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE (3000000) #define MICROPY_HW_USB_VID 0x2341 -#define MICROPY_HW_USB_PID 0x045F +#define MICROPY_HW_USB_PID 0x055F #define MICROPY_HW_USB_PID_CDC_MSC (MICROPY_HW_USB_PID) #define MICROPY_HW_USB_PID_CDC_HID (MICROPY_HW_USB_PID) #define MICROPY_HW_USB_PID_CDC (MICROPY_HW_USB_PID) diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h index 6b77b16093fb4..900bf18673728 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h @@ -30,6 +30,7 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENABLE_SDCARD (1) #define MICROPY_HW_ENABLE_MMCARD (0) #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) +#define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1) // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) From d89e71e6c08b7dff21967868887f4503fc8ac5aa Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 7 Jan 2025 14:19:11 +1100 Subject: [PATCH 213/546] tools/autobuild,esp32: Template the generation of esp32 port deploy.md. Allows two source files (ports/esp32/boards/deploy.md and deploy_nativeusb.md for boards with only native USB) for all esp32 installation steps, with templated chip name and flash offset inserted via string formatting. The new files add more text to explain the esptool.py port auto-detection, remove the unnecessary -z feature (already enabled by default), and add a bit of troubleshooting and port detection info. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/README.md | 9 ++++ .../boards/ARDUINO_NANO_ESP32/board.json | 3 ++ .../esp32/boards/ARDUINO_NANO_ESP32/deploy.md | 2 + ports/esp32/boards/ESP32_GENERIC/board.json | 3 ++ .../esp32/boards/ESP32_GENERIC_C3/board.json | 5 +- .../esp32/boards/ESP32_GENERIC_C6/board.json | 5 +- .../esp32/boards/ESP32_GENERIC_S2/board.json | 5 +- .../esp32/boards/ESP32_GENERIC_S3/board.json | 5 +- .../boards/LILYGO_TTGO_LORA32/board.json | 3 ++ ports/esp32/boards/LOLIN_C3_MINI/board.json | 5 +- ports/esp32/boards/LOLIN_S2_MINI/board.json | 5 +- ports/esp32/boards/LOLIN_S2_PICO/board.json | 5 +- ports/esp32/boards/M5STACK_ATOM/board.json | 3 ++ .../boards/M5STACK_ATOMS3_LITE/board.json | 5 +- .../boards/M5STACK_ATOMS3_LITE/deploy.md | 22 -------- ports/esp32/boards/M5STACK_NANOC6/board.json | 5 +- .../boards/M5STACK_NANOC6/deploy_nanoc6.md | 18 ------- .../esp32/boards/OLIMEX_ESP32_EVB/board.json | 3 ++ .../esp32/boards/OLIMEX_ESP32_POE/board.json | 3 ++ ports/esp32/boards/SIL_WESP32/board.json | 3 ++ ports/esp32/boards/UM_FEATHERS2/board.json | 6 ++- ports/esp32/boards/UM_FEATHERS2/deploy.md | 50 ----------------- ports/esp32/boards/UM_FEATHERS2NEO/board.json | 6 ++- ports/esp32/boards/UM_FEATHERS2NEO/deploy.md | 50 ----------------- ports/esp32/boards/UM_FEATHERS3/board.json | 6 ++- ports/esp32/boards/UM_FEATHERS3/deploy.md | 52 ------------------ ports/esp32/boards/UM_FEATHERS3NEO/board.json | 6 ++- ports/esp32/boards/UM_FEATHERS3NEO/deploy.md | 52 ------------------ ports/esp32/boards/UM_NANOS3/board.json | 6 ++- ports/esp32/boards/UM_NANOS3/deploy.md | 53 ------------------- .../boards/UM_NANOS3/deploy_flashmode.md | 9 ++++ ports/esp32/boards/UM_OMGS3/board.json | 6 ++- ports/esp32/boards/UM_OMGS3/deploy.md | 53 ------------------- .../esp32/boards/UM_OMGS3/deploy_flashmode.md | 8 +++ ports/esp32/boards/UM_PROS3/board.json | 6 ++- ports/esp32/boards/UM_PROS3/deploy.md | 52 ------------------ .../esp32/boards/UM_RGBTOUCH_MINI/board.json | 6 ++- ports/esp32/boards/UM_RGBTOUCH_MINI/deploy.md | 52 ------------------ ports/esp32/boards/UM_TINYC6/board.json | 6 ++- .../boards/UM_TINYC6/deploy_flashmode.md | 2 + ports/esp32/boards/UM_TINYC6/deploy_tinyc6.md | 18 ------- ports/esp32/boards/UM_TINYPICO/board.json | 5 +- ports/esp32/boards/UM_TINYPICO/deploy.md | 46 ---------------- ports/esp32/boards/UM_TINYS2/board.json | 6 ++- ports/esp32/boards/UM_TINYS2/deploy.md | 50 ----------------- ports/esp32/boards/UM_TINYS3/board.json | 6 ++- ports/esp32/boards/UM_TINYS3/deploy.md | 52 ------------------ ports/esp32/boards/UM_TINYWATCHS3/board.json | 6 ++- ports/esp32/boards/UM_TINYWATCHS3/deploy.md | 52 ------------------ ports/esp32/boards/deploy.md | 48 +++++++++++++++-- ports/esp32/boards/deploy_c3.md | 14 ----- ports/esp32/boards/deploy_c6.md | 14 ----- ports/esp32/boards/deploy_flashmode.md | 9 ++++ ports/esp32/boards/deploy_nativeusb.md | 51 ++++++++++++++++++ ports/esp32/boards/deploy_s2.md | 14 ----- ports/esp32/boards/deploy_s3.md | 14 ----- tools/autobuild/build-downloads.py | 13 ++++- 57 files changed, 267 insertions(+), 755 deletions(-) delete mode 100644 ports/esp32/boards/M5STACK_ATOMS3_LITE/deploy.md delete mode 100644 ports/esp32/boards/M5STACK_NANOC6/deploy_nanoc6.md delete mode 100644 ports/esp32/boards/UM_FEATHERS2/deploy.md delete mode 100644 ports/esp32/boards/UM_FEATHERS2NEO/deploy.md delete mode 100644 ports/esp32/boards/UM_FEATHERS3/deploy.md delete mode 100644 ports/esp32/boards/UM_FEATHERS3NEO/deploy.md delete mode 100644 ports/esp32/boards/UM_NANOS3/deploy.md create mode 100644 ports/esp32/boards/UM_NANOS3/deploy_flashmode.md delete mode 100644 ports/esp32/boards/UM_OMGS3/deploy.md create mode 100644 ports/esp32/boards/UM_OMGS3/deploy_flashmode.md delete mode 100644 ports/esp32/boards/UM_PROS3/deploy.md delete mode 100644 ports/esp32/boards/UM_RGBTOUCH_MINI/deploy.md create mode 100644 ports/esp32/boards/UM_TINYC6/deploy_flashmode.md delete mode 100644 ports/esp32/boards/UM_TINYC6/deploy_tinyc6.md delete mode 100644 ports/esp32/boards/UM_TINYPICO/deploy.md delete mode 100644 ports/esp32/boards/UM_TINYS2/deploy.md delete mode 100644 ports/esp32/boards/UM_TINYS3/deploy.md delete mode 100644 ports/esp32/boards/UM_TINYWATCHS3/deploy.md delete mode 100644 ports/esp32/boards/deploy_c3.md delete mode 100644 ports/esp32/boards/deploy_c6.md create mode 100644 ports/esp32/boards/deploy_flashmode.md create mode 100644 ports/esp32/boards/deploy_nativeusb.md delete mode 100644 ports/esp32/boards/deploy_s2.md delete mode 100644 ports/esp32/boards/deploy_s3.md diff --git a/ports/esp32/README.md b/ports/esp32/README.md index a8bada7510f4e..bc24e5cd71ad6 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -229,6 +229,15 @@ files that configure ESP-IDF settings. Some standard `sdkconfig` files are provided in the `boards/` directory, like `boards/sdkconfig.ble`. You can also define custom ones in your board directory. +Deployment instructions usually invoke the `boards/deploy.md` file (for boards +with a USB/Serial converter connection), or the `boards/deploy_nativeusb.md` +file (for boards with only a native USB port connection). These files are +formatted for each board using template strings found in the `boards.json` +files. You can also include the common `boards/deploy_flashmode.md` file if you +have a board which requires manual resetting via the RESET and BOOT buttons. +Boards with unique flashing steps can include custom `deploy.md` file(s). +Existing `board.json` files contain examples of all of these combinations. + See existing board definitions for further examples of configuration. Configuration diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json index 77aa192959954..9d0016017fc6c 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json @@ -2,6 +2,9 @@ "deploy": [ "deploy.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/deploy.md b/ports/esp32/boards/ARDUINO_NANO_ESP32/deploy.md index bb32ba755932a..36836da9199f7 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/deploy.md +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/deploy.md @@ -11,3 +11,5 @@ Please note that the DFU bootloader comes factory flashed. Should you for any re entire flash, the DFU bootloader will have to be re-installed. Please follow the instructions [here](https://support.arduino.cc/hc/en-us/articles/9810414060188-Reset-the-Arduino-bootloader-on-the-Nano-ESP32) to do so. + +**Important** From the options below, download the `.app-bin` file for your board. diff --git a/ports/esp32/boards/ESP32_GENERIC/board.json b/ports/esp32/boards/ESP32_GENERIC/board.json index 130f6b88c8856..81c38a6ad48a7 100644 --- a/ports/esp32/boards/ESP32_GENERIC/board.json +++ b/ports/esp32/boards/ESP32_GENERIC/board.json @@ -2,6 +2,9 @@ "deploy": [ "../deploy.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/board.json b/ports/esp32/boards/ESP32_GENERIC_C3/board.json index 4a81d227a0224..fd20cb51bcb9d 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C3/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_C3/board.json @@ -1,7 +1,10 @@ { "deploy": [ - "../deploy_c3.md" + "../deploy.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/ESP32_GENERIC_C6/board.json b/ports/esp32/boards/ESP32_GENERIC_C6/board.json index cf644f9038a6b..7363333f86eb8 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C6/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_C6/board.json @@ -1,7 +1,10 @@ { "deploy": [ - "../deploy_c6.md" + "../deploy.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/ESP32_GENERIC_S2/board.json b/ports/esp32/boards/ESP32_GENERIC_S2/board.json index 6ebf16be1a21a..fde04d9c4b908 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S2/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_S2/board.json @@ -1,7 +1,10 @@ { "deploy": [ - "../deploy_s2.md" + "../deploy.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "External Flash", diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.json b/ports/esp32/boards/ESP32_GENERIC_S3/board.json index c9794dba86131..fd0c9edce09ec 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.json @@ -1,7 +1,10 @@ { "deploy": [ - "../deploy_s3.md" + "../deploy.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json index 525551df3b174..d68a9baaddaa6 100644 --- a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json @@ -2,6 +2,9 @@ "deploy": [ "../deploy.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/LOLIN_C3_MINI/board.json b/ports/esp32/boards/LOLIN_C3_MINI/board.json index c9b91641e5288..dd47862d9f74f 100644 --- a/ports/esp32/boards/LOLIN_C3_MINI/board.json +++ b/ports/esp32/boards/LOLIN_C3_MINI/board.json @@ -1,7 +1,10 @@ { "deploy": [ - "../deploy_c3.md" + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/LOLIN_S2_MINI/board.json b/ports/esp32/boards/LOLIN_S2_MINI/board.json index a45d0434d995d..a7098c9afa302 100644 --- a/ports/esp32/boards/LOLIN_S2_MINI/board.json +++ b/ports/esp32/boards/LOLIN_S2_MINI/board.json @@ -1,7 +1,10 @@ { "deploy": [ - "../deploy_s2.md" + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "External Flash", diff --git a/ports/esp32/boards/LOLIN_S2_PICO/board.json b/ports/esp32/boards/LOLIN_S2_PICO/board.json index 6c72e60f80546..724f47e1a9d08 100644 --- a/ports/esp32/boards/LOLIN_S2_PICO/board.json +++ b/ports/esp32/boards/LOLIN_S2_PICO/board.json @@ -1,7 +1,10 @@ { "deploy": [ - "../deploy_s2.md" + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "Display", diff --git a/ports/esp32/boards/M5STACK_ATOM/board.json b/ports/esp32/boards/M5STACK_ATOM/board.json index 4b6c09db3a7e8..3a1e7ce359c50 100644 --- a/ports/esp32/boards/M5STACK_ATOM/board.json +++ b/ports/esp32/boards/M5STACK_ATOM/board.json @@ -2,6 +2,9 @@ "deploy": [ "../deploy.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "External Flash", diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json index d00bb673be8a8..fe0e97f9f9ce6 100644 --- a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json @@ -1,7 +1,10 @@ { "deploy": [ - "deploy.md" + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "https://docs.m5stack.com/en/core/AtomS3%20Lite", "features": [ "BLE", diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/deploy.md b/ports/esp32/boards/M5STACK_ATOMS3_LITE/deploy.md deleted file mode 100644 index 9a20a6a43dc6b..0000000000000 --- a/ports/esp32/boards/M5STACK_ATOMS3_LITE/deploy.md +++ /dev/null @@ -1,22 +0,0 @@ -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_NANOC6/board.json b/ports/esp32/boards/M5STACK_NANOC6/board.json index 15654341b44dc..087851ae5ee0b 100644 --- a/ports/esp32/boards/M5STACK_NANOC6/board.json +++ b/ports/esp32/boards/M5STACK_NANOC6/board.json @@ -1,7 +1,10 @@ { "deploy": [ - "deploy_nanoc6.md" + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/M5STACK_NANOC6/deploy_nanoc6.md b/ports/esp32/boards/M5STACK_NANOC6/deploy_nanoc6.md deleted file mode 100644 index d8c212c5d2980..0000000000000 --- a/ports/esp32/boards/M5STACK_NANOC6/deploy_nanoc6.md +++ /dev/null @@ -1,18 +0,0 @@ -Program your board using the esptool.py program, found -[here](https://github.com/espressif/esptool). - -To put the NanoC6 into 'update mode', hold the button while connecting the USB -cable. It can be released after the connection is made. - -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 esp32c6 --port /dev/ttyUSB0 erase_flash -``` - -From then on program the firmware starting at address 0x0: - -```bash -esptool.py --chip esp32c6 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x0 M5STACK_NANOC6-20240602-v1.24.0.bin -``` diff --git a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json index de7a74d8a8faa..3eb9a5e0a71e5 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json +++ b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json @@ -2,6 +2,9 @@ "deploy": [ "../deploy.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json index e62b960ccd681..cda26178d75f2 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json +++ b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json @@ -2,6 +2,9 @@ "deploy": [ "../deploy.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/SIL_WESP32/board.json b/ports/esp32/boards/SIL_WESP32/board.json index 50dd2cc660d48..53a50f3286d23 100644 --- a/ports/esp32/boards/SIL_WESP32/board.json +++ b/ports/esp32/boards/SIL_WESP32/board.json @@ -2,6 +2,9 @@ "deploy": [ "../deploy.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/UM_FEATHERS2/board.json b/ports/esp32/boards/UM_FEATHERS2/board.json index 4de9a7d4f71c2..b6cacf8c5c784 100644 --- a/ports/esp32/boards/UM_FEATHERS2/board.json +++ b/ports/esp32/boards/UM_FEATHERS2/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy.md" + "../deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "Battery Charging", diff --git a/ports/esp32/boards/UM_FEATHERS2/deploy.md b/ports/esp32/boards/UM_FEATHERS2/deploy.md deleted file mode 100644 index 24bced3cda9b1..0000000000000 --- a/ports/esp32/boards/UM_FEATHERS2/deploy.md +++ /dev/null @@ -1,50 +0,0 @@ -Program your board using the esptool.py program, found [here](https://github.com/espressif/esptool). - -To flash or erase your FeatherS2, you have to first put it into download mode. -To do this, follow these steps: - -- Press and hold the [BOOT] button -- Press and release the [RESET] button -- Release the [BOOT] button - -Now the board is in download mode and the native USB will have enumerated as a serial device. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32s2 --port /dev/ttyACM0 erase_flash -``` - -### Mac -```bash -esptool.py --chip esp32s2 --port /dev/cu.usbmodem01 erase_flash -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s2 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options -below, then use the following command to program the firmware starting at address -0x1000, remembering to replace `feathers2-micropython-firmware-version.bin` with the -name of the firmware you just downloaded: - -### Linux -```bash -esptool.py --chip esp32s2 --port /dev/ttyACM0 write_flash -z 0x1000 feathers2-micropython-firmware-version.bin -``` - -### Mac -```bash -esptool.py --chip esp32s2 --port /dev/cu.usbmodem01 write_flash -z 0x1000 feathers2-micropython-firmware-version.bin -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s2 --port COM(X) write_flash -z 0x1000 feathers2-micropython-firmware-version.bin -``` diff --git a/ports/esp32/boards/UM_FEATHERS2NEO/board.json b/ports/esp32/boards/UM_FEATHERS2NEO/board.json index 5b79d3195a763..5b9a1935c9760 100644 --- a/ports/esp32/boards/UM_FEATHERS2NEO/board.json +++ b/ports/esp32/boards/UM_FEATHERS2NEO/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy.md" + "../deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "Battery Charging", diff --git a/ports/esp32/boards/UM_FEATHERS2NEO/deploy.md b/ports/esp32/boards/UM_FEATHERS2NEO/deploy.md deleted file mode 100644 index b1a116e8074c5..0000000000000 --- a/ports/esp32/boards/UM_FEATHERS2NEO/deploy.md +++ /dev/null @@ -1,50 +0,0 @@ -Program your board using the esptool.py program, found [here](https://github.com/espressif/esptool). - -To flash or erase your FeatherS2 Neo, you have to first put it into download mode. -To do this, follow these steps: - -- Press and hold the [BOOT] button -- Press and release the [RESET] button -- Release the [BOOT] button - -Now the board is in download mode and the native USB will have enumerated as a serial device. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32s2 --port /dev/ttyACM0 erase_flash -``` - -### Mac -```bash -esptool.py --chip esp32s2 --port /dev/cu.usbmodem01 erase_flash -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s2 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options below, -then use the following command to program the firmware starting at address 0x1000, -remembering to replace `feathers2neo-micropython-firmware-version.bin` with the name of -the firmware you just downloaded: - -### Linux -```bash -esptool.py --chip esp32s2 --port /dev/ttyACM0 write_flash -z 0x1000 feathers2neo-micropython-firmware-version.bin -``` - -### Mac -```bash -esptool.py --chip esp32s2 --port /dev/cu.usbmodem01 write_flash -z 0x1000 feathers2neo-micropython-firmware-version.bin -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s2 --port COM(X) write_flash -z 0x1000 feathers2-feathers2neo-firmware-version.bin -``` diff --git a/ports/esp32/boards/UM_FEATHERS3/board.json b/ports/esp32/boards/UM_FEATHERS3/board.json index 235d52a12d4fd..19d2eb105f3f9 100644 --- a/ports/esp32/boards/UM_FEATHERS3/board.json +++ b/ports/esp32/boards/UM_FEATHERS3/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy.md" + "../deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/UM_FEATHERS3/deploy.md b/ports/esp32/boards/UM_FEATHERS3/deploy.md deleted file mode 100644 index 3a6a21a522807..0000000000000 --- a/ports/esp32/boards/UM_FEATHERS3/deploy.md +++ /dev/null @@ -1,52 +0,0 @@ -Program your board using the latest version of the esptool.py program, found [here](https://github.com/espressif/esptool). - -To flash or erase your FeatherS3, you have to first put it into download mode. -To do this, follow these steps: - -- Press and hold the [BOOT] button -- Press and release the [RESET] button -- Release the [BOOT] button - -Now the board is in download mode and the native USB will have enumerated as a serial device. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 erase_flash -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options below, -then use the following command to program the firmware starting at address 0x0, -remembering to replace `feathers3-micropython-firmware-version.bin` with the name of -the firmware you just downloaded: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0x0 feathers3-micropython-firmware-version.bin -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 write_flash -z 0x0 feathers3-micropython-firmware-version.bin -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) write_flash -z 0x0 feathers3-micropython-firmware-version.bin -``` diff --git a/ports/esp32/boards/UM_FEATHERS3NEO/board.json b/ports/esp32/boards/UM_FEATHERS3NEO/board.json index 0531582007250..178219ce251ed 100644 --- a/ports/esp32/boards/UM_FEATHERS3NEO/board.json +++ b/ports/esp32/boards/UM_FEATHERS3NEO/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy.md" + "../deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/UM_FEATHERS3NEO/deploy.md b/ports/esp32/boards/UM_FEATHERS3NEO/deploy.md deleted file mode 100644 index ea9bb56dbfeb5..0000000000000 --- a/ports/esp32/boards/UM_FEATHERS3NEO/deploy.md +++ /dev/null @@ -1,52 +0,0 @@ -Program your board using the latest version of the esptool.py program, found [here](https://github.com/espressif/esptool). - -To flash or erase your FeatherS3 Neo, you have to first put it into download mode. -To do this, follow these steps: - -- Press and hold the [BOOT] button -- Press and release the [RESET] button -- Release the [BOOT] button - -Now the board is in download mode and the native USB will have enumerated as a serial device. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 erase_flash -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options below, -then use the following command to program the firmware starting at address 0x0, -remembering to replace `feathers3neo-micropython-firmware-version.bin` with the name of -the firmware you just downloaded: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0x0 feathers3neo-micropython-firmware-version.bin -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 write_flash -z 0x0 feathers3neo-micropython-firmware-version.bin -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) write_flash -z 0x0 feathers3neo-micropython-firmware-version.bin -``` diff --git a/ports/esp32/boards/UM_NANOS3/board.json b/ports/esp32/boards/UM_NANOS3/board.json index 958f9adf514d7..3da4b33c12e06 100644 --- a/ports/esp32/boards/UM_NANOS3/board.json +++ b/ports/esp32/boards/UM_NANOS3/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy.md" + "./deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "Battery Charging", diff --git a/ports/esp32/boards/UM_NANOS3/deploy.md b/ports/esp32/boards/UM_NANOS3/deploy.md deleted file mode 100644 index 4285b02753893..0000000000000 --- a/ports/esp32/boards/UM_NANOS3/deploy.md +++ /dev/null @@ -1,53 +0,0 @@ -Program your board using the latest version of the esptool.py program, found [here](https://github.com/espressif/esptool). - -To flash or erase your NANOS3, you have to first put it into download mode. -NANOS3 doesn't include buttons for RESET and IO0, which should be provided by adding external buttons via a carrier board or other method. -To put the NANOS3 into download, follow these steps: - -- Press and hold the [BOOT] button -- Press and release the [RESET] button -- Release the [BOOT] button - -Now the board is in download mode and the native USB will have enumerated as a serial device. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 erase_flash -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options below, -then use the following command to program the firmware starting at address 0x0, -remembering to replace `nanos3-micropython-firmware-version.bin` with the name of -the firmware you just downloaded: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0x0 nanos3-micropython-firmware-version.bin -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 write_flash -z 0x0 nanos3-micropython-firmware-version.bin -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) write_flash -z 0x0 nanos3-micropython-firmware-version.bin -``` diff --git a/ports/esp32/boards/UM_NANOS3/deploy_flashmode.md b/ports/esp32/boards/UM_NANOS3/deploy_flashmode.md new file mode 100644 index 0000000000000..b200970409f43 --- /dev/null +++ b/ports/esp32/boards/UM_NANOS3/deploy_flashmode.md @@ -0,0 +1,9 @@ +To flash or erase your NANOS3, you have to first put it into download mode. +NANOS3 doesn't include buttons for RESET and IO0, which should be provided by adding external buttons via a carrier board or other method. +To put the NANOS3 into download, follow these steps: + +- Press and hold the [BOOT] button +- Press and release the [RESET] button +- Release the [BOOT] button + +Now the board is in download mode and the native USB will have enumerated as a serial device. diff --git a/ports/esp32/boards/UM_OMGS3/board.json b/ports/esp32/boards/UM_OMGS3/board.json index 7e976ad63034e..4b3cd9b8faf34 100644 --- a/ports/esp32/boards/UM_OMGS3/board.json +++ b/ports/esp32/boards/UM_OMGS3/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy.md" + "./deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "Battery Charging", diff --git a/ports/esp32/boards/UM_OMGS3/deploy.md b/ports/esp32/boards/UM_OMGS3/deploy.md deleted file mode 100644 index 09f2ef8c2326e..0000000000000 --- a/ports/esp32/boards/UM_OMGS3/deploy.md +++ /dev/null @@ -1,53 +0,0 @@ -Program your board using the latest version of the esptool.py program, found [here](https://github.com/espressif/esptool). - -To flash or erase your OMGS3, you have to first put it into download mode. -OMGS3 doesn't include buttons for RESET and IO0, which should be provided by adding external buttons via a carrier board or other method. -To put the OMGS3 into download, follow these steps: - -- Press and hold the [BOOT] button -- Press and release the [RESET] button -- Release the [BOOT] button - -Now the board is in download mode and the native USB will have enumerated as a serial device. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 erase_flash -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options below, -then use the following command to program the firmware starting at address 0x0, -remembering to replace `omgs3-micropython-firmware-version.bin` with the name of -the firmware you just downloaded: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0x0 omgs3-micropython-firmware-version.bin -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 write_flash -z 0x0 omgs3-micropython-firmware-version.bin -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) write_flash -z 0x0 omgs3-micropython-firmware-version.bin -``` diff --git a/ports/esp32/boards/UM_OMGS3/deploy_flashmode.md b/ports/esp32/boards/UM_OMGS3/deploy_flashmode.md new file mode 100644 index 0000000000000..93e28d2275b55 --- /dev/null +++ b/ports/esp32/boards/UM_OMGS3/deploy_flashmode.md @@ -0,0 +1,8 @@ +To flash or erase your OMGS3, you have to first put it into download mode. + +OMGS3 doesn't include buttons for RESET and IO0, which should be provided by adding external buttons via a carrier board or other method. +To put the OMGS3 into download, follow these steps: + +- Press and hold the [BOOT] button +- Press and release the [RESET] button +- Release the [BOOT] button diff --git a/ports/esp32/boards/UM_PROS3/board.json b/ports/esp32/boards/UM_PROS3/board.json index 8efc4e5ab55a7..3d168b2505ace 100644 --- a/ports/esp32/boards/UM_PROS3/board.json +++ b/ports/esp32/boards/UM_PROS3/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy.md" + "../deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/UM_PROS3/deploy.md b/ports/esp32/boards/UM_PROS3/deploy.md deleted file mode 100644 index d35d7a02fe376..0000000000000 --- a/ports/esp32/boards/UM_PROS3/deploy.md +++ /dev/null @@ -1,52 +0,0 @@ -Program your board using the latest version of the esptool.py program, found [here](https://github.com/espressif/esptool). - -To flash or erase your ProS3, you have to first put it into download mode. -To do this, follow these steps: - -- Press and hold the [BOOT] button -- Press and release the [RESET] button -- Release the [BOOT] button - -Now the board is in download mode and the native USB will have enumerated as a serial device. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 erase_flash -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options below, -then use the following command to program the firmware starting at address 0x0, -remembering to replace `pros3-micropython-firmware-version.bin` with the name of -the firmware you just downloaded: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0x0 pros3-micropython-firmware-version.bin -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 write_flash -z 0x0 pros3-micropython-firmware-version.bin -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) write_flash -z 0x0 pros3-pros3-firmware-version.bin -``` diff --git a/ports/esp32/boards/UM_RGBTOUCH_MINI/board.json b/ports/esp32/boards/UM_RGBTOUCH_MINI/board.json index 6b3ec0f06733d..4e3940005ef59 100644 --- a/ports/esp32/boards/UM_RGBTOUCH_MINI/board.json +++ b/ports/esp32/boards/UM_RGBTOUCH_MINI/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy.md" + "../deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/UM_RGBTOUCH_MINI/deploy.md b/ports/esp32/boards/UM_RGBTOUCH_MINI/deploy.md deleted file mode 100644 index afe1ff1de874d..0000000000000 --- a/ports/esp32/boards/UM_RGBTOUCH_MINI/deploy.md +++ /dev/null @@ -1,52 +0,0 @@ -Program your board using the latest version of the esptool.py program, found [here](https://github.com/espressif/esptool). - -To flash or erase your RGB Touch Mini, you have to first put it into download mode. -To do this, follow these steps: - -- Press and hold the [BOOT] button -- Press and release the [RESET] button -- Release the [BOOT] button - -Now the board is in download mode and the native USB will have enumerated as a serial device. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 erase_flash -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options below, -then use the following command to program the firmware starting at address 0x0, -remembering to replace `rgbtouch_mini-micropython-firmware-version.bin` with the name of -the firmware you just downloaded: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0x0 rgbtouch_mini-micropython-firmware-version.bin -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 write_flash -z 0x0 rgbtouch_mini-micropython-firmware-version.bin -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) write_flash -z 0x0 rgbtouch_mini-micropython-firmware-version.bin -``` diff --git a/ports/esp32/boards/UM_TINYC6/board.json b/ports/esp32/boards/UM_TINYC6/board.json index 122b411a30487..7bf920d64c15f 100644 --- a/ports/esp32/boards/UM_TINYC6/board.json +++ b/ports/esp32/boards/UM_TINYC6/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy_tinyc6.md" + "deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "Battery Charging", diff --git a/ports/esp32/boards/UM_TINYC6/deploy_flashmode.md b/ports/esp32/boards/UM_TINYC6/deploy_flashmode.md new file mode 100644 index 0000000000000..5dc696a24f983 --- /dev/null +++ b/ports/esp32/boards/UM_TINYC6/deploy_flashmode.md @@ -0,0 +1,2 @@ +To put the TinyC6 into 'download mode', hold the _BOOT_ button while connecting +the USB cable. It can be released after the connection is made. diff --git a/ports/esp32/boards/UM_TINYC6/deploy_tinyc6.md b/ports/esp32/boards/UM_TINYC6/deploy_tinyc6.md deleted file mode 100644 index 4e0603a10772a..0000000000000 --- a/ports/esp32/boards/UM_TINYC6/deploy_tinyc6.md +++ /dev/null @@ -1,18 +0,0 @@ -Program your board using the esptool.py program, found -[here](https://github.com/espressif/esptool). - -To put the TinyC6 into 'download mode', hold the _BOOT_ button while connecting -the USB cable. It can be released after the connection is made. - -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 esp32c6 --port /dev/ttyUSB0 erase_flash -``` - -From then on program the firmware starting at address 0x0: - -```bash -esptool.py --chip esp32c6 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x0 UM_TINYC6-20240602-v1.24.0.bin -``` diff --git a/ports/esp32/boards/UM_TINYPICO/board.json b/ports/esp32/boards/UM_TINYPICO/board.json index 5ec7913c42ec6..06584832bb013 100644 --- a/ports/esp32/boards/UM_TINYPICO/board.json +++ b/ports/esp32/boards/UM_TINYPICO/board.json @@ -1,7 +1,10 @@ { "deploy": [ - "deploy.md" + "../deploy.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/UM_TINYPICO/deploy.md b/ports/esp32/boards/UM_TINYPICO/deploy.md deleted file mode 100644 index 8c15d4d41359d..0000000000000 --- a/ports/esp32/boards/UM_TINYPICO/deploy.md +++ /dev/null @@ -1,46 +0,0 @@ -Program your board using the esptool.py program, found [here](https://github.com/espressif/esptool). - -Your TinyPICO has an auto-reset circuit on it, so there is no need to put it into a -download mode first to erase or flash it. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash -``` - -### Mac -```bash -esptool.py --chip esp32 --port /dev/tty.SLAB_USBtoUART erase_flash -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options below, -then use the following command to program the firmware starting at address 0x1000, -remembering to replace `tinypico-micropython-firmware-version.bin` with the name of the -firmware you just downloaded: - -From then on program the firmware starting at address 0x1000: - -### Linux -```bash -esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 write_flash -z 0x1000 tinypico-micropython-firmware-version.bin -``` - -### Mac -```bash -esptool.py --chip esp32 --port /dev/tty.SLAB_USBtoUART --baud 921600 write_flash -z 0x1000 tinypico-micropython-firmware-version.bin -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32 --port COM(X) --baud 921600 write_flash -z 0x1000 tinypico-micropython-firmware-version.bin -``` diff --git a/ports/esp32/boards/UM_TINYS2/board.json b/ports/esp32/boards/UM_TINYS2/board.json index 9bbf9058bb8cf..9077ca6bb4b85 100644 --- a/ports/esp32/boards/UM_TINYS2/board.json +++ b/ports/esp32/boards/UM_TINYS2/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy.md" + "../deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0x1000" + }, "docs": "", "features": [ "Battery Charging", diff --git a/ports/esp32/boards/UM_TINYS2/deploy.md b/ports/esp32/boards/UM_TINYS2/deploy.md deleted file mode 100644 index a46bc9bd1aee9..0000000000000 --- a/ports/esp32/boards/UM_TINYS2/deploy.md +++ /dev/null @@ -1,50 +0,0 @@ -Program your board using the esptool.py program, found [here](https://github.com/espressif/esptool). - -To flash or erase your TinyS2, you have to first put it into download mode. -To do this, follow these steps: - -- Press and hold the [BOOT] button -- Press and release the [RESET] button -- Release the [BOOT] button - -Now the board is in download mode and the native USB will have enumerated as a serial device. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32s2 --port /dev/ttyACM0 erase_flash -``` - -### Mac -```bash -esptool.py --chip esp32s2 --port /dev/cu.usbmodem01 erase_flash -``` - -#### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s2 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options below, -then use the following command to program the firmware starting at address 0x1000, -remembering to replace `tinys2-micropython-firmware-version.bin` with the name of the -firmware you just downloaded: - -#### Linux -```bash -esptool.py --chip esp32s2 --port /dev/ttyACM0 write_flash -z 0x1000 tinys2-micropython-firmware-version.bin -``` - -#### Mac -```bash -esptool.py --chip esp32s2 --port /dev/cu.usbmodem01 write_flash -z 0x1000 tinys2-micropython-firmware-version.bin -``` - -#### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s2 --port COM(X) write_flash -z 0x1000 tinys2-micropython-firmware-version.bin -``` diff --git a/ports/esp32/boards/UM_TINYS3/board.json b/ports/esp32/boards/UM_TINYS3/board.json index 27ae46a249a81..5fea9e3a46279 100644 --- a/ports/esp32/boards/UM_TINYS3/board.json +++ b/ports/esp32/boards/UM_TINYS3/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy.md" + "../deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/UM_TINYS3/deploy.md b/ports/esp32/boards/UM_TINYS3/deploy.md deleted file mode 100644 index d65014e012480..0000000000000 --- a/ports/esp32/boards/UM_TINYS3/deploy.md +++ /dev/null @@ -1,52 +0,0 @@ -Program your board using the latest version of the esptool.py program, found [here](https://github.com/espressif/esptool). - -To flash or erase your TinyS3, you have to first put it into download mode. -To do this, follow these steps: - -- Press and hold the [BOOT] button -- Press and release the [RESET] button -- Release the [BOOT] button - -Now the board is in download mode and the native USB will have enumerated as a serial device. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 erase_flash -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options below, -then use the following command to program the firmware starting at address 0x0, -remembering to replace `tinys3-micropython-firmware-version.bin` with the name of -the firmware you just downloaded: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0x0 tinys3-micropython-firmware-version.bin -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 write_flash -z 0x0 tinys3-micropython-firmware-version.bin -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) write_flash -z 0x0 tinys3-micropython-firmware-version.bin -``` diff --git a/ports/esp32/boards/UM_TINYWATCHS3/board.json b/ports/esp32/boards/UM_TINYWATCHS3/board.json index 61dadcfb64b46..d1330239f1af8 100644 --- a/ports/esp32/boards/UM_TINYWATCHS3/board.json +++ b/ports/esp32/boards/UM_TINYWATCHS3/board.json @@ -1,7 +1,11 @@ { "deploy": [ - "deploy.md" + "../deploy_flashmode.md", + "../deploy_nativeusb.md" ], + "deploy_options": { + "flash_offset": "0" + }, "docs": "", "features": [ "BLE", diff --git a/ports/esp32/boards/UM_TINYWATCHS3/deploy.md b/ports/esp32/boards/UM_TINYWATCHS3/deploy.md deleted file mode 100644 index bad7d7c7d897b..0000000000000 --- a/ports/esp32/boards/UM_TINYWATCHS3/deploy.md +++ /dev/null @@ -1,52 +0,0 @@ -Program your board using the latest version of the esptool.py program, found [here](https://github.com/espressif/esptool). - -To flash or erase your TinyWATCH S3, you have to first put it into download mode. -To do this, follow these steps: - -- Press and hold the [BOOT] button -- Press and release the [RESET] button -- Release the [BOOT] button - -Now the board is in download mode and the native USB will have enumerated as a serial device. - -If you are putting MicroPython on your board for the first time then you should -first erase the entire flash using: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 erase_flash -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) erase_flash -``` - -Now download the version of the firmware you would like to install from the options below, -then use the following command to program the firmware starting at address 0x0, -remembering to replace `tinywatchs3-micropython-firmware-version.bin` with the name of -the firmware you just downloaded: - -### Linux -```bash -esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0x0 tinywatchs3-micropython-firmware-version.bin -``` - -### Mac -Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. -```bash -esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 write_flash -z 0x0 tinywatchs3-micropython-firmware-version.bin -``` - -### Windows -Change (X) to whatever COM port is being used by the board -```bash -esptool --chip esp32s3 --port COM(X) write_flash -z 0x0 tinywatchs3-micropython-firmware-version.bin -``` diff --git a/ports/esp32/boards/deploy.md b/ports/esp32/boards/deploy.md index 64e683edfc6f9..aa86a37215947 100644 --- a/ports/esp32/boards/deploy.md +++ b/ports/esp32/boards/deploy.md @@ -1,14 +1,54 @@ -Program your board using the esptool.py program, found [here](https://github.com/espressif/esptool). +Program your board using the esptool.py program, found [here](https://docs.espressif.com/projects/esptool/en/latest/{mcu}/). + +*Windows users:* You may find the installed program is called `esptool` instead of `esptool.py`. + +### Erasing 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 esp32 --port /dev/ttyUSB0 erase_flash +esptool.py erase_flash ``` -From then on program the firmware starting at address 0x1000: +`esptool.py` will try to detect the serial port with the ESP32 automatically, +but if this fails or there might be more than one Espressif-based device +attached to your computer then pass the `--port` option with the name of the +target serial port. For example: ```bash -esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x1000 esp32-20190125-v1.10.bin +esptool.py --port PORTNAME erase_flash ``` + +* On Linux, the port name is usually similar to `/dev/ttyUSB` or `/dev/ttyACM0`. +* On Mac, the port name is usually similar to `/dev/cu.usbmodem01`. +* On Windows, the port name is usually similar to `COM4`. + +### Flashing + +Then deploy the firmware to the board, starting at address {deploy_options[flash_offset]}: + +```bash +esptool.py --baud 460800 write_flash {deploy_options[flash_offset]} ESP32_BOARD_NAME-DATE-VERSION.bin +``` + +Replace `ESP32_BOARD_NAME-DATE-VERSION.bin` with the `.bin` file downloaded from this page. + +As above, if `esptool.py` can't automatically detect the serial port +then you can pass it explicitly on the command line instead. For example: + +```bash +esptool.py --port PORTNAME --baud 460800 write_flash {deploy_options[flash_offset]} ESP32_BOARD_NAME-DATE-VERSION.bin +``` + +### Troubleshooting + +If flashing starts and then fails partway through, try removing the `--baud +460800` option to flash at the slower default speed. + +If these steps don't work, consult the [MicroPython ESP32 Troubleshooting +steps](https://docs.micropython.org/en/latest/esp32/tutorial/intro.html#troubleshooting-installation-problems) +and the [esptool +documentation](https://docs.espressif.com/projects/esptool/en/latest/{mcu}/esptool/basic-options.html). + +**Important**: From the options below, download the ``.bin`` file for your board. diff --git a/ports/esp32/boards/deploy_c3.md b/ports/esp32/boards/deploy_c3.md deleted file mode 100644 index 016ba7cabb3ad..0000000000000 --- a/ports/esp32/boards/deploy_c3.md +++ /dev/null @@ -1,14 +0,0 @@ -Program your board using the esptool.py program, found [here](https://github.com/espressif/esptool). - -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 esp32c3 --port /dev/ttyUSB0 erase_flash -``` - -From then on program the firmware starting at address 0x0: - -```bash -esptool.py --chip esp32c3 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x0 esp32c3-20220117-v1.18.bin -``` diff --git a/ports/esp32/boards/deploy_c6.md b/ports/esp32/boards/deploy_c6.md deleted file mode 100644 index 941c69cdefb34..0000000000000 --- a/ports/esp32/boards/deploy_c6.md +++ /dev/null @@ -1,14 +0,0 @@ -Program your board using the esptool.py program, found [here](https://github.com/espressif/esptool). - -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 esp32c6 --port /dev/ttyUSB0 erase_flash -``` - -From then on program the firmware starting at address 0x0: - -```bash -esptool.py --chip esp32c6 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x0 ESP32_GENERIC_C6-20240602-v1.24.0.bin -``` diff --git a/ports/esp32/boards/deploy_flashmode.md b/ports/esp32/boards/deploy_flashmode.md new file mode 100644 index 0000000000000..ff7cbf9a46308 --- /dev/null +++ b/ports/esp32/boards/deploy_flashmode.md @@ -0,0 +1,9 @@ +To flash or erase your {product}, you have to first put it into download mode. + +To put the {product} into download, follow these steps: + +- Press and hold the [BOOT] button +- Press and release the [RESET] button +- Release the [BOOT] button + +Now the board is in download mode and the native USB will have enumerated as a serial device. diff --git a/ports/esp32/boards/deploy_nativeusb.md b/ports/esp32/boards/deploy_nativeusb.md new file mode 100644 index 0000000000000..c653330ca8df4 --- /dev/null +++ b/ports/esp32/boards/deploy_nativeusb.md @@ -0,0 +1,51 @@ +Program your board using the esptool.py program, found [here](https://docs.espressif.com/projects/esptool/en/latest/{mcu}/). + +*Windows users:* You may find the installed program is called `esptool` instead of `esptool.py`. + +### Erasing + +If you are putting MicroPython on your board for the first time then you should +first erase the entire flash using: + +```bash +esptool.py erase_flash +``` + +`esptool.py` will try to detect the serial port with the ESP32 automatically, +but if this fails or there might be more than one Espressif-based device +attached to your computer then pass the `--port` option with the name of the +target serial port. For example: + +```bash +esptool.py --port PORTNAME erase_flash +``` + +* On Linux, the port name is usually similar to `/dev/ttyACM0`. +* On Mac, the port name is usually similar to `/dev/cu.usbmodem01`. +* On Windows, the port name is usually similar to `COM4`. + +### Flashing + +Then deploy the firmware to the board, starting at address {deploy_options[flash_offset]}: + +```bash +esptool.py write_flash {deploy_options[flash_offset]} ESP32_BOARD_NAME-DATE-VERSION.bin +``` + +Replace `ESP32_BOARD_NAME-DATE-VERSION.bin` with the `.bin` file downloaded from this page. + +As above, if `esptool.py` can't automatically detect the serial port +then you can pass it explicitly on the command line instead. For example: + +```bash +esptool.py --port PORTNAME write_flash {deploy_options[flash_offset]} ESP32_BOARD_NAME-DATE-VERSION.bin +``` + +### Troubleshooting + +If these steps don't work, consult the [MicroPython ESP32 Troubleshooting +steps](https://docs.micropython.org/en/latest/esp32/tutorial/intro.html#troubleshooting-installation-problems) +and the [esptool +documentation](https://docs.espressif.com/projects/esptool/en/latest/{mcu}/esptool/basic-options.html). + +**Important**: From the options below, download the ``.bin`` file for your board. diff --git a/ports/esp32/boards/deploy_s2.md b/ports/esp32/boards/deploy_s2.md deleted file mode 100644 index 5b3057087d1e2..0000000000000 --- a/ports/esp32/boards/deploy_s2.md +++ /dev/null @@ -1,14 +0,0 @@ -Program your board using the esptool.py program, found [here](https://github.com/espressif/esptool). - -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 esp32s2 --port /dev/ttyACM0 erase_flash -``` - -From then on program the firmware starting at address 0x1000: - -```bash -esptool.py --chip esp32s2 --port /dev/ttyACM0 write_flash -z 0x1000 board-20210902-v1.17.bin -``` diff --git a/ports/esp32/boards/deploy_s3.md b/ports/esp32/boards/deploy_s3.md deleted file mode 100644 index 7f564a95e310a..0000000000000 --- a/ports/esp32/boards/deploy_s3.md +++ /dev/null @@ -1,14 +0,0 @@ -Program your board using the esptool.py program, found [here](https://github.com/espressif/esptool). - -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-20210902-v1.17.bin -``` diff --git a/tools/autobuild/build-downloads.py b/tools/autobuild/build-downloads.py index d6f6d0324691c..f7411f5980ffe 100755 --- a/tools/autobuild/build-downloads.py +++ b/tools/autobuild/build-downloads.py @@ -91,7 +91,18 @@ def main(repo_path, output_path): f.write("\n\n## Installation instructions\n") for deploy in blob["deploy"]: with open(os.path.join(board_dir, deploy), "r") as fin: - f.write(fin.read()) + body = fin.read() + # any key in the board.json file can be substituted via Python str.format() + try: + body = body.format(**blob) + except Exception as e: + raise RuntimeError( + "Failed to format deploy file {} according to {}: {}".format( + fin.name, board_json, e + ) + ) + f.write(body) + f.write("\n") # Write the full index for the website to load. with open(os.path.join(output_path, "index.json"), "w") as f: From 600e46800d9f2f7ec27df6e6851a408b9860c7f4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 7 Jan 2025 12:14:30 +1100 Subject: [PATCH 214/546] docs/esp32: Update tutorial flashing steps to match deploy.md. Includes fixing the flashing address for newer SoCs, as reported in discussion https://github.com/orgs/micropython/discussions/16417 Also removes some redundant or out of date information, and adds links to the Espressif esptool docs which are quite comprehensive. Information about ESP32_GENERIC variants is moved to the board page, as it only applies to that board. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/esp32/tutorial/intro.rst | 125 +++++++++++++--------- ports/esp32/boards/ESP32_GENERIC/board.md | 19 +++- 2 files changed, 86 insertions(+), 58 deletions(-) diff --git a/docs/esp32/tutorial/intro.rst b/docs/esp32/tutorial/intro.rst index cf4d0bcbd2f55..660af87448aca 100644 --- a/docs/esp32/tutorial/intro.rst +++ b/docs/esp32/tutorial/intro.rst @@ -36,19 +36,26 @@ Getting the firmware The first thing you need to do is download the most recent MicroPython firmware .bin file to load onto your ESP32 device. You can download it from the -`MicroPython downloads page `_. -From here, you have 3 main choices: +`MicroPython download page`_. Search for your particular board on this page. -* Stable firmware builds -* Daily firmware builds -* Daily firmware builds with SPIRAM support +.. note:: If you don't see your specific board on the download page, then it's + very likely that one of the generic firmwares will work. These are + listed at the top of the download page and have names matching the + onboard Espressif chip (i.e. `ESP32 / WROOM`_, `ESP32-C3`_, + `ESP32-S3`_, etc). -If you are just starting with MicroPython, the best bet is to go for the Stable -firmware builds. If you are an advanced, experienced MicroPython ESP32 user -who would like to follow development closely and help with testing new -features, there are daily builds. If your board has SPIRAM support you can -use either the standard firmware or the firmware with SPIRAM support, and in -the latter case you will have access to more RAM for Python objects. + However, you may need to double check with the vendor you purchased + the board from. + +From here, you have a choice to make: + +* Download a stable firmware release. +* Download a daily firmware "Preview" build. + +If you are just starting with MicroPython, the best bet is to go for the stable +Release firmware builds. If you are an advanced, experienced MicroPython ESP32 +user who would like to follow development closely and help with testing new +features, then you may find the Preview builds useful. .. _esp32_flashing: @@ -71,71 +78,83 @@ For best results it is recommended to first erase the entire flash of your device before putting on new MicroPython firmware. Currently we only support esptool.py to copy across the firmware. You can find -this tool here: ``__, or install it +this tool here: ``__, or install it using pip:: pip install esptool -Versions starting with 1.3 support both Python 2.7 and Python 3.4 (or newer). -An older version (at least 1.2.1 is needed) works fine but will require Python -2.7. - Using esptool.py you can erase the flash with the command:: - esptool.py --port /dev/ttyUSB0 erase_flash + esptool.py erase_flash + +.. note:: On Windows, the command may be named ``esptool`` not ``esptool.py`` + +Then deploy the new firmware. Use this command for original ESP32 and ESP32-S2 SoCs:: + + esptool.py --baud 460800 write_flash 0x1000 ESP32_BOARD_NAME-DATE-VERSION.bin -And then deploy the new firmware using:: +Use this command for all other SoCs (including ESP32-S3, ESP32-C3, and all newer chips):: - esptool.py --chip esp32 --port /dev/ttyUSB0 write_flash -z 0x1000 esp32-20180511-v1.9.4.bin + esptool.py --baud 460800 write_flash 0 ESP32_BOARD_NAME-DATE-VERSION.bin -Notes: +Replace ``ESP32_BOARD_NAME-DATE-VERSION.bin`` with the name of your firmware .bin file. -* You might need to change the "port" setting to something else relevant for your - PC -* You may need to reduce the baudrate if you get errors when flashing - (eg down to 115200 by adding ``--baud 115200`` into the command) -* For some boards with a particular FlashROM configuration you may need to - change the flash mode (eg by adding ``-fm dio`` into the command) -* The filename of the firmware should match the file that you have +.. note:: If you're unsure which command line to use, check the `MicroPython + download page`_ for your board. Each download page shows an accurate command + line example for that board and its SoC. If the above commands run without error then MicroPython should be installed on -your board! +your board! If the command fails, see :ref:`esp32_troubleshooting_install` below. Serial prompt ------------- Once you have the firmware on the device you can access the REPL (Python prompt) -over UART0 (GPIO1=TX, GPIO3=RX), which might be connected to a USB-serial -converter, depending on your board. The baudrate is 115200. +over either UART0, which might be connected to a USB-serial converter depending +on your board, or the chip's built-in USB device. The baudrate is 115200. From here you can now follow the ESP8266 tutorial, because these two Espressif chips are very similar when it comes to using MicroPython on them. The ESP8266 tutorial is found at :ref:`esp8266_tutorial` (but skip the Introduction section). +.. _esp32_troubleshooting_install: + Troubleshooting installation problems ------------------------------------- If you experience problems during flashing or with running firmware immediately -after it, here are troubleshooting recommendations: - -* Be aware of and try to exclude hardware problems. There are 2 common - problems: bad power source quality, and worn-out/defective FlashROM. - Speaking of power source, not just raw amperage is important, but also low - ripple and noise/EMI in general. The most reliable and convenient power - source is a USB port. - -* The flashing instructions above use flashing speed of 460800 baud, which is - good compromise between speed and stability. However, depending on your - module/board, USB-UART converter, cables, host OS, etc., the above baud - rate may be too high and lead to errors. Try a more common 115200 baud - rate instead in such cases. - -* To catch incorrect flash content (e.g. from a defective sector on a chip), - add ``--verify`` switch to the commands above. - -* If you still experience problems with flashing the firmware please - refer to esptool.py project page, https://github.com/espressif/esptool - for additional documentation and a bug tracker where you can report problems. - -* If you are able to flash the firmware but the ``--verify`` option returns - errors even after multiple retries the you may have a defective FlashROM chip. +after flashing, here are some troubleshooting recommendations: + +* Esptool will try to detect the serial port where your ESP32 is connected. If + this doesn't work, or you have multiple serial ports, then you may need to + manually specify the port by adding the ``--port`` option to the start of the + ``esptool.py`` command line. For example, ``esptool.py --port /dev/ttyUSB0 + `` for Linux or ``esptool --port COM4 `` for + Windows. +* If the board isn't responding to esptool at all, it may need to be manually + reset into the bootloader download mode. Look for a button marked "BOOT" or + "IO0" on your board and a second button marked "RESET" or "RST". If you have + both buttons, try these steps: + + 1. Press "BOOT" (or "IO0") and hold it down. + 2. Press "RESET" (or "RST") and immediately release it. + 3. Release "BOOT" (or "IO0"). + 4. Re-run the flashing steps from the download page. + + If your board doesn't have these buttons, consult the board manufacturer's + documentation about entering bootloader download mode. +* If you get errors part-way through the flashing process then try reducing the + speed of data transfer by removing the ``--baud 460800`` argument. +* Hardware problems can cause flashing to fail. There are two common problems: + bad power source quality, and defective hardware (especially very low cost + unbranded development boards). Speaking of power source, not just raw amperage + is important, but also low ripple and noise/EMI in general. The most reliable + and convenient power source is a USB port. +* If you still experience problems with flashing the firmware then please also + refer to the `esptool Troubleshooting documentation`_. + +.. _esptool Troubleshooting documentation: https://docs.espressif.com/projects/esptool/en/latest/esp32/troubleshooting.html +.. _MicroPython download page: https://micropython.org/download/?port=esp32 +.. _ESP32 / WROOM: https://micropython.org/download/ESP32_GENERIC +.. _ESP32-C3: https://micropython.org/download/ESP32_GENERIC_C3 +.. _ESP32-S3: https://micropython.org/download/ESP32_GENERIC_S3 diff --git a/ports/esp32/boards/ESP32_GENERIC/board.md b/ports/esp32/boards/ESP32_GENERIC/board.md index 8a669411d06a1..9346d18d84a3c 100644 --- a/ports/esp32/boards/ESP32_GENERIC/board.md +++ b/ports/esp32/boards/ESP32_GENERIC/board.md @@ -1,9 +1,18 @@ The following files are firmware that should work on most ESP32-based boards with 4MiB of flash, including WROOM WROVER, SOLO, PICO, and MINI modules. -If your board is based on a WROVER module, or otherwise has SPIRAM (also known -as PSRAM), then use the "spiram" variant. +This board has multiple variants available: -The "d2wd" variant is for ESP32-D2WD chips (with 2MiB flash), and "unicore" is -for single-core ESP32 chips (e.g. the "SOLO" modules). The "ota" variant sets -up the partition table to allow for Over-the-Air updates. +* If your board is based on a WROVER module, or otherwise has SPIRAM (also known + as PSRAM) then it's recommended to use the "spiram" variant. Look for heading + **Support for SPIRAM / WROVER)**. +* If your board has a ESP32-D2WD chip (with only 2MiB flash) then use the "d2wd" + variant. Look for heading **ESP32 D2WD**. +* If your board has a single-core ESP32 (e.g. the "SOLO" modules) then choose + the "unicore" variant. Look for heading **ESP32 Unicore**. +* If you'd like to perform Over-the-Air updates of the MicroPython firmware, + then choose the "ota" variant. This variant has less room in the flash for + Python files as a result of supporting OTA. Look for heading **Support for + OTA**. + +Otherwise, download the generic variant (under the first heading below). From 6fc18ec647c6c9f399b433aa53b07ee04132d4a8 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 7 Jan 2025 17:29:56 +1100 Subject: [PATCH 215/546] esp32/boards: Update the product name for some UM boards. The previous deploy.md refactors revealed that these boards had a different "product" entry in boards.json compared to the name given in the board.md file. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/boards/UM_FEATHERS2/board.json | 2 +- ports/esp32/boards/UM_FEATHERS2NEO/board.json | 2 +- ports/esp32/boards/UM_TINYS2/board.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/esp32/boards/UM_FEATHERS2/board.json b/ports/esp32/boards/UM_FEATHERS2/board.json index b6cacf8c5c784..4a59fbe70c27d 100644 --- a/ports/esp32/boards/UM_FEATHERS2/board.json +++ b/ports/esp32/boards/UM_FEATHERS2/board.json @@ -24,7 +24,7 @@ "unexpectedmaker_feathers2.jpg" ], "mcu": "esp32s2", - "product": "Feather S2", + "product": "FeatherS2", "thumbnail": "", "url": "https://feathers2.io/", "vendor": "Unexpected Maker" diff --git a/ports/esp32/boards/UM_FEATHERS2NEO/board.json b/ports/esp32/boards/UM_FEATHERS2NEO/board.json index 5b9a1935c9760..a8271dfd3dfb0 100644 --- a/ports/esp32/boards/UM_FEATHERS2NEO/board.json +++ b/ports/esp32/boards/UM_FEATHERS2NEO/board.json @@ -24,7 +24,7 @@ "unexpectedmaker_feathers2neo.jpg" ], "mcu": "esp32s2", - "product": "Feather S2 Neo", + "product": "FeatherS2 Neo", "thumbnail": "", "url": "https://unexpectedmaker.com/feathers2-neo", "vendor": "Unexpected Maker" diff --git a/ports/esp32/boards/UM_TINYS2/board.json b/ports/esp32/boards/UM_TINYS2/board.json index 9077ca6bb4b85..dc788a4bc04da 100644 --- a/ports/esp32/boards/UM_TINYS2/board.json +++ b/ports/esp32/boards/UM_TINYS2/board.json @@ -23,7 +23,7 @@ "unexpectedmaker_tinys2.jpg" ], "mcu": "esp32s2", - "product": "Tiny S2", + "product": "TinyS2", "thumbnail": "", "url": "https://unexpectedmaker.com/tinys2", "vendor": "Unexpected Maker" From 84e0aca0fb3b95a4f6c3a46e1a40894b488ba655 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 15 Jan 2025 11:17:07 +1100 Subject: [PATCH 216/546] docs/esp32: Defer to the download page for flashing steps. The user already has it open, and its customised for their particular board. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/esp32/tutorial/intro.rst | 72 ++++++++++------------------------- 1 file changed, 21 insertions(+), 51 deletions(-) diff --git a/docs/esp32/tutorial/intro.rst b/docs/esp32/tutorial/intro.rst index 660af87448aca..599731ad755af 100644 --- a/docs/esp32/tutorial/intro.rst +++ b/docs/esp32/tutorial/intro.rst @@ -62,60 +62,17 @@ features, then you may find the Preview builds useful. Deploying the firmware ---------------------- -Once you have the MicroPython firmware you need to load it onto your ESP32 device. -There are two main steps to do this: first you need to put your device in -bootloader mode, and second you need to copy across the firmware. The exact -procedure for these steps is highly dependent on the particular board and you will -need to refer to its documentation for details. +Once you have the MicroPython firmware you need to load it onto your ESP32 +device. There are two main steps to do this: first you need to put your device +in bootloader mode, and second you need to copy across the firmware. The exact +procedure for these steps is highly dependent on the particular board. -Fortunately, most boards have a USB connector, a USB-serial converter, and the DTR -and RTS pins wired in a special way then deploying the firmware should be easy as -all steps can be done automatically. Boards that have such features -include the Adafruit Feather HUZZAH32, M5Stack, Wemos LOLIN32, and TinyPICO -boards, along with the Espressif DevKitC, PICO-KIT, WROVER-KIT dev-kits. - -For best results it is recommended to first erase the entire flash of your -device before putting on new MicroPython firmware. - -Currently we only support esptool.py to copy across the firmware. You can find -this tool here: ``__, or install it -using pip:: - - pip install esptool - -Using esptool.py you can erase the flash with the command:: - - esptool.py erase_flash - -.. note:: On Windows, the command may be named ``esptool`` not ``esptool.py`` - -Then deploy the new firmware. Use this command for original ESP32 and ESP32-S2 SoCs:: - - esptool.py --baud 460800 write_flash 0x1000 ESP32_BOARD_NAME-DATE-VERSION.bin - -Use this command for all other SoCs (including ESP32-S3, ESP32-C3, and all newer chips):: - - esptool.py --baud 460800 write_flash 0 ESP32_BOARD_NAME-DATE-VERSION.bin - -Replace ``ESP32_BOARD_NAME-DATE-VERSION.bin`` with the name of your firmware .bin file. - -.. note:: If you're unsure which command line to use, check the `MicroPython - download page`_ for your board. Each download page shows an accurate command - line example for that board and its SoC. +Detailed steps can be found on the same `MicroPython download page`_ for your +board. It's recommended that you follow the steps on the download page, as they +are customised for your particular board. If the above commands run without error then MicroPython should be installed on -your board! If the command fails, see :ref:`esp32_troubleshooting_install` below. - -Serial prompt -------------- - -Once you have the firmware on the device you can access the REPL (Python prompt) -over either UART0, which might be connected to a USB-serial converter depending -on your board, or the chip's built-in USB device. The baudrate is 115200. - -From here you can now follow the ESP8266 tutorial, because these two Espressif chips -are very similar when it comes to using MicroPython on them. The ESP8266 tutorial -is found at :ref:`esp8266_tutorial` (but skip the Introduction section). +your board! Skip ahead to :ref:`esp32_serial_prompt`. .. _esp32_troubleshooting_install: @@ -153,6 +110,19 @@ after flashing, here are some troubleshooting recommendations: * If you still experience problems with flashing the firmware then please also refer to the `esptool Troubleshooting documentation`_. +.. _esp32_serial_prompt: + +Serial prompt +------------- + +Once you have the firmware on the device you can access the REPL (Python prompt) +over either UART0, which might be connected to a USB-serial converter depending +on your board, or the chip's built-in USB device. The baudrate is 115200. + +From here you can now follow the ESP8266 tutorial, because these two Espressif chips +are very similar when it comes to using MicroPython on them. The ESP8266 tutorial +is found at :ref:`esp8266_tutorial` (but skip the Introduction section). + .. _esptool Troubleshooting documentation: https://docs.espressif.com/projects/esptool/en/latest/esp32/troubleshooting.html .. _MicroPython download page: https://micropython.org/download/?port=esp32 .. _ESP32 / WROOM: https://micropython.org/download/ESP32_GENERIC From 6db29978ac8954f3686f9eb59dd71b55c3495456 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 16 Jan 2025 15:54:47 +1100 Subject: [PATCH 217/546] py/mkrules.mk: Move comment about partial clones outside make rule. Otherwise the comment is printed each time the rule is run. Follow up to fdd606dd5395d9c474775945fa4458a27199653b. Signed-off-by: Damien George --- py/mkrules.mk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py/mkrules.mk b/py/mkrules.mk index 875ddee8523b8..74978b0d15bf2 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -248,13 +248,13 @@ clean-prog: .PHONY: clean-prog endif +# If available, do blobless partial clones of submodules to save time and space. +# A blobless partial clone lazily fetches data as needed, but has all the metadata available (tags, etc.). +# Fallback to standard submodule update if blobless isn't available (earlier than 2.36.0) submodules: $(ECHO) "Updating submodules: $(GIT_SUBMODULES)" ifneq ($(GIT_SUBMODULES),) $(Q)cd $(TOP) && git submodule sync $(GIT_SUBMODULES) - # If available, do blobless partial clones of submodules to save time and space. - # A blobless partial clone lazily fetches data as needed, but has all the metadata available (tags, etc.). - # Fallback to standard submodule update if blobless isn't available (earlier than 2.36.0) $(Q)cd $(TOP) && git submodule update --init --filter=blob:none $(GIT_SUBMODULES) || \ git submodule update --init $(GIT_SUBMODULES) endif From b4f53a0e51a7bb098f67a747fb1c62c21492a1dd Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Jan 2025 15:16:36 +1100 Subject: [PATCH 218/546] qemu/boards: Change boards to use a subdirectory like other ports. This commit moves `.mk` to `/mpconfigboard.mk` for all qemu boards, making it the same as other bare-metal ports. Signed-off-by: Damien George --- ports/qemu/Makefile | 7 ++++++- .../qemu/boards/{MICROBIT.mk => MICROBIT/mpconfigboard.mk} | 0 .../boards/{MPS2_AN385.mk => MPS2_AN385/mpconfigboard.mk} | 0 .../boards/{NETDUINO2.mk => NETDUINO2/mpconfigboard.mk} | 0 .../boards/{SABRELITE.mk => SABRELITE/mpconfigboard.mk} | 0 .../boards/{VIRT_RV32.mk => VIRT_RV32/mpconfigboard.mk} | 0 6 files changed, 6 insertions(+), 1 deletion(-) rename ports/qemu/boards/{MICROBIT.mk => MICROBIT/mpconfigboard.mk} (100%) rename ports/qemu/boards/{MPS2_AN385.mk => MPS2_AN385/mpconfigboard.mk} (100%) rename ports/qemu/boards/{NETDUINO2.mk => NETDUINO2/mpconfigboard.mk} (100%) rename ports/qemu/boards/{SABRELITE.mk => SABRELITE/mpconfigboard.mk} (100%) rename ports/qemu/boards/{VIRT_RV32.mk => VIRT_RV32/mpconfigboard.mk} (100%) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index bf9addc6f3256..d2550cf883b7d 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -2,6 +2,11 @@ # Initial setup of Makefile environment BOARD ?= MPS2_AN385 +BOARD_DIR ?= boards/$(BOARD) + +ifeq ($(wildcard $(BOARD_DIR)/.),) +$(error Invalid BOARD specified: $(BOARD_DIR)) +endif # Make the build directory reflect the board. BUILD ?= build-$(BOARD) @@ -10,7 +15,7 @@ include ../../py/mkenv.mk -include mpconfigport.mk # Include board specific .mk file. -include boards/$(BOARD).mk +include $(BOARD_DIR)/mpconfigboard.mk # qstr definitions (must come before including py.mk) QSTR_DEFS = qstrdefsport.h diff --git a/ports/qemu/boards/MICROBIT.mk b/ports/qemu/boards/MICROBIT/mpconfigboard.mk similarity index 100% rename from ports/qemu/boards/MICROBIT.mk rename to ports/qemu/boards/MICROBIT/mpconfigboard.mk diff --git a/ports/qemu/boards/MPS2_AN385.mk b/ports/qemu/boards/MPS2_AN385/mpconfigboard.mk similarity index 100% rename from ports/qemu/boards/MPS2_AN385.mk rename to ports/qemu/boards/MPS2_AN385/mpconfigboard.mk diff --git a/ports/qemu/boards/NETDUINO2.mk b/ports/qemu/boards/NETDUINO2/mpconfigboard.mk similarity index 100% rename from ports/qemu/boards/NETDUINO2.mk rename to ports/qemu/boards/NETDUINO2/mpconfigboard.mk diff --git a/ports/qemu/boards/SABRELITE.mk b/ports/qemu/boards/SABRELITE/mpconfigboard.mk similarity index 100% rename from ports/qemu/boards/SABRELITE.mk rename to ports/qemu/boards/SABRELITE/mpconfigboard.mk diff --git a/ports/qemu/boards/VIRT_RV32.mk b/ports/qemu/boards/VIRT_RV32/mpconfigboard.mk similarity index 100% rename from ports/qemu/boards/VIRT_RV32.mk rename to ports/qemu/boards/VIRT_RV32/mpconfigboard.mk From 87f04d5935ef4e190873f4f31afdc25a58647976 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 18 Jan 2025 12:06:33 +0100 Subject: [PATCH 219/546] esp8266/Makefile: Fix local toolchain builds on recent Linux systems. This commit fixes compilation for the ESP8266 port when using a local toolchain on relatively recent Linux systems. The documentation asks the user to delete the esptool instance that comes with the toolchain, in favour of using the one provided by the system. On Linux systems that are at least two years old (looking at the CI Ubuntu image as an example), the version of esptool installed with the package manager isn't called `esptool.py` but just `esptool`. The Makefile didn't take that into account and used `esptool.py` without checking if such a command exists, making builds fail. Now preference is given to the `esptool` command, falling back to `esptool.py` only if the former command does not exist or it is not available to the current user, to maintain compatibility with old setups. Signed-off-by: Alessandro Gatti --- ports/esp8266/Makefile | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index cd94a38a726e3..1721075eae172 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -185,6 +185,11 @@ else ifneq ($(shell cat $(CONFVARS_FILE)), $(FROZEN_MANIFEST) $(UART_OS)) $(shell echo $(FROZEN_MANIFEST) $(UART_OS) > $(CONFVARS_FILE)) endif +ESPTOOL := $(shell command -v esptool 2> /dev/null) +ifndef ESPTOOL +ESPTOOL = esptool.py +endif + $(BUILD)/uart.o: $(CONFVARS_FILE) FROZEN_EXTRA_DEPS = $(CONFVARS_FILE) @@ -193,11 +198,11 @@ FROZEN_EXTRA_DEPS = $(CONFVARS_FILE) deploy: $(FWBIN) $(ECHO) "Writing $< to the board" - $(Q)esptool.py --port $(PORT) --baud $(BAUD) write_flash --verify --flash_size=$(FLASH_SIZE) --flash_mode=$(FLASH_MODE) 0 $< + $(Q)$(ESPTOOL) --port $(PORT) --baud $(BAUD) write_flash --verify --flash_size=$(FLASH_SIZE) --flash_mode=$(FLASH_MODE) 0 $< erase: $(ECHO) "Erase flash" - $(Q)esptool.py --port $(PORT) --baud $(BAUD) erase_flash + $(Q)$(ESPTOOL) --port $(PORT) --baud $(BAUD) erase_flash reset: echo -e "\r\nimport machine; machine.reset()\r\n" >$(PORT) @@ -205,7 +210,7 @@ reset: ifeq ($(BOARD_VARIANT),OTA) $(FWBIN): $(BUILD)/firmware.elf $(ECHO) "Create $@" - $(Q)esptool.py elf2image $^ + $(Q)$(ESPTOOL) elf2image $^ $(Q)$(PYTHON) makeimg.py $(BUILD)/firmware.elf-0x00000.bin $(BUILD)/firmware.elf-0x[0-5][1-f]000.bin $(BUILD)/firmware-ota.bin $(Q)cat $(YAOTA8266)/yaota8266.bin $(BUILD)/firmware-ota.bin > $@ @@ -213,7 +218,7 @@ $(FWBIN): $(BUILD)/firmware.elf else $(FWBIN): $(BUILD)/firmware.elf $(ECHO) "Create $@" - $(Q)esptool.py elf2image $^ + $(Q)$(ESPTOOL) elf2image $^ $(Q)$(PYTHON) makeimg.py $(BUILD)/firmware.elf-0x00000.bin $(BUILD)/firmware.elf-0x[0-5][1-f]000.bin $@ endif From 1100aa63c908f60d03fa5b1d1b3abae3a53050f8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 16 Jan 2025 17:09:43 +1100 Subject: [PATCH 220/546] LICENSE,docs: Update copyright year range to include 2025. Signed-off-by: Damien George --- LICENSE | 2 +- docs/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 550ed9574d06a..929a2e97de7bf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-2024 Damien P. George +Copyright (c) 2013-2025 Damien P. George Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/conf.py b/docs/conf.py index e5c6ba98090af..eb61487582024 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -71,7 +71,7 @@ # General information about the project. project = "MicroPython" -copyright = "- The MicroPython Documentation is Copyright © 2014-2024, " + micropy_authors +copyright = "- The MicroPython Documentation is Copyright © 2014-2025, " + micropy_authors # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 70b95d8f9314ebcddee15e5ac2f4bdb03143846d Mon Sep 17 00:00:00 2001 From: Graeme Winter Date: Thu, 23 Mar 2023 05:20:46 +0000 Subject: [PATCH 221/546] samd/machine_dac: Fix SAMD51 DAC for two channels. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improvements to DAC support for SAMD51: - properly validate DAC id - correctly use dac_init flag, as a 2-ple for A0, A1 channels - disable DAC before adjusting settings, see SAMD5x data sheet §47.6.2.3 Co-authored-by: robert-hh Signed-off-by: Graeme Winter --- ports/samd/machine_dac.c | 52 +++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/ports/samd/machine_dac.c b/ports/samd/machine_dac.c index c611f95e653d0..7dcc654644d79 100644 --- a/ports/samd/machine_dac.c +++ b/ports/samd/machine_dac.c @@ -72,7 +72,8 @@ static uint8_t dac_vref_table[] = { #define MAX_DAC_VALUE (4095) #define DEFAULT_DAC_VREF (2) #define MAX_DAC_VREF (3) -static bool dac_init = false; +static bool dac_init[2] = {false, false}; + #endif @@ -91,10 +92,10 @@ static mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ uint8_t id = args[ARG_id].u_int; dac_obj_t *self = NULL; - if (0 <= id && id <= MP_ARRAY_SIZE(dac_obj)) { + if (0 <= id && id < MP_ARRAY_SIZE(dac_obj)) { self = &dac_obj[id]; } else { - mp_raise_ValueError(MP_ERROR_TEXT("invalid Pin for DAC")); + mp_raise_ValueError(MP_ERROR_TEXT("invalid id for DAC")); } uint8_t vref = args[ARG_vref].u_int; @@ -102,9 +103,10 @@ static mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ self->vref = vref; } - Dac *dac = dac_bases[0]; // Just one DAC + Dac *dac = dac_bases[0]; // Just one DAC register block + + // initialize DAC - // Init DAC #if defined(MCU_SAMD21) // Configuration SAMD21 @@ -127,21 +129,39 @@ static mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ // Configuration SAMD51 // Enable APBD clocks and PCHCTRL clocks; GCLK3 at 8 MHz - dac_init = true; - MCLK->APBDMASK.reg |= MCLK_APBDMASK_DAC; - GCLK->PCHCTRL[DAC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK3 | GCLK_PCHCTRL_CHEN; - // Reset DAC registers - dac->CTRLA.bit.SWRST = 1; - while (dac->CTRLA.bit.SWRST) { + if (!(dac_init[0] | dac_init[1])) { + MCLK->APBDMASK.reg |= MCLK_APBDMASK_DAC; + GCLK->PCHCTRL[DAC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK3 | \ + GCLK_PCHCTRL_CHEN; + + // Reset DAC registers + dac->CTRLA.bit.SWRST = 1; + while (dac->CTRLA.bit.SWRST) { + } + dac->CTRLB.reg = DAC_CTRLB_REFSEL(dac_vref_table[self->vref]); + } - dac->CTRLB.reg = DAC_CTRLB_REFSEL(dac_vref_table[self->vref]); - dac->DACCTRL[self->id].reg = DAC_DACCTRL_ENABLE | DAC_DACCTRL_REFRESH(2) | DAC_DACCTRL_CCTRL_CC12M; - // Enable DAC and wait to be ready - dac->CTRLA.bit.ENABLE = 1; - while (dac->SYNCBUSY.bit.ENABLE) { + // Modify DAC config - requires disabling see Section 47.6.2.3 of data sheet + if (!dac_init[self->id]) { + // Disable DAC and wait + dac->CTRLA.bit.ENABLE = 0; + while (dac->SYNCBUSY.bit.ENABLE) { + } + + // Modify configuration + dac->DACCTRL[self->id].reg = DAC_DACCTRL_ENABLE | \ + DAC_DACCTRL_REFRESH(2) | DAC_DACCTRL_CCTRL_CC12M; + dac->DATA[self->id].reg = 0; + dac_init[self->id] = true; + + // Enable DAC and wait + dac->CTRLA.bit.ENABLE = 1; + while (dac->SYNCBUSY.bit.ENABLE) { + } } + #endif // Set the port as given in self->gpio_id as DAC From c69f0e4eeeb1058ac4a4b1b5b5d1896371e0ecd2 Mon Sep 17 00:00:00 2001 From: "I. Tomita" Date: Thu, 19 Oct 2023 12:54:37 +0300 Subject: [PATCH 222/546] samd/samd_qspiflash: Correct QSPI baud calculation. The QSPI baud is derived from the AHB clock, not from the APB (peripheral) clock. Datasheet: The QSPI Baud rate clock is generated by dividing the module clock (CLK_QSPI_AHB) by a value between 1 and 255. As previously implemented, all baudrates are 2.5 times greater than expected. Signed-off-by: I. Tomita --- ports/samd/samd_qspiflash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/samd/samd_qspiflash.c b/ports/samd/samd_qspiflash.c index d027a0495f3c0..e25ddf21efe8f 100644 --- a/ports/samd/samd_qspiflash.c +++ b/ports/samd/samd_qspiflash.c @@ -238,7 +238,7 @@ static void wait_for_flash_ready(void) { } static uint8_t get_baud(int32_t freq_mhz) { - int baud = get_peripheral_freq() / (freq_mhz * 1000000) - 1; + int baud = get_cpu_freq() / (freq_mhz * 1000000) - 1; if (baud < 1) { baud = 1; } From 865a4c8bf6868ce488347a76f5f3db8e0dabd11d Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Tue, 21 Jan 2025 08:48:25 +0200 Subject: [PATCH 223/546] esp32: Add support for IDF v5.4. Add WIFI_AUTH_WPA3_ENTERPRISE and WIFI_AUTH_WPA2_WPA3_ENTERPRISE, and update PPP callback signature for latest lwIP. Co-authored-by: Daniel van de Giessen Signed-off-by: IhorNehrutsa --- ports/esp32/README.md | 2 +- ports/esp32/network_ppp.c | 9 +++++++-- ports/esp32/network_wlan.c | 8 +++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index bc24e5cd71ad6..8597c85ec3103 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.2, v5.2.2, and v5.3. +Currently MicroPython supports v5.2, v5.2.2, v5.3 and v5.4. 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). diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index 5c41cf948c1ed..4dd5a3718c295 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -100,7 +100,12 @@ static mp_obj_t ppp_make_new(mp_obj_t stream) { } MP_DEFINE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj, ppp_make_new); -static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +static u32_t ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx) +#else +static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) +#endif +{ ppp_if_obj_t *self = ctx; mp_obj_t stream = self->stream; @@ -109,7 +114,7 @@ static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) } int err; - return mp_stream_rw(stream, data, len, &err, MP_STREAM_RW_WRITE); + return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); } static void pppos_client_task(void *self_in) { diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index fed81d28cb6a6..e85d1328fdc2b 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -763,6 +763,10 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) { MP_ROM_QSTR(MP_QSTR_SEC_DPP), MP_ROM_INT(WIFI_AUTH_DPP) }, #endif + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) + { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_ENT), MP_ROM_INT(WIFI_AUTH_WPA3_ENTERPRISE) }, + { MP_ROM_QSTR(MP_QSTR_SEC_WPA2_WPA3_ENT), MP_ROM_INT(WIFI_AUTH_WPA2_WPA3_ENTERPRISE) }, + #endif { MP_ROM_QSTR(MP_QSTR_PM_NONE), MP_ROM_INT(WIFI_PS_NONE) }, { MP_ROM_QSTR(MP_QSTR_PM_PERFORMANCE), MP_ROM_INT(WIFI_PS_MIN_MODEM) }, @@ -770,7 +774,9 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { }; static MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +_Static_assert(WIFI_AUTH_MAX == 16, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types_generic.h"); +#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) _Static_assert(WIFI_AUTH_MAX == 14, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types_generic.h"); #elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) _Static_assert(WIFI_AUTH_MAX == 13, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); From a4ab847688406bb31eb531e07155bf173a8785aa Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Tue, 21 Jan 2025 09:55:55 +0200 Subject: [PATCH 224/546] py/persistentcode: Initialize prelude_ptr to prevent compiler warning. The esp32 IDF toolchain can give a "may be used uninitialized" warning, at least for ESP32-S3 with gcc 14.2.0. Silence that warning by initializing the variable with NULL. Co-authored-by: Daniel van de Giessen Signed-off-by: IhorNehrutsa --- py/persistentcode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/persistentcode.c b/py/persistentcode.c index 0843d1a2c52e6..840ee49d3e4eb 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -402,7 +402,7 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co #if MICROPY_EMIT_MACHINE_CODE } else { - const uint8_t *prelude_ptr; + const uint8_t *prelude_ptr = NULL; #if MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE if (kind == MP_CODE_NATIVE_PY) { // Executable code cannot be accessed byte-wise on this architecture, so copy From 7b3f189b1723fe642f122a3b7826d16fe32f801a Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 8 Feb 2024 16:56:52 -0800 Subject: [PATCH 225/546] tests/basics/nanbox_smallint.py: Fix incorrect use of int() in test. The literal is in base 16 but int()'s default radix in CPython is 10, not 0. Signed-off-by: Jeff Epler --- tests/basics/nanbox_smallint.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/basics/nanbox_smallint.py b/tests/basics/nanbox_smallint.py index b3a502e447e3a..9451ab3284661 100644 --- a/tests/basics/nanbox_smallint.py +++ b/tests/basics/nanbox_smallint.py @@ -23,17 +23,17 @@ raise SystemExit micropython.heap_lock() -print(int("0x80000000")) +print(int("0x80000000", 16)) micropython.heap_unlock() # This is the most positive small integer. micropython.heap_lock() -print(int("0x3fffffffffff")) +print(int("0x3fffffffffff", 16)) micropython.heap_unlock() # This is the most negative small integer. micropython.heap_lock() -print(int("-0x3fffffffffff") - 1) +print(int("-0x3fffffffffff", 16) - 1) micropython.heap_unlock() x = 1 From 13b13d1fdd05549d504eeded0b5aa8871d5e5dcf Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 3 Jan 2024 19:31:35 -0600 Subject: [PATCH 226/546] py/parsenum: Throw an exception for invalid int literals like "01". This includes making int("01") parse in base 10 like standard Python. When a base of 0 is specified it means auto-detect based on the prefix, and literals begining with 0 (except when the literal is all 0's) like "01" are then invalid and now throw an exception. The new error message is different from CPython. It says e.g., `SyntaxError: invalid syntax for integer with base 0: '09'` Additional test cases were added to cover the changed & added code. Co-authored-by: Damien George Signed-off-by: Jeff Epler --- py/objint.c | 2 +- py/parsenum.c | 4 ++-- py/parsenumbase.c | 29 +++++++++++------------------ tests/basics/int1.py | 15 +++++++++++++++ tests/basics/lexer.py | 8 ++++++++ 5 files changed, 37 insertions(+), 21 deletions(-) diff --git a/py/objint.c b/py/objint.c index 773e180343aeb..4be6009a440e4 100644 --- a/py/objint.c +++ b/py/objint.c @@ -55,7 +55,7 @@ static mp_obj_t mp_obj_int_make_new(const mp_obj_type_t *type_in, size_t n_args, return o; } else if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_READ)) { // a textual representation, parse it - return mp_parse_num_integer(bufinfo.buf, bufinfo.len, 0, NULL); + return mp_parse_num_integer(bufinfo.buf, bufinfo.len, 10, NULL); #if MICROPY_PY_BUILTINS_FLOAT } else if (mp_obj_is_float(args[0])) { return mp_obj_new_int_from_float(mp_obj_float_get(args[0])); diff --git a/py/parsenum.c b/py/parsenum.c index b33ffb6ff23e0..27d6641198730 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -151,13 +151,13 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m raise_exc(exc, lex); #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL mp_obj_t exc = mp_obj_new_exception_msg_varg(&mp_type_ValueError, - MP_ERROR_TEXT("invalid syntax for integer with base %d"), base); + MP_ERROR_TEXT("invalid syntax for integer with base %d"), base == 1 ? 0 : base); raise_exc(exc, lex); #else vstr_t vstr; mp_print_t print; vstr_init_print(&vstr, 50, &print); - mp_printf(&print, "invalid syntax for integer with base %d: ", base); + mp_printf(&print, "invalid syntax for integer with base %d: ", base == 1 ? 0 : base); mp_str_print_quoted(&print, str_val_start, top - str_val_start, true); mp_obj_t exc = mp_obj_new_exception_arg1(&mp_type_ValueError, mp_obj_new_str_from_utf8_vstr(&vstr)); diff --git a/py/parsenumbase.c b/py/parsenumbase.c index 94523a666d325..cc3275c456e40 100644 --- a/py/parsenumbase.c +++ b/py/parsenumbase.c @@ -30,35 +30,28 @@ // find real radix base, and strip preceding '0x', '0o' and '0b' // puts base in *base, and returns number of bytes to skip the prefix +// in base-0, puts 1 in *base to indicate a number that starts with 0, to provoke a +// ValueError if it's not all-digits-zero. size_t mp_parse_num_base(const char *str, size_t len, int *base) { const byte *p = (const byte *)str; if (len <= 1) { goto no_prefix; } unichar c = *(p++); - if ((*base == 0 || *base == 16) && c == '0') { - c = *(p++); - if ((c | 32) == 'x') { + if (c == '0') { + c = *(p++) | 32; + int b = *base; + if (c == 'x' && !(b & ~16)) { *base = 16; - } else if (*base == 0 && (c | 32) == 'o') { + } else if (c == 'o' && !(b & ~8)) { *base = 8; - } else if (*base == 0 && (c | 32) == 'b') { + } else if (c == 'b' && !(b & ~2)) { *base = 2; } else { - if (*base == 0) { - *base = 10; - } - p -= 2; - } - } else if (*base == 8 && c == '0') { - c = *(p++); - if ((c | 32) != 'o') { - p -= 2; - } - } else if (*base == 2 && c == '0') { - c = *(p++); - if ((c | 32) != 'b') { p -= 2; + if (b == 0) { + *base = 1; + } } } else { p--; diff --git a/tests/basics/int1.py b/tests/basics/int1.py index 2d92105c73e88..94723af4d00b4 100644 --- a/tests/basics/int1.py +++ b/tests/basics/int1.py @@ -13,6 +13,7 @@ print(int('+1')) print(int('-1')) print(int('01')) +print(int('00')) print(int('9')) print(int('10')) print(int('+10')) @@ -31,6 +32,7 @@ print(int('0', 10)) print(int('1', 10)) print(int(' \t 1 \t ', 10)) +print(int(' \t 00 \t ', 10)) print(int('11', 10)) print(int('11', 16)) print(int('11', 8)) @@ -52,6 +54,17 @@ print(int('0o12 \t ', 8)) print(int(b"12", 10)) print(int(b"12")) +print(int('000 ', 0)) +print(int('000 ', 2)) +print(int('000 ', 8)) +print(int('000 ', 10)) +print(int('000 ', 16)) +print(int('000 ', 36)) +print(int('010 ', 2)) +print(int('010 ', 8)) +print(int('010 ', 10)) +print(int('010 ', 16)) +print(int('010 ', 36)) def test(value, base): @@ -79,6 +92,8 @@ def test(value, base): test('0xg', 16) test('1 1', 16) test('123', 37) +test('01', 0) +test('01 ', 0) # check that we don't parse this as a floating point number print(0x1e+1) diff --git a/tests/basics/lexer.py b/tests/basics/lexer.py index 181d62db1aadb..addb8a13df36a 100644 --- a/tests/basics/lexer.py +++ b/tests/basics/lexer.py @@ -83,3 +83,11 @@ def a(x): exec(r"'\U0000000'") except SyntaxError: print("SyntaxError") + +# Properly formed integer literals +print(eval("00")) +# badly formed integer literals +try: + eval("01") +except SyntaxError: + print("SyntaxError") From bfc0d7b0b97cbf52db892775b0f371dd06d2ba08 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 23 Jan 2025 23:55:57 +0100 Subject: [PATCH 227/546] py/emitnative: Optimise Viper register offset load/stores on Xtensa. This commit improves the emitted code sequences for address generation in the Viper subsystem when loading/storing 16 and 32 bit values via a register offset. The Xtensa opcodes ADDX2 and ADDX4 are used to avoid performing the extra shifts to align the final operation offset. Those opcodes are available on both xtensa and xtensawin MicroPython architectures. Signed-off-by: Alessandro Gatti --- py/asmxtensa.h | 8 ++++++++ py/emitnative.c | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/py/asmxtensa.h b/py/asmxtensa.h index f226624a82631..d2f37bf828e86 100644 --- a/py/asmxtensa.h +++ b/py/asmxtensa.h @@ -143,6 +143,14 @@ static inline void asm_xtensa_op_addi(asm_xtensa_t *as, uint reg_dest, uint reg_ asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 12, reg_src, reg_dest, imm8 & 0xff)); } +static inline void asm_xtensa_op_addx2(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 9, reg_dest, reg_src_a, reg_src_b)); +} + +static inline void asm_xtensa_op_addx4(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 10, reg_dest, reg_src_a, reg_src_b)); +} + static inline void asm_xtensa_op_and(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 1, reg_dest, reg_src_a, reg_src_b)); } diff --git a/py/emitnative.c b/py/emitnative.c index 66c345b233d76..7d856e13f753a 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -1625,6 +1625,11 @@ static void emit_native_load_subscr(emit_t *emit) { } case VTYPE_PTR16: { // pointer to 16-bit memory + #if N_XTENSA || N_XTENSAWIN + asm_xtensa_op_addx2(emit->as, REG_ARG_1, reg_index, REG_ARG_1); + asm_xtensa_op_l16ui(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_LOAD16_REG_REG(emit->as, REG_RET, REG_ARG_1); // load from (base+2*index) @@ -1637,6 +1642,10 @@ static void emit_native_load_subscr(emit_t *emit) { asm_rv32_opcode_cadd(emit->as, REG_ARG_1, REG_TEMP2); asm_rv32_opcode_lw(emit->as, REG_RET, REG_ARG_1, 0); break; + #elif N_XTENSA || N_XTENSAWIN + asm_xtensa_op_addx4(emit->as, REG_ARG_1, reg_index, REG_ARG_1); + asm_xtensa_op_l32i_n(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 @@ -1900,6 +1909,10 @@ static void emit_native_store_subscr(emit_t *emit) { #if N_ARM asm_arm_strh_reg_reg_reg(emit->as, reg_value, REG_ARG_1, reg_index); break; + #elif N_XTENSA || N_XTENSAWIN + asm_xtensa_op_addx2(emit->as, REG_ARG_1, reg_index, REG_ARG_1); + asm_xtensa_op_s16i(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 @@ -1916,6 +1929,10 @@ static void emit_native_store_subscr(emit_t *emit) { asm_rv32_opcode_cadd(emit->as, REG_ARG_1, REG_TEMP2); asm_rv32_opcode_sw(emit->as, reg_value, REG_ARG_1, 0); break; + #elif N_XTENSA || N_XTENSAWIN + asm_xtensa_op_addx4(emit->as, REG_ARG_1, reg_index, REG_ARG_1); + asm_xtensa_op_s32i_n(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 40585eaa8f1b603f0094b73764e8ce5623812ecf Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 24 Jan 2025 00:20:39 +0100 Subject: [PATCH 228/546] py/emitnative: Emit shorter exception handler entry code on RV32. This commit improves the RV32 code sequence that is emitted if a function needs to set up an exception handler as its prologue. The old code would clear a temporary register and then copy that value to places that needed to be initialised with zero values. On RV32 there's a dedicated register that's hardwired to be equal to zero, which allows us to bypass the extra register clear and use the zero register to initialise values. Signed-off-by: Alessandro Gatti --- py/asmrv32.h | 2 ++ py/emitnative.c | 15 ++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/py/asmrv32.h b/py/asmrv32.h index 584e3ffd22436..b09f48eb12f66 100644 --- a/py/asmrv32.h +++ b/py/asmrv32.h @@ -694,6 +694,7 @@ static inline void asm_rv32_opcode_xori(asm_rv32_t *state, mp_uint_t rd, mp_uint #define REG_LOCAL_1 ASM_RV32_REG_S3 #define REG_LOCAL_2 ASM_RV32_REG_S4 #define REG_LOCAL_3 ASM_RV32_REG_S5 +#define REG_ZERO ASM_RV32_REG_ZERO 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); @@ -756,6 +757,7 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_ #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) +#define ASM_CLR_REG(state, rd) #endif diff --git a/py/emitnative.c b/py/emitnative.c index 7d856e13f753a..f846e8bb4e2cb 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -288,6 +288,11 @@ struct _emit_t { ASM_T *as; }; +#ifndef REG_ZERO +#define REG_ZERO REG_TEMP0 +#define ASM_CLR_REG(state, rd) ASM_XOR_REG_REG(state, rd, rd) +#endif + static void emit_load_reg_with_object(emit_t *emit, int reg, mp_obj_t obj); static void emit_native_global_exc_entry(emit_t *emit); static void emit_native_global_exc_exit(emit_t *emit); @@ -1200,12 +1205,12 @@ static void emit_native_global_exc_entry(emit_t *emit) { ASM_JUMP_IF_REG_ZERO(emit->as, REG_RET, start_label, true); } else { // Clear the unwind state - ASM_XOR_REG_REG(emit->as, REG_TEMP0, REG_TEMP0); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_UNWIND(emit), REG_TEMP0); + ASM_CLR_REG(emit->as, REG_ZERO); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_UNWIND(emit), REG_ZERO); // 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); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_ZERO); // Put PC of start code block into REG_LOCAL_1 ASM_MOV_REG_PCREL(emit->as, REG_LOCAL_1, start_label); @@ -1221,8 +1226,8 @@ static void emit_native_global_exc_entry(emit_t *emit) { ASM_JUMP_IF_REG_NONZERO(emit->as, REG_RET, global_except_label, true); // Clear PC of current code block, and jump there to resume execution - ASM_XOR_REG_REG(emit->as, REG_TEMP0, REG_TEMP0); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_PC(emit), REG_TEMP0); + ASM_CLR_REG(emit->as, REG_ZERO); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_PC(emit), REG_ZERO); ASM_JUMP_REG(emit->as, REG_LOCAL_1); // Global exception handler: check for valid exception handler From 55ca3fd67512555707304c6b68b836eb89f09d1c Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 24 Jan 2025 09:22:36 +0100 Subject: [PATCH 229/546] py/emitnative: Optimise Viper immediate offset load/stores on Xtensa. This commit introduces the ability to emit optimised code paths on Xtensa for load/store operations indexed via an immediate offset. If an immediate offset for a load/store operation is within a certain range that allows it to be embedded into an available opcode then said opcode is emitted instead of the generic code sequence. Signed-off-by: Alessandro Gatti --- py/emitnative.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/py/emitnative.c b/py/emitnative.c index f846e8bb4e2cb..82ee729d3d50f 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -1550,6 +1550,11 @@ static void emit_native_load_subscr(emit_t *emit) { asm_rv32_opcode_lbu(emit->as, REG_RET, reg_base, index_value); break; } + #elif N_XTENSA || N_XTENSAWIN + if (index_value > 0 && index_value < 256) { + asm_xtensa_op_l8ui(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); @@ -1573,6 +1578,11 @@ static void emit_native_load_subscr(emit_t *emit) { asm_rv32_opcode_lhu(emit->as, REG_RET, reg_base, index_value << 1); break; } + #elif N_XTENSA || N_XTENSAWIN + if (index_value > 0 && index_value < 256) { + asm_xtensa_op_l16ui(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 << 1); @@ -1596,6 +1606,10 @@ static void emit_native_load_subscr(emit_t *emit) { asm_rv32_opcode_lw(emit->as, REG_RET, reg_base, index_value << 2); break; } + #elif N_XTENSA || N_XTENSAWIN + if (index_value > 0 && index_value < 256) { + asm_xtensa_l32i_optimised(emit->as, REG_RET, reg_base, index_value); + } #endif need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2); @@ -1812,6 +1826,11 @@ static void emit_native_store_subscr(emit_t *emit) { asm_rv32_opcode_sb(emit->as, reg_value, reg_base, index_value); break; } + #elif N_XTENSA || N_XTENSAWIN + if (index_value > 0 && index_value < 256) { + asm_xtensa_op_s8i(emit->as, REG_RET, reg_base, index_value); + break; + } #endif ASM_MOV_REG_IMM(emit->as, reg_index, index_value); #if N_ARM @@ -1838,6 +1857,11 @@ static void emit_native_store_subscr(emit_t *emit) { asm_rv32_opcode_sh(emit->as, reg_value, reg_base, index_value << 1); break; } + #elif N_XTENSA || N_XTENSAWIN + if (index_value > 0 && index_value < 256) { + asm_xtensa_op_s16i(emit->as, REG_RET, reg_base, index_value); + 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 @@ -1860,6 +1884,10 @@ static void emit_native_store_subscr(emit_t *emit) { asm_rv32_opcode_sw(emit->as, reg_value, reg_base, index_value << 2); break; } + #elif N_XTENSA || N_XTENSAWIN + if (index_value > 0 && index_value < 256) { + asm_xtensa_s32i_optimised(emit->as, REG_RET, reg_base, index_value); + } #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); From 0d46e45a1f72ee61a153c41aaaf6c63818ccffb0 Mon Sep 17 00:00:00 2001 From: Hans Maerki Date: Sun, 26 Jan 2025 18:00:38 +0100 Subject: [PATCH 230/546] tools/mpremote: Avoid initial blocking read in read_until(). If the target does not return any data then `read_until()` will block indefinitely. Fix this by making the initial read part of the general read look, which always checks `inWaiting() > 0` before reading from the serial device. Also added the UART timeout to the constructor. This is not currently used but may be used as an additional safeguard. Signed-off-by: Hans Maerki --- tools/mpremote/mpremote/transport_serial.py | 30 ++++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tools/mpremote/mpremote/transport_serial.py b/tools/mpremote/mpremote/transport_serial.py index 28ccaf6d8c907..449708a5ee43d 100644 --- a/tools/mpremote/mpremote/transport_serial.py +++ b/tools/mpremote/mpremote/transport_serial.py @@ -42,7 +42,7 @@ class SerialTransport(Transport): - def __init__(self, device, baudrate=115200, wait=0, exclusive=True): + def __init__(self, device, baudrate=115200, wait=0, exclusive=True, timeout=None): self.in_raw_repl = False self.use_raw_paste = True self.device_name = device @@ -52,7 +52,11 @@ def __init__(self, device, baudrate=115200, wait=0, exclusive=True): import serial.tools.list_ports # Set options, and exclusive if pyserial supports it - serial_kwargs = {"baudrate": baudrate, "interCharTimeout": 1} + serial_kwargs = { + "baudrate": baudrate, + "timeout": timeout, + "interCharTimeout": 1, + } if serial.__version__ >= "3.3": serial_kwargs["exclusive"] = exclusive @@ -95,13 +99,20 @@ def close(self): self.serial.close() def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): - # if data_consumer is used then data is not accumulated and the ending must be 1 byte long + """ + min_num_bytes: Obsolete. + ending: Return if 'ending' matches. + timeout [s]: Return if timeout between characters. None: Infinite timeout. + data_consumer: Use callback for incoming characters. + If data_consumer is used then data is not accumulated and the ending must be 1 byte long + + It is not visible to the caller why the function returned. It could be ending or timeout. + """ assert data_consumer is None or len(ending) == 1 + assert isinstance(timeout, (type(None), int, float)) - data = self.serial.read(min_num_bytes) - if data_consumer: - data_consumer(data) - timeout_count = 0 + data = b"" + begin_char_s = time.monotonic() while True: if data.endswith(ending): break @@ -112,10 +123,9 @@ def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): data = new_data else: data = data + new_data - timeout_count = 0 + begin_char_s = time.monotonic() else: - timeout_count += 1 - if timeout is not None and timeout_count >= 100 * timeout: + if timeout is not None and time.monotonic() >= begin_char_s + timeout: break time.sleep(0.01) return data From 03fe9c55ea8a3ef8bede5547476f37d7fca20119 Mon Sep 17 00:00:00 2001 From: Hans Maerki Date: Mon, 27 Jan 2025 07:29:44 +0100 Subject: [PATCH 231/546] tools/mpremote: Introduce timeout_overall for read_until(). And use it in `enter_raw_repl()`. This prevents waiting forever for a serial device that does not respond to the Ctrl-C/Ctrl-D/etc commands and is constantly outputting data. Signed-off-by: Hans Maerki --- tools/mpremote/mpremote/transport_serial.py | 23 +++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tools/mpremote/mpremote/transport_serial.py b/tools/mpremote/mpremote/transport_serial.py index 449708a5ee43d..d3148f9ace318 100644 --- a/tools/mpremote/mpremote/transport_serial.py +++ b/tools/mpremote/mpremote/transport_serial.py @@ -98,11 +98,14 @@ def __init__(self, device, baudrate=115200, wait=0, exclusive=True, timeout=None def close(self): self.serial.close() - def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): + def read_until( + self, min_num_bytes, ending, timeout=10, data_consumer=None, timeout_overall=None + ): """ min_num_bytes: Obsolete. ending: Return if 'ending' matches. timeout [s]: Return if timeout between characters. None: Infinite timeout. + timeout_overall [s]: Return not later than timeout_overall. None: Infinite timeout. data_consumer: Use callback for incoming characters. If data_consumer is used then data is not accumulated and the ending must be 1 byte long @@ -110,9 +113,10 @@ def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): """ assert data_consumer is None or len(ending) == 1 assert isinstance(timeout, (type(None), int, float)) + assert isinstance(timeout_overall, (type(None), int, float)) data = b"" - begin_char_s = time.monotonic() + begin_overall_s = begin_char_s = time.monotonic() while True: if data.endswith(ending): break @@ -127,10 +131,15 @@ def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): else: if timeout is not None and time.monotonic() >= begin_char_s + timeout: break + if ( + timeout_overall is not None + and time.monotonic() >= begin_overall_s + timeout_overall + ): + break time.sleep(0.01) return data - def enter_raw_repl(self, soft_reset=True): + def enter_raw_repl(self, soft_reset=True, timeout_overall=10): self.serial.write(b"\r\x03") # ctrl-C: interrupt any running program # flush input (without relying on serial.flushInput()) @@ -142,7 +151,9 @@ def enter_raw_repl(self, soft_reset=True): self.serial.write(b"\r\x01") # ctrl-A: enter raw REPL if soft_reset: - data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n>") + data = self.read_until( + 1, b"raw REPL; CTRL-B to exit\r\n>", timeout_overall=timeout_overall + ) if not data.endswith(b"raw REPL; CTRL-B to exit\r\n>"): print(data) raise TransportError("could not enter raw repl") @@ -152,12 +163,12 @@ def enter_raw_repl(self, soft_reset=True): # Waiting for "soft reboot" independently to "raw REPL" (done below) # allows boot.py to print, which will show up after "soft reboot" # and before "raw REPL". - data = self.read_until(1, b"soft reboot\r\n") + data = self.read_until(1, b"soft reboot\r\n", timeout_overall=timeout_overall) if not data.endswith(b"soft reboot\r\n"): print(data) raise TransportError("could not enter raw repl") - data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n") + data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n", timeout_overall=timeout_overall) if not data.endswith(b"raw REPL; CTRL-B to exit\r\n"): print(data) raise TransportError("could not enter raw repl") From f29bd5a65e9688222474e54b4f5d0fb127a23db3 Mon Sep 17 00:00:00 2001 From: Jared Hancock Date: Sun, 19 Jan 2025 15:37:55 -0600 Subject: [PATCH 232/546] extmod/modlwip: Fix incorrect peer address for IPv6. For IPv6 connections, the peer address was previously defined as only the first four bytes of the IP address. For IPv6 addresses, this resulted in an incorrect IPv4 address. For instance, receiving a packet via `::recvfrom` from `'fe80::87:e7ff:fe48:629a'` is returned as having a peer address of `'254.128.0.0'` Signed-off-by: Jared Hancock --- extmod/modlwip.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index ce70627c31c60..bcccd363e673c 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -37,7 +37,6 @@ #if MICROPY_PY_LWIP -#include "shared/netutils/netutils.h" #include "modnetwork.h" #include "lwip/init.h" @@ -308,7 +307,7 @@ typedef struct _lwip_socket_obj_t { } connection; } incoming; mp_obj_t callback; - byte peer[4]; + ip_addr_t peer; mp_uint_t peer_port; mp_uint_t timeout; uint16_t recv_offset; @@ -638,7 +637,7 @@ static mp_uint_t lwip_raw_udp_send(lwip_socket_obj_t *socket, const byte *buf, m } // Helper function for recv/recvfrom to handle raw/UDP packets -static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) { +static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, ip_addr_t *ip, mp_uint_t *port, int *_errno) { if (socket->incoming.pbuf == NULL) { if (socket->timeout == 0) { @@ -1132,7 +1131,7 @@ static mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) { mp_raise_OSError(error_lookup_table[-err]); } socket->peer_port = (mp_uint_t)port; - memcpy(socket->peer, &dest, sizeof(socket->peer)); + memcpy(&socket->peer, &dest, sizeof(socket->peer)); MICROPY_PY_LWIP_EXIT // And now we wait... @@ -1294,13 +1293,13 @@ static mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { mp_int_t len = mp_obj_get_int(len_in); vstr_t vstr; vstr_init_len(&vstr, len); - byte ip[4]; + ip_addr_t ip; mp_uint_t port; mp_uint_t ret = 0; switch (socket->type) { case MOD_NETWORK_SOCK_STREAM: { - memcpy(ip, &socket->peer, sizeof(socket->peer)); + memcpy(&ip, &socket->peer, sizeof(socket->peer)); port = (mp_uint_t)socket->peer_port; ret = lwip_tcp_receive(socket, (byte *)vstr.buf, len, &_errno); break; @@ -1309,7 +1308,7 @@ static mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { #if MICROPY_PY_LWIP_SOCK_RAW case MOD_NETWORK_SOCK_RAW: #endif - ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, ip, &port, &_errno); + ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, &ip, &port, &_errno); break; } if (ret == -1) { @@ -1323,7 +1322,8 @@ static mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { vstr.len = ret; tuple[0] = mp_obj_new_bytes_from_vstr(&vstr); } - tuple[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); + tuple[1] = lwip_format_inet_addr(&ip, port); + return mp_obj_new_tuple(2, tuple); } static MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recvfrom_obj, lwip_socket_recvfrom); From ec527a11136e1c83ddf51099a418ea785215284f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 14 Jan 2025 11:38:43 +1100 Subject: [PATCH 233/546] esp32: Disable component manager when running 'make submodules'. - ECHO_SUBMODULES=1 exits CMake early. With idf_component_manager 1.x this seems to leave the managed_components directory in a state that causes later builds to fail. - Looks like the component manager isn't needed for this step, so disable it. This invocation logs a warning (not visible in normal output) but completes successfully and returns the correct list of submodules. Signed-off-by: Angus Gratton --- ports/esp32/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 8888180766b79..1c2fa82ce1265 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -112,6 +112,6 @@ size-files: # output and passes the list of submodules to py/mkrules.mk which does the # `git submodule init` on each. submodules: - @GIT_SUBMODULES=$$(idf.py $(IDFPY_FLAGS) -B $(BUILD)/submodules -D ECHO_SUBMODULES=1 build 2>&1 | \ + @GIT_SUBMODULES=$$(IDF_COMPONENT_MANAGER=0 idf.py $(IDFPY_FLAGS) -B $(BUILD)/submodules -D ECHO_SUBMODULES=1 build 2>&1 | \ grep '^GIT_SUBMODULES=' | cut -d= -f2); \ $(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="$${GIT_SUBMODULES}" submodules From 43e3ab6131b11d7fb7409a35fe5127317ba27397 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 14 Jan 2025 11:39:32 +1100 Subject: [PATCH 234/546] esp32: Don't add TinyUSB files to an ECHO_SUBMODULES build. Similar to other places, CMake will error out if this file doesn't exist yet but we don't want this if we're only getting the list of submodules. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/esp32_common.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index b6bfcdacc3cb4..e03d2ff11aada 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -60,7 +60,7 @@ list(APPEND MICROPY_SOURCE_DRIVERS ) string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/tinyusb) -if(MICROPY_PY_TINYUSB) +if(MICROPY_PY_TINYUSB AND NOT ECHO_SUBMODULES) set(TINYUSB_SRC "${MICROPY_DIR}/lib/tinyusb/src") string(TOUPPER OPT_MCU_${IDF_TARGET} tusb_mcu) From 22353e9e1ed5bf8bc5bad179896ca2877f7511e2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 14 Jan 2025 11:50:27 +1100 Subject: [PATCH 235/546] py/mkrules: Add GIT_SUBMODULES_FAIL_IF_EMPTY flag for CMake ports. The way CMake gathers the submodule list, it can quietly be empty if the previous step fails. This makes it an explicit error. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/Makefile | 2 +- ports/rp2/Makefile | 2 +- py/mkrules.mk | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 1c2fa82ce1265..1ce4d97208da7 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -114,4 +114,4 @@ size-files: submodules: @GIT_SUBMODULES=$$(IDF_COMPONENT_MANAGER=0 idf.py $(IDFPY_FLAGS) -B $(BUILD)/submodules -D ECHO_SUBMODULES=1 build 2>&1 | \ grep '^GIT_SUBMODULES=' | cut -d= -f2); \ - $(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="$${GIT_SUBMODULES}" submodules + $(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="$${GIT_SUBMODULES}" GIT_SUBMODULES_FAIL_IF_EMPTY=1 submodules diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile index afa21cc7a4383..200899d338e6a 100644 --- a/ports/rp2/Makefile +++ b/ports/rp2/Makefile @@ -76,4 +76,4 @@ submodules: $(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="lib/pico-sdk" submodules @GIT_SUBMODULES=$$(cmake -B $(BUILD)/submodules -DECHO_SUBMODULES=1 ${CMAKE_ARGS} -S . 2>&1 | \ grep '^GIT_SUBMODULES=' | cut -d= -f2); \ - $(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="$${GIT_SUBMODULES}" submodules + $(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="$${GIT_SUBMODULES}" GIT_SUBMODULES_FAIL_IF_EMPTY=1 submodules diff --git a/py/mkrules.mk b/py/mkrules.mk index 74978b0d15bf2..373bda8996f19 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -257,6 +257,13 @@ ifneq ($(GIT_SUBMODULES),) $(Q)cd $(TOP) && git submodule sync $(GIT_SUBMODULES) $(Q)cd $(TOP) && git submodule update --init --filter=blob:none $(GIT_SUBMODULES) || \ git submodule update --init $(GIT_SUBMODULES) +else +ifeq ($(GIT_SUBMODULES_FAIL_IF_EMPTY),1) + # If you see this error, it may mean the internal step run by the port's build + # system to find git submodules has failed. Double-check dependencies are set correctly. + $(ECHO) "Internal build error: The submodule list should not be empty." + exit 1 +endif endif .PHONY: submodules From abb13b1e1e37c42876f69c83b96c9ead3285a9a7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 25 Jan 2025 17:45:40 +1100 Subject: [PATCH 236/546] extmod/lwip-include: Factor common lwIP config into lwipopts_common.h. This lwIP configuration file has options that are common to all ports, and the ports are updated to use this file. This change is a no-op, the lwIP configuration remains the same for the four ports using this common file. This reduces code duplication, keeps the ports in sync, and makes it easier to update the configuration for all ports at once. Signed-off-by: Damien George --- extmod/lwip-include/lwipopts_common.h | 109 ++++++++++++++++++++++++++ ports/mimxrt/lwip_inc/lwipopts.h | 47 +---------- ports/renesas-ra/lwip_inc/lwipopts.h | 42 +--------- ports/rp2/lwip_inc/lwipopts.h | 56 +------------ ports/stm32/lwip_inc/lwipopts.h | 77 +----------------- 5 files changed, 122 insertions(+), 209 deletions(-) create mode 100644 extmod/lwip-include/lwipopts_common.h diff --git a/extmod/lwip-include/lwipopts_common.h b/extmod/lwip-include/lwipopts_common.h new file mode 100644 index 0000000000000..8688db064ef6f --- /dev/null +++ b/extmod/lwip-include/lwipopts_common.h @@ -0,0 +1,109 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_LWIPOPTS_COMMON_H +#define MICROPY_INCLUDED_LWIPOPTS_COMMON_H + +#include "py/mpconfig.h" + +// This sys-arch protection is not needed. +// Ports either protect lwIP code with flags, or run it at PendSV priority. +#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) +#define SYS_ARCH_PROTECT(lev) do { } while (0) +#define SYS_ARCH_UNPROTECT(lev) do { } while (0) + +#define NO_SYS 1 +#define SYS_LIGHTWEIGHT_PROT 1 +#define MEM_ALIGNMENT 4 + +#define LWIP_CHKSUM_ALGORITHM 3 +#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 + +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_RAW 1 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define LWIP_STATS 0 +#define LWIP_NETIF_HOSTNAME 1 + +#define LWIP_DHCP 1 +#define LWIP_DHCP_CHECK_LINK_UP 1 +#define LWIP_DHCP_DOES_ACD_CHECK 0 // to speed DHCP up +#define LWIP_DNS 1 +#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 +#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) + +#define SO_REUSE 1 +#define TCP_LISTEN_BACKLOG 1 + +// TCP memory settings. +// Default lwIP settings takes 15800 bytes; TCP d/l: 380k/s local, 7.2k/s remote; TCP u/l is very slow. +#ifndef MEM_SIZE + +#if 0 +// lwIP takes 19159 bytes; TCP d/l and u/l are around 320k/s on local network. +#define MEM_SIZE (5000) +#define TCP_WND (4 * TCP_MSS) +#define TCP_SND_BUF (4 * TCP_MSS) +#endif + +#if 1 +// lwIP takes 26700 bytes; TCP dl/ul are around 750/600 k/s on local network. +#define MEM_SIZE (8000) +#define TCP_MSS (800) +#define TCP_WND (8 * TCP_MSS) +#define TCP_SND_BUF (8 * TCP_MSS) +#define MEMP_NUM_TCP_SEG (32) +#endif + +#if 0 +// lwIP takes 45600 bytes; TCP dl/ul are around 1200/1000 k/s on local network. +#define MEM_SIZE (16000) +#define TCP_MSS (1460) +#define TCP_WND (8 * TCP_MSS) +#define TCP_SND_BUF (8 * TCP_MSS) +#define MEMP_NUM_TCP_SEG (32) +#endif + +#endif // MEM_SIZE + +// Needed for PPP. +#define sys_jiffies sys_now + +typedef uint32_t sys_prot_t; + +#endif // MICROPY_INCLUDED_LWIPOPTS_COMMON_H diff --git a/ports/mimxrt/lwip_inc/lwipopts.h b/ports/mimxrt/lwip_inc/lwipopts.h index 411ca4b60f342..cf25597f95c1f 100644 --- a/ports/mimxrt/lwip_inc/lwipopts.h +++ b/ports/mimxrt/lwip_inc/lwipopts.h @@ -1,56 +1,15 @@ #ifndef MICROPY_INCLUDED_MIMXRT_LWIP_LWIPOPTS_H #define MICROPY_INCLUDED_MIMXRT_LWIP_LWIPOPTS_H -#include - -// This protection is not needed, instead we execute all lwIP code at PendSV priority -#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) -#define SYS_ARCH_PROTECT(lev) do { } while (0) -#define SYS_ARCH_UNPROTECT(lev) do { } while (0) - -#define NO_SYS 1 -#define SYS_LIGHTWEIGHT_PROT 1 -#define MEM_ALIGNMENT 4 - -#define LWIP_CHKSUM_ALGORITHM 3 -// The checksum flags are set in eth.c -#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 - -#define LWIP_ARP 1 -#define LWIP_ETHERNET 1 -#define LWIP_RAW 1 -#define LWIP_NETCONN 0 -#define LWIP_SOCKET 0 -#define LWIP_STATS 0 -#define LWIP_NETIF_HOSTNAME 1 #define LWIP_NETIF_EXT_STATUS_CALLBACK 1 #define LWIP_IPV6 0 -#define LWIP_DHCP 1 -#define LWIP_DHCP_CHECK_LINK_UP 1 -#define LWIP_DHCP_DOES_ACD_CHECK 0 // to speed DHCP up -#define LWIP_DNS 1 -#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 -#define LWIP_MDNS_RESPONDER 1 -#define LWIP_IGMP 1 -#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) - -#define SO_REUSE 1 -#define TCP_LISTEN_BACKLOG 1 - -extern uint32_t trng_random_u32(void); #define LWIP_RAND() trng_random_u32() -// lwip takes 26700 bytes -#define MEM_SIZE (8000) -#define TCP_MSS (800) -#define TCP_WND (8 * TCP_MSS) -#define TCP_SND_BUF (8 * TCP_MSS) -#define MEMP_NUM_TCP_SEG (32) +// Include common lwIP configuration. +#include "extmod/lwip-include/lwipopts_common.h" -typedef uint32_t sys_prot_t; +extern uint32_t trng_random_u32(void); #endif // MICROPY_INCLUDED_MIMXRT_LWIP_LWIPOPTS_H diff --git a/ports/renesas-ra/lwip_inc/lwipopts.h b/ports/renesas-ra/lwip_inc/lwipopts.h index 74e39022a6a85..77645ae6c3c3d 100644 --- a/ports/renesas-ra/lwip_inc/lwipopts.h +++ b/ports/renesas-ra/lwip_inc/lwipopts.h @@ -1,45 +1,8 @@ #ifndef MICROPY_INCLUDED_RA_LWIP_LWIPOPTS_H #define MICROPY_INCLUDED_RA_LWIP_LWIPOPTS_H -#include - -// This protection is not needed, instead protect lwIP code with flags -#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) -#define SYS_ARCH_PROTECT(lev) do { } while (0) -#define SYS_ARCH_UNPROTECT(lev) do { } while (0) - -#define NO_SYS 1 -#define SYS_LIGHTWEIGHT_PROT 1 -#define MEM_ALIGNMENT 4 - -#define LWIP_CHKSUM_ALGORITHM 3 -#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 - -#define LWIP_ARP 1 -#define LWIP_ETHERNET 1 -#define LWIP_RAW 1 -#define LWIP_NETCONN 0 -#define LWIP_SOCKET 0 -#define LWIP_STATS 0 -#define LWIP_NETIF_HOSTNAME 1 - #define LWIP_IPV6 0 -#define LWIP_DHCP 1 -#define LWIP_DHCP_CHECK_LINK_UP 1 -#define LWIP_DHCP_DOES_ACD_CHECK 0 // to speed DHCP up -#define LWIP_DNS 1 -#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 -#define LWIP_MDNS_RESPONDER 1 -#define LWIP_IGMP 1 -#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) - -#define SO_REUSE 1 -#define TCP_LISTEN_BACKLOG 1 - -extern uint32_t rng_read(void); #define LWIP_RAND() rng_read() #define MEM_SIZE (16 * 1024) @@ -51,6 +14,9 @@ extern uint32_t rng_read(void); #define TCP_QUEUE_OOSEQ (1) #define MEMP_NUM_TCP_SEG (2 * TCP_SND_QUEUELEN) -typedef uint32_t sys_prot_t; +// Include common lwIP configuration. +#include "extmod/lwip-include/lwipopts_common.h" + +extern uint32_t rng_read(void); #endif // MICROPY_INCLUDED_RA_LWIP_LWIPOPTS_H diff --git a/ports/rp2/lwip_inc/lwipopts.h b/ports/rp2/lwip_inc/lwipopts.h index 15679e0cf018d..da45a5735b28c 100644 --- a/ports/rp2/lwip_inc/lwipopts.h +++ b/ports/rp2/lwip_inc/lwipopts.h @@ -1,28 +1,6 @@ #ifndef MICROPY_INCLUDED_RP2_LWIP_LWIPOPTS_H #define MICROPY_INCLUDED_RP2_LWIP_LWIPOPTS_H -#include "py/mpconfig.h" - -// This protection is not needed, instead protect lwIP code with flags -#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) -#define SYS_ARCH_PROTECT(lev) do { } while (0) -#define SYS_ARCH_UNPROTECT(lev) do { } while (0) - -#define NO_SYS 1 -#define SYS_LIGHTWEIGHT_PROT 1 -#define MEM_ALIGNMENT 4 - -#define LWIP_CHKSUM_ALGORITHM 3 -// The checksum flags are set in eth.c -#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 - -#define LWIP_ARP 1 -#define LWIP_ETHERNET 1 -#define LWIP_RAW 1 -#define LWIP_NETCONN 0 -#define LWIP_SOCKET 0 -#define LWIP_STATS 0 -#define LWIP_NETIF_HOSTNAME 1 #define LWIP_NETIF_EXT_STATUS_CALLBACK 1 #define LWIP_NETIF_STATUS_CALLBACK 1 @@ -30,40 +8,12 @@ #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 LWIP_DHCP_DOES_ACD_CHECK 0 // to speed DHCP up -#define LWIP_DNS 1 -#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 -#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) - -#define SO_REUSE 1 -#define TCP_LISTEN_BACKLOG 1 - -extern uint32_t rosc_random_u32(void); #define LWIP_RAND() rosc_random_u32() -// lwip takes 26700 bytes -#define MEM_SIZE (8000) -#define TCP_MSS (800) -#define TCP_WND (8 * TCP_MSS) -#define TCP_SND_BUF (8 * TCP_MSS) -#define MEMP_NUM_TCP_SEG (32) +// Include common lwIP configuration. +#include "extmod/lwip-include/lwipopts_common.h" -typedef uint32_t sys_prot_t; - -// Needed for PPP. -#define sys_jiffies sys_now +extern uint32_t rosc_random_u32(void); #endif // MICROPY_INCLUDED_RP2_LWIP_LWIPOPTS_H diff --git a/ports/stm32/lwip_inc/lwipopts.h b/ports/stm32/lwip_inc/lwipopts.h index 9e1aa8d0f14c0..9e2402c8dc5dc 100644 --- a/ports/stm32/lwip_inc/lwipopts.h +++ b/ports/stm32/lwip_inc/lwipopts.h @@ -1,89 +1,18 @@ #ifndef MICROPY_INCLUDED_STM32_LWIP_LWIPOPTS_H #define MICROPY_INCLUDED_STM32_LWIP_LWIPOPTS_H -#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) -#define SYS_ARCH_PROTECT(lev) do { } while (0) -#define SYS_ARCH_UNPROTECT(lev) do { } while (0) - -#define NO_SYS 1 -#define SYS_LIGHTWEIGHT_PROT 1 -#define MEM_ALIGNMENT 4 - -#define LWIP_CHKSUM_ALGORITHM 3 -#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 - -#define LWIP_ARP 1 -#define LWIP_ETHERNET 1 -#define LWIP_RAW 1 -#define LWIP_NETCONN 0 -#define LWIP_SOCKET 0 -#define LWIP_STATS 0 -#define LWIP_NETIF_HOSTNAME 1 #define LWIP_NETIF_EXT_STATUS_CALLBACK 1 #define LWIP_LOOPIF_MULTICAST 1 #define LWIP_LOOPBACK_MAX_PBUFS 8 #define LWIP_IPV6 0 -#define LWIP_DHCP 1 -#define LWIP_DHCP_CHECK_LINK_UP 1 -#define LWIP_DHCP_DOES_ACD_CHECK 0 // to speed DHCP up -#define LWIP_DNS 1 -#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 -#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) - -#define SO_REUSE 1 -#define TCP_LISTEN_BACKLOG 1 -extern uint32_t rng_get(void); #define LWIP_RAND() rng_get() -// default -// lwip takes 15800 bytes; TCP d/l: 380k/s local, 7.2k/s remote -// TCP u/l is very slow - -#if 0 -// lwip takes 19159 bytes; TCP d/l and u/l are around 320k/s on local network -#define MEM_SIZE (5000) -#define TCP_WND (4 * TCP_MSS) -#define TCP_SND_BUF (4 * TCP_MSS) -#endif - -#if 1 -// lwip takes 26700 bytes; TCP dl/ul are around 750/600 k/s on local network -#define MEM_SIZE (8000) -#define TCP_MSS (800) -#define TCP_WND (8 * TCP_MSS) -#define TCP_SND_BUF (8 * TCP_MSS) -#define MEMP_NUM_TCP_SEG (32) -#endif +// Include common lwIP configuration. +#include "extmod/lwip-include/lwipopts_common.h" -#if 0 -// lwip takes 45600 bytes; TCP dl/ul are around 1200/1000 k/s on local network -#define MEM_SIZE (16000) -#define TCP_MSS (1460) -#define TCP_WND (8 * TCP_MSS) -#define TCP_SND_BUF (8 * TCP_MSS) -#define MEMP_NUM_TCP_SEG (32) -#endif - -typedef uint32_t sys_prot_t; - -// Needed for PPP. -#define sys_jiffies sys_now +extern uint32_t rng_get(void); #endif // MICROPY_INCLUDED_STM32_LWIP_LWIPOPTS_H From bfb1bee6feba9f06452a0e4b572ec4d15df325cf Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 26 Jan 2025 20:54:14 -0600 Subject: [PATCH 237/546] py/parsenumbase: Favor clarity of code over manual optimisation. Follow up to 13b13d1fdd05549d504eeded0b5aa8871d5e5dcf, based on some testing on godbolt, the manual code optimisation seems unnecessary for code size, at least on gcc x86_64 and ARM, and it's definitely not good for clarity. Signed-off-by: Jeff Epler --- py/parsenumbase.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py/parsenumbase.c b/py/parsenumbase.c index cc3275c456e40..fbf07a119584f 100644 --- a/py/parsenumbase.c +++ b/py/parsenumbase.c @@ -41,11 +41,11 @@ size_t mp_parse_num_base(const char *str, size_t len, int *base) { if (c == '0') { c = *(p++) | 32; int b = *base; - if (c == 'x' && !(b & ~16)) { + if (c == 'x' && (b == 0 || b == 16)) { *base = 16; - } else if (c == 'o' && !(b & ~8)) { + } else if (c == 'o' && (b == 0 || b == 8)) { *base = 8; - } else if (c == 'b' && !(b & ~2)) { + } else if (c == 'b' && (b == 0 || b == 2)) { *base = 2; } else { p -= 2; From 195bf051153cff1d49b3645c51099e9ff6e1d80d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 15 Oct 2024 12:23:28 +1100 Subject: [PATCH 238/546] tests: Add a test for SSL socket memory leaks. Test is for an issue reported on the micropython-lib Discord as effecting the rp2 port umqtt.simple interface when reconnecting with TLS, however it's a more generic problem. Currently this test fails on RPI_PICO_W and ESP32_GENERIC_C3 (and no doubt others). Fixes are in the subsequent commits. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/extmod/ssl_noleak.py | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 tests/extmod/ssl_noleak.py diff --git a/tests/extmod/ssl_noleak.py b/tests/extmod/ssl_noleak.py new file mode 100644 index 0000000000000..870032d58e63e --- /dev/null +++ b/tests/extmod/ssl_noleak.py @@ -0,0 +1,50 @@ +# Ensure that SSLSockets can be allocated sequentially +# without running out of available memory. +try: + import io + import tls +except ImportError: + print("SKIP") + raise SystemExit + +import unittest + + +class TestSocket(io.IOBase): + def write(self, buf): + return len(buf) + + def readinto(self, buf): + return 0 + + def ioctl(self, cmd, arg): + return 0 + + def setblocking(self, value): + pass + + +ITERS = 128 + + +class TLSNoLeaks(unittest.TestCase): + def test_unique_context(self): + for n in range(ITERS): + print(n) + s = TestSocket() + ctx = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT) + ctx.verify_mode = tls.CERT_NONE + s = ctx.wrap_socket(s, do_handshake_on_connect=False) + + def test_shared_context(self): + # Single SSLContext, multiple sockets + ctx = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT) + ctx.verify_mode = tls.CERT_NONE + for n in range(ITERS): + print(n) + s = TestSocket() + s = ctx.wrap_socket(s, do_handshake_on_connect=False) + + +if __name__ == "__main__": + unittest.main() From 97f444bfa04408dd510f118baaea5760b93bd892 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 28 Jan 2025 15:31:18 +1100 Subject: [PATCH 239/546] extmod/mbedtls: Try GC before failing to setup socket on esp32, unix. On mbedTLS ports with non-baremetal configs (mostly esp32, technically also unix port), mbedTLS memory is allocated from the libc heap. This means an old SSL socket may be holding large SSL buffers and preventing a new SSL socket from being allocated. As a workaround, trigger a GC pass and retry before failing outright. This was originally implemented as a global mbedTLS calloc function, but there is complexity around the possibility of C user modules calling into mbedTLS without holding the GIL. It would be interesting to try making a generic version for any malloc which fails, but this would require checking for a Python thread and probably making the GIL recursive. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- extmod/modtls_mbedtls.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/extmod/modtls_mbedtls.c b/extmod/modtls_mbedtls.c index 0a1b8828af5ca..3fd416d72f5ef 100644 --- a/extmod/modtls_mbedtls.c +++ b/extmod/modtls_mbedtls.c @@ -37,6 +37,7 @@ #include "py/stream.h" #include "py/objstr.h" #include "py/reader.h" +#include "py/gc.h" #include "extmod/vfs.h" // mbedtls_time_t @@ -58,6 +59,10 @@ #include "mbedtls/asn1.h" #endif +#ifndef MICROPY_MBEDTLS_CONFIG_BARE_METAL +#define MICROPY_MBEDTLS_CONFIG_BARE_METAL (0) +#endif + #define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR) // This corresponds to an SSLContext object. @@ -545,6 +550,16 @@ static mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t mbedtls_ssl_init(&o->ssl); ret = mbedtls_ssl_setup(&o->ssl, &ssl_context->conf); + #if !MICROPY_MBEDTLS_CONFIG_BARE_METAL + if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) { + // If mbedTLS relies on platform libc heap for buffers (i.e. esp32 + // port), then run a GC pass and then try again. This is useful because + // it may free a Python object (like an old SSL socket) whose finaliser + // frees some platform-level heap. + gc_collect(); + ret = mbedtls_ssl_setup(&o->ssl, &ssl_context->conf); + } + #endif if (ret != 0) { goto cleanup; } From d642cce27a9a07e043211f099c31fca390f96f1a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Oct 2024 16:43:13 +1100 Subject: [PATCH 240/546] unix: Use the bare metal mbedTLS config in the coverage buiid. This allows coverage to test MicroPython-specific features such as the tracked alloc cleanups added in the parent commit. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/unix/mbedtls/mbedtls_config_port.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ports/unix/mbedtls/mbedtls_config_port.h b/ports/unix/mbedtls/mbedtls_config_port.h index c619de9b8b1b9..aec65e6581e73 100644 --- a/ports/unix/mbedtls/mbedtls_config_port.h +++ b/ports/unix/mbedtls/mbedtls_config_port.h @@ -32,7 +32,18 @@ // Enable mbedtls modules #define MBEDTLS_TIMING_C +#if defined(MICROPY_UNIX_COVERAGE) +// Test the "bare metal" memory management in the coverage build +#define MICROPY_MBEDTLS_CONFIG_BARE_METAL (1) +#endif + // Include common mbedtls configuration. #include "extmod/mbedtls/mbedtls_config_common.h" +#if defined(MICROPY_UNIX_COVERAGE) +// See comment above, but fall back to the default platform entropy functions +#undef MBEDTLS_ENTROPY_HARDWARE_ALT +#undef MBEDTLS_NO_PLATFORM_ENTROPY +#endif + #endif /* MICROPY_INCLUDED_MBEDTLS_CONFIG_H */ From 8a2ff2ca7366f605dd55c93f6b393552b365cd10 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 4 Dec 2024 10:53:08 +1100 Subject: [PATCH 241/546] py/gc: Split out running finalizers to a separate pass. Currently a finalizer may run and access memory which has already been freed. (This happens mostly during gc_sweep_all() but could happen during any garbage collection pass.) Includes some speed improvement tweaks to skip empty FTB blocks. These help compensate for the inherent slowdown of having to walk the heap twice. Signed-off-by: Angus Gratton --- py/gc.c | 67 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/py/gc.c b/py/gc.c index bee44925076f0..297f4f85d3f6a 100644 --- a/py/gc.c +++ b/py/gc.c @@ -477,29 +477,20 @@ static void gc_deal_with_stack_overflow(void) { } } -static void gc_sweep(void) { - #if MICROPY_PY_GC_COLLECT_RETVAL - MP_STATE_MEM(gc_collected) = 0; - #endif - // free unmarked heads and their tails - int free_tail = 0; - #if MICROPY_GC_SPLIT_HEAP_AUTO - mp_state_mem_area_t *prev_area = NULL; - #endif - for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { - size_t end_block = area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; - if (area->gc_last_used_block < end_block) { - end_block = area->gc_last_used_block + 1; - } - - size_t last_used_block = 0; - - for (size_t block = 0; block < end_block; block++) { - MICROPY_GC_HOOK_LOOP(block); - switch (ATB_GET_KIND(area, block)) { - case AT_HEAD: - #if MICROPY_ENABLE_FINALISER - if (FTB_GET(area, block)) { +// Run finalisers for all to-be-freed blocks +static void gc_sweep_run_finalisers(void) { + #if MICROPY_ENABLE_FINALISER + for (const mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + assert(area->gc_last_used_block <= area->gc_alloc_table_byte_len * BLOCKS_PER_ATB); + // Small speed optimisation: skip over empty FTB blocks + size_t ftb_end = area->gc_last_used_block / BLOCKS_PER_FTB; // index is inclusive + for (size_t ftb_idx = 0; ftb_idx <= ftb_end; ftb_idx++) { + byte ftb = area->gc_finaliser_table_start[ftb_idx]; + size_t block = ftb_idx * BLOCKS_PER_FTB; + while (ftb) { + MICROPY_GC_HOOK_LOOP(block); + if (ftb & 1) { // FTB_GET(area, block) shortcut + if (ATB_GET_KIND(area, block) == AT_HEAD) { mp_obj_base_t *obj = (mp_obj_base_t *)PTR_FROM_BLOCK(area, block); if (obj->type != NULL) { // if the object has a type then see if it has a __del__ method @@ -519,7 +510,35 @@ static void gc_sweep(void) { // clear finaliser flag FTB_CLEAR(area, block); } - #endif + } + ftb >>= 1; + block++; + } + } + } + #endif // MICROPY_ENABLE_FINALISER +} + +static void gc_sweep(void) { + #if MICROPY_PY_GC_COLLECT_RETVAL + MP_STATE_MEM(gc_collected) = 0; + #endif + // free unmarked heads and their tails + int free_tail = 0; + #if MICROPY_GC_SPLIT_HEAP_AUTO + mp_state_mem_area_t *prev_area = NULL; + #endif + + gc_sweep_run_finalisers(); + + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + size_t last_used_block = 0; + assert(area->gc_last_used_block <= area->gc_alloc_table_byte_len * BLOCKS_PER_ATB); + + for (size_t block = 0; block <= area->gc_last_used_block; block++) { + MICROPY_GC_HOOK_LOOP(block); + switch (ATB_GET_KIND(area, block)) { + case AT_HEAD: free_tail = 1; DEBUG_printf("gc_sweep(%p)\n", (void *)PTR_FROM_BLOCK(area, block)); #if MICROPY_PY_GC_COLLECT_RETVAL From 40e1c111e17864044190596dff6d32955d11280c Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 4 Dec 2024 10:58:06 +1100 Subject: [PATCH 242/546] py/gc: Allow gc_free from inside a gc_sweep finalizer. Do this by tracking being inside gc collection with a separate flag, GC_COLLECT_FLAG. In gc_free(), ignore this flag when determining if the heap is locked. * For finalisers calling gc_free() when heap is otherwise unlocked, this allows memory to be immediately freed (potentially avoiding a MemoryError). * Hard IRQs still can't call gc_free(), as heap will be locked via gc_lock(). * If finalisers are disabled then all of this code can be compiled out to save some code size. Signed-off-by: Angus Gratton --- py/gc.c | 35 +++++++++++++++++++++-------------- py/modmicropython.c | 4 ++-- py/mpstate.h | 13 +++++++++++++ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/py/gc.c b/py/gc.c index 297f4f85d3f6a..3d48e062494a8 100644 --- a/py/gc.c +++ b/py/gc.c @@ -334,12 +334,12 @@ void gc_lock(void) { // - each thread has its own gc_lock_depth so there are no races between threads; // - a hard interrupt will only change gc_lock_depth during its execution, and // upon return will restore the value of gc_lock_depth. - MP_STATE_THREAD(gc_lock_depth)++; + MP_STATE_THREAD(gc_lock_depth) += (1 << GC_LOCK_DEPTH_SHIFT); } void gc_unlock(void) { // This does not need to be atomic, See comment above in gc_lock. - MP_STATE_THREAD(gc_lock_depth)--; + MP_STATE_THREAD(gc_lock_depth) -= (1 << GC_LOCK_DEPTH_SHIFT); } bool gc_is_locked(void) { @@ -581,13 +581,18 @@ static void gc_sweep(void) { } } -void gc_collect_start(void) { +static void gc_collect_start_common(void) { GC_ENTER(); - MP_STATE_THREAD(gc_lock_depth)++; + assert((MP_STATE_THREAD(gc_lock_depth) & GC_COLLECT_FLAG) == 0); + MP_STATE_THREAD(gc_lock_depth) |= GC_COLLECT_FLAG; + MP_STATE_MEM(gc_stack_overflow) = 0; +} + +void gc_collect_start(void) { + gc_collect_start_common(); #if MICROPY_GC_ALLOC_THRESHOLD MP_STATE_MEM(gc_alloc_amount) = 0; #endif - MP_STATE_MEM(gc_stack_overflow) = 0; // Trace root pointers. This relies on the root pointers being organised // correctly in the mp_state_ctx structure. We scan nlr_top, dict_locals, @@ -658,14 +663,12 @@ void gc_collect_end(void) { for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { area->gc_last_free_atb_index = 0; } - MP_STATE_THREAD(gc_lock_depth)--; + MP_STATE_THREAD(gc_lock_depth) &= ~GC_COLLECT_FLAG; GC_EXIT(); } void gc_sweep_all(void) { - GC_ENTER(); - MP_STATE_THREAD(gc_lock_depth)++; - MP_STATE_MEM(gc_stack_overflow) = 0; + gc_collect_start_common(); gc_collect_end(); } @@ -902,10 +905,13 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) { // force the freeing of a piece of memory // TODO: freeing here does not call finaliser void gc_free(void *ptr) { - if (MP_STATE_THREAD(gc_lock_depth) > 0) { - // Cannot free while the GC is locked. However free is an optimisation - // to reclaim the memory immediately, this means it will now be left - // until the next collection. + // Cannot free while the GC is locked, unless we're only doing a gc sweep. + // However free is an optimisation to reclaim the memory immediately, this + // means it will now be left until the next collection. + // + // (We have the optimisation to free immediately from inside a gc sweep so + // that finalisers can free more memory when trying to avoid MemoryError.) + if (MP_STATE_THREAD(gc_lock_depth) & ~GC_COLLECT_FLAG) { return; } @@ -930,7 +936,8 @@ void gc_free(void *ptr) { #endif size_t block = BLOCK_FROM_PTR(area, ptr); - assert(ATB_GET_KIND(area, block) == AT_HEAD); + assert(ATB_GET_KIND(area, block) == AT_HEAD + || (ATB_GET_KIND(area, block) == AT_MARK && (MP_STATE_THREAD(gc_lock_depth) & GC_COLLECT_FLAG))); #if MICROPY_ENABLE_FINALISER FTB_CLEAR(area, block); diff --git a/py/modmicropython.c b/py/modmicropython.c index 1bf0a000c2026..d1a687f10e14e 100644 --- a/py/modmicropython.c +++ b/py/modmicropython.c @@ -132,13 +132,13 @@ static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_lock_obj, mp_micropython_he static mp_obj_t mp_micropython_heap_unlock(void) { gc_unlock(); - return MP_OBJ_NEW_SMALL_INT(MP_STATE_THREAD(gc_lock_depth)); + return MP_OBJ_NEW_SMALL_INT(MP_STATE_THREAD(gc_lock_depth) >> GC_LOCK_DEPTH_SHIFT); } static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_unlock_obj, mp_micropython_heap_unlock); #if MICROPY_PY_MICROPYTHON_HEAP_LOCKED static mp_obj_t mp_micropython_heap_locked(void) { - return MP_OBJ_NEW_SMALL_INT(MP_STATE_THREAD(gc_lock_depth)); + return MP_OBJ_NEW_SMALL_INT(MP_STATE_THREAD(gc_lock_depth) >> GC_LOCK_DEPTH_SHIFT); } static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_locked_obj, mp_micropython_heap_locked); #endif diff --git a/py/mpstate.h b/py/mpstate.h index 54eca596daa28..51f290b55b5ed 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -77,6 +77,18 @@ typedef struct _mp_sched_item_t { mp_obj_t arg; } mp_sched_item_t; +// gc_lock_depth field is a combination of the GC_COLLECT_FLAG +// bit and a lock depth shifted GC_LOCK_DEPTH_SHIFT bits left. +#if MICROPY_ENABLE_FINALISER +#define GC_COLLECT_FLAG 1 +#define GC_LOCK_DEPTH_SHIFT 1 +#else +// If finalisers are disabled then this check doesn't matter, as gc_lock() +// is called anywhere else that heap can't be changed. So save some code size. +#define GC_COLLECT_FLAG 0 +#define GC_LOCK_DEPTH_SHIFT 0 +#endif + // This structure holds information about a single contiguous area of // memory reserved for the memory manager. typedef struct _mp_state_mem_area_t { @@ -268,6 +280,7 @@ typedef struct _mp_state_thread_t { #endif // Locking of the GC is done per thread. + // See GC_LOCK_DEPTH_SHIFT for an explanation of this field. uint16_t gc_lock_depth; //////////////////////////////////////////////////////////// From 4bcbe88e74de245fdee029b6a0746e3485d82a7e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 10 Dec 2024 14:50:42 +1100 Subject: [PATCH 243/546] py: Add optional support for recursive mutexes, use for gc mutex. Enabled by default if using threading and no GIL This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- py/gc.c | 11 ++++++----- py/mpconfig.h | 5 +++++ py/mpstate.h | 2 +- py/mpthread.h | 6 ++++++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/py/gc.c b/py/gc.c index 3d48e062494a8..6a3f48ac3c0ce 100644 --- a/py/gc.c +++ b/py/gc.c @@ -113,9 +113,12 @@ #endif #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL -#define GC_ENTER() mp_thread_mutex_lock(&MP_STATE_MEM(gc_mutex), 1) -#define GC_EXIT() mp_thread_mutex_unlock(&MP_STATE_MEM(gc_mutex)) +#define GC_MUTEX_INIT() mp_thread_recursive_mutex_init(&MP_STATE_MEM(gc_mutex)) +#define GC_ENTER() mp_thread_recursive_mutex_lock(&MP_STATE_MEM(gc_mutex), 1) +#define GC_EXIT() mp_thread_recursive_mutex_unlock(&MP_STATE_MEM(gc_mutex)) #else +// Either no threading, or assume callers to gc_collect() hold the GIL +#define GC_MUTEX_INIT() #define GC_ENTER() #define GC_EXIT() #endif @@ -210,9 +213,7 @@ void gc_init(void *start, void *end) { MP_STATE_MEM(gc_alloc_amount) = 0; #endif - #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL - mp_thread_mutex_init(&MP_STATE_MEM(gc_mutex)); - #endif + GC_MUTEX_INIT(); } #if MICROPY_GC_SPLIT_HEAP diff --git a/py/mpconfig.h b/py/mpconfig.h index e84d258a1220a..64138a9ea7acc 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1629,6 +1629,11 @@ typedef double mp_float_t; #define MICROPY_PY_THREAD_GIL_VM_DIVISOR (32) #endif +// Is a recursive mutex type in use? +#ifndef MICROPY_PY_THREAD_RECURSIVE_MUTEX +#define MICROPY_PY_THREAD_RECURSIVE_MUTEX (MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL) +#endif + // Extended modules #ifndef MICROPY_PY_ASYNCIO diff --git a/py/mpstate.h b/py/mpstate.h index 51f290b55b5ed..138c5617300e8 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -145,7 +145,7 @@ typedef struct _mp_state_mem_t { #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL // This is a global mutex used to make the GC thread-safe. - mp_thread_mutex_t gc_mutex; + mp_thread_recursive_mutex_t gc_mutex; #endif } mp_state_mem_t; diff --git a/py/mpthread.h b/py/mpthread.h index f335cc02911fc..795f230bb4a0c 100644 --- a/py/mpthread.h +++ b/py/mpthread.h @@ -48,6 +48,12 @@ void mp_thread_mutex_init(mp_thread_mutex_t *mutex); int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait); void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex); +#if MICROPY_PY_THREAD_RECURSIVE_MUTEX +void mp_thread_recursive_mutex_init(mp_thread_recursive_mutex_t *mutex); +int mp_thread_recursive_mutex_lock(mp_thread_recursive_mutex_t *mutex, int wait); +void mp_thread_recursive_mutex_unlock(mp_thread_recursive_mutex_t *mutex); +#endif + #endif // MICROPY_PY_THREAD #if MICROPY_PY_THREAD && MICROPY_PY_THREAD_GIL From 3bfedd0f4a9e8b11ff97851615ec4761663b0e94 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 10 Dec 2024 14:51:10 +1100 Subject: [PATCH 244/546] rp2: Migrate to the new mp_thread_recursive_mutex_t. Necessary for GC support, also refactored pendsv usage. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/mpthreadport.c | 4 ++-- ports/rp2/mpthreadport.h | 20 +++++++++++++++++++- ports/rp2/pendsv.c | 14 +++++++------- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/ports/rp2/mpthreadport.c b/ports/rp2/mpthreadport.c index 0d3b343cab067..8fd5e5d790277 100644 --- a/ports/rp2/mpthreadport.c +++ b/ports/rp2/mpthreadport.c @@ -84,10 +84,10 @@ void mp_thread_deinit(void) { assert(get_core_num() == 0); // Must ensure that core1 is not currently holding the GC lock, otherwise // it will be terminated while holding the lock. - mp_thread_mutex_lock(&MP_STATE_MEM(gc_mutex), 1); + mp_thread_recursive_mutex_lock(&MP_STATE_MEM(gc_mutex), 1); multicore_reset_core1(); core1_entry = NULL; - mp_thread_mutex_unlock(&MP_STATE_MEM(gc_mutex)); + mp_thread_recursive_mutex_unlock(&MP_STATE_MEM(gc_mutex)); } void mp_thread_gc_others(void) { diff --git a/ports/rp2/mpthreadport.h b/ports/rp2/mpthreadport.h index 67a0da0e9373a..f2f2e17bb079d 100644 --- a/ports/rp2/mpthreadport.h +++ b/ports/rp2/mpthreadport.h @@ -26,9 +26,10 @@ #ifndef MICROPY_INCLUDED_RP2_MPTHREADPORT_H #define MICROPY_INCLUDED_RP2_MPTHREADPORT_H -#include "pico/mutex.h" +#include "mutex_extra.h" typedef struct mutex mp_thread_mutex_t; +typedef recursive_mutex_nowait_t mp_thread_recursive_mutex_t; extern void *core_state[2]; @@ -65,4 +66,21 @@ static inline void mp_thread_mutex_unlock(mp_thread_mutex_t *m) { mutex_exit(m); } +static inline void mp_thread_recursive_mutex_init(mp_thread_recursive_mutex_t *m) { + recursive_mutex_nowait_init(m); +} + +static inline int mp_thread_recursive_mutex_lock(mp_thread_recursive_mutex_t *m, int wait) { + if (wait) { + recursive_mutex_nowait_enter_blocking(m); + return 1; + } else { + return recursive_mutex_nowait_try_enter(m, NULL); + } +} + +static inline void mp_thread_recursive_mutex_unlock(mp_thread_recursive_mutex_t *m) { + recursive_mutex_nowait_exit(m); +} + #endif // MICROPY_INCLUDED_RP2_MPTHREADPORT_H diff --git a/ports/rp2/pendsv.c b/ports/rp2/pendsv.c index 905a5aa162ec5..4ba1e81604b0f 100644 --- a/ports/rp2/pendsv.c +++ b/ports/rp2/pendsv.c @@ -26,7 +26,7 @@ #include #include "py/mpconfig.h" -#include "mutex_extra.h" +#include "py/mpthread.h" #include "pendsv.h" #if PICO_RP2040 @@ -47,21 +47,21 @@ void PendSV_Handler(void); // 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; +static mp_thread_recursive_mutex_t pendsv_mutex; void pendsv_init(void) { - recursive_mutex_nowait_init(&pendsv_mutex); + mp_thread_recursive_mutex_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_nowait_enter_blocking(&pendsv_mutex); + mp_thread_recursive_mutex_lock(&pendsv_mutex, 1); } void pendsv_resume(void) { - recursive_mutex_nowait_exit(&pendsv_mutex); + mp_thread_recursive_mutex_unlock(&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. @@ -97,7 +97,7 @@ 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_nowait_try_enter(&pendsv_mutex, NULL)) { + if (!mp_thread_recursive_mutex_lock(&pendsv_mutex, 0)) { // Failure here means core 1 holds pendsv_mutex. ISR will // run again after core 1 calls pendsv_resume(). return; @@ -117,5 +117,5 @@ void PendSV_Handler(void) { } } - recursive_mutex_nowait_exit(&pendsv_mutex); + mp_thread_recursive_mutex_unlock(&pendsv_mutex); } From fd0e529a476650a070046a38b7ba7e253b65a77a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 9 Jan 2025 14:38:31 +1100 Subject: [PATCH 245/546] unix: Add recursive mutex support. Allows refactoring the existing thread_mutex atomic section support to use the new recursive mutex type. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/unix/mpthreadport.c | 31 ++++++++++++++++++++++++------- ports/unix/mpthreadport.h | 1 + 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index 16ac4da8bf56f..5172645bc147a 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -65,7 +65,7 @@ static pthread_key_t tls_key; // The mutex is used for any code in this port that needs to be thread safe. // Specifically for thread management, access to the linked list is one example. // But also, e.g. scheduler state. -static pthread_mutex_t thread_mutex; +static mp_thread_recursive_mutex_t thread_mutex; static mp_thread_t *thread; // this is used to synchronise the signal handler of the thread @@ -78,11 +78,11 @@ static sem_t thread_signal_done; #endif void mp_thread_unix_begin_atomic_section(void) { - pthread_mutex_lock(&thread_mutex); + mp_thread_recursive_mutex_lock(&thread_mutex, true); } void mp_thread_unix_end_atomic_section(void) { - pthread_mutex_unlock(&thread_mutex); + mp_thread_recursive_mutex_unlock(&thread_mutex); } // this signal handler is used to scan the regs and stack of a thread @@ -113,10 +113,7 @@ void mp_thread_init(void) { // Needs to be a recursive mutex to emulate the behavior of // BEGIN_ATOMIC_SECTION on bare metal. - pthread_mutexattr_t thread_mutex_attr; - pthread_mutexattr_init(&thread_mutex_attr); - pthread_mutexattr_settype(&thread_mutex_attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&thread_mutex, &thread_mutex_attr); + mp_thread_recursive_mutex_init(&thread_mutex); // create first entry in linked list of all threads thread = malloc(sizeof(mp_thread_t)); @@ -321,6 +318,26 @@ void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { // TODO check return value } +#if MICROPY_PY_THREAD_RECURSIVE_MUTEX + +void mp_thread_recursive_mutex_init(mp_thread_recursive_mutex_t *mutex) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(mutex, &attr); + pthread_mutexattr_destroy(&attr); +} + +int mp_thread_recursive_mutex_lock(mp_thread_recursive_mutex_t *mutex, int wait) { + return mp_thread_mutex_lock(mutex, wait); +} + +void mp_thread_recursive_mutex_unlock(mp_thread_recursive_mutex_t *mutex) { + mp_thread_mutex_unlock(mutex); +} + +#endif // MICROPY_PY_THREAD_RECURSIVE_MUTEX + #endif // MICROPY_PY_THREAD // this is used even when MICROPY_PY_THREAD is disabled diff --git a/ports/unix/mpthreadport.h b/ports/unix/mpthreadport.h index b365f200edf97..a38223e720b45 100644 --- a/ports/unix/mpthreadport.h +++ b/ports/unix/mpthreadport.h @@ -28,6 +28,7 @@ #include typedef pthread_mutex_t mp_thread_mutex_t; +typedef pthread_mutex_t mp_thread_recursive_mutex_t; void mp_thread_init(void); void mp_thread_deinit(void); From 990f50fbb829e41ce275d942c056bcd3f4b857df Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 29 Jan 2025 10:10:10 +1100 Subject: [PATCH 246/546] py/gc: Reorder static functions for clarity. - Renamed gc_sweep to gc_sweep_free_blocks. - Call gc_sweep_run_finalisers from top level. - Reordered the gc static functions to be in approximate runtime sequence (with forward declarations) rather than in declaration order. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- py/gc.c | 175 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 93 insertions(+), 82 deletions(-) diff --git a/py/gc.c b/py/gc.c index 6a3f48ac3c0ce..eda63187b20a3 100644 --- a/py/gc.c +++ b/py/gc.c @@ -123,6 +123,18 @@ #define GC_EXIT() #endif +// Static functions for individual steps of the GC mark/sweep sequence +static void gc_collect_start_common(void); +static void *gc_get_ptr(void **ptrs, int i); +#if MICROPY_GC_SPLIT_HEAP +static void gc_mark_subtree(mp_state_mem_area_t *area, size_t block); +#else +static void gc_mark_subtree(size_t block); +#endif +static void gc_deal_with_stack_overflow(void); +static void gc_sweep_run_finalisers(void); +static void gc_sweep_free_blocks(void); + // TODO waste less memory; currently requires that all entries in alloc_table have a corresponding block in pool static void gc_setup_area(mp_state_mem_area_t *area, void *start, void *end) { // calculate parameters for GC (T=total, A=alloc table, F=finaliser table, P=pool; all in bytes): @@ -379,6 +391,64 @@ static inline mp_state_mem_area_t *gc_get_ptr_area(const void *ptr) { #endif #endif +void gc_collect_start(void) { + gc_collect_start_common(); + #if MICROPY_GC_ALLOC_THRESHOLD + MP_STATE_MEM(gc_alloc_amount) = 0; + #endif + + // Trace root pointers. This relies on the root pointers being organised + // correctly in the mp_state_ctx structure. We scan nlr_top, dict_locals, + // dict_globals, then the root pointer section of mp_state_vm. + void **ptrs = (void **)(void *)&mp_state_ctx; + size_t root_start = offsetof(mp_state_ctx_t, thread.dict_locals); + size_t root_end = offsetof(mp_state_ctx_t, vm.qstr_last_chunk); + gc_collect_root(ptrs + root_start / sizeof(void *), (root_end - root_start) / sizeof(void *)); + + #if MICROPY_ENABLE_PYSTACK + // Trace root pointers from the Python stack. + ptrs = (void **)(void *)MP_STATE_THREAD(pystack_start); + gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void *)); + #endif +} + +static void gc_collect_start_common(void) { + GC_ENTER(); + assert((MP_STATE_THREAD(gc_lock_depth) & GC_COLLECT_FLAG) == 0); + MP_STATE_THREAD(gc_lock_depth) |= GC_COLLECT_FLAG; + MP_STATE_MEM(gc_stack_overflow) = 0; +} + +void gc_collect_root(void **ptrs, size_t len) { + #if !MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t *area = &MP_STATE_MEM(area); + #endif + for (size_t i = 0; i < len; i++) { + MICROPY_GC_HOOK_LOOP(i); + void *ptr = gc_get_ptr(ptrs, i); + #if MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t *area = gc_get_ptr_area(ptr); + if (!area) { + continue; + } + #else + if (!VERIFY_PTR(ptr)) { + continue; + } + #endif + size_t block = BLOCK_FROM_PTR(area, ptr); + if (ATB_GET_KIND(area, block) == AT_HEAD) { + // An unmarked head: mark it, and mark all its children + ATB_HEAD_TO_MARK(area, block); + #if MICROPY_GC_SPLIT_HEAP + gc_mark_subtree(area, block); + #else + gc_mark_subtree(block); + #endif + } + } +} + // Take the given block as the topmost block on the stack. Check all it's // children: mark the unmarked child blocks and put those newly marked // blocks on the stack. When all children have been checked, pop off the @@ -457,6 +527,25 @@ static void gc_mark_subtree(size_t block) } } +void gc_sweep_all(void) { + gc_collect_start_common(); + gc_collect_end(); +} + +void gc_collect_end(void) { + gc_deal_with_stack_overflow(); + gc_sweep_run_finalisers(); + gc_sweep_free_blocks(); + #if MICROPY_GC_SPLIT_HEAP + MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area); + #endif + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + area->gc_last_free_atb_index = 0; + } + MP_STATE_THREAD(gc_lock_depth) &= ~GC_COLLECT_FLAG; + GC_EXIT(); +} + static void gc_deal_with_stack_overflow(void) { while (MP_STATE_MEM(gc_stack_overflow)) { MP_STATE_MEM(gc_stack_overflow) = 0; @@ -520,18 +609,16 @@ static void gc_sweep_run_finalisers(void) { #endif // MICROPY_ENABLE_FINALISER } -static void gc_sweep(void) { +// Free unmarked heads and their tails +static void gc_sweep_free_blocks(void) { #if MICROPY_PY_GC_COLLECT_RETVAL MP_STATE_MEM(gc_collected) = 0; #endif - // free unmarked heads and their tails int free_tail = 0; #if MICROPY_GC_SPLIT_HEAP_AUTO mp_state_mem_area_t *prev_area = NULL; #endif - gc_sweep_run_finalisers(); - for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { size_t last_used_block = 0; assert(area->gc_last_used_block <= area->gc_alloc_table_byte_len * BLOCKS_PER_ATB); @@ -541,7 +628,7 @@ static void gc_sweep(void) { switch (ATB_GET_KIND(area, block)) { case AT_HEAD: free_tail = 1; - DEBUG_printf("gc_sweep(%p)\n", (void *)PTR_FROM_BLOCK(area, block)); + DEBUG_printf("gc_sweep_free_blocks(%p)\n", (void *)PTR_FROM_BLOCK(area, block)); #if MICROPY_PY_GC_COLLECT_RETVAL MP_STATE_MEM(gc_collected)++; #endif @@ -572,7 +659,7 @@ static void gc_sweep(void) { #if MICROPY_GC_SPLIT_HEAP_AUTO // Free any empty area, aside from the first one if (last_used_block == 0 && prev_area != NULL) { - DEBUG_printf("gc_sweep free empty area %p\n", area); + DEBUG_printf("gc_sweep_free_blocks free empty area %p\n", area); NEXT_AREA(prev_area) = NEXT_AREA(area); MP_PLAT_FREE_HEAP(area); area = prev_area; @@ -582,34 +669,6 @@ static void gc_sweep(void) { } } -static void gc_collect_start_common(void) { - GC_ENTER(); - assert((MP_STATE_THREAD(gc_lock_depth) & GC_COLLECT_FLAG) == 0); - MP_STATE_THREAD(gc_lock_depth) |= GC_COLLECT_FLAG; - MP_STATE_MEM(gc_stack_overflow) = 0; -} - -void gc_collect_start(void) { - gc_collect_start_common(); - #if MICROPY_GC_ALLOC_THRESHOLD - MP_STATE_MEM(gc_alloc_amount) = 0; - #endif - - // Trace root pointers. This relies on the root pointers being organised - // correctly in the mp_state_ctx structure. We scan nlr_top, dict_locals, - // dict_globals, then the root pointer section of mp_state_vm. - void **ptrs = (void **)(void *)&mp_state_ctx; - size_t root_start = offsetof(mp_state_ctx_t, thread.dict_locals); - size_t root_end = offsetof(mp_state_ctx_t, vm.qstr_last_chunk); - gc_collect_root(ptrs + root_start / sizeof(void *), (root_end - root_start) / sizeof(void *)); - - #if MICROPY_ENABLE_PYSTACK - // Trace root pointers from the Python stack. - ptrs = (void **)(void *)MP_STATE_THREAD(pystack_start); - gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void *)); - #endif -} - // Address sanitizer needs to know that the access to ptrs[i] must always be // considered OK, even if it's a load from an address that would normally be // prohibited (due to being undefined, in a red zone, etc). @@ -625,54 +684,6 @@ static void *gc_get_ptr(void **ptrs, int i) { return ptrs[i]; } -void gc_collect_root(void **ptrs, size_t len) { - #if !MICROPY_GC_SPLIT_HEAP - mp_state_mem_area_t *area = &MP_STATE_MEM(area); - #endif - for (size_t i = 0; i < len; i++) { - MICROPY_GC_HOOK_LOOP(i); - void *ptr = gc_get_ptr(ptrs, i); - #if MICROPY_GC_SPLIT_HEAP - mp_state_mem_area_t *area = gc_get_ptr_area(ptr); - if (!area) { - continue; - } - #else - if (!VERIFY_PTR(ptr)) { - continue; - } - #endif - size_t block = BLOCK_FROM_PTR(area, ptr); - if (ATB_GET_KIND(area, block) == AT_HEAD) { - // An unmarked head: mark it, and mark all its children - ATB_HEAD_TO_MARK(area, block); - #if MICROPY_GC_SPLIT_HEAP - gc_mark_subtree(area, block); - #else - gc_mark_subtree(block); - #endif - } - } -} - -void gc_collect_end(void) { - gc_deal_with_stack_overflow(); - gc_sweep(); - #if MICROPY_GC_SPLIT_HEAP - MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area); - #endif - for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { - area->gc_last_free_atb_index = 0; - } - MP_STATE_THREAD(gc_lock_depth) &= ~GC_COLLECT_FLAG; - GC_EXIT(); -} - -void gc_sweep_all(void) { - gc_collect_start_common(); - gc_collect_end(); -} - void gc_info(gc_info_t *info) { GC_ENTER(); info->total = 0; From 112f65776588ef0cc3307f8bef208a480037d7d0 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Thu, 23 Jan 2025 23:17:00 -0800 Subject: [PATCH 247/546] stm32/eth: Make ETH DMA buffer attributes configurable. Signed-off-by: Kwabena W. Agyeman --- ports/stm32/eth.c | 2 +- ports/stm32/mpconfigboard_common.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/stm32/eth.c b/ports/stm32/eth.c index fd46bde23cb3f..9f655306816d9 100644 --- a/ports/stm32/eth.c +++ b/ports/stm32/eth.c @@ -121,7 +121,7 @@ typedef struct _eth_t { int16_t (*phy_get_link_status)(uint32_t phy_addr); } eth_t; -static eth_dma_t eth_dma __attribute__((aligned(16384))); +static eth_dma_t eth_dma MICROPY_HW_ETH_DMA_ATTRIBUTE; eth_t eth_instance; diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index e1c9c159ecf6f..4ce0a75b87e2b 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -669,3 +669,7 @@ #endif #define MICROPY_HW_USES_BOOTLOADER (MICROPY_HW_VTOR != 0x08000000) + +#ifndef MICROPY_HW_ETH_DMA_ATTRIBUTE +#define MICROPY_HW_ETH_DMA_ATTRIBUTE __attribute__((aligned(16384))); +#endif From 55ae597bb6044c392e9264f1af09ddcc19901f44 Mon Sep 17 00:00:00 2001 From: Carl Pottle Date: Sun, 29 Dec 2024 15:20:37 -0800 Subject: [PATCH 248/546] rp2/modmachine: Make lightsleep preserve SLEEP_EN0 and SLEEP_EN1. The problem was introduced in d1423ef7a23793de3777e84d985f9902241e788e, calling `machine.lightsleep()` overwrites RP2xxx registers `SLEEP_EN0` and `SLEEP_EN1` with their power on default values. Prior to that commit the register values were saved on entry to lightsleep and restored before returning. These changes restores the earlier behavior. Fixes issue #16502. Signed-off-by: Carl Pottle --- ports/rp2/modmachine.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 3229aed277b35..954ea216497fe 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -196,6 +196,8 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { #endif xosc_dormant(); } else { + uint32_t save_sleep_en0 = clocks_hw->sleep_en0; + uint32_t save_sleep_en1 = clocks_hw->sleep_en1; bool timer3_enabled = irq_is_enabled(3); const uint32_t alarm_num = 3; @@ -251,8 +253,8 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { if (!timer3_enabled) { irq_set_enabled(irq_num, false); } - clocks_hw->sleep_en0 |= ~(0u); - clocks_hw->sleep_en1 |= ~(0u); + clocks_hw->sleep_en0 = save_sleep_en0; + clocks_hw->sleep_en1 = save_sleep_en1; } // Enable ROSC. From 81ab49a60759376271ad1e1fbf05bae0ffe9e7b1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 23 Jan 2025 16:37:33 +1100 Subject: [PATCH 249/546] tests/ports/rp2: Add test for SLEEP_ENx registers over lightsleep. Signed-off-by: Damien George --- tests/ports/rp2/rp2_lightsleep_regs.py | 58 ++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 tests/ports/rp2/rp2_lightsleep_regs.py diff --git a/tests/ports/rp2/rp2_lightsleep_regs.py b/tests/ports/rp2/rp2_lightsleep_regs.py new file mode 100644 index 0000000000000..4a833d0a9c339 --- /dev/null +++ b/tests/ports/rp2/rp2_lightsleep_regs.py @@ -0,0 +1,58 @@ +# Test that SLEEP_ENx registers are preserved over a call to machine.lightsleep(). + +import sys +from machine import mem32, lightsleep +import unittest + +is_rp2350 = "RP2350" in sys.implementation._machine + +if is_rp2350: + CLOCK_BASE = 0x40010000 + SLEEP_EN0 = CLOCK_BASE + 0xB4 + SLEEP_EN1 = CLOCK_BASE + 0xB8 + TO_DISABLE_EN0 = 1 << 30 # SHA256 + TO_DISABLE_EN1 = 1 << 4 # SRAM0 +else: + CLOCK_BASE = 0x40008000 + SLEEP_EN0 = CLOCK_BASE + 0xA8 + SLEEP_EN1 = CLOCK_BASE + 0xAC + TO_DISABLE_EN0 = 1 << 28 # SRAM0 + TO_DISABLE_EN1 = 1 << 0 # SRAM4 + + +class Test(unittest.TestCase): + def setUp(self): + self.orig_sleep_en0 = mem32[SLEEP_EN0] + self.orig_sleep_en1 = mem32[SLEEP_EN1] + + def tearDown(self): + mem32[SLEEP_EN0] = self.orig_sleep_en0 + mem32[SLEEP_EN1] = self.orig_sleep_en1 + + def test_sleep_en_regs(self): + print() + + # Disable some bits so the registers aren't just 0xffff. + mem32[SLEEP_EN0] &= ~TO_DISABLE_EN0 + mem32[SLEEP_EN1] &= ~TO_DISABLE_EN1 + + # Get the registers before the lightsleep. + sleep_en0_before = mem32[SLEEP_EN0] & 0xFFFFFFFF + sleep_en1_before = mem32[SLEEP_EN1] & 0xFFFFFFFF + print(hex(sleep_en0_before), hex(sleep_en1_before)) + + # Do a lightsleep. + lightsleep(100) + + # Get the registers after a lightsleep. + sleep_en0_after = mem32[SLEEP_EN0] & 0xFFFFFFFF + sleep_en1_after = mem32[SLEEP_EN1] & 0xFFFFFFFF + print(hex(sleep_en0_after), hex(sleep_en1_after)) + + # Check the registers have not changed. + self.assertEqual(sleep_en0_before, sleep_en0_after) + self.assertEqual(sleep_en1_before, sleep_en1_after) + + +if __name__ == "__main__": + unittest.main() From 3699cf5f38736b670a26786af4025584dcb5aef2 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Tue, 21 Jan 2025 21:35:08 +0000 Subject: [PATCH 250/546] rp2/rp2_flash: Workaround multicore lockout not being reset. With regression test. See upstream bug https://github.com/raspberrypi/pico-sdk/issues/2201 Tested-by: Angus Gratton Signed-off-by: Mike Bell --- ports/rp2/rp2_flash.c | 14 ++++++++++++-- tests/ports/rp2/rp2_thread_reset_part1.py | 18 ++++++++++++++++++ tests/ports/rp2/rp2_thread_reset_part1.py.exp | 1 + tests/ports/rp2/rp2_thread_reset_part2.py | 12 ++++++++++++ tests/ports/rp2/rp2_thread_reset_part2.py.exp | 3 +++ 5 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/ports/rp2/rp2_thread_reset_part1.py create mode 100644 tests/ports/rp2/rp2_thread_reset_part1.py.exp create mode 100644 tests/ports/rp2/rp2_thread_reset_part2.py create mode 100644 tests/ports/rp2/rp2_thread_reset_part2.py.exp diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c index c1acb54e75748..a487fb1633b21 100644 --- a/ports/rp2/rp2_flash.c +++ b/ports/rp2/rp2_flash.c @@ -70,10 +70,20 @@ bi_decl(bi_block_device( BINARY_INFO_BLOCK_DEV_FLAG_WRITE | BINARY_INFO_BLOCK_DEV_FLAG_PT_UNKNOWN)); +// This is a workaround to pico-sdk #2201: https://github.com/raspberrypi/pico-sdk/issues/2201 +// which means the multicore_lockout_victim_is_initialized returns true even after core1 is reset. +static bool use_multicore_lockout(void) { + return multicore_lockout_victim_is_initialized(1 - get_core_num()) + #if MICROPY_PY_THREAD + && core1_entry != NULL + #endif + ; +} + // Flash erase and write must run with interrupts disabled and the other core suspended, // because the XIP bit gets disabled. static uint32_t begin_critical_flash_section(void) { - if (multicore_lockout_victim_is_initialized(1 - get_core_num())) { + if (use_multicore_lockout()) { multicore_lockout_start_blocking(); } return save_and_disable_interrupts(); @@ -81,7 +91,7 @@ static uint32_t begin_critical_flash_section(void) { static void end_critical_flash_section(uint32_t state) { restore_interrupts(state); - if (multicore_lockout_victim_is_initialized(1 - get_core_num())) { + if (use_multicore_lockout()) { multicore_lockout_end_blocking(); } } diff --git a/tests/ports/rp2/rp2_thread_reset_part1.py b/tests/ports/rp2/rp2_thread_reset_part1.py new file mode 100644 index 0000000000000..d43868113f5eb --- /dev/null +++ b/tests/ports/rp2/rp2_thread_reset_part1.py @@ -0,0 +1,18 @@ +# This is a regression test for https://github.com/micropython/micropython/issues/16619 +# it runs in two parts by necessity: +# +# - This "part1" creates a non-terminating thread. +# - The test runner issues a soft reset, which will terminate that thread. +# - "part2" is the actual test, which is whether flash access works correctly +# after the thread was terminated by soft reset. + +import _thread + + +def infinite(): + while True: + pass + + +_thread.start_new_thread(infinite, ()) +print("Part 1 complete...") diff --git a/tests/ports/rp2/rp2_thread_reset_part1.py.exp b/tests/ports/rp2/rp2_thread_reset_part1.py.exp new file mode 100644 index 0000000000000..48ea7efad97ae --- /dev/null +++ b/tests/ports/rp2/rp2_thread_reset_part1.py.exp @@ -0,0 +1 @@ +Part 1 complete... diff --git a/tests/ports/rp2/rp2_thread_reset_part2.py b/tests/ports/rp2/rp2_thread_reset_part2.py new file mode 100644 index 0000000000000..15f0eaab8f857 --- /dev/null +++ b/tests/ports/rp2/rp2_thread_reset_part2.py @@ -0,0 +1,12 @@ +# This is part2 of a two-part regression test, see part1 +# for details of what's expected. +import os + +FILENAME = "/rp2_thread_reset_test.txt" + +print("Starting") +with open(FILENAME, "w") as f: + f.write("test") +print("Written") +os.unlink(FILENAME) +print("Removed") diff --git a/tests/ports/rp2/rp2_thread_reset_part2.py.exp b/tests/ports/rp2/rp2_thread_reset_part2.py.exp new file mode 100644 index 0000000000000..d7581c7d2601e --- /dev/null +++ b/tests/ports/rp2/rp2_thread_reset_part2.py.exp @@ -0,0 +1,3 @@ +Starting +Written +Removed From 4bed77cc233a84846a0c47347b520ce5b816758b Mon Sep 17 00:00:00 2001 From: Glenn Moloney Date: Fri, 19 May 2023 17:51:52 +1000 Subject: [PATCH 251/546] esp8266/network_wlan: Make WLAN.config('channel') use wifi_get_channel. Prior to this fix, `WLAN.config('channel')` would return an incorrect channel for AP_IF if STA has connected to an external AP running on a different channel. The esp8266 now has the same behaviour as for esp32 per commit 98d1c50159fe9427d72ec358ba0219eaebb1d991. Fixes issue #11463. Signed-off-by: Glenn Moloney --- ports/esp8266/network_wlan.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ports/esp8266/network_wlan.c b/ports/esp8266/network_wlan.c index 44154ff6d8e54..2e43ebfb2b7b1 100644 --- a/ports/esp8266/network_wlan.c +++ b/ports/esp8266/network_wlan.c @@ -641,8 +641,7 @@ static mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs val = MP_OBJ_NEW_SMALL_INT(cfg.ap.authmode); break; case MP_QSTR_channel: - req_if = SOFTAP_IF; - val = MP_OBJ_NEW_SMALL_INT(cfg.ap.channel); + val = MP_OBJ_NEW_SMALL_INT(wifi_get_channel()); break; case MP_QSTR_hostname: case MP_QSTR_dhcp_hostname: { From b1e6c2b655209022fb1dd1e7b3271af010fcccb5 Mon Sep 17 00:00:00 2001 From: Glenn Moloney Date: Mon, 26 Jun 2023 18:05:37 +1000 Subject: [PATCH 252/546] esp8266/network_wlan: Make WLAN.config(channel=x) use wifi_set_channel. Also permits channel option to be used for STA_IF interface. This provides compatibility with esp32 code, especially for espnow users. Signed-off-by: Glenn Moloney --- ports/esp8266/network_wlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp8266/network_wlan.c b/ports/esp8266/network_wlan.c index 2e43ebfb2b7b1..7131b88bac9ab 100644 --- a/ports/esp8266/network_wlan.c +++ b/ports/esp8266/network_wlan.c @@ -565,8 +565,8 @@ static mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs break; } case MP_QSTR_channel: { - req_if = SOFTAP_IF; cfg.ap.channel = mp_obj_get_int(kwargs->table[i].value); + error_check(wifi_set_channel(cfg.ap.channel), "can't set channel"); break; } case MP_QSTR_hostname: From 921f19fc9d697488f4f86bf8db73d43b04b781a6 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 4 Feb 2025 15:14:51 +1100 Subject: [PATCH 253/546] tests/multi_wlan: Remove esp8266 port workaround. Not needed due to parent commit. Signed-off-by: Angus Gratton --- tests/multi_wlan/01_ap_sta.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/multi_wlan/01_ap_sta.py b/tests/multi_wlan/01_ap_sta.py index 1a8e80cd33aa3..368addf1393e2 100644 --- a/tests/multi_wlan/01_ap_sta.py +++ b/tests/multi_wlan/01_ap_sta.py @@ -96,15 +96,7 @@ def instance1(): print("STA connected") - # Print the current channel, if the port support this - try: - print("channel", sta.config("channel")) - except OSError as e: - if "AP" in str(e): - # ESP8266 only supports reading channel on the AP interface, so fake this result - print("channel", CHANNEL) - else: - raise + print("channel", sta.config("channel")) print("STA waiting for disconnect...") From 71e8b27b26f46f11f642381d4f6699ee1fc073f6 Mon Sep 17 00:00:00 2001 From: eggfly Date: Mon, 23 Dec 2024 19:40:06 +0800 Subject: [PATCH 254/546] esp32/README: Fix board in octal-SPIRAM example make command. Signed-off-by: eggfly --- ports/esp32/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 8597c85ec3103..6ed7eddb8b80f 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -152,7 +152,7 @@ $ make BOARD=ESP32_GENERIC BOARD_VARIANT=OTA or to enable octal-SPIRAM support for the `ESP32_GENERIC_S3` board: ```bash -$ make BOARD=ESP32_GENERIC BOARD_VARIANT=SPIRAM_OCT +$ make BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT ``` From b603fa38b25c5136d36cdbfff91b69f96efacd69 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Mon, 3 Feb 2025 16:03:22 +1100 Subject: [PATCH 255/546] py/mkrules.mk: Reset USER_C_MODULES when building mpy-cross dependency. When a port automatically compiles `mpy-cross`, if `USER_C_MODULES` is provided by the user on the command line then it is also applied to the `mpy-cross` build. That can lead to build errors if the path is relative and not found when building `mpy-cross`. Fix that by explicitly resetting `USER_C_MODULES` when invoking the `mpy-cross` build. Signed-off-by: Andrew Leech --- py/mkrules.cmake | 2 +- py/mkrules.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/py/mkrules.cmake b/py/mkrules.cmake index bfc56abfe80b7..3ee4c4c31aa57 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -211,7 +211,7 @@ if(MICROPY_FROZEN_MANIFEST) endif() add_custom_command( OUTPUT ${MICROPY_MPYCROSS_DEPENDENCY} - COMMAND ${MICROPY_MAKE_EXECUTABLE} -C ${MICROPY_DIR}/mpy-cross + COMMAND ${MICROPY_MAKE_EXECUTABLE} -C ${MICROPY_DIR}/mpy-cross USER_C_MODULES= ) endif() diff --git a/py/mkrules.mk b/py/mkrules.mk index 373bda8996f19..e9504ce39f839 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -176,7 +176,7 @@ $(HEADER_BUILD): ifneq ($(MICROPY_MPYCROSS_DEPENDENCY),) # to automatically build mpy-cross, if needed $(MICROPY_MPYCROSS_DEPENDENCY): - $(MAKE) -C "$(abspath $(dir $@)..)" + $(MAKE) -C "$(abspath $(dir $@)..)" USER_C_MODULES= endif ifneq ($(FROZEN_DIR),) From dfd1d69a72956e650af5cb071e4d9be61b5d322a Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 24 Jan 2025 16:26:55 +0100 Subject: [PATCH 256/546] tests/run-natmodtests.py: Autodetect the test target architecture. This commit lets the natmod tests runner to automatically detect the architecture of the test target. This allows to avoid to explicitly pass the architecture name to the runner in test scripts. However, the ability to manually specify a target was not removed but it was made optional. This way the user is able to override the architecture name if needed (like if one wants to test an armv6 MPY on an armv7 board). Signed-off-by: Alessandro Gatti --- ports/qemu/README.md | 4 +- ports/qemu/boards/VIRT_RV32/mpconfigboard.mk | 2 - tests/run-natmodtests.py | 58 ++++++++++++++++++-- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/ports/qemu/README.md b/ports/qemu/README.md index 70edf97f58e37..c7d0dc1f4ea70 100644 --- a/ports/qemu/README.md +++ b/ports/qemu/README.md @@ -105,9 +105,7 @@ can also be tested with this command (this is currently supported only for the $ make test_natmod The same remarks about manually running the tests apply for native modules, but -`run-natmodtests.py` should be run instead of `run-tests.py`. In this case you -also have to explicitly pass the architecture you are running native modules to -`run-natmodtests.py` ("--arch rv32imc" for the `VIRT_RV32` board). +`run-natmodtests.py` should be run instead of `run-tests.py`. Extra make options ------------------ diff --git a/ports/qemu/boards/VIRT_RV32/mpconfigboard.mk b/ports/qemu/boards/VIRT_RV32/mpconfigboard.mk index dd926480052e1..ce12720928e32 100644 --- a/ports/qemu/boards/VIRT_RV32/mpconfigboard.mk +++ b/ports/qemu/boards/VIRT_RV32/mpconfigboard.mk @@ -9,5 +9,3 @@ LDSCRIPT = mcu/rv32/virt.ld SRC_BOARD_O += shared/runtime/gchelper_native.o shared/runtime/gchelper_rv32i.o MPY_CROSS_FLAGS += -march=rv32imc - -RUN_NATMODTESTS_ARGS = --arch rv32imc diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index 1fe44bec1615a..b858989daa434 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -28,6 +28,23 @@ "re": "re/re_$(ARCH).mpy", } +# Supported architectures for native mpy modules +AVAILABLE_ARCHS = ( + "x86", + "x64", + "armv6", + "armv6m", + "armv7m", + "armv7em", + "armv7emsp", + "armv7emdp", + "xtensa", + "xtensawin", + "rv32imc", +) + +ARCH_MAPPINGS = {"armv7em": "armv7m"} + # Code to allow a target MicroPython to import an .mpy from RAM injected_import_hook_code = """\ import sys, io, vfs @@ -96,14 +113,33 @@ def run_script(self, script): return b"", er -def run_tests(target_truth, target, args, stats): +def detect_architecture(target): + with open("./feature_check/target_info.py", "rb") as f: + target_info_data = f.read() + result_out, error = target.run_script(target_info_data) + if error is not None: + return None, None, error + info = result_out.split(b" ") + if len(info) < 2: + return None, None, "unexpected target info: {}".format(info) + platform = info[0].strip().decode() + arch = info[1].strip().decode() + if arch not in AVAILABLE_ARCHS: + if arch == "None": + return None, None, "the target does not support dynamic modules" + else: + return None, None, "{} is not a supported architecture".format(arch) + return platform, arch, None + + +def run_tests(target_truth, target, args, stats, resolved_arch): for test_file in args.files: # Find supported test test_file_basename = os.path.basename(test_file) for k, v in TEST_MAPPINGS.items(): if test_file_basename.startswith(k): test_module = k - test_mpy = v.replace("$(ARCH)", args.arch) + test_mpy = v.replace("$(ARCH)", resolved_arch) break else: print("---- {} - no matching mpy".format(test_file)) @@ -174,7 +210,7 @@ def main(): "-d", "--device", default="/dev/ttyACM0", help="the device for pyboard.py" ) cmd_parser.add_argument( - "-a", "--arch", default="x64", help="native architecture of the target" + "-a", "--arch", choices=AVAILABLE_ARCHS, help="override native architecture of the target" ) cmd_parser.add_argument("files", nargs="*", help="input test files") args = cmd_parser.parse_args() @@ -186,8 +222,22 @@ def main(): else: target = TargetSubprocess([MICROPYTHON]) + if hasattr(args, "arch") and args.arch is not None: + target_arch = args.arch + target_platform = None + else: + target_platform, target_arch, error = detect_architecture(target) + if error: + print("Cannot run tests: {}".format(error)) + sys.exit(1) + target_arch = ARCH_MAPPINGS.get(target_arch, target_arch) + + if target_platform: + print("platform={} ".format(target_platform), end="") + print("arch={}".format(target_arch)) + stats = {"total": 0, "pass": 0, "fail": 0, "skip": 0} - run_tests(target_truth, target, args, stats) + run_tests(target_truth, target, args, stats, target_arch) target.close() target_truth.close() From ca3090a33f06da57221827ad506d80cb1f826b99 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 24 Jan 2025 11:18:39 +0100 Subject: [PATCH 257/546] qemu/Makefile: Fix shell interpolation for automated natmod tests. This commit fixes the command used to run natmod tests, as it relied on a string interpolation feature of the POSIX shell that was not working as expected inside a makefile. The interpolation was not performed from inside the makefile and the raw command string was sent to the operating system for execution. Now the command is run by using a different type of string substitution, which explicitly performs the interpolation using a POSIX shell for-loop. Signed-off-by: Alessandro Gatti --- ports/qemu/Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index d2550cf883b7d..befef5d520061 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -189,11 +189,14 @@ test_full: $(BUILD)/firmware.elf cd $(TOP)/tests && ./run-tests.py $(RUN_TESTS_FULL_ARGS) --via-mpy cd $(TOP)/tests && ./run-tests.py $(RUN_TESTS_FULL_ARGS) --via-mpy --emit native +# "btree" currently does not build for rv32imc (Picolibc TLS incompatibility). .PHONY: test_natmod test_natmod: $(BUILD)/firmware.elf $(eval DIRNAME=ports/$(notdir $(CURDIR))) - # "btree" cannot build against Picolibc right now. - cd $(TOP)/tests && ./run-natmodtests.py -p -d execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_NATMODTESTS_ARGS) extmod/{deflate,framebuf,heapq,random_basic,re}*.py + cd $(TOP)/tests && \ + for natmod in deflate framebuf heapq random_basic re; do \ + ./run-natmodtests.py -p -d execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" extmod/$$natmod*.py; \ + done $(BUILD)/firmware.elf: $(LDSCRIPT) $(OBJ) $(Q)$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) From f594c6f66ec49e02ba06165d1f1db202d82575ec Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 24 Jan 2025 18:00:05 +0100 Subject: [PATCH 258/546] tools/ci.sh: Add natmod tests for QEMU/Arm. This commit adds the natmod tests for the MPS2_AN385 board running inside QEMU to the CI pipeline. Now natmod tests capabilities are equal between the Arm and RV32 platforms for the QEMU port. Signed-off-by: Alessandro Gatti --- ports/qemu/Makefile | 2 ++ tools/ci.sh | 25 ++++++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index befef5d520061..e9e1e0f957e2b 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -40,6 +40,8 @@ endif include $(TOP)/py/py.mk include $(TOP)/extmod/extmod.mk +GIT_SUBMODULES += lib/berkeley-db-1.xx + CFLAGS += -DMICROPY_HEAP_SIZE=$(MICROPY_HEAP_SIZE) ################################################################################ diff --git a/tools/ci.sh b/tools/ci.sh index 374395abd2ca5..2c647012f144d 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -266,6 +266,7 @@ function ci_powerpc_build { # ports/qemu function ci_qemu_setup_arm { + ci_mpy_format_setup ci_gcc_arm_setup sudo apt-get update sudo apt-get install qemu-system @@ -287,6 +288,10 @@ function ci_qemu_build_arm { make ${MAKEOPTS} -C ports/qemu clean make ${MAKEOPTS} -C ports/qemu test_full make ${MAKEOPTS} -C ports/qemu BOARD=SABRELITE test_full + + # Test building and running native .mpy with armv7m architecture. + ci_native_mpy_modules_build armv7m + make ${MAKEOPTS} -C ports/qemu test_natmod } function ci_qemu_build_rv32 { @@ -480,20 +485,18 @@ function ci_native_mpy_modules_build { else arch=$1 fi - make -C examples/natmod/features1 ARCH=$arch + for natmod in features1 features3 features4 deflate framebuf heapq random re + do + make -C examples/natmod/$natmod ARCH=$arch + done + # btree requires thread local storage support on rv32imc. if [ $arch != rv32imc ]; then - # This requires soft-float support on rv32imc. - make -C examples/natmod/features2 ARCH=$arch - # This requires thread local storage support on rv32imc. make -C examples/natmod/btree ARCH=$arch fi - make -C examples/natmod/features3 ARCH=$arch - make -C examples/natmod/features4 ARCH=$arch - make -C examples/natmod/deflate ARCH=$arch - make -C examples/natmod/framebuf ARCH=$arch - make -C examples/natmod/heapq ARCH=$arch - make -C examples/natmod/random ARCH=$arch - make -C examples/natmod/re ARCH=$arch + # features2 requires soft-float on armv7m and rv32imc. + if [ $arch != rv32imc ] && [ $arch != armv7m ]; then + make -C examples/natmod/features2 ARCH=$arch + fi } function ci_native_mpy_modules_32bit_build { From e37d498cc0891b67ea6b782fc1e9b1b7dab3c740 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 4 Feb 2025 00:02:49 +0100 Subject: [PATCH 259/546] py/emitnative: Mark condition code tables as const. This commit marks as const the condition code tables used when figuring out which opcode sequence must be emitted depending on the requested comparison type. Signed-off-by: Alessandro Gatti --- py/emitnative.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/py/emitnative.c b/py/emitnative.c index 82ee729d3d50f..dd0644793231c 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -2541,7 +2541,7 @@ static void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { #if N_X64 asm_x64_xor_r64_r64(emit->as, REG_RET, REG_RET); asm_x64_cmp_r64_with_r64(emit->as, reg_rhs, REG_ARG_2); - static byte ops[6 + 6] = { + static const byte ops[6 + 6] = { // unsigned ASM_X64_CC_JB, ASM_X64_CC_JA, @@ -2561,7 +2561,7 @@ static void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { #elif N_X86 asm_x86_xor_r32_r32(emit->as, REG_RET, REG_RET); asm_x86_cmp_r32_with_r32(emit->as, reg_rhs, REG_ARG_2); - static byte ops[6 + 6] = { + static const byte ops[6 + 6] = { // unsigned ASM_X86_CC_JB, ASM_X86_CC_JA, @@ -2581,7 +2581,7 @@ static void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { #elif N_THUMB asm_thumb_cmp_rlo_rlo(emit->as, REG_ARG_2, reg_rhs); if (asm_thumb_allow_armv7m(emit->as)) { - static uint16_t ops[6 + 6] = { + static const uint16_t ops[6 + 6] = { // unsigned ASM_THUMB_OP_ITE_CC, ASM_THUMB_OP_ITE_HI, @@ -2601,7 +2601,7 @@ static void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { asm_thumb_mov_rlo_i8(emit->as, REG_RET, 1); asm_thumb_mov_rlo_i8(emit->as, REG_RET, 0); } else { - static uint16_t ops[6 + 6] = { + static const uint16_t ops[6 + 6] = { // unsigned ASM_THUMB_CC_CC, ASM_THUMB_CC_HI, @@ -2624,7 +2624,7 @@ static void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { } #elif N_ARM asm_arm_cmp_reg_reg(emit->as, REG_ARG_2, reg_rhs); - static uint ccs[6 + 6] = { + static const uint ccs[6 + 6] = { // unsigned ASM_ARM_CC_CC, ASM_ARM_CC_HI, @@ -2642,7 +2642,7 @@ static void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { }; asm_arm_setcc_reg(emit->as, REG_RET, ccs[op_idx]); #elif N_XTENSA || N_XTENSAWIN - static uint8_t ccs[6 + 6] = { + static const uint8_t ccs[6 + 6] = { // unsigned ASM_XTENSA_CC_LTU, 0x80 | ASM_XTENSA_CC_LTU, // for GTU we'll swap args From 44a7731669f1c8429bb61b790a7e8bf8a86b045d Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 4 Feb 2025 00:05:44 +0100 Subject: [PATCH 260/546] py/emitnative: Load and store words just once for Viper code. This commit fixes two Xtensa sequences in order to terminate early when loading and storing word values via an immediate index. This was meant to be part of 55ca3fd67512555707304c6b68b836eb89f09d1c but whilst it was part of the code being tested, it didn't end up in the commit. Signed-off-by: Alessandro Gatti --- py/emitnative.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/py/emitnative.c b/py/emitnative.c index dd0644793231c..1aab0a9eb7865 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -1609,6 +1609,7 @@ static void emit_native_load_subscr(emit_t *emit) { #elif N_XTENSA || N_XTENSAWIN if (index_value > 0 && index_value < 256) { asm_xtensa_l32i_optimised(emit->as, REG_RET, reg_base, index_value); + break; } #endif need_reg_single(emit, reg_index, 0); @@ -1887,6 +1888,7 @@ static void emit_native_store_subscr(emit_t *emit) { #elif N_XTENSA || N_XTENSAWIN if (index_value > 0 && index_value < 256) { asm_xtensa_s32i_optimised(emit->as, REG_RET, reg_base, index_value); + break; } #elif N_ARM ASM_MOV_REG_IMM(emit->as, reg_index, index_value); From 304467518fbe9fc679dbba087fba7931c51655fb Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 23 Jan 2025 13:34:47 +0100 Subject: [PATCH 261/546] samd/boards: Add generic SAMD21x18 board definitions. The definition uses the internal oscillator for clock and only internal flash for the file system. It works at SAMD21G18 and SAMD21E18 devices as well, only that fewer pins are accessible. Tested with a SAMD21E18, SAM21G18 and SAMD21J18 board. Signed-off-by: robert-hh --- ports/samd/boards/SAMD_GENERIC_D21X18/board.json | 16 ++++++++++++++++ ports/samd/boards/SAMD_GENERIC_D21X18/board.md | 4 ++++ .../boards/SAMD_GENERIC_D21X18/mpconfigboard.h | 4 ++++ .../boards/SAMD_GENERIC_D21X18/mpconfigboard.mk | 4 ++++ ports/samd/boards/SAMD_GENERIC_D21X18/pins.csv | 10 ++++++++++ 5 files changed, 38 insertions(+) create mode 100644 ports/samd/boards/SAMD_GENERIC_D21X18/board.json create mode 100644 ports/samd/boards/SAMD_GENERIC_D21X18/board.md create mode 100644 ports/samd/boards/SAMD_GENERIC_D21X18/mpconfigboard.h create mode 100644 ports/samd/boards/SAMD_GENERIC_D21X18/mpconfigboard.mk create mode 100644 ports/samd/boards/SAMD_GENERIC_D21X18/pins.csv diff --git a/ports/samd/boards/SAMD_GENERIC_D21X18/board.json b/ports/samd/boards/SAMD_GENERIC_D21X18/board.json new file mode 100644 index 0000000000000..a14730bd1bf49 --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D21X18/board.json @@ -0,0 +1,16 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "USB" + ], + "images": [ + "generic_board.jpg" + ], + "mcu": "samd21", + "vendor": "Microchip", + "product": "Generic SAMD21J18", + "thumbnail": "" +} diff --git a/ports/samd/boards/SAMD_GENERIC_D21X18/board.md b/ports/samd/boards/SAMD_GENERIC_D21X18/board.md new file mode 100644 index 0000000000000..a10b08f2f1cd7 --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D21X18/board.md @@ -0,0 +1,4 @@ +The following firmware should work on most boards with a SAMD21E18, +SAMD21G18 and SAMD21J18 MCU. It uses only the features built into +the MCU. Additional devices at the board like external flash +are not supported. diff --git a/ports/samd/boards/SAMD_GENERIC_D21X18/mpconfigboard.h b/ports/samd/boards/SAMD_GENERIC_D21X18/mpconfigboard.h new file mode 100644 index 0000000000000..945975ab2384d --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D21X18/mpconfigboard.h @@ -0,0 +1,4 @@ +#define MICROPY_HW_BOARD_NAME "Generic SAMD21J18" +#define MICROPY_HW_MCU_NAME "SAMD21J18A" + +#define MICROPY_HW_DFLL_USB_SYNC (1) diff --git a/ports/samd/boards/SAMD_GENERIC_D21X18/mpconfigboard.mk b/ports/samd/boards/SAMD_GENERIC_D21X18/mpconfigboard.mk new file mode 100644 index 0000000000000..f95c6549381ba --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D21X18/mpconfigboard.mk @@ -0,0 +1,4 @@ +MCU_SERIES = SAMD21 +CMSIS_MCU = SAMD21J18A +LD_FILES = boards/samd21x18a.ld sections.ld +TEXT0 = 0x2000 diff --git a/ports/samd/boards/SAMD_GENERIC_D21X18/pins.csv b/ports/samd/boards/SAMD_GENERIC_D21X18/pins.csv new file mode 100644 index 0000000000000..d35bc9d8ea4a9 --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D21X18/pins.csv @@ -0,0 +1,10 @@ +# The lines contain pairs of Pin name and Pin number. +# Pin names must be valid Python identifiers. +# Pin numbers have the form Pxnn, with x being A, B, C or D. +# Lines starting with # or empty lines are ignored. + +USB_DM,PA24 +USB_DP,PA25 + +SWCLK,PA30 +SWDIO,PA31 From 9ced693ade1a1aca1e76d1eb6f11094e8aa3bfc9 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 23 Jan 2025 13:42:18 +0100 Subject: [PATCH 262/546] samd/boards: Add generic SAMD51x19 board definitions. The definition uses the internal oscillator for clock and only internal flash for the file system. It works at SAMD51G19 and SAMD51J19 devices as well, only that fewer pins are accessible. Tested with a SAMD51G19 and SAMD51J9 board. Signed-off-by: robert-hh --- .../samd/boards/SAMD_GENERIC_D51X19/board.json | 16 ++++++++++++++++ ports/samd/boards/SAMD_GENERIC_D51X19/board.md | 4 ++++ .../boards/SAMD_GENERIC_D51X19/mpconfigboard.h | 2 ++ .../SAMD_GENERIC_D51X19/mpconfigboard.mk | 11 +++++++++++ ports/samd/boards/SAMD_GENERIC_D51X19/pins.csv | 18 ++++++++++++++++++ 5 files changed, 51 insertions(+) create mode 100644 ports/samd/boards/SAMD_GENERIC_D51X19/board.json create mode 100644 ports/samd/boards/SAMD_GENERIC_D51X19/board.md create mode 100644 ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.h create mode 100644 ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.mk create mode 100644 ports/samd/boards/SAMD_GENERIC_D51X19/pins.csv diff --git a/ports/samd/boards/SAMD_GENERIC_D51X19/board.json b/ports/samd/boards/SAMD_GENERIC_D51X19/board.json new file mode 100644 index 0000000000000..21cb114bf65fb --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D51X19/board.json @@ -0,0 +1,16 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "USB" + ], + "images": [ + "generic_board.jpg" + ], + "mcu": "samd51", + "vendor": "Microchip", + "product": "Generic SAMD51P19", + "thumbnail": "" +} diff --git a/ports/samd/boards/SAMD_GENERIC_D51X19/board.md b/ports/samd/boards/SAMD_GENERIC_D51X19/board.md new file mode 100644 index 0000000000000..b4694cc2857d5 --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D51X19/board.md @@ -0,0 +1,4 @@ +The following firmware should work on most boards with a SAMD51G19, +SAMD51J19 and SAMD51P19 MCU. It uses only the features built into +the MCU. Additional devices at the board like external flash +are not supported. diff --git a/ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.h b/ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.h new file mode 100644 index 0000000000000..cce157f9e0ffc --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.h @@ -0,0 +1,2 @@ +#define MICROPY_HW_BOARD_NAME "Generic SAMD51P19" +#define MICROPY_HW_MCU_NAME "SAMD51P19A" diff --git a/ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.mk b/ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.mk new file mode 100644 index 0000000000000..1a20643214f1a --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.mk @@ -0,0 +1,11 @@ +MCU_SERIES = SAMD51 +CMSIS_MCU = SAMD51P19A +LD_FILES = boards/samd51x19a.ld sections.ld +TEXT0 = 0x4000 + +# The ?='s allow overriding in mpconfigboard.mk. +# MicroPython settings +# The size of a MCU flash filesystem will be +# 496k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE +# The default for MICROPY_HW_VFSROMSIZE is 64K +MICROPY_HW_CODESIZE ?= 368K diff --git a/ports/samd/boards/SAMD_GENERIC_D51X19/pins.csv b/ports/samd/boards/SAMD_GENERIC_D51X19/pins.csv new file mode 100644 index 0000000000000..76e38a98467dc --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D51X19/pins.csv @@ -0,0 +1,18 @@ +# The lines contain pairs of Pin name and Pin number. +# Pin names must be valid Python identifiers. +# Pin numbers have the form Pxnn, with x being A, B, C or D. +# Lines starting with # or empty lines are ignored. + +USB_DM,PA24 +USB_DP,PA25 +USB_SOF,PA23 + +QSPI_CS,PB11 +QSPI_SCK,PB10 +QSPI_D0,PA08 +QSPI_D1,PA09 +QSPI_D2,PA10 +QSPI_D3,PA11 + +SWCLK,PA30 +SWDIO,PA31 From 6b2e359076ea394d8a20f2d17b1cd2c68e3b5c74 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 23 Jan 2025 13:54:28 +0100 Subject: [PATCH 263/546] samd/boards: Add generic SAMD51x20 board definitions. The definition uses the internal oscillator for clock and only internal flash for the file system. It works at SAMD51J20 device as well, only that fewer pins are accessible. Tested with a SAMD51J20 board. Signed-off-by: robert-hh --- .../samd/boards/SAMD_GENERIC_D51X20/board.json | 16 ++++++++++++++++ ports/samd/boards/SAMD_GENERIC_D51X20/board.md | 4 ++++ .../boards/SAMD_GENERIC_D51X20/mpconfigboard.h | 2 ++ .../boards/SAMD_GENERIC_D51X20/mpconfigboard.mk | 13 +++++++++++++ ports/samd/boards/SAMD_GENERIC_D51X20/pins.csv | 17 +++++++++++++++++ 5 files changed, 52 insertions(+) create mode 100644 ports/samd/boards/SAMD_GENERIC_D51X20/board.json create mode 100644 ports/samd/boards/SAMD_GENERIC_D51X20/board.md create mode 100644 ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.h create mode 100644 ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk create mode 100644 ports/samd/boards/SAMD_GENERIC_D51X20/pins.csv diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/board.json b/ports/samd/boards/SAMD_GENERIC_D51X20/board.json new file mode 100644 index 0000000000000..ae4f83472ecd4 --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D51X20/board.json @@ -0,0 +1,16 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "USB" + ], + "images": [ + "generic_board.jpg" + ], + "mcu": "samd51", + "vendor": "Microchip", + "product": "Generic SAMD51P20", + "thumbnail": "" +} diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/board.md b/ports/samd/boards/SAMD_GENERIC_D51X20/board.md new file mode 100644 index 0000000000000..4e75f901e3ca5 --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D51X20/board.md @@ -0,0 +1,4 @@ +The following firmware should work on most boards with a +SAMD51J20 and SAMD51P20 MCU. It uses only the features built into +the MCU. Additional devices at the board like external flash +are not supported. diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.h b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.h new file mode 100644 index 0000000000000..30e5fa9e6ebb3 --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.h @@ -0,0 +1,2 @@ +#define MICROPY_HW_BOARD_NAME "Generic SAMD51P20" +#define MICROPY_HW_MCU_NAME "SAMD51P20A" diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk new file mode 100644 index 0000000000000..ddba3dcbbade9 --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk @@ -0,0 +1,13 @@ +MCU_SERIES = SAMD51 +CMSIS_MCU = SAMD51P20A +LD_FILES = boards/samd51x19a.ld sections.ld +TEXT0 = 0x4000 + + +# The ?='s allow overriding in mpconfigboard.mk. +# MicroPython settings +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE +# The default for MICROPY_HW_VFSROMSIZE is 64K +MICROPY_HW_CODESIZE ?= 752K +MICROPY_HW_VFSROMSIZE ?= 128K diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/pins.csv b/ports/samd/boards/SAMD_GENERIC_D51X20/pins.csv new file mode 100644 index 0000000000000..24820c25bbdc9 --- /dev/null +++ b/ports/samd/boards/SAMD_GENERIC_D51X20/pins.csv @@ -0,0 +1,17 @@ +# The lines contain pairs of Pin name and Pin number. +# Pin names must be valid Python identifiers. +# Pin numbers have the form Pxnn, with x being A, B, C or D. +# Lines starting with # or empty lines are ignored. + +USB_DM,PA24 +USB_DP,PA25 + +QSPI_CS,PB11 +QSPI_SCK,PB10 +QSPI_D0,PA08 +QSPI_D1,PA09 +QSPI_D2,PA10 +QSPI_D3,PA11 + +SWCLK,PA30 +SWDIO,PA31 From ff9c6da88c82e8423e577e131d91c5708b788f26 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 23 Jan 2025 14:16:13 +0100 Subject: [PATCH 264/546] samd/Makefile: Add support for board variants. Tested with a Adafruit SAMD QT board, which may optionally be equipped with SPIFLASH memory. Signed-off-by: robert-hh --- ports/samd/Makefile | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ports/samd/Makefile b/ports/samd/Makefile index 75a4d9b1de355..005664f178d3c 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -13,7 +13,19 @@ 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),) +BUILD ?= build-$(BOARD)-$(BOARD_VARIANT) +else BUILD ?= build-$(BOARD) +endif CROSS_COMPILE ?= arm-none-eabi- UF2CONV ?= $(TOP)/tools/uf2conv.py @@ -21,7 +33,13 @@ UF2CONV ?= $(TOP)/tools/uf2conv.py MCU_SERIES_LOWER = $(shell echo $(MCU_SERIES) | tr '[:upper:]' '[:lower:]') include ../../py/mkenv.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 include mcu/$(MCU_SERIES_LOWER)/mpconfigmcu.mk # Qstr definitions (must come before including py.mk) From 6cbe145ca8b46857cd2ba5289021022bb92cb02e Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 23 Jan 2025 14:19:07 +0100 Subject: [PATCH 265/546] samd/boards: Add support for the Adafruit QT Py board. Supporting a variant with an optional SPIFLASH device as well. Tested both variants with a QT Py board. Signed-off-by: robert-hh --- .../boards/ADAFRUIT_QTPY_SAMD21/board.json | 20 +++++++++++++++ .../ADAFRUIT_QTPY_SAMD21/mpconfigboard.h | 6 +++++ .../ADAFRUIT_QTPY_SAMD21/mpconfigboard.mk | 4 +++ .../mpconfigvariant_SPIFLASH.mk | 2 ++ .../samd/boards/ADAFRUIT_QTPY_SAMD21/pins.csv | 25 +++++++++++++++++++ 5 files changed, 57 insertions(+) create mode 100644 ports/samd/boards/ADAFRUIT_QTPY_SAMD21/board.json create mode 100644 ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.h create mode 100644 ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.mk create mode 100644 ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigvariant_SPIFLASH.mk create mode 100644 ports/samd/boards/ADAFRUIT_QTPY_SAMD21/pins.csv diff --git a/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/board.json b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/board.json new file mode 100644 index 0000000000000..f48416dd02ab2 --- /dev/null +++ b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/board.json @@ -0,0 +1,20 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "USB-C" + ], + "images": [ + "qt_py_samd21.jpg" + ], + "mcu": "samd21", + "product": "QT Py - SAMD21", + "thumbnail": "", + "url": "https://www.adafruit.com/product/4600", + "variants": { + "SPIFLASH": "Support for an external Flash chip" + }, + "vendor": "Adafruit" +} diff --git a/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.h b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.h new file mode 100644 index 0000000000000..0acf28afbfe15 --- /dev/null +++ b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.h @@ -0,0 +1,6 @@ +#define MICROPY_HW_BOARD_NAME "QT Py" +#define MICROPY_HW_MCU_NAME "SAMD21E18A" + +#define MICROPY_HW_DFLL_USB_SYNC (1) + +#define MICROPY_HW_SPIFLASH_ID (3) diff --git a/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.mk new file mode 100644 index 0000000000000..5b4d0b63e7e5a --- /dev/null +++ b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.mk @@ -0,0 +1,4 @@ +MCU_SERIES = SAMD21 +CMSIS_MCU = SAMD21E18A +LD_FILES = boards/samd21x18a.ld sections.ld +TEXT0 = 0x2000 diff --git a/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigvariant_SPIFLASH.mk b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigvariant_SPIFLASH.mk new file mode 100644 index 0000000000000..69537d5bf3c51 --- /dev/null +++ b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigvariant_SPIFLASH.mk @@ -0,0 +1,2 @@ +CFLAGS += -DMICROPY_HW_SPIFLASH=1 +MICROPY_HW_CODESIZE ?= 232K diff --git a/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/pins.csv b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/pins.csv new file mode 100644 index 0000000000000..c3ada219f16b2 --- /dev/null +++ b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/pins.csv @@ -0,0 +1,25 @@ +A0,PA02 +A1,PA03 +A2,PA04 +A3,PA05 +SDA,PA16 +SCL,PA17 +TX,PA06 +RX,PA07 +SCK,PA11 +MISO,PA09 +MOSI,PA10 + +NEO_PWR,PA15 +NEOPIX,PA18 + +FLASH_MOSI,PA22 +FLASH_MISO,PA19 +FLASH_SCK,PA23 +FLASH_CS,PA08 + +USB_DM,PA24 +USB_DP,PA25 + +SWCLK,PA30 +SWDIO,PA31 From 36a0a839974c6e49723794421f46f6282e60e56f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 23 Jan 2025 14:21:52 +0100 Subject: [PATCH 266/546] samd/boards: Add support for the Adafruit NeoKey Trinkey board. Tested with that board. Signed-off-by: robert-hh --- .../boards/ADAFRUIT_NEOKEY_TRINKEY/board.json | 18 ++++++++++++++++++ .../ADAFRUIT_NEOKEY_TRINKEY/mpconfigboard.h | 14 ++++++++++++++ .../ADAFRUIT_NEOKEY_TRINKEY/mpconfigboard.mk | 4 ++++ .../boards/ADAFRUIT_NEOKEY_TRINKEY/pins.csv | 14 ++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/board.json create mode 100644 ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/mpconfigboard.h create mode 100644 ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/mpconfigboard.mk create mode 100644 ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/pins.csv diff --git a/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/board.json b/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/board.json new file mode 100644 index 0000000000000..9b0c307d679c3 --- /dev/null +++ b/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/board.json @@ -0,0 +1,18 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "RGB LED", + "USB" + ], + "images": [ + "neokey_trinkey.jpg" + ], + "mcu": "samd21", + "product": "NeoKey Trinkey", + "thumbnail": "", + "url": "https://www.adafruit.com/product/5020", + "vendor": "Adafruit" +} diff --git a/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/mpconfigboard.h b/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/mpconfigboard.h new file mode 100644 index 0000000000000..eb4704ff8cb97 --- /dev/null +++ b/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/mpconfigboard.h @@ -0,0 +1,14 @@ +#define MICROPY_HW_BOARD_NAME "NeoKey Trinkey" +#define MICROPY_HW_MCU_NAME "SAMD21E18A" + +#define MICROPY_HW_DFLL_USB_SYNC (1) + +// The NEOKEY board has just two accessible GPIO pins. +// So many classes and modules are useless. +#define MICROPY_PY_MACHINE_SOFTI2C (0) +#define MICROPY_PY_MACHINE_SOFTSPI (0) +#define MICROPY_PY_MACHINE_I2C (0) +#define MICROPY_PY_MACHINE_SPI (0) +#define MICROPY_PY_MACHINE_UART (0) +#define MICROPY_PY_MACHINE_ADC (0) +#define MICROPY_PY_MACHINE_DAC (0) diff --git a/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/mpconfigboard.mk new file mode 100644 index 0000000000000..5b4d0b63e7e5a --- /dev/null +++ b/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/mpconfigboard.mk @@ -0,0 +1,4 @@ +MCU_SERIES = SAMD21 +CMSIS_MCU = SAMD21E18A +LD_FILES = boards/samd21x18a.ld sections.ld +TEXT0 = 0x2000 diff --git a/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/pins.csv b/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/pins.csv new file mode 100644 index 0000000000000..c979f5b9bc147 --- /dev/null +++ b/ports/samd/boards/ADAFRUIT_NEOKEY_TRINKEY/pins.csv @@ -0,0 +1,14 @@ +# The lines contain pairs of Pin name and Pin number. +# Pin names must be valid Python identifiers. +# Pin numbers have the form Pxnn, with x being A, B, C or D. +# Lines starting with # or empty lines are ignored. + +NEOPIXEL,PA15 +SWITCH,PA18 +TOUCH,PA07 + +USB_DM,PA24 +USB_DP,PA25 + +SWCLK,PA30 +SWDIO,PA31 From a84143d2230daa3843f00dd1f09474441810f08d Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 23 Jan 2025 17:38:40 +0100 Subject: [PATCH 267/546] docs/samd/pinout: Add pinout for Adafruit NeoKey Trinkey and QT Py. Only pins accessible at the board are shown. Signed-off-by: robert-hh --- docs/samd/pinout.rst | 60 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/docs/samd/pinout.rst b/docs/samd/pinout.rst index 5da4ae78d45b5..f6473e5495f5a 100644 --- a/docs/samd/pinout.rst +++ b/docs/samd/pinout.rst @@ -567,6 +567,66 @@ The default devices at the board are: - SPI 0 at pins PA06/PA09/PA08, labelled D4, D2 and D0 - DAC output on pin PA02, labelled D1 +Adafruit QT PY pin assignment table +----------------------------------- + +=== ==== ============ ==== ==== ====== ====== ====== ====== +Pin GPIO Pin name IRQ ADC Serial Serial TCC/TC TCC/TC +=== ==== ============ ==== ==== ====== ====== ====== ====== + 2 PA02 A0 2 0 - - - - + 3 PA03 A1 3 1 - - - - + 4 PA04 A2 4 4 - 0/0 0/0 - + 5 PA05 A3 5 5 - 0/1 0/1 - + 7 PA07 RX 7 7 - 0/3 1/1 - + 6 PA06 TX 6 6 - 0/2 1/0 - + 8 PA08 FLASH_CS - 16 0/0 2/0 0/0 1/2 + 19 PA19 FLASH_MISO 3 - 1/3 3/3 3/1 0/3 + 22 PA22 FLASH_MOSI 6 - 3/0 5/0 4/0 0/4 + 23 PA23 FLASH_SCK 7 - 3/1 5/1 4/1 0/5 + 9 PA09 MISO 9 17 0/1 2/1 0/1 1/3 + 10 PA10 MOSI 10 18 0/2 2/2 1/0 0/2 + 18 PA18 NEOPIX 2 - 1/2 3/2 3/0 0/2 + 15 PA15 NEO_PWR 15 - 2/3 4/3 3/1 0/5 + 11 PA11 SCK 11 19 0/3 2/3 1/1 0/3 + 17 PA17 SCL 1 - 1/1 3/1 2/1 0/7 + 16 PA16 SDA 0 - 1/0 3/0 2/0 0/6 + 30 PA30 SWCLK 10 - - 1/2 1/0 - + 31 PA31 SWDIO 11 - - 1/3 1/1 - + 24 PA24 USB_DM 12 - 3/2 5/2 5/0 1/2 + 25 PA25 USB_DP 13 - 3/3 5/3 5/1 1/3 +=== ==== ============ ==== ==== ====== ====== ====== ====== + +For the definition of the table columns see the explanation at the table for +Adafruit ItsyBitsy M0 Express :ref:`samd21_pinout_table`. + +The default devices at the board are: + +- UART 0 at pins PA07/PA06, labelled RX/TX +- I2C 1 at pins PA16/PA17, labelled SDA/SCL +- SPI 0 at pins PA09/PA10/PA11, labelled MISO, MOSI and SCK +- DAC output on pin PA02, labelled A0 + +Adafruit NeoKey Trinkey pin assignment table +-------------------------------------------- + +=== ==== ============ ==== ==== ====== ====== ====== ====== +Pin GPIO Pin name IRQ ADC Serial Serial TCC/TC TCC/TC +=== ==== ============ ==== ==== ====== ====== ====== ====== + 15 PA15 NEOPIXEL 15 - 2/3 4/3 3/1 0/5 + 30 PA30 SWCLK 10 - - 1/2 1/0 - + 31 PA31 SWDIO 11 - - 1/3 1/1 - + 18 PA18 SWITCH 2 - 1/2 3/2 3/0 0/2 + 7 PA07 TOUCH 7 7 - 0/3 1/1 - + 24 PA24 USB_DM 12 - 3/2 5/2 5/0 1/2 + 25 PA25 USB_DP 13 - 3/3 5/3 5/1 1/3 +=== ==== ============ ==== ==== ====== ====== ====== ====== + +For the definition of the table columns see the explanation at the table for +Adafruit ItsyBitsy M0 Express :ref:`samd21_pinout_table`. + +The board does not provide access to UART, I2C, SPI or DAC. + + SAMD21 Xplained PRO pin assignment table ---------------------------------------- From e44a2c6921919d0d19356a70a23bf21cd301856c Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 23 Jan 2025 20:14:27 +0100 Subject: [PATCH 268/546] docs/samd/pinout: Add pinout for the Generic SAMD board types. The table shows the devices available at the pin and the respective package letter. Signed-off-by: robert-hh --- docs/samd/pinout.rst | 182 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/docs/samd/pinout.rst b/docs/samd/pinout.rst index f6473e5495f5a..776ab74bcdb22 100644 --- a/docs/samd/pinout.rst +++ b/docs/samd/pinout.rst @@ -935,6 +935,188 @@ The default devices at the board are: - SPI 4 at pins PB12/PB11/PB13, labelled MOSI, MISO and SCK - DAC output on pins PA02 and PA05, labelled A0 and A4 +Generic SAMD21x18 pin assignment table +-------------------------------------- + +=== ==== ============ ==== ==== ====== ====== ====== ====== +Pin GPIO Name/Package IRQ ADC Serial Serial TCC/TC TCC/TC +=== ==== ============ ==== ==== ====== ====== ====== ====== + 0 PA00 EGJ 0 - - 1/0 2/0 - + 1 PA01 EGJ 1 - - 1/1 2/1 - + 2 PA02 EGJ 2 0 - - - - + 3 PA03 EGJ 3 1 - - - - + 4 PA04 EGJ 4 4 - 0/0 0/0 - + 5 PA05 EGJ 5 5 - 0/1 0/1 - + 6 PA06 EGJ 6 6 - 0/2 1/0 - + 7 PA07 EGJ 7 7 - 0/3 1/1 - + 8 PA08 EGJ - 16 0/0 2/0 0/0 1/2 + 9 PA09 EGJ 9 17 0/1 2/1 0/1 1/3 + 10 PA10 EGJ 10 18 0/2 2/2 1/0 0/2 + 11 PA11 EGJ 11 19 0/3 2/3 1/1 0/3 + 12 PA12 GJ 12 - 2/0 4/0 2/0 0/6 + 13 PA13 GJ 13 - 2/1 4/1 2/0 0/7 + 14 PA14 EGJ 14 - 2/2 4/2 3/0 0/4 + 15 PA15 EGJ 15 - 2/3 4/3 3/1 0/5 + 16 PA16 EGJ 0 - 1/0 3/0 2/0 0/6 + 17 PA17 EGJ 1 - 1/1 3/1 2/1 0/7 + 18 PA18 EGJ 2 - 1/2 3/2 3/0 0/2 + 19 PA19 EGJ 3 - 1/3 3/3 3/1 0/3 + 20 PA20 GJ 4 - 5/2 3/2 7/0 0/4 + 21 PA21 GJ 5 - 5/3 3/3 7/1 0/7 + 22 PA22 EGJ 6 - 3/0 5/0 4/0 0/4 + 23 PA23 EGJ 7 - 3/1 5/1 4/1 0/5 + 24 PA24 USB_DM 12 - 3/2 5/2 5/0 1/2 + 25 PA25 USB_DP 13 - 3/3 5/3 5/1 1/3 + 27 PA27 EGJ 15 - - - - - + 28 PA28 EGJ 8 - - - - - + 30 PA30 SWCLK 10 - - 1/2 1/0 - + 31 PA31 SWDIO 11 - - 1/3 1/1 - + 32 PB00 J 0 8 - 5/2 7/0 - + 33 PB01 J 1 9 - 5/3 7/1 - + 34 PB02 GJ 2 10 - 5/0 6/0 - + 35 PB03 GJ 3 11 - 5/1 6/1 - + 36 PB04 J 4 12 - - - - + 37 PB05 J 5 13 - - - - + 38 PB06 J 6 14 - - - - + 39 PB07 J 7 15 - - - - + 40 PB08 GJ 8 2 - 4/0 4/0 - + 41 PB09 GJ 9 3 - 4/1 4/1 - + 42 PB10 GJ 10 - - 4/2 5/0 0/4 + 43 PB11 GJ 11 - - 4/3 5/1 0/5 + 44 PB12 J 12 - 4/0 - 4/0 0/6 + 45 PB13 J 13 - 4/1 - 4/1 0/7 + 46 PB14 J 14 - 4/2 - 5/0 - + 47 PB15 J 15 - 4/3 - 5/1 - + 48 PB16 J 0 - 5/0 - 6/0 0/4 + 49 PB17 J 1 - 5/1 - 6/1 0/5 + 54 PB22 GJ 6 - - 5/2 7/0 - + 55 PB23 GJ 7 - - 5/3 7/1 - + 62 PB30 J 14 - - 5/0 0/0 1/2 + 63 PB31 J 15 - - 5/1 0/1 1/3 +=== ==== ============ ==== ==== ====== ====== ====== ====== + +For the definition of the table columns see the explanation at the table for +Adafruit ItsyBitsy M0 Express :ref:`samd21_pinout_table`. + +The Package column indicates the package letter providing this pin. An entry +EGJ tells for instance, that the pin is available for SAMD21E18, SAMD21G18 and +SAMD21J18. + + +Generic SAMD51x19 and SAM51x20 pin assignment table +--------------------------------------------------- + +For the definition of the table columns see the explanation at the table for +Adafruit ItsyBitsy M4 Express :ref:`samd51_pinout_table`. + +=== ==== ============ ==== ==== ==== ====== ====== ===== ===== ===== +Pin GPIO Name/Package IRQ ADC ADC Serial Serial TC PWM PWM +=== ==== ============ ==== ==== ==== ====== ====== ===== ===== ===== + 8 PA08 QSPI_D0 - 8 2 0/0 2/1 0/0 0/0 1/4 + 9 PA09 QSPI_D1 9 9 3 0/1 2/0 0/1 0/1 1/5 + 10 PA10 QSPI_D2 10 10 - 0/2 2/2 1/0 0/2 1/6 + 11 PA11 QSPI_D3 11 11 - 0/3 2/3 1/1 0/3 1/7 + 42 PB10 QSPI_SCK 10 - - - 4/2 5/0 0/4 1/0 + 23 PA23 USB_SOF 7 - - 3/1 5/0 4/1 1/7 0/3 + 24 PA24 USB_DM 8 - - 3/2 5/2 5/0 2/2 - + 25 PA25 USB_DP 9 - - 3/3 5/3 5/1 - - + 0 PA00 GJP 0 - - - 1/0 2/0 - - + 1 PA01 GJP 1 - - - 1/1 2/1 - - + 2 PA02 GJP 2 0 - - - - - - + 3 PA03 GJP 3 10 - - - - - - + 4 PA04 GJP 4 4 - - 0/0 0/0 - - + 5 PA05 GJP 5 5 - - 0/1 0/1 - - + 6 PA06 GJP 6 6 - - 0/2 1/0 - - + 7 PA07 GJP 7 7 - - 0/3 1/1 - - + 12 PA12 GJP 12 - - 2/0 4/1 2/0 0/6 1/2 + 13 PA13 GJP 13 - - 2/1 4/0 2/1 0/7 1/3 + 14 PA14 GJP 14 - - 2/2 4/2 3/0 2/0 1/2 + 15 PA15 GJP 15 - - 2/3 4/3 3/1 2/1 1/3 + 16 PA16 GJP 0 - - 1/0 3/1 2/0 1/0 0/4 + 17 PA17 GJP 1 - - 1/1 3/0 2/1 1/1 0/5 + 18 PA18 GJP 2 - - 1/2 3/2 3/0 1/2 0/6 + 19 PA19 GJP 3 - - 1/3 3/3 3/1 1/3 0/7 + 20 PA20 GJP 4 - - 5/2 3/2 7/0 1/4 0/0 + 21 PA21 GJP 5 - - 5/3 3/3 7/1 1/5 0/1 + 22 PA22 GJP 6 - - 3/0 5/1 4/0 1/6 0/2 + 27 PA27 GJP 11 - - - - - - - + 30 PA30 SWCLK 14 - - 7/2 1/2 6/0 2/0 - + 31 PA31 SWDIO 15 - - 7/3 1/3 6/1 2/1 - + 32 PB00 JP 0 12 - - 5/2 7/0 - - + 33 PB01 JP 1 13 - - 5/3 7/1 - - + 34 PB02 GJP 2 14 - - 5/0 6/0 2/2 - + 35 PB03 GJP 3 15 - - 5/1 6/1 - - + 36 PB04 JP 4 - 6 - - - - - + 37 PB05 JP 5 - 7 - - - - - + 38 PB06 JP 6 - 8 - - - - - + 39 PB07 JP 7 - 9 - - - - - + 40 PB08 GJP 8 2 0 - 4/0 4/0 - - + 41 PB09 GJP 9 3 1 - 4/1 4/1 - - + 44 PB12 JP 12 - - 4/0 - 4/0 3/0 0/0 + 45 PB13 JP 13 - - 4/1 - 4/1 3/1 0/1 + 46 PB14 JP 14 - - 4/2 - 5/0 4/0 0/2 + 47 PB15 JP 15 - - 4/3 - 5/1 4/1 0/3 + 48 PB16 JP 0 - - 5/0 - 6/0 3/0 0/4 + 49 PB17 JP 1 - - 5/1 - 6/1 3/1 0/5 + 50 PB18 P 2 - - 5/2 7/2 - 1/0 - + 51 PB19 P 3 - - 5/3 7/3 - 1/1 - + 52 PB20 P 4 - - 3/0 7/1 - 1/2 - + 53 PB21 P 5 - - 3/1 7/0 - 1/3 - + 54 PB22 GJP 6 - - 1/2 5/2 7/0 - - + 55 PB23 GJP 7 - - 1/3 5/3 7/1 - - + 56 PB24 P 8 - - 0/0 2/1 - - - + 57 PB25 P 9 - - 0/1 2/0 - - - + 58 PB26 P 12 - - 2/0 4/1 - 1/2 - + 59 PB27 P 13 - - 2/1 4/0 - 1/3 - + 60 PB28 P 14 - - 2/2 4/2 - 1/4 - + 61 PB29 P 15 - - 2/3 4/3 - 1/5 - + 62 PB30 JP 14 - - 7/0 5/1 0/0 4/0 0/6 + 63 PB31 JP 15 - - 7/1 5/0 0/1 4/1 0/7 + 64 PC00 P 0 - 10 - - - - - + 65 PC01 P 1 - 11 - - - - - + 66 PC02 P 2 - 4 - - - - - + 67 PC03 P 3 - 5 - - - - - + 68 PC04 P 4 - - 6/0 - - 0/0 - + 69 PC05 P 5 - - 6/1 - - - - + 70 PC06 P 6 - - 6/2 - - - - + 71 PC07 P 9 - - 6/3 - - - - + 74 PC10 P 10 - - 6/2 7/2 - 0/0 1/4 + 75 PC11 P 11 - - 6/3 7/3 - 0/1 1/5 + 76 PC12 P 12 - - 7/0 6/1 - 0/2 1/6 + 77 PC13 P 13 - - 7/1 6/0 - 0/3 1/7 + 78 PC14 P 14 - - 7/2 6/2 - 0/4 1/0 + 79 PC15 P 15 - - 7/3 6/3 - 0/5 1/1 + 80 PC16 P 0 - - 6/0 0/1 - 0/0 - + 81 PC17 P 1 - - 6/1 0/0 - 0/1 - + 82 PC18 P 2 - - 6/2 0/2 - 0/2 - + 83 PC19 P 3 - - 6/3 0/3 - 0/3 - + 84 PC20 P 4 - - - - - 0/4 - + 85 PC21 P 5 - - - - - 0/5 - + 86 PC22 P 6 - - 1/0 3/1 - 0/5 - + 87 PC23 P 7 - - 1/1 3/0 - 0/7 - + 88 PC24 P 8 - - 0/2 2/2 - - - + 89 PC25 P 9 - - 0/3 2/3 - - - + 90 PC26 P 10 - - - - - - - + 91 PC27 P 11 - - 1/0 - - - - + 92 PC28 P 12 - - 1/1 - - - - + 94 PC30 P 14 - 12 - - - - - + 95 PC31 P 15 - 13 - - - - - + 96 PD00 P 0 - 14 - - - - - + 97 PD01 P 1 - 15 - - - - - +104 PD08 P 3 - - 7/0 6/1 - 0/1 - +105 PD09 P 4 - - 7/1 6/0 - 0/2 - +106 PD10 P 5 - - 7/2 6/2 - 0/3 - +107 PD11 P 6 - - 7/3 6/3 - 0/4 - +108 PD12 P 7 - - - - - 0/5 - +116 PD20 P 10 - - 1/2 3/2 - 1/0 - +117 PD21 P 11 - - 1/3 3/3 - 1/1 - +=== ==== ============ ==== ==== ==== ====== ====== ===== ===== ===== + + +The Package column indicates the package letter providing this pin. An entry +GJP tells for instance, that the pin is available for SAMD51G19, SAMD51J19/-J20 and +SAMD51P19/-P20. + Scripts for creating the pin assignment tables ---------------------------------------------- From e176fea95cda017cf351e779d1aa323da52f1151 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 25 Jan 2025 08:50:45 +0100 Subject: [PATCH 269/546] mimxrt/irq: Add CSI IRQ. Signed-off-by: iabdalkader --- ports/mimxrt/irq.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/mimxrt/irq.h b/ports/mimxrt/irq.h index b83eb49ffabfa..8202a98e8748c 100644 --- a/ports/mimxrt/irq.h +++ b/ports/mimxrt/irq.h @@ -69,6 +69,8 @@ static inline void restore_irq_pri(uint32_t basepri) { #define IRQ_PRI_SYSTICK NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 0, 0) +#define IRQ_PRI_CSI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 3, 0) + #define IRQ_PRI_OTG_HS NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 6, 0) #define IRQ_PRI_EXTINT NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 14, 0) From 67ebc537c3d0095d3b6a0ef8a7160ba06ea95dff Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 25 Jan 2025 08:59:47 +0100 Subject: [PATCH 270/546] mimxrt/machine_rtc: Fix build with new SDKs. In more recent SDKs, this feature is actually disabled for the MIMXRT1062. Signed-off-by: iabdalkader --- ports/mimxrt/machine_rtc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/mimxrt/machine_rtc.c b/ports/mimxrt/machine_rtc.c index 5feeb0da096ae..e6c519991983b 100644 --- a/ports/mimxrt/machine_rtc.c +++ b/ports/mimxrt/machine_rtc.c @@ -156,8 +156,10 @@ void machine_rtc_start(void) { SNVS->HPCOMR |= SNVS_HPCOMR_NPSWA_EN_MASK; // Do a basic init. SNVS_LP_Init(SNVS); + #if FSL_FEATURE_SNVS_HAS_MULTIPLE_TAMPER // Disable all external Tamper SNVS_LP_DisableAllExternalTamper(SNVS); + #endif SNVS_LP_SRTC_StartTimer(SNVS); // If the date is not set, set it to a more recent start date, From d76733d05856d389f216d60c71d1a7043d78c510 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 25 Jan 2025 13:42:24 +0100 Subject: [PATCH 271/546] mimxrt/mpconfigport: Remove hard-coded CMSIS header. Signed-off-by: iabdalkader --- ports/mimxrt/mpconfigport.h | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 146c86015afd2..923eff4dc7064 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -29,7 +29,6 @@ // Board specific definitions #include "mpconfigboard.h" #include "fsl_common.h" -#include "lib/nxp_driver/sdk/CMSIS/Include/core_cm7.h" uint32_t trng_random_u32(void); From 9d0a5ac7e9ac8b90d1bbea402fd16a410aa4008f Mon Sep 17 00:00:00 2001 From: StrayCat Date: Thu, 2 Jan 2025 23:46:44 +1300 Subject: [PATCH 272/546] esp32/boards: Enable I2S on ESP32C3 boards. Signed-off-by: StrayCat --- ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h | 1 - ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h | 1 - 2 files changed, 2 deletions(-) diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h b/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h index 0dbfae03a367c..a6cebdf6ee64f 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h +++ b/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h @@ -4,7 +4,6 @@ #define MICROPY_HW_MCU_NAME "ESP32C3" #define MICROPY_HW_ENABLE_SDCARD (0) -#define MICROPY_PY_MACHINE_I2S (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) diff --git a/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h b/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h index 9b304b69f6fba..46d720edc758f 100644 --- a/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h +++ b/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h @@ -3,7 +3,6 @@ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-c3-mini" #define MICROPY_HW_ENABLE_SDCARD (0) -#define MICROPY_PY_MACHINE_I2S (0) #define MICROPY_HW_I2C0_SCL (10) #define MICROPY_HW_I2C0_SDA (8) From 0662c551216db1a3ab40dafbeffcc3ee929d7690 Mon Sep 17 00:00:00 2001 From: Markus Gyger Date: Tue, 31 Dec 2024 15:34:17 +0700 Subject: [PATCH 273/546] rp2/rp2_pio: Add side_pindir support for PIO. Side-setting can also be used to change pin directions instead of pin values. This adds a parameter `side_pindir` to decorator `asm_pio()` to configure it. Also replaces a few close-by 0s with corresponding PIO.* constants. Addresses issue #10027. Signed-off-by: Markus Gyger --- docs/library/rp2.rst | 8 +++++--- ports/rp2/modules/rp2.py | 9 +++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/library/rp2.rst b/docs/library/rp2.rst index f0189327dfb77..a215e98b51e55 100644 --- a/docs/library/rp2.rst +++ b/docs/library/rp2.rst @@ -23,7 +23,7 @@ The ``rp2`` module includes functions for assembling PIO programs. For running PIO programs, see :class:`rp2.StateMachine`. -.. function:: asm_pio(*, out_init=None, set_init=None, sideset_init=None, in_shiftdir=0, out_shiftdir=0, autopush=False, autopull=False, push_thresh=32, pull_thresh=32, fifo_join=PIO.JOIN_NONE) +.. function:: asm_pio(*, out_init=None, set_init=None, sideset_init=None, side_pindir=False, in_shiftdir=PIO.SHIFT_LEFT, out_shiftdir=PIO.SHIFT_LEFT, autopush=False, autopull=False, push_thresh=32, pull_thresh=32, fifo_join=PIO.JOIN_NONE) Assemble a PIO program. @@ -35,8 +35,10 @@ For running PIO programs, see :class:`rp2.StateMachine`. - *out_init* configures the pins used for ``out()`` instructions. - *set_init* configures the pins used for ``set()`` instructions. There can be at most 5. - - *sideset_init* configures the pins used side-setting. There can be at - most 5. + - *sideset_init* configures the pins used for ``.side()`` modifiers. There + can be at most 5. + - *side_pindir* when set to ``True`` configures ``.side()`` modifiers to be + used for pin directions, instead of pin values (the default, when ``False``). The following parameters are used by default, but can be overridden in `StateMachine.init()`: diff --git a/ports/rp2/modules/rp2.py b/ports/rp2/modules/rp2.py index 9d13bf1b52cd5..e9be7dac8d58e 100644 --- a/ports/rp2/modules/rp2.py +++ b/ports/rp2/modules/rp2.py @@ -26,20 +26,21 @@ def __init__( out_init=None, set_init=None, sideset_init=None, - in_shiftdir=0, - out_shiftdir=0, + side_pindir=False, + in_shiftdir=PIO.SHIFT_LEFT, + out_shiftdir=PIO.SHIFT_LEFT, autopush=False, autopull=False, push_thresh=32, pull_thresh=32, - fifo_join=0, + fifo_join=PIO.JOIN_NONE, ): # array is a built-in module so importing it here won't require # scanning the filesystem. from array import array self.labels = {} - execctrl = 0 + execctrl = side_pindir << 29 shiftctrl = ( fifo_join << 30 | (pull_thresh & 0x1F) << 25 From b2ce9b6fb03ed27943733235eac2f3f5e0d5b97d Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Wed, 4 Dec 2024 16:32:42 -0700 Subject: [PATCH 274/546] rp2/boards: Add SparkFun IoT Node LoRaWAN board. Signed-off-by: Dryw Wade --- .../board.json | 25 ++++++ .../mpconfigboard.cmake | 7 ++ .../mpconfigboard.h | 33 +++++++ .../SPARKFUN_IOTNODE_LORAWAN_RP2350/pins.csv | 34 +++++++ .../sparkfun_iotnode_lorawan_rp2350.h | 88 +++++++++++++++++++ 5 files changed, 187 insertions(+) create mode 100644 ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/board.json create mode 100644 ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.cmake create mode 100644 ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.h create mode 100644 ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/pins.csv create mode 100644 ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/sparkfun_iotnode_lorawan_rp2350.h diff --git a/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/board.json b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/board.json new file mode 100644 index 0000000000000..4ddcd999fe5f8 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/board.json @@ -0,0 +1,25 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Battery Charging", + "Dual-core", + "External Flash", + "External RAM", + "JST-SH", + "LoRa", + "RGB LED", + "USB-C", + "microSD" + ], + "images": [ + "26060-IoT-Node-LoRaWAN-Feature-new.jpg" + ], + "mcu": "rp2350", + "product": "IoT Node LoRaWAN RP2350", + "thumbnail": "", + "url": "https://www.sparkfun.com/products/26060", + "vendor": "Sparkfun" +} diff --git a/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.cmake b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.cmake new file mode 100644 index 0000000000000..11fe1ee28fce2 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.cmake @@ -0,0 +1,7 @@ +# cmake file for SparkFun IoT Node LoRaWAN RP2350 + +# TODO: DELETE THIS LINE ONCE PICO SDK 2.1.1 IS RELEASED +set(PICO_BOARD_HEADER_DIRS ${MICROPY_PORT_DIR}/boards/${MICROPY_BOARD}) + +set(PICO_BOARD "sparkfun_iotnode_lorawan_rp2350") +set(PICO_PLATFORM "rp2350") diff --git a/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.h new file mode 100644 index 0000000000000..106ec95aa255a --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.h @@ -0,0 +1,33 @@ +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "SparkFun IoT Node LoRaWAN" +#define MICROPY_HW_FLASH_STORAGE_BYTES (14 * 1024 * 1024) + +#define MICROPY_HW_USB_VID (0x1B4F) +#define MICROPY_HW_USB_PID (0x0044) + +#define MICROPY_HW_I2C0_SDA (20) +#define MICROPY_HW_I2C0_SCL (21) + +#define MICROPY_HW_I2C1_SDA (6) +#define MICROPY_HW_I2C1_SCL (7) + +#define MICROPY_HW_SPI0_SCK (2) +#define MICROPY_HW_SPI0_MOSI (3) +#define MICROPY_HW_SPI0_MISO (4) + +#define MICROPY_HW_SPI1_SCK (14) +#define MICROPY_HW_SPI1_MOSI (15) +#define MICROPY_HW_SPI1_MISO (12) + +#define MICROPY_HW_UART0_TX (18) +#define MICROPY_HW_UART0_RX (19) +#define MICROPY_HW_UART0_CTS (2) +#define MICROPY_HW_UART0_RTS (3) + +#define MICROPY_HW_UART1_TX (4) +#define MICROPY_HW_UART1_RX (5) +#define MICROPY_HW_UART1_CTS (6) +#define MICROPY_HW_UART1_RTS (7) + +#define MICROPY_HW_PSRAM_CS_PIN (0) +#define MICROPY_HW_ENABLE_PSRAM (1) diff --git a/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/pins.csv b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/pins.csv new file mode 100644 index 0000000000000..2526b36d10ea2 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/pins.csv @@ -0,0 +1,34 @@ +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 +GP23,GPIO23 +GP24,GPIO24 +GP25,GPIO25 +GP26,GPIO26 +GP27,GPIO27 +GP28,GPIO28 +GP29,GPIO29 +LED,GPIO25 +LED_RGB,GPIO25 +RGB_LED,GPIO25 +NEOPIXEL,GPIO25 diff --git a/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/sparkfun_iotnode_lorawan_rp2350.h b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/sparkfun_iotnode_lorawan_rp2350.h new file mode 100644 index 0000000000000..5bea33cc4c219 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/sparkfun_iotnode_lorawan_rp2350.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// ----------------------------------------------------- +// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO +// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES +// ----------------------------------------------------- +// Board definition for the SparkFun IoT Node LoRaWAN +// +// This header may be included by other board headers as "boards/sparkfun_iotnode_lorawan_rp2350.h" + +// pico_cmake_set PICO_PLATFORM=rp2350 + +#ifndef _BOARDS_SPARKFUN_IOTNODE_LORAWAN_RP2350_H +#define _BOARDS_SPARKFUN_IOTNODE_LORAWAN_RP2350_H + +// For board detection +#define SPARKFUN_IOTNODE_LORAWAN_RP2350 + +// --- RP2350 VARIANT --- +#define PICO_RP2350A 1 + +// --- UART --- +#ifndef PICO_DEFAULT_UART +#define PICO_DEFAULT_UART 0 +#endif +#ifndef PICO_DEFAULT_UART_TX_PIN +#define PICO_DEFAULT_UART_TX_PIN 18 +#endif +#ifndef PICO_DEFAULT_UART_RX_PIN +#define PICO_DEFAULT_UART_RX_PIN 19 +#endif + +// --- LED --- +#ifndef PICO_DEFAULT_WS2812_PIN +#define PICO_DEFAULT_WS2812_PIN 25 +#endif + +// --- I2C --- +#ifndef PICO_DEFAULT_I2C +#define PICO_DEFAULT_I2C 0 +#endif +#ifndef PICO_DEFAULT_I2C_SDA_PIN +#define PICO_DEFAULT_I2C_SDA_PIN 20 +#endif +#ifndef PICO_DEFAULT_I2C_SCL_PIN +#define PICO_DEFAULT_I2C_SCL_PIN 21 +#endif + +// --- SPI --- +#ifndef PICO_DEFAULT_SPI +#define PICO_DEFAULT_SPI 1 +#endif +#ifndef PICO_DEFAULT_SPI_SCK_PIN +#define PICO_DEFAULT_SPI_SCK_PIN 14 +#endif +#ifndef PICO_DEFAULT_SPI_TX_PIN +#define PICO_DEFAULT_SPI_TX_PIN 15 +#endif +#ifndef PICO_DEFAULT_SPI_RX_PIN +#define PICO_DEFAULT_SPI_RX_PIN 12 +#endif +#ifndef PICO_DEFAULT_SPI_CSN_PIN +#define PICO_DEFAULT_SPI_CSN_PIN 13 +#endif + +// --- FLASH --- + +#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1 + +#ifndef PICO_FLASH_SPI_CLKDIV +#define PICO_FLASH_SPI_CLKDIV 2 +#endif + +// pico_cmake_set_default PICO_FLASH_SIZE_BYTES = (16 * 1024 * 1024) +#ifndef PICO_FLASH_SIZE_BYTES +#define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) +#endif + +// pico_cmake_set_default PICO_RP2350_A2_SUPPORTED = 1 +#ifndef PICO_RP2350_A2_SUPPORTED +#define PICO_RP2350_A2_SUPPORTED 1 +#endif + +#endif From e574f6882082058b868b2f81955d8204fa1c852d Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 3 Oct 2024 09:07:48 +0200 Subject: [PATCH 275/546] mimxrt: Add support for a UF2 bootloader. Allowing to use e.g. the Adafruit bootloaders with MicroPython. The .uf2 file is created in addition to the .bin and .hex files allowing to use the latter ones without the bootloader for debugging and testing. Changes: - Set the location of the ISR Vector and .text segment to 0x6000C000 and 0x6000C400. - Reserve an area at the start of ITCM for a copy of the interrupt vector table and copy the table on reset to this place. - Extend `machine.bootloader()` by setting the magic number to enable the bootloader on reset. - Create a .uf2 file which skips the segments below 0x6000C000. The bootloader has to be installed as a preparation step using the board specific methods, but then the firmware's .uf2 file version can be installed using the bootloader. The bootloader can be invoked with: - double reset - calling machine.bootloader() - Using the touch1200 method Double reset is hard to achieve on MIMXRT boards, since there is no clean reset pin. Some MIMXRT boards provide it by switching the power. Some boards are excluded from the .uf2 build: - MIMXRT1050_EVK: The uf2 bootloader is built for the QSPI version of the board. MicroPython supports the Hyperflash version. - MIMXRT1176_EVK: No support for this board yet, but it should be possible. Signed-off-by: robert-hh --- ports/mimxrt/Makefile | 21 +++++++++++++++++++ .../boards/ADAFRUIT_METRO_M7/mpconfigboard.mk | 2 ++ .../boards/MIMXRT1010_EVK/mpconfigboard.mk | 2 ++ ports/mimxrt/boards/MIMXRT1011.ld | 4 ++-- ports/mimxrt/boards/MIMXRT1015.ld | 4 ++-- .../boards/MIMXRT1015_EVK/mpconfigboard.mk | 2 ++ .../boards/MIMXRT1020_EVK/mpconfigboard.mk | 2 ++ ports/mimxrt/boards/MIMXRT1021.ld | 4 ++-- ports/mimxrt/boards/MIMXRT1052.ld | 4 ++-- .../boards/MIMXRT1060_EVK/mpconfigboard.mk | 2 ++ ports/mimxrt/boards/MIMXRT1062.ld | 4 ++-- ports/mimxrt/boards/MIMXRT1064.ld | 4 ++-- .../boards/MIMXRT1064_EVK/mpconfigboard.mk | 2 ++ .../boards/OLIMEX_RT1010/mpconfigboard.mk | 2 ++ .../boards/SEEED_ARCH_MIX/mpconfigboard.mk | 2 ++ ports/mimxrt/boards/TEENSY40/mpconfigboard.mk | 2 ++ ports/mimxrt/boards/TEENSY41/mpconfigboard.mk | 2 ++ ports/mimxrt/boards/common.ld | 11 +++++----- ports/mimxrt/hal/resethandler_MIMXRT10xx.S | 13 ++++++++++++ ports/mimxrt/modmachine.c | 14 +++++++++++-- ports/mimxrt/mpconfigport.h | 2 ++ tools/autobuild/build-boards.sh | 2 +- 22 files changed, 87 insertions(+), 20 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 3a9550cc974d8..ad7d853b677a5 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -21,6 +21,7 @@ BUILD ?= build-$(BOARD) PORT ?= /dev/ttyACM0 CROSS_COMPILE ?= arm-none-eabi- GIT_SUBMODULES += lib/tinyusb lib/nxp_driver +UF2CONV ?= $(TOP)/tools/uf2conv.py # MicroPython feature configurations MICROPY_VFS_LFS2 ?= 1 @@ -162,6 +163,13 @@ SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/fsl_romapi.c endif +# If not empty, then it is 10xx. +ifneq ($(findstring MIMXRT10, $(MCU_SERIES)),) +APPLICATION_ADDR := 0x6000C000 +else +APPLICATION_ADDR := 0x3000C000 +endif + ifeq ($(MCU_SERIES), MIMXRT1176) INC += -I$(TOP)/$(MCU_DIR)/drivers/cm7 @@ -253,6 +261,11 @@ else $(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) endif +# Set a flag if the UF2 bootloader is used +ifeq ($(USE_UF2_BOOTLOADER),1) + CFLAGS += -DMICROPY_MACHINE_UF2_BOOTLOADER=1 +endif + # Add sources for respective board flash type # Add hal/flexspi_nor_flash.c or hal/flashspi_hyper_flash.c respectively SRC_HAL_C += hal/flexspi_$(subst qspi_,,$(FLEXSPI_FLASH_TYPE)).c @@ -470,7 +483,11 @@ $(BUILD)/lib/tinyusb/src/device/usbd.o: CFLAGS += -Wno-missing-braces # Build targets # ============================================================================= +ifeq ($(USE_UF2_BOOTLOADER),1) +all: $(BUILD)/firmware.hex $(BUILD)/firmware.bin $(BUILD)/firmware.uf2 +else all: $(BUILD)/firmware.hex $(BUILD)/firmware.bin +endif # Process linker scripts with C preprocessor to exchange LDDEFINES and # aggregate output of preprocessor in a single linker script `link.ld` @@ -487,6 +504,10 @@ $(BUILD)/firmware.bin: $(BUILD)/firmware.elf $(BUILD)/firmware.hex: $(BUILD)/firmware.elf $(Q)$(OBJCOPY) -O ihex -R .eeprom $< $@ +$(BUILD)/firmware.uf2: $(BUILD)/firmware.elf + $(Q)$(OBJCOPY) -O binary -R .stack -R .ivt -R .flash_config $^ $@-binpart + $(Q)$(PYTHON) $(UF2CONV) -b $(APPLICATION_ADDR) -f MIMXRT10XX -c -o $@ $@-binpart + # Making OBJ use an order-only dependency on the generated pins.h file # has the side effect of making the pins.h file before we actually compile # any of the objects. The normal dependency generation will deal with the diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk index a8f7add6d26ae..74d3a6f0f10e3 100644 --- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk +++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk @@ -8,3 +8,5 @@ MICROPY_HW_FLASH_SIZE ?= 0x800000 # 8MB MICROPY_PY_NETWORK_NINAW10 ?= 1 MICROPY_PY_SSL ?= 1 MICROPY_SSL_MBEDTLS ?= 1 + +USE_UF2_BOOTLOADER = 1 diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk index dd525859063d9..1aef451e2a6a5 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk @@ -5,6 +5,8 @@ MICROPY_FLOAT_IMPL = single MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x1000000 # 16MB +USE_UF2_BOOTLOADER = 1 + JLINK_PATH ?= /media/RT1010-EVK/ JLINK_COMMANDER_SCRIPT = $(BUILD)/script.jlink diff --git a/ports/mimxrt/boards/MIMXRT1011.ld b/ports/mimxrt/boards/MIMXRT1011.ld index 908eefffd643f..ab363bd56a025 100644 --- a/ports/mimxrt/boards/MIMXRT1011.ld +++ b/ports/mimxrt/boards/MIMXRT1011.ld @@ -14,9 +14,9 @@ flash_config_start = flash_start + 0x00000400; flash_config_size = 0x00000C00; ivt_start = flash_start + 0x00001000; ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; +interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; +text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); diff --git a/ports/mimxrt/boards/MIMXRT1015.ld b/ports/mimxrt/boards/MIMXRT1015.ld index 90336a2437127..0237d348c2498 100644 --- a/ports/mimxrt/boards/MIMXRT1015.ld +++ b/ports/mimxrt/boards/MIMXRT1015.ld @@ -14,9 +14,9 @@ flash_config_start = flash_start; flash_config_size = 0x00001000; ivt_start = flash_start + 0x00001000; ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; +interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; +text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk index 34e5cdee51134..0f719ac0a9753 100644 --- a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk @@ -6,3 +6,5 @@ MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x1000000 # 16MB MICROPY_BOOT_BUFFER_SIZE = (32 * 1024) + +USE_UF2_BOOTLOADER = 1 diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk index c98843a1a3c3d..7715e669f8d09 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk @@ -12,6 +12,8 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +USE_UF2_BOOTLOADER = 1 + FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py JLINK_PATH ?= /media/RT1020-EVK/ diff --git a/ports/mimxrt/boards/MIMXRT1021.ld b/ports/mimxrt/boards/MIMXRT1021.ld index bef0c13df5505..78add04c0c26c 100644 --- a/ports/mimxrt/boards/MIMXRT1021.ld +++ b/ports/mimxrt/boards/MIMXRT1021.ld @@ -14,9 +14,9 @@ flash_config_start = flash_start; flash_config_size = 0x00001000; ivt_start = flash_start + 0x00001000; ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; +interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; +text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); diff --git a/ports/mimxrt/boards/MIMXRT1052.ld b/ports/mimxrt/boards/MIMXRT1052.ld index ca656711a5a77..ea034d713e2f6 100644 --- a/ports/mimxrt/boards/MIMXRT1052.ld +++ b/ports/mimxrt/boards/MIMXRT1052.ld @@ -16,9 +16,9 @@ flash_config_start = flash_start; flash_config_size = 0x00001000; ivt_start = flash_start + 0x00001000; ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; +interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; +text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00200000; text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk index 3af7cd231a2ab..f57aaff3b8b17 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk @@ -12,6 +12,8 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +USE_UF2_BOOTLOADER = 1 + FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py JLINK_PATH ?= /media/RT1060-EVK/ diff --git a/ports/mimxrt/boards/MIMXRT1062.ld b/ports/mimxrt/boards/MIMXRT1062.ld index 5b91550d97d60..3d7e6d0634196 100644 --- a/ports/mimxrt/boards/MIMXRT1062.ld +++ b/ports/mimxrt/boards/MIMXRT1062.ld @@ -16,9 +16,9 @@ flash_config_start = flash_start; flash_config_size = 0x00001000; ivt_start = flash_start + 0x00001000; ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; +interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; +text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); diff --git a/ports/mimxrt/boards/MIMXRT1064.ld b/ports/mimxrt/boards/MIMXRT1064.ld index 708dac4d515c4..7c35cb60c750b 100644 --- a/ports/mimxrt/boards/MIMXRT1064.ld +++ b/ports/mimxrt/boards/MIMXRT1064.ld @@ -10,9 +10,9 @@ flash_config_start = flash_start; flash_config_size = 0x00001000; ivt_start = flash_start + 0x00001000; ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; +interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; +text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk index 95cfb4585acc1..f175d26377abc 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk @@ -12,6 +12,8 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +USE_UF2_BOOTLOADER = 1 + FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py JLINK_PATH ?= /media/RT1064-EVK/ diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk index 58429d298118e..1de497d74e3f0 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk @@ -6,6 +6,8 @@ MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x200000 # 2MB MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB +USE_UF2_BOOTLOADER = 1 + CFLAGS += -DMICROPY_HW_FLASH_DQS=kFlexSPIReadSampleClk_LoopbackInternally SRC_C += \ diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk index 63e68e1e57994..c691e2dcb55c4 100644 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk +++ b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk @@ -12,6 +12,8 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +USE_UF2_BOOTLOADER = 1 + FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py CFLAGS += -DSPI_RETRY_TIMES=1000000 diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk index 07f174944b79c..87c122da9d49d 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk @@ -6,5 +6,7 @@ MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x200000 # 2MB MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB +USE_UF2_BOOTLOADER = 1 + deploy: $(BUILD)/firmware.hex teensy_loader_cli --mcu=imxrt1062 -v -w $< diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk index b297448a307e7..e28310d091ad2 100755 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk @@ -10,6 +10,8 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +USE_UF2_BOOTLOADER = 1 + FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py deploy: $(BUILD)/firmware.hex diff --git a/ports/mimxrt/boards/common.ld b/ports/mimxrt/boards/common.ld index 587fc82a1953d..477ba38bc89a3 100644 --- a/ports/mimxrt/boards/common.ld +++ b/ports/mimxrt/boards/common.ld @@ -42,7 +42,8 @@ MEMORY m_vfs (RX) : ORIGIN = vfs_start, LENGTH = vfs_size /* Teensy uses the last bit of flash for recovery. */ m_reserved (RX) : ORIGIN = (vfs_start + vfs_size), LENGTH = reserved_size - m_itcm (RX) : ORIGIN = itcm_start, LENGTH = itcm_size + m_isr (RX) : ORIGIN = itcm_start, LENGTH = 0x400 + m_itcm (RX) : ORIGIN = itcm_start + 0x400, LENGTH = itcm_size - 0x400 m_dtcm (RW) : ORIGIN = dtcm_start, LENGTH = dtcm_size m_ocrm (RW) : ORIGIN = ocrm_start, LENGTH = ocrm_size @@ -80,7 +81,8 @@ SECTIONS . = ALIGN(4); } > m_ivt - /* The startup code goes first into internal RAM */ + /* ISR Vector table in flash. Copied to ITCM by ResetHandler(). */ + .interrupts : { __Vectors = .; @@ -90,10 +92,9 @@ SECTIONS . = ALIGN(4); } > m_interrupts - __VECTOR_RAM = __Vectors; - __RAM_VECTOR_TABLE_SIZE_BYTES = 0x0; + __Vectors_RAM = ORIGIN(m_isr); - /* The program code and other data goes into internal RAM */ + /* Some program code and other data goes into internal RAM */ .text : { . = ALIGN(4); diff --git a/ports/mimxrt/hal/resethandler_MIMXRT10xx.S b/ports/mimxrt/hal/resethandler_MIMXRT10xx.S index fe933c6d6002b..2fb8772850b6a 100644 --- a/ports/mimxrt/hal/resethandler_MIMXRT10xx.S +++ b/ports/mimxrt/hal/resethandler_MIMXRT10xx.S @@ -64,6 +64,19 @@ Reset_Handler: * __ram_function_start__/__ram_function_end__ : ramfunction region * copied to. Both must be aligned to 4 bytes boundary. */ +/* Copy the ISR Vector table to the start of ITCM to be available when the + .uf2 bootloader is used */ + + ldr r1, = __Vectors + ldr r2, = __Vectors_RAM + movs r3, 1024 + +.LC_ISR: + subs r3, #4 + ldr r0, [r1, r3] + str r0, [r2, r3] + bgt .LC_ISR + ldr r1, =__etext ldr r2, =__data_start__ ldr r3, =__data_end__ diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index 1212914d3b452..204699bcc4263 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -76,6 +76,11 @@ typedef enum { MP_SOFT_RESET } reset_reason_t; +// Copied from inc/uf2.h in https://github.com/Microsoft/uf2-samd21 +#define DBL_TAP_REG SNVS->LPGPR[3] +#define DBL_TAP_MAGIC 0xf01669ef // Randomly selected, adjusted to have first and last bit set +#define DBL_TAP_MAGIC_QUICK_BOOT 0xf02669ef + static mp_obj_t mp_machine_unique_id(void) { unsigned char id[8]; mp_hal_get_unique_id(id); @@ -158,12 +163,17 @@ NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { #if defined(MICROPY_BOARD_ENTER_BOOTLOADER) // If a board has a custom bootloader, call it first. MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); - #elif FSL_ROM_HAS_RUNBOOTLOADER_API + #elif FSL_ROM_HAS_RUNBOOTLOADER_API && !MICROPY_MACHINE_UF2_BOOTLOADER // If not, enter ROM bootloader in serial downloader / USB mode. + // Skip that in case of the UF2 bootloader being available. uint32_t arg = 0xEB110000; ROM_RunBootloader(&arg); #else - // No custom bootloader, or run bootloader API, then just reset. + // No custom bootloader, or run bootloader API, the set + // the flag for the UF2 bootloader + // Pretend to be the first of the two reset presses needed to enter the + // bootloader. That way one reset will end in the bootloader. + DBL_TAP_REG = DBL_TAP_MAGIC; WDOG_TriggerSystemSoftwareReset(WDOG1); #endif while (1) { diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 923eff4dc7064..b45f8408e6415 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -115,6 +115,7 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_MACHINE_UART_SENDBREAK (1) #define MICROPY_PY_MACHINE_UART_IRQ (1) #define MICROPY_PY_ONEWIRE (1) +#define MICROPY_PY_MACHINE_BOOTLOADER (1) // fatfs configuration used in ffconf.h #define MICROPY_FATFS_ENABLE_LFN (2) @@ -151,6 +152,7 @@ uint32_t trng_random_u32(void); #define MICROPY_HW_ENABLE_USBDEV (1) #define MICROPY_HW_USB_CDC (1) +#define MICROPY_HW_USB_CDC_1200BPS_TOUCH (1) // Hooks to add builtins diff --git a/tools/autobuild/build-boards.sh b/tools/autobuild/build-boards.sh index 801e7062e5798..bd6828cb40f50 100755 --- a/tools/autobuild/build-boards.sh +++ b/tools/autobuild/build-boards.sh @@ -99,7 +99,7 @@ function build_esp8266_boards { } function build_mimxrt_boards { - build_boards modmimxrt.c $1 $2 bin hex + build_boards modmimxrt.c $1 $2 bin hex uf2 } function build_nrf_boards { From b251aec0fcfef75b43b69c329b9a34c3703a6b90 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 5 Oct 2024 12:57:47 +0200 Subject: [PATCH 276/546] mimxrt/hal: Update the LUT and re-enable PAGEPROGRAM_QUAD. Changes: - Change the LUT table ordering to be similar to the order of the UF2-Bootloader and fsl_romapi.h. - Rewrite the LUT entry for PAGEPROGRAM_QUAD and update the LUT. That enabled QUAD program again. Signed-off-by: robert-hh --- ports/mimxrt/flash.c | 6 +-- ports/mimxrt/hal/flexspi_flash_config.h | 24 ++++++---- ports/mimxrt/hal/flexspi_hyper_flash.c | 3 ++ ports/mimxrt/hal/flexspi_nor_flash.c | 40 ++++++++-------- ports/mimxrt/hal/qspi_nor_flash_config.c | 60 +++++++++++------------- 5 files changed, 67 insertions(+), 66 deletions(-) diff --git a/ports/mimxrt/flash.c b/ports/mimxrt/flash.c index 3a18f8f51b930..7df799884e9e7 100644 --- a/ports/mimxrt/flash.c +++ b/ports/mimxrt/flash.c @@ -28,10 +28,8 @@ void flash_init(void) { // Upload the custom flash configuration - // This should be performed by the boot ROM but for some reason it is not. - FLEXSPI_UpdateLUT(BOARD_FLEX_SPI, 0, - qspiflash_config.memConfig.lookupTable, - ARRAY_SIZE(qspiflash_config.memConfig.lookupTable)); + // And fix the entry for PAGEPROGRAM_QUAD + flexspi_nor_update_lut(); // Configure FLEXSPI IP FIFO access. BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ARDFEN_MASK); diff --git a/ports/mimxrt/hal/flexspi_flash_config.h b/ports/mimxrt/hal/flexspi_flash_config.h index 5320231a1495c..7538537aeae5b 100644 --- a/ports/mimxrt/hal/flexspi_flash_config.h +++ b/ports/mimxrt/hal/flexspi_flash_config.h @@ -75,6 +75,12 @@ (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) +#define EMPTY_LUT \ + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), \ + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), \ + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), \ + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), \ + // !@brief Definitions for FlexSPI Serial Clock Frequency typedef enum _FlexSpiSerialClockFreq { @@ -209,20 +215,20 @@ typedef struct _FlexSPIConfig } flexspi_mem_config_t; /* */ -#define NOR_CMD_LUT_SEQ_IDX_READ_NORMAL 0 +#define NOR_CMD_LUT_SEQ_IDX_READ 0 #define NOR_CMD_LUT_SEQ_IDX_READSTATUSREG 1 -#define NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD 2 +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI 2 #define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE 3 -#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI 4 +#define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG 4 #define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5 -#define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG 6 -#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD 7 -#define NOR_CMD_LUT_SEQ_IDX_READID 8 +#define NOR_CMD_LUT_SEQ_IDX_READQUAD 6 +#define NOR_CMD_LUT_SEQ_IDX_READID 7 +#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 8 #define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM 9 -#define NOR_CMD_LUT_SEQ_IDX_ENTERQPI 10 +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD 10 #define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 -#define NOR_CMD_LUT_SEQ_IDX_EXITQPI 12 -#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 13 +// Index 12 is left empty +#define NOR_CMD_LUT_SEQ_IDX_READ_SFDP 13 #define HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA 0 #define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA 1 diff --git a/ports/mimxrt/hal/flexspi_hyper_flash.c b/ports/mimxrt/hal/flexspi_hyper_flash.c index 5e5d87166d850..c0cd63ca0be8e 100644 --- a/ports/mimxrt/hal/flexspi_hyper_flash.c +++ b/ports/mimxrt/hal/flexspi_hyper_flash.c @@ -9,6 +9,9 @@ #include "fsl_clock.h" #include "flexspi_hyper_flash.h" +void flexspi_nor_update_lut(void) { +} + // Copy of a few (pseudo-)functions from fsl_clock.h, which were nor reliably // inlined when they should be. That caused DEBUG mode to fail. // It does not increase the code size, since they were supposed to be inline. diff --git a/ports/mimxrt/hal/flexspi_nor_flash.c b/ports/mimxrt/hal/flexspi_nor_flash.c index 956fb657db6dd..a890533a69661 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.c +++ b/ports/mimxrt/hal/flexspi_nor_flash.c @@ -36,6 +36,23 @@ #include #include "fsl_common.h" #include "flexspi_nor_flash.h" +#include "flexspi_flash_config.h" + +uint32_t LUT_pageprogram_quad[4] = { + // 10 Page Program - quad mode + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x32, RADDR_SDR, FLEXSPI_1PAD, 24), + FLEXSPI_LUT_SEQ(WRITE_SDR, FLEXSPI_4PAD, 0x04, STOP, FLEXSPI_1PAD, 0), + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler +}; + +void flexspi_nor_update_lut(void) { + uint32_t lookuptable_copy[64]; + memcpy(lookuptable_copy, (const uint32_t *)&qspiflash_config.memConfig.lookupTable, 64 * sizeof(uint32_t)); + // write PAGEPROGRAM_QUAD code to entry 10 + memcpy(&lookuptable_copy[10 * 4], LUT_pageprogram_quad, 4 * sizeof(uint32_t)); + FLEXSPI_UpdateLUT(BOARD_FLEX_SPI, 0, lookuptable_copy, 64); +} void flexspi_nor_reset(FLEXSPI_Type *base) __attribute__((section(".ram_functions"))); void flexspi_nor_reset(FLEXSPI_Type *base) { @@ -106,9 +123,9 @@ status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base) __attribute__((section status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base) { flexspi_transfer_t flashXfer; status_t status; - uint32_t writeValue = 0x40; + uint32_t writeValue = qspiflash_config.memConfig.deviceModeArg; - /* Write neable */ + /* Write enable */ status = flexspi_nor_write_enable(base, 0); if (status != kStatus_Success) { @@ -228,22 +245,3 @@ status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, co return status; } - -status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId) __attribute__((section(".ram_functions"))); -status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId) { - uint32_t temp; - flexspi_transfer_t flashXfer; - flashXfer.deviceAddress = 0; - flashXfer.port = kFLEXSPI_PortA1; - flashXfer.cmdType = kFLEXSPI_Read; - flashXfer.SeqNumber = 1; - flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READID; - flashXfer.data = &temp; - flashXfer.dataSize = 2; - - status_t status = FLEXSPI_TransferBlocking(base, &flashXfer); - - *vendorId = temp; - - return status; -} diff --git a/ports/mimxrt/hal/qspi_nor_flash_config.c b/ports/mimxrt/hal/qspi_nor_flash_config.c index a8040737e51b0..9edf44fc64c84 100644 --- a/ports/mimxrt/hal/qspi_nor_flash_config.c +++ b/ports/mimxrt/hal/qspi_nor_flash_config.c @@ -49,76 +49,75 @@ const flexspi_nor_config_t qspiflash_config = { .seqNum = 1u, }, .deviceModeArg = 0x40, - // Enable DDR mode, Wordaddassable, Safe configuration, Differential clock .deviceType = kFlexSpiDeviceType_SerialNOR, .sflashPadType = kSerialFlash_4Pads, .serialClkFreq = MICROPY_HW_FLASH_CLK, .sflashA1Size = MICROPY_HW_FLASH_SIZE, .lookupTable = { - // 0 Read LUTs 0 -> 0 - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), + // 0 Read LUTs 0 + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 24), FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04), FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 1 Read status register -> 1 + // 1 Read status register FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x05, READ_SDR, FLEXSPI_1PAD, 0x01), FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 2 Fast read quad mode - SDR - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x6B, RADDR_SDR, FLEXSPI_1PAD, 0x18), - FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x08, READ_SDR, FLEXSPI_4PAD, 0x04), + // 2 Read extend parameters + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x81, READ_SDR, FLEXSPI_1PAD, 0x04), + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 3 Write Enable -> 3 + // 3 Write Enable FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x06, STOP, FLEXSPI_1PAD, 0), FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 4 Read extend parameters - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x81, READ_SDR, FLEXSPI_1PAD, 0x04), + // 4 Write Status Reg + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x01, WRITE_SDR, FLEXSPI_1PAD, 0x01), FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 5 Erase Sector -> 5 + // 5 Erase Sector FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x20, RADDR_SDR, FLEXSPI_1PAD, 24), FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 6 Write Status Reg - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x01, WRITE_SDR, FLEXSPI_1PAD, 0x04), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + // 6 Fast read quad mode - SDR + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x6B, RADDR_SDR, FLEXSPI_1PAD, 24), + FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x08, READ_SDR, FLEXSPI_4PAD, 0x04), FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 7 Page Program - quad mode (-> 9) - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x32, RADDR_SDR, FLEXSPI_1PAD, 0x18), - FLEXSPI_LUT_SEQ(WRITE_SDR, FLEXSPI_4PAD, 0x04, STOP, FLEXSPI_1PAD, 0), + // 7 Read ID + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x90, DUMMY_SDR, FLEXSPI_1PAD, 24), + FLEXSPI_LUT_SEQ(READ_SDR, FLEXSPI_1PAD, 0x00, 0, 0, 0), FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 8 Read ID - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x90, DUMMY_SDR, FLEXSPI_1PAD, 24), - FLEXSPI_LUT_SEQ(READ_SDR, FLEXSPI_1PAD, 0x00, 0, 0, 0), + // 8 Erase Block (32k) + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x52, RADDR_SDR, FLEXSPI_1PAD, 24), + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 9 Page Program - single mode -> 9 + // 9 Page Program - single mode FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x02, RADDR_SDR, FLEXSPI_1PAD, 24), FLEXSPI_LUT_SEQ(WRITE_SDR, FLEXSPI_1PAD, 0, 0, 0, 0), FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 10 Enter QPI mode - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x35, STOP, FLEXSPI_1PAD, 0), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + // 10 Page Program - quad mode + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x32, RADDR_SDR, FLEXSPI_1PAD, 24), + FLEXSPI_LUT_SEQ(WRITE_SDR, FLEXSPI_4PAD, 0x04, STOP, FLEXSPI_1PAD, 0), FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler @@ -128,15 +127,12 @@ const flexspi_nor_config_t qspiflash_config = { FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 12 Exit QPI mode - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_4PAD, 0xF5, STOP, FLEXSPI_1PAD, 0), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + // 12 Empty LUT + EMPTY_LUT - // 13 Erase Block (32k) -> 13 - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x52, RADDR_SDR, FLEXSPI_1PAD, 24), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + // 13 READ SDFP + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x5A, RADDR_SDR, FLEXSPI_1PAD, 24), + FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_1PAD, 8, READ_SDR, FLEXSPI_1PAD, 0x04), FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler }, From 2a80d5c68b5e03570784d18985d5388995ba0e40 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 26 Jan 2025 21:39:58 +0100 Subject: [PATCH 277/546] mimxrt/flash: Swap the order of disabling IRQ and disabling the cache. This change stopped problems with USB IRQ happening during flash writes. Signed-off-by: robert-hh --- ports/mimxrt/flash.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/ports/mimxrt/flash.c b/ports/mimxrt/flash.c index 7df799884e9e7..cdcdc7483d833 100644 --- a/ports/mimxrt/flash.c +++ b/ports/mimxrt/flash.c @@ -43,14 +43,13 @@ void flash_init(void) { __attribute__((section(".ram_functions"))) status_t flash_erase_block(uint32_t erase_addr) { status_t status = kStatus_Fail; - SCB_CleanInvalidateDCache(); - SCB_DisableDCache(); __disable_irq(); + SCB_DisableDCache(); status = flexspi_nor_flash_erase_block(BOARD_FLEX_SPI, erase_addr); - __enable_irq(); SCB_EnableDCache(); + __enable_irq(); return status; } @@ -60,14 +59,13 @@ __attribute__((section(".ram_functions"))) status_t flash_erase_block(uint32_t e __attribute__((section(".ram_functions"))) status_t flash_erase_sector(uint32_t erase_addr) { status_t status = kStatus_Fail; - SCB_CleanInvalidateDCache(); - SCB_DisableDCache(); __disable_irq(); + SCB_DisableDCache(); status = flexspi_nor_flash_erase_sector(BOARD_FLEX_SPI, erase_addr); - __enable_irq(); SCB_EnableDCache(); + __enable_irq(); return status; } @@ -83,10 +81,6 @@ __attribute__((section(".ram_functions"))) status_t flash_write_block(uint32_t d if (length == 0) { status = kStatus_Success; // Nothing to do } else { - - SCB_CleanInvalidateDCache(); - SCB_DisableDCache(); - // write data in chunks not crossing a page boundary do { next_addr = dest_addr - (dest_addr % PAGE_SIZE_BYTES) + PAGE_SIZE_BYTES; // next page boundary @@ -96,7 +90,11 @@ __attribute__((section(".ram_functions"))) status_t flash_write_block(uint32_t d } __disable_irq(); + SCB_DisableDCache(); + status = flexspi_nor_flash_page_program(BOARD_FLEX_SPI, dest_addr, (uint32_t *)src, write_length); + + SCB_EnableDCache(); __enable_irq(); // Update remaining data length @@ -106,9 +104,6 @@ __attribute__((section(".ram_functions"))) status_t flash_write_block(uint32_t d src += write_length; dest_addr += write_length; } while ((length > 0) && (status == kStatus_Success)); - - SCB_EnableDCache(); - } return status; } From 9eb9451d8377c2adc5187cfb51ef68de843b680f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 7 Oct 2024 08:45:54 +0200 Subject: [PATCH 278/546] mimxrt/boards: Update the deploy instructions for the UF2 bootloader. Signed-off-by: robert-hh --- .../ADAFRUIT_METRO_M7/deploy_metro_m7.md | 148 ++---------------- .../boards/OLIMEX_RT1010/deploy_olimex.md | 42 ++--- ports/mimxrt/boards/SEEED_ARCH_MIX/deploy.md | 58 ++++++- ports/mimxrt/boards/deploy_mimxrt.md | 44 +++++- 4 files changed, 123 insertions(+), 169 deletions(-) diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/deploy_metro_m7.md b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/deploy_metro_m7.md index bc92016b71751..c5bfd1a28a79f 100644 --- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/deploy_metro_m7.md +++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/deploy_metro_m7.md @@ -1,141 +1,11 @@ ## 1. Deploy the MicroPython firmware to the Metro M7 board. -### 1.1 Deploy the firmware using the serial bootloader. - -For initial deployment of the firmware a few preparation steps are required, which -have to be done once. - -1. Get the files ufconv.py and uf2families.json from the micropython/tools directory, -e.g. at https://github.com/micropython/micropython/tree/master/tools. - -2. Get the NXP program sdphost for your operating system, e.g. from -https://github.com/adafruit/tinyuf2/tree/master/ports/mimxrt10xx/sdphost. -You can also get them from the NXP web sites. - -3. Get the UF2 boot-loader package https://github.com/adafruit/tinyuf2/releases/download/0.9.0/tinyuf2-imxrt1010_evk-0.9.0.zip -and extract the file tinyuf2-imxrt1010_evk-0.9.0.bin. - -Now you have all files at hand that you will need for updating. - -1. Get the firmware you want to upload from the MicroPython download page. - -2. Set the two BOOTSEL DIP switches to the 1/0 position, which is the opposite position of the normal use mode. - -3. Push the reset button. - -4. Run the commands: - -``` -sudo ./sdphost -u 0x1fc9,0x0145 -- write-file 0x20206400 tinyuf2-imxrt1010_evk-0.9.0.bin -sudo ./sdphost -u 0x1fc9,0x0145 -- jump-address 0x20207000 -``` -Wait until a drive icon appears on the computer (or mount it explicitly), and then run: -``` -python3 uf2conv.py --base 0x60000400 -f 0x4fb2d5bd -``` -You can put all of that in a script. Just add a short wait before the 3rd command to let the drive connect. - -5. Once the upload is finished, set the BOOTSEL DIP switches back to the 0/1 position and push reset. - -Using sudo is Linux specific. You may not need it at all, if the access rights are set properly, -and you will not need it for Windows. - -### 1.2 Deploy the firmware using a JTAG adapter. - -With a JTAG adapter the firmware can be easily installed. Appropriate tools are Segger JFlash Lite and -the Segger Edu Mini adapter. Just use the firmware.hex file for loading, which will be loaded at the -proper address. - - -## 2. Deploy the WiFi firmware. - -The NINA firmware in the NINA module has to be updated for use with MicroPython. That can be done -using MicroPython and two small Python scripts. - -The firmware binaries are available at -https://github.com/micropython/micropython-lib/tree/master/micropython/espflash -or https://github.com/robert-hh/Shared-Stuff. For the Metro M7 board, the -NINA_FW_v1.5.0_Airlift.bin file is needed. - -For firmware upload, the following connections to the WiFi module are required: - -- Pin Reset (as above) -- Pin GPIO0 -- UART RX -- UART TX - -The GPIO pins and UART device id varies between boards. At the Adafruit Metro M7 board, -the UART is UART(1), and the Pin names for reset and GPIO0 are ESP_RESET and ESP_GPIO0. -The firmware can be uploaded, using the espflash.py module, a short script -using espflash.py and mpremote. espflash.py is available at -https://github.com/micropython/micropython-lib/tree/master/micropython/espflash. -This place also holds the example script. - -``` -import espflash -from machine import Pin -from machine import UART -import sys -sys.path.append("/flash") - -reset = Pin("ESP_RESET", Pin.OUT) -gpio0 = Pin("ESP_GPIO0", Pin.OUT) -uart = UART(0, 115200, timeout=350) - -md5sum = b"b0b9ab23da820a469e597c41364acb3a" -path = "/remote/NINA_FW_v1.5.0_Airlift.bin" - -esp = espflash.ESPFlash(reset, gpio0, uart) -# Enter bootloader download mode, at 115200 -esp.bootloader() -# Can now change to higher/lower baud rate -esp.set_baudrate(921600) -# Must call this first before any flash functions. -esp.flash_attach() -# Read flash size -size = esp.flash_read_size() -# Configure flash parameters. -esp.flash_config(size) -# Write firmware image from internal storage. -esp.flash_write_file(path) -# Compares file and flash MD5 checksum. -esp.flash_verify_file(path, md5sum) -# Resets the ESP32 chip. -esp.reboot() -``` - -The script shows the set-up for the Metro M7 board. -The md5sum is the one of the WiFi firmware. It may change and -can be recalculated using e.g. the Linux `md5sum` command. It is used to -verify the firmware upload. To upload the firmware, place the firmware -and the above script (let's call it ninaflash.py) into the same directory -on your PC, and run the command: -``` -mpremote connect mount . run ninaflash.py -``` -After a while, the upload will start. A typical start sequence looks like: -``` -Local directory . is mounted at /remote -Failed to read response to command 8. -Failed to read response to command 8. -Changing baudrate => 921600 -Flash attached -Flash size 2.0 MBytes -Flash write size: 1310720 total_blocks: 320 block size: 4096 -Writing sequence number 0/320... -Writing sequence number 1/320... -Writing sequence number 2/320... -Writing sequence number 3/320... -Writing sequence number 4/320... -.... -.... -Writing sequence number 317/320... -Writing sequence number 318/320... -Writing sequence number 319/320... -Flash write finished -Flash verify: File MD5 b'b0b9ab23da820a469e597c41364acb3a' -Flash verify: Flash MD5 b'b0b9ab23da820a469e597c41364acb3a' -Firmware verified. -``` -The initial messages `Failed to read response to command 8.` -can be ignored. +The Metro M7 board comes pre-installed with a UF2 bootloader. It can +be started by pushing reset twice. Then the bootloader drive will +appear. If that does not happen or the bootloader was lost, you can +reinstall the bootloader using the instructions by Adafruit +here: https://learn.adafruit.com/adafruit-metro-m7-microsd/installing-the-bootloader + +Once the bootloader is installed and started, you can install MicroPython +by copying the .uf2 version of the firmware file to the bootloader +drive. When the firmware is installed, the drive will disappear. diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/deploy_olimex.md b/ports/mimxrt/boards/OLIMEX_RT1010/deploy_olimex.md index 92f3df57c9b84..7abba784b940b 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/deploy_olimex.md +++ b/ports/mimxrt/boards/OLIMEX_RT1010/deploy_olimex.md @@ -1,39 +1,43 @@ -For initial deployment of the firmware a few preparation steps are required, which -have to be done once. +The Olimex RT1011 board is delivered without firmware. The best option to +install MicroPython is installing a UF2 bootstrap loader first, which then can be +used to install and update MicroPython. The bootloader has to be installed +only once. -1. Get the files ufconv.py and uf2families.json from the micropython/tools directory, -e.g. at https://github.com/micropython/micropython/tree/master/tools. +For initial deployment of the bootloader a few preparation steps are required, which +have to be done once. -2. Get the NXP program sdphost for your operating system, e.g. from +1. Get the NXP program sdphost for your operating system, e.g. from https://github.com/adafruit/tinyuf2/tree/master/ports/mimxrt10xx/sdphost. You can also get them from the NXP web sites. -3. Get the UF2 boot-loader package https://github.com/adafruit/tinyuf2/releases/download/0.9.0/tinyuf2-imxrt1010_evk-0.9.0.zip -and extract the file tinyuf2-imxrt1010_evk-0.9.0.bin. +2. Get the UF2 boot-loader package https://github.com/adafruit/tinyuf2/releases/tag/0.21.0/tinyuf2-imxrt1010_evk-0.20.1.zip and extract the files `tinyuf2-imxrt1010_evk-0.21.0.bin` +. You may as well go for a newer release. Now you have all files at hand that you will need for updating. -1. Get the firmware you want to upload from the MicroPython download page. +1. Get the firmware file you want to upload with the .uf2 extension from the MicroPython download page. 2. Push and hold the "Boot" button, then press "Reset", and release both buttons. 3. Run the commands: ``` -sudo ./sdphost -u 0x1fc9,0x0145 -- write-file 0x20206400 tinyuf2-imxrt1010_evk-0.9.0.bin +sudo ./sdphost -u 0x1fc9,0x0145 -- write-file 0x20206400 tinyuf2-imxrt1010_evk-0.21.0.bin sudo ./sdphost -u 0x1fc9,0x0145 -- jump-address 0x20207000 ``` -Wait until a drive icon appears on the computer (or mount it explicitly), and then run: -``` -python3 uf2conv.py --base 0x60000400 -f 0x4fb2d5bd -``` -You can put all of that in a script. Just add a short wait before the 3rd command to let the drive connect. - -4. Once the upload is finished, push Reset again. +Wait until a drive icon appears on the computer (or mount it explicitly). Then the UF2 bootloader +is permanently installed. Using sudo is Linux specific. You may not need it at all, if the access rights are set properly, and you will not need it for Windows. -Once the generic boot-loader is available, this procedure is only required for the first -firmware load or in case the flash is corrupted and the existing firmware is not functioning -any more. +4. Once the upload of the bootloader is finished, push Reset twice. + +The bootloader should start and show a drive icon. Copy the .uf2 version of MicroPython +to this drive to install or update MicroPython. + +Once the UF2 bootloader is installed, only step 4 is required to deploy MicroPython. If +MicroPython is already installed, the bootloader can as well be invoked by calling +`machine.bootloader()`. + +If at any time the flash content is corrupted you can always start over from the beginning. diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/deploy.md b/ports/mimxrt/boards/SEEED_ARCH_MIX/deploy.md index e91c48d46cafd..9178c717e36e6 100644 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/deploy.md +++ b/ports/mimxrt/boards/SEEED_ARCH_MIX/deploy.md @@ -1,6 +1,52 @@ -Firmware upload to the Seed ARCH MIX board can be done using the J-Link interface -For that, follow the instructions given by Seed in their Wiki at -https://wiki.seeedstudio.com/Arch_Mix/#flashing-arduino-bootloader-to-arch-mix. -You will need a J-Link debug probe and software. What has been tested was the -Segger JLlink edu or Segger JLink edu mini. As matching loader tool you can use -Segger JFlashLite. The target address for loading is 0x60000000. +The Seeed Arch Mix board is delivered without firmware. The best option to +install MicroPython is installing a UF2 bootstrap loader first, which then can be +used to install and update MicroPython. The bootloader has to be installed +only once. + +For initial deployment of the bootloader a few preparation steps are required, which +have to be done once. + +1. Get the NXP program sdphost for your operating system, e.g. from +https://github.com/adafruit/tinyuf2/tree/master/ports/mimxrt10xx/sdphost. +You can also get them from the NXP web sites. + +2. Get the UF2 boot-loader package https://github.com/adafruit/tinyuf2/releases/tag/0.20.1/tinyuf2-imxrt1050_evkb-0.21.0.zip and extract the files `tinyuf2-imxrt1050_evkb-0.21.0.bin`. +You may as well go for a newer release. + +Now you have all files at hand that you will need for updating. + +1. Get the firmware file you want to upload with the .uf2 extension from the MicroPython download page. + +2. At the Seeed Arch Mix board, connect the RX pin (J3-19) with 3.3V, and change the DIP switches +3 an 4 at SW1 from 1-0 to 0-1. + +3. Push Reset. + +4. Run the commands: + +``` +sudo ./sdphost -u 0x1fc9,0x0130 -- write-file 0x1000 tinyuf2-imxrt1050_evkb-0.21.0.bin +sudo ./sdphost -u 0x1fc9,0x0130 -- jump-address 0x2000 +``` +Wait until a drive icon appears on the computer (or mount it explicitly). When using the above +mentioned bootloader, it has the label `RT1050BOOT`. Then the UF2 bootloader +is permanently installed. + +Using sudo is Linux specific. You may not need it at all, if the access rights are set properly, +and you will not need it for Windows. + +5. At the Seeed Arch Mix board, disconnect the RX pin (J3-19) with 3.3V, and change the DIP switches +3 an 4 at SW1 back to 1-0. + +6. Once the upload of the bootloader is finished or when it is already installed, push Reset twice. + +The bootloader should start and show a drive icon. Do not push too fast. The i.MX RT MCU +have no dedicated Reset Pin and are reset through power cycling, which may be slow. +Copy the .uf2 version of MicroPython to this drive to install or update MicroPython. +If after steps 1-4 the bootloader drive is already shown, you do not have to reset again. + +Once the UF2 bootloader is installed, only step 6 is required to deploy MicroPython. If +MicroPython is already installed, the bootloader can as well be invoked by calling +`machine.bootloader()` or switching the USB baud rate at the PC to 1200 baud and back. + +If at any time the flash content is corrupted you can always start over from the beginning. diff --git a/ports/mimxrt/boards/deploy_mimxrt.md b/ports/mimxrt/boards/deploy_mimxrt.md index 35752a8362cef..7ad21fe64ca17 100644 --- a/ports/mimxrt/boards/deploy_mimxrt.md +++ b/ports/mimxrt/boards/deploy_mimxrt.md @@ -1,11 +1,45 @@ -Firmware can be loaded to the MIMXRT development boards in various ways. The most convenient -one is using the built-in support MCU. When a PC is connected to the debug USB port, a drive -icon will appear. Firmware can be uploaded to the board by copying it to this drive. The copy -and flashing will take a few moments. At the end of the upload, the drive icon will disappear -and reappear again. Then the reset button has to be pushed, which starts the MicroPython firmware. +## Firmware installation options + +There are two ways to load the MicroPython firmware to the device: + +1. Load the MicroPython firmware directly to the device. The MicroPython +firmware files for that method have the extension .bin or .hex and are available +at the MicroPython download site. +2. Install a resident UF2 bootstrap loader to the device first and use that later for loading +MicroPython. The MicroPython firmware files for that method have the extension .uf2 +and are available at the MicroPython download site. The UF2 bootstrap loader can be obtained +from the site https://github.com/adafruit/tinyuf2. Open the recent release page and +get the version of the bootloader for your board. If there is no specific bootloader +for a specific board, get versions for the respective imxrt10xx-evk board. The file +with the .bin or .hex extension is the one to be installed first. + +## Direct loading of MicroPython or installation of the UF2 bootloader + +The MicroPython firmware or the UF2 bootstrap loader can be loaded to the MIMXRT development +boards in various ways. The most convenient one is using the built-in support MCU. When a PC +is connected to the debug USB port, a drive icon will appear. Firmware can be uploaded to +the board by copying it to this drive. The copy and flashing will take a few moments. +At the end of the upload, the drive icon will disappear and reappear again. Then the reset +button has to be pushed, which starts the MicroPython firmware. Depending on the power jumper settings, both the debug USB and OTG USB port have to be powered during firmware upload. You may as well load the firmware using the JLink port or openSDA interface with the appropriate tools. For more options, consult the user guide of the board. + +## Installing the MicroPython firmware using the UF2 bootloader + +When using the UF2 bootloader, the OTG USB port will be used. +Once the UF2 bootloader is installed, it has to be started to upload MicroPython.The +methods to start the bootloader are: + +- Push reset twice. +- Call machine.bootloader() e.g. from REPL. +- Switch the USB port shortly to 1200 baud and back. That requires MicroPython to be +installed. + +If there is no valid Firmware on the device, the bootloader will start automatically. +Once it's started, a drive ICON will appear. The MicroPython firmware file with .uf2 +extension must then be copied to that drive. When the file is copied and MicroPython +is installed, the drive disappears and MicroPython starts. From d40849d07d2523fa16070bfcae34b51b7b14942b Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 3 Feb 2025 21:12:35 +0100 Subject: [PATCH 279/546] mimxrt/boards: Add flash configuration constants to mpconfigboard.mk. And use these to initialize the LUT table properly for the various flash types. The different flash types differ by 3 parameters. Thus it is easier to just keep one copy of the qspiflash_config structure with the LUT table and update it during flash initialisation as needed. Signed-off-by: robert-hh --- ports/mimxrt/Makefile | 6 ++++ .../boards/ADAFRUIT_METRO_M7/mpconfigboard.mk | 3 ++ .../boards/MIMXRT1010_EVK/mpconfigboard.mk | 3 ++ .../boards/MIMXRT1015_EVK/mpconfigboard.mk | 3 ++ .../boards/MIMXRT1020_EVK/mpconfigboard.mk | 3 ++ .../boards/MIMXRT1050_EVK/mpconfigboard.mk | 3 ++ .../boards/MIMXRT1060_EVK/mpconfigboard.mk | 3 ++ .../boards/MIMXRT1064_EVK/mpconfigboard.mk | 3 ++ .../boards/MIMXRT1170_EVK/mpconfigboard.mk | 3 ++ .../boards/OLIMEX_RT1010/mpconfigboard.mk | 3 ++ .../boards/SEEED_ARCH_MIX/mpconfigboard.mk | 3 ++ ports/mimxrt/boards/TEENSY40/mpconfigboard.mk | 3 ++ ports/mimxrt/boards/TEENSY41/mpconfigboard.mk | 3 ++ ports/mimxrt/hal/flexspi_flash_config.h | 2 +- ports/mimxrt/hal/flexspi_nor_flash.c | 16 +++++++-- ports/mimxrt/hal/flexspi_nor_flash.h | 1 + ports/mimxrt/hal/qspi_nor_flash_config.c | 34 ++++++++----------- 17 files changed, 72 insertions(+), 23 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index ad7d853b677a5..224b38384f718 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -425,6 +425,12 @@ endif ifdef MICROPY_HW_FLASH_CLK CFLAGS += -DMICROPY_HW_FLASH_CLK=$(MICROPY_HW_FLASH_CLK) endif +ifdef MICROPY_HW_FLASH_QE_CMD + CFLAGS += -DMICROPY_HW_FLASH_QE_CMD=$(MICROPY_HW_FLASH_QE_CMD) +endif +ifdef MICROPY_HW_FLASH_QE_ARG + CFLAGS += -DMICROPY_HW_FLASH_QE_ARG=$(MICROPY_HW_FLASH_QE_ARG) +endif CFLAGS += $(CFLAGS_EXTRA) diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk index 74d3a6f0f10e3..b9c1716a7f60c 100644 --- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk +++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk @@ -4,6 +4,9 @@ MCU_VARIANT = MIMXRT1011DAE5A MICROPY_FLOAT_IMPL = single MICROPY_HW_FLASH_TYPE ?= qspi_nor_flash MICROPY_HW_FLASH_SIZE ?= 0x800000 # 8MB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_133MHz +MICROPY_HW_FLASH_QE_CMD = 0x31 +MICROPY_HW_FLASH_QE_ARG = 0x02 MICROPY_PY_NETWORK_NINAW10 ?= 1 MICROPY_PY_SSL ?= 1 diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk index 1aef451e2a6a5..592b89c68ecf7 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk @@ -4,6 +4,9 @@ MCU_VARIANT = MIMXRT1011DAE5A MICROPY_FLOAT_IMPL = single MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x1000000 # 16MB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_100MHz +MICROPY_HW_FLASH_QE_CMD = 0x31 +MICROPY_HW_FLASH_QE_ARG = 0x02 USE_UF2_BOOTLOADER = 1 diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk index 0f719ac0a9753..c5d02294bc3df 100644 --- a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk @@ -4,6 +4,9 @@ MCU_VARIANT = MIMXRT1015DAF5A MICROPY_FLOAT_IMPL = single MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x1000000 # 16MB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_100MHz +MICROPY_HW_FLASH_QE_CMD = 0x31 +MICROPY_HW_FLASH_QE_ARG = 0x02 MICROPY_BOOT_BUFFER_SIZE = (32 * 1024) diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk index 7715e669f8d09..5ef078210a2a2 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk @@ -4,6 +4,9 @@ MCU_VARIANT = MIMXRT1021DAG5A MICROPY_FLOAT_IMPL = double MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_133MHz +MICROPY_HW_FLASH_QE_CMD = 0x01 +MICROPY_HW_FLASH_QE_ARG = 0x40 MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk index ef7bbc8f56cb9..8b048c85eaa98 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk @@ -7,6 +7,9 @@ MICROPY_HW_FLASH_SIZE = 0x4000000 # 64MB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_133MHz +MICROPY_HW_FLASH_QE_CMD = 0x01 +MICROPY_HW_FLASH_QE_ARG = 0x40 MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk index f57aaff3b8b17..54539952fb4f9 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk @@ -4,6 +4,9 @@ MCU_VARIANT = MIMXRT1062DVJ6A MICROPY_FLOAT_IMPL = double MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_133MHz +MICROPY_HW_FLASH_QE_CMD = 0x01 +MICROPY_HW_FLASH_QE_ARG = 0x40 MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk index f175d26377abc..b393d7ed9f2c6 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk @@ -4,6 +4,9 @@ MCU_VARIANT = MIMXRT1064DVL6A MICROPY_FLOAT_IMPL = double MICROPY_HW_FLASH_TYPE = internal MICROPY_HW_FLASH_SIZE = 0x400000 # 4MB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_100MHz +MICROPY_HW_FLASH_QE_CMD = 0x31 +MICROPY_HW_FLASH_QE_ARG = 0x02 MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB diff --git a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk index 6e5ee56aa1081..ef4ab683a215c 100644 --- a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk @@ -6,6 +6,9 @@ MICROPY_FLOAT_IMPL = double MICROPY_HW_FLASH_TYPE ?= qspi_nor_flash MICROPY_HW_FLASH_SIZE ?= 0x1000000 # 16MB MICROPY_HW_FLASH_RESERVED ?= 0x100000 # 1MB CM4 Code address space +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_133MHz +MICROPY_HW_FLASH_QE_CMD = 0x31 +MICROPY_HW_FLASH_QE_ARG = 0x02 MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x4000000 # 64MB diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk index 1de497d74e3f0..7e9987de0cfe3 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk @@ -5,6 +5,9 @@ MICROPY_FLOAT_IMPL = single MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x200000 # 2MB MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_100MHz +MICROPY_HW_FLASH_QE_CMD = 0x01 +MICROPY_HW_FLASH_QE_ARG = 0x40 USE_UF2_BOOTLOADER = 1 diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk index c691e2dcb55c4..7ea107b00df86 100644 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk +++ b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk @@ -4,6 +4,9 @@ MCU_VARIANT = MIMXRT1052DVL6B MICROPY_FLOAT_IMPL = double MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_133MHz +MICROPY_HW_FLASH_QE_CMD = 0x01 +MICROPY_HW_FLASH_QE_ARG = 0x40 MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk index 87c122da9d49d..9393252d1a863 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk @@ -5,6 +5,9 @@ MICROPY_FLOAT_IMPL = double MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x200000 # 2MB MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_133MHz +MICROPY_HW_FLASH_QE_CMD = 0x31 +MICROPY_HW_FLASH_QE_ARG = 0x02 USE_UF2_BOOTLOADER = 1 diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk index e28310d091ad2..6bb9e607f6522 100755 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk @@ -5,6 +5,9 @@ MICROPY_FLOAT_IMPL = double MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_133MHz +MICROPY_HW_FLASH_QE_CMD = 0x31 +MICROPY_HW_FLASH_QE_ARG = 0x02 MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 diff --git a/ports/mimxrt/hal/flexspi_flash_config.h b/ports/mimxrt/hal/flexspi_flash_config.h index 7538537aeae5b..d9aa5ed5eab69 100644 --- a/ports/mimxrt/hal/flexspi_flash_config.h +++ b/ports/mimxrt/hal/flexspi_flash_config.h @@ -75,7 +75,7 @@ (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) -#define EMPTY_LUT \ +#define EMPTY_LUT_SEQ \ FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), \ FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), \ FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), \ diff --git a/ports/mimxrt/hal/flexspi_nor_flash.c b/ports/mimxrt/hal/flexspi_nor_flash.c index a890533a69661..6d5b830383f68 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.c +++ b/ports/mimxrt/hal/flexspi_nor_flash.c @@ -46,11 +46,23 @@ uint32_t LUT_pageprogram_quad[4] = { FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler }; +uint32_t LUT_write_status[4] = { + // 4 Write status word for Quad mode + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, MICROPY_HW_FLASH_QE_CMD, WRITE_SDR, FLEXSPI_1PAD, 0x01), + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler +}; + void flexspi_nor_update_lut(void) { uint32_t lookuptable_copy[64]; memcpy(lookuptable_copy, (const uint32_t *)&qspiflash_config.memConfig.lookupTable, 64 * sizeof(uint32_t)); + // write WRITESTATUSREG code to entry 10 + memcpy(&lookuptable_copy[NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG * 4], + LUT_write_status, 4 * sizeof(uint32_t)); // write PAGEPROGRAM_QUAD code to entry 10 - memcpy(&lookuptable_copy[10 * 4], LUT_pageprogram_quad, 4 * sizeof(uint32_t)); + memcpy(&lookuptable_copy[NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD * 4], + LUT_pageprogram_quad, 4 * sizeof(uint32_t)); FLEXSPI_UpdateLUT(BOARD_FLEX_SPI, 0, lookuptable_copy, 64); } @@ -123,7 +135,7 @@ status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base) __attribute__((section status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base) { flexspi_transfer_t flashXfer; status_t status; - uint32_t writeValue = qspiflash_config.memConfig.deviceModeArg; + uint32_t writeValue = MICROPY_HW_FLASH_QE_ARG; /* Write enable */ status = flexspi_nor_write_enable(base, 0); diff --git a/ports/mimxrt/hal/flexspi_nor_flash.h b/ports/mimxrt/hal/flexspi_nor_flash.h index 6526142af22f1..2e61effc2b7d8 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.h +++ b/ports/mimxrt/hal/flexspi_nor_flash.h @@ -47,6 +47,7 @@ extern flexspi_nor_config_t qspiflash_config; status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId); status_t flexspi_nor_init(void); void flexspi_nor_update_lut(void); +status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base); status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address); status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size); diff --git a/ports/mimxrt/hal/qspi_nor_flash_config.c b/ports/mimxrt/hal/qspi_nor_flash_config.c index 9edf44fc64c84..36ccc30fc1922 100644 --- a/ports/mimxrt/hal/qspi_nor_flash_config.c +++ b/ports/mimxrt/hal/qspi_nor_flash_config.c @@ -48,7 +48,7 @@ const flexspi_nor_config_t qspiflash_config = { .seqId = 4u, .seqNum = 1u, }, - .deviceModeArg = 0x40, + .deviceModeArg = MICROPY_HW_FLASH_QE_ARG, .deviceType = kFlexSpiDeviceType_SerialNOR, .sflashPadType = kSerialFlash_4Pads, .serialClkFreq = MICROPY_HW_FLASH_CLK, @@ -68,10 +68,7 @@ const flexspi_nor_config_t qspiflash_config = { FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler // 2 Read extend parameters - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x81, READ_SDR, FLEXSPI_1PAD, 0x04), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + EMPTY_LUT_SEQ // 3 Write Enable FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x06, STOP, FLEXSPI_1PAD, 0), @@ -80,7 +77,7 @@ const flexspi_nor_config_t qspiflash_config = { FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler // 4 Write Status Reg - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x01, WRITE_SDR, FLEXSPI_1PAD, 0x01), + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, MICROPY_HW_FLASH_QE_CMD, WRITE_SDR, FLEXSPI_1PAD, 0x01), FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler @@ -92,16 +89,10 @@ const flexspi_nor_config_t qspiflash_config = { FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler // 6 Fast read quad mode - SDR - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x6B, RADDR_SDR, FLEXSPI_1PAD, 24), - FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x08, READ_SDR, FLEXSPI_4PAD, 0x04), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + EMPTY_LUT_SEQ // 7 Read ID - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x90, DUMMY_SDR, FLEXSPI_1PAD, 24), - FLEXSPI_LUT_SEQ(READ_SDR, FLEXSPI_1PAD, 0x00, 0, 0, 0), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + EMPTY_LUT_SEQ // 8 Erase Block (32k) FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x52, RADDR_SDR, FLEXSPI_1PAD, 24), @@ -127,14 +118,17 @@ const flexspi_nor_config_t qspiflash_config = { FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - // 12 Empty LUT - EMPTY_LUT + // 12 Not used + EMPTY_LUT_SEQ // 13 READ SDFP - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x5A, RADDR_SDR, FLEXSPI_1PAD, 24), - FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_1PAD, 8, READ_SDR, FLEXSPI_1PAD, 0x04), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + EMPTY_LUT_SEQ + + // 14 Not used + EMPTY_LUT_SEQ + + // 15 Not used + EMPTY_LUT_SEQ }, }, .pageSize = 256u, From 0a433a02e1eab1c95e57129a1e828780c871be42 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 4 Feb 2025 18:17:47 +0100 Subject: [PATCH 280/546] mimxrt/hal: Set the flexspi flash CLK frequency on boot. The flash clock frequency may have been set to a different value by a bootloader. Set the frequency according to the configured value. Use a table of pre-calculated dividers to get the closest value for the flash frequency, achieving for MIMXRT10xx: 30 -> 30.85 MHz 50 -> 49.65 MHz 60 -> 60 MHz 75 -> 75.13 MHz 80 -> 80 MHz 100 -> 99.31 Mhz 133 -> 132.92 MHz 166 -> 166.15 MHz for MIMXRT1176: 30 -> 31 MHz 50 -> 52.8 MJz 60 -> 58.7 MHz 75 -> 75.4 MHz 80 -> 75.4 MHz 100 -> 105.6 MHz 133 -> 132 MHz 166 -> 176 MHz Signed-off-by: robert-hh --- ports/mimxrt/flash.c | 3 +- ports/mimxrt/hal/flexspi_hyper_flash.c | 2 +- ports/mimxrt/hal/flexspi_hyper_flash.h | 2 +- ports/mimxrt/hal/flexspi_nor_flash.c | 93 +++++++++++++++++++++++++- ports/mimxrt/hal/flexspi_nor_flash.h | 3 +- 5 files changed, 95 insertions(+), 8 deletions(-) diff --git a/ports/mimxrt/flash.c b/ports/mimxrt/flash.c index cdcdc7483d833..57091b36ddcfd 100644 --- a/ports/mimxrt/flash.c +++ b/ports/mimxrt/flash.c @@ -29,7 +29,8 @@ void flash_init(void) { // Upload the custom flash configuration // And fix the entry for PAGEPROGRAM_QUAD - flexspi_nor_update_lut(); + // Update the flash CLK + flexspi_nor_update_lut_clk(MICROPY_HW_FLASH_CLK); // Configure FLEXSPI IP FIFO access. BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ARDFEN_MASK); diff --git a/ports/mimxrt/hal/flexspi_hyper_flash.c b/ports/mimxrt/hal/flexspi_hyper_flash.c index c0cd63ca0be8e..9171640a9ed83 100644 --- a/ports/mimxrt/hal/flexspi_hyper_flash.c +++ b/ports/mimxrt/hal/flexspi_hyper_flash.c @@ -9,7 +9,7 @@ #include "fsl_clock.h" #include "flexspi_hyper_flash.h" -void flexspi_nor_update_lut(void) { +void flexspi_nor_update_lut_clk(uint32_t freq_index) { } // Copy of a few (pseudo-)functions from fsl_clock.h, which were nor reliably diff --git a/ports/mimxrt/hal/flexspi_hyper_flash.h b/ports/mimxrt/hal/flexspi_hyper_flash.h index a6454a1c9a234..40416d6e21d7f 100644 --- a/ports/mimxrt/hal/flexspi_hyper_flash.h +++ b/ports/mimxrt/hal/flexspi_hyper_flash.h @@ -47,7 +47,7 @@ extern flexspi_nor_config_t qspiflash_config; status_t flexspi_nor_hyperflash_cfi(FLEXSPI_Type *base); void flexspi_hyper_flash_init(void); -void flexspi_nor_update_lut(void); +void flexspi_nor_update_lut_clk(uint32_t freq_index); status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address); status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size); diff --git a/ports/mimxrt/hal/flexspi_nor_flash.c b/ports/mimxrt/hal/flexspi_nor_flash.c index 6d5b830383f68..7fdb6b7c13df6 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.c +++ b/ports/mimxrt/hal/flexspi_nor_flash.c @@ -38,6 +38,9 @@ #include "flexspi_nor_flash.h" #include "flexspi_flash_config.h" +bool flash_busy_status_pol = 0; +bool flash_busy_status_offset = 0; + uint32_t LUT_pageprogram_quad[4] = { // 10 Page Program - quad mode FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x32, RADDR_SDR, FLEXSPI_1PAD, 24), @@ -54,16 +57,100 @@ uint32_t LUT_write_status[4] = { FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler }; -void flexspi_nor_update_lut(void) { +#if defined(MIMXRT117x_SERIES) +static uint8_t div_table_mhz[] = { + 17, // Entry 0 is out of range + 17, // 30 -> 31 MHz + 10, // 50 -> 52.8 MHz + 9, // 60 -> 58.7 MHz + 7, // 75 -> 75.4 MHz + 7, // 80 -> 75.4 MHz + 5, // 100 -> 105.6 Mhz + 4, // 133 -> 132 MHz + 3 // 166 -> 176 MHz +}; + +#else +typedef struct _ps_div_t { + uint8_t pfd480_div; + uint8_t podf_div; +} ps_div_t; + +static ps_div_t div_table_mhz[] = { + { 35, 8 }, // Entry 0 is out of range + { 35, 8 }, // 30 -> 30.85 MHz + { 29, 6 }, // 50 -> 49.65 MHz + { 18, 8 }, // 60 -> 60 MHz + { 23, 5 }, // 75 -> 75.13 MHz + { 18, 6 }, // 80 -> 80 MHz + { 17, 5 }, // 100 -> 191 Mhz + { 13, 5 }, // 133 -> 132.92 MHz + { 13, 4 } // 166 -> 166.15 MHz +}; +#endif + +__attribute__((section(".ram_functions"))) void flexspi_nor_update_lut_clk(uint32_t freq_index) { + // Create a local copy of the LookupTable. Modify the entry for WRITESTATUSREG + // Add an entry for PAGEPROGRAM_QUAD. uint32_t lookuptable_copy[64]; memcpy(lookuptable_copy, (const uint32_t *)&qspiflash_config.memConfig.lookupTable, 64 * sizeof(uint32_t)); - // write WRITESTATUSREG code to entry 10 + // write local WRITESTATUSREG code to index 4 memcpy(&lookuptable_copy[NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG * 4], LUT_write_status, 4 * sizeof(uint32_t)); - // write PAGEPROGRAM_QUAD code to entry 10 + // write local PAGEPROGRAM_QUAD code to index 10 memcpy(&lookuptable_copy[NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD * 4], LUT_pageprogram_quad, 4 * sizeof(uint32_t)); + // Update the LookupTable. FLEXSPI_UpdateLUT(BOARD_FLEX_SPI, 0, lookuptable_copy, 64); + + __DSB(); + __ISB(); + __disable_irq(); + SCB_DisableDCache(); + + #if defined(MIMXRT117x_SERIES) + volatile uint8_t pll2_div = div_table_mhz[freq_index] - 1; + + while (!FLEXSPI_GetBusIdleStatus(BOARD_FLEX_SPI)) { + } + FLEXSPI_Enable(BOARD_FLEX_SPI, false); + + // Disable FlexSPI clock + // Flexspi is clocked by PLL2. Only the divider can be changed. + CCM->LPCG[kCLOCK_Flexspi1].DIRECT = ((uint32_t)kCLOCK_Off & CCM_LPCG_DIRECT_ON_MASK); + // Change the PLL divider + CCM->CLOCK_ROOT[kCLOCK_Root_Flexspi1].CONTROL = (CCM->CLOCK_ROOT[kCLOCK_Root_Flexspi1].CONTROL & ~CCM_CLOCK_ROOT_CONTROL_DIV_MASK) | + CCM_CLOCK_ROOT_CONTROL_DIV(pll2_div); + // Re-enable FlexSPI clock + CCM->LPCG[kCLOCK_Flexspi1].DIRECT = ((uint32_t)kCLOCK_On & CCM_LPCG_DIRECT_ON_MASK); + + #else + + volatile uint8_t pfd480_div = div_table_mhz[freq_index].pfd480_div; + volatile uint8_t podf_div = div_table_mhz[freq_index].podf_div - 1; + + while (!FLEXSPI_GetBusIdleStatus(BOARD_FLEX_SPI)) { + } + FLEXSPI_Enable(BOARD_FLEX_SPI, false); + + // Disable FlexSPI clock + CCM->CCGR6 &= ~CCM_CCGR6_CG5_MASK; + // Changing the clock is OK now. + // Change the PFD + CCM_ANALOG->PFD_480 = (CCM_ANALOG->PFD_480 & ~CCM_ANALOG_PFD_480_TOG_PFD0_FRAC_MASK) | CCM_ANALOG_PFD_480_TOG_PFD0_FRAC(pfd480_div); + // Change the flexspi divider + CCM->CSCMR1 = (CCM->CSCMR1 & ~CCM_CSCMR1_FLEXSPI_PODF_MASK) | CCM_CSCMR1_FLEXSPI_PODF(podf_div); + // Re-enable FlexSPI + CCM->CCGR6 |= CCM_CCGR6_CG5_MASK; + #endif + + FLEXSPI_Enable(BOARD_FLEX_SPI, true); + FLEXSPI_SoftwareReset(BOARD_FLEX_SPI); + while (!FLEXSPI_GetBusIdleStatus(BOARD_FLEX_SPI)) { + } + + SCB_EnableDCache(); + __enable_irq(); } void flexspi_nor_reset(FLEXSPI_Type *base) __attribute__((section(".ram_functions"))); diff --git a/ports/mimxrt/hal/flexspi_nor_flash.h b/ports/mimxrt/hal/flexspi_nor_flash.h index 2e61effc2b7d8..dc5798d97f409 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.h +++ b/ports/mimxrt/hal/flexspi_nor_flash.h @@ -45,8 +45,7 @@ extern flexspi_nor_config_t qspiflash_config; status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId); -status_t flexspi_nor_init(void); -void flexspi_nor_update_lut(void); +void flexspi_nor_update_lut_clk(uint32_t freq_index); status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base); status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address); status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address); From 2f646f93d375577d7bc5b500e6fe50c9381875c2 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 29 Nov 2024 16:22:51 +0100 Subject: [PATCH 281/546] mimxrt: Add optional MSC support. Add MSC support using internal flash storage or SD card. Note this is disabled by default, and can be enabled by boards if needed. Signed-off-by: iabdalkader --- ports/mimxrt/Makefile | 1 + ports/mimxrt/main.c | 15 ++++ ports/mimxrt/modmimxrt.c | 3 + ports/mimxrt/modmimxrt.h | 1 + ports/mimxrt/mpconfigport.h | 7 ++ ports/mimxrt/msc_disk.c | 153 ++++++++++++++++++++++++++++++++++++ 6 files changed, 180 insertions(+) create mode 100644 ports/mimxrt/msc_disk.c diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 224b38384f718..8106ab4f3ff03 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -217,6 +217,7 @@ SRC_C += \ modmimxrt.c \ mphalport.c \ mpnetworkport.c \ + msc_disk.c \ network_lan.c \ pendsv.c \ pin.c \ diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index adb071a7fee8f..6b9d4fa17afe4 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -39,6 +39,7 @@ #include "led.h" #include "pendsv.h" #include "modmachine.h" +#include "modmimxrt.h" #if MICROPY_PY_LWIP #include "lwip/init.h" @@ -55,6 +56,7 @@ #include "systick.h" #include "extmod/modnetwork.h" +#include "extmod/vfs.h" extern uint8_t _sstack, _estack, _gc_heap_start, _gc_heap_end; @@ -113,6 +115,19 @@ int main(void) { // Execute _boot.py to set up the filesystem. pyexec_frozen_module("_boot.py", false); + #if MICROPY_HW_USB_MSC + // Set the USB medium to flash block device. + mimxrt_msc_medium = &mimxrt_flash_type; + + #if MICROPY_PY_MACHINE_SDCARD + const char *path = "/sdcard"; + // If SD is mounted, set the USB medium to SD. + if (mp_vfs_lookup_path(path, &path) != MP_VFS_NONE) { + mimxrt_msc_medium = &machine_sdcard_type; + } + #endif + #endif + // Execute user scripts. int ret = pyexec_file_if_exists("boot.py"); diff --git a/ports/mimxrt/modmimxrt.c b/ports/mimxrt/modmimxrt.c index 0f67538ce0d21..d699027388521 100644 --- a/ports/mimxrt/modmimxrt.c +++ b/ports/mimxrt/modmimxrt.c @@ -31,6 +31,9 @@ static const mp_rom_map_elem_t mimxrt_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mimxrt) }, { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&mimxrt_flash_type) }, + #if MICROPY_HW_USB_MSC + { MP_ROM_QSTR(MP_QSTR_MSC), MP_ROM_TRUE }, + #endif }; static MP_DEFINE_CONST_DICT(mimxrt_module_globals, mimxrt_module_globals_table); diff --git a/ports/mimxrt/modmimxrt.h b/ports/mimxrt/modmimxrt.h index e047716691807..19cf799f88771 100644 --- a/ports/mimxrt/modmimxrt.h +++ b/ports/mimxrt/modmimxrt.h @@ -30,5 +30,6 @@ extern const mp_obj_type_t mimxrt_flash_type; extern const mp_obj_module_t mp_module_mimxrt; +extern const mp_obj_type_t *mimxrt_msc_medium; #endif // MICROPY_INCLUDED_MIMXRT_MODMIMXRT_H diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index b45f8408e6415..7da85f1aee579 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -151,8 +151,15 @@ uint32_t trng_random_u32(void); #endif #define MICROPY_HW_ENABLE_USBDEV (1) +// Enable USB-CDC serial port +#ifndef MICROPY_HW_USB_CDC #define MICROPY_HW_USB_CDC (1) #define MICROPY_HW_USB_CDC_1200BPS_TOUCH (1) +#endif +// Enable USB Mass Storage with FatFS filesystem. +#ifndef MICROPY_HW_USB_MSC +#define MICROPY_HW_USB_MSC (0) +#endif // Hooks to add builtins diff --git a/ports/mimxrt/msc_disk.c b/ports/mimxrt/msc_disk.c new file mode 100644 index 0000000000000..c3801f481e618 --- /dev/null +++ b/ports/mimxrt/msc_disk.c @@ -0,0 +1,153 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * Copyright (c) 2024-2025 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 "tusb.h" +#if CFG_TUD_MSC +#include "flash.h" +#include BOARD_FLASH_OPS_HEADER_H +#include "stdlib.h" +#include "modmimxrt.h" +#if MICROPY_PY_MACHINE_SDCARD +#include "sdcard.h" + +#ifndef MICROPY_HW_SDCARD_SDMMC +#define MICROPY_HW_SDCARD_SDMMC (1) +#endif + +#define MSC_SDCARD_INDEX (MICROPY_HW_SDCARD_SDMMC - 1) +#endif + +// This implementation does Not support Flash sector caching. +// MICROPY_FATFS_MAX_SS must be identical to SECTOR_SIZE_BYTES +#define BLOCK_SIZE (SECTOR_SIZE_BYTES) +#define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_BYTES / BLOCK_SIZE) +#define FLASH_BASE_ADDR (MICROPY_HW_FLASH_STORAGE_BASE) + +static bool msc_ejected = false; + +const mp_obj_type_t *mimxrt_msc_medium = NULL; + +// Invoked when received SCSI_CMD_INQUIRY +// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { + memcpy(vendor_id, MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING), 8)); + memcpy(product_id, MICROPY_HW_USB_MSC_INQUIRY_PRODUCT_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_PRODUCT_STRING), 16)); + memcpy(product_rev, MICROPY_HW_USB_MSC_INQUIRY_REVISION_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_REVISION_STRING), 4)); +} + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool tud_msc_test_unit_ready_cb(uint8_t lun) { + if (msc_ejected || mimxrt_msc_medium == NULL) { + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); + return false; + } + return true; +} + +// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size +// Application update block count and block size +void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) { + if (mimxrt_msc_medium == &mimxrt_flash_type) { + *block_size = BLOCK_SIZE; + *block_count = BLOCK_COUNT; + #if MICROPY_PY_MACHINE_SDCARD + } else if (mimxrt_msc_medium == &machine_sdcard_type) { + mimxrt_sdcard_obj_t *card = &mimxrt_sdcard_objs[MSC_SDCARD_INDEX]; + *block_size = card->block_len; + *block_count = card->block_count; + #endif + } +} + +// Invoked when received Start Stop Unit command +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { + if (load_eject) { + if (start) { + // load disk storage + msc_ejected = false; + } else { + // unload disk storage + msc_ejected = true; + } + } + return true; +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) { + if (mimxrt_msc_medium == &mimxrt_flash_type) { + flash_read_block(FLASH_BASE_ADDR + lba * BLOCK_SIZE, buffer, bufsize); + #if MICROPY_PY_MACHINE_SDCARD + } else if (mimxrt_msc_medium == &machine_sdcard_type) { + mimxrt_sdcard_obj_t *card = &mimxrt_sdcard_objs[MSC_SDCARD_INDEX]; + sdcard_read(card, buffer, lba, bufsize / card->block_len); + #endif + } + return bufsize; +} + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) { + if (mimxrt_msc_medium == &mimxrt_flash_type) { + // Erase count sectors starting at lba + for (int n = 0; n < (bufsize / BLOCK_SIZE); n++) { + flash_erase_sector(FLASH_BASE_ADDR + (lba + n) * BLOCK_SIZE); + } + flash_write_block(FLASH_BASE_ADDR + lba * BLOCK_SIZE, buffer, bufsize); + #if MICROPY_PY_MACHINE_SDCARD + } else if (mimxrt_msc_medium == &machine_sdcard_type) { + mimxrt_sdcard_obj_t *card = &mimxrt_sdcard_objs[MSC_SDCARD_INDEX]; + sdcard_write(card, buffer, lba, bufsize / card->block_len); + + #endif + } + return bufsize; +} + +// Callback invoked when received an SCSI command not in built-in list below +// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE +// - READ10 and WRITE10 has their own callbacks +int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) { + int32_t resplen = 0; + switch (scsi_cmd[0]) { + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + // Sync the logical unit if needed. + break; + + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + // negative means error -> tinyusb could stall and/or response with failed status + resplen = -1; + break; + } + return resplen; +} +#endif From cb417505f3c39a526162fe8db9365035ea415c98 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 7 Feb 2025 13:39:02 +0100 Subject: [PATCH 282/546] mimxrt/boards: Reduce stack size for 1011 and 1015 MCUs. Reduced to 16KBs to allow enabling MSC. Signed-off-by: iabdalkader --- ports/mimxrt/boards/MIMXRT1011.ld | 4 ++-- ports/mimxrt/boards/MIMXRT1015.ld | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/mimxrt/boards/MIMXRT1011.ld b/ports/mimxrt/boards/MIMXRT1011.ld index ab363bd56a025..0e961a49433f2 100644 --- a/ports/mimxrt/boards/MIMXRT1011.ld +++ b/ports/mimxrt/boards/MIMXRT1011.ld @@ -27,8 +27,8 @@ dtcm_size = 0x00008000; ocrm_start = 0x20200000; ocrm_size = 0x00010000; -/* 20kiB stack. */ -__stack_size__ = 0x5000; +/* 16kiB stack. */ +__stack_size__ = 0x4000; _estack = __StackTop; _sstack = __StackLimit; diff --git a/ports/mimxrt/boards/MIMXRT1015.ld b/ports/mimxrt/boards/MIMXRT1015.ld index 0237d348c2498..58b88e0fe3084 100644 --- a/ports/mimxrt/boards/MIMXRT1015.ld +++ b/ports/mimxrt/boards/MIMXRT1015.ld @@ -27,8 +27,8 @@ dtcm_size = 0x00008000; ocrm_start = 0x20200000; ocrm_size = 0x00010000; -/* 24kiB stack. */ -__stack_size__ = 0x5000; +/* 16kiB stack. */ +__stack_size__ = 0x4000; _estack = __StackTop; _sstack = __StackLimit; From 752c1672af7f516922416e8afe7f4cb42e151cae Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 7 Feb 2025 13:45:44 +0100 Subject: [PATCH 283/546] tools/ci.sh: Build MIMXRT1060_EVK with MSC enabled as part of mimxrt CI. Signed-off-by: iabdalkader --- tools/ci.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/ci.sh b/tools/ci.sh index 2c647012f144d..ff362efd29e2d 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -230,6 +230,8 @@ function ci_mimxrt_build { make ${MAKEOPTS} -C ports/mimxrt BOARD=MIMXRT1020_EVK make ${MAKEOPTS} -C ports/mimxrt BOARD=TEENSY40 submodules make ${MAKEOPTS} -C ports/mimxrt BOARD=TEENSY40 + make ${MAKEOPTS} -C ports/mimxrt BOARD=MIMXRT1060_EVK submodules + make ${MAKEOPTS} -C ports/mimxrt BOARD=MIMXRT1060_EVK CFLAGS_EXTRA=-DMICROPY_HW_USB_MSC=1 } ######################################################################################## From 11c9656fad716d42a33e9f5bc65596300d141daa Mon Sep 17 00:00:00 2001 From: Glenn Moloney Date: Tue, 19 Sep 2023 11:41:46 +1000 Subject: [PATCH 284/546] tools/mpremote: Support mip install from package.json on local fs. Add support for `mpremote mip install package.json` where `package.json` is a json file on the local filesystem. Without this, package json files can only be loaded from http, https, github or gitlab URLs. This is useful for testing `package.json` files for pacages in development and for constructing one's own `package.json` files for Python packages which are not yet available for installation using mip. Signed-off-by: Glenn Moloney --- tools/mpremote/mpremote/mip.py | 40 ++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/tools/mpremote/mpremote/mip.py b/tools/mpremote/mpremote/mip.py index d23a0e2cbcb4f..4ce62b7f77bbb 100644 --- a/tools/mpremote/mpremote/mip.py +++ b/tools/mpremote/mpremote/mip.py @@ -13,6 +13,8 @@ _PACKAGE_INDEX = "https://micropython.org/pi/v2" +allowed_mip_url_prefixes = ("http://", "https://", "github:", "gitlab:") + # This implements os.makedirs(os.dirname(path)) def _ensure_path_exists(transport, path): @@ -78,16 +80,25 @@ def _download_file(transport, url, dest): def _install_json(transport, package_json_url, index, target, version, mpy): - try: - with urllib.request.urlopen(_rewrite_url(package_json_url, version)) as response: - package_json = json.load(response) - except urllib.error.HTTPError as e: - if e.status == 404: - raise CommandError(f"Package not found: {package_json_url}") - else: - raise CommandError(f"Error {e.status} requesting {package_json_url}") - except urllib.error.URLError as e: - raise CommandError(f"{e.reason} requesting {package_json_url}") + if package_json_url.startswith(allowed_mip_url_prefixes): + try: + with urllib.request.urlopen(_rewrite_url(package_json_url, version)) as response: + package_json = json.load(response) + except urllib.error.HTTPError as e: + if e.status == 404: + raise CommandError(f"Package not found: {package_json_url}") + else: + raise CommandError(f"Error {e.status} requesting {package_json_url}") + except urllib.error.URLError as e: + raise CommandError(f"{e.reason} requesting {package_json_url}") + elif package_json_url.endswith(".json"): + try: + with open(package_json_url, "r") as f: + package_json = json.load(f) + except OSError: + raise CommandError(f"Error opening {package_json_url}") + else: + raise CommandError(f"Invalid url for package: {package_json_url}") for target_path, short_hash in package_json.get("hashes", ()): fs_target_path = target + "/" + target_path file_url = f"{index}/file/{short_hash[:2]}/{short_hash}" @@ -100,12 +111,7 @@ def _install_json(transport, package_json_url, index, target, version, mpy): def _install_package(transport, package, index, target, version, mpy): - if ( - package.startswith("http://") - or package.startswith("https://") - or package.startswith("github:") - or package.startswith("gitlab:") - ): + if package.startswith(allowed_mip_url_prefixes): if package.endswith(".py") or package.endswith(".mpy"): print(f"Downloading {package} to {target}") _download_file( @@ -118,6 +124,8 @@ def _install_package(transport, package, index, target, version, mpy): package += "/" package += "package.json" print(f"Installing {package} to {target}") + elif package.endswith(".json"): + pass else: if not version: version = "latest" From b11ba39c57ef6dd4d5a865464229a2aede9c825e Mon Sep 17 00:00:00 2001 From: Neil Ludban Date: Sat, 25 Jan 2025 16:00:24 -0500 Subject: [PATCH 285/546] rp2/modules: Fix memory leak and logic bug in handling of _pio_funcs. The `rp2` package use a global dict `_pio_funcs` to populate a namespace for `@asm_pio` functions to be executed in. That dict is not cleaned up after use, keeping references to bound methods of a `PIOASMEmit`. By not setting/clearing all the functions, `asm_pio_encode` unintentionally allows the use of the old directives (harmless) as well as `jmp` (in general, produces the wrong output). Fix that by making sure `_pio_funcs` is returned to its original state after using it: - For `@asm_pio` update the target dict from `_pio_funcs` and then set additional functions as needed, leaving `_pio_funcs` unchanged. - For `asm_pio_encode`, borrow `_pio_funcs` to use as globals (avoiding a bunch of memory alloc/free) but delete the instruction entries after use. Signed-off-by: Neil Ludban --- ports/rp2/modules/rp2.py | 91 +++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 49 deletions(-) diff --git a/ports/rp2/modules/rp2.py b/ports/rp2/modules/rp2.py index e9be7dac8d58e..6068926036baf 100644 --- a/ports/rp2/modules/rp2.py +++ b/ports/rp2/modules/rp2.py @@ -215,49 +215,46 @@ def set(self, dest, data): # "block": see above "clear": 0x40, "rel": lambda x: x | 0x10, - # functions - "wrap_target": None, - "wrap": None, - "label": None, - "word": None, - "nop": None, - "jmp": None, - "wait": None, - "in_": None, - "out": None, - "push": None, - "pull": None, - "mov": None, - "irq": None, - "set": None, } +_pio_directives = ( + "wrap_target", + "wrap", + "label", +) + + +_pio_instructions = ( + "word", + "nop", + "jmp", + "wait", + "in_", + "out", + "push", + "pull", + "mov", + "irq", + "set", +) + + def asm_pio(**kw): emit = PIOASMEmit(**kw) def dec(f): nonlocal emit - gl = _pio_funcs - gl["wrap_target"] = emit.wrap_target - gl["wrap"] = emit.wrap - gl["label"] = emit.label - gl["word"] = emit.word - gl["nop"] = emit.nop - gl["jmp"] = emit.jmp - gl["wait"] = emit.wait - gl["in_"] = emit.in_ - gl["out"] = emit.out - gl["push"] = emit.push - gl["pull"] = emit.pull - gl["mov"] = emit.mov - gl["irq"] = emit.irq - gl["set"] = emit.set - - old_gl = f.__globals__.copy() - f.__globals__.clear() - f.__globals__.update(gl) + gl = f.__globals__ + old_gl = gl.copy() + gl.clear() + + gl.update(_pio_funcs) + for name in _pio_directives: + gl[name] = getattr(emit, name) + for name in _pio_instructions: + gl[name] = getattr(emit, name) emit.start_pass(0) f() @@ -265,8 +262,8 @@ def dec(f): emit.start_pass(1) f() - f.__globals__.clear() - f.__globals__.update(old_gl) + gl.clear() + gl.update(old_gl) return emit.prog @@ -284,19 +281,15 @@ def asm_pio_encode(instr, sideset_count, sideset_opt=False): emit.num_sideset = 0 gl = _pio_funcs - gl["word"] = emit.word - gl["nop"] = emit.nop - # gl["jmp"] = emit.jmp currently not supported - gl["wait"] = emit.wait - gl["in_"] = emit.in_ - gl["out"] = emit.out - gl["push"] = emit.push - gl["pull"] = emit.pull - gl["mov"] = emit.mov - gl["irq"] = emit.irq - gl["set"] = emit.set - - exec(instr, gl) + for name in _pio_instructions: + gl[name] = getattr(emit, name) + gl["jmp"] = None # emit.jmp currently not supported + + try: + exec(instr, gl) + finally: + for name in _pio_instructions: + del gl[name] if len(emit.prog[_PROG_DATA]) != 1: raise PIOASMError("expecting exactly 1 instruction") From 12dd9cb74598bb0144cb7ac7dcbc5f5631372afa Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 29 Jan 2025 15:18:31 +1100 Subject: [PATCH 286/546] docs/esp32: Add documentation for SPI Ethernet devices on esp32 port. Also cross-link with the other WIZNET5K driver, to avoid confusion. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/esp32/quickref.rst | 110 ++++++++++++++++++++++++++---- docs/library/network.LAN.rst | 7 +- docs/library/network.WIZNET5K.rst | 3 + 3 files changed, 104 insertions(+), 16 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index c5c2dfb38e570..0780e01300801 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -121,11 +121,20 @@ calling ``wlan.config(reconnects=n)``, where n are the number of desired reconne attempts (0 means it won't retry, -1 will restore the default behaviour of trying to reconnect forever). +.. _esp32_network_lan: + LAN ^^^ -To use the wired interfaces via :class:`network.LAN` one has to specify the pins -and mode :: +Built-in MAC (original ESP32) +""""""""""""""""""""""""""""" + +The original ESP32 SoC has a built-in Ethernet MAC. Using this MAC requires an +external Ethernet PHY to be wired to the chip's EMAC pins. Most of the EMAC pin +assignments are fixed, consult the ESP32 datasheet for details. + +If the PHY is connected, the internal Ethernet MAC can be configured via +the :class:`network.LAN` constructor:: import network @@ -134,20 +143,33 @@ and mode :: lan.ipconfig('addr4') # get the interface's IPv4 addresses -The keyword arguments for the constructor defining the PHY type and interface are: +Required keyword arguments for the constructor: + +- ``mdc`` and ``mdio`` - :class:`machine.Pin` objects (or integers) specifying + the MDC and MDIO pins. +- ``phy_type`` - Select the PHY device type. Supported devices are + ``PHY_LAN8710``, ``PHY_LAN8720``, ``PHY_IP101``, ``PHY_RTL8201``, + ``PHY_DP83848``, ``PHY_KSZ8041`` and ``PHY_KSZ8081``. These values are all + constants defined in the ``network`` module. +- ``phy_addr`` - The address number of the PHY device. Must be an integer in the + range 0x00 to 0x1f, inclusive. Common values are ``0`` and ``1``. + +All of the above keyword arguments must be present to configure the interface. + +Optional keyword arguments: -- mdc=pin-object # set the mdc and mdio pins. -- mdio=pin-object -- reset=pin-object # set the reset pin of the PHY device. -- power=pin-object # set the pin which switches the power of the PHY device. -- phy_type= # Select the PHY device type. Supported devices are PHY_LAN8710, - PHY_LAN8720, PH_IP101, PHY_RTL8201, PHY_DP83848 and PHY_KSZ8041 -- phy_addr=number # The address number of the PHY device. -- ref_clk_mode=mode # Defines, whether the ref_clk at the ESP32 is an input - or output. Suitable values are Pin.IN and Pin.OUT. -- ref_clk=pin-object # defines the Pin used for ref_clk. +- ``reset`` - :class:`machine.Pin` object (or integer) specifying the PHY reset pin. +- ``power`` - :class:`machine.Pin` object (or integer) specifying a pin which + switches the power of the PHY device. +- ``ref_clk`` - :class:`machine.Pin` object (or integer) specifying the pin used + for the EMAC ``ref_clk`` signal. If not specified, the board default is used + (typically GPIO 0, but may be different if a particular board has Ethernet.) +- ``ref_clk_mode`` - Defines whether the EMAC ``ref_clk`` pin of the ESP32 + should be an input or an output. Suitable values are ``machine.Pin.IN`` and + ``machine.Pin.OUT``. If not specified, the board default is used + (typically input, but may be different if a particular board has Ethernet.) -These are working configurations for LAN interfaces of popular boards:: +These are working configurations for LAN interfaces of some popular ESP32 boards:: # Olimex ESP32-GATEWAY: power controlled by Pin(5) # Olimex ESP32 PoE and ESP32-PoE ISO: power controlled by Pin(12) @@ -172,6 +194,66 @@ These are working configurations for LAN interfaces of popular boards:: lan = network.LAN(id=0, mdc=Pin(23), mdio=Pin(18), power=Pin(5), phy_type=network.PHY_IP101, phy_addr=1) + +.. _esp32_spi_ethernet: + +SPI Ethernet Interface +"""""""""""""""""""""" + +All ESP32 SoCs support external SPI Ethernet interface chips. These are Ethernet +interfaces that connect via a SPI bus, rather than an Ethernet RMII interface. + +.. note:: The only exception is the ESP32 ``d2wd`` variant, where this feature is disabled + to save code size. + +SPI Ethernet uses the same :class:`network.LAN` constructor, with a different +set of keyword arguments:: + + import machine, network + + spi = machine.SPI(1, sck=SCK_PIN, mosi=MOSI_PIN, miso=MISO_PIN) + lan = network.LAN(spi=spi, cs=CS_PIN, ...) # Set the pin and mode configuration + lan.active(True) # activate the interface + lan.ipconfig('addr4') # get the interface's IPv4 addresses + +Required keyword arguments for the constructor: + +- ``spi`` - Should be a :class:`machine.SPI` object configured for this + connection. Note that any clock speed configured on the SPI object is ignored, + the SPI Ethernet clock speed is configured at compile time. +- ``cs`` - :class:`machine.Pin` object (or integer) specifying the CS pin + connected to the interface. +- ``int`` - :class:`machine.Pin` object (or integer) specifying the INT pin + connected to the interface. +- ``phy_type`` - Select the SPI Ethernet interface type. Supported devices are + ``PHY_KSZ8851SNL``, ``PHY_DM9051``, ``PHY_W5500``. These values are all + constants defined in the ``network`` module. +- ``phy_addr`` - The address number of the PHY device. Must be an integer in the + range 0x00 to 0x1f, inclusive. This is usually ``0`` for SPI Ethernet devices. + +All of the above keyword arguments must be present to configure the interface. + +Optional keyword arguments for the constructor: + +- ``reset`` - :class:`machine.Pin` object (or integer) specifying the SPI Ethernet + interface reset pin. +- ``power`` - :class:`machine.Pin` object (or integer) specifying a pin which + switches the power of the SPI Ethernet interface. + +Here is a sample configuration for a WIZNet W5500 chip connected to pins on +an ESP32-S3 development board:: + + import machine, network + from machine import Pin, SPI + + spi = SPI(1, sck=Pin(12), mosi=Pin(13), miso=Pin(14)) + lan = network.LAN(spi=spi, phy_type=network.PHY_W5500, phy_addr=0, + cs=Pin(10), int=Pin(11)) + +.. note:: WIZnet W5500 Ethernet is also supported on some other MicroPython + ports, but using a :ref:`different software interface + `. + Delay and timing ---------------- diff --git a/docs/library/network.LAN.rst b/docs/library/network.LAN.rst index 31ce8e98411c2..1b3c19c560b36 100644 --- a/docs/library/network.LAN.rst +++ b/docs/library/network.LAN.rst @@ -6,7 +6,7 @@ class LAN -- control an Ethernet module This class allows you to control the Ethernet interface. The PHY hardware type is board-specific. -Example usage:: +Example usage, for a board with built-in LAN support:: import network nic = network.LAN(0) @@ -32,7 +32,7 @@ Constructors - *phy_addr* specifies the address of the PHY interface. As with *phy_type*, the hardwired value has to be used for most boards and that value is the default. - *ref_clk_mode* specifies, whether the data clock is provided by the Ethernet controller or - the PYH interface. + the PHY interface. The default value is the one that matches the board. If set to ``LAN.OUT`` or ``Pin.OUT`` or ``True``, the clock is driven by the Ethernet controller, if set to ``LAN.IN`` or ``Pin.IN`` or ``False``, the clock is driven by the PHY interface. @@ -41,6 +41,9 @@ Constructors nic = LAN(0, phy_type=LAN.PHY_LAN8720, phy_addr=1, ref_clk_mode=Pin.IN) + .. note:: On esp32 port the constructor requires different arguments. See + :ref:`esp32 port reference `. + Methods ------- diff --git a/docs/library/network.WIZNET5K.rst b/docs/library/network.WIZNET5K.rst index a19bd45282399..d0778a7a37ce8 100644 --- a/docs/library/network.WIZNET5K.rst +++ b/docs/library/network.WIZNET5K.rst @@ -9,6 +9,9 @@ the W5200 and W5500 chipsets. The particular chipset that is supported by the firmware is selected at compile-time via the MICROPY_PY_NETWORK_WIZNET5K option. +.. note:: The esp32 port also supports WIZnet W5500 chipsets, but this port + uses the :ref:`network.LAN interface `. + Example usage:: import network From bab099826e956bcc000b8a3c45b144c97d870fe2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 28 Jan 2025 11:54:57 +1100 Subject: [PATCH 287/546] docs: Note which ports have default or optional network.PPP support. Also add the default values of these macros to the respective `mpconfigport.h` files, to improve discoverability. Signed-off-by: Angus Gratton --- docs/library/network.PPP.rst | 7 ++++++- ports/rp2/mpconfigport.h | 4 ++++ ports/stm32/mpconfigport.h | 14 +++++++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/docs/library/network.PPP.rst b/docs/library/network.PPP.rst index 17a8d1c1cd89d..15ab1da193448 100644 --- a/docs/library/network.PPP.rst +++ b/docs/library/network.PPP.rst @@ -5,7 +5,12 @@ 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. +the PPP protocol. + +.. note:: Currently only the esp32 port has PPP support enabled in the default + firmware build. PPP support can be enabled in custom builds of the + stm32 and rp2 ports by enabling networking support and setting + ``MICROPY_PY_NETWORK_PPP_LWIP`` to 1. Example usage:: diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index bc289bcbf97cf..fe8287ba1d6e5 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -212,6 +212,10 @@ #ifndef MICROPY_PY_WEBREPL #define MICROPY_PY_WEBREPL (1) #endif + +#ifndef MICROPY_PY_NETWORK_PPP_LWIP +#define MICROPY_PY_NETWORK_PPP_LWIP (0) +#endif #endif #if MICROPY_PY_NETWORK_CYW43 diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index dc0a767fb07ee..2b57446ee8233 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -146,9 +146,6 @@ #define MICROPY_HW_SOFTSPI_MAX_BAUDRATE (HAL_RCC_GetSysClockFreq() / 48) #define MICROPY_PY_WEBSOCKET (MICROPY_PY_LWIP) #define MICROPY_PY_WEBREPL (MICROPY_PY_LWIP) -#ifndef MICROPY_PY_SOCKET -#define MICROPY_PY_SOCKET (1) -#endif #ifndef MICROPY_PY_NETWORK #define MICROPY_PY_NETWORK (1) #endif @@ -156,6 +153,17 @@ #define MICROPY_PY_ONEWIRE (1) #endif +// optional network features +#if MICROPY_PY_NETWORK +#ifndef MICROPY_PY_SOCKET +#define MICROPY_PY_SOCKET (1) +#endif + +#ifndef MICROPY_PY_NETWORK_PPP_LWIP +#define MICROPY_PY_NETWORK_PPP_LWIP (0) +#endif +#endif + // fatfs configuration used in ffconf.h #define MICROPY_FATFS_ENABLE_LFN (2) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ From 0a55f1f40c8a0a69f34f550d45138d9ba7a1d467 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 4 Feb 2025 12:20:38 +1100 Subject: [PATCH 288/546] docs/reference: Add strings vs bytes to speed optimisation tips. Also add some additional context links, suggestions for alternative classes, etc. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/library/array.rst | 4 ++++ docs/library/builtins.rst | 4 ++++ docs/reference/speed_python.rst | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/library/array.rst b/docs/library/array.rst index f417a7046e2ff..957260c2c7f92 100644 --- a/docs/library/array.rst +++ b/docs/library/array.rst @@ -19,6 +19,10 @@ Classes array are given by *iterable*. If it is not provided, an empty array is created. + In addition to the methods below, array objects also implement the buffer + protocol. This means the contents of the entire array can be accessed as raw + bytes via a `memoryview` or other interfaces which use this protocol. + .. method:: append(val) Append new element *val* to the end of array, growing it. diff --git a/docs/library/builtins.rst b/docs/library/builtins.rst index 5956aea7ab20f..b5d08ba7fed50 100644 --- a/docs/library/builtins.rst +++ b/docs/library/builtins.rst @@ -19,6 +19,8 @@ Functions and types .. class:: bytearray() + |see_cpython| `python:bytearray`. + .. class:: bytes() |see_cpython| `python:bytes`. @@ -104,6 +106,8 @@ Functions and types .. class:: memoryview() + |see_cpython| `python:memoryview`. + .. function:: min() .. function:: next() diff --git a/docs/reference/speed_python.rst b/docs/reference/speed_python.rst index 0c68dd60bdedc..a660432f6da37 100644 --- a/docs/reference/speed_python.rst +++ b/docs/reference/speed_python.rst @@ -57,6 +57,8 @@ and used in various methods. This is covered in further detail :ref:`Controlling garbage collection ` below. +.. _speed_buffers: + Buffers ~~~~~~~ @@ -69,6 +71,13 @@ example, objects which support stream interface (e.g., file or UART) provide ``r method which allocates new buffer for read data, but also a ``readinto()`` method to read data into an existing buffer. +Some useful classes for creating reusable buffer objects: + +- :class:`bytearray` +- :mod:`array` (:ref:`discussed below`) +- :class:`io.StringIO` and :class:`io.BytesIO` +- :class:`micropython.RingIO` + Floating point ~~~~~~~~~~~~~~ @@ -80,15 +89,20 @@ point to sections of the code where performance is not paramount. For example, capture ADC readings as integers values to an array in one quick go, and only then convert them to floating-point numbers for signal processing. +.. _speed_arrays: + Arrays ~~~~~~ Consider the use of the various types of array classes as an alternative to lists. -The `array` module supports various element types with 8-bit elements supported +The :mod:`array` module supports various element types with 8-bit elements supported by Python's built in `bytes` and `bytearray` classes. These data structures all store elements in contiguous memory locations. Once again to avoid memory allocation in critical code these should be pre-allocated and passed as arguments or as bound objects. +Memoryviews +~~~~~~~~~~~ + When passing slices of objects such as `bytearray` instances, Python creates a copy which involves allocation of the size proportional to the size of slice. This can be alleviated using a `memoryview` object. The `memoryview` itself @@ -118,6 +132,23 @@ of buffer and fills in entire buffer. What if you need to put data in the middle of existing buffer? Just create a memoryview into the needed section of buffer and pass it to ``readinto()``. +Strings vs Bytes +~~~~~~~~~~~~~~~~ + +MicroPython uses :ref:`string interning ` to save space when there are +multiple identical strings. Each time a new string is allocated at runtime (for +example, when two other strings are concatenated), MicroPython checks whether +the new string can be interned to save RAM. + +If you have code which performs performance-critical string operations then +consider using :class:`bytes` objects and literals (i.e. ``b"abc"``). This skips +the interning check, and can be several times faster than performing the same +operations with string objects. + +.. note:: The fastest performance will always be achieved by avoiding new object + creation entirely, for example with a reusable :ref:`buffer as described + above`. + Identifying the slowest section of code --------------------------------------- From 3b6252466139d5dfc7458eeea5d759b6c9850146 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 10 Feb 2025 17:20:29 +0100 Subject: [PATCH 289/546] docs/library/espnow: Clarify usage of the "rate" configuration key. This commit adds a clarification for the ESPNow module's documentation regarding its "config" method. The original documentation for that method could be interpreted as having all its configuration keys being able to be queried, but the "rate" configuration key is actually write-only due to ESP-IDF's lack of a way to retrieve that bit of information from the radio's configuration. The documentation changes highlight the fact that said configuration key is actually write-only. Signed-off-by: Alessandro Gatti --- docs/library/espnow.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/library/espnow.rst b/docs/library/espnow.rst index 379e60486f224..84e9e9465de96 100644 --- a/docs/library/espnow.rst +++ b/docs/library/espnow.rst @@ -164,11 +164,13 @@ Configuration wait forever. The timeout can also be provided as arg to `recv()`/`irecv()`/`recvinto()`. - *rate*: (ESP32 only, IDF>=4.3.0 only) Set the transmission speed for + *rate*: (ESP32 only) Set the transmission speed for ESPNow packets. Must be set to a number from the allowed numeric values in `enum wifi_phy_rate_t - `_. + `_. This + parameter is actually *write-only* due to ESP-IDF not providing any + means for querying the radio interface's rate parameter. .. data:: Returns: From 372ecfef02eccc4e52a5d0abef068c7d25fc4315 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 21 Jan 2025 00:56:59 +1100 Subject: [PATCH 290/546] tests/run-tests.py: Give more information when CPython crashes. To make it easier to diagnose why CPython crashed. Signed-off-by: Damien George --- tests/run-tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index 1df1b35d23b83..b108b16395074 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -955,8 +955,8 @@ def run_one_test(test_file): cwd=os.path.dirname(test_file), stderr=subprocess.STDOUT, ) - except subprocess.CalledProcessError: - output_expected = b"CPYTHON3 CRASH" + except subprocess.CalledProcessError as er: + output_expected = b"CPYTHON3 CRASH:\n" + er.output # Canonical form for all host platforms is to use \n for end-of-line. output_expected = output_expected.replace(b"\r\n", b"\n") From 62e821ccb82fd8362a8198ad59ccb51b8a5c441e Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 19 Jan 2025 23:30:59 +1100 Subject: [PATCH 291/546] py/objcode: Factor code object out into its own file. The `mp_obj_code_t` and `mp_type_code` code object was defined internally in both `py/builtinevex.c` and `py/profile.c`, with completely different implementations (the former very minimal, the latter quite complete). This commit factors these implementations into a new, separate source file, and allows the code object to have four different modes, selected at compile-time: - MICROPY_PY_BUILTINS_CODE_NONE: code object not included in the build. - MICROPY_PY_BUILTINS_CODE_MINIMUM: very simple code object that just holds a reference to the function that it represents. This level is used when MICROPY_PY_BUILTINS_COMPILE is enabled. - MICROPY_PY_BUILTINS_CODE_BASIC: simple code object that holds a reference to the proto-function and its constants. - MICROPY_PY_BUILTINS_CODE_FULL: almost complete implementation of the code object. This level is used when MICROPY_PY_SYS_SETTRACE is enabled. Signed-off-by: Damien George --- py/builtinevex.c | 51 +++++++++----- py/compile.c | 2 +- py/compile.h | 5 +- py/mpconfig.h | 9 +++ py/obj.h | 1 + py/objcode.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++ py/objcode.h | 99 +++++++++++++++++++++++++++ py/profile.c | 140 ++----------------------------------- py/profile.h | 12 +--- py/py.cmake | 1 + py/py.mk | 1 + py/runtime.c | 7 +- 12 files changed, 337 insertions(+), 166 deletions(-) create mode 100644 py/objcode.c create mode 100644 py/objcode.h diff --git a/py/builtinevex.c b/py/builtinevex.c index e25cbd4d08502..74a4640492674 100644 --- a/py/builtinevex.c +++ b/py/builtinevex.c @@ -26,6 +26,7 @@ #include +#include "py/objcode.h" #include "py/objfun.h" #include "py/compile.h" #include "py/runtime.h" @@ -33,17 +34,6 @@ #if MICROPY_PY_BUILTINS_COMPILE -typedef struct _mp_obj_code_t { - mp_obj_base_t base; - mp_obj_t module_fun; -} mp_obj_code_t; - -static MP_DEFINE_CONST_OBJ_TYPE( - mp_type_code, - MP_QSTR_code, - MP_TYPE_FLAG_NONE - ); - static mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj_dict_t *locals) { // save context nlr_jump_callback_node_globals_locals_t ctx; @@ -57,19 +47,28 @@ static mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj // set exception handler to restore context if an exception is raised nlr_push_jump_callback(&ctx.callback, mp_globals_locals_set_from_nlr_jump_callback); + #if MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_BASIC + mp_module_context_t *module_context = m_new_obj(mp_module_context_t); + module_context->module.base.type = &mp_type_module; + module_context->module.globals = globals; + module_context->constants = *mp_code_get_constants(self); + mp_obj_t module_fun = mp_make_function_from_proto_fun(mp_code_get_proto_fun(self), module_context, NULL); + #else // The call to mp_parse_compile_execute() in mp_builtin_compile() below passes // NULL for the globals, so repopulate that entry now with the correct globals. + mp_obj_t module_fun = self->module_fun; if (mp_obj_is_type(self->module_fun, &mp_type_fun_bc) #if MICROPY_EMIT_NATIVE || mp_obj_is_type(self->module_fun, &mp_type_fun_native) #endif ) { - mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(self->module_fun); + mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(module_fun); ((mp_module_context_t *)fun_bc->context)->module.globals = globals; } + #endif // execute code - mp_obj_t ret = mp_call_function_0(self->module_fun); + mp_obj_t ret = mp_call_function_0(module_fun); // deregister exception handler and restore context nlr_pop_jump_callback(true); @@ -108,9 +107,29 @@ static mp_obj_t mp_builtin_compile(size_t n_args, const mp_obj_t *args) { mp_raise_ValueError(MP_ERROR_TEXT("bad compile mode")); } - mp_obj_code_t *code = mp_obj_malloc(mp_obj_code_t, &mp_type_code); - code->module_fun = mp_parse_compile_execute(lex, parse_input_kind, NULL, NULL); - return MP_OBJ_FROM_PTR(code); + #if MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_BASIC + + mp_parse_tree_t parse_tree = mp_parse(lex, parse_input_kind); + mp_module_context_t ctx; + ctx.module.globals = NULL; + mp_compiled_module_t cm; + cm.context = &ctx; + mp_compile_to_raw_code(&parse_tree, lex->source_name, parse_input_kind == MP_PARSE_SINGLE_INPUT, &cm); + + #if MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_FULL + mp_module_context_t *ctx_ptr = m_new_obj(mp_module_context_t); + *ctx_ptr = ctx; + return mp_obj_new_code(ctx_ptr, cm.rc, true); + #else + return mp_obj_new_code(ctx.constants, cm.rc); + #endif + + #else + + mp_obj_t module_fun = mp_parse_compile_execute(lex, parse_input_kind, NULL, NULL); + return mp_obj_new_code(module_fun); + + #endif } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_compile_obj, 3, 6, mp_builtin_compile); diff --git a/py/compile.c b/py/compile.c index 60f06d7773e34..7a1151bcd66f0 100644 --- a/py/compile.c +++ b/py/compile.c @@ -3467,7 +3467,7 @@ static void scope_compute_things(scope_t *scope) { } } -#if !MICROPY_PERSISTENT_CODE_SAVE +#if !MICROPY_EXPOSE_MP_COMPILE_TO_RAW_CODE static #endif void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm) { diff --git a/py/compile.h b/py/compile.h index f9970a521d644..64afc487d7c3d 100644 --- a/py/compile.h +++ b/py/compile.h @@ -30,6 +30,9 @@ #include "py/parse.h" #include "py/emitglue.h" +// Whether mp_compile_to_raw_code is exposed as a public function. +#define MICROPY_EXPOSE_MP_COMPILE_TO_RAW_CODE (MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_BASIC || MICROPY_PERSISTENT_CODE_SAVE) + #if MICROPY_COMP_ALLOW_TOP_LEVEL_AWAIT // set to `true` to allow top-level await expressions extern bool mp_compile_allow_top_level_await; @@ -40,7 +43,7 @@ extern bool mp_compile_allow_top_level_await; // mp_globals_get() will be used for the context mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl); -#if MICROPY_PERSISTENT_CODE_SAVE +#if MICROPY_EXPOSE_MP_COMPILE_TO_RAW_CODE // this has the same semantics as mp_compile void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm); #endif diff --git a/py/mpconfig.h b/py/mpconfig.h index 64138a9ea7acc..76aff4681d32a 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1129,6 +1129,15 @@ typedef double mp_float_t; #define MICROPY_PY_BUILTINS_BYTEARRAY (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif +// Whether to support code objects, and how many features they have +#define MICROPY_PY_BUILTINS_CODE_NONE (0) +#define MICROPY_PY_BUILTINS_CODE_MINIMUM (1) +#define MICROPY_PY_BUILTINS_CODE_BASIC (2) +#define MICROPY_PY_BUILTINS_CODE_FULL (3) +#ifndef MICROPY_PY_BUILTINS_CODE +#define MICROPY_PY_BUILTINS_CODE (MICROPY_PY_SYS_SETTRACE ? MICROPY_PY_BUILTINS_CODE_FULL : (MICROPY_PY_BUILTINS_COMPILE ? MICROPY_PY_BUILTINS_CODE_MINIMUM : MICROPY_PY_BUILTINS_CODE_NONE)) +#endif + // Whether to support dict.fromkeys() class method #ifndef MICROPY_PY_BUILTINS_DICT_FROMKEYS #define MICROPY_PY_BUILTINS_DICT_FROMKEYS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) diff --git a/py/obj.h b/py/obj.h index bd31ef11d0928..93d1e0ee34349 100644 --- a/py/obj.h +++ b/py/obj.h @@ -840,6 +840,7 @@ extern const mp_obj_type_t mp_type_fun_bc; extern const mp_obj_type_t mp_type_fun_native; extern const mp_obj_type_t mp_type_fun_viper; extern const mp_obj_type_t mp_type_fun_asm; +extern const mp_obj_type_t mp_type_code; extern const mp_obj_type_t mp_type_module; extern const mp_obj_type_t mp_type_staticmethod; extern const mp_obj_type_t mp_type_classmethod; diff --git a/py/objcode.c b/py/objcode.c new file mode 100644 index 0000000000000..9b98a696798d4 --- /dev/null +++ b/py/objcode.c @@ -0,0 +1,175 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/objcode.h" + +#if MICROPY_PY_BUILTINS_CODE == MICROPY_PY_BUILTINS_CODE_NONE + +// Code object not implemented at this configuration level. + +#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_BASIC + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_code, + MP_QSTR_code, + MP_TYPE_FLAG_NONE + ); + +#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_FULL + +#include "py/profile.h" + +static void code_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_code_t *o = MP_OBJ_TO_PTR(o_in); + const mp_raw_code_t *rc = o->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + mp_printf(print, + "", + MP_CODE_QSTR_MAP(o->context, prelude->qstr_block_name_idx), + o, + MP_CODE_QSTR_MAP(o->context, 0), + rc->line_of_definition + ); +} + +static mp_obj_tuple_t *code_consts(const mp_module_context_t *context, const mp_raw_code_t *rc) { + mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(rc->n_children + 1, NULL)); + + size_t const_no = 0; + for (size_t i = 0; i < rc->n_children; ++i) { + mp_obj_t code = mp_obj_new_code(context, rc->children[i], true); + consts->items[const_no++] = code; + } + consts->items[const_no++] = mp_const_none; + + return consts; +} + +static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) { + // const mp_bytecode_prelude_t *prelude = &rc->prelude; + uint start = 0; + uint stop = rc->fun_data_len - start; + + uint last_lineno = mp_prof_bytecode_lineno(rc, start); + uint lasti = 0; + + const uint buffer_chunk_size = (stop - start) >> 2; // heuristic magic + uint buffer_size = buffer_chunk_size; + byte *buffer = m_new(byte, buffer_size); + uint buffer_index = 0; + + for (uint i = start; i < stop; ++i) { + uint lineno = mp_prof_bytecode_lineno(rc, i); + size_t line_diff = lineno - last_lineno; + if (line_diff > 0) { + uint instr_diff = (i - start) - lasti; + + assert(instr_diff < 256); + assert(line_diff < 256); + + if (buffer_index + 2 > buffer_size) { + buffer = m_renew(byte, buffer, buffer_size, buffer_size + buffer_chunk_size); + buffer_size = buffer_size + buffer_chunk_size; + } + last_lineno = lineno; + lasti = i - start; + buffer[buffer_index++] = instr_diff; + buffer[buffer_index++] = line_diff; + } + } + + mp_obj_t o = mp_obj_new_bytes(buffer, buffer_index); + m_del(byte, buffer, buffer_size); + return o; +} + +static void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + mp_obj_code_t *o = MP_OBJ_TO_PTR(self_in); + const mp_raw_code_t *rc = o->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + switch (attr) { + case MP_QSTR_co_code: + dest[0] = mp_obj_new_bytes( + (void *)prelude->opcodes, + rc->fun_data_len - (prelude->opcodes - (const byte *)rc->fun_data) + ); + break; + case MP_QSTR_co_consts: + dest[0] = MP_OBJ_FROM_PTR(code_consts(o->context, rc)); + break; + case MP_QSTR_co_filename: + dest[0] = MP_OBJ_NEW_QSTR(MP_CODE_QSTR_MAP(o->context, 0)); + break; + case MP_QSTR_co_firstlineno: + dest[0] = MP_OBJ_NEW_SMALL_INT(mp_prof_bytecode_lineno(rc, 0)); + break; + case MP_QSTR_co_name: + dest[0] = MP_OBJ_NEW_QSTR(MP_CODE_QSTR_MAP(o->context, prelude->qstr_block_name_idx)); + break; + case MP_QSTR_co_names: + dest[0] = MP_OBJ_FROM_PTR(o->dict_locals); + break; + case MP_QSTR_co_lnotab: + if (!o->lnotab) { + o->lnotab = raw_code_lnotab(rc); + } + dest[0] = o->lnotab; + break; + } +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_code, + MP_QSTR_code, + MP_TYPE_FLAG_NONE, + print, code_print, + attr, code_attr + ); + +mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc, bool result_required) { + mp_obj_code_t *o; + if (result_required) { + o = m_new_obj(mp_obj_code_t); + } else { + o = m_new_obj_maybe(mp_obj_code_t); + if (o == NULL) { + return MP_OBJ_NULL; + } + } + o->base.type = &mp_type_code; + o->context = context; + o->rc = rc; + o->dict_locals = mp_locals_get(); // this is a wrong! how to do this properly? + o->lnotab = MP_OBJ_NULL; + return MP_OBJ_FROM_PTR(o); +} + +#endif diff --git a/py/objcode.h b/py/objcode.h new file mode 100644 index 0000000000000..8db9a34b6e1c9 --- /dev/null +++ b/py/objcode.h @@ -0,0 +1,99 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_PY_OBJCODE_H +#define MICROPY_INCLUDED_PY_OBJCODE_H + +#include "py/bc.h" + +#if MICROPY_PY_BUILTINS_CODE == MICROPY_PY_BUILTINS_CODE_NONE + +// Code object not implemented at this configuration level. + +#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_MINIMUM + +typedef struct _mp_obj_code_t { + mp_obj_base_t base; + mp_obj_t module_fun; +} mp_obj_code_t; + +static inline mp_obj_t mp_obj_new_code(mp_obj_t module_fun) { + mp_obj_code_t *code = mp_obj_malloc(mp_obj_code_t, &mp_type_code); + code->module_fun = module_fun; + return MP_OBJ_FROM_PTR(code); +} + +#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_BASIC + +typedef struct _mp_obj_code_t { + mp_obj_base_t base; + mp_module_constants_t constants; + const void *proto_fun; +} mp_obj_code_t; + +static inline mp_obj_t mp_obj_new_code(const mp_module_constants_t constants, const void *proto_fun) { + mp_obj_code_t *code = mp_obj_malloc(mp_obj_code_t, &mp_type_code); + code->constants = constants; + code->proto_fun = proto_fun; + return MP_OBJ_FROM_PTR(code); +} + +static inline const mp_module_constants_t *mp_code_get_constants(mp_obj_code_t *self) { + return &self->constants; +} + +static inline const void *mp_code_get_proto_fun(mp_obj_code_t *self) { + return self->proto_fun; +} + +#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_FULL + +#include "py/emitglue.h" + +#define MP_CODE_QSTR_MAP(context, idx) (context->constants.qstr_table[idx]) + +typedef struct _mp_obj_code_t { + // TODO this was 4 words + mp_obj_base_t base; + const mp_module_context_t *context; + const mp_raw_code_t *rc; + mp_obj_dict_t *dict_locals; + mp_obj_t lnotab; +} mp_obj_code_t; + +mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc, bool result_required); + +static inline const mp_module_constants_t *mp_code_get_constants(mp_obj_code_t *self) { + return &self->context->constants; +} + +static inline const void *mp_code_get_proto_fun(mp_obj_code_t *self) { + // A mp_raw_code_t is always a proto_fun (but not the other way around). + return self->rc; +} + +#endif + +#endif // MICROPY_INCLUDED_PY_OBJCODE_H diff --git a/py/profile.c b/py/profile.c index 92f414ace7c92..397d0291f9fad 100644 --- a/py/profile.c +++ b/py/profile.c @@ -38,9 +38,8 @@ #endif #define prof_trace_cb MP_STATE_THREAD(prof_trace_callback) -#define QSTR_MAP(context, idx) (context->constants.qstr_table[idx]) -static uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc) { +uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc) { const mp_bytecode_prelude_t *prelude = &rc->prelude; return mp_bytecode_get_source_line(prelude->line_info, prelude->line_info_top, bc); } @@ -68,137 +67,6 @@ void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelud prelude->line_info = ip; } -/******************************************************************************/ -// code object - -static void code_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { - (void)kind; - mp_obj_code_t *o = MP_OBJ_TO_PTR(o_in); - const mp_raw_code_t *rc = o->rc; - const mp_bytecode_prelude_t *prelude = &rc->prelude; - mp_printf(print, - "", - QSTR_MAP(o->context, prelude->qstr_block_name_idx), - o, - QSTR_MAP(o->context, 0), - rc->line_of_definition - ); -} - -static mp_obj_tuple_t *code_consts(const mp_module_context_t *context, const mp_raw_code_t *rc) { - mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(rc->n_children + 1, NULL)); - - size_t const_no = 0; - for (size_t i = 0; i < rc->n_children; ++i) { - mp_obj_t code = mp_obj_new_code(context, rc->children[i]); - if (code == MP_OBJ_NULL) { - m_malloc_fail(sizeof(mp_obj_code_t)); - } - consts->items[const_no++] = code; - } - consts->items[const_no++] = mp_const_none; - - return consts; -} - -static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) { - // const mp_bytecode_prelude_t *prelude = &rc->prelude; - uint start = 0; - uint stop = rc->fun_data_len - start; - - uint last_lineno = mp_prof_bytecode_lineno(rc, start); - uint lasti = 0; - - const uint buffer_chunk_size = (stop - start) >> 2; // heuristic magic - uint buffer_size = buffer_chunk_size; - byte *buffer = m_new(byte, buffer_size); - uint buffer_index = 0; - - for (uint i = start; i < stop; ++i) { - uint lineno = mp_prof_bytecode_lineno(rc, i); - size_t line_diff = lineno - last_lineno; - if (line_diff > 0) { - uint instr_diff = (i - start) - lasti; - - assert(instr_diff < 256); - assert(line_diff < 256); - - if (buffer_index + 2 > buffer_size) { - buffer = m_renew(byte, buffer, buffer_size, buffer_size + buffer_chunk_size); - buffer_size = buffer_size + buffer_chunk_size; - } - last_lineno = lineno; - lasti = i - start; - buffer[buffer_index++] = instr_diff; - buffer[buffer_index++] = line_diff; - } - } - - mp_obj_t o = mp_obj_new_bytes(buffer, buffer_index); - m_del(byte, buffer, buffer_size); - return o; -} - -static void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { - if (dest[0] != MP_OBJ_NULL) { - // not load attribute - return; - } - mp_obj_code_t *o = MP_OBJ_TO_PTR(self_in); - const mp_raw_code_t *rc = o->rc; - const mp_bytecode_prelude_t *prelude = &rc->prelude; - switch (attr) { - case MP_QSTR_co_code: - dest[0] = mp_obj_new_bytes( - (void *)prelude->opcodes, - rc->fun_data_len - (prelude->opcodes - (const byte *)rc->fun_data) - ); - break; - case MP_QSTR_co_consts: - dest[0] = MP_OBJ_FROM_PTR(code_consts(o->context, rc)); - break; - case MP_QSTR_co_filename: - dest[0] = MP_OBJ_NEW_QSTR(QSTR_MAP(o->context, 0)); - break; - case MP_QSTR_co_firstlineno: - dest[0] = MP_OBJ_NEW_SMALL_INT(mp_prof_bytecode_lineno(rc, 0)); - break; - case MP_QSTR_co_name: - dest[0] = MP_OBJ_NEW_QSTR(QSTR_MAP(o->context, prelude->qstr_block_name_idx)); - break; - case MP_QSTR_co_names: - dest[0] = MP_OBJ_FROM_PTR(o->dict_locals); - break; - case MP_QSTR_co_lnotab: - if (!o->lnotab) { - o->lnotab = raw_code_lnotab(rc); - } - dest[0] = o->lnotab; - break; - } -} - -MP_DEFINE_CONST_OBJ_TYPE( - mp_type_settrace_codeobj, - MP_QSTR_code, - MP_TYPE_FLAG_NONE, - print, code_print, - attr, code_attr - ); - -mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc) { - mp_obj_code_t *o = m_new_obj_maybe(mp_obj_code_t); - if (o == NULL) { - return MP_OBJ_NULL; - } - o->base.type = &mp_type_settrace_codeobj; - o->context = context; - o->rc = rc; - o->dict_locals = mp_locals_get(); // this is a wrong! how to do this properly? - o->lnotab = MP_OBJ_NULL; - return MP_OBJ_FROM_PTR(o); -} - /******************************************************************************/ // frame object @@ -211,9 +79,9 @@ static void frame_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t mp_printf(print, "", frame, - QSTR_MAP(code->context, 0), + MP_CODE_QSTR_MAP(code->context, 0), frame->lineno, - QSTR_MAP(code->context, prelude->qstr_block_name_idx) + MP_CODE_QSTR_MAP(code->context, prelude->qstr_block_name_idx) ); } @@ -265,7 +133,7 @@ mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state) { return MP_OBJ_NULL; } - mp_obj_code_t *code = o->code = MP_OBJ_TO_PTR(mp_obj_new_code(code_state->fun_bc->context, code_state->fun_bc->rc)); + mp_obj_code_t *code = o->code = MP_OBJ_TO_PTR(mp_obj_new_code(code_state->fun_bc->context, code_state->fun_bc->rc, false)); if (code == NULL) { return MP_OBJ_NULL; } diff --git a/py/profile.h b/py/profile.h index 7f3f914034623..db72b9f076818 100644 --- a/py/profile.h +++ b/py/profile.h @@ -28,20 +28,12 @@ #define MICROPY_INCLUDED_PY_PROFILING_H #include "py/emitglue.h" +#include "py/objcode.h" #if MICROPY_PY_SYS_SETTRACE #define mp_prof_is_executing MP_STATE_THREAD(prof_callback_is_executing) -typedef struct _mp_obj_code_t { - // TODO this was 4 words - mp_obj_base_t base; - const mp_module_context_t *context; - const mp_raw_code_t *rc; - mp_obj_dict_t *dict_locals; - mp_obj_t lnotab; -} mp_obj_code_t; - typedef struct _mp_obj_frame_t { mp_obj_base_t base; const mp_code_state_t *code_state; @@ -53,9 +45,9 @@ typedef struct _mp_obj_frame_t { bool trace_opcodes; } mp_obj_frame_t; +uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc); void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude); -mp_obj_t mp_obj_new_code(const mp_module_context_t *mc, const mp_raw_code_t *rc); mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state); // This is the implementation for the sys.settrace diff --git a/py/py.cmake b/py/py.cmake index 0fee74ddcb489..1c81ed4c58f32 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -72,6 +72,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/objattrtuple.c ${MICROPY_PY_DIR}/objbool.c ${MICROPY_PY_DIR}/objboundmeth.c + ${MICROPY_PY_DIR}/objcode.c ${MICROPY_PY_DIR}/objcell.c ${MICROPY_PY_DIR}/objclosure.c ${MICROPY_PY_DIR}/objcomplex.c diff --git a/py/py.mk b/py/py.mk index c0b7e1ac8b90e..e352d89792bfd 100644 --- a/py/py.mk +++ b/py/py.mk @@ -149,6 +149,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ objboundmeth.o \ objcell.o \ objclosure.o \ + objcode.o \ objcomplex.o \ objdeque.o \ objdict.o \ diff --git a/py/runtime.c b/py/runtime.c index deb55bf283abc..bf26921f9ab9f 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1620,10 +1620,13 @@ mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_i mp_obj_t module_fun = mp_compile(&parse_tree, source_name, parse_input_kind == MP_PARSE_SINGLE_INPUT); mp_obj_t ret; - if (MICROPY_PY_BUILTINS_COMPILE && globals == NULL) { + #if MICROPY_PY_BUILTINS_COMPILE && MICROPY_PY_BUILTINS_CODE == MICROPY_PY_BUILTINS_CODE_MINIMUM + if (globals == NULL) { // for compile only, return value is the module function ret = module_fun; - } else { + } else + #endif + { // execute module function and get return value ret = mp_call_function_0(module_fun); } From ceb8ba60b4fea0c32e4977d0e45d5c0203b27b34 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 20 Jan 2025 22:23:48 +1100 Subject: [PATCH 292/546] py/objfun: Implement function.__code__ and function constructor. This allows retrieving the code object of a function using `function.__code__`, and then reconstructing a function from a code object using `FunctionType(code_object)`. This feature is controlled by `MICROPY_PY_FUNCTION_ATTRS_CODE` and is enabled at the full-features level. Signed-off-by: Damien George --- py/mpconfig.h | 7 ++- py/objfun.c | 47 +++++++++++++++++++ tests/basics/fun_code.py | 36 ++++++++++++++ tests/basics/fun_code_micropython.py | 19 ++++++++ tests/basics/fun_code_micropython.py.exp | 1 + tests/basics/subclass_native1.py | 6 +-- tests/micropython/native_fun_attrs_code.py | 21 +++++++++ .../micropython/native_fun_attrs_code.py.exp | 1 + 8 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 tests/basics/fun_code.py create mode 100644 tests/basics/fun_code_micropython.py create mode 100644 tests/basics/fun_code_micropython.py.exp create mode 100644 tests/micropython/native_fun_attrs_code.py create mode 100644 tests/micropython/native_fun_attrs_code.py.exp diff --git a/py/mpconfig.h b/py/mpconfig.h index 76aff4681d32a..5c4d19bb56f11 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1041,6 +1041,11 @@ typedef double mp_float_t; #define MICROPY_PY_FUNCTION_ATTRS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +// Whether to implement the __code__ attribute on functions, and function constructor +#ifndef MICROPY_PY_FUNCTION_ATTRS_CODE +#define MICROPY_PY_FUNCTION_ATTRS_CODE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_FULL_FEATURES) +#endif + // Whether to support the descriptors __get__, __set__, __delete__ // This costs some code size and makes load/store/delete of instance // attributes slower for the classes that use this feature @@ -1135,7 +1140,7 @@ typedef double mp_float_t; #define MICROPY_PY_BUILTINS_CODE_BASIC (2) #define MICROPY_PY_BUILTINS_CODE_FULL (3) #ifndef MICROPY_PY_BUILTINS_CODE -#define MICROPY_PY_BUILTINS_CODE (MICROPY_PY_SYS_SETTRACE ? MICROPY_PY_BUILTINS_CODE_FULL : (MICROPY_PY_BUILTINS_COMPILE ? MICROPY_PY_BUILTINS_CODE_MINIMUM : MICROPY_PY_BUILTINS_CODE_NONE)) +#define MICROPY_PY_BUILTINS_CODE (MICROPY_PY_SYS_SETTRACE ? MICROPY_PY_BUILTINS_CODE_FULL : (MICROPY_PY_FUNCTION_ATTRS_CODE ? MICROPY_PY_BUILTINS_CODE_BASIC : (MICROPY_PY_BUILTINS_COMPILE ? MICROPY_PY_BUILTINS_CODE_MINIMUM : MICROPY_PY_BUILTINS_CODE_NONE))) #endif // Whether to support dict.fromkeys() class method diff --git a/py/objfun.c b/py/objfun.c index 0b1b8c115f236..a742c5267254c 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -28,6 +28,8 @@ #include #include +#include "py/emitglue.h" +#include "py/objcode.h" #include "py/objtuple.h" #include "py/objfun.h" #include "py/runtime.h" @@ -151,6 +153,30 @@ qstr mp_obj_fun_get_name(mp_const_obj_t fun_in) { return name; } +#if MICROPY_PY_FUNCTION_ATTRS_CODE +static mp_obj_t fun_bc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type; + mp_arg_check_num(n_args, n_kw, 2, 2, false); + + if (!mp_obj_is_type(args[0], &mp_type_code)) { + mp_raise_TypeError(NULL); + } + if (!mp_obj_is_type(args[1], &mp_type_dict)) { + mp_raise_TypeError(NULL); + } + + mp_obj_code_t *code = MP_OBJ_TO_PTR(args[0]); + mp_obj_t globals = args[1]; + + mp_module_context_t *module_context = m_new_obj(mp_module_context_t); + module_context->module.base.type = &mp_type_module; + module_context->module.globals = MP_OBJ_TO_PTR(globals); + module_context->constants = *mp_code_get_constants(code); + + return mp_make_function_from_proto_fun(mp_code_get_proto_fun(code), module_context, NULL); +} +#endif + #if MICROPY_CPYTHON_COMPAT static void fun_bc_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; @@ -340,9 +366,29 @@ void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); dest[0] = MP_OBJ_FROM_PTR(self->context->module.globals); } + #if MICROPY_PY_FUNCTION_ATTRS_CODE + if (attr == MP_QSTR___code__) { + const mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); + if ((self->base.type == &mp_type_fun_bc + || self->base.type == &mp_type_gen_wrap) + && self->child_table == NULL) { + #if MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_BASIC + dest[0] = mp_obj_new_code(self->context->constants, self->bytecode); + #else + dest[0] = mp_obj_new_code(self->context, self->rc, true); + #endif + } + } + #endif } #endif +#if MICROPY_PY_FUNCTION_ATTRS_CODE +#define FUN_BC_MAKE_NEW make_new, fun_bc_make_new, +#else +#define FUN_BC_MAKE_NEW +#endif + #if MICROPY_CPYTHON_COMPAT #define FUN_BC_TYPE_PRINT print, fun_bc_print, #else @@ -359,6 +405,7 @@ MP_DEFINE_CONST_OBJ_TYPE( mp_type_fun_bc, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF, + FUN_BC_MAKE_NEW FUN_BC_TYPE_PRINT FUN_BC_TYPE_ATTR call, fun_bc_call diff --git a/tests/basics/fun_code.py b/tests/basics/fun_code.py new file mode 100644 index 0000000000000..59e1f7ec0483d --- /dev/null +++ b/tests/basics/fun_code.py @@ -0,0 +1,36 @@ +# Test function.__code__ attribute. + +try: + (lambda: 0).__code__ +except AttributeError: + print("SKIP") + raise SystemExit + + +def f(): + return a + + +ftype = type(f) + +# Test __code__ access and function constructor. +code = f.__code__ +print(type(ftype(code, {})) is ftype) + +# Test instantiating multiple code's with different globals dicts. +code = f.__code__ +f1 = ftype(code, {"a": 1}) +f2 = ftype(code, {"a": 2}) +print(f1(), f2()) + +# Test bad first argument type. +try: + ftype(None, {}) +except TypeError: + print("TypeError") + +# Test bad second argument type. +try: + ftype(f.__code__, None) +except TypeError: + print("TypeError") diff --git a/tests/basics/fun_code_micropython.py b/tests/basics/fun_code_micropython.py new file mode 100644 index 0000000000000..2c319a2db8c75 --- /dev/null +++ b/tests/basics/fun_code_micropython.py @@ -0,0 +1,19 @@ +# Test MicroPython-specific restrictions of function.__code__ attribute. + +try: + (lambda: 0).__code__ +except AttributeError: + print("SKIP") + raise SystemExit + + +def f_with_children(): + def g(): + pass + + +# Can't access __code__ when function has children. +try: + f_with_children.__code__ +except AttributeError: + print("AttributeError") diff --git a/tests/basics/fun_code_micropython.py.exp b/tests/basics/fun_code_micropython.py.exp new file mode 100644 index 0000000000000..d169edffb4cfb --- /dev/null +++ b/tests/basics/fun_code_micropython.py.exp @@ -0,0 +1 @@ +AttributeError diff --git a/tests/basics/subclass_native1.py b/tests/basics/subclass_native1.py index 288a686d1a756..74b377eac91b9 100644 --- a/tests/basics/subclass_native1.py +++ b/tests/basics/subclass_native1.py @@ -21,11 +21,9 @@ class mylist(list): # TODO: Faults #print(a + a) -def foo(): - print("hello from foo") - +# subclassing a type that doesn't have make_new at the C level (not allowed) try: - class myfunc(type(foo)): + class myfunc(type([].append)): pass except TypeError: print("TypeError") diff --git a/tests/micropython/native_fun_attrs_code.py b/tests/micropython/native_fun_attrs_code.py new file mode 100644 index 0000000000000..d277a7b9b2670 --- /dev/null +++ b/tests/micropython/native_fun_attrs_code.py @@ -0,0 +1,21 @@ +# Test MicroPython-specific restrictions of function.__code__ attribute. + +try: + (lambda: 0).__code__ +except AttributeError: + print("SKIP") + raise SystemExit + +import micropython + + +@micropython.native +def f_native(): + pass + + +# Can't access __code__ when function is native code. +try: + f_native.__code__ +except AttributeError: + print("AttributeError") diff --git a/tests/micropython/native_fun_attrs_code.py.exp b/tests/micropython/native_fun_attrs_code.py.exp new file mode 100644 index 0000000000000..d169edffb4cfb --- /dev/null +++ b/tests/micropython/native_fun_attrs_code.py.exp @@ -0,0 +1 @@ +AttributeError From a11ba7775e600b45c0e93443ca05dffb09a49389 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 17 Jul 2024 16:06:12 +1000 Subject: [PATCH 293/546] py/persistentcode: Add mp_raw_code_save_fun_to_bytes. Serialises a bytecode function/generator to a valid .mpy as bytes. Signed-off-by: Damien George --- py/mpconfig.h | 5 ++ py/persistentcode.c | 190 +++++++++++++++++++++++++++++++++++++++++++- py/persistentcode.h | 1 + 3 files changed, 194 insertions(+), 2 deletions(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 5c4d19bb56f11..a25d8cd32a2ba 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -342,6 +342,11 @@ #define MICROPY_PERSISTENT_CODE_SAVE_FILE (0) #endif +// Whether to support converting functions to persistent code (bytes) +#ifndef MICROPY_PERSISTENT_CODE_SAVE_FUN +#define MICROPY_PERSISTENT_CODE_SAVE_FUN (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + // Whether generated code can persist independently of the VM/runtime instance // This is enabled automatically when needed by other features #ifndef MICROPY_PERSISTENT_CODE diff --git a/py/persistentcode.c b/py/persistentcode.c index 840ee49d3e4eb..2a42b904bc138 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -526,7 +526,7 @@ void mp_raw_code_load_file(qstr filename, mp_compiled_module_t *context) { #endif // MICROPY_PERSISTENT_CODE_LOAD -#if MICROPY_PERSISTENT_CODE_SAVE +#if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_PERSISTENT_CODE_SAVE_FUN #include "py/objstr.h" @@ -624,6 +624,10 @@ static void save_obj(mp_print_t *print, mp_obj_t o) { } } +#endif // MICROPY_PERSISTENT_CODE_SAVE || MICROPY_PERSISTENT_CODE_SAVE_FUN + +#if MICROPY_PERSISTENT_CODE_SAVE + static void save_raw_code(mp_print_t *print, const mp_raw_code_t *rc) { // Save function kind and data length mp_print_uint(print, (rc->fun_data_len << 3) | ((rc->n_children != 0) << 2) | (rc->kind - MP_CODE_BYTECODE)); @@ -693,6 +697,8 @@ void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) { save_raw_code(print, cm->rc); } +#endif // MICROPY_PERSISTENT_CODE_SAVE + #if MICROPY_PERSISTENT_CODE_SAVE_FILE #include @@ -723,4 +729,184 @@ void mp_raw_code_save_file(mp_compiled_module_t *cm, qstr filename) { #endif // MICROPY_PERSISTENT_CODE_SAVE_FILE -#endif // MICROPY_PERSISTENT_CODE_SAVE +#if MICROPY_PERSISTENT_CODE_SAVE_FUN + +#include "py/bc0.h" +#include "py/objfun.h" +#include "py/smallint.h" +#include "py/gc.h" + +#define MP_BC_OPCODE_HAS_SIGNED_OFFSET(opcode) (MP_BC_UNWIND_JUMP <= (opcode) && (opcode) <= MP_BC_POP_JUMP_IF_FALSE) + +typedef struct _bit_vector_t { + size_t max_bit_set; + size_t alloc; + uintptr_t *bits; +} bit_vector_t; + +static void bit_vector_init(bit_vector_t *self) { + self->max_bit_set = 0; + self->alloc = 1; + self->bits = m_new(uintptr_t, self->alloc); +} + +static void bit_vector_clear(bit_vector_t *self) { + m_del(uintptr_t, self->bits, self->alloc); +} + +static bool bit_vector_is_set(bit_vector_t *self, size_t index) { + const size_t bits_size = sizeof(*self->bits) * MP_BITS_PER_BYTE; + return index / bits_size < self->alloc + && (self->bits[index / bits_size] & (1 << (index % bits_size))) != 0; +} + +static void bit_vector_set(bit_vector_t *self, size_t index) { + const size_t bits_size = sizeof(*self->bits) * MP_BITS_PER_BYTE; + self->max_bit_set = MAX(self->max_bit_set, index); + if (index / bits_size >= self->alloc) { + size_t new_alloc = self->alloc * 2; + self->bits = m_renew(uintptr_t, self->bits, self->alloc, new_alloc); + self->alloc = new_alloc; + } + self->bits[index / bits_size] |= 1 << (index % bits_size); +} + +typedef struct _mp_opcode_t { + uint8_t opcode; + uint8_t format; + uint8_t size; + mp_int_t arg; + uint8_t extra_arg; +} mp_opcode_t; + +static mp_opcode_t mp_opcode_decode(const uint8_t *ip) { + const uint8_t *ip_start = ip; + uint8_t opcode = *ip++; + uint8_t opcode_format = MP_BC_FORMAT(opcode); + mp_uint_t arg = 0; + uint8_t extra_arg = 0; + if (opcode_format == MP_BC_FORMAT_QSTR || opcode_format == MP_BC_FORMAT_VAR_UINT) { + arg = *ip & 0x7f; + if (opcode == MP_BC_LOAD_CONST_SMALL_INT && (arg & 0x40) != 0) { + arg |= (mp_uint_t)(-1) << 7; + } + while ((*ip & 0x80) != 0) { + arg = (arg << 7) | (*++ip & 0x7f); + } + ++ip; + } else if (opcode_format == MP_BC_FORMAT_OFFSET) { + if ((*ip & 0x80) == 0) { + arg = *ip++; + if (MP_BC_OPCODE_HAS_SIGNED_OFFSET(opcode)) { + arg -= 0x40; + } + } else { + arg = (ip[0] & 0x7f) | (ip[1] << 7); + ip += 2; + if (MP_BC_OPCODE_HAS_SIGNED_OFFSET(opcode)) { + arg -= 0x4000; + } + } + } + if ((opcode & MP_BC_MASK_EXTRA_BYTE) == 0) { + extra_arg = *ip++; + } + + mp_opcode_t op = { opcode, opcode_format, ip - ip_start, arg, extra_arg }; + return op; +} + +mp_obj_t mp_raw_code_save_fun_to_bytes(const mp_module_constants_t *consts, const uint8_t *bytecode) { + const uint8_t *fun_data = bytecode; + const uint8_t *fun_data_top = fun_data + gc_nbytes(fun_data); + + // Extract function information. + const byte *ip = fun_data; + MP_BC_PRELUDE_SIG_DECODE(ip); + MP_BC_PRELUDE_SIZE_DECODE(ip); + + // Track the qstrs used by the function. + bit_vector_t qstr_table_used; + bit_vector_init(&qstr_table_used); + + // Track the objects used by the function. + bit_vector_t obj_table_used; + bit_vector_init(&obj_table_used); + + const byte *ip_names = ip; + mp_uint_t simple_name = mp_decode_uint(&ip_names); + bit_vector_set(&qstr_table_used, simple_name); + for (size_t i = 0; i < n_pos_args + n_kwonly_args; ++i) { + mp_uint_t arg_name = mp_decode_uint(&ip_names); + bit_vector_set(&qstr_table_used, arg_name); + } + + // Skip pass source code info and cell info. + // Then ip points to the start of the opcodes. + ip += n_info + n_cell; + + // Decode bytecode. + while (ip < fun_data_top) { + mp_opcode_t op = mp_opcode_decode(ip); + if (op.opcode == MP_BC_BASE_RESERVED) { + // End of opcodes. + fun_data_top = ip; + } else if (op.opcode == MP_BC_LOAD_CONST_OBJ) { + bit_vector_set(&obj_table_used, op.arg); + } else if (op.format == MP_BC_FORMAT_QSTR) { + bit_vector_set(&qstr_table_used, op.arg); + } + ip += op.size; + } + + mp_uint_t fun_data_len = fun_data_top - fun_data; + + mp_print_t print; + vstr_t vstr; + vstr_init_print(&vstr, 64, &print); + + // Start with .mpy header. + const uint8_t header[4] = { 'M', MPY_VERSION, 0, MP_SMALL_INT_BITS }; + mp_print_bytes(&print, header, sizeof(header)); + + // Number of entries in constant table. + mp_print_uint(&print, qstr_table_used.max_bit_set + 1); + mp_print_uint(&print, obj_table_used.max_bit_set + 1); + + // Save qstrs. + for (size_t i = 0; i <= qstr_table_used.max_bit_set; ++i) { + if (bit_vector_is_set(&qstr_table_used, i)) { + save_qstr(&print, consts->qstr_table[i]); + } else { + save_qstr(&print, MP_QSTR_); + } + } + + // Save constant objects. + for (size_t i = 0; i <= obj_table_used.max_bit_set; ++i) { + if (bit_vector_is_set(&obj_table_used, i)) { + save_obj(&print, consts->obj_table[i]); + } else { + save_obj(&print, mp_const_none); + } + } + + bit_vector_clear(&qstr_table_used); + bit_vector_clear(&obj_table_used); + + // Save function kind and data length. + mp_print_uint(&print, fun_data_len << 3); + + // Save function code. + mp_print_bytes(&print, fun_data, fun_data_len); + + // Create and return bytes representing the .mpy data. + return mp_obj_new_bytes_from_vstr(&vstr); +} + +#endif // MICROPY_PERSISTENT_CODE_SAVE_FUN + +#if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE +// An mp_obj_list_t that tracks relocated native code to prevent the GC from reclaiming them. +MP_REGISTER_ROOT_POINTER(mp_obj_t track_reloc_code_list); +#endif diff --git a/py/persistentcode.h b/py/persistentcode.h index f0b7f70f7d7fa..cf257a7ab1fb6 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -121,6 +121,7 @@ void mp_raw_code_load_file(qstr filename, mp_compiled_module_t *ctx); void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print); void mp_raw_code_save_file(mp_compiled_module_t *cm, qstr filename); +mp_obj_t mp_raw_code_save_fun_to_bytes(const mp_module_constants_t *consts, const uint8_t *bytecode); void mp_native_relocate(void *reloc, uint8_t *text, uintptr_t reloc_text); From c3a18d74ebebe1c68955c3dce3c782af949aa4c7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 20 Jan 2025 22:24:10 +1100 Subject: [PATCH 294/546] extmod/modmarshal: Add new marshal module. This commit implements a small subset of the CPython `marshal` module. It implements `marshal.dumps()` and `marshal.loads()`, but only supports (un)marshalling code objects at this stage. The semantics match CPython, except that the actual marshalled bytes is not compatible with CPython's marshalled bytes. The module is enabled at the everything level (only on the unix coverage build at this stage). Signed-off-by: Damien George --- extmod/extmod.cmake | 1 + extmod/extmod.mk | 1 + extmod/modmarshal.c | 88 ++++++++++++++++++ ports/windows/msvc/sources.props | 1 + py/mpconfig.h | 7 +- tests/extmod/marshal_basic.py | 38 ++++++++ tests/extmod/marshal_micropython.py | 21 +++++ tests/extmod/marshal_stress.py | 122 +++++++++++++++++++++++++ tests/ports/unix/extra_coverage.py.exp | 10 +- 9 files changed, 283 insertions(+), 6 deletions(-) create mode 100644 extmod/modmarshal.c create mode 100644 tests/extmod/marshal_basic.py create mode 100644 tests/extmod/marshal_micropython.py create mode 100644 tests/extmod/marshal_stress.py diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index 532ce83f9075f..3643f1aee7bf5 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -24,6 +24,7 @@ set(MICROPY_SOURCE_EXTMOD ${MICROPY_EXTMOD_DIR}/modframebuf.c ${MICROPY_EXTMOD_DIR}/modlwip.c ${MICROPY_EXTMOD_DIR}/modmachine.c + ${MICROPY_EXTMOD_DIR}/modmarshal.c ${MICROPY_EXTMOD_DIR}/modnetwork.c ${MICROPY_EXTMOD_DIR}/modonewire.c ${MICROPY_EXTMOD_DIR}/modasyncio.c diff --git a/extmod/extmod.mk b/extmod/extmod.mk index 6d54ae2222e74..a510f3c54c25c 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -29,6 +29,7 @@ SRC_EXTMOD_C += \ extmod/modjson.c \ extmod/modlwip.c \ extmod/modmachine.c \ + extmod/modmarshal.c \ extmod/modnetwork.c \ extmod/modonewire.c \ extmod/modopenamp.c \ diff --git a/extmod/modmarshal.c b/extmod/modmarshal.c new file mode 100644 index 0000000000000..93d2bcf115065 --- /dev/null +++ b/extmod/modmarshal.c @@ -0,0 +1,88 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "py/objcode.h" +#include "py/objfun.h" +#include "py/persistentcode.h" +#include "py/runtime.h" + +#if MICROPY_PY_MARSHAL + +static mp_obj_t marshal_dumps(mp_obj_t value_in) { + if (mp_obj_is_type(value_in, &mp_type_code)) { + mp_obj_code_t *code = MP_OBJ_TO_PTR(value_in); + const void *proto_fun = mp_code_get_proto_fun(code); + const uint8_t *bytecode; + if (mp_proto_fun_is_bytecode(proto_fun)) { + bytecode = proto_fun; + } else { + const mp_raw_code_t *rc = proto_fun; + if (!(rc->kind == MP_CODE_BYTECODE && rc->children == NULL)) { + mp_raise_ValueError(MP_ERROR_TEXT("function must be bytecode with no children")); + } + bytecode = rc->fun_data; + } + return mp_raw_code_save_fun_to_bytes(mp_code_get_constants(code), bytecode); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("unmarshallable object")); + } +} +static MP_DEFINE_CONST_FUN_OBJ_1(marshal_dumps_obj, marshal_dumps); + +static mp_obj_t marshal_loads(mp_obj_t data_in) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ); + mp_module_context_t ctx; + ctx.module.globals = mp_globals_get(); + mp_compiled_module_t cm = { .context = &ctx }; + mp_raw_code_load_mem(bufinfo.buf, bufinfo.len, &cm); + #if MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_BASIC + return mp_obj_new_code(ctx.constants, cm.rc); + #else + mp_module_context_t *ctx_ptr = m_new_obj(mp_module_context_t); + *ctx_ptr = ctx; + return mp_obj_new_code(ctx_ptr, cm.rc, true); + #endif +} +static MP_DEFINE_CONST_FUN_OBJ_1(marshal_loads_obj, marshal_loads); + +static const mp_rom_map_elem_t mod_marshal_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_marshal) }, + { MP_ROM_QSTR(MP_QSTR_dumps), MP_ROM_PTR(&marshal_dumps_obj) }, + { MP_ROM_QSTR(MP_QSTR_loads), MP_ROM_PTR(&marshal_loads_obj) }, +}; + +static MP_DEFINE_CONST_DICT(mod_marshal_globals, mod_marshal_globals_table); + +const mp_obj_module_t mp_module_marshal = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mod_marshal_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_marshal, mp_module_marshal); + +#endif // MICROPY_PY_MARSHAL diff --git a/ports/windows/msvc/sources.props b/ports/windows/msvc/sources.props index f7c4c6bcac01b..dcd10ddee913c 100644 --- a/ports/windows/msvc/sources.props +++ b/ports/windows/msvc/sources.props @@ -15,6 +15,7 @@ + diff --git a/py/mpconfig.h b/py/mpconfig.h index a25d8cd32a2ba..66b3d125e78de 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -344,7 +344,7 @@ // Whether to support converting functions to persistent code (bytes) #ifndef MICROPY_PERSISTENT_CODE_SAVE_FUN -#define MICROPY_PERSISTENT_CODE_SAVE_FUN (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#define MICROPY_PERSISTENT_CODE_SAVE_FUN (MICROPY_PY_MARSHAL) #endif // Whether generated code can persist independently of the VM/runtime instance @@ -1382,6 +1382,11 @@ typedef double mp_float_t; #define MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif +// Whether to provide "marshal" module +#ifndef MICROPY_PY_MARSHAL +#define MICROPY_PY_MARSHAL (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + // Whether to provide "math" module #ifndef MICROPY_PY_MATH #define MICROPY_PY_MATH (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) diff --git a/tests/extmod/marshal_basic.py b/tests/extmod/marshal_basic.py new file mode 100644 index 0000000000000..9e7b70be4829d --- /dev/null +++ b/tests/extmod/marshal_basic.py @@ -0,0 +1,38 @@ +# Test the marshal module, basic functionality. + +try: + import marshal + + (lambda: 0).__code__ +except (AttributeError, ImportError): + print("SKIP") + raise SystemExit + +ftype = type(lambda: 0) + +# Test basic dumps and loads. +print(ftype(marshal.loads(marshal.dumps((lambda: a).__code__)), {"a": 4})()) + +# Test dumps of a result from compile(). +ftype(marshal.loads(marshal.dumps(compile("print(a)", "", "exec"))), {"print": print, "a": 5})() + +# Test marshalling a function with arguments. +print(ftype(marshal.loads(marshal.dumps((lambda x, y: x + y).__code__)), {})(1, 2)) + +# Test marshalling a function with default arguments. +print(ftype(marshal.loads(marshal.dumps((lambda x=0: x).__code__)), {})("arg")) + +# Test marshalling a function containing constant objects (a tuple). +print(ftype(marshal.loads(marshal.dumps((lambda: (None, ...)).__code__)), {})()) + +# Test instantiating multiple code's with different globals dicts. +code = marshal.loads(marshal.dumps((lambda: a).__code__)) +f1 = ftype(code, {"a": 1}) +f2 = ftype(code, {"a": 2}) +print(f1(), f2()) + +# Test unmarshallable object. +try: + marshal.dumps(type) +except ValueError: + print("ValueError") diff --git a/tests/extmod/marshal_micropython.py b/tests/extmod/marshal_micropython.py new file mode 100644 index 0000000000000..213b3bf31895d --- /dev/null +++ b/tests/extmod/marshal_micropython.py @@ -0,0 +1,21 @@ +# Test the marshal module, MicroPython-specific functionality. + +try: + import marshal +except ImportError: + print("SKIP") + raise SystemExit + +import unittest + + +class Test(unittest.TestCase): + def test_function_with_children(self): + # Can't marshal a function with children (in this case the module has a child function f). + code = compile("def f(): pass", "", "exec") + with self.assertRaises(ValueError): + marshal.dumps(code) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/extmod/marshal_stress.py b/tests/extmod/marshal_stress.py new file mode 100644 index 0000000000000..b52475c0361dc --- /dev/null +++ b/tests/extmod/marshal_stress.py @@ -0,0 +1,122 @@ +# Test the marshal module, stressing edge cases. + +try: + import marshal + + (lambda: 0).__code__ +except (AttributeError, ImportError): + print("SKIP") + raise SystemExit + +ftype = type(lambda: 0) + +# Test a large function. + + +def large_function(arg0, arg1, arg2, arg3): + # Arguments. + print(arg0, arg1, arg2, arg3) + + # Positive medium-sized integer (still a small-int though). + print(1234) + + # Negative small-ish integer. + print(-20) + + # More than 64 constant objects. + x = (0,) + x = (1,) + x = (2,) + x = (3,) + x = (4,) + x = (5,) + x = (6,) + x = (7,) + x = (8,) + x = (9,) + x = (10,) + x = (11,) + x = (12,) + x = (13,) + x = (14,) + x = (15,) + x = (16,) + x = (17,) + x = (18,) + x = (19,) + x = (20,) + x = (21,) + x = (22,) + x = (23,) + x = (24,) + x = (25,) + x = (26,) + x = (27,) + x = (28,) + x = (29,) + x = (30,) + x = (31,) + x = (32,) + x = (33,) + x = (34,) + x = (35,) + x = (36,) + x = (37,) + x = (38,) + x = (39,) + x = (40,) + x = (41,) + x = (42,) + x = (43,) + x = (44,) + x = (45,) + x = (46,) + x = (47,) + x = (48,) + x = (49,) + x = (50,) + x = (51,) + x = (52,) + x = (53,) + x = (54,) + x = (55,) + x = (56,) + x = (57,) + x = (58,) + x = (59,) + x = (60,) + x = (61,) + x = (62,) + x = (63,) + x = (64,) + + # Small jump. + x = 0 + while x < 2: + print("loop", x) + x += 1 + + # Large jump. + x = 0 + while x < 2: + try: + try: + try: + print + except Exception as e: + print + finally: + print + except Exception as e: + print + finally: + print + except Exception as e: + print + finally: + print("loop", x) + x += 1 + + +code = marshal.dumps(large_function.__code__) +ftype(marshal.loads(code), {"print": print})(0, 1, 2, 3) diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp index 176db8e9f8479..5ff947e883d37 100644 --- a/tests/ports/unix/extra_coverage.py.exp +++ b/tests/ports/unix/extra_coverage.py.exp @@ -56,13 +56,13 @@ cmath collections cppexample cryptolib deflate errno example_package ffi framebuf gc hashlib heapq io json machine -math os platform random -re select socket struct -sys termios time tls -uctypes vfs websocket +marshal math os platform +random re select socket +struct sys termios time +tls uctypes vfs websocket me -micropython machine math +micropython machine marshal math argv atexit byteorder exc_info executable exit getsizeof implementation From e40a3fdb81fc7184e945754c06d42e169a296ab8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 22 Jan 2025 15:13:13 +1100 Subject: [PATCH 295/546] docs/library/marshal: Document the marshal module. Signed-off-by: Damien George --- docs/library/index.rst | 1 + docs/library/marshal.rst | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 docs/library/marshal.rst diff --git a/docs/library/index.rst b/docs/library/index.rst index 4209a0781a60e..2919378ce13b2 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -69,6 +69,7 @@ library. heapq.rst io.rst json.rst + marshal.rst math.rst os.rst platform.rst diff --git a/docs/library/marshal.rst b/docs/library/marshal.rst new file mode 100644 index 0000000000000..6d3213e0a6ec8 --- /dev/null +++ b/docs/library/marshal.rst @@ -0,0 +1,28 @@ +:mod:`marshal` -- Python object serialization +============================================= + +.. module:: marshal + :synopsis: Convert Python objects to and from a binary format + +|see_cpython_module| :mod:`python:marshal`. + +This module implements conversion between Python objects and a binary format. +The format is specific to MicroPython but does not depend on the machine +architecture, so the data can be transferred and used on a different MicroPython +instance, as long as the version of the binary data matches (it's currently +versioned as the mpy file version, see :ref:`mpy_files`). + +Functions +--------- + +.. function:: dumps(value, /) + + Convert the given *value* to binary format and return a corresponding ``bytes`` + object. + + Currently, code objects are the only supported values that can be converted. + +.. function:: loads(data, /) + + Convert the given bytes-like *data* to its corresponding Python object, and + return it. From 30acb16ad34babf25d126a7b471616421167cb35 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 3 Jan 2025 12:02:12 +1100 Subject: [PATCH 296/546] extmod/vfs_rom: Remove ability to create VfsRom from an address. It's not necessary to support this, which allows an arbitrary memory address to be specified and potentially allows invalid memory accesses. Requiring an object with the buffer protocol is safer, and also means that the length of the region is always specified. Signed-off-by: Damien George --- extmod/vfs_rom.c | 12 +++++------- tests/extmod/vfs_rom.py | 3 ++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/extmod/vfs_rom.c b/extmod/vfs_rom.c index b8b6f8e40d12b..99ddaba95ed79 100644 --- a/extmod/vfs_rom.c +++ b/extmod/vfs_rom.c @@ -198,15 +198,13 @@ static mp_obj_t vfs_rom_make_new(const mp_obj_type_t *type, size_t n_args, size_ self->base.type = type; self->memory = args[0]; + // Get the ROMFS memory region. mp_buffer_info_t bufinfo; - if (mp_get_buffer(self->memory, &bufinfo, MP_BUFFER_READ)) { - if (bufinfo.len < ROMFS_SIZE_MIN) { - mp_raise_OSError(MP_ENODEV); - } - self->filesystem = bufinfo.buf; - } else { - self->filesystem = (const uint8_t *)(uintptr_t)mp_obj_get_int_truncated(self->memory); + mp_get_buffer_raise(self->memory, &bufinfo, MP_BUFFER_READ); + if (bufinfo.len < ROMFS_SIZE_MIN) { + mp_raise_OSError(MP_ENODEV); } + self->filesystem = bufinfo.buf; // Verify it is a ROMFS. if (!(self->filesystem[0] == ROMFS_HEADER_BYTE0 diff --git a/tests/extmod/vfs_rom.py b/tests/extmod/vfs_rom.py index d416d83870558..f7958a93962d0 100644 --- a/tests/extmod/vfs_rom.py +++ b/tests/extmod/vfs_rom.py @@ -226,7 +226,8 @@ def test_unknown_record(self): class TestStandalone(TestBase): def test_constructor(self): self.assertIsInstance(vfs.VfsRom(self.romfs), vfs.VfsRom) - self.assertIsInstance(vfs.VfsRom(self.romfs_addr), vfs.VfsRom) + with self.assertRaises(TypeError): + vfs.VfsRom(self.romfs_addr) def test_mount(self): vfs.VfsRom(self.romfs).mount(True, False) From 1a67d720c786fd85c848ffba4b792048d262095f Mon Sep 17 00:00:00 2001 From: rufusclark <50201718+rufusclark@users.noreply.github.com> Date: Tue, 6 Aug 2024 00:32:35 +0100 Subject: [PATCH 297/546] tools/pyboard.py: Make get_time use machine.RTC instead of pyb.RTC. The current code evaluates `pyb.RTC().datetime()` resulting in a remote side exception, as `pyb` is not defined on most ports (only stm32). The code should evaluate `machine.RTC().datetime()` and hence return the current time. Signed-off-by: rufusclark <50201718+rufusclark@users.noreply.github.com> Signed-off-by: Damien George --- tools/pyboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pyboard.py b/tools/pyboard.py index dac51e7d6b896..0cf5b3d46596a 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -506,7 +506,7 @@ def execfile(self, filename): return self.exec_(pyfile) def get_time(self): - t = str(self.eval("pyb.RTC().datetime()"), encoding="utf8")[1:-1].split(", ") + t = str(self.eval("machine.RTC().datetime()"), encoding="utf8")[1:-1].split(", ") return int(t[4]) * 3600 + int(t[5]) * 60 + int(t[6]) def fs_exists(self, src): From b675c87992b8b62ca8b18a05862e9c7b9427b9de Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 5 Feb 2025 15:08:25 +1100 Subject: [PATCH 298/546] esp32/machine_sdcard: Fix invalid result of SDCard.read/writeblocks. Functions would return NULL instead of `mp_const_false` if failed to init. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/machine_sdcard.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/esp32/machine_sdcard.c b/ports/esp32/machine_sdcard.c index 92c6e64698c33..da6b0df282caf 100644 --- a/ports/esp32/machine_sdcard.c +++ b/ports/esp32/machine_sdcard.c @@ -364,7 +364,7 @@ static mp_obj_t machine_sdcard_readblocks(mp_obj_t self_in, mp_obj_t block_num, err = sdcard_ensure_card_init((sdcard_card_obj_t *)self, false); if (err != ESP_OK) { - return false; + return mp_const_false; } mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); @@ -381,7 +381,7 @@ static mp_obj_t machine_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t block_num, err = sdcard_ensure_card_init((sdcard_card_obj_t *)self, false); if (err != ESP_OK) { - return false; + return mp_const_false; } mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); From 842e3617a0f152f837b2e8cc4e9d4efae6cb52a4 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 11 Dec 2024 13:17:47 +0100 Subject: [PATCH 299/546] renesas-ra/Makefile: Remove id_code section from binary file generation. The linker scripts for most of these microcontrollers contain a non-contiguous flash section for the ID code that results in big binary files, which exceed the flash size. This commit removes the ID code section from the main firmware binary, and outputs it to a separate binary, which can be deployed manually if ID code is enabled. Signed-off-by: iabdalkader --- ports/renesas-ra/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ports/renesas-ra/Makefile b/ports/renesas-ra/Makefile index dd70f56d7ca63..03eb9b52b41b8 100644 --- a/ports/renesas-ra/Makefile +++ b/ports/renesas-ra/Makefile @@ -442,7 +442,8 @@ endef define GENERATE_BIN $(ECHO) "GEN $(1)" - $(Q)$(OBJCOPY) -I ihex -O binary $(2) $(1) + $(Q)$(OBJCOPY) -O binary -j .id_code $(2) $(BUILD)/id_code.bin + $(Q)$(OBJCOPY) -O binary --remove-section=.id_code $(2) $(1) endef define GENERATE_HEX @@ -452,7 +453,7 @@ endef .PHONY: -$(BUILD)/firmware.bin: $(BUILD)/firmware.hex +$(BUILD)/firmware.bin: $(BUILD)/firmware.elf $(call GENERATE_BIN,$@,$^) $(BUILD)/firmware.hex: $(BUILD)/firmware.elf From aef6705a321fbefb06288b5be1f5931bf8c42fe3 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Wed, 22 Jan 2025 17:17:03 -0600 Subject: [PATCH 300/546] extmod/lwip-include: Increase number of lwIP timers when mDNS enabled. Despite the code comments claiming one is sufficient, the mDNS application is capable of using up to twelve timers. Three per IP protocol are started at once in `mdns_start_multicast_timeouts_ipvX`, then another two per protocol can be started in `mdns_handle_question`. Further timers can be started for two additional callbacks. Having certain timers, such as `MDNS_MULTICAST_TIMEOUT`, fail to start due to none being free will break mDNS forever as the app will never realize it's safe to transmit a packet. Therefore, this commit goes somewhat overkill and allocates the maximal amount of timers; it's uncertain if all can run simultaneously, or how many callback timers are needed. Each timer struct is 16 bytes on standard 32 bit builds. Plus, say, 8 bytes of allocater overhead, that's 288 more bytes of RAM used which shouldn't be too horrible. Users who don't need mDNS can manually disable it to recover the RAM if necessary. This fixes mDNS on W5500_EVB_PICO (among other boards). Before, mDNS would work for a bit after connection until the host's cache expired a minute or two later. Then the board would never respond to further queries. With this patch, all works well. Signed-off-by: Thomas Watson --- extmod/lwip-include/lwipopts_common.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extmod/lwip-include/lwipopts_common.h b/extmod/lwip-include/lwipopts_common.h index 8688db064ef6f..3e4230909499e 100644 --- a/extmod/lwip-include/lwipopts_common.h +++ b/extmod/lwip-include/lwipopts_common.h @@ -65,7 +65,9 @@ #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) + +// The mDNS responder requires 5 timers per IP version plus 2 others. Not having enough silently breaks it. +#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + (LWIP_MDNS_RESPONDER * (2 + (5 * (LWIP_IPV4 + LWIP_IPV6))))) #define SO_REUSE 1 #define TCP_LISTEN_BACKLOG 1 From 321b30ca564bb33c625292247d00f7dd29dc9559 Mon Sep 17 00:00:00 2001 From: Keenan Johnson Date: Thu, 13 Feb 2025 13:11:38 -0800 Subject: [PATCH 301/546] extmod/modtls_mbedtls: Wire in support for DTLS. This commit enables support for DTLS, i.e. TLS over datagram transport protocols like UDP. While support for DTLS is absent in CPython, it is worth supporting it in MicroPython because it is the basis of the ubiquitous CoAP protocol, used in many IoT projects. To select DTLS, a new set of "protocols" are added to SSLContext: - ssl.PROTOCOL_DTLS_CLIENT - ssl.PROTOCOL_DTLS_SERVER If one of these is set, the library assumes that the underlying socket is a datagram-like socket (i.e. UDP or similar). Our own timer callbacks are implemented because the out of the box implementation relies on `gettimeofday()`. This new DTLS feature is enabled on all ports that use mbedTLS. This commit is an update to a previous PR #10062. Addresses issue #5270 which requested DTLS support. Signed-off-by: Keenan Johnson --- docs/library/ssl.rst | 21 +++++++ extmod/mbedtls/mbedtls_config_common.h | 1 + extmod/modtls_mbedtls.c | 76 ++++++++++++++++++++++++-- ports/esp32/boards/sdkconfig.base | 3 + tests/extmod/tls_dtls.py | 51 +++++++++++++++++ tests/extmod/tls_dtls.py.exp | 3 + 6 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 tests/extmod/tls_dtls.py create mode 100644 tests/extmod/tls_dtls.py.exp diff --git a/docs/library/ssl.rst b/docs/library/ssl.rst index dff90b8da58b9..4327c74bad6c8 100644 --- a/docs/library/ssl.rst +++ b/docs/library/ssl.rst @@ -117,11 +117,32 @@ Exceptions This exception does NOT exist. Instead its base class, OSError, is used. +DTLS support +------------ + +.. admonition:: Difference to CPython + :class: attention + + This is a MicroPython extension. + +This module supports DTLS in client and server mode via the `PROTOCOL_DTLS_CLIENT` +and `PROTOCOL_DTLS_SERVER` constants that can be used as the ``protocol`` argument +of `SSLContext`. + +In this case the underlying socket is expected to behave as a datagram socket (i.e. +like the socket opened with ``socket.socket`` with ``socket.AF_INET`` as ``af`` and +``socket.SOCK_DGRAM`` as ``type``). + +DTLS is only supported on ports that use mbed TLS, and it is not enabled by default: +it requires enabling ``MBEDTLS_SSL_PROTO_DTLS`` in the specific port configuration. + Constants --------- .. data:: ssl.PROTOCOL_TLS_CLIENT ssl.PROTOCOL_TLS_SERVER + ssl.PROTOCOL_DTLS_CLIENT (when DTLS support is enabled) + ssl.PROTOCOL_DTLS_SERVER (when DTLS support is enabled) Supported values for the *protocol* parameter. diff --git a/extmod/mbedtls/mbedtls_config_common.h b/extmod/mbedtls/mbedtls_config_common.h index 6ea8540af9925..6cd14befc3196 100644 --- a/extmod/mbedtls/mbedtls_config_common.h +++ b/extmod/mbedtls/mbedtls_config_common.h @@ -89,6 +89,7 @@ #define MBEDTLS_SHA384_C #define MBEDTLS_SHA512_C #define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_PROTO_DTLS #define MBEDTLS_SSL_SRV_C #define MBEDTLS_SSL_TLS_C #define MBEDTLS_X509_CRT_PARSE_C diff --git a/extmod/modtls_mbedtls.c b/extmod/modtls_mbedtls.c index 3fd416d72f5ef..6c34805da42cb 100644 --- a/extmod/modtls_mbedtls.c +++ b/extmod/modtls_mbedtls.c @@ -37,6 +37,7 @@ #include "py/stream.h" #include "py/objstr.h" #include "py/reader.h" +#include "py/mphal.h" #include "py/gc.h" #include "extmod/vfs.h" @@ -47,6 +48,9 @@ #include "mbedtls/pk.h" #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" +#ifdef MBEDTLS_SSL_PROTO_DTLS +#include "mbedtls/timing.h" +#endif #include "mbedtls/debug.h" #include "mbedtls/error.h" #if MBEDTLS_VERSION_NUMBER >= 0x03000000 @@ -65,6 +69,14 @@ #define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR) +#define MP_ENDPOINT_IS_SERVER (1 << 0) +#define MP_TRANSPORT_IS_DTLS (1 << 1) + +#define MP_PROTOCOL_TLS_CLIENT 0 +#define MP_PROTOCOL_TLS_SERVER MP_ENDPOINT_IS_SERVER +#define MP_PROTOCOL_DTLS_CLIENT MP_TRANSPORT_IS_DTLS +#define MP_PROTOCOL_DTLS_SERVER MP_ENDPOINT_IS_SERVER | MP_TRANSPORT_IS_DTLS + // This corresponds to an SSLContext object. typedef struct _mp_obj_ssl_context_t { mp_obj_base_t base; @@ -91,6 +103,12 @@ typedef struct _mp_obj_ssl_socket_t { uintptr_t poll_mask; // Indicates which read or write operations the protocol needs next int last_error; // The last error code, if any + + #ifdef MBEDTLS_SSL_PROTO_DTLS + mp_uint_t timer_start_ms; + mp_uint_t timer_fin_ms; + mp_uint_t timer_int_ms; + #endif } mp_obj_ssl_socket_t; static const mp_obj_type_t ssl_context_type; @@ -242,7 +260,10 @@ static mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args mp_arg_check_num(n_args, n_kw, 1, 1, false); // This is the "protocol" argument. - mp_int_t endpoint = mp_obj_get_int(args[0]); + mp_int_t protocol = mp_obj_get_int(args[0]); + + int endpoint = (protocol & MP_ENDPOINT_IS_SERVER) ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT; + int transport = (protocol & MP_TRANSPORT_IS_DTLS) ? MBEDTLS_SSL_TRANSPORT_DATAGRAM : MBEDTLS_SSL_TRANSPORT_STREAM; // Create SSLContext object. #if MICROPY_PY_SSL_FINALISER @@ -282,7 +303,7 @@ static mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args } ret = mbedtls_ssl_config_defaults(&self->conf, endpoint, - MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + transport, MBEDTLS_SSL_PRESET_DEFAULT); if (ret != 0) { mbedtls_raise_error(ret); } @@ -525,6 +546,39 @@ static int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) { } } +#ifdef MBEDTLS_SSL_PROTO_DTLS +static void _mbedtls_timing_set_delay(void *ctx, uint32_t int_ms, uint32_t fin_ms) { + mp_obj_ssl_socket_t *o = (mp_obj_ssl_socket_t *)ctx; + + o->timer_int_ms = int_ms; + o->timer_fin_ms = fin_ms; + + if (fin_ms != 0) { + o->timer_start_ms = mp_hal_ticks_ms(); + } +} + +static int _mbedtls_timing_get_delay(void *ctx) { + mp_obj_ssl_socket_t *o = (mp_obj_ssl_socket_t *)ctx; + + if (o->timer_fin_ms == 0) { + return -1; + } + + mp_uint_t elapsed_ms = mp_hal_ticks_ms() - o->timer_start_ms; + + if (elapsed_ms >= o->timer_fin_ms) { + return 2; + } + + if (elapsed_ms >= o->timer_int_ms) { + return 1; + } + + return 0; +} +#endif + static mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t sock, bool server_side, bool do_handshake_on_connect, mp_obj_t server_hostname) { @@ -577,6 +631,10 @@ static mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t mp_raise_ValueError(MP_ERROR_TEXT("CERT_REQUIRED requires server_hostname")); } + #ifdef MBEDTLS_SSL_PROTO_DTLS + mbedtls_ssl_set_timer_cb(&o->ssl, o, _mbedtls_timing_set_delay, _mbedtls_timing_get_delay); + #endif + mbedtls_ssl_set_bio(&o->ssl, &o->sock, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL); if (do_handshake_on_connect) { @@ -788,6 +846,12 @@ static const mp_rom_map_elem_t ssl_socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + #ifdef MBEDTLS_SSL_PROTO_DTLS + { MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&mp_stream_read1_obj) }, + { MP_ROM_QSTR(MP_QSTR_recv_into), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&mp_stream_write1_obj) }, + { MP_ROM_QSTR(MP_QSTR_sendall), MP_ROM_PTR(&mp_stream_write_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, #if MICROPY_PY_SSL_FINALISER @@ -879,8 +943,12 @@ static const mp_rom_map_elem_t mp_module_tls_globals_table[] = { // Constants. { MP_ROM_QSTR(MP_QSTR_MBEDTLS_VERSION), MP_ROM_PTR(&mbedtls_version_obj)}, - { MP_ROM_QSTR(MP_QSTR_PROTOCOL_TLS_CLIENT), MP_ROM_INT(MBEDTLS_SSL_IS_CLIENT) }, - { MP_ROM_QSTR(MP_QSTR_PROTOCOL_TLS_SERVER), MP_ROM_INT(MBEDTLS_SSL_IS_SERVER) }, + { MP_ROM_QSTR(MP_QSTR_PROTOCOL_TLS_CLIENT), MP_ROM_INT(MP_PROTOCOL_TLS_CLIENT) }, + { MP_ROM_QSTR(MP_QSTR_PROTOCOL_TLS_SERVER), MP_ROM_INT(MP_PROTOCOL_TLS_SERVER) }, + #ifdef MBEDTLS_SSL_PROTO_DTLS + { MP_ROM_QSTR(MP_QSTR_PROTOCOL_DTLS_CLIENT), MP_ROM_INT(MP_PROTOCOL_DTLS_CLIENT) }, + { MP_ROM_QSTR(MP_QSTR_PROTOCOL_DTLS_SERVER), MP_ROM_INT(MP_PROTOCOL_DTLS_SERVER) }, + #endif { MP_ROM_QSTR(MP_QSTR_CERT_NONE), MP_ROM_INT(MBEDTLS_SSL_VERIFY_NONE) }, { MP_ROM_QSTR(MP_QSTR_CERT_OPTIONAL), MP_ROM_INT(MBEDTLS_SSL_VERIFY_OPTIONAL) }, { MP_ROM_QSTR(MP_QSTR_CERT_REQUIRED), MP_ROM_INT(MBEDTLS_SSL_VERIFY_REQUIRED) }, diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index e20835c70c42d..530db427119ca 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -64,6 +64,9 @@ CONFIG_MBEDTLS_HAVE_TIME_DATE=y CONFIG_MBEDTLS_PLATFORM_TIME_ALT=y CONFIG_MBEDTLS_HAVE_TIME=y +# Enable DTLS +CONFIG_MBEDTLS_SSL_PROTO_DTLS=y + # Disable ALPN support as it's not implemented in MicroPython CONFIG_MBEDTLS_SSL_ALPN=n diff --git a/tests/extmod/tls_dtls.py b/tests/extmod/tls_dtls.py new file mode 100644 index 0000000000000..b2d716769d3f7 --- /dev/null +++ b/tests/extmod/tls_dtls.py @@ -0,0 +1,51 @@ +# Test DTLS functionality including timeout handling + +try: + from tls import PROTOCOL_DTLS_CLIENT, PROTOCOL_DTLS_SERVER, SSLContext, CERT_NONE + import io +except ImportError: + print("SKIP") + raise SystemExit + + +class DummySocket(io.IOBase): + def __init__(self): + self.write_buffer = bytearray() + self.read_buffer = bytearray() + + def write(self, data): + return len(data) + + def readinto(self, buf): + # This is a placeholder socket that doesn't actually read anything + # so the read buffer is always empty. + return None + + def ioctl(self, req, arg): + if req == 4: # MP_STREAM_CLOSE + return 0 + return -1 + + +# Create dummy sockets for testing +server_socket = DummySocket() +client_socket = DummySocket() + +# Wrap the DTLS Server +dtls_server_ctx = SSLContext(PROTOCOL_DTLS_SERVER) +dtls_server_ctx.verify_mode = CERT_NONE +dtls_server = dtls_server_ctx.wrap_socket(server_socket, do_handshake_on_connect=False) +print("Wrapped DTLS Server") + +# Wrap the DTLS Client +dtls_client_ctx = SSLContext(PROTOCOL_DTLS_CLIENT) +dtls_client_ctx.verify_mode = CERT_NONE +dtls_client = dtls_client_ctx.wrap_socket(client_socket, do_handshake_on_connect=False) +print("Wrapped DTLS Client") + +# Trigger the timing check multiple times with different elapsed times +for i in range(10): # Try multiple iterations to hit the timing window + dtls_client.write(b"test") + data = dtls_server.read(1024) # This should eventually hit the timing condition + +print("OK") diff --git a/tests/extmod/tls_dtls.py.exp b/tests/extmod/tls_dtls.py.exp new file mode 100644 index 0000000000000..78d72bff18816 --- /dev/null +++ b/tests/extmod/tls_dtls.py.exp @@ -0,0 +1,3 @@ +Wrapped DTLS Server +Wrapped DTLS Client +OK From 8987b39e0b772d22539022f0961c8ea0f7162751 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 14 Feb 2025 09:54:56 +1100 Subject: [PATCH 302/546] tests/multi_net: Add test for DTLS server and client. This adds a multi-test for DTLS server and client behaviour. It works on all ports that enable this feature (eg unix, esp32, rp2, stm32), but bare-metal ports that use lwIP are not reliable as the DTLS server because the lwIP bindings only support queuing one UDP packet at a time (that needs to be fixed). Also, to properly implement a DTLS server sockets need to support `socket.recvfrom(n, MSG_PEEK)`. That can be implemented in the future. Signed-off-by: Damien George --- tests/multi_net/tls_dtls_server_client.py | 89 +++++++++++++++++++ tests/multi_net/tls_dtls_server_client.py.exp | 14 +++ 2 files changed, 103 insertions(+) create mode 100644 tests/multi_net/tls_dtls_server_client.py create mode 100644 tests/multi_net/tls_dtls_server_client.py.exp diff --git a/tests/multi_net/tls_dtls_server_client.py b/tests/multi_net/tls_dtls_server_client.py new file mode 100644 index 0000000000000..d50deb354ed4d --- /dev/null +++ b/tests/multi_net/tls_dtls_server_client.py @@ -0,0 +1,89 @@ +# Test DTLS server and client, sending a small amount of data between them. + +try: + import socket + import tls +except ImportError: + print("SKIP") + raise SystemExit + +PORT = 8000 + +# These are test certificates. See tests/README.md for details. +certfile = "ec_cert.der" +keyfile = "ec_key.der" + +try: + 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 + + +# DTLS server. +def instance0(): + multitest.globals(IP=multitest.get_network_ip()) + + # Create a UDP socket and bind it to accept incoming connections. + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) + + multitest.next() + + # Wait for the client to connect. + data, client_addr = s.recvfrom(1) + print("incoming connection", data) + + # Connect back to the client, so the UDP socket can be used like a stream. + s.connect(client_addr) + + # Create the DTLS context and load the certificate. + ctx = tls.SSLContext(tls.PROTOCOL_DTLS_SERVER) + ctx.load_cert_chain(cert, key) + + # Wrap the UDP socket in server mode. + print("wrap socket") + s = ctx.wrap_socket(s, server_side=1) + + # Transfer some data. + for _ in range(4): + print(s.recv(16)) + s.send(b"server to client") + + # Close the DTLS and UDP connection. + s.close() + + +# DTLS client. +def instance1(): + multitest.next() + + # Create a UDP socket and connect to the server. + addr = socket.getaddrinfo(IP, PORT)[0][-1] + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + print("connect") + s.connect(addr) + + # Send one byte to indicate a connection, and so the server can obtain our address. + s.write("X") + + # Create a DTLS context and load the certificate. + ctx = tls.SSLContext(tls.PROTOCOL_DTLS_CLIENT) + ctx.verify_mode = tls.CERT_REQUIRED + ctx.load_verify_locations(cadata) + + # Wrap the UDP socket. + print("wrap socket") + s = ctx.wrap_socket(s, server_hostname="micropython.local") + + # Transfer some data. + for _ in range(4): + s.send(b"client to server") + print(s.recv(16)) + + # Close the DTLS and UDP connection. + s.close() diff --git a/tests/multi_net/tls_dtls_server_client.py.exp b/tests/multi_net/tls_dtls_server_client.py.exp new file mode 100644 index 0000000000000..f2ff396e181df --- /dev/null +++ b/tests/multi_net/tls_dtls_server_client.py.exp @@ -0,0 +1,14 @@ +--- instance0 --- +incoming connection b'X' +wrap socket +b'client to server' +b'client to server' +b'client to server' +b'client to server' +--- instance1 --- +connect +wrap socket +b'server to client' +b'server to client' +b'server to client' +b'server to client' From 71df9d0636c8b0b95ab009f82bde7d5b970835ec Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 11 Feb 2025 16:42:03 +1100 Subject: [PATCH 303/546] rp2: Fix build failure if threads are disabled. Regression in 3af006ef meant that pendsv.c no longer compiled if threads were disabled in the build config. Add an implementation based on the earlier one (simple counter) for the non-threads case. It seems like with the current usage patterns there's no need for the counter to be incremented/decremented atomically on a single core config. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/pendsv.c | 49 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/ports/rp2/pendsv.c b/ports/rp2/pendsv.c index 4ba1e81604b0f..2c086f89429be 100644 --- a/ports/rp2/pendsv.c +++ b/ports/rp2/pendsv.c @@ -43,10 +43,14 @@ static pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS]; +static inline void pendsv_resume_run_dispatch(void); + void PendSV_Handler(void); -// 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. +#if MICROPY_PY_THREAD + +// Important to use a 'nowait' mutex 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 mp_thread_recursive_mutex_t pendsv_mutex; void pendsv_init(void) { @@ -62,7 +66,40 @@ void pendsv_suspend(void) { void pendsv_resume(void) { mp_thread_recursive_mutex_unlock(&pendsv_mutex); + pendsv_resume_run_dispatch(); +} + +static inline int pendsv_suspend_count(void) { + return pendsv_mutex.mutex.enter_count; +} + +#else + +// Without threads we don't include any pico-sdk mutex in the build, +// but also we don't need to worry about cross-thread contention (or +// races with interrupts that update this counter). +static int pendsv_lock; + +void pendsv_init(void) { +} + +void pendsv_suspend(void) { + pendsv_lock++; +} + +void pendsv_resume(void) { + assert(pendsv_lock > 0); + pendsv_lock--; + pendsv_resume_run_dispatch(); +} +static inline int pendsv_suspend_count(void) { + return pendsv_lock; +} + +#endif + +static inline void pendsv_resume_run_dispatch(void) { // 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; @@ -76,7 +113,7 @@ void pendsv_resume(void) { void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { pendsv_dispatch_table[slot] = f; - if (pendsv_mutex.mutex.enter_count == 0) { + if (pendsv_suspend_count() == 0) { #if PICO_ARM // There is a race here where other core calls pendsv_suspend() before // ISR can execute, but dispatch will happen later when other core @@ -97,6 +134,7 @@ void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { // PendSV interrupt handler to perform background processing. void PendSV_Handler(void) { + #if MICROPY_PY_THREAD if (!mp_thread_recursive_mutex_lock(&pendsv_mutex, 0)) { // Failure here means core 1 holds pendsv_mutex. ISR will // run again after core 1 calls pendsv_resume(). @@ -104,6 +142,9 @@ void PendSV_Handler(void) { } // Core 0 should not already have locked pendsv_mutex assert(pendsv_mutex.mutex.enter_count == 1); + #else + assert(pendsv_suspend_count() == 0); + #endif #if MICROPY_PY_NETWORK_CYW43 CYW43_STAT_INC(PENDSV_RUN_COUNT); @@ -117,5 +158,7 @@ void PendSV_Handler(void) { } } + #if MICROPY_PY_THREAD mp_thread_recursive_mutex_unlock(&pendsv_mutex); + #endif } From 516709be88571da0195125c840e772a3ec70fa66 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 11 Feb 2025 17:44:19 +1100 Subject: [PATCH 304/546] py/mkrules.cmake: Support passing CFLAGS_EXTRA in environment variable. This works similarly to the existing support in "bare metal" make ports, with the caveat that CMake will only set this value on a clean build and will reuse the previous value otherwise. This is slightly different to the CMake built-in support for CFLAGS, as this variable is used when evaluating source files for qstr generation, etc. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- py/mkrules.cmake | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/py/mkrules.cmake b/py/mkrules.cmake index 3ee4c4c31aa57..907500dd67534 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -53,6 +53,15 @@ foreach(_arg ${MICROPY_CPP_DEF}) endforeach() list(APPEND MICROPY_CPP_FLAGS ${MICROPY_CPP_FLAGS_EXTRA}) +# Include anything passed in via CFLAGS_EXTRA +# in both MICROPY_CPP_FLAGS and CMAKE_C_FLAGS +if(DEFINED ENV{CFLAGS_EXTRA}) + set(CFLAGS_EXTRA $ENV{CFLAGS_EXTRA}) + string(APPEND CMAKE_C_FLAGS " ${CFLAGS_EXTRA}") # ... not a list + separate_arguments(CFLAGS_EXTRA) + list(APPEND MICROPY_CPP_FLAGS ${CFLAGS_EXTRA}) # ... a list +endif() + find_package(Python3 REQUIRED COMPONENTS Interpreter) target_sources(${MICROPY_TARGET} PRIVATE From 1034b17558b108b65aca480d3b5b8f933e128369 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 11 Feb 2025 17:46:33 +1100 Subject: [PATCH 305/546] tools/ci.sh: Build the W5100S_EVB_PICO board with no threads. Serves as a build test for a config we don't otherwise support. Signed-off-by: Angus Gratton --- tools/ci.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/ci.sh b/tools/ci.sh index ff362efd29e2d..d50af4a0ae2ee 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -342,7 +342,8 @@ function ci_rp2_build { make ${MAKEOPTS} -C ports/rp2 BOARD=RPI_PICO2 submodules make ${MAKEOPTS} -C ports/rp2 BOARD=RPI_PICO2 make ${MAKEOPTS} -C ports/rp2 BOARD=W5100S_EVB_PICO submodules - make ${MAKEOPTS} -C ports/rp2 BOARD=W5100S_EVB_PICO + # This build doubles as a build test for disabling threads in the config + make ${MAKEOPTS} -C ports/rp2 BOARD=W5100S_EVB_PICO CFLAGS_EXTRA=-DMICROPY_PY_THREAD=0 # Test building ninaw10 driver and NIC interface. make ${MAKEOPTS} -C ports/rp2 BOARD=ARDUINO_NANO_RP2040_CONNECT submodules From 6425c9ecc746eec797f7ba9a37d803244f7d2192 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 14 Feb 2025 00:40:22 +0100 Subject: [PATCH 306/546] esp32: Remove unneeded "memory.h" header file. This commit removes "memory.h" from the ESP32 port tree, as it is no longer needed with recent ESP-IDF versions. Signed-off-by: Alessandro Gatti --- ports/esp32/memory.h | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 ports/esp32/memory.h diff --git a/ports/esp32/memory.h b/ports/esp32/memory.h deleted file mode 100644 index 1f07fe409d7eb..0000000000000 --- a/ports/esp32/memory.h +++ /dev/null @@ -1,2 +0,0 @@ -// this is needed for lib/crypto-algorithms/sha256.c -#include From 1e3cce1397c76f7714a5c4940b5021965944d268 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 20 Feb 2025 09:19:33 +0100 Subject: [PATCH 307/546] mimxrt/boards/ADAFRUIT_METRO_M7: Reduce flash freq to 100MHz. It was set to 133Mhz, but that is not stable. Reduce to 100MHz. The UF2 bootloader runs at 100MHz, so no need for a change of the bootloader. Signed-off-by: robert-hh --- ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk index b9c1716a7f60c..520cea063fb7e 100644 --- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk +++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk @@ -4,7 +4,7 @@ MCU_VARIANT = MIMXRT1011DAE5A MICROPY_FLOAT_IMPL = single MICROPY_HW_FLASH_TYPE ?= qspi_nor_flash MICROPY_HW_FLASH_SIZE ?= 0x800000 # 8MB -MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_133MHz +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_100MHz MICROPY_HW_FLASH_QE_CMD = 0x31 MICROPY_HW_FLASH_QE_ARG = 0x02 From 4364d9411ad4c71bbf6de9bcc82683d6ee671cf5 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 20 Feb 2025 09:19:33 +0100 Subject: [PATCH 308/546] mimxrt/hal/flexspi_nor_flash: Fix typo in comment about frequency. Signed-off-by: robert-hh --- ports/mimxrt/hal/flexspi_nor_flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/mimxrt/hal/flexspi_nor_flash.c b/ports/mimxrt/hal/flexspi_nor_flash.c index 7fdb6b7c13df6..1476c1ec3a5db 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.c +++ b/ports/mimxrt/hal/flexspi_nor_flash.c @@ -83,7 +83,7 @@ static ps_div_t div_table_mhz[] = { { 18, 8 }, // 60 -> 60 MHz { 23, 5 }, // 75 -> 75.13 MHz { 18, 6 }, // 80 -> 80 MHz - { 17, 5 }, // 100 -> 191 Mhz + { 17, 5 }, // 100 -> 101 Mhz { 13, 5 }, // 133 -> 132.92 MHz { 13, 4 } // 166 -> 166.15 MHz }; From 2992e3495630a8b83683db28241d638dbc53c911 Mon Sep 17 00:00:00 2001 From: Glenn Moloney Date: Tue, 19 Sep 2023 13:04:46 +1000 Subject: [PATCH 309/546] tools/mpremote: Add support for relative urls in package.json files. URLs in `package.json` may now be specified relative to the base URL of the `package.json` file. Relative URLs wil work for `package.json` files installed from the web as well as local file paths. Docs: update `docs/reference/packages.rst` to add documentation for: - Installing packages from local filesystems (PR #12476); and - Using relative URLs in the `package.json` file (PR #12477); - Update the packaging example to encourage relative URLs as the default in `package.json`. Add `tools/mpremote/tests/test_mip_local_install.sh` to test the installation of a package from local files using relative URLs in the `package.json`. Signed-off-by: Glenn Moloney --- docs/reference/packages.rst | 48 +++++++++++-- tools/mpremote/mpremote/mip.py | 42 ++++++++---- .../mpremote/tests/test_mip_local_install.sh | 67 +++++++++++++++++++ .../tests/test_mip_local_install.sh.exp | 11 +++ 4 files changed, 149 insertions(+), 19 deletions(-) create mode 100755 tools/mpremote/tests/test_mip_local_install.sh create mode 100644 tools/mpremote/tests/test_mip_local_install.sh.exp diff --git a/docs/reference/packages.rst b/docs/reference/packages.rst index 86b8efaa1953d..5b5f626d45232 100644 --- a/docs/reference/packages.rst +++ b/docs/reference/packages.rst @@ -96,6 +96,18 @@ The ``--target=path``, ``--no-mpy``, and ``--index`` arguments can be set:: $ mpremote mip install --no-mpy pkgname $ mpremote mip install --index https://host/pi pkgname +:term:`mpremote` can also install packages from files stored on the host's local +filesystem:: + + $ mpremote mip install path/to/pkg.py + $ mpremote mip install path/to/app/package.json + $ mpremote mip install \\path\\to\\pkg.py + +This is especially useful for testing packages during development and for +installing packages from local clones of GitHub repositories. Note that URLs in +``package.json`` files must use forward slashes ("/") as directory separators, +even on Windows, so that they are compatible with installing from the web. + Installing packages manually ---------------------------- @@ -116,12 +128,25 @@ To write a "self-hosted" package that can be downloaded by ``mip`` or ``mpremote``, you need a static webserver (or GitHub) to host either a single .py file, or a ``package.json`` file alongside your .py files. -A typical ``package.json`` for an example ``mlx90640`` library looks like:: +An example ``mlx90640`` library hosted on GitHub could be installed with:: + + $ mpremote mip install github:org/micropython-mlx90640 + +The layout for the package on GitHub might look like:: + + https://github.com/org/micropython-mlx90640/ + package.json + mlx90640/ + __init__.py + utils.py + +The ``package.json`` specifies the location of files to be installed and other +dependencies:: { "urls": [ - ["mlx90640/__init__.py", "github:org/micropython-mlx90640/mlx90640/__init__.py"], - ["mlx90640/utils.py", "github:org/micropython-mlx90640/mlx90640/utils.py"] + ["mlx90640/__init__.py", "mlx90640/__init__.py"], + ["mlx90640/utils.py", "mlx90640/utils.py"] ], "deps": [ ["collections-defaultdict", "latest"], @@ -132,9 +157,20 @@ A typical ``package.json`` for an example ``mlx90640`` library looks like:: "version": "0.2" } -This includes two files, hosted at a GitHub repo named -``org/micropython-mlx90640``, which install into the ``mlx90640`` directory on -the device. It depends on ``collections-defaultdict`` and ``os-path`` which will +The ``urls`` list specifies the files to be installed according to:: + + "urls": [ + [destination_path, source_url] + ... + +where ``destination_path`` is the location and name of the file to be installed +on the device and ``source_url`` is the URL of the file to be installed. The +source URL would usually be specified relative to the directory containing the +``package.json`` file, but can also be an absolute URL, eg:: + + ["mlx90640/utils.py", "github:org/micropython-mlx90640/mlx90640/utils.py"] + +The package depends on ``collections-defaultdict`` and ``os-path`` which will be installed automatically from the :term:`micropython-lib`. The third dependency installs the content as defined by the ``package.json`` file of the ``main`` branch of the GitHub repo ``org/micropython-additions``. diff --git a/tools/mpremote/mpremote/mip.py b/tools/mpremote/mpremote/mip.py index 4ce62b7f77bbb..858b7933d11d8 100644 --- a/tools/mpremote/mpremote/mip.py +++ b/tools/mpremote/mpremote/mip.py @@ -7,6 +7,7 @@ import json import tempfile import os +import os.path from .commands import CommandError, show_progress_bar @@ -64,22 +65,33 @@ def _rewrite_url(url, branch=None): def _download_file(transport, url, dest): - try: - with urllib.request.urlopen(url) as src: - data = src.read() - print("Installing:", dest) - _ensure_path_exists(transport, dest) - transport.fs_writefile(dest, data, progress_callback=show_progress_bar) - except urllib.error.HTTPError as e: - if e.status == 404: - raise CommandError(f"File not found: {url}") - else: - raise CommandError(f"Error {e.status} requesting {url}") - except urllib.error.URLError as e: - raise CommandError(f"{e.reason} requesting {url}") + if url.startswith(allowed_mip_url_prefixes): + try: + with urllib.request.urlopen(url) as src: + data = src.read() + except urllib.error.HTTPError as e: + if e.status == 404: + raise CommandError(f"File not found: {url}") + else: + raise CommandError(f"Error {e.status} requesting {url}") + except urllib.error.URLError as e: + raise CommandError(f"{e.reason} requesting {url}") + else: + if "\\" in url: + raise CommandError(f'Use "/" instead of "\\" in file URLs: {url!r}\n') + try: + with open(url, "rb") as f: + data = f.read() + except OSError as e: + raise CommandError(f"{e.strerror} opening {url}") + + print("Installing:", dest) + _ensure_path_exists(transport, dest) + transport.fs_writefile(dest, data, progress_callback=show_progress_bar) def _install_json(transport, package_json_url, index, target, version, mpy): + base_url = "" if package_json_url.startswith(allowed_mip_url_prefixes): try: with urllib.request.urlopen(_rewrite_url(package_json_url, version)) as response: @@ -91,12 +103,14 @@ def _install_json(transport, package_json_url, index, target, version, mpy): raise CommandError(f"Error {e.status} requesting {package_json_url}") except urllib.error.URLError as e: raise CommandError(f"{e.reason} requesting {package_json_url}") + base_url = package_json_url.rpartition("/")[0] elif package_json_url.endswith(".json"): try: with open(package_json_url, "r") as f: package_json = json.load(f) except OSError: raise CommandError(f"Error opening {package_json_url}") + base_url = os.path.dirname(package_json_url) else: raise CommandError(f"Invalid url for package: {package_json_url}") for target_path, short_hash in package_json.get("hashes", ()): @@ -105,6 +119,8 @@ def _install_json(transport, package_json_url, index, target, version, mpy): _download_file(transport, file_url, fs_target_path) for target_path, url in package_json.get("urls", ()): fs_target_path = target + "/" + target_path + if base_url and not url.startswith(allowed_mip_url_prefixes): + url = f"{base_url}/{url}" # Relative URLs _download_file(transport, _rewrite_url(url, version), fs_target_path) for dep, dep_version in package_json.get("deps", ()): _install_package(transport, dep, index, target, dep_version, mpy) diff --git a/tools/mpremote/tests/test_mip_local_install.sh b/tools/mpremote/tests/test_mip_local_install.sh new file mode 100755 index 0000000000000..fb8c597bd1484 --- /dev/null +++ b/tools/mpremote/tests/test_mip_local_install.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# This test the "mpremote mip install" from local files. It creates a package +# and "mip installs" it into a ramdisk. The package is then imported and +# executed. The package is a simple "Hello, world!" example. + +set -e + +PACKAGE=mip_example +PACKAGE_DIR=${TMP}/example +MODULE_DIR=${PACKAGE_DIR}/${PACKAGE} + +target=/__ramdisk +block_size=512 +num_blocks=50 + +# Create the smallest permissible ramdisk. +cat << EOF > "${TMP}/ramdisk.py" +class RAMBlockDev: + def __init__(self, block_size, num_blocks): + self.block_size = block_size + self.data = bytearray(block_size * num_blocks) + + def readblocks(self, block_num, buf): + for i in range(len(buf)): + buf[i] = self.data[block_num * self.block_size + i] + + def writeblocks(self, block_num, buf): + for i in range(len(buf)): + self.data[block_num * self.block_size + i] = buf[i] + + def ioctl(self, op, arg): + if op == 4: # get number of blocks + return len(self.data) // self.block_size + if op == 5: # get block size + return self.block_size + +import os + +bdev = RAMBlockDev(${block_size}, ${num_blocks}) +os.VfsFat.mkfs(bdev) +os.mount(bdev, '${target}') +EOF + +echo ----- Setup +mkdir -p ${MODULE_DIR} +echo "def hello(): print('Hello, world!')" > ${MODULE_DIR}/hello.py +echo "from .hello import hello" > ${MODULE_DIR}/__init__.py +cat > ${PACKAGE_DIR}/package.json < Date: Wed, 12 Feb 2025 14:03:31 +1100 Subject: [PATCH 310/546] github/workflows: Stop using ubuntu-20.04. For GitHub Actions, ubuntu-20.04 is deprecated and will be removed by 1st April 2025. See announcement at https://github.com/actions/runner-images/issues/11101 This commit changes actions that use ubuntu-20.04 to a newer image. Signed-off-by: Damien George --- .github/workflows/mpy_format.yml | 2 +- .github/workflows/ports_esp32.yml | 2 +- .github/workflows/ports_mimxrt.yml | 2 +- .github/workflows/ports_nrf.yml | 2 +- .github/workflows/ports_renesas-ra.yml | 2 +- .github/workflows/ports_stm32.yml | 2 +- .github/workflows/ports_unix.yml | 8 ++++---- tools/ci.sh | 17 +++++++++++------ 8 files changed, 21 insertions(+), 16 deletions(-) diff --git a/.github/workflows/mpy_format.yml b/.github/workflows/mpy_format.yml index baa02ce08d507..b6768a46c3cdc 100644 --- a/.github/workflows/mpy_format.yml +++ b/.github/workflows/mpy_format.yml @@ -15,7 +15,7 @@ concurrency: jobs: test: - runs-on: ubuntu-20.04 # use 20.04 to get python2 + runs-on: ubuntu-22.04 # use 22.04 to get python2 steps: - uses: actions/checkout@v4 - name: Install packages diff --git a/.github/workflows/ports_esp32.yml b/.github/workflows/ports_esp32.yml index 45808b659add7..0d76974f01946 100644 --- a/.github/workflows/ports_esp32.yml +++ b/.github/workflows/ports_esp32.yml @@ -25,7 +25,7 @@ jobs: ci_func: # names are functions in ci.sh - esp32_build_cmod_spiram_s2 - esp32_build_s3_c3 - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/ports_mimxrt.yml b/.github/workflows/ports_mimxrt.yml index 9e782bf63b31c..7743e036ab377 100644 --- a/.github/workflows/ports_mimxrt.yml +++ b/.github/workflows/ports_mimxrt.yml @@ -19,7 +19,7 @@ concurrency: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest defaults: run: working-directory: 'micropython repo' # test build with space in path diff --git a/.github/workflows/ports_nrf.yml b/.github/workflows/ports_nrf.yml index d9cffb9778cba..76727c9d1f6bd 100644 --- a/.github/workflows/ports_nrf.yml +++ b/.github/workflows/ports_nrf.yml @@ -19,7 +19,7 @@ concurrency: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install packages diff --git a/.github/workflows/ports_renesas-ra.yml b/.github/workflows/ports_renesas-ra.yml index b1a30c2f11798..b9fa74331dc0b 100644 --- a/.github/workflows/ports_renesas-ra.yml +++ b/.github/workflows/ports_renesas-ra.yml @@ -19,7 +19,7 @@ concurrency: jobs: build_renesas_ra_board: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install packages diff --git a/.github/workflows/ports_stm32.yml b/.github/workflows/ports_stm32.yml index f5e01dc1f620e..8800f145189b8 100644 --- a/.github/workflows/ports_stm32.yml +++ b/.github/workflows/ports_stm32.yml @@ -26,7 +26,7 @@ jobs: - stm32_pyb_build - stm32_nucleo_build - stm32_misc_build - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Install packages diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index a095136b4a65e..2547015038e4d 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -98,7 +98,7 @@ jobs: run: tests/run-tests.py --print-failures coverage_32bit: - runs-on: ubuntu-20.04 # use 20.04 to get libffi-dev:i386 + runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 steps: - uses: actions/checkout@v4 - name: Install packages @@ -116,7 +116,7 @@ jobs: run: tests/run-tests.py --print-failures nanbox: - runs-on: ubuntu-20.04 # use 20.04 to get python2, and libffi-dev:i386 + runs-on: ubuntu-22.04 # use 22.04 to get python2, and libffi-dev:i386 steps: - uses: actions/checkout@v4 - name: Install packages @@ -142,7 +142,7 @@ jobs: run: tests/run-tests.py --print-failures stackless_clang: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install packages @@ -156,7 +156,7 @@ jobs: run: tests/run-tests.py --print-failures float_clang: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install packages diff --git a/tools/ci.sh b/tools/ci.sh index d50af4a0ae2ee..9125b5bc9f135 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -106,12 +106,16 @@ function ci_code_size_build { # .mpy file format function ci_mpy_format_setup { + sudo apt-get update + sudo apt-get install python2.7 sudo pip3 install pyelftools + python2.7 --version + python3 --version } function ci_mpy_format_test { # Test mpy-tool.py dump feature on bytecode - python2 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy + python2.7 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy python3 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy # Test mpy-tool.py dump feature on native code @@ -268,18 +272,18 @@ function ci_powerpc_build { # ports/qemu function ci_qemu_setup_arm { - ci_mpy_format_setup ci_gcc_arm_setup sudo apt-get update sudo apt-get install qemu-system + sudo pip3 install pyelftools qemu-system-arm --version } function ci_qemu_setup_rv32 { - ci_mpy_format_setup ci_gcc_riscv_setup sudo apt-get update sudo apt-get install qemu-system + sudo pip3 install pyelftools qemu-system-riscv32 --version } @@ -580,10 +584,11 @@ function ci_unix_coverage_run_native_mpy_tests { function ci_unix_32bit_setup { sudo dpkg --add-architecture i386 sudo apt-get update - sudo apt-get install gcc-multilib g++-multilib libffi-dev:i386 + sudo apt-get install gcc-multilib g++-multilib libffi-dev:i386 python2.7 sudo pip3 install setuptools sudo pip3 install pyelftools gcc --version + python2.7 --version python3 --version } @@ -602,12 +607,12 @@ function ci_unix_coverage_32bit_run_native_mpy_tests { function ci_unix_nanbox_build { # Use Python 2 to check that it can run the build scripts - ci_unix_build_helper PYTHON=python2 VARIANT=nanbox CFLAGS_EXTRA="-DMICROPY_PY_MATH_CONSTANTS=1" + ci_unix_build_helper PYTHON=python2.7 VARIANT=nanbox CFLAGS_EXTRA="-DMICROPY_PY_MATH_CONSTANTS=1" ci_unix_build_ffi_lib_helper gcc -m32 } function ci_unix_nanbox_run_tests { - ci_unix_run_tests_full_helper nanbox PYTHON=python2 + ci_unix_run_tests_full_helper nanbox PYTHON=python2.7 } function ci_unix_float_build { From 8b1ed4473dd215d564498f577cb6f3339c3c2e57 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 12 Feb 2025 16:46:43 +1100 Subject: [PATCH 311/546] github/workflows: Include the Python version in the ESP-IDF cache key. Changing runner OS can change Python version, and ESP-IDF installs are keyed on ESP-IDF and Python version together. Signed-off-by: Angus Gratton --- .github/workflows/ports_esp32.yml | 4 ++-- tools/ci.sh | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ports_esp32.yml b/.github/workflows/ports_esp32.yml index 0d76974f01946..4c07f89437757 100644 --- a/.github/workflows/ports_esp32.yml +++ b/.github/workflows/ports_esp32.yml @@ -30,8 +30,8 @@ jobs: - uses: actions/checkout@v4 - id: idf_ver - name: Read the ESP-IDF version - run: source tools/ci.sh && echo "IDF_VER=$IDF_VER" | tee "$GITHUB_OUTPUT" + name: Read the ESP-IDF version (including Python version) + run: source tools/ci.sh && echo "IDF_VER=${IDF_VER}-py${PYTHON_VER}" | tee "$GITHUB_OUTPUT" - name: Cached ESP-IDF install id: cache_esp_idf diff --git a/tools/ci.sh b/tools/ci.sh index 9125b5bc9f135..5b108d620258c 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -140,6 +140,7 @@ function ci_cc3200_build { # GitHub tag of ESP-IDF to use for CI (note: must be a tag or a branch) IDF_VER=v5.2.2 +PYTHON_VER=$(python --version | cut -d' ' -f2) export IDF_CCACHE_ENABLE=1 From 5e206fdeb5225ce24efb9411cc879bafaa04b86f Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 18 Feb 2025 13:24:11 +0100 Subject: [PATCH 312/546] all: Upgrade codespell to v2.4.1. This commit upgrades from codespell==2.2.6 to the current codespell==2.4.1, adding emac to the ignore-words-list. Signed-off-by: Christian Clauss --- .github/workflows/codespell.yml | 2 +- .pre-commit-config.yaml | 2 +- docs/reference/isr_rules.rst | 2 +- docs/reference/speed_python.rst | 2 +- examples/usercmodule/cexample/examplemodule.c | 2 +- extmod/modlwip.c | 2 +- ports/esp32/mpthreadport.c | 2 +- ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.h | 2 +- ports/mimxrt/eth.c | 2 +- ports/mimxrt/hal/flexspi_flash_config.h | 2 +- ports/mimxrt/sdcard.c | 2 +- ports/nrf/boards/MICROBIT/modules/microbitfont.h | 2 +- ports/rp2/main.c | 2 +- ports/samd/mcu/samd21/mpconfigmcu.h | 2 +- ports/samd/mcu/samd51/mpconfigmcu.h | 2 +- ports/windows/windows_mphal.c | 2 +- py/reader.h | 2 +- pyproject.toml | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 2d8b4627aac2d..1d6b1dc9d2b27 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -8,6 +8,6 @@ jobs: steps: - uses: actions/checkout@v4 # codespell version should be kept in sync with .pre-commit-config.yml - - run: pip install --user codespell==2.2.6 tomli + - run: pip install --user codespell==2.4.1 tomli - run: codespell diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a1c811339adcc..193b2c6e83559 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: - id: ruff-format - repo: https://github.com/codespell-project/codespell # Version should be kept in sync with .github/workflows/codespell.yml - rev: v2.2.6 + rev: v2.4.1 hooks: - id: codespell name: Spellcheck for changed files (codespell) diff --git a/docs/reference/isr_rules.rst b/docs/reference/isr_rules.rst index 14010fb207fdd..5e8d6ad61201a 100644 --- a/docs/reference/isr_rules.rst +++ b/docs/reference/isr_rules.rst @@ -209,7 +209,7 @@ issue a further interrupt. It then schedules a callback to process the data. Scheduled callbacks should comply with the principles of interrupt handler design outlined below. This is to avoid problems resulting from I/O activity and the modification of shared data which can arise in any code -which pre-empts the main program loop. +which preempts the main program loop. Execution time needs to be considered in relation to the frequency with which interrupts can occur. If an interrupt occurs while the previous callback is executing, a further instance of the callback will be queued diff --git a/docs/reference/speed_python.rst b/docs/reference/speed_python.rst index a660432f6da37..64fd9df6c058a 100644 --- a/docs/reference/speed_python.rst +++ b/docs/reference/speed_python.rst @@ -219,7 +219,7 @@ process known as garbage collection reclaims the memory used by these redundant objects and the allocation is then tried again - a process which can take several milliseconds. -There may be benefits in pre-empting this by periodically issuing `gc.collect()`. +There may be benefits in preempting this by periodically issuing `gc.collect()`. Firstly doing a collection before it is actually required is quicker - typically on the order of 1ms if done frequently. Secondly you can determine the point in code where this time is used rather than have a longer delay occur at random points, diff --git a/examples/usercmodule/cexample/examplemodule.c b/examples/usercmodule/cexample/examplemodule.c index d13515e72cdd5..83cc3b27c058e 100644 --- a/examples/usercmodule/cexample/examplemodule.c +++ b/examples/usercmodule/cexample/examplemodule.c @@ -74,7 +74,7 @@ MP_DEFINE_CONST_OBJ_TYPE( // - 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 +// It reuses 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__. diff --git a/extmod/modlwip.c b/extmod/modlwip.c index bcccd363e673c..f109e0029bb0f 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -476,7 +476,7 @@ static void _lwip_tcp_err_unaccepted(void *arg, err_t err) { // because it's only ever used by lwIP if tcp_connect is called on the TCP PCB. lwip_socket_obj_t *socket = (lwip_socket_obj_t *)pcb->connected; - // Array is not volatile because thiss callback is executed within the lwIP context + // Array is not volatile because this callback is executed within the lwIP context uint8_t alloc = socket->incoming.connection.alloc; struct tcp_pcb **tcp_array = (struct tcp_pcb **)lwip_socket_incoming_array(socket); diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c index f436ebb80a7ad..962b5780d08dd 100644 --- a/ports/esp32/mpthreadport.c +++ b/ports/esp32/mpthreadport.c @@ -213,7 +213,7 @@ 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 + // Python threads run at equal priority, so preemptively 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. diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.h b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.h index 784373c939478..1ce06e7f408d7 100644 --- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.h +++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.h @@ -52,7 +52,7 @@ { IOMUXC_GPIO_02_LPI2C1_SCL }, { IOMUXC_GPIO_01_LPI2C1_SDA }, \ { IOMUXC_GPIO_10_LPI2C2_SCL }, { IOMUXC_GPIO_09_LPI2C2_SDA }, -// Wifi Deinitions +// Wifi Definitions #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-metro-m7" #define MICROPY_HW_WIFI_SPI_ID (0) diff --git a/ports/mimxrt/eth.c b/ports/mimxrt/eth.c index 3eb1a0cc35202..1ac19b83d6d44 100644 --- a/ports/mimxrt/eth.c +++ b/ports/mimxrt/eth.c @@ -327,7 +327,7 @@ static void eth_gpio_init(const iomux_table_t iomux_table[], size_t iomux_table_ } } -// eth_phy_init: Initilaize the PHY interface +// eth_phy_init: Initialize the PHY interface static void eth_phy_init(phy_handle_t *phyHandle, phy_config_t *phy_config, phy_speed_t *speed, phy_duplex_t *duplex, uint32_t phy_settle_time) { diff --git a/ports/mimxrt/hal/flexspi_flash_config.h b/ports/mimxrt/hal/flexspi_flash_config.h index d9aa5ed5eab69..239d7b3b4f868 100644 --- a/ports/mimxrt/hal/flexspi_flash_config.h +++ b/ports/mimxrt/hal/flexspi_flash_config.h @@ -119,7 +119,7 @@ enum kFlexSpiMiscOffset_WordAddressableEnable = 3, // !< Bit for Word Addressable enable kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, // !< Bit for Safe Configuration Frequency enable kFlexSpiMiscOffset_PadSettingOverrideEnable = 5, // !< Bit for Pad setting override enable - kFlexSpiMiscOffset_DdrModeEnable = 6, // !< Bit for DDR clock confiuration indication. + kFlexSpiMiscOffset_DdrModeEnable = 6, // !< Bit for DDR clock configuration indication. }; // !@brief Flash Type Definition diff --git a/ports/mimxrt/sdcard.c b/ports/mimxrt/sdcard.c index ae52ed5d2cd09..84e2c98949d22 100644 --- a/ports/mimxrt/sdcard.c +++ b/ports/mimxrt/sdcard.c @@ -980,7 +980,7 @@ bool sdcard_power_on(mimxrt_sdcard_obj_t *card) { return false; } - // Finialize initialization + // Finalize initialization card->state->initialized = true; return true; } diff --git a/ports/nrf/boards/MICROBIT/modules/microbitfont.h b/ports/nrf/boards/MICROBIT/modules/microbitfont.h index 2ae0c8fab86ba..6e80acfe07214 100644 --- a/ports/nrf/boards/MICROBIT/modules/microbitfont.h +++ b/ports/nrf/boards/MICROBIT/modules/microbitfont.h @@ -36,7 +36,7 @@ DEALINGS IN THE SOFTWARE. * * Example: { 0x08, 0x08, 0x08, 0x0, 0x08 } * - * The above will produce an exclaimation mark on the second column in form the left. + * The above will produce an exclamation mark on the second column in form the left. * * We could compress further, but the complexity of decode would likely outweigh the gains. */ diff --git a/ports/rp2/main.c b/ports/rp2/main.c index d6bf448267152..58da63c06f12d 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -90,7 +90,7 @@ int main(int argc, char **argv) { // Set the MCU frequency and as a side effect the peripheral clock to 48 MHz. set_sys_clock_khz(SYS_CLK_KHZ, false); - // Hook for setting up anything that needs to be super early in the bootup process. + // Hook for setting up anything that needs to be super early in the boot-up process. MICROPY_BOARD_STARTUP(); #if MICROPY_HW_ENABLE_UART_REPL diff --git a/ports/samd/mcu/samd21/mpconfigmcu.h b/ports/samd/mcu/samd21/mpconfigmcu.h index 29965f50f63a8..f0a7a73e0c027 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.h +++ b/ports/samd/mcu/samd21/mpconfigmcu.h @@ -1,4 +1,4 @@ -// Deinitions common to all SAMD21 boards +// Definitions common to all SAMD21 boards #include "samd21.h" #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES) diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h index 9a7b8528f3573..831949bab56a0 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.h +++ b/ports/samd/mcu/samd51/mpconfigmcu.h @@ -1,4 +1,4 @@ -// Deinitions common to all SAMD51 boards +// Definitions common to all SAMD51 boards #include "samd51.h" #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) diff --git a/ports/windows/windows_mphal.c b/ports/windows/windows_mphal.c index b636393f5401c..5a65b2fd2aeae 100644 --- a/ports/windows/windows_mphal.c +++ b/ports/windows/windows_mphal.c @@ -70,7 +70,7 @@ void mp_hal_stdio_mode_orig(void) { } // Handler to be installed by SetConsoleCtrlHandler, currently used only to handle Ctrl-C. -// This handler has to be installed just once (this has to be done elswhere in init code). +// This handler has to be installed just once (this has to be done elsewhere in init code). // Previous versions of the mp_hal code would install a handler whenever Ctrl-C input is // allowed and remove the handler again when it is not. That is not necessary though (1), // and it might introduce problems (2) because console notifications are delivered to the diff --git a/py/reader.h b/py/reader.h index 301c70ab3f0d4..6378457007cb3 100644 --- a/py/reader.h +++ b/py/reader.h @@ -48,7 +48,7 @@ void mp_reader_new_file(mp_reader_t *reader, qstr filename); void mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd); // Try to efficiently read the given number of bytes from a ROM-based reader. -// Returns a valid, non-NULL pointer to the requseted data if the reader points to ROM. +// Returns a valid, non-NULL pointer to the requested data if the reader points to ROM. // Returns NULL if the reader does not point to ROM. const uint8_t *mp_reader_try_read_rom(mp_reader_t *reader, size_t len); diff --git a/pyproject.toml b/pyproject.toml index 1650bd088ea22..aeb25c8a03a2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.codespell] count = "" ignore-regex = '\b[A-Z]{3}\b' -ignore-words-list = "ans,asend,deques,dout,extint,hsi,iput,mis,numer,shft,synopsys,technic,ure" +ignore-words-list = "ans,asend,deques,dout,emac,extint,hsi,iput,mis,numer,shft,synopsys,technic,ure" quiet-level = 3 skip = """ */build*,\ From 3f0dd13d931c88a759a779853dfeaee566f0e79a Mon Sep 17 00:00:00 2001 From: Ronald Weber Date: Mon, 24 Feb 2025 17:34:29 +0100 Subject: [PATCH 313/546] docs: Fix double 'the' in documentation. Signed-off-by: Ronald Weber --- docs/esp8266/tutorial/repl.rst | 2 +- docs/library/framebuf.rst | 2 +- docs/library/machine.UART.rst | 2 +- docs/reference/repl.rst | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/esp8266/tutorial/repl.rst b/docs/esp8266/tutorial/repl.rst index bc0142aaef5f9..db55d45facde8 100644 --- a/docs/esp8266/tutorial/repl.rst +++ b/docs/esp8266/tutorial/repl.rst @@ -38,7 +38,7 @@ browser. The latest versions of Firefox and Chrome are supported. For your convenience, WebREPL client is hosted at ``__. Alternatively, you can install it -locally from the the GitHub repository +locally from the GitHub repository ``__. Before connecting to WebREPL, you should set a password and enable it via diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index 149f4d6609be9..f22a3613bdbcb 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -114,7 +114,7 @@ Drawing text .. method:: FrameBuffer.text(s, x, y[, c]) - Write text to the FrameBuffer using the the coordinates as the upper-left + Write text to the FrameBuffer using the coordinates as the upper-left corner of the text. The color of the text can be defined by the optional argument but is otherwise a default value of 1. All characters have dimensions of 8x8 pixels and there is currently no way to change the font. diff --git a/docs/library/machine.UART.rst b/docs/library/machine.UART.rst index 4dcb4a1e7c4a6..5be79cccce884 100644 --- a/docs/library/machine.UART.rst +++ b/docs/library/machine.UART.rst @@ -83,7 +83,7 @@ Methods - *pins* is a 4 or 2 item list indicating the TX, RX, RTS and CTS pins (in that order). Any of the pins can be None if one wants the UART to operate with limited functionality. - If the RTS pin is given the the RX pin must be given as well. The same applies to CTS. + If the RTS pin is given the RX pin must be given as well. The same applies to CTS. When no pins are given, then the default set of TX and RX pins is taken, and hardware flow control will be disabled. If *pins* is ``None``, no pin assignment will be made. diff --git a/docs/reference/repl.rst b/docs/reference/repl.rst index be02ddebde8e2..2c019350f7e2a 100644 --- a/docs/reference/repl.rst +++ b/docs/reference/repl.rst @@ -213,7 +213,7 @@ echo turned off, and with optional flow control. Raw mode is entered using Ctrl-A. You then send your python code, followed by a Ctrl-D. The Ctrl-D will be acknowledged by 'OK' and then the python code will be compiled and executed. Any output (or errors) will be sent back. Entering -Ctrl-B will leave raw mode and return the the regular (aka friendly) REPL. +Ctrl-B will leave raw mode and return the regular (aka friendly) REPL. Raw-paste mode is an additional mode within the raw REPL that includes flow control, and which compiles code as it receives it. This makes it more robust for high-speed From dc2fcfcc5511a371ff684f7d7772e7a7b479246d Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 18 Feb 2025 10:01:09 +0100 Subject: [PATCH 314/546] all: Upgrade to ruff v0.9.6. Signed-off-by: Christian Clauss --- .github/workflows/ruff.yml | 2 +- .pre-commit-config.yaml | 2 +- ports/nrf/examples/seeed_tft.py | 1 + pyproject.toml | 22 ++++++++++++++----- tests/cpydiff/builtin_next_arg2.py | 1 + tests/cpydiff/core_class_delnotimpl.py | 1 + tests/cpydiff/core_fstring_concat.py | 2 ++ tests/cpydiff/core_function_argcount.py | 1 + tests/cpydiff/core_import_all.py | 1 + tests/cpydiff/core_import_path.py | 1 + tests/cpydiff/core_import_split_ns_pkgs.py | 1 + tests/cpydiff/core_locals_eval.py | 1 + tests/cpydiff/module_array_comparison.py | 1 + tests/cpydiff/module_array_constructor.py | 1 + tests/cpydiff/modules_array_containment.py | 1 + tests/cpydiff/modules_array_deletion.py | 1 + tests/cpydiff/modules_array_subscrstep.py | 1 + tests/cpydiff/modules_json_nonserializable.py | 1 + tests/cpydiff/modules_os_environ.py | 1 + tests/cpydiff/modules_os_getenv.py | 1 + tests/cpydiff/modules_struct_fewargs.py | 1 + tests/cpydiff/modules_struct_manyargs.py | 1 + .../modules_struct_whitespace_in_format.py | 1 + tests/cpydiff/modules_sys_stdassign.py | 1 + tests/cpydiff/syntax_assign_expr.py | 1 + tests/cpydiff/syntax_spaces.py | 1 + tests/cpydiff/syntax_unicode_nameesc.py | 1 + tests/cpydiff/types_bytearray_sliceassign.py | 1 + tests/cpydiff/types_bytes_format.py | 1 + tests/cpydiff/types_bytes_keywords.py | 1 + tests/cpydiff/types_bytes_subscrstep.py | 1 + tests/cpydiff/types_dict_keys_set.py | 1 + tests/cpydiff/types_exception_attrs.py | 1 + tests/cpydiff/types_exception_chaining.py | 1 + tests/cpydiff/types_exception_instancevar.py | 1 + tests/cpydiff/types_exception_loops.py | 1 + tests/cpydiff/types_float_rounding.py | 1 + tests/cpydiff/types_list_delete_subscrstep.py | 1 + tests/cpydiff/types_list_store_noniter.py | 1 + tests/cpydiff/types_list_store_subscrstep.py | 1 + tests/cpydiff/types_memoryview_invalid.py | 1 + tests/cpydiff/types_str_endswith.py | 1 + tests/cpydiff/types_str_formatsubscr.py | 1 + tests/cpydiff/types_str_keywords.py | 1 + tests/cpydiff/types_str_ljust_rjust.py | 1 + tests/cpydiff/types_str_rsplitnone.py | 1 + tests/cpydiff/types_str_subscrstep.py | 1 + tests/cpydiff/types_tuple_subscrstep.py | 1 + tests/ports/cc3200/pin.py | 1 + tests/unicode/unicode.py | 2 +- tools/gen-cpydiff.py | 6 ++--- tools/mpremote/mpremote/transport.py | 2 +- tools/pyboard.py | 2 +- tools/pydfu.py | 5 ++--- 54 files changed, 73 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 29e9ddbf8b61a..9265c25bcba13 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -8,6 +8,6 @@ jobs: steps: - uses: actions/checkout@v4 # ruff version should be kept in sync with .pre-commit-config.yaml - - run: pip install --user ruff==0.1.3 + - run: pipx install ruff==0.9.6 - run: ruff check --output-format=github . - run: ruff format --diff . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 193b2c6e83559..d07f9b0fda2a9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: stages: [commit-msg] - repo: https://github.com/charliermarsh/ruff-pre-commit # Version should be kept in sync with .github/workflows/ruff.yml - rev: v0.1.3 + rev: v0.9.6 hooks: - id: ruff - id: ruff-format diff --git a/ports/nrf/examples/seeed_tft.py b/ports/nrf/examples/seeed_tft.py index f53b560226363..263da7ae0be7f 100644 --- a/ports/nrf/examples/seeed_tft.py +++ b/ports/nrf/examples/seeed_tft.py @@ -44,6 +44,7 @@ tf = mount_tf() os.listdir() """ + import vfs import time import framebuf diff --git a/pyproject.toml b/pyproject.toml index aeb25c8a03a2b..d5e3d0e847464 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,17 @@ line-length = 99 target-version = "py37" [tool.ruff.lint] +exclude = [ # Ruff finds Python SyntaxError in these files + "tests/cmdline/repl_autocomplete.py", + "tests/cmdline/repl_autoindent.py", + "tests/cmdline/repl_basic.py", + "tests/cmdline/repl_cont.py", + "tests/cmdline/repl_emacs_keys.py", + "tests/cmdline/repl_words_move.py", + "tests/feature_check/repl_emacs_check.py", + "tests/feature_check/repl_words_move_check.py", + "tests/micropython/viper_args.py", +] extend-select = ["C9", "PLC"] ignore = [ "E401", @@ -37,14 +48,12 @@ ignore = [ "F401", "F403", "F405", - "PLC1901", + "PLC0206", ] +mccabe.max-complexity = 40 -[tool.ruff.mccabe] -max-complexity = 40 - -[tool.ruff.per-file-ignores] -# Exclude all tests from linting (does not apply to formatting). +[tool.ruff.lint.per-file-ignores] +# Exclude all tests from linting. "tests/**/*.py" = ["ALL"] "ports/cc3200/tools/uniflash.py" = ["E711"] # manifest.py files are evaluated with some global names pre-defined @@ -57,3 +66,4 @@ max-complexity = 40 # repl_: not real python files # viper_args: uses f(*) exclude = ["tests/basics/*.py", "tests/*/repl_*.py", "tests/micropython/viper_args.py"] +quote-style = "preserve" diff --git a/tests/cpydiff/builtin_next_arg2.py b/tests/cpydiff/builtin_next_arg2.py index 5df2d6e70f837..ed9565fe0fe77 100644 --- a/tests/cpydiff/builtin_next_arg2.py +++ b/tests/cpydiff/builtin_next_arg2.py @@ -9,4 +9,5 @@ except StopIteration: val = deflt """ + print(next(iter(range(0)), 42)) diff --git a/tests/cpydiff/core_class_delnotimpl.py b/tests/cpydiff/core_class_delnotimpl.py index 18c176e9bb1f7..6fac764ac9828 100644 --- a/tests/cpydiff/core_class_delnotimpl.py +++ b/tests/cpydiff/core_class_delnotimpl.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Unknown """ + import gc diff --git a/tests/cpydiff/core_fstring_concat.py b/tests/cpydiff/core_fstring_concat.py index 83bf18820ece1..3daa13d75360e 100644 --- a/tests/cpydiff/core_fstring_concat.py +++ b/tests/cpydiff/core_fstring_concat.py @@ -6,7 +6,9 @@ """ x, y = 1, 2 +# fmt: off print("aa" f"{x}") # works print(f"{x}" "ab") # works print("a{}a" f"{x}") # fails print(f"{x}" "a{}b") # fails +# fmt: on diff --git a/tests/cpydiff/core_function_argcount.py b/tests/cpydiff/core_function_argcount.py index 5f3dca4dcdbe4..c257def726aca 100644 --- a/tests/cpydiff/core_function_argcount.py +++ b/tests/cpydiff/core_function_argcount.py @@ -4,6 +4,7 @@ cause: MicroPython counts "self" as an argument. workaround: Interpret error messages with the information above in mind. """ + try: [].append() except Exception as e: diff --git a/tests/cpydiff/core_import_all.py b/tests/cpydiff/core_import_all.py index 5adf9ae3eb1e6..0fbe9d4d4ecd6 100644 --- a/tests/cpydiff/core_import_all.py +++ b/tests/cpydiff/core_import_all.py @@ -4,6 +4,7 @@ cause: Not implemented. workaround: Manually import the sub-modules directly in __init__.py using ``from . import foo, bar``. """ + from modules3 import * foo.hello() diff --git a/tests/cpydiff/core_import_path.py b/tests/cpydiff/core_import_path.py index 959fd571f57ab..2028386be84b0 100644 --- a/tests/cpydiff/core_import_path.py +++ b/tests/cpydiff/core_import_path.py @@ -4,6 +4,7 @@ cause: MicroPython doesn't support namespace packages split across filesystem. Beyond that, MicroPython's import system is highly optimized for minimal memory usage. workaround: Details of import handling is inherently implementation dependent. Don't rely on such details in portable applications. """ + import modules print(modules.__path__) diff --git a/tests/cpydiff/core_import_split_ns_pkgs.py b/tests/cpydiff/core_import_split_ns_pkgs.py index 5c92b63124a50..a1cfd84893d4e 100644 --- a/tests/cpydiff/core_import_split_ns_pkgs.py +++ b/tests/cpydiff/core_import_split_ns_pkgs.py @@ -4,6 +4,7 @@ cause: MicroPython's import system is highly optimized for simplicity, minimal memory usage, and minimal filesystem search overhead. workaround: Don't install modules belonging to the same namespace package in different directories. For MicroPython, it's recommended to have at most 3-component module search paths: for your current application, per-user (writable), system-wide (non-writable). """ + import sys sys.path.append(sys.path[1] + "/modules") diff --git a/tests/cpydiff/core_locals_eval.py b/tests/cpydiff/core_locals_eval.py index 025d226372986..3f6ad2a1dbbc6 100644 --- a/tests/cpydiff/core_locals_eval.py +++ b/tests/cpydiff/core_locals_eval.py @@ -4,6 +4,7 @@ cause: MicroPython doesn't maintain symbolic local environment, it is optimized to an array of slots. Thus, local variables can't be accessed by a name. Effectively, ``eval(expr)`` in MicroPython is equivalent to ``eval(expr, globals(), globals())``. workaround: Unknown """ + val = 1 diff --git a/tests/cpydiff/module_array_comparison.py b/tests/cpydiff/module_array_comparison.py index a442af3f5bc96..4929b1e590dac 100644 --- a/tests/cpydiff/module_array_comparison.py +++ b/tests/cpydiff/module_array_comparison.py @@ -4,6 +4,7 @@ cause: Code size workaround: Compare individual elements """ + import array array.array("b", [1, 2]) == array.array("i", [1, 2]) diff --git a/tests/cpydiff/module_array_constructor.py b/tests/cpydiff/module_array_constructor.py index 08cf2ef2d1c3c..a53589d22ae76 100644 --- a/tests/cpydiff/module_array_constructor.py +++ b/tests/cpydiff/module_array_constructor.py @@ -4,6 +4,7 @@ cause: MicroPython implements implicit truncation in order to reduce code size and execution time workaround: If CPython compatibility is needed then mask the value explicitly """ + import array a = array.array("b", [257]) diff --git a/tests/cpydiff/modules_array_containment.py b/tests/cpydiff/modules_array_containment.py index 8f25b0d49149d..b29895aa75892 100644 --- a/tests/cpydiff/modules_array_containment.py +++ b/tests/cpydiff/modules_array_containment.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Unknown """ + import array print(1 in array.array("B", b"12")) diff --git a/tests/cpydiff/modules_array_deletion.py b/tests/cpydiff/modules_array_deletion.py index 3376527373edf..2e7c31124b2da 100644 --- a/tests/cpydiff/modules_array_deletion.py +++ b/tests/cpydiff/modules_array_deletion.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Unknown """ + import array a = array.array("b", (1, 2, 3)) diff --git a/tests/cpydiff/modules_array_subscrstep.py b/tests/cpydiff/modules_array_subscrstep.py index 24308bd9042d1..65b12332f54cd 100644 --- a/tests/cpydiff/modules_array_subscrstep.py +++ b/tests/cpydiff/modules_array_subscrstep.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Unknown """ + import array a = array.array("b", (1, 2, 3)) diff --git a/tests/cpydiff/modules_json_nonserializable.py b/tests/cpydiff/modules_json_nonserializable.py index d6a5660cadfdb..d83e7beca2d5a 100644 --- a/tests/cpydiff/modules_json_nonserializable.py +++ b/tests/cpydiff/modules_json_nonserializable.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Unknown """ + import json try: diff --git a/tests/cpydiff/modules_os_environ.py b/tests/cpydiff/modules_os_environ.py index 491d8a3101636..4edde16656781 100644 --- a/tests/cpydiff/modules_os_environ.py +++ b/tests/cpydiff/modules_os_environ.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Use ``getenv``, ``putenv`` and ``unsetenv`` """ + import os try: diff --git a/tests/cpydiff/modules_os_getenv.py b/tests/cpydiff/modules_os_getenv.py index d1e828438e456..d5acd414eb3da 100644 --- a/tests/cpydiff/modules_os_getenv.py +++ b/tests/cpydiff/modules_os_getenv.py @@ -4,6 +4,7 @@ cause: The ``environ`` attribute is not implemented workaround: Unknown """ + import os print(os.getenv("NEW_VARIABLE")) diff --git a/tests/cpydiff/modules_struct_fewargs.py b/tests/cpydiff/modules_struct_fewargs.py index cb6b0fd874ec4..49b2a3213c898 100644 --- a/tests/cpydiff/modules_struct_fewargs.py +++ b/tests/cpydiff/modules_struct_fewargs.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Unknown """ + import struct try: diff --git a/tests/cpydiff/modules_struct_manyargs.py b/tests/cpydiff/modules_struct_manyargs.py index 03395baad3b59..e3b78930f219f 100644 --- a/tests/cpydiff/modules_struct_manyargs.py +++ b/tests/cpydiff/modules_struct_manyargs.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Unknown """ + import struct try: diff --git a/tests/cpydiff/modules_struct_whitespace_in_format.py b/tests/cpydiff/modules_struct_whitespace_in_format.py index a882b38569a95..a7a1d2facdfff 100644 --- a/tests/cpydiff/modules_struct_whitespace_in_format.py +++ b/tests/cpydiff/modules_struct_whitespace_in_format.py @@ -4,6 +4,7 @@ cause: MicroPython is optimised for code size. workaround: Don't use spaces in format strings. """ + import struct try: diff --git a/tests/cpydiff/modules_sys_stdassign.py b/tests/cpydiff/modules_sys_stdassign.py index 7d086078a93d6..29afe1b1203c9 100644 --- a/tests/cpydiff/modules_sys_stdassign.py +++ b/tests/cpydiff/modules_sys_stdassign.py @@ -4,6 +4,7 @@ cause: They are stored in read-only memory. workaround: Unknown """ + import sys sys.stdin = None diff --git a/tests/cpydiff/syntax_assign_expr.py b/tests/cpydiff/syntax_assign_expr.py index d4ed063b39ae7..58f57ca1fbe02 100644 --- a/tests/cpydiff/syntax_assign_expr.py +++ b/tests/cpydiff/syntax_assign_expr.py @@ -4,4 +4,5 @@ cause: MicroPython is optimised for code size and doesn't check this case. workaround: Do not rely on this behaviour if writing CPython compatible code. """ + print([i := -1 for i in range(4)]) diff --git a/tests/cpydiff/syntax_spaces.py b/tests/cpydiff/syntax_spaces.py index c308240a78da5..03d25d56199d1 100644 --- a/tests/cpydiff/syntax_spaces.py +++ b/tests/cpydiff/syntax_spaces.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Unknown """ + try: print(eval("1and 0")) except SyntaxError: diff --git a/tests/cpydiff/syntax_unicode_nameesc.py b/tests/cpydiff/syntax_unicode_nameesc.py index 21628c974d80f..838394b6ebd09 100644 --- a/tests/cpydiff/syntax_unicode_nameesc.py +++ b/tests/cpydiff/syntax_unicode_nameesc.py @@ -4,4 +4,5 @@ cause: Unknown workaround: Unknown """ + print("\N{LATIN SMALL LETTER A}") diff --git a/tests/cpydiff/types_bytearray_sliceassign.py b/tests/cpydiff/types_bytearray_sliceassign.py index e4068b04b988b..353f61988fa04 100644 --- a/tests/cpydiff/types_bytearray_sliceassign.py +++ b/tests/cpydiff/types_bytearray_sliceassign.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Unknown """ + b = bytearray(4) b[0:1] = [1, 2] print(b) diff --git a/tests/cpydiff/types_bytes_format.py b/tests/cpydiff/types_bytes_format.py index ad0498771165c..1a15e572c8ade 100644 --- a/tests/cpydiff/types_bytes_format.py +++ b/tests/cpydiff/types_bytes_format.py @@ -4,4 +4,5 @@ cause: MicroPython strives to be a more regular implementation, so if both `str` and `bytes` support ``__mod__()`` (the % operator), it makes sense to support ``format()`` for both too. Support for ``__mod__`` can also be compiled out, which leaves only ``format()`` for bytes formatting. workaround: If you are interested in CPython compatibility, don't use ``.format()`` on bytes objects. """ + print(b"{}".format(1)) diff --git a/tests/cpydiff/types_bytes_keywords.py b/tests/cpydiff/types_bytes_keywords.py index ade83d0a709eb..a459c94b41ed9 100644 --- a/tests/cpydiff/types_bytes_keywords.py +++ b/tests/cpydiff/types_bytes_keywords.py @@ -4,4 +4,5 @@ cause: Unknown workaround: Pass the encoding as a positional parameter, e.g. ``print(bytes('abc', 'utf-8'))`` """ + print(bytes("abc", encoding="utf8")) diff --git a/tests/cpydiff/types_bytes_subscrstep.py b/tests/cpydiff/types_bytes_subscrstep.py index 51b94cb710f1c..c566cbdb630aa 100644 --- a/tests/cpydiff/types_bytes_subscrstep.py +++ b/tests/cpydiff/types_bytes_subscrstep.py @@ -4,4 +4,5 @@ cause: MicroPython is highly optimized for memory usage. workaround: Use explicit loop for this very rare operation. """ + print(b"123"[0:3:2]) diff --git a/tests/cpydiff/types_dict_keys_set.py b/tests/cpydiff/types_dict_keys_set.py index 3a0849a35564c..a5f127962e6c2 100644 --- a/tests/cpydiff/types_dict_keys_set.py +++ b/tests/cpydiff/types_dict_keys_set.py @@ -4,4 +4,5 @@ cause: Not implemented. workaround: Explicitly convert keys to a set before using set operations. """ + print({1: 2, 3: 4}.keys() & {1}) diff --git a/tests/cpydiff/types_exception_attrs.py b/tests/cpydiff/types_exception_attrs.py index ad72b62a61a5d..5fed45451d2d9 100644 --- a/tests/cpydiff/types_exception_attrs.py +++ b/tests/cpydiff/types_exception_attrs.py @@ -4,6 +4,7 @@ cause: MicroPython is optimised to reduce code size. workaround: Only use ``value`` on ``StopIteration`` exceptions, and ``errno`` on ``OSError`` exceptions. Do not use or rely on these attributes on other exceptions. """ + e = Exception(1) print(e.value) print(e.errno) diff --git a/tests/cpydiff/types_exception_chaining.py b/tests/cpydiff/types_exception_chaining.py index 836c4eb3e73f5..cff68d4124aec 100644 --- a/tests/cpydiff/types_exception_chaining.py +++ b/tests/cpydiff/types_exception_chaining.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Unknown """ + try: raise TypeError except TypeError: diff --git a/tests/cpydiff/types_exception_instancevar.py b/tests/cpydiff/types_exception_instancevar.py index adc353361f015..fb67771baf33e 100644 --- a/tests/cpydiff/types_exception_instancevar.py +++ b/tests/cpydiff/types_exception_instancevar.py @@ -4,6 +4,7 @@ cause: MicroPython is highly optimized for memory usage. workaround: Use user-defined exception subclasses. """ + e = Exception() e.x = 0 print(e.x) diff --git a/tests/cpydiff/types_exception_loops.py b/tests/cpydiff/types_exception_loops.py index 8d326cbbbb000..549f1dd0a5bf0 100644 --- a/tests/cpydiff/types_exception_loops.py +++ b/tests/cpydiff/types_exception_loops.py @@ -4,6 +4,7 @@ cause: Condition checks are optimized to happen at the end of loop body, and that line number is reported. workaround: Unknown """ + l = ["-foo", "-bar"] i = 0 diff --git a/tests/cpydiff/types_float_rounding.py b/tests/cpydiff/types_float_rounding.py index a5b591966b0de..206e359ed9be7 100644 --- a/tests/cpydiff/types_float_rounding.py +++ b/tests/cpydiff/types_float_rounding.py @@ -4,4 +4,5 @@ cause: Unknown workaround: Unknown """ + print("%.1g" % -9.9) diff --git a/tests/cpydiff/types_list_delete_subscrstep.py b/tests/cpydiff/types_list_delete_subscrstep.py index 36e6f526b3d23..3c801d84949e0 100644 --- a/tests/cpydiff/types_list_delete_subscrstep.py +++ b/tests/cpydiff/types_list_delete_subscrstep.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Use explicit loop for this rare operation. """ + l = [1, 2, 3, 4] del l[0:4:2] print(l) diff --git a/tests/cpydiff/types_list_store_noniter.py b/tests/cpydiff/types_list_store_noniter.py index 1d69b4a823188..581b1369801ac 100644 --- a/tests/cpydiff/types_list_store_noniter.py +++ b/tests/cpydiff/types_list_store_noniter.py @@ -4,6 +4,7 @@ cause: RHS is restricted to be a tuple or list workaround: Use ``list()`` on RHS to convert the iterable to a list """ + l = [10, 20] l[0:1] = range(4) print(l) diff --git a/tests/cpydiff/types_list_store_subscrstep.py b/tests/cpydiff/types_list_store_subscrstep.py index 1460372bb1754..97590267f4a64 100644 --- a/tests/cpydiff/types_list_store_subscrstep.py +++ b/tests/cpydiff/types_list_store_subscrstep.py @@ -4,6 +4,7 @@ cause: Unknown workaround: Use explicit loop for this rare operation. """ + l = [1, 2, 3, 4] l[0:4:2] = [5, 6] print(l) diff --git a/tests/cpydiff/types_memoryview_invalid.py b/tests/cpydiff/types_memoryview_invalid.py index c995a2e8991c6..f288c50ab57b7 100644 --- a/tests/cpydiff/types_memoryview_invalid.py +++ b/tests/cpydiff/types_memoryview_invalid.py @@ -6,6 +6,7 @@ In the worst case scenario, resizing an object which is the target of a memoryview can cause the memoryview(s) to reference invalid freed memory (a use-after-free bug) and corrupt the MicroPython runtime. workaround: Do not change the size of any ``bytearray`` or ``io.bytesIO`` object that has a ``memoryview`` assigned to it. """ + b = bytearray(b"abcdefg") m = memoryview(b) b.extend(b"hijklmnop") diff --git a/tests/cpydiff/types_str_endswith.py b/tests/cpydiff/types_str_endswith.py index f222ac1cd3ad3..890c7ba5ef47f 100644 --- a/tests/cpydiff/types_str_endswith.py +++ b/tests/cpydiff/types_str_endswith.py @@ -4,4 +4,5 @@ cause: Unknown workaround: Unknown """ + print("abc".endswith("c", 1)) diff --git a/tests/cpydiff/types_str_formatsubscr.py b/tests/cpydiff/types_str_formatsubscr.py index 1b83cfff6cd34..5c59a1d200a9b 100644 --- a/tests/cpydiff/types_str_formatsubscr.py +++ b/tests/cpydiff/types_str_formatsubscr.py @@ -4,4 +4,5 @@ cause: Unknown workaround: Unknown """ + print("{a[0]}".format(a=[1, 2])) diff --git a/tests/cpydiff/types_str_keywords.py b/tests/cpydiff/types_str_keywords.py index 77a4eac1c1db2..640dfa6c39132 100644 --- a/tests/cpydiff/types_str_keywords.py +++ b/tests/cpydiff/types_str_keywords.py @@ -4,4 +4,5 @@ cause: Unknown workaround: Input the encoding format directly. eg ``print(bytes('abc', 'utf-8'))`` """ + print(str(b"abc", encoding="utf8")) diff --git a/tests/cpydiff/types_str_ljust_rjust.py b/tests/cpydiff/types_str_ljust_rjust.py index 72e5105e025b2..7f91c137e905f 100644 --- a/tests/cpydiff/types_str_ljust_rjust.py +++ b/tests/cpydiff/types_str_ljust_rjust.py @@ -4,4 +4,5 @@ cause: MicroPython is highly optimized for memory usage. Easy workarounds available. workaround: Instead of ``s.ljust(10)`` use ``"%-10s" % s``, instead of ``s.rjust(10)`` use ``"% 10s" % s``. Alternatively, ``"{:<10}".format(s)`` or ``"{:>10}".format(s)``. """ + print("abc".ljust(10)) diff --git a/tests/cpydiff/types_str_rsplitnone.py b/tests/cpydiff/types_str_rsplitnone.py index 5d334fea2f846..ae524620a08a8 100644 --- a/tests/cpydiff/types_str_rsplitnone.py +++ b/tests/cpydiff/types_str_rsplitnone.py @@ -4,4 +4,5 @@ cause: Unknown workaround: Unknown """ + print("a a a".rsplit(None, 1)) diff --git a/tests/cpydiff/types_str_subscrstep.py b/tests/cpydiff/types_str_subscrstep.py index 2d3245e5582f5..cd57f18ccb345 100644 --- a/tests/cpydiff/types_str_subscrstep.py +++ b/tests/cpydiff/types_str_subscrstep.py @@ -4,4 +4,5 @@ cause: Unknown workaround: Unknown """ + print("abcdefghi"[0:9:2]) diff --git a/tests/cpydiff/types_tuple_subscrstep.py b/tests/cpydiff/types_tuple_subscrstep.py index f90f3c5bf4d16..60ba31af4cf74 100644 --- a/tests/cpydiff/types_tuple_subscrstep.py +++ b/tests/cpydiff/types_tuple_subscrstep.py @@ -4,4 +4,5 @@ cause: Unknown workaround: Unknown """ + print((1, 2, 3, 4)[0:4:2]) diff --git a/tests/ports/cc3200/pin.py b/tests/ports/cc3200/pin.py index 43537bba5ce07..d7b0fa976cd2a 100644 --- a/tests/ports/cc3200/pin.py +++ b/tests/ports/cc3200/pin.py @@ -3,6 +3,7 @@ pull up or pull down connected. GP12 and GP17 must be connected together """ + from machine import Pin import os diff --git a/tests/unicode/unicode.py b/tests/unicode/unicode.py index 072e049fde416..58d406e63eb2a 100644 --- a/tests/unicode/unicode.py +++ b/tests/unicode/unicode.py @@ -5,7 +5,7 @@ # Test all three forms of Unicode escape, and # all blocks of UTF-8 byte patterns -s = "a\xA9\xFF\u0123\u0800\uFFEE\U0001F44C" +s = "a\xa9\xff\u0123\u0800\uffee\U0001f44c" for i in range(-len(s), len(s)): print("s[%d]: %s %X" % (i, s[i], ord(s[i]))) print("s[:%d]: %d chars, '%s'" % (i, len(s[:i]), s[:i])) diff --git a/tools/gen-cpydiff.py b/tools/gen-cpydiff.py index 578b6c136f568..c0b5734389cdf 100644 --- a/tools/gen-cpydiff.py +++ b/tools/gen-cpydiff.py @@ -22,9 +22,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -""" gen-cpydiff generates documentation which outlines operations that differ between MicroPython - and CPython. This script is called by the docs Makefile for html and Latex and may be run - manually using the command make gen-cpydiff. """ +"""gen-cpydiff generates documentation which outlines operations that differ between MicroPython +and CPython. This script is called by the docs Makefile for html and Latex and may be run +manually using the command make gen-cpydiff.""" import os import subprocess diff --git a/tools/mpremote/mpremote/transport.py b/tools/mpremote/mpremote/transport.py index d845796953a29..c8fdc54a8891a 100644 --- a/tools/mpremote/mpremote/transport.py +++ b/tools/mpremote/mpremote/transport.py @@ -73,7 +73,7 @@ def fs_listdir(self, src=""): def repr_consumer(b): buf.extend(b.replace(b"\x04", b"")) - cmd = "import os\nfor f in os.ilistdir(%s):\n" " print(repr(f), end=',')" % ( + cmd = "import os\nfor f in os.ilistdir(%s):\n print(repr(f), end=',')" % ( ("'%s'" % src) if src else "" ) try: diff --git a/tools/pyboard.py b/tools/pyboard.py index 0cf5b3d46596a..d49365f617efd 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -530,7 +530,7 @@ def fs_listdir(self, src=""): def repr_consumer(b): buf.extend(b.replace(b"\x04", b"")) - cmd = "import os\nfor f in os.ilistdir(%s):\n" " print(repr(f), end=',')" % ( + cmd = "import os\nfor f in os.ilistdir(%s):\n print(repr(f), end=',')" % ( ("'%s'" % src) if src else "" ) try: diff --git a/tools/pydfu.py b/tools/pydfu.py index e1e4074a6baff..cd7354818cdea 100755 --- a/tools/pydfu.py +++ b/tools/pydfu.py @@ -344,8 +344,7 @@ def read_dfu_file(filename): # B uint8_t targets Number of targets dfu_prefix, data = consume("<5sBIB", data, "signature version size targets") print( - " %(signature)s v%(version)d, image size: %(size)d, " - "targets: %(targets)d" % dfu_prefix + " %(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d" % dfu_prefix ) for target_idx in range(dfu_prefix["targets"]): # Decode the Image Prefix @@ -359,7 +358,7 @@ def read_dfu_file(filename): # I uint32_t size Size of image (without prefix) # I uint32_t elements Number of elements in the image img_prefix, data = consume( - "<6sBI255s2I", data, "signature altsetting named name " "size elements" + "<6sBI255s2I", data, "signature altsetting named name size elements" ) img_prefix["num"] = target_idx if img_prefix["named"]: From 8ce7a58be21eb77532b109594abcc4e47aa8d887 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 25 Feb 2025 10:43:01 +0100 Subject: [PATCH 315/546] tests: Four typos in tests directory. Found by codespell. Signed-off-by: Christian Clauss --- tests/README.md | 2 +- tests/extmod_hardware/machine_pwm.py | 2 +- tests/ports/esp32/esp32_nvs.py | 2 +- tests/run-tests.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/README.md b/tests/README.md index 891668c8b8737..21e14eee5e128 100644 --- a/tests/README.md +++ b/tests/README.md @@ -13,7 +13,7 @@ target platform and run the appropriate set of tests for that platform. For exa That will run tests on the `/dev/ttyACM0` serial port. You can also use shortcut device names like `a` for `/dev/ttyACM` and `c` for `COM`. Use -`./run-tests.py --help` to see all of the device possibilites, and other options. +`./run-tests.py --help` to see all of the device possibilities, and other options. There are three kinds of tests: diff --git a/tests/extmod_hardware/machine_pwm.py b/tests/extmod_hardware/machine_pwm.py index 014030be5c601..e27da32548659 100644 --- a/tests/extmod_hardware/machine_pwm.py +++ b/tests/extmod_hardware/machine_pwm.py @@ -1,4 +1,4 @@ -# Test machine.PWM, frequncy and duty cycle (using machine.time_pulse_us). +# Test machine.PWM, frequency and duty cycle (using machine.time_pulse_us). # # IMPORTANT: This test requires hardware connections: the PWM-output and pulse-input # pins must be wired together (see the variable `pwm_pulse_pins`). diff --git a/tests/ports/esp32/esp32_nvs.py b/tests/ports/esp32/esp32_nvs.py index fd8b152ca71d8..e6c308cde2f30 100644 --- a/tests/ports/esp32/esp32_nvs.py +++ b/tests/ports/esp32/esp32_nvs.py @@ -4,7 +4,7 @@ nvs = NVS("mp-test") -# test setting and gettin an integer kv +# test setting and getting an integer kv nvs.set_i32("key1", 1234) print(nvs.get_i32("key1")) nvs.set_i32("key2", -503) diff --git a/tests/run-tests.py b/tests/run-tests.py index b108b16395074..1af3dfc2973fc 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -848,7 +848,7 @@ def run_one_test(test_file): test_file_abspath = os.path.abspath(test_file).replace("\\", "/") if args.filters: - # Default verdict is the opposit of the first action + # Default verdict is the opposite of the first action verdict = "include" if args.filters[0][0] == "exclude" else "exclude" for action, pat in args.filters: if pat.search(test_file): From 731f7adf4f4541a36b32d59087148f78b39d7a27 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 18 Feb 2025 16:11:13 +1100 Subject: [PATCH 316/546] stm32/sdcard: Fix unchecked uint32_t overflow in SD card driver. Manifests as `readblocks(-1, buf)` failing. The ST HAL does a bounds check, but it checks `(block_num + num_blocks)` is within bounds, so if these values overflow then it allows the read which seems to hang some SD Cards (but not all). Fix by explicitly testing for overflow in our layer of the driver. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/stm32/sdcard.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index a7d0629399651..eef6548ddf4ed 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -498,13 +498,27 @@ static HAL_StatusTypeDef sdcard_wait_finished(uint32_t timeout) { return HAL_OK; } -mp_uint_t sdcard_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { +static HAL_StatusTypeDef sdcard_common_checks(uint32_t block_num, uint32_t num_blocks) { // check that SD card is initialised if (!(pyb_sdmmc_flags & PYB_SDMMC_FLAG_ACTIVE)) { return HAL_ERROR; } - HAL_StatusTypeDef err = HAL_OK; + // check that adding block_num & num_blocks don't overflow + // (the ST HAL does a bounds check, but only after adding them...) + uint32_t end_block; + if (__builtin_add_overflow(block_num, num_blocks, &end_block)) { + return HAL_ERROR; + } + + return HAL_OK; +} + +mp_uint_t sdcard_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { + HAL_StatusTypeDef err = sdcard_common_checks(block_num, num_blocks); + if (err != HAL_OK) { + return err; + } // check that dest pointer is aligned on a 4-byte boundary uint8_t *orig_dest = NULL; @@ -595,13 +609,11 @@ mp_uint_t sdcard_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blo } mp_uint_t sdcard_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { - // check that SD card is initialised - if (!(pyb_sdmmc_flags & PYB_SDMMC_FLAG_ACTIVE)) { - return HAL_ERROR; + HAL_StatusTypeDef err = sdcard_common_checks(block_num, num_blocks); + if (err != HAL_OK) { + return err; } - HAL_StatusTypeDef err = HAL_OK; - // check that src pointer is aligned on a 4-byte boundary if (((uint32_t)src & 3) != 0) { // pointer is not aligned, so allocate a temporary block to do the write From 78728dc94abf9482d07efb3056d03ff6d41d7b1a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 18 Feb 2025 16:43:58 +1100 Subject: [PATCH 317/546] stm32/sdcard: Drop the pyb.SDCard timeout from 60 to 30 seconds. 60 seconds is long enough that the USB serial connection drops out before it times out (at least on my computer). Also refactor out the timeout argument from sdcard_wait_finished, to try and save a little code size. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/stm32/sdcard.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index eef6548ddf4ed..706d6315c44d4 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -158,6 +158,8 @@ static uint8_t pyb_sdmmc_flags; +#define TIMEOUT_MS 30000 + // TODO: I think that as an optimization, we can allocate these dynamically // if an sd card is detected. This will save approx 260 bytes of RAM // when no sdcard was being used. @@ -441,7 +443,7 @@ static void sdcard_reset_periph(void) { SDIO->ICR = SDMMC_STATIC_FLAGS; } -static HAL_StatusTypeDef sdcard_wait_finished(uint32_t timeout) { +static HAL_StatusTypeDef sdcard_wait_finished(void) { // Wait for HAL driver to be ready (eg for DMA to finish) uint32_t start = HAL_GetTick(); for (;;) { @@ -463,7 +465,7 @@ static HAL_StatusTypeDef sdcard_wait_finished(uint32_t timeout) { } __WFI(); enable_irq(irq_state); - if (HAL_GetTick() - start >= timeout) { + if (HAL_GetTick() - start >= TIMEOUT_MS) { return HAL_TIMEOUT; } } @@ -490,7 +492,7 @@ static HAL_StatusTypeDef sdcard_wait_finished(uint32_t timeout) { if (!(state == HAL_SD_CARD_SENDING || state == HAL_SD_CARD_RECEIVING || state == HAL_SD_CARD_PROGRAMMING)) { return HAL_ERROR; } - if (HAL_GetTick() - start >= timeout) { + if (HAL_GetTick() - start >= TIMEOUT_MS) { return HAL_TIMEOUT; } __WFI(); @@ -569,7 +571,7 @@ mp_uint_t sdcard_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blo err = HAL_SD_ReadBlocks_DMA(&sdmmc_handle.sd, dest, block_num, num_blocks); } if (err == HAL_OK) { - err = sdcard_wait_finished(60000); + err = sdcard_wait_finished(); } #if SDIO_USE_GPDMA @@ -588,14 +590,14 @@ mp_uint_t sdcard_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blo } else { #if MICROPY_HW_ENABLE_MMCARD if (pyb_sdmmc_flags & PYB_SDMMC_FLAG_MMC) { - err = HAL_MMC_ReadBlocks(&sdmmc_handle.mmc, dest, block_num, num_blocks, 60000); + err = HAL_MMC_ReadBlocks(&sdmmc_handle.mmc, dest, block_num, num_blocks, TIMEOUT_MS); } else #endif { - err = HAL_SD_ReadBlocks(&sdmmc_handle.sd, dest, block_num, num_blocks, 60000); + err = HAL_SD_ReadBlocks(&sdmmc_handle.sd, dest, block_num, num_blocks, TIMEOUT_MS); } if (err == HAL_OK) { - err = sdcard_wait_finished(60000); + err = sdcard_wait_finished(); } } @@ -662,7 +664,7 @@ mp_uint_t sdcard_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t n err = HAL_SD_WriteBlocks_DMA(&sdmmc_handle.sd, (uint8_t *)src, block_num, num_blocks); } if (err == HAL_OK) { - err = sdcard_wait_finished(60000); + err = sdcard_wait_finished(); } #if SDIO_USE_GPDMA @@ -681,14 +683,14 @@ mp_uint_t sdcard_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t n } else { #if MICROPY_HW_ENABLE_MMCARD if (pyb_sdmmc_flags & PYB_SDMMC_FLAG_MMC) { - err = HAL_MMC_WriteBlocks(&sdmmc_handle.mmc, (uint8_t *)src, block_num, num_blocks, 60000); + err = HAL_MMC_WriteBlocks(&sdmmc_handle.mmc, (uint8_t *)src, block_num, num_blocks, TIMEOUT_MS); } else #endif { - err = HAL_SD_WriteBlocks(&sdmmc_handle.sd, (uint8_t *)src, block_num, num_blocks, 60000); + err = HAL_SD_WriteBlocks(&sdmmc_handle.sd, (uint8_t *)src, block_num, num_blocks, TIMEOUT_MS); } if (err == HAL_OK) { - err = sdcard_wait_finished(60000); + err = sdcard_wait_finished(); } } From e3101ce1b3782955ea2d103922f0dc19bc0331f6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 25 Feb 2025 15:14:08 +1100 Subject: [PATCH 318/546] qemu/boards/SABRELITE: Increase MicroPython heap to 160k. Signed-off-by: Damien George --- ports/qemu/boards/SABRELITE/mpconfigboard.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/qemu/boards/SABRELITE/mpconfigboard.mk b/ports/qemu/boards/SABRELITE/mpconfigboard.mk index 80ea08f12cf76..e56dba712ff44 100644 --- a/ports/qemu/boards/SABRELITE/mpconfigboard.mk +++ b/ports/qemu/boards/SABRELITE/mpconfigboard.mk @@ -12,6 +12,10 @@ LDSCRIPT = mcu/arm/imx6.ld SRC_BOARD_O = shared/runtime/gchelper_generic.o +# Use a larger heap than the default so tests run with the native emitter have +# enough memory (because emitted ARM machine code is larger than Thumb2 code). +MICROPY_HEAP_SIZE ?= 163840 + # It's really armv7a but closest supported value is armv6. MPY_CROSS_FLAGS += -march=armv6 From 14ba32bb205fa1f7d6ac456879b08aadb4e1aaf7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 24 Feb 2025 23:14:48 +1100 Subject: [PATCH 319/546] extmod/vfs_rom: Add bounds checking for all filesystem accesses. Testing with ROMFS shows that it is relatively easy to end up with a corrupt filesystem on the device -- eg due to the ROMFS deploy process stopping half way through -- which could lead to hard crashes. Notably, there can be boot loops trying to mount a corrupt filesystem, crashes when importing modules like `os` that first scan the filesystem for `os.py`, and crashing when deploying a new ROMFS in certain cases because the old one is removed while still mounted. The main problem is that `mp_decode_uint()` has an loop that keeps going as long as it reads 0xff byte values, which can happen in the case of erased and unwritten flash. This commit adds full bounds checking in the new `mp_decode_uint_checked()` function, and that makes all ROMFS filesystem accesses robust. Signed-off-by: Damien George --- extmod/vfs_rom.c | 114 +++++++++++++++++++++++++++++++--------- tests/extmod/vfs_rom.py | 73 +++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 24 deletions(-) diff --git a/extmod/vfs_rom.c b/extmod/vfs_rom.c index 99ddaba95ed79..ff3652d2ce3a2 100644 --- a/extmod/vfs_rom.c +++ b/extmod/vfs_rom.c @@ -91,6 +91,7 @@ #define ROMFS_RECORD_KIND_DATA_POINTER (3) #define ROMFS_RECORD_KIND_DIRECTORY (4) #define ROMFS_RECORD_KIND_FILE (5) +#define ROMFS_RECORD_KIND_FILESYSTEM (0x14a6b1) typedef mp_uint_t record_kind_t; @@ -101,34 +102,72 @@ struct _mp_obj_vfs_rom_t { const uint8_t *filesystem_end; }; -static record_kind_t extract_record(const uint8_t **fs, const uint8_t **fs_next) { - record_kind_t record_kind = mp_decode_uint(fs); - mp_uint_t record_len = mp_decode_uint(fs); +// Returns 0 for success, -1 for failure. +static int mp_decode_uint_checked(const uint8_t **ptr, const uint8_t *ptr_max, mp_uint_t *value_out) { + mp_uint_t unum = 0; + byte val; + const uint8_t *p = *ptr; + do { + if (p >= ptr_max) { + return -1; + } + val = *p++; + unum = (unum << 7) | (val & 0x7f); + } while ((val & 0x80) != 0); + *ptr = p; + *value_out = unum; + return 0; +} + +static record_kind_t extract_record(const uint8_t **fs, const uint8_t **fs_next, const uint8_t *fs_max) { + mp_uint_t record_kind; + if (mp_decode_uint_checked(fs, fs_max, &record_kind) != 0) { + return ROMFS_RECORD_KIND_UNUSED; + } + mp_uint_t record_len; + if (mp_decode_uint_checked(fs, fs_max, &record_len) != 0) { + return ROMFS_RECORD_KIND_UNUSED; + } *fs_next = *fs + record_len; return record_kind; } -static void extract_data(mp_obj_vfs_rom_t *self, const uint8_t *fs, const uint8_t *fs_top, size_t *size_out, const uint8_t **data_out) { - *size_out = 0; - *data_out = NULL; +// Returns 0 for success, a negative integer for failure. +static int extract_data(mp_obj_vfs_rom_t *self, const uint8_t *fs, const uint8_t *fs_top, size_t *size_out, const uint8_t **data_out) { while (fs < fs_top) { const uint8_t *fs_next; - record_kind_t record_kind = extract_record(&fs, &fs_next); - if (record_kind == ROMFS_RECORD_KIND_DATA_VERBATIM) { - // Verbatim data. - *size_out = fs_next - fs; - *data_out = fs; + record_kind_t record_kind = extract_record(&fs, &fs_next, fs_top); + if (record_kind == ROMFS_RECORD_KIND_UNUSED) { + // Corrupt filesystem. break; + } else if (record_kind == ROMFS_RECORD_KIND_DATA_VERBATIM) { + // Verbatim data. + if (size_out != NULL) { + *size_out = fs_next - fs; + *data_out = fs; + } + return 0; } else if (record_kind == ROMFS_RECORD_KIND_DATA_POINTER) { // Pointer to data. - *size_out = mp_decode_uint(&fs); - *data_out = self->filesystem + mp_decode_uint(&fs); - break; + mp_uint_t size; + if (mp_decode_uint_checked(&fs, fs_next, &size) != 0) { + break; + } + mp_uint_t offset; + if (mp_decode_uint_checked(&fs, fs_next, &offset) != 0) { + break; + } + if (size_out != NULL) { + *size_out = size; + *data_out = self->filesystem + offset; + } + return 0; } else { // Skip this record. fs = fs_next; } } + return -MP_EIO; } // Searches for `path` in the filesystem. @@ -144,10 +183,17 @@ mp_import_stat_t mp_vfs_rom_search_filesystem(mp_obj_vfs_rom_t *self, const char } while (path_len > 0 && fs < fs_top) { const uint8_t *fs_next; - record_kind_t record_kind = extract_record(&fs, &fs_next); - if (record_kind == ROMFS_RECORD_KIND_DIRECTORY || record_kind == ROMFS_RECORD_KIND_FILE) { + record_kind_t record_kind = extract_record(&fs, &fs_next, fs_top); + if (record_kind == ROMFS_RECORD_KIND_UNUSED) { + // Corrupt filesystem. + return MP_IMPORT_STAT_NO_EXIST; + } else if (record_kind == ROMFS_RECORD_KIND_DIRECTORY || record_kind == ROMFS_RECORD_KIND_FILE) { // A directory or file record. - mp_uint_t name_len = mp_decode_uint(&fs); + mp_uint_t name_len; + if (mp_decode_uint_checked(&fs, fs_next, &name_len) != 0) { + // Corrupt filesystem. + return MP_IMPORT_STAT_NO_EXIST; + } if ((name_len == path_len || (name_len < path_len && path[name_len] == '/')) && memcmp(path, fs, name_len) == 0) { @@ -167,8 +213,9 @@ mp_import_stat_t mp_vfs_rom_search_filesystem(mp_obj_vfs_rom_t *self, const char if (path_len != 0) { return MP_IMPORT_STAT_NO_EXIST; } - if (size_out != NULL) { - extract_data(self, fs, fs_top, size_out, data_out); + if (extract_data(self, fs, fs_top, size_out, data_out) != 0) { + // Corrupt filesystem. + return MP_IMPORT_STAT_NO_EXIST; } return MP_IMPORT_STAT_FILE; } @@ -214,7 +261,15 @@ static mp_obj_t vfs_rom_make_new(const mp_obj_type_t *type, size_t n_args, size_ } // The ROMFS is a record itself, so enter into it and compute its limit. - extract_record(&self->filesystem, &self->filesystem_end); + record_kind_t record_kind = extract_record(&self->filesystem, &self->filesystem_end, self->filesystem + bufinfo.len); + if (record_kind != ROMFS_RECORD_KIND_FILESYSTEM) { + mp_raise_OSError(MP_ENODEV); + } + + // Check the filesystem is within the limits of the input buffer. + if (self->filesystem_end > (const uint8_t *)bufinfo.buf + bufinfo.len) { + mp_raise_OSError(MP_ENODEV); + } return MP_OBJ_FROM_PTR(self); } @@ -259,13 +314,21 @@ static mp_obj_t vfs_rom_ilistdir_it_iternext(mp_obj_t self_in) { while (self->index < self->index_top) { const uint8_t *index_next; - record_kind_t record_kind = extract_record(&self->index, &index_next); + record_kind_t record_kind = extract_record(&self->index, &index_next, self->index_top); uint32_t type; mp_uint_t name_len; size_t data_len; - if (record_kind == ROMFS_RECORD_KIND_DIRECTORY || record_kind == ROMFS_RECORD_KIND_FILE) { + if (record_kind == ROMFS_RECORD_KIND_UNUSED) { + // Corrupt filesystem. + self->index = self->index_top; + break; + } else if (record_kind == ROMFS_RECORD_KIND_DIRECTORY || record_kind == ROMFS_RECORD_KIND_FILE) { // A directory or file record. - name_len = mp_decode_uint(&self->index); + if (mp_decode_uint_checked(&self->index, index_next, &name_len) != 0) { + // Corrupt filesystem. + self->index = self->index_top; + break; + } if (record_kind == ROMFS_RECORD_KIND_DIRECTORY) { // A directory. type = MP_S_IFDIR; @@ -274,7 +337,10 @@ static mp_obj_t vfs_rom_ilistdir_it_iternext(mp_obj_t self_in) { // A file. type = MP_S_IFREG; const uint8_t *data_value; - extract_data(self->vfs_rom, self->index + name_len, index_next, &data_len, &data_value); + if (extract_data(self->vfs_rom, self->index + name_len, index_next, &data_len, &data_value) != 0) { + // Corrupt filesystem. + break; + } } } else { // Skip this record. diff --git a/tests/extmod/vfs_rom.py b/tests/extmod/vfs_rom.py index f7958a93962d0..dc88481c028ac 100644 --- a/tests/extmod/vfs_rom.py +++ b/tests/extmod/vfs_rom.py @@ -223,6 +223,79 @@ def test_unknown_record(self): self.assertEqual(f.read(), b"contents") +class TestCorrupt(unittest.TestCase): + def test_corrupt_filesystem(self): + # Make the filesystem length bigger than the buffer. + romfs = bytearray(make_romfs(())) + romfs[3] = 0x01 + with self.assertRaises(OSError): + vfs.VfsRom(romfs) + + # Corrupt the filesystem length. + romfs = bytearray(make_romfs(())) + romfs[3] = 0xFF + with self.assertRaises(OSError): + vfs.VfsRom(romfs) + + # Corrupt the contents of the filesystem. + romfs = bytearray(make_romfs(())) + romfs[3] = 0x01 + romfs.extend(b"\xff\xff") + fs = vfs.VfsRom(romfs) + with self.assertRaises(OSError): + fs.stat("a") + self.assertEqual(list(fs.ilistdir("")), []) + + def test_corrupt_file_entry(self): + romfs = make_romfs((("file", b"data"),)) + + # Corrupt the length of filename. + romfs_corrupt = bytearray(romfs) + romfs_corrupt[7:] = b"\xff" * (len(romfs) - 7) + fs = vfs.VfsRom(romfs_corrupt) + with self.assertRaises(OSError): + fs.stat("file") + self.assertEqual(list(fs.ilistdir("")), []) + + # Erase the data record (change it to a padding record). + romfs_corrupt = bytearray(romfs) + romfs_corrupt[12] = VfsRomWriter.ROMFS_RECORD_KIND_PADDING + fs = vfs.VfsRom(romfs_corrupt) + with self.assertRaises(OSError): + fs.stat("file") + self.assertEqual(list(fs.ilistdir("")), []) + + # Corrupt the header of the data record. + romfs_corrupt = bytearray(romfs) + romfs_corrupt[12:] = b"\xff" * (len(romfs) - 12) + fs = vfs.VfsRom(romfs_corrupt) + with self.assertRaises(OSError): + fs.stat("file") + + # Corrupt the interior of the data record. + romfs_corrupt = bytearray(romfs) + romfs_corrupt[13:] = b"\xff" * (len(romfs) - 13) + fs = vfs.VfsRom(romfs_corrupt) + with self.assertRaises(OSError): + fs.stat("file") + + # Change the data record to an indirect pointer and corrupt the length. + romfs_corrupt = bytearray(romfs) + romfs_corrupt[12] = VfsRomWriter.ROMFS_RECORD_KIND_DATA_POINTER + romfs_corrupt[14:18] = b"\xff\xff\xff\xff" + fs = vfs.VfsRom(romfs_corrupt) + with self.assertRaises(OSError): + fs.stat("file") + + # Change the data record to an indirect pointer and corrupt the offset. + romfs_corrupt = bytearray(romfs) + romfs_corrupt[12] = VfsRomWriter.ROMFS_RECORD_KIND_DATA_POINTER + romfs_corrupt[14:18] = b"\x00\xff\xff\xff" + fs = vfs.VfsRom(romfs_corrupt) + with self.assertRaises(OSError): + fs.stat("file") + + class TestStandalone(TestBase): def test_constructor(self): self.assertIsInstance(vfs.VfsRom(self.romfs), vfs.VfsRom) From dc2c33b07f83ac4fcbcbcbb294e5726a95507490 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 4 Jan 2025 12:51:18 +0100 Subject: [PATCH 320/546] py/emitinlinerv32: Fix compilation with ESP-IDF v5.2 and later. This commit fixes a compilation warning (turned error) about a potentially uninitialised variable being used. The warning can be ignored as the variable in question is always written to, but the code has been changed to silence that warning. Signed-off-by: Alessandro Gatti --- py/emitinlinerv32.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/py/emitinlinerv32.c b/py/emitinlinerv32.c index 179e289c67b05..ba171b4e6fae0 100644 --- a/py/emitinlinerv32.c +++ b/py/emitinlinerv32.c @@ -709,9 +709,11 @@ static bool handle_load_store_opcode_with_offset(emit_inline_asm_t *emit, qstr o return false; } - mp_uint_t rd; - mp_uint_t rs1; - parse_register_node(nodes[0], &rd, opcode_data->argument1_kind & C); + mp_uint_t rd = 0; + mp_uint_t rs1 = 0; + if (!parse_register_node(nodes[0], &rd, opcode_data->argument1_kind & C)) { + return false; + } if (!parse_register_node(nodes[1], &rs1, opcode_data->argument3_kind & C)) { return false; } From 8633abc08291d98035c65904dc4dab9983ddada3 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 4 Jan 2025 12:54:44 +0100 Subject: [PATCH 321/546] py/emitinlinerv32: Reduce the footprint of compiled code. This commit introduces a few changes aimed at reducing the amount of space taken by the inline assembler once compiled: * The register string table uses 2 bytes for each qstr rather than the usual 4 * The opcode table uses 2 bytes for each qstr rather than the usual 4 * Opcode masks are not embedded in each opcode entry but looked up via an additional smaller table, reducing the number of bytes taken by an opcode's masks from 12 to 2 (with a fixed overhead of 24 bytes for the the masks themselves stored elsewhere) * Some error messages had a trailing period, now removed * Error messages have been parameterised when possible, and the overall text length is smaller. Signed-off-by: Alessandro Gatti --- py/emitinlinerv32.c | 332 ++++++++++++++++++++++++-------------------- 1 file changed, 181 insertions(+), 151 deletions(-) diff --git a/py/emitinlinerv32.c b/py/emitinlinerv32.c index ba171b4e6fae0..a9a81ddf16c4c 100644 --- a/py/emitinlinerv32.c +++ b/py/emitinlinerv32.c @@ -60,7 +60,7 @@ struct _emit_inline_asm_t { qstr *label_lookup; }; -static const qstr REGISTERS_QSTR_TABLE[] = { +static const qstr_short_t REGISTERS_QSTR_TABLE[] = { MP_QSTR_zero, MP_QSTR_ra, MP_QSTR_sp, MP_QSTR_gp, MP_QSTR_tp, MP_QSTR_t0, MP_QSTR_t1, MP_QSTR_t2, MP_QSTR_s0, MP_QSTR_s1, MP_QSTR_a0, MP_QSTR_a1, MP_QSTR_a2, MP_QSTR_a3, MP_QSTR_a4, MP_QSTR_a5, MP_QSTR_a6, MP_QSTR_a7, MP_QSTR_s2, MP_QSTR_s3, MP_QSTR_s4, MP_QSTR_s5, MP_QSTR_s6, MP_QSTR_s7, @@ -185,18 +185,21 @@ static bool emit_inline_rv32_label(emit_inline_asm_t *emit, mp_uint_t label_num, return true; } -#define CALL_RRR 0 // Register, Register, Register -#define CALL_RR 1 // Register, Register -#define CALL_RRI 2 // Register, Register, Immediate -#define CALL_RRL 3 // Register, Register, Label -#define CALL_RI 4 // Register, Immediate -#define CALL_L 5 // Label -#define CALL_R 6 // Register -#define CALL_RL 7 // Register, Label -#define CALL_N 8 // No arguments -#define CALL_I 9 // Immediate -#define CALL_RII 10 // Register, Immediate, Immediate -#define CALL_RIR 11 // Register, Immediate(Register) +typedef enum { + CALL_RRR, // Opcode Register, Register, Register + CALL_RR, // Opcode Register, Register + CALL_RRI, // Opcode Register, Register, Immediate + CALL_RRL, // Opcode Register, Register, Label + CALL_RI, // Opcode Register, Immediate + CALL_L, // Opcode Label + CALL_R, // Opcode Register + CALL_RL, // Opcode Register, Label + CALL_N, // Opcode + CALL_I, // Opcode Immediate + CALL_RII, // Opcode Register, Register, Immediate + CALL_RIR, // Opcode Register, Immediate(Register) + CALL_COUNT +} call_convention_t; #define N 0 // No argument #define R 1 // Register @@ -217,18 +220,21 @@ typedef void (*call_r_t)(asm_rv32_t *state, mp_uint_t rd); typedef void (*call_n_t)(asm_rv32_t *state); typedef struct _opcode_t { - qstr qstring; - void *emitter; + qstr_short_t qstring; + uint16_t argument1_mask : 4; + uint16_t argument2_mask : 4; + uint16_t argument3_mask : 4; + uint16_t arguments_count : 2; + // 2 bits available here uint32_t calling_convention : 4; uint32_t argument1_kind : 4; - uint32_t argument1_shift : 5; + uint32_t argument1_shift : 4; uint32_t argument2_kind : 4; - uint32_t argument2_shift : 5; + uint32_t argument2_shift : 4; uint32_t argument3_kind : 4; - uint32_t argument3_shift : 5; - uint32_t argument1_mask; - uint32_t argument2_mask; - uint32_t argument3_mask; + uint32_t argument3_shift : 4; + // 4 bits available here + void *emitter; } opcode_t; #define opcode_li asm_rv32_emit_optimised_load_immediate @@ -249,89 +255,129 @@ static void opcode_la(asm_rv32_t *state, mp_uint_t rd, mp_int_t displacement) { #define IZ (I | Z) #define IUZ (I | U | Z) +#define MASK_NOT_USED 0 + +enum { + MASK_FFFFFFFF, + MASK_00000FFF, + MASK_FFFFF000, + MASK_00001FFE, + MASK_0000001F, + MASK_FFFFFFFE, + MASK_0000003F, + MASK_0000FF00, + MASK_000003FC, + MASK_000001FE, + MASK_00000FFE, + MASK_FFFFFFFA, + MASK_0001F800, + MASK_0000007C, + MASK_000000FC, + MASK_001FFFFE, +}; + +static const uint32_t OPCODE_MASKS[] = { + [MASK_FFFFFFFF] = 0xFFFFFFFF, + [MASK_00000FFF] = 0x00000FFF, + [MASK_FFFFF000] = 0xFFFFF000, + [MASK_00001FFE] = 0x00001FFE, + [MASK_0000001F] = 0x0000001F, + [MASK_FFFFFFFE] = 0xFFFFFFFE, + [MASK_0000003F] = 0x0000003F, + [MASK_0000FF00] = 0x0000FF00, + [MASK_000003FC] = 0x000003FC, + [MASK_000001FE] = 0x000001FE, + [MASK_00000FFE] = 0x00000FFE, + [MASK_FFFFFFFA] = 0xFFFFFFFA, + [MASK_0001F800] = 0x0001F800, + [MASK_0000007C] = 0x0000007C, + [MASK_000000FC] = 0x000000FC, + [MASK_001FFFFE] = 0x001FFFFE, +}; + static const opcode_t OPCODES[] = { - { MP_QSTR_add, asm_rv32_opcode_add, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_addi, asm_rv32_opcode_addi, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, - { MP_QSTR_and_, asm_rv32_opcode_and, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_andi, asm_rv32_opcode_andi, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, - { MP_QSTR_auipc, asm_rv32_opcode_auipc, CALL_RI, R, 0, I, 12, N, 0, 0xFFFFFFFF, 0xFFFFF000, 0x00000000 }, - { MP_QSTR_beq, asm_rv32_opcode_beq, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, - { MP_QSTR_bge, asm_rv32_opcode_bge, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, - { MP_QSTR_bgeu, asm_rv32_opcode_bgeu, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, - { MP_QSTR_blt, asm_rv32_opcode_blt, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, - { MP_QSTR_bltu, asm_rv32_opcode_bltu, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, - { MP_QSTR_bne, asm_rv32_opcode_bne, CALL_RRL, R, 0, R, 0, L, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00001FFE }, - { MP_QSTR_csrrc, asm_rv32_opcode_csrrc, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, - { MP_QSTR_csrrs, asm_rv32_opcode_csrrs, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, - { MP_QSTR_csrrw, asm_rv32_opcode_csrrw, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, - { MP_QSTR_csrrci, asm_rv32_opcode_csrrci, CALL_RII, R, 0, IU, 0, IU, 0, 0xFFFFFFFF, 0x00000FFF, 0x0000001F }, - { MP_QSTR_csrrsi, asm_rv32_opcode_csrrsi, CALL_RII, R, 0, IU, 0, IU, 0, 0xFFFFFFFF, 0x00000FFF, 0x0000001F }, - { MP_QSTR_csrrwi, asm_rv32_opcode_csrrwi, CALL_RII, R, 0, IU, 0, IU, 0, 0xFFFFFFFF, 0x00000FFF, 0x0000001F }, - { MP_QSTR_c_add, asm_rv32_opcode_cadd, CALL_RR, R, 0, R, 0, N, 0, 0xFFFFFFFE, 0xFFFFFFFE, 0x00000000 }, - { MP_QSTR_c_addi, asm_rv32_opcode_caddi, CALL_RI, R, 0, IZ, 0, N, 0, 0xFFFFFFFE, 0x0000003F, 0x00000000 }, - { MP_QSTR_c_addi4spn, asm_rv32_opcode_caddi4spn, CALL_RI, R, 0, IUZ, 0, N, 0, 0x0000FF00, 0x000003FC, 0x00000000 }, - { MP_QSTR_c_and, asm_rv32_opcode_cand, CALL_RR, RC, 0, RC, 0, N, 0, 0x0000FF00, 0x0000FF00, 0x00000000 }, - { MP_QSTR_c_andi, asm_rv32_opcode_candi, CALL_RI, RC, 0, I, 0, N, 0, 0x0000FF00, 0x0000003F, 0x00000000 }, - { MP_QSTR_c_beqz, asm_rv32_opcode_cbeqz, CALL_RL, RC, 0, L, 0, N, 0, 0x0000FF00, 0x000001FE, 0x00000000 }, - { MP_QSTR_c_bnez, asm_rv32_opcode_cbnez, CALL_RL, RC, 0, L, 0, N, 0, 0x0000FF00, 0x000001FE, 0x00000000 }, - { MP_QSTR_c_ebreak, asm_rv32_opcode_cebreak, CALL_N, N, 0, N, 0, N, 0, 0x00000000, 0x00000000, 0x00000000 }, - { MP_QSTR_c_j, asm_rv32_opcode_cj, CALL_L, L, 0, N, 0, N, 0, 0x00000FFE, 0x00000000, 0x00000000 }, - { MP_QSTR_c_jal, asm_rv32_opcode_cjal, CALL_L, L, 0, N, 0, N, 0, 0x00000FFE, 0x00000000, 0x00000000 }, - { MP_QSTR_c_jalr, asm_rv32_opcode_cjalr, CALL_R, R, 0, N, 0, N, 0, 0xFFFFFFFE, 0x00000000, 0x00000000 }, - { MP_QSTR_c_jr, asm_rv32_opcode_cjr, CALL_R, R, 0, N, 0, N, 0, 0xFFFFFFFE, 0x00000000, 0x00000000 }, - { MP_QSTR_c_li, asm_rv32_opcode_cli, CALL_RI, R, 0, I, 0, N, 0, 0xFFFFFFFE, 0x0000003F, 0x00000000 }, - { MP_QSTR_c_lui, asm_rv32_opcode_clui, CALL_RI, R, 0, IUZ, 12, N, 0, 0xFFFFFFFA, 0x0001F800, 0x00000000 }, - { MP_QSTR_c_lw, asm_rv32_opcode_clw, CALL_RIR, RC, 0, I, 0, RC, 0, 0x0000FF00, 0x0000007C, 0x0000FF00 }, - { MP_QSTR_c_lwsp, asm_rv32_opcode_clwsp, CALL_RI, R, 0, I, 0, N, 0, 0xFFFFFFFE, 0x000000FC, 0x00000000 }, - { MP_QSTR_c_mv, asm_rv32_opcode_cmv, CALL_RR, R, 0, R, 0, N, 0, 0xFFFFFFFE, 0xFFFFFFFE, 0x00000000 }, - { MP_QSTR_c_nop, asm_rv32_opcode_cnop, CALL_N, N, 0, N, 0, N, 0, 0x00000000, 0x00000000, 0x00000000 }, - { MP_QSTR_c_or, asm_rv32_opcode_cor, CALL_RR, RC, 0, RC, 0, N, 0, 0x0000FF00, 0x0000FF00, 0x00000000 }, - { MP_QSTR_c_slli, asm_rv32_opcode_cslli, CALL_RI, R, 0, IU, 0, N, 0, 0xFFFFFFFE, 0x0000001F, 0x00000000 }, - { MP_QSTR_c_srai, asm_rv32_opcode_csrai, CALL_RI, RC, 0, IU, 0, N, 0, 0x0000FF00, 0x0000001F, 0x00000000 }, - { MP_QSTR_c_srli, asm_rv32_opcode_csrli, CALL_RI, RC, 0, IU, 0, N, 0, 0x0000FF00, 0x0000001F, 0x00000000 }, - { MP_QSTR_c_sub, asm_rv32_opcode_csub, CALL_RR, RC, 0, RC, 0, N, 0, 0x0000FF00, 0x0000FF00, 0x00000000 }, - { MP_QSTR_c_sw, asm_rv32_opcode_csw, CALL_RIR, RC, 0, I, 0, RC, 0, 0x0000FF00, 0x0000007C, 0x0000FF00 }, - { MP_QSTR_c_swsp, asm_rv32_opcode_cswsp, CALL_RI, R, 0, I, 0, N, 0, 0xFFFFFFFF, 0x000000FC, 0x00000000 }, - { MP_QSTR_c_xor, asm_rv32_opcode_cxor, CALL_RR, RC, 0, RC, 0, N, 0, 0x0000FF00, 0x0000FF00, 0x00000000 }, - { MP_QSTR_div, asm_rv32_opcode_div, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_divu, asm_rv32_opcode_divu, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_ebreak, asm_rv32_opcode_ebreak, CALL_N, N, 0, N, 0, N, 0, 0x00000000, 0x00000000, 0x00000000 }, - { MP_QSTR_ecall, asm_rv32_opcode_ecall, CALL_N, N, 0, N, 0, N, 0, 0x00000000, 0x00000000, 0x00000000 }, - { MP_QSTR_jal, asm_rv32_opcode_jal, CALL_RL, R, 0, L, 0, N, 0, 0xFFFFFFFF, 0x001FFFFE, 0x00000000 }, - { MP_QSTR_jalr, asm_rv32_opcode_jalr, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, - { MP_QSTR_la, opcode_la, CALL_RL, R, 0, L, 0, N, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 }, - { MP_QSTR_lb, asm_rv32_opcode_lb, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, - { MP_QSTR_lbu, asm_rv32_opcode_lbu, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, - { MP_QSTR_lh, asm_rv32_opcode_lh, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, - { MP_QSTR_lhu, asm_rv32_opcode_lhu, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, - { MP_QSTR_li, opcode_li, CALL_RI, R, 0, I, 0, N, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 }, - { MP_QSTR_lui, asm_rv32_opcode_lui, CALL_RI, R, 0, I, 12, N, 0, 0xFFFFFFFF, 0xFFFFF000, 0x00000000 }, - { MP_QSTR_lw, asm_rv32_opcode_lw, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, - { MP_QSTR_mv, asm_rv32_opcode_cmv, CALL_RR, R, 0, R, 0, N, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 }, - { MP_QSTR_mul, asm_rv32_opcode_mul, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_mulh, asm_rv32_opcode_mulh, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_mulhsu, asm_rv32_opcode_mulhsu, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_mulhu, asm_rv32_opcode_mulhu, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_or_, asm_rv32_opcode_or, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_ori, asm_rv32_opcode_ori, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, - { MP_QSTR_rem, asm_rv32_opcode_rem, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_remu, asm_rv32_opcode_remu, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_sb, asm_rv32_opcode_sb, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, - { MP_QSTR_sh, asm_rv32_opcode_sh, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, - { MP_QSTR_sll, asm_rv32_opcode_sll, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_slli, asm_rv32_opcode_slli, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000001F }, - { MP_QSTR_slt, asm_rv32_opcode_slt, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_slti, asm_rv32_opcode_slti, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, - { MP_QSTR_sltiu, asm_rv32_opcode_sltiu, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, - { MP_QSTR_sltu, asm_rv32_opcode_sltu, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_sra, asm_rv32_opcode_sra, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_srai, asm_rv32_opcode_srai, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000001F }, - { MP_QSTR_srl, asm_rv32_opcode_srl, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_srli, asm_rv32_opcode_srli, CALL_RRI, R, 0, R, 0, IU, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000001F }, - { MP_QSTR_sub, asm_rv32_opcode_sub, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_sw, asm_rv32_opcode_sw, CALL_RIR, R, 0, I, 0, R, 0, 0xFFFFFFFF, 0x00000FFF, 0xFFFFFFFF }, - { MP_QSTR_xor, asm_rv32_opcode_xor, CALL_RRR, R, 0, R, 0, R, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, - { MP_QSTR_xori, asm_rv32_opcode_xori, CALL_RRI, R, 0, R, 0, I, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF }, + { MP_QSTR_add, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_add }, + { MP_QSTR_addi, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_addi }, + { MP_QSTR_and_, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_and }, + { MP_QSTR_andi, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_andi }, + { MP_QSTR_auipc, MASK_FFFFFFFF, MASK_FFFFF000, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 12, N, 0, asm_rv32_opcode_auipc }, + { MP_QSTR_beq, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_beq }, + { MP_QSTR_bge, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bge }, + { MP_QSTR_bgeu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bgeu }, + { MP_QSTR_blt, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_blt }, + { MP_QSTR_bltu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bltu }, + { MP_QSTR_bne, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bne }, + { MP_QSTR_csrrc, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_csrrc }, + { MP_QSTR_csrrs, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_csrrs }, + { MP_QSTR_csrrw, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_csrrw }, + { MP_QSTR_csrrci, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, asm_rv32_opcode_csrrci }, + { MP_QSTR_csrrsi, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, asm_rv32_opcode_csrrsi }, + { MP_QSTR_csrrwi, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, asm_rv32_opcode_csrrwi }, + { MP_QSTR_c_add, MASK_FFFFFFFE, MASK_FFFFFFFE, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, asm_rv32_opcode_cadd }, + { MP_QSTR_c_addi, MASK_FFFFFFFE, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, R, 0, IZ, 0, N, 0, asm_rv32_opcode_caddi }, + { MP_QSTR_c_addi4spn, MASK_0000FF00, MASK_000003FC, MASK_NOT_USED, 2, CALL_RI, R, 0, IUZ, 0, N, 0, asm_rv32_opcode_caddi4spn }, + { MP_QSTR_c_and, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_cand }, + { MP_QSTR_c_andi, MASK_0000FF00, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, RC, 0, I, 0, N, 0, asm_rv32_opcode_candi }, + { MP_QSTR_c_beqz, MASK_0000FF00, MASK_000001FE, MASK_NOT_USED, 2, CALL_RL, RC, 0, L, 0, N, 0, asm_rv32_opcode_cbeqz }, + { MP_QSTR_c_bnez, MASK_0000FF00, MASK_000001FE, MASK_NOT_USED, 2, CALL_RL, RC, 0, L, 0, N, 0, asm_rv32_opcode_cbnez }, + { MP_QSTR_c_ebreak, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_cebreak }, + { MP_QSTR_c_j, MASK_00000FFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_L, L, 0, N, 0, N, 0, asm_rv32_opcode_cj }, + { MP_QSTR_c_jal, MASK_00000FFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_L, L, 0, N, 0, N, 0, asm_rv32_opcode_cjal }, + { MP_QSTR_c_jalr, MASK_FFFFFFFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_R, R, 0, N, 0, N, 0, asm_rv32_opcode_cjalr }, + { MP_QSTR_c_jr, MASK_FFFFFFFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_R, R, 0, N, 0, N, 0, asm_rv32_opcode_cjr }, + { MP_QSTR_c_li, MASK_FFFFFFFE, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, asm_rv32_opcode_cli }, + { MP_QSTR_c_lui, MASK_FFFFFFFA, MASK_0001F800, MASK_NOT_USED, 2, CALL_RI, R, 0, IUZ, 12, N, 0, asm_rv32_opcode_clui }, + { MP_QSTR_c_lw, MASK_0000FF00, MASK_0000007C, MASK_0000FF00, 3, CALL_RIR, RC, 0, I, 0, RC, 0, asm_rv32_opcode_clw }, + { MP_QSTR_c_lwsp, MASK_FFFFFFFE, MASK_000000FC, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, asm_rv32_opcode_clwsp }, + { MP_QSTR_c_mv, MASK_FFFFFFFE, MASK_FFFFFFFE, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, asm_rv32_opcode_cmv }, + { MP_QSTR_c_nop, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_cnop }, + { MP_QSTR_c_or, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_cor }, + { MP_QSTR_c_slli, MASK_FFFFFFFE, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, R, 0, IU, 0, N, 0, asm_rv32_opcode_cslli }, + { MP_QSTR_c_srai, MASK_0000FF00, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, RC, 0, IU, 0, N, 0, asm_rv32_opcode_csrai }, + { MP_QSTR_c_srli, MASK_0000FF00, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, RC, 0, IU, 0, N, 0, asm_rv32_opcode_csrli }, + { MP_QSTR_c_sub, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_csub }, + { MP_QSTR_c_sw, MASK_0000FF00, MASK_0000007C, MASK_0000FF00, 3, CALL_RIR, RC, 0, I, 0, RC, 0, asm_rv32_opcode_csw }, + { MP_QSTR_c_swsp, MASK_FFFFFFFF, MASK_000000FC, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, asm_rv32_opcode_cswsp }, + { MP_QSTR_c_xor, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_cxor }, + { MP_QSTR_div, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_div }, + { MP_QSTR_divu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_divu }, + { MP_QSTR_ebreak, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_ebreak }, + { MP_QSTR_ecall, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_ecall }, + { MP_QSTR_jal, MASK_FFFFFFFF, MASK_001FFFFE, MASK_NOT_USED, 2, CALL_RL, R, 0, L, 0, N, 0, asm_rv32_opcode_jal }, + { MP_QSTR_jalr, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_jalr }, + { MP_QSTR_la, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RL, R, 0, L, 0, N, 0, opcode_la }, + { MP_QSTR_lb, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lb }, + { MP_QSTR_lbu, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lbu }, + { MP_QSTR_lh, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lh }, + { MP_QSTR_lhu, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lhu }, + { MP_QSTR_li, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, opcode_li }, + { MP_QSTR_lui, MASK_FFFFFFFF, MASK_FFFFF000, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 12, N, 0, asm_rv32_opcode_lui }, + { MP_QSTR_lw, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lw }, + { MP_QSTR_mv, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, asm_rv32_opcode_cmv }, + { MP_QSTR_mul, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mul }, + { MP_QSTR_mulh, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mulh }, + { MP_QSTR_mulhsu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mulhsu }, + { MP_QSTR_mulhu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mulhu }, + { MP_QSTR_or_, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_or }, + { MP_QSTR_ori, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_ori }, + { MP_QSTR_rem, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_rem }, + { MP_QSTR_remu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_remu }, + { MP_QSTR_sb, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_sb }, + { MP_QSTR_sh, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_sh }, + { MP_QSTR_sll, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sll }, + { MP_QSTR_slli, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_slli }, + { MP_QSTR_slt, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_slt }, + { MP_QSTR_slti, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_slti }, + { MP_QSTR_sltiu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_sltiu }, + { MP_QSTR_sltu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sltu }, + { MP_QSTR_sra, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sra }, + { MP_QSTR_srai, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_srai }, + { MP_QSTR_srl, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_srl }, + { MP_QSTR_srli, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_srli }, + { MP_QSTR_sub, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sub }, + { MP_QSTR_sw, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_sw }, + { MP_QSTR_xor, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_xor }, + { MP_QSTR_xori, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_xori }, }; #undef RC @@ -384,6 +430,10 @@ static bool validate_integer(mp_uint_t value, mp_uint_t mask, mp_uint_t flags) { return true; } +#define ET_WRONG_ARGUMENT_KIND MP_ERROR_TEXT("opcode '%q' argument %d: expecting %q") +#define ET_WRONG_ARGUMENTS_COUNT MP_ERROR_TEXT("opcode '%q': expecting %d arguments") +#define ET_OUT_OF_RANGE MP_ERROR_TEXT("opcode '%q' argument %d: out of range") + static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, const opcode_t *opcode, mp_parse_node_t node, mp_uint_t node_index) { assert((node_index < 3) && "Invalid argument node number."); @@ -396,19 +446,19 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, case 0: kind = opcode->argument1_kind; shift = opcode->argument1_shift; - mask = opcode->argument1_mask; + mask = OPCODE_MASKS[opcode->argument1_mask]; break; case 1: kind = opcode->argument2_kind; shift = opcode->argument2_shift; - mask = opcode->argument2_mask; + mask = OPCODE_MASKS[opcode->argument2_mask]; break; case 2: kind = opcode->argument3_kind; shift = opcode->argument3_shift; - mask = opcode->argument3_mask; + mask = OPCODE_MASKS[opcode->argument3_mask]; break; default: @@ -417,6 +467,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, switch (kind & 0x03) { case N: + assert(mask == OPCODE_MASKS[MASK_NOT_USED] && "Invalid mask index for missing operand."); return true; case R: { @@ -424,15 +475,14 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, if (!parse_register_node(node, ®ister_index, false)) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("opcode '%q' argument %d must be a register"), - opcode_qstr, node_index + 1)); + ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_register)); return false; } if ((mask & (1U << register_index)) == 0) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("opcode '%q' argument %d is an invalid register"), + MP_ERROR_TEXT("opcode '%q' argument %d: unknown register"), opcode_qstr, node_index + 1)); return false; } @@ -446,8 +496,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, if (!mp_parse_node_get_int_maybe(node, &object)) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("opcode '%q' argument %d must be an integer"), - opcode_qstr, node_index + 1)); + ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_integer)); return false; } @@ -474,8 +523,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, if (!MP_PARSE_NODE_IS_ID(node)) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("opcode '%q' argument %d must be a label"), - opcode_qstr, node_index + 1)); + ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_label)); return false; } @@ -484,7 +532,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, if (label_index >= emit->max_num_labels && emit->pass == MP_PASS_EMIT) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("opcode '%q' argument %d label '%q' is undefined"), + MP_ERROR_TEXT("opcode '%q' argument %d: undefined label '%q'"), opcode_qstr, node_index + 1, qstring)); return false; } @@ -512,15 +560,13 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, out_of_range: emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("opcode '%q' argument %d is out of range"), - opcode_qstr, node_index + 1)); + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode_qstr, node_index + 1)); return false; zero_immediate: emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("opcode '%q' argument %d must not be zero"), + MP_ERROR_TEXT("opcode '%q' argument %d: must not be zero"), opcode_qstr, node_index + 1)); return false; } @@ -556,18 +602,14 @@ static bool parse_register_offset_node(emit_inline_asm_t *emit, qstr opcode_qstr mp_obj_t object; if (!mp_parse_node_get_int_maybe(node_struct->nodes[0], &object)) { emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("opcode '%q' argument %d must be an integer"), - opcode_qstr, 2)); + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_WRONG_ARGUMENT_KIND, opcode_qstr, 2, MP_QSTR_integer)); return false; } mp_uint_t value = mp_obj_get_int_truncated(object); value = (~value + 1) & (mp_uint_t)-1; - if (!validate_integer(value << opcode_data->argument2_shift, opcode_data->argument2_mask, opcode_data->argument2_kind)) { + if (!validate_integer(value << opcode_data->argument2_shift, OPCODE_MASKS[opcode_data->argument2_mask], opcode_data->argument2_kind)) { emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("opcode '%q' argument %d is out of range"), - opcode_qstr, 2)); + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode_qstr, 2)); return false; } } else { @@ -587,8 +629,7 @@ static bool parse_register_offset_node(emit_inline_asm_t *emit, qstr opcode_qstr invalid_structure: emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("opcode '%q' argument %d must be an integer offset to a register"), - opcode_qstr, node_index + 1)); + ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_offset)); return false; } @@ -724,11 +765,9 @@ static bool handle_load_store_opcode_with_offset(emit_inline_asm_t *emit, qstr o if (negative) { immediate = (~immediate + 1) & (mp_uint_t)-1; } - if (!is_in_signed_mask(opcode_data->argument2_mask, immediate)) { + if (!is_in_signed_mask(OPCODE_MASKS[opcode_data->argument2_mask], immediate)) { emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("opcode '%q' argument %d is out of range"), - opcode, 2)); + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode, 2)); return false; } @@ -748,49 +787,40 @@ static void emit_inline_rv32_opcode(emit_inline_asm_t *emit, qstr opcode, mp_uin if (!opcode_data) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("unsupported or unknown RV32 instruction '%q'."), opcode)); + MP_ERROR_TEXT("unknown RV32 instruction '%q'"), opcode)); return; } - size_t required_arguments = 0; - if (opcode_data->argument1_kind != N) { - required_arguments++; - } - if (opcode_data->argument2_kind != N) { - required_arguments++; - } - if (opcode_data->argument3_kind != N) { - required_arguments++; - } - + assert((opcode_data->argument1_mask < MP_ARRAY_SIZE(OPCODE_MASKS)) && "Argument #1 opcode mask index out of bounds."); + assert((opcode_data->argument2_mask < MP_ARRAY_SIZE(OPCODE_MASKS)) && "Argument #2 opcode mask index out of bounds."); + assert((opcode_data->argument3_mask < MP_ARRAY_SIZE(OPCODE_MASKS)) && "Argument #3 opcode mask index out of bounds."); + assert((opcode_data->calling_convention < CALL_COUNT) && "Calling convention index out of bounds."); if (opcode_data->calling_convention != CALL_RIR) { - if (required_arguments != arguments_count) { + if (opcode_data->arguments_count != arguments_count) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("RV32 instruction '%q' requires %d arguments."), opcode, required_arguments)); + ET_WRONG_ARGUMENTS_COUNT, opcode, opcode_data->arguments_count)); return; } - if (required_arguments >= 1 && !validate_argument(emit, opcode, opcode_data, argument_nodes[0], 0)) { + if (opcode_data->arguments_count >= 1 && !validate_argument(emit, opcode, opcode_data, argument_nodes[0], 0)) { return; } - if (required_arguments >= 2 && !validate_argument(emit, opcode, opcode_data, argument_nodes[1], 1)) { + if (opcode_data->arguments_count >= 2 && !validate_argument(emit, opcode, opcode_data, argument_nodes[1], 1)) { return; } - if (required_arguments >= 3 && !validate_argument(emit, opcode, opcode_data, argument_nodes[2], 2)) { + if (opcode_data->arguments_count >= 3 && !validate_argument(emit, opcode, opcode_data, argument_nodes[2], 2)) { return; } handle_opcode(emit, opcode, opcode_data, argument_nodes); return; } - assert(required_arguments == 3 && "Invalid arguments count for calling convention."); assert((opcode_data->argument2_kind & U) == 0 && "Offset must not be unsigned."); assert((opcode_data->argument2_kind & Z) == 0 && "Offset can be zero."); if (arguments_count != 2) { emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("RV32 instruction '%q' requires %d arguments."), opcode, 2)); + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_WRONG_ARGUMENTS_COUNT, opcode, 2)); return; } From 50fab08e6b861adf65905d6adacd74201c87ddb9 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 28 Jan 2025 14:58:29 +0100 Subject: [PATCH 322/546] py/emitinlinextensa: Simplify register name lookup. This commit changes the Xtensa inline assembly parser to use a slightly simpler (and probably a tiny bit more efficient) way to look up register names when decoding instruction parameters. Signed-off-by: Alessandro Gatti --- py/emitinlinextensa.c | 49 +++++++++---------------------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/py/emitinlinextensa.c b/py/emitinlinextensa.c index 57056d597aab7..fed259cfc6b20 100644 --- a/py/emitinlinextensa.c +++ b/py/emitinlinextensa.c @@ -115,50 +115,21 @@ static bool emit_inline_xtensa_label(emit_inline_asm_t *emit, mp_uint_t label_nu return true; } -typedef struct _reg_name_t { byte reg; - byte name[3]; -} reg_name_t; -static const reg_name_t reg_name_table[] = { - {0, "a0\0"}, - {1, "a1\0"}, - {2, "a2\0"}, - {3, "a3\0"}, - {4, "a4\0"}, - {5, "a5\0"}, - {6, "a6\0"}, - {7, "a7\0"}, - {8, "a8\0"}, - {9, "a9\0"}, - {10, "a10"}, - {11, "a11"}, - {12, "a12"}, - {13, "a13"}, - {14, "a14"}, - {15, "a15"}, +static const qstr_short_t REGISTERS[16] = { + MP_QSTR_a0, MP_QSTR_a1, MP_QSTR_a2, MP_QSTR_a3, MP_QSTR_a4, MP_QSTR_a5, MP_QSTR_a6, MP_QSTR_a7, + MP_QSTR_a8, MP_QSTR_a9, MP_QSTR_a10, MP_QSTR_a11, MP_QSTR_a12, MP_QSTR_a13, MP_QSTR_a14, MP_QSTR_a15 }; -// return empty string in case of error, so we can attempt to parse the string -// without a special check if it was in fact a string -static const char *get_arg_str(mp_parse_node_t pn) { - if (MP_PARSE_NODE_IS_ID(pn)) { - qstr qst = MP_PARSE_NODE_LEAF_ARG(pn); - return qstr_str(qst); - } else { - return ""; - } -} - static mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) { - const char *reg_str = get_arg_str(pn); - for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) { - const reg_name_t *r = ®_name_table[i]; - if (reg_str[0] == r->name[0] - && reg_str[1] == r->name[1] - && reg_str[2] == r->name[2] - && (reg_str[2] == '\0' || reg_str[3] == '\0')) { - return r->reg; + if (MP_PARSE_NODE_IS_ID(pn)) { + qstr node_qstr = MP_PARSE_NODE_LEAF_ARG(pn); + for (size_t i = 0; i < MP_ARRAY_SIZE(REGISTERS); i++) { + if (node_qstr == REGISTERS[i]) { + return i; + } } } + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects a register"), op)); From 5fdd249c55d44ed0513e931c9b7364dd8b3454d9 Mon Sep 17 00:00:00 2001 From: Yoctopuce dev Date: Thu, 30 Jan 2025 10:23:06 +0100 Subject: [PATCH 323/546] py/parsenum: Reduce code footprint of mp_parse_num_float. The mantissa parsing code uses a floating point variable to accumulate digits. Using an `mp_float_uint_t` variable instead and casting to `mp_float_t` at the very end reduces code size. In some cases, it also improves the rounding behaviour as extra digits are taken into account by the int-to-float conversion code. An extra test case handles the special case where mantissa overflow occurs while processing deferred trailing zeros. Signed-off-by: Yoctopuce dev --- py/parsenum.c | 25 ++++++++++++++----------- tests/float/float_parse.py | 3 +++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/py/parsenum.c b/py/parsenum.c index 27d6641198730..3281eb4b8511a 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -179,39 +179,40 @@ typedef enum { } parse_dec_in_t; #if MICROPY_PY_BUILTINS_FLOAT -// DEC_VAL_MAX only needs to be rough and is used to retain precision while not overflowing +// MANTISSA_MAX is used to retain precision while not overflowing mantissa // SMALL_NORMAL_VAL is the smallest power of 10 that is still a normal float // EXACT_POWER_OF_10 is the largest value of x so that 10^x can be stored exactly in a float // Note: EXACT_POWER_OF_10 is at least floor(log_5(2^mantissa_length)). Indeed, 10^n = 2^n * 5^n // so we only have to store the 5^n part in the mantissa (the 2^n part will go into the float's // exponent). #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT -#define DEC_VAL_MAX 1e20F +#define MANTISSA_MAX 0x19999998U #define SMALL_NORMAL_VAL (1e-37F) #define SMALL_NORMAL_EXP (-37) #define EXACT_POWER_OF_10 (9) #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE -#define DEC_VAL_MAX 1e200 +#define MANTISSA_MAX 0x1999999999999998ULL #define SMALL_NORMAL_VAL (1e-307) #define SMALL_NORMAL_EXP (-307) #define EXACT_POWER_OF_10 (22) #endif // Break out inner digit accumulation routine to ease trailing zero deferral. -static void accept_digit(mp_float_t *p_dec_val, int dig, int *p_exp_extra, int in) { +static mp_float_uint_t accept_digit(mp_float_uint_t p_mantissa, unsigned int dig, int *p_exp_extra, int in) { // Core routine to ingest an additional digit. - if (*p_dec_val < DEC_VAL_MAX) { + if (p_mantissa < MANTISSA_MAX) { // dec_val won't overflow so keep accumulating - *p_dec_val = 10 * *p_dec_val + dig; if (in == PARSE_DEC_IN_FRAC) { --(*p_exp_extra); } + return 10u * p_mantissa + dig; } else { // dec_val might overflow and we anyway can't represent more digits // of precision, so ignore the digit and just adjust the exponent if (in == PARSE_DEC_IN_INTG) { ++(*p_exp_extra); } + return p_mantissa; } } #endif // MICROPY_PY_BUILTINS_FLOAT @@ -273,6 +274,7 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex // string should be a decimal number parse_dec_in_t in = PARSE_DEC_IN_INTG; bool exp_neg = false; + mp_float_uint_t mantissa = 0; int exp_val = 0; int exp_extra = 0; int trailing_zeros_intg = 0, trailing_zeros_frac = 0; @@ -288,9 +290,9 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex exp_val = 10 * exp_val + dig; } } else { - if (dig == 0 || dec_val >= DEC_VAL_MAX) { + if (dig == 0 || mantissa >= MANTISSA_MAX) { // Defer treatment of zeros in fractional part. If nothing comes afterwards, ignore them. - // Also, once we reach DEC_VAL_MAX, treat every additional digit as a trailing zero. + // Also, once we reach MANTISSA_MAX, treat every additional digit as a trailing zero. if (in == PARSE_DEC_IN_INTG) { ++trailing_zeros_intg; } else { @@ -299,14 +301,14 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex } else { // Time to un-defer any trailing zeros. Intg zeros first. while (trailing_zeros_intg) { - accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_INTG); + mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_INTG); --trailing_zeros_intg; } while (trailing_zeros_frac) { - accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_FRAC); + mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_FRAC); --trailing_zeros_frac; } - accept_digit(&dec_val, dig, &exp_extra, in); + mantissa = accept_digit(mantissa, dig, &exp_extra, in); } } } else if (in == PARSE_DEC_IN_INTG && dig == '.') { @@ -340,6 +342,7 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex // apply the exponent, making sure it's not a subnormal value exp_val += exp_extra + trailing_zeros_intg; + dec_val = (mp_float_t)mantissa; if (exp_val < SMALL_NORMAL_EXP) { exp_val -= SMALL_NORMAL_EXP; dec_val *= SMALL_NORMAL_VAL; diff --git a/tests/float/float_parse.py b/tests/float/float_parse.py index de27c33e7be57..6131da0a63ab9 100644 --- a/tests/float/float_parse.py +++ b/tests/float/float_parse.py @@ -31,6 +31,9 @@ print(float("1e18446744073709551621")) print(float("1e-18446744073709551621")) +# mantissa overflow while processing deferred trailing zeros +print(float("10000000000000000000001")) + # check small decimals are as close to their true value as possible for n in range(1, 10): print(float("0.%u" % n) == n / 10) From 86526e9c2b5fcde2ecdee3817cc002920078b07b Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 26 Feb 2025 14:37:55 +1100 Subject: [PATCH 324/546] tools/mpremote: Optimise readline support in mount. This significantly speeds up readline on files opened directly from an mpremote mount. Signed-off-by: Andrew Leech --- tools/mpremote/mpremote/transport_serial.py | 41 ++++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/tools/mpremote/mpremote/transport_serial.py b/tools/mpremote/mpremote/transport_serial.py index d3148f9ace318..6aed0bb496b93 100644 --- a/tools/mpremote/mpremote/transport_serial.py +++ b/tools/mpremote/mpremote/transport_serial.py @@ -404,12 +404,13 @@ def umount_local(self): "CMD_OPEN": 4, "CMD_CLOSE": 5, "CMD_READ": 6, - "CMD_WRITE": 7, - "CMD_SEEK": 8, - "CMD_REMOVE": 9, - "CMD_RENAME": 10, - "CMD_MKDIR": 11, - "CMD_RMDIR": 12, + "CMD_READLINE": 7, + "CMD_WRITE": 8, + "CMD_SEEK": 9, + "CMD_REMOVE": 10, + "CMD_RENAME": 11, + "CMD_MKDIR": 12, + "CMD_RMDIR": 13, } fs_hook_code = """\ @@ -592,12 +593,16 @@ def readinto(self, buf): return n def readline(self): - l = '' - while 1: - c = self.read(1) - l += c - if c == '\\n' or c == '': - return l + c = self.cmd + c.begin(CMD_READLINE) + c.wr_s8(self.fd) + data = c.rd_bytes(None) + c.end() + if self.is_text: + data = str(data, 'utf8') + else: + data = bytes(data) + return data def readlines(self): ls = [] @@ -746,8 +751,7 @@ def __mount(): """ # Apply basic compression on hook code. -for key, value in fs_hook_cmds.items(): - fs_hook_code = re.sub(key, str(value), fs_hook_code) +fs_hook_code = re.sub(r"CMD_[A-Z_]+", lambda m: str(fs_hook_cmds[m.group(0)]), fs_hook_code) fs_hook_code = re.sub(" *#.*$", "", fs_hook_code, flags=re.MULTILINE) fs_hook_code = re.sub("\n\n+", "\n", fs_hook_code) fs_hook_code = re.sub(" ", " ", fs_hook_code) @@ -887,6 +891,14 @@ def do_read(self): self.wr_bytes(buf) # self.log_cmd(f"read {fd} {n} -> {len(buf)}") + def do_readline(self): + fd = self.rd_s8() + buf = self.data_files[fd][0].readline() + if self.data_files[fd][1]: + buf = bytes(buf, "utf8") + self.wr_bytes(buf) + # self.log_cmd(f"readline {fd} -> {len(buf)}") + def do_seek(self): fd = self.rd_s8() n = self.rd_s32() @@ -960,6 +972,7 @@ def do_rmdir(self): fs_hook_cmds["CMD_OPEN"]: do_open, fs_hook_cmds["CMD_CLOSE"]: do_close, fs_hook_cmds["CMD_READ"]: do_read, + fs_hook_cmds["CMD_READLINE"]: do_readline, fs_hook_cmds["CMD_WRITE"]: do_write, fs_hook_cmds["CMD_SEEK"]: do_seek, fs_hook_cmds["CMD_REMOVE"]: do_remove, From 71c7c03e411121b0193590bb683961e83c787ab1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 26 Feb 2025 17:12:45 +1100 Subject: [PATCH 325/546] tools/mpremote/tests: Add test for RemoteFile.readline. Signed-off-by: Damien George --- tools/mpremote/tests/test_mount.sh | 4 ++++ tools/mpremote/tests/test_mount.sh.exp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/tools/mpremote/tests/test_mount.sh b/tools/mpremote/tests/test_mount.sh index 9eab0b0d6e8c5..724ae1417fccc 100755 --- a/tools/mpremote/tests/test_mount.sh +++ b/tools/mpremote/tests/test_mount.sh @@ -26,3 +26,7 @@ $MPREMOTE mount ${TMP} exec "import mount_package; mount_package.x(); mount_pack echo ----- $MPREMOTE mount ${TMP} exec "open('test.txt', 'w').write('hello world\n')" cat "${TMP}/test.txt" + +# Test RemoteFile.readline and RemoteFile.readlines methods. +echo ----- +$MPREMOTE mount ${TMP} exec "print(open('test.txt').readlines())" diff --git a/tools/mpremote/tests/test_mount.sh.exp b/tools/mpremote/tests/test_mount.sh.exp index 560f5e4f10a9b..616cb75bcf5f1 100644 --- a/tools/mpremote/tests/test_mount.sh.exp +++ b/tools/mpremote/tests/test_mount.sh.exp @@ -5,3 +5,6 @@ Local directory ${TMP} is mounted at /remote ----- Local directory ${TMP} is mounted at /remote hello world +----- +['hello world\n'] +Local directory ${TMP} is mounted at /remote From 13b02376af4e9d4b76bf053935b51ddc7c87df03 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 19 Feb 2025 12:44:19 +1100 Subject: [PATCH 326/546] lib/pico-sdk: Update to version 2.1.1. Release notes: https://github.com/raspberrypi/pico-sdk/releases/tag/2.1.1 Signed-off-by: Damien George --- lib/pico-sdk | 2 +- ports/rp2/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pico-sdk b/lib/pico-sdk index 95ea6acad1311..bddd20f928ce7 160000 --- a/lib/pico-sdk +++ b/lib/pico-sdk @@ -1 +1 @@ -Subproject commit 95ea6acad131124694cda1c162c52cd30e0aece0 +Subproject commit bddd20f928ce76142793bef434d4f75f4af6e433 diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 8a4092529b1f2..7002ad8769ea9 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -276,7 +276,7 @@ if(PICO_RP2040) elseif(PICO_RP2350 AND PICO_ARM) target_sources(pico_float_micropython INTERFACE ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_aeabi_dcp.S - ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_conv_m33.S + ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_common_m33.S ) endif() From 9939b6c6b906cec702e825a7b58f5647c70c178e Mon Sep 17 00:00:00 2001 From: machdyne Date: Fri, 20 Dec 2024 23:39:02 +0100 Subject: [PATCH 327/546] rp2/boards/MACHDYNE_WERKZEUG: Add support for Machdyne Werkzeug. Signed-off-by: machdyne --- ports/rp2/boards/MACHDYNE_WERKZEUG/board.json | 19 ++++++++++++ .../MACHDYNE_WERKZEUG/mpconfigboard.cmake | 3 ++ .../boards/MACHDYNE_WERKZEUG/mpconfigboard.h | 3 ++ ports/rp2/boards/MACHDYNE_WERKZEUG/pins.csv | 30 +++++++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 ports/rp2/boards/MACHDYNE_WERKZEUG/board.json create mode 100644 ports/rp2/boards/MACHDYNE_WERKZEUG/mpconfigboard.cmake create mode 100644 ports/rp2/boards/MACHDYNE_WERKZEUG/mpconfigboard.h create mode 100644 ports/rp2/boards/MACHDYNE_WERKZEUG/pins.csv diff --git a/ports/rp2/boards/MACHDYNE_WERKZEUG/board.json b/ports/rp2/boards/MACHDYNE_WERKZEUG/board.json new file mode 100644 index 0000000000000..c657f18b21de6 --- /dev/null +++ b/ports/rp2/boards/MACHDYNE_WERKZEUG/board.json @@ -0,0 +1,19 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Dual-core", + "External Flash", + "USB-C" + ], + "images": [ + "werkzeug.jpg" + ], + "mcu": "rp2040", + "product": "Werkzeug", + "thumbnail": "", + "url": "https://machdyne.com/product/werkzeug-multi-tool/", + "vendor": "Machdyne" +} diff --git a/ports/rp2/boards/MACHDYNE_WERKZEUG/mpconfigboard.cmake b/ports/rp2/boards/MACHDYNE_WERKZEUG/mpconfigboard.cmake new file mode 100644 index 0000000000000..281c4e6f1b45f --- /dev/null +++ b/ports/rp2/boards/MACHDYNE_WERKZEUG/mpconfigboard.cmake @@ -0,0 +1,3 @@ +# cmake file for Machdyne Werkzeug +set(PICO_BOARD "machdyne_werkzeug") +set(PICO_PLATFORM "rp2040") diff --git a/ports/rp2/boards/MACHDYNE_WERKZEUG/mpconfigboard.h b/ports/rp2/boards/MACHDYNE_WERKZEUG/mpconfigboard.h new file mode 100644 index 0000000000000..476ea23b9c86b --- /dev/null +++ b/ports/rp2/boards/MACHDYNE_WERKZEUG/mpconfigboard.h @@ -0,0 +1,3 @@ +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "Machdyne Werkzeug" +#define MICROPY_HW_FLASH_STORAGE_BYTES (384 * 1024) diff --git a/ports/rp2/boards/MACHDYNE_WERKZEUG/pins.csv b/ports/rp2/boards/MACHDYNE_WERKZEUG/pins.csv new file mode 100644 index 0000000000000..c3c578132069c --- /dev/null +++ b/ports/rp2/boards/MACHDYNE_WERKZEUG/pins.csv @@ -0,0 +1,30 @@ +GP0,GPIO0 +GP1,GPIO1 +GP2,GPIO2 +GP3,GPIO3 +GP4,GPIO4 +GP5,GPIO5 +GP6,GPIO6 +GP7,GPIO7 +GP8,GPIO8 +GP9,GPIO9 +GP10,GPIO10 +GP11,GPIO11 +PMOD10,GPIO12 +PMOD4,GPIO13 +PMOD9,GPIO14 +PMOD3,GPIO15 +PMOD8,GPIO16 +PMOD2,GPIO17 +PMOD7,GPIO18 +PMOD1,GPIO19 +LED_GREEN,GPIO20 +LED_RED,GPIO21 +USBA_POWER,GPIO22 +USBA_DN,GPIO23 +USBA_DP,GPIO24 +USBA_DP_PU,GPIO25 +GP26,GPIO26 +GP27,GPIO27 +GP28,GPIO28 +GP29,GPIO29 From cad62c20f2971b1248f55f27ab9f6d9bf8ccdafa Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Tue, 18 Feb 2025 14:57:53 -0700 Subject: [PATCH 328/546] rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA: Add SparkFun XRP Controller. Signed-off-by: Dryw Wade --- .../SPARKFUN_XRP_CONTROLLER_BETA/board.json | 23 +++++++ .../SPARKFUN_XRP_CONTROLLER_BETA/manifest.py | 6 ++ .../mpconfigboard.cmake | 14 ++++ .../mpconfigboard.h | 28 ++++++++ .../SPARKFUN_XRP_CONTROLLER_BETA/pins.csv | 65 +++++++++++++++++++ 5 files changed, 136 insertions(+) create mode 100644 ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json create mode 100644 ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/manifest.py create mode 100644 ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.cmake create mode 100644 ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h create mode 100644 ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/pins.csv diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json new file mode 100644 index 0000000000000..cece574326503 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json @@ -0,0 +1,23 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "BLE", + "Dual-core", + "External Flash", + "IMU", + "JST-SH", + "USB", + "WiFi" + ], + "images": [ + "22727-_01.jpg" + ], + "mcu": "rp2040", + "product": "XRP Controller (Beta)", + "thumbnail": "", + "url": "https://www.sparkfun.com/sparkfun-experiential-robotics-platform-xrp-controller.html", + "vendor": "Sparkfun" +} diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/manifest.py b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/manifest.py new file mode 100644 index 0000000000000..4e38f09cdee4f --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/manifest.py @@ -0,0 +1,6 @@ +include("$(PORT_DIR)/boards/manifest.py") + +require("bundle-networking") + +# Bluetooth +require("aioble") diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.cmake b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.cmake new file mode 100644 index 0000000000000..6c06efefb6479 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.cmake @@ -0,0 +1,14 @@ +# cmake file for SparkFun XRP Controller (Beta) + +set(PICO_BOARD "pico_w") + +set(MICROPY_PY_LWIP ON) +set(MICROPY_PY_NETWORK_CYW43 ON) + +# Bluetooth +set(MICROPY_PY_BLUETOOTH ON) +set(MICROPY_BLUETOOTH_BTSTACK ON) +set(MICROPY_PY_BLUETOOTH_CYW43 ON) + +# Board specific version of the frozen manifest +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h new file mode 100644 index 0000000000000..56071e1873341 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h @@ -0,0 +1,28 @@ +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "SparkFun XRP Controller (Beta)" + +// todo: We need something to check our binary size +#define MICROPY_HW_FLASH_STORAGE_BYTES (848 * 1024) + +// Enable networking. +#define MICROPY_PY_NETWORK 1 +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "XRP" + +// CYW43 driver configuration. +#define CYW43_USE_SPI (1) +#define CYW43_LWIP (1) +#define CYW43_GPIO (1) +#define CYW43_SPI_PIO (1) + +// For debugging mbedtls - also set +// Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose +// #define MODUSSL_MBEDTLS_DEBUG_LEVEL 1 + +#define MICROPY_HW_PIN_EXT_COUNT CYW43_WL_GPIO_COUNT + +// If this returns true for a pin then its irq will not be disabled on a soft reboot +int mp_hal_is_pin_reserved(int n); +#define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i) + +#define MICROPY_HW_I2C1_SDA (18) +#define MICROPY_HW_I2C1_SCL (19) diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/pins.csv b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/pins.csv new file mode 100644 index 0000000000000..dd047063b5c02 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/pins.csv @@ -0,0 +1,65 @@ +# XRP default pin names +MOTOR_L_IN_1,GPIO6 +MOTOR_L_IN_2,GPIO7 +MOTOR_R_IN_1,GPIO14 +MOTOR_R_IN_2,GPIO15 +MOTOR_3_IN_1,GPIO2 +MOTOR_3_IN_2,GPIO3 +MOTOR_4_IN_1,GPIO10 +MOTOR_4_IN_2,GPIO11 +MOTOR_L_ENCODER_A,GPIO4 +MOTOR_L_ENCODER_B,GPIO5 +MOTOR_R_ENCODER_A,GPIO12 +MOTOR_R_ENCODER_B,GPIO13 +MOTOR_3_ENCODER_A,GPIO0 +MOTOR_3_ENCODER_B,GPIO1 +MOTOR_4_ENCODER_A,GPIO8 +MOTOR_4_ENCODER_B,GPIO9 +SERVO_1,GPIO16 +SERVO_2,GPIO17 +I2C_SDA_1,GPIO18 +I2C_SCL_1,GPIO19 +DISTANCE_TRIGGER,GPIO20 +DISTANCE_ECHO,GPIO21 +LINE_L,GPIO26 +LINE_R,GPIO27 +BOARD_VIN_MEASURE,GPIO28 +BOARD_USER_BUTTON,GPIO22 +BOARD_LED,EXT_GPIO0 + +# XRP alternate pin names +ML_IN_1,GPIO6 +ML_IN_2,GPIO7 +MR_IN_1,GPIO14 +MR_IN_2,GPIO15 +M3_IN_1,GPIO2 +M3_IN_2,GPIO3 +M4_IN_1,GPIO10 +M4_IN_2,GPIO11 +ML_ENC_A,GPIO4 +ML_ENC_B,GPIO5 +MR_ENC_A,GPIO12 +MR_ENC_B,GPIO13 +M3_ENC_A,GPIO0 +M3_ENC_B,GPIO1 +M4_ENC_A,GPIO8 +M4_ENC_B,GPIO9 +S1,GPIO16 +S2,GPIO17 +SDA_1,GPIO18 +SCL_1,GPIO19 +RANGE_TRIGGER,GPIO20 +RANGE_ECHO,GPIO21 +REFLECTANCE_L,GPIO26 +REFLECTANCE_R,GPIO27 +BRD_VIN,GPIO28 +BRD_USR_BTN,GPIO22 +BRD_LED,EXT_GPIO0 + +# LED default names +LED,EXT_GPIO0 + +# Radio GPIO pins +WL_GPIO0,EXT_GPIO0 +WL_GPIO1,EXT_GPIO1 +WL_GPIO2,EXT_GPIO2 From bb4ec886f8994ad7b2db6f80c7b46e6eedbc40e7 Mon Sep 17 00:00:00 2001 From: Malcolm McKellips Date: Fri, 24 Jan 2025 09:36:02 -0700 Subject: [PATCH 329/546] rp2/machine_i2c: Make I2C bus ID arg optional with default. This commit gives the option to not pass an I2C Bus ID when creating a machine I2C object. If the ID is not provided, the default bus ID (which is `PICO_DEFAULT_I2C`) is used. This allows users to simply declare an I2C object with `machine.I2C()` without passing any arguments, thus creating an object with the default I2C ID, SCL, and SDA. Signed-off-by: Malcolm McKellips --- ports/rp2/machine_i2c.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ports/rp2/machine_i2c.c b/ports/rp2/machine_i2c.c index 28032e8085c24..e97a852b24305 100644 --- a/ports/rp2/machine_i2c.c +++ b/ports/rp2/machine_i2c.c @@ -97,7 +97,11 @@ static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_prin mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_id, ARG_freq, ARG_scl, ARG_sda, ARG_timeout }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + #ifdef PICO_DEFAULT_I2C + { MP_QSTR_id, MP_ARG_INT, {.u_int = PICO_DEFAULT_I2C} }, + #else + { MP_QSTR_id, MP_ARG_INT, {.u_int = -1} }, + #endif { MP_QSTR_freq, MP_ARG_INT, {.u_int = DEFAULT_I2C_FREQ} }, { MP_QSTR_scl, MICROPY_I2C_PINS_ARG_OPTS | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_sda, MICROPY_I2C_PINS_ARG_OPTS | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, @@ -108,8 +112,9 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - // Get I2C bus. - int i2c_id = mp_obj_get_int(args[ARG_id].u_obj); + int i2c_id = args[ARG_id].u_int; + + // Check if the I2C bus is valid if (i2c_id < 0 || i2c_id >= MP_ARRAY_SIZE(machine_i2c_obj)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id); } From c143eb50248b3a3f521775f09f5073675095e48a Mon Sep 17 00:00:00 2001 From: Malcolm McKellips Date: Fri, 24 Jan 2025 13:05:07 -0700 Subject: [PATCH 330/546] esp32/machine_i2c: Make I2C bus ID arg optional with default. Similar to the previous commit, this allows constructing an I2C instance without specifying an ID. The default ID is I2C_NUM_0. Signed-off-by: Malcolm McKellips --- ports/esp32/machine_i2c.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ports/esp32/machine_i2c.c b/ports/esp32/machine_i2c.c index 732a62f47f193..12b86e4aa0294 100644 --- a/ports/esp32/machine_i2c.c +++ b/ports/esp32/machine_i2c.c @@ -146,12 +146,15 @@ static void machine_hw_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_p } mp_obj_t machine_hw_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - MP_MACHINE_I2C_CHECK_FOR_LEGACY_SOFTI2C_CONSTRUCTION(n_args, n_kw, all_args); + // Create a SoftI2C instance if no id is specified (or is -1) but other arguments are given + if (n_args != 0) { + MP_MACHINE_I2C_CHECK_FOR_LEGACY_SOFTI2C_CONSTRUCTION(n_args, n_kw, all_args); + } // Parse args enum { ARG_id, ARG_scl, ARG_sda, ARG_freq, ARG_timeout }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_id, MP_ARG_INT, {.u_int = I2C_NUM_0} }, { MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} }, @@ -161,7 +164,9 @@ mp_obj_t machine_hw_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_ mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // Get I2C bus - mp_int_t i2c_id = mp_obj_get_int(args[ARG_id].u_obj); + mp_int_t i2c_id = args[ARG_id].u_int; + + // Check if the I2C bus is valid if (!(I2C_NUM_0 <= i2c_id && i2c_id < I2C_NUM_MAX)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id); } From ec876a5e27163299ff60ee70e4ef21073dd67704 Mon Sep 17 00:00:00 2001 From: garywill Date: Sun, 22 Dec 2024 13:35:21 +0800 Subject: [PATCH 331/546] esp32/README: Make some minor improvements to the README. Changes: - To add user to Linux dialout group, usermod is the universal Linux way. adduser is Debian-based way. - When installing IDF, we don't have to install all toolchains for all chips. - List currently supported chip models. - Other minor typo and gramma corrections. Signed-off-by: garywill --- ports/esp32/README.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 6ed7eddb8b80f..d9115072a45c3 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -5,6 +5,9 @@ This is a port of MicroPython to the Espressif ESP32 series of microcontrollers. It uses the ESP-IDF framework and MicroPython runs as a task under FreeRTOS. +Currently supports ESP32, ESP32-C3, ESP32-C6, ESP32-S2 and ESP32-S3 +(ESP8266 is supported by a separate MicroPython port). + Supported features include: - REPL (Python prompt) over UART0. - 16k stack for the MicroPython task and approximately 100k Python heap. @@ -67,12 +70,16 @@ After you've cloned and checked out the IDF to the correct version, run the ```bash $ cd esp-idf -$ ./install.sh # (or install.bat on Windows) +$ ./install.sh esp32 # (or install.bat on Windows) $ source export.sh # (or export.bat on Windows) ``` -The `install.sh` step only needs to be done once. You will need to source -`export.sh` for every new session. +The `install.sh` step only needs to be done once. Change `esp32` if you are +targeting other chip. Use comma-separated list like `esp32,esp32s2` to +install for multiple chips. Or omit the chip to install for all Espressif +chips (which is slower). + +You will need to source `export.sh` for every new session. Building the firmware --------------------- @@ -85,7 +92,7 @@ this repository): $ make -C mpy-cross ``` -Then to build MicroPython for the ESP32 run: +Then to build MicroPython for ESP32 run: ```bash $ cd ports/esp32 @@ -106,7 +113,7 @@ rebooting or logging out and in again. (Note: on some distributions this may be the `uucp` group, run `ls -la /dev/ttyUSB0` to check.) ```bash -$ sudo adduser dialout +$ sudo usermod -aG dialout ``` If you are installing MicroPython to your module for the first time, or @@ -141,7 +148,7 @@ $ idf.py -D MICROPY_BOARD=ESP32_GENERIC build $ idf.py flash ``` -Some boards also support "variants", which are allow for small variations of +Some boards also support "variants", which allow for small variations of an otherwise similar board. For example different flash sizes or features. For example to build the `OTA` variant of `ESP32_GENERIC`. @@ -173,7 +180,7 @@ or $ miniterm.py /dev/ttyUSB0 115200 ``` -You can also use `idf.py monitor`. +You can also use `idf.py monitor` or `mpremote`. Configuring the WiFi and using the board ---------------------------------------- From cbd21b39778d3f7b808d0c631a193625035b2190 Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Mon, 27 Jan 2025 16:05:07 +0000 Subject: [PATCH 332/546] esp32/esp32_common.cmake: Allow overriding linker.lf. Particularly for out of tree builds, one may need to provide alternative or extra linker fragment files, or specify an absolute path to the default `linker.lf` file. In the default case, do nothing, provide a plain `linker.lf`, as before. Signed-off-by: Karl Palsson --- ports/esp32/esp32_common.cmake | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index e03d2ff11aada..059989c10a4db 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -178,6 +178,13 @@ list(APPEND IDF_COMPONENTS vfs ) +# Provide the default LD fragment if not set +if (MICROPY_USER_LDFRAGMENTS) + set(MICROPY_LDFRAGMENTS ${MICROPY_USER_LDFRAGMENTS}) +else() + set(MICROPY_LDFRAGMENTS linker.lf) +endif() + # Register the main IDF component. idf_component_register( SRCS @@ -197,7 +204,7 @@ idf_component_register( ${MICROPY_BOARD_DIR} ${CMAKE_BINARY_DIR} LDFRAGMENTS - linker.lf + ${MICROPY_LDFRAGMENTS} REQUIRES ${IDF_COMPONENTS} ) From 61cb293b761d22de9ece8d7cfdb38555fc0a410a Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 17 Jan 2025 21:12:19 +0100 Subject: [PATCH 333/546] esp32/machine_pin: Implement Pin.toggle() method. The actual output pin value is taken from the OUT register, not from the pad. Tested with: - ESP32 low and high Pin numbers - ESP32C3 low Pin numbers - ESP32C6 low Pin numbers - ESP32S2 low and high Pin numbers - ESP32S3 low and high Pin numbers Signed-off-by: robert-hh --- ports/esp32/machine_pin.c | 10 ++++++++++ ports/esp32/mphalport.h | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index 74202d6f758cd..b821abf1fc18a 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -287,6 +287,15 @@ static mp_obj_t machine_pin_on(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_on_obj, machine_pin_on); +// pin.toggle() +static mp_obj_t machine_pin_toggle(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + gpio_num_t index = PIN_OBJ_PTR_INDEX(self); + gpio_set_level(index, 1 - mp_hal_pin_read_output(index)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle); + // pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING) static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_handler, ARG_trigger, ARG_wake }; @@ -366,6 +375,7 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) }, { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_off_obj) }, { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_on_obj) }, + { MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) }, { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) }, // class attributes diff --git a/ports/esp32/mphalport.h b/ports/esp32/mphalport.h index a908d784d7f61..a919a859a4279 100644 --- a/ports/esp32/mphalport.h +++ b/ports/esp32/mphalport.h @@ -36,6 +36,7 @@ #include "freertos/task.h" #include "driver/spi_master.h" +#include "soc/gpio_reg.h" #define MICROPY_PLATFORM_VERSION "IDF" IDF_VER @@ -133,6 +134,15 @@ static inline void mp_hal_pin_od_high(mp_hal_pin_obj_t pin) { static inline int mp_hal_pin_read(mp_hal_pin_obj_t pin) { return gpio_get_level(pin); } +static inline int mp_hal_pin_read_output(mp_hal_pin_obj_t pin) { + #if defined(GPIO_OUT1_REG) + return pin < 32 + ? (*(uint32_t *)GPIO_OUT_REG >> pin) & 1 + : (*(uint32_t *)GPIO_OUT1_REG >> (pin - 32)) & 1; + #else + return (*(uint32_t *)GPIO_OUT_REG >> pin) & 1; + #endif +} static inline void mp_hal_pin_write(mp_hal_pin_obj_t pin, int v) { gpio_set_level(pin, v); } From e009ab06c581cf2e8b6dbf8d2002105c97131a1f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 19 Jan 2025 10:31:56 +0100 Subject: [PATCH 334/546] esp8266/machine_pin: Implement Pin.toggle() method. Tested with a generic ESP8266 device. The actual output value is taken from the output register, not by reading the pad level. Signed-off-by: robert-hh --- ports/esp8266/esp_mphal.h | 7 +++++++ ports/esp8266/machine_pin.c | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/ports/esp8266/esp_mphal.h b/ports/esp8266/esp_mphal.h index e677fa450d339..0a4f92ac0bf29 100644 --- a/ports/esp8266/esp_mphal.h +++ b/ports/esp8266/esp_mphal.h @@ -100,6 +100,13 @@ void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin); else { gpio_output_set(1 << (p), 0, 1 << (p), 0); } \ } while (0) #define mp_hal_pin_read(p) pin_get(p) +static inline int mp_hal_pin_read_output(mp_hal_pin_obj_t pin) { + if (pin >= 16) { + return READ_PERI_REG(RTC_GPIO_OUT) & 1; + } else { + return (GPIO_REG_READ(GPIO_OUT_ADDRESS) >> pin) & 1; + } +} #define mp_hal_pin_write(p, v) pin_set((p), (v)) void *ets_get_esf_buf_ctlblk(void); diff --git a/ports/esp8266/machine_pin.c b/ports/esp8266/machine_pin.c index ec22c7c588c4e..658966679d1ae 100644 --- a/ports/esp8266/machine_pin.c +++ b/ports/esp8266/machine_pin.c @@ -373,6 +373,14 @@ static mp_obj_t pyb_pin_on(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(pyb_pin_on_obj, pyb_pin_on); +// pin.toggle() +static mp_obj_t machine_pin_toggle(mp_obj_t self_in) { + pyb_pin_obj_t *self = self_in; + pin_set(self->phys_port, 1 - mp_hal_pin_read_output(self->phys_port)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle); + // pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING, hard=False) static mp_obj_t pyb_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_handler, ARG_trigger, ARG_hard }; @@ -435,6 +443,7 @@ static const mp_rom_map_elem_t pyb_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&pyb_pin_value_obj) }, { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&pyb_pin_off_obj) }, { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&pyb_pin_on_obj) }, + { MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) }, { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&pyb_pin_irq_obj) }, // class constants From 22310ae27aea56377b31bdcc2d7c2ba712316c4f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 19 Jan 2025 11:06:26 +0100 Subject: [PATCH 335/546] cc3200/mods/pybpin: Implement Pin.toggle() method. Tested with a WiPy 1 board. Signed-off-by: robert-hh --- ports/cc3200/mods/pybpin.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ports/cc3200/mods/pybpin.c b/ports/cc3200/mods/pybpin.c index 037c78a32cbf2..ea40aa9df8d20 100644 --- a/ports/cc3200/mods/pybpin.c +++ b/ports/cc3200/mods/pybpin.c @@ -680,6 +680,20 @@ static mp_obj_t pin_value(size_t n_args, const mp_obj_t *args) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_value_obj, 1, 2, pin_value); +// pin.toggle() +static mp_obj_t pin_toggle(mp_obj_t self_in) { + pin_obj_t *self = self_in; + if (self->value) { + self->value = 0; + MAP_GPIOPinWrite(self->port, self->bit, 0); + } else { + self->value = 1; + MAP_GPIOPinWrite(self->port, self->bit, self->bit); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(pin_toggle_obj, pin_toggle); + static mp_obj_t pin_id(mp_obj_t self_in) { pin_obj_t *self = self_in; return MP_OBJ_NEW_QSTR(self->name); @@ -902,6 +916,7 @@ static const mp_rom_map_elem_t pin_locals_dict_table[] = { // instance methods { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pin_init_obj) }, { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&pin_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&pin_toggle_obj) }, { MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&pin_id_obj) }, { MP_ROM_QSTR(MP_QSTR_mode), MP_ROM_PTR(&pin_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_pull), MP_ROM_PTR(&pin_pull_obj) }, From 48925fd7932c9d27f7248bc3308a77c172702878 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 22 Jan 2025 18:03:52 +0100 Subject: [PATCH 336/546] docs/library/machine.Pin: Show availability of low, high and toggle. Signed-off-by: robert-hh --- docs/library/machine.Pin.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/library/machine.Pin.rst b/docs/library/machine.Pin.rst index 68070133b8fb6..4aac0f4b94be2 100644 --- a/docs/library/machine.Pin.rst +++ b/docs/library/machine.Pin.rst @@ -209,13 +209,13 @@ The following methods are not part of the core Pin API and only implemented on c Set pin to "0" output level. - Availability: nrf, rp2, stm32 ports. + Availability: mimxrt, nrf, renesas-ra, rp2, samd, stm32 ports. .. method:: Pin.high() Set pin to "1" output level. - Availability: nrf, rp2, stm32 ports. + Availability: mimxrt, nrf, renesas-ra, rp2, samd, stm32 ports. .. method:: Pin.mode([mode]) @@ -242,7 +242,7 @@ The following methods are not part of the core Pin API and only implemented on c Toggle output pin from "0" to "1" or vice-versa. - Availability: mimxrt, samd, rp2 ports. + Availability: cc3200, esp32, esp8266, mimxrt, rp2, samd ports. Constants --------- From 69ffd2aaf09e35b68ea045872a63af6403fc1c8f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 8 Dec 2024 21:37:18 +0100 Subject: [PATCH 337/546] renesas-ra/modrenesas: Expose the Flash block device to Python code. A new module called renesas is added, like in other ports. The accessible block device allows to use Python methods for creating and modifying the file system. The Flash block device for the file system can be accessed with: from renesas import Flash bdev = Flash(start=0) Signed-off-by: robert-hh --- ports/renesas-ra/Makefile | 1 + ports/renesas-ra/modrenesas.c | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 ports/renesas-ra/modrenesas.c diff --git a/ports/renesas-ra/Makefile b/ports/renesas-ra/Makefile index 03eb9b52b41b8..ec47510d981ad 100644 --- a/ports/renesas-ra/Makefile +++ b/ports/renesas-ra/Makefile @@ -250,6 +250,7 @@ SRC_C += \ machine_pin.c \ machine_rtc.c \ machine_sdcard.c \ + modrenesas.c \ extint.c \ usrsw.c \ flash.c \ diff --git a/ports/renesas-ra/modrenesas.c b/ports/renesas-ra/modrenesas.c new file mode 100644 index 0000000000000..2524bbe6d7d0c --- /dev/null +++ b/ports/renesas-ra/modrenesas.c @@ -0,0 +1,41 @@ +/* + * 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 "py/runtime.h" +#include "storage.h" + +static const mp_rom_map_elem_t renesas_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_renesas) }, + { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&pyb_flash_type) }, +}; +static MP_DEFINE_CONST_DICT(renesas_module_globals, renesas_module_globals_table); + +const mp_obj_module_t mp_module_renesas = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&renesas_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_renesas, mp_module_renesas); From eb45d97898abd9aae93d0c953634cabb5ea327e3 Mon Sep 17 00:00:00 2001 From: Glenn Moloney Date: Tue, 25 Feb 2025 14:36:29 +1100 Subject: [PATCH 338/546] py/objstr: Support tuples and start/end args in startswith and endswith. This change allows tuples to be passed as the prefix/suffix argument to the `str.startswith()` and `str.endswith()` methods. The methods will return `True` if the string starts/ends with any of the prefixes/suffixes in the tuple. Also adds full support for the `start` and `end` arguments to both methods for compatibility with CPython. Tests have been updated for the new behaviour. Signed-off-by: Glenn Moloney --- py/objstr.c | 67 ++++++++++++++--------- tests/basics/string_endswith.py | 27 +++++++-- tests/basics/string_endswith_upy.py | 6 -- tests/basics/string_endswith_upy.py.exp | 1 - tests/basics/string_startswith.py | 19 +++++++ tests/basics/string_startswith_upy.py | 6 -- tests/basics/string_startswith_upy.py.exp | 1 - tests/misc/non_compliant.py | 6 -- tests/misc/non_compliant.py.exp | 1 - 9 files changed, 83 insertions(+), 51 deletions(-) delete mode 100644 tests/basics/string_endswith_upy.py delete mode 100644 tests/basics/string_endswith_upy.py.exp delete mode 100644 tests/basics/string_startswith_upy.py delete mode 100644 tests/basics/string_startswith_upy.py.exp diff --git a/py/objstr.c b/py/objstr.c index fc0623eb7af2c..307e956a15236 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -67,6 +67,26 @@ static void check_is_str_or_bytes(mp_obj_t self_in) { mp_check_self(mp_obj_is_str_or_bytes(self_in)); } +static const byte *get_substring_data(const mp_obj_t obj, size_t n_args, const mp_obj_t *args, size_t *len) { + // Get substring data from obj, using args[0,1] to specify start and end indices. + GET_STR_DATA_LEN(obj, str, str_len); + if (n_args > 0) { + const mp_obj_type_t *self_type = mp_obj_get_type(obj); + const byte *end = str + str_len; + if (n_args > 1 && args[1] != mp_const_none) { + end = str_index_to_ptr(self_type, str, str_len, args[1], true); + } + if (args[0] != mp_const_none) { + str = str_index_to_ptr(self_type, str, str_len, args[0], true); + } + str_len = MAX(end - str, 0); + } + if (len) { + *len = str_len; + } + return str; +} + /******************************************************************************/ /* str */ @@ -802,37 +822,34 @@ static mp_obj_t str_rindex(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_rindex_obj, 2, 4, str_rindex); -// TODO: (Much) more variety in args -static mp_obj_t str_startswith(size_t n_args, const mp_obj_t *args) { - const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); - GET_STR_DATA_LEN(args[0], str, str_len); - size_t prefix_len; - const char *prefix = mp_obj_str_get_data(args[1], &prefix_len); - const byte *start = str; - if (n_args > 2) { - start = str_index_to_ptr(self_type, str, str_len, args[2], true); +static mp_obj_t str_startendswith(size_t n_args, const mp_obj_t *args, bool ends_with) { + size_t str_len; + const byte *str = get_substring_data(args[0], n_args - 2, args + 2, &str_len); + mp_obj_t *prefixes = (mp_obj_t *)&args[1]; + size_t n_prefixes = 1; + if (mp_obj_is_type(args[1], &mp_type_tuple)) { + mp_obj_tuple_get(args[1], &n_prefixes, &prefixes); } - if (prefix_len + (start - str) > str_len) { - return mp_const_false; + size_t prefix_len; + for (size_t i = 0; i < n_prefixes; i++) { + const char *prefix = mp_obj_str_get_data(prefixes[i], &prefix_len); + const byte *s = str + (ends_with ? str_len - prefix_len : 0); + if (prefix_len <= str_len && memcmp(s, prefix, prefix_len) == 0) { + return mp_const_true; + } } - return mp_obj_new_bool(memcmp(start, prefix, prefix_len) == 0); + return mp_const_false; } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_startswith_obj, 2, 3, str_startswith); -static mp_obj_t str_endswith(size_t n_args, const mp_obj_t *args) { - GET_STR_DATA_LEN(args[0], str, str_len); - size_t suffix_len; - const char *suffix = mp_obj_str_get_data(args[1], &suffix_len); - if (n_args > 2) { - mp_raise_NotImplementedError(MP_ERROR_TEXT("start/end indices")); - } +static mp_obj_t str_startswith(size_t n_args, const mp_obj_t *args) { + return str_startendswith(n_args, args, false); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_startswith_obj, 2, 4, str_startswith); - if (suffix_len > str_len) { - return mp_const_false; - } - return mp_obj_new_bool(memcmp(str + (str_len - suffix_len), suffix, suffix_len) == 0); +static mp_obj_t str_endswith(size_t n_args, const mp_obj_t *args) { + return str_startendswith(n_args, args, true); } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_endswith_obj, 2, 3, str_endswith); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_endswith_obj, 2, 4, str_endswith); enum { LSTRIP, RSTRIP, STRIP }; diff --git a/tests/basics/string_endswith.py b/tests/basics/string_endswith.py index 683562d10c32e..2b0a063988b45 100644 --- a/tests/basics/string_endswith.py +++ b/tests/basics/string_endswith.py @@ -5,11 +5,28 @@ print("foobar".endswith("")) print("foobar".endswith("foobarbaz")) -#print("1foobar".startswith("foo", 1)) -#print("1foo".startswith("foo", 1)) -#print("1foo".startswith("1foo", 1)) -#print("1fo".startswith("foo", 1)) -#print("1fo".startswith("foo", 10)) +print("foobar".endswith("bar", 3)) +print("foobar".endswith("bar", 4)) +print("foobar".endswith("foo", 0, 3)) +print("foobar".endswith("foo", 0, 4)) +print("foobar".endswith("foo", 1, 3)) +print("foobar".endswith("foo", 1, 3)) +print("foobar".endswith("oo", 1, 3)) +print("foobar".endswith("o", 2, 3)) +print("foobar".endswith("o", 3, 3)) +print("foobar".endswith("o", 4, 3)) + +print("foobar".endswith("bar", None, None)) +print("foobar".endswith("bar", None, 3)) +print("foobar".endswith("bar", 3, None)) +print("foobar".endswith("bar", 2, None)) +print("foobar".endswith("foo", None, 3)) + +print("foobar".endswith(("bar", "foo"))) +print("foobar".endswith(("foo", "bar"))) +print("foobar".endswith(("foo", "bar1"))) +print("foobar".endswith(("bar", ))) +print("foobar".endswith(("foo", ))) try: "foobar".endswith(1) diff --git a/tests/basics/string_endswith_upy.py b/tests/basics/string_endswith_upy.py deleted file mode 100644 index 06a4e71d2c927..0000000000000 --- a/tests/basics/string_endswith_upy.py +++ /dev/null @@ -1,6 +0,0 @@ -# MicroPython doesn't support tuple argument - -try: - "foobar".endswith(("bar", "sth")) -except TypeError: - print("TypeError") diff --git a/tests/basics/string_endswith_upy.py.exp b/tests/basics/string_endswith_upy.py.exp deleted file mode 100644 index 6002b71c56ea0..0000000000000 --- a/tests/basics/string_endswith_upy.py.exp +++ /dev/null @@ -1 +0,0 @@ -TypeError diff --git a/tests/basics/string_startswith.py b/tests/basics/string_startswith.py index e63ae3c1866df..eefdea82198f1 100644 --- a/tests/basics/string_startswith.py +++ b/tests/basics/string_startswith.py @@ -10,6 +10,25 @@ print("1fo".startswith("foo", 1)) print("1fo".startswith("foo", 10)) +print("1foobar".startswith("foo", 1, 5)) +print("1foobar".startswith("foo", 1, 4)) +print("1foobar".startswith("foo", 1, 3)) +print("1foobar".startswith("oo", 2, 4)) +print("1foobar".startswith("o", 3, 4)) +print("1foobar".startswith("o", 4, 4)) +print("1foobar".startswith("o", 5, 4)) + +print("foobar".startswith("foo", None, None)) +print("foobar".startswith("foo", None, 3)) +print("foobar".startswith("foo", None, 2)) +print("foobar".startswith("bar", 3, None)) + + +print("foobar".startswith(("foo", "sth"))) +print("foobar".startswith(("sth", "foo"))) +print("foobar".startswith(("sth", "foo2"))) +print("foobar".startswith(("foo", ))) + try: "foobar".startswith(1) except TypeError: diff --git a/tests/basics/string_startswith_upy.py b/tests/basics/string_startswith_upy.py deleted file mode 100644 index 9ea1796c218fd..0000000000000 --- a/tests/basics/string_startswith_upy.py +++ /dev/null @@ -1,6 +0,0 @@ -# MicroPython doesn't support tuple argument - -try: - "foobar".startswith(("foo", "sth")) -except TypeError: - print("TypeError") diff --git a/tests/basics/string_startswith_upy.py.exp b/tests/basics/string_startswith_upy.py.exp deleted file mode 100644 index 6002b71c56ea0..0000000000000 --- a/tests/basics/string_startswith_upy.py.exp +++ /dev/null @@ -1 +0,0 @@ -TypeError diff --git a/tests/misc/non_compliant.py b/tests/misc/non_compliant.py index da90f90ac3ed7..dd62f63255c72 100644 --- a/tests/misc/non_compliant.py +++ b/tests/misc/non_compliant.py @@ -63,12 +63,6 @@ except NotImplementedError: print("NotImplementedError") -# str.endswith(s, start) not implemented -try: - "abc".endswith("c", 1) -except NotImplementedError: - print("NotImplementedError") - # str subscr with step!=1 not implemented try: print("abc"[1:2:3]) diff --git a/tests/misc/non_compliant.py.exp b/tests/misc/non_compliant.py.exp index 3f15a14406092..deefe78a712d0 100644 --- a/tests/misc/non_compliant.py.exp +++ b/tests/misc/non_compliant.py.exp @@ -13,7 +13,6 @@ NotImplementedError NotImplementedError NotImplementedError NotImplementedError -NotImplementedError b'\x01\x02' b'\x01\x00' NotImplementedError From 213f1c1ee07016c1a4d21cc3b1ea4ea85ba33c98 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 7 Feb 2025 21:40:09 +0100 Subject: [PATCH 339/546] samd/machine_i2c: Support default instance and SCL/SDA pin values. If a board configures a default I2C instance and/or SCL/SDA pins, then these no longer need to be given in the constructor. This allows the user to easily construct the default I2C instance via `machine.I2C()` and that will work on the default pins as designated on the board silkscreen. Signed-off-by: robert-hh --- ports/samd/machine_i2c.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/ports/samd/machine_i2c.c b/ports/samd/machine_i2c.c index a7b728ddaf362..03af3d29ed614 100644 --- a/ports/samd/machine_i2c.c +++ b/ports/samd/machine_i2c.c @@ -34,6 +34,7 @@ #include "extmod/modmachine.h" #include "samd_soc.h" #include "pin_af.h" +#include "genhdr/pins.h" #include "clock_config.h" #define DEFAULT_I2C_FREQ (400000) @@ -119,17 +120,26 @@ void common_i2c_irq_handler(int i2c_id) { static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "I2C(%u, freq=%u, scl=%u, sda=%u)", - self->id, self->freq, self->scl, self->sda); + mp_printf(print, "I2C(%u, freq=%u, scl=\"%q\", sda=\"%q\")", + self->id, self->freq, pin_find_by_id(self->scl)->name, pin_find_by_id(self->sda)->name); } mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_id, ARG_freq, ARG_scl, ARG_sda }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + #if MICROPY_HW_DEFAULT_I2C_ID < 0 + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + #else + { MP_QSTR_id, MP_ARG_INT, {.u_int = MICROPY_HW_DEFAULT_I2C_ID} }, + #endif { MP_QSTR_freq, MP_ARG_INT, {.u_int = DEFAULT_I2C_FREQ} }, + #if defined(pin_SCL) && defined(pin_SDA) + { MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = pin_SCL} }, + { MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = pin_SDA} }, + #else { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + #endif }; // Parse args. @@ -137,7 +147,7 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // Get I2C bus. - int id = mp_obj_get_int(args[ARG_id].u_obj); + int id = args[ARG_id].u_int; if (id < 0 || id >= SERCOM_INST_NUM) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), id); } @@ -148,15 +158,13 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n self->instance = sercom_instance[self->id]; // Set SCL/SDA pins. - sercom_pad_config_t scl_pad_config; self->scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj); - scl_pad_config = get_sercom_config(self->scl, self->id); - - sercom_pad_config_t sda_pad_config; self->sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj); - sda_pad_config = get_sercom_config(self->sda, self->id); + + sercom_pad_config_t scl_pad_config = get_sercom_config(self->scl, self->id); + sercom_pad_config_t sda_pad_config = get_sercom_config(self->sda, self->id); if (sda_pad_config.pad_nr != 0 || scl_pad_config.pad_nr != 1) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid pin for sda or scl")); + mp_raise_ValueError(MP_ERROR_TEXT("invalid sda/scl pin")); } MP_STATE_PORT(sercom_table[self->id]) = self; self->freq = args[ARG_freq].u_int; From 62ed69b016d98167db01c5283758bfde95c6aec9 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 7 Feb 2025 21:43:36 +0100 Subject: [PATCH 340/546] samd/machine_spi: Support default instance and SCK/MOSI/MISO pin values. If a board configures a default SPI instance and/or SCK/MOSI/MISO pins, then the user can create a default SPI object using `machine.SPI()`. Also, if MISO is not going to be used, then MISO can be set to `None` with `miso=None`. Signed-off-by: robert-hh --- ports/samd/machine_spi.c | 43 ++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/ports/samd/machine_spi.c b/ports/samd/machine_spi.c index 9c31cc2778dba..744de2000b00a 100644 --- a/ports/samd/machine_spi.c +++ b/ports/samd/machine_spi.c @@ -33,6 +33,7 @@ #include "extmod/modmachine.h" #include "samd_soc.h" #include "pin_af.h" +#include "genhdr/pins.h" #include "clock_config.h" #define DEFAULT_SPI_BAUDRATE (1000000) @@ -82,8 +83,15 @@ void common_spi_irq_handler(int spi_id) { static void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "SPI(%u, baudrate=%u, firstbit=%u, polarity=%u, phase=%u, bits=8)", - self->id, self->baudrate, self->firstbit, self->polarity, self->phase); + mp_printf(print, "SPI(%u, baudrate=%u, firstbit=%u, polarity=%u, phase=%u, bits=8," + " sck=\"%q\", mosi=\"%q\", miso=", + self->id, self->baudrate, self->firstbit, self->polarity, self->phase, + pin_find_by_id(self->sck)->name, pin_find_by_id(self->mosi)->name); + if (self->miso == 0xff) { + mp_printf(print, "None)"); + } else { + mp_printf(print, "\"%q\")", pin_find_by_id(self->miso)->name); + } } static void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -96,7 +104,7 @@ static void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj { MP_QSTR_firstbit, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, - { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, }; machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -132,14 +140,16 @@ static void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj if (args[ARG_mosi].u_obj != mp_const_none) { self->mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj); } - if (args[ARG_miso].u_obj != mp_const_none) { - self->miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj); + if (args[ARG_miso].u_obj != MP_ROM_INT(-1)) { + self->miso = args[ARG_miso].u_obj == mp_const_none ? 0xff : mp_hal_get_pin_obj(args[ARG_miso].u_obj); } - // Initialise the SPI peripheral if any arguments given, or it was not initialised previously. if (n_args > 0 || kw_args->used > 0 || self->new) { self->new = false; + if (self->sck == 0xff || self->mosi == 0xff) { + mp_raise_ValueError(MP_ERROR_TEXT("missing sck/mosi")); + } // Get the pad and alt-fct numbers. self->sck_pad_config = get_sercom_config(self->sck, self->id); self->mosi_pad_config = get_sercom_config(self->mosi, self->id); @@ -155,7 +165,7 @@ static void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj } else if (self->mosi_pad_config.pad_nr == 0 && self->sck_pad_config.pad_nr == 3) { dopo = 3; } else { - mp_raise_ValueError(MP_ERROR_TEXT("invalid pin for sck or mosi")); + mp_raise_ValueError(MP_ERROR_TEXT("invalid sck/mosi pin")); } #elif defined(MCU_SAMD51) if (self->mosi_pad_config.pad_nr == 0 && self->sck_pad_config.pad_nr == 1) { @@ -232,10 +242,16 @@ static void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj } static mp_obj_t machine_spi_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); + mp_arg_check_num(n_args, n_kw, MICROPY_HW_DEFAULT_SPI_ID < 0 ? 1 : 0, MP_OBJ_FUN_ARGS_MAX, true); // Get SPI bus. - int spi_id = mp_obj_get_int(args[0]); + int spi_id = MICROPY_HW_DEFAULT_SPI_ID; + + if (n_args > 0) { + spi_id = mp_obj_get_int(args[0]); + n_args--; + args++; + } if (spi_id < 0 || spi_id > SERCOM_INST_NUM) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); } @@ -247,16 +263,23 @@ static mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, s self->polarity = DEFAULT_SPI_POLARITY; self->phase = DEFAULT_SPI_PHASE; self->firstbit = DEFAULT_SPI_FIRSTBIT; + #if defined(pin_SCK) && defined(pin_MOSI) && defined(pin_MISO) + // Initialize with the default pins + self->sck = mp_hal_get_pin_obj((mp_obj_t)pin_SCK); + self->mosi = mp_hal_get_pin_obj((mp_obj_t)pin_MOSI); + self->miso = mp_hal_get_pin_obj((mp_obj_t)pin_MISO); + #else self->mosi = 0xff; // 0xff: pin not defined (yet) self->miso = 0xff; self->sck = 0xff; + #endif self->new = true; MP_STATE_PORT(sercom_table[spi_id]) = self; mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); - machine_spi_init((mp_obj_base_t *)self, n_args - 1, args + 1, &kw_args); + machine_spi_init((mp_obj_base_t *)self, n_args, args, &kw_args); return self; } From daef1c1d146207e6a42219a7d6717df337a11377 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 7 Feb 2025 22:08:17 +0100 Subject: [PATCH 341/546] samd/machine_uart: Support default instance and TX/RX pin values. If a board configures a default UART instance and/or TX/RX pins then the user can create a default UART object using `machine.UART()`. Signed-off-by: robert-hh --- ports/samd/machine_uart.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c index 6e9a5538647bc..45bfc3ddd5002 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 "genhdr/pins.h" #include "shared/runtime/softtimer.h" #define DEFAULT_UART_BAUDRATE (115200) @@ -298,7 +299,7 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ } mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, " - "timeout=%u, timeout_char=%u, rxbuf=%d" + "tx=\"%q\", rx=\"%q\", timeout=%u, timeout_char=%u, rxbuf=%d" #if MICROPY_HW_UART_TXBUF ", txbuf=%d" #endif @@ -310,7 +311,8 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ #endif ")", self->id, self->baudrate, self->bits, _parity_name[self->parity], - self->stop + 1, self->timeout, self->timeout_char, rxbuf_len + self->stop + 1, pin_find_by_id(self->tx)->name, pin_find_by_id(self->rx)->name, + self->timeout, self->timeout_char, rxbuf_len #if MICROPY_HW_UART_TXBUF , txbuf_len #endif @@ -448,7 +450,7 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, // Check the rx/tx pin assignments if (self->tx == 0xff || self->rx == 0xff || (self->tx / 4) != (self->rx / 4)) { - mp_raise_ValueError(MP_ERROR_TEXT("Non-matching or missing rx/tx")); + mp_raise_ValueError(MP_ERROR_TEXT("invalid or missing rx/tx")); } self->rx_pad_config = get_sercom_config(self->rx, self->id); self->tx_pad_config = get_sercom_config(self->tx, self->id); @@ -479,10 +481,16 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, } static mp_obj_t mp_machine_uart_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); + mp_arg_check_num(n_args, n_kw, MICROPY_HW_DEFAULT_UART_ID < 0 ? 1 : 0, MP_OBJ_FUN_ARGS_MAX, true); // Get UART bus. - int uart_id = mp_obj_get_int(args[0]); + int uart_id = MICROPY_HW_DEFAULT_UART_ID; + + if (n_args > 0) { + uart_id = mp_obj_get_int(args[0]); + n_args--; + args++; + } if (uart_id < 0 || uart_id > SERCOM_INST_NUM) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) doesn't exist"), uart_id); } @@ -495,8 +503,15 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->stop = 0; self->timeout = 1; self->timeout_char = 1; + #if defined(pin_TX) && defined(pin_RX) + // Initialize with the default pins + self->tx = mp_hal_get_pin_obj((mp_obj_t)pin_TX); + self->rx = mp_hal_get_pin_obj((mp_obj_t)pin_RX); + #else + // Have to be defined self->tx = 0xff; self->rx = 0xff; + #endif #if MICROPY_HW_UART_RTSCTS self->rts = 0xff; self->cts = 0xff; @@ -510,7 +525,7 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); - mp_machine_uart_init_helper(self, n_args - 1, args + 1, &kw_args); + mp_machine_uart_init_helper(self, n_args, args, &kw_args); return MP_OBJ_FROM_PTR(self); } From 4cbaab17669b54ddfe8c509eb0f7653706120211 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 8 Feb 2025 09:23:11 +0100 Subject: [PATCH 342/546] samd/boards: Add missing TX/RX, SCL/SDA and SCK/MOSI/MISO pin names. These were missing and are needed to support UART/I2C/SPI default pins. Signed-off-by: robert-hh --- ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/pins.csv | 4 ++-- ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/pins.csv | 2 ++ ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/pins.csv | 2 ++ ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/pins.csv | 3 +++ ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/pins.csv | 3 +++ ports/samd/boards/ADAFRUIT_TRINKET_M0/pins.csv | 8 +++++++- ports/samd/boards/MINISAM_M4/pins.csv | 6 ++++-- ports/samd/boards/SEEED_WIO_TERMINAL/pins.csv | 2 ++ ports/samd/boards/SEEED_XIAO_SAMD21/pins.csv | 9 ++++++++- ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/pins.csv | 3 ++- 10 files changed, 35 insertions(+), 7 deletions(-) diff --git a/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/pins.csv b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/pins.csv index 8993419a268c5..f2676693ec41d 100644 --- a/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/pins.csv +++ b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/pins.csv @@ -21,8 +21,8 @@ A2,PB09 A3,PA04 A4,PA05 A5,PB02 -TX,PB22 -RX,PB23 +RX,PA11 +TX,PA10 SCL,PA23 SDA,PA22 MOSI,PB10 diff --git a/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/pins.csv b/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/pins.csv index ec7a8bc1638da..056339de1335f 100644 --- a/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/pins.csv +++ b/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/pins.csv @@ -15,6 +15,8 @@ A2,PB08 A3,PB09 A4,PA04 A5,PB06 +RX,PB17 +TX,PB16 SCL,PA13 SDA,PA12 MOSI,PB23 diff --git a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/pins.csv b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/pins.csv index 845cefb8a972b..250e42d9d04bc 100644 --- a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/pins.csv +++ b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/pins.csv @@ -16,6 +16,8 @@ A2,PB09 A3,PA04 A4,PA05 A5,PB02 +RX,PA11 +TX,PA10 SDA,PA22 SCL,PA23 MOSI,PB10 diff --git a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/pins.csv b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/pins.csv index c82beccfa2be3..8597815a124d9 100644 --- a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/pins.csv +++ b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/pins.csv @@ -16,6 +16,9 @@ A2,PB08 A3,PB09 A4,PA04 A5,PA06 + +RX,PA16 +TX,PA17 SDA,PA12 SCL,PA13 MOSI,PA00 diff --git a/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/pins.csv b/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/pins.csv index cee4bf02b7a39..aef7391d12d3b 100644 --- a/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/pins.csv +++ b/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/pins.csv @@ -21,6 +21,9 @@ D11,PA19 D12,PA17 D13,PA16 +RX,PA23 +TX,PA22 + SDA,PB02 SCL,PB03 diff --git a/ports/samd/boards/ADAFRUIT_TRINKET_M0/pins.csv b/ports/samd/boards/ADAFRUIT_TRINKET_M0/pins.csv index 47306f8de1ce0..fb1e645cf4ddc 100644 --- a/ports/samd/boards/ADAFRUIT_TRINKET_M0/pins.csv +++ b/ports/samd/boards/ADAFRUIT_TRINKET_M0/pins.csv @@ -1,3 +1,10 @@ +RX,PA07 +TX,PA06 +SDA,PA08 +SCL,PA09 +MOSI,PA06 +MISO,PA09 +SCK,PA07 D0,PA08 D1,PA02 D2,PA09 @@ -5,7 +12,6 @@ D3,PA07 D4,PA06 DOTSTAR_DATA,PA00 DOTSTAR_CLK,PA01 - LED,PA10 USB_DM,PA24 diff --git a/ports/samd/boards/MINISAM_M4/pins.csv b/ports/samd/boards/MINISAM_M4/pins.csv index 3c1ab4c6038d3..d6a3ef803200b 100644 --- a/ports/samd/boards/MINISAM_M4/pins.csv +++ b/ports/samd/boards/MINISAM_M4/pins.csv @@ -7,6 +7,8 @@ A5,PA06 A6,PA07 RX,PA16 TX,PA17 +D0,PA16 +D1,PA17 D3,PA19 D4,PA20 D5,PA21 @@ -15,8 +17,8 @@ BUTTON,PA00 AREF,PA03 SDA,PA12 SCL,PA13 -MOSI,PB22 -MISO,PB23 +MOSI,PB23 +MISO,PB22 SCK,PA01 DOTSTAR_DATA,PB03 DOTSTAR_CLK,PB02 diff --git a/ports/samd/boards/SEEED_WIO_TERMINAL/pins.csv b/ports/samd/boards/SEEED_WIO_TERMINAL/pins.csv index f263253cbaae4..c335ba2baff18 100644 --- a/ports/samd/boards/SEEED_WIO_TERMINAL/pins.csv +++ b/ports/samd/boards/SEEED_WIO_TERMINAL/pins.csv @@ -19,6 +19,8 @@ ENABLE_5V,PC14 ENABLE_3V3,PC15 TX,PB26 RX,PB27 +SDA,PA13 +SCL,PA12 SDA0,PA13 SCL0,PA12 SDA1,PA17 diff --git a/ports/samd/boards/SEEED_XIAO_SAMD21/pins.csv b/ports/samd/boards/SEEED_XIAO_SAMD21/pins.csv index 969c60028b41b..6f70f3308e703 100644 --- a/ports/samd/boards/SEEED_XIAO_SAMD21/pins.csv +++ b/ports/samd/boards/SEEED_XIAO_SAMD21/pins.csv @@ -1,3 +1,11 @@ +RX,PB09 +TX,PB08 +SCL,PA09 +SDA,PA08 +MOSI,PA06 +MISO,PA05 +SCK,PA07 + A0_D0,PA02 A1_D1,PA04 A2_D2,PA10 @@ -9,7 +17,6 @@ A7_D7,PB09 A8_D8,PA07 A9_D9,PA05 A10_D10,PA06 - USER_LED,PA17 RX_LED,PA18 TX_LED,PA19 diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/pins.csv b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/pins.csv index 0ae269724da85..fe47fef27f29c 100644 --- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/pins.csv +++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/pins.csv @@ -15,6 +15,8 @@ A2,PB09 A3,PA04 A4,PA05 A5,PB02 +RX,PA13 +TX,PA12 SDA,PA22 SCL,PA23 MOSI,PB12 @@ -30,7 +32,6 @@ TXLED,PA27 USB_DM,PA24 USB_DP,PA25 -USB_SOF,PA23 SWCLK,PA30 SWDIO,PA31 From b9b4f1b40b9f97a5b8893afe9cb139a09fb76fd9 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 11 Feb 2025 10:07:07 +0100 Subject: [PATCH 343/546] samd/boards: Provide default IDs for UART, I2C and SPI. In combination with the defautl Pins the default device can now be instantiated e.g. as: uart = UART(). Similar for I2C and SPI. Signed-off-by: robert-hh --- .../boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.h | 4 ++++ .../boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.h | 4 ++++ .../boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.h | 4 ++++ .../boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.h | 4 ++++ .../boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.h | 4 ++++ ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.h | 4 ++++ ports/samd/boards/ADAFRUIT_TRINKET_M0/mpconfigboard.h | 4 ++++ ports/samd/boards/MINISAM_M4/mpconfigboard.h | 4 ++++ ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.h | 4 ++++ ports/samd/boards/SEEED_XIAO_SAMD21/mpconfigboard.h | 4 ++++ .../boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h | 4 ++++ ports/samd/mpconfigport.h | 9 +++++++++ 12 files changed, 53 insertions(+) diff --git a/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.h b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.h index 880df8d200390..a2df633765443 100644 --- a/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.h +++ b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.h @@ -3,5 +3,9 @@ #define MICROPY_HW_XOSC32K (1) +#define MICROPY_HW_DEFAULT_UART_ID (2) +#define MICROPY_HW_DEFAULT_I2C_ID (3) +#define MICROPY_HW_DEFAULT_SPI_ID (4) + #define MICROPY_HW_SPIFLASH (1) #define MICROPY_HW_SPIFLASH_ID (2) diff --git a/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.h b/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.h index a9f7d518e2364..f68a26303197c 100644 --- a/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.h +++ b/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.h @@ -4,4 +4,8 @@ #define MICROPY_HW_XOSC32K (1) #define MICROPY_HW_MCU_OSC32KULP (1) +#define MICROPY_HW_DEFAULT_UART_ID (5) +#define MICROPY_HW_DEFAULT_I2C_ID (2) +#define MICROPY_HW_DEFAULT_SPI_ID (1) + #define MICROPY_HW_QSPIFLASH GD25Q16C diff --git a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.h b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.h index 16018fdc56356..84b75414f5e32 100644 --- a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.h +++ b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.h @@ -3,5 +3,9 @@ #define MICROPY_HW_DFLL_USB_SYNC (1) +#define MICROPY_HW_DEFAULT_SPI_ID (4) +#define MICROPY_HW_DEFAULT_I2C_ID (3) +#define MICROPY_HW_DEFAULT_UART_ID (0) + #define MICROPY_HW_SPIFLASH (1) #define MICROPY_HW_SPIFLASH_ID (5) diff --git a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.h b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.h index 2f246c60b188c..47eabbd8787b6 100644 --- a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.h +++ b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.h @@ -3,4 +3,8 @@ #define MICROPY_HW_DFLL_USB_SYNC (1) +#define MICROPY_HW_DEFAULT_SPI_ID (1) +#define MICROPY_HW_DEFAULT_I2C_ID (2) +#define MICROPY_HW_DEFAULT_UART_ID (3) + #define MICROPY_HW_QSPIFLASH GD25Q16C diff --git a/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.h b/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.h index 7893cd706ac1a..0a2a1c2debe0b 100644 --- a/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.h +++ b/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.h @@ -4,6 +4,10 @@ #define MICROPY_HW_XOSC32K (1) #define MICROPY_HW_QSPIFLASH GD25Q16C +#define MICROPY_HW_DEFAULT_UART_ID (3) +#define MICROPY_HW_DEFAULT_I2C_ID (5) +#define MICROPY_HW_DEFAULT_SPI_ID (2) + // defines for WLAN #define MICROPY_HW_WIFI_SPI_ID (2) #define MICROPY_HW_WIFI_SPI_BAUDRATE (8000000) diff --git a/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.h b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.h index 0acf28afbfe15..78b03ce7fbdb8 100644 --- a/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.h +++ b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigboard.h @@ -3,4 +3,8 @@ #define MICROPY_HW_DFLL_USB_SYNC (1) +#define MICROPY_HW_DEFAULT_UART_ID (0) +#define MICROPY_HW_DEFAULT_I2C_ID (1) +#define MICROPY_HW_DEFAULT_SPI_ID (0) + #define MICROPY_HW_SPIFLASH_ID (3) diff --git a/ports/samd/boards/ADAFRUIT_TRINKET_M0/mpconfigboard.h b/ports/samd/boards/ADAFRUIT_TRINKET_M0/mpconfigboard.h index 5732a20e3adc4..1edff009859c1 100644 --- a/ports/samd/boards/ADAFRUIT_TRINKET_M0/mpconfigboard.h +++ b/ports/samd/boards/ADAFRUIT_TRINKET_M0/mpconfigboard.h @@ -2,3 +2,7 @@ #define MICROPY_HW_MCU_NAME "SAMD21E18A" #define MICROPY_HW_DFLL_USB_SYNC (1) + +#define MICROPY_HW_DEFAULT_UART_ID (0) +#define MICROPY_HW_DEFAULT_I2C_ID (2) +#define MICROPY_HW_DEFAULT_SPI_ID (0) diff --git a/ports/samd/boards/MINISAM_M4/mpconfigboard.h b/ports/samd/boards/MINISAM_M4/mpconfigboard.h index 87acf301e36b5..6d908bdcb81d2 100644 --- a/ports/samd/boards/MINISAM_M4/mpconfigboard.h +++ b/ports/samd/boards/MINISAM_M4/mpconfigboard.h @@ -3,4 +3,8 @@ #define MICROPY_HW_DFLL_USB_SYNC (1) +#define MICROPY_HW_DEFAULT_UART_ID (3) +#define MICROPY_HW_DEFAULT_I2C_ID (2) +#define MICROPY_HW_DEFAULT_SPI_ID (1) + #define MICROPY_HW_QSPIFLASH GD25Q16C diff --git a/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.h b/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.h index 062f69ae4e773..7f6d42345776a 100644 --- a/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.h +++ b/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.h @@ -3,4 +3,8 @@ #define MICROPY_HW_XOSC32K (1) +#define MICROPY_HW_DEFAULT_UART_ID (2) +#define MICROPY_HW_DEFAULT_I2C_ID (4) +#define MICROPY_HW_DEFAULT_SPI_ID (5) + #define MICROPY_HW_QSPIFLASH W25Q32JV_IQ diff --git a/ports/samd/boards/SEEED_XIAO_SAMD21/mpconfigboard.h b/ports/samd/boards/SEEED_XIAO_SAMD21/mpconfigboard.h index 7447c5c3acf70..f0213a88e2990 100644 --- a/ports/samd/boards/SEEED_XIAO_SAMD21/mpconfigboard.h +++ b/ports/samd/boards/SEEED_XIAO_SAMD21/mpconfigboard.h @@ -3,3 +3,7 @@ #define MICROPY_HW_XOSC32K (1) #define MICROPY_HW_ADC_VREF (2) + +#define MICROPY_HW_DEFAULT_UART_ID (4) +#define MICROPY_HW_DEFAULT_I2C_ID (2) +#define MICROPY_HW_DEFAULT_SPI_ID (0) diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h index 706fc3c64c37b..fe2226a59eb58 100644 --- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h +++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h @@ -3,6 +3,10 @@ #define MICROPY_HW_XOSC32K (1) +#define MICROPY_HW_DEFAULT_UART_ID (2) +#define MICROPY_HW_DEFAULT_I2C_ID (3) +#define MICROPY_HW_DEFAULT_SPI_ID (4) + // There seems to be an inconsistency in the SAMD51 Thing bootloader in that // the bootloader magic address is at the end of a 192k RAM area, instead of // 256k. Since the SAMD51x20A has 256k RAM, the loader symbol is at that address diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 0b47500bf7e7c..514f383948838 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -138,6 +138,15 @@ #ifndef MICROPY_HW_USB_PID #define MICROPY_HW_USB_PID (0x9802) #endif +#ifndef MICROPY_HW_DEFAULT_UART_ID +#define MICROPY_HW_DEFAULT_UART_ID (-1) +#endif +#ifndef MICROPY_HW_DEFAULT_I2C_ID +#define MICROPY_HW_DEFAULT_I2C_ID (-1) +#endif +#ifndef MICROPY_HW_DEFAULT_SPI_ID +#define MICROPY_HW_DEFAULT_SPI_ID (-1) +#endif // Additional entries for use with pendsv_schedule_dispatch. #ifndef MICROPY_BOARD_PENDSV_ENTRIES From 016ae19cf0e148ba7b33c2c80312b6f3641217dc Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 9 Feb 2025 10:24:49 +0100 Subject: [PATCH 344/546] docs/samd: Update the SAMD documentation describing default IDs/pins. Changes are: - Add the RX/TX pins to the table. In most cases these are the D0/D1 pins. - Document the ability for the instantiation of the default devices without submitting ID or pins. - Improve the example script creating the pin list to show multiple name assigments to the same pin. - Fix errors in the pinout document. Signed-off-by: robert-hh --- docs/samd/pinout.rst | 78 ++++++++++++++++++++++++++++-------------- docs/samd/quickref.rst | 29 ++++++++++++++-- 2 files changed, 79 insertions(+), 28 deletions(-) diff --git a/docs/samd/pinout.rst b/docs/samd/pinout.rst index 776ab74bcdb22..3945e2bf2ff1c 100644 --- a/docs/samd/pinout.rst +++ b/docs/samd/pinout.rst @@ -41,9 +41,11 @@ Pin GPIO Pin name IRQ ADC Serial Serial TCC/TC TCC/TC 35 PB03 FLASH_MISO 3 11 - 5/1 6/1 - 54 PB22 FLASH_MOSI 6 - - 5/2 7/0 - 55 PB23 FLASH_SCK 7 - - 5/3 7/1 - + 11 PA11 RX 11 19 0/3 2/3 1/1 0/3 + 10 PA10 TX 10 18 0/2 2/2 1/0 0/2 12 PA12 MISO 12 - 2/0 4/0 2/0 0/6 - 42 PB10 MOSI 10 - - 4/2 5/0 0/4 - 43 PB11 SCK 11 - - 4/3 5/1 0/5 + 42 PA12 MOSI 10 - - 4/2 5/0 0/4 + 43 PA13 SCK 11 - - 4/3 5/1 0/5 23 PA23 SCL 7 - 3/1 5/1 4/1 0/5 22 PA22 SDA 6 - 3/0 5/0 4/0 0/4 30 PA30 SWCLK 10 - - 1/2 1/0 - @@ -127,7 +129,7 @@ Examples for Adafruit ItsyBitsy M0 Express: - SPI 1 at pins D11/D12/D13 - SPI 2 at pins D0/D4/D1 - SPI 3 at pins D11/D12/D13 -- SPI 4 at Pin MOSI/MISO/SCK This is the default SPI device at the MOSI/MISO/SCK labelled pins. +- SPI 2 at Pin MOSI/MISO/SCK This is the default SPI device at the MOSI/MISO/SCK labelled pins. or other combinations. @@ -169,6 +171,8 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM 22 PA22 D13 6 - - 3/0 5/1 4/0 1/6 0/2 34 PB02 DOTSTAR_CLK 2 14 - - 5/0 6/0 2/2 - 35 PB03 DOTSTAR_DATA 9 15 - - 5/1 6/1 - - + 16 PA16 RX 0 - - 1/0 3/1 2/0 1/0 0/4 + 17 PA17 TX 1 - - 1/1 3/0 2/1 1/1 0/5 55 PB23 MISO 7 - - 1/3 5/3 7/1 - - 0 PA00 MOSI 0 - - - 1/0 2/0 - - 43 PB11 QSPI_CS 12 - - - 4/3 5/1 0/5 1/1 @@ -235,7 +239,7 @@ The I2C devices and signals must be chosen according to the following rules: - The SDA signal must be at a Pin with pad numbers 0. - The SCL signal must be at a Pin with pad numbers 1. -Examples for Adafruit ItsyBitsy M0 Express: +Examples for Adafruit ItsyBitsy M4 Express: - I2C 0 at pins A3/A4 - I2C 1 at pins D0/D1 @@ -253,7 +257,7 @@ The SPI devices and signals must be chosen according to the following rules: - The following pad number pairs are suitable for MOSI/SCK: 0/1 and 3/1. - The MISO signal must be at a Pin with a different pad number than MOSI or SCK. -Examples for Adafruit ItsyBitsy M0 Express: +Examples for Adafruit ItsyBitsy M4 Express: - SPI 1 at Pin MOSI/MISO/SCK This is the default SPI device at the MOSI/MISO/SCK labelled pins. - SPI 3 at pins D13/D11/D12 @@ -296,6 +300,8 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM 21 PA21 D11 5 - - 5/3 3/3 7/1 1/5 0/1 22 PA22 D12 6 - - 3/0 5/1 4/0 1/6 0/2 23 PA23 D13 7 - - 3/1 5/0 4/1 1/7 0/3 + 49 PB17 RX 1 - - 5/1 - 6/1 3/1 0/5 + 48 PB16 TX 0 - - 5/0 - 6/0 3/0 0/4 54 PB22 MISO 22 - - 1/2 5/2 7/0 - - 55 PB23 MOSI 7 - - 1/3 5/3 7/1 - - 35 PB03 NEOPIXEL 9 15 - - 5/1 6/1 - - @@ -381,6 +387,8 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM 8 PA08 FLASH_MOSI - 8 2 0/0 2/1 0/0 0/0 1/4 42 PB10 FLASH_SCK 10 - - - 4/2 5/0 0/4 1/0 10 PA10 FLASH_WP 10 10 - 0/2 2/2 1/0 0/2 1/6 + 23 PA23 RX 7 - - 3/1 5/0 4/1 1/7 0/3 + 22 PA22 TX 6 - - 3/0 5/1 4/0 1/6 0/2 14 PA14 MISO 14 - - 2/2 4/2 3/0 2/0 1/2 12 PA12 MOSI 12 - - 2/0 4/1 2/0 0/6 1/2 54 PB22 NEOPIXEL 22 - - 1/2 5/2 7/0 - - @@ -429,6 +437,13 @@ Pin GPIO Pin name IRQ ADC Serial Serial TCC/TC TCC/TC 5 PA05 A9_D9 5 5 - 0/1 0/1 - 6 PA06 A10_D10 6 6 - 0/2 1/0 - 18 PA18 RX_LED 2 - 1/2 3/2 3/0 0/2 + 41 PB09 RX 9 3 - 4/1 4/1 - + 40 PB08 TX 8 2 - 4/0 4/0 - + 8 PA08 SDA - 16 0/0 2/0 0/0 1/2 + 9 PA09 SCL 9 17 0/1 2/1 0/1 1/3 + 6 PA06 MOSI 6 6 - 0/2 1/0 - + 5 PA05 MISO 5 5 - 0/1 0/1 - + 7 PA07 SCK 7 7 - 0/3 1/1 - 30 PA30 SWCLK 10 - - 1/2 1/0 - 31 PA31 SWDIO 11 - - 1/3 1/1 - 19 PA19 TX_LED 3 - 1/3 3/3 3/1 0/3 @@ -503,6 +518,8 @@ Pin GPIO Pin name IRQ ADC Serial Serial TCC/TC TCC/TC 43 PB11 SCK 11 - - 4/3 5/1 0/5 23 PA23 SCL 7 - 3/1 5/1 4/1 0/5 22 PA22 SDA 6 - 3/0 5/0 4/0 0/4 + 11 PA11 RX 11 19 0/3 2/3 1/1 0/3 + 10 PA10 TX 10 18 0/2 2/2 1/0 0/2 30 PA30 SWCLK 10 - - 1/2 1/0 - 31 PA31 SWDIO 11 - - 1/3 1/1 - 24 PA24 USB_DM 12 - 3/2 5/2 5/0 1/2 @@ -518,9 +535,9 @@ Adafruit ItsyBitsy M0 Express :ref:`samd21_pinout_table`. The default devices at the board are: -- UART 5 at pins PB23/PB22, labelled RX/TX +- UART 2 at pins PA11/PA10, labelled RX/TX - I2C 3 at pins PA22/PA23, labelled SDA/SCL -- SPI 4 at pins PA10/PA12/PA11, labelled MOSI, MISO and SCK +- SPI 4 at pins PB10/PA12/PB11, labelled MOSI, MISO and SCK - DAC output on pin PA02, labelled A0 Adafruit Trinket M0 pin assignment table @@ -536,6 +553,13 @@ Pin GPIO Pin name IRQ ADC Serial Serial TCC/TC TCC/TC 6 PA06 D4 6 6 - 0/2 1/0 - 1 PA01 DOTSTAR_CLK 1 - - 1/1 2/1 - 0 PA00 DOTSTAR_DATA 0 - - 1/0 2/0 - + 7 PA07 RX 7 7 - 0/3 1/1 - + 6 PA06 TX 6 6 - 0/2 1/0 - + 8 PA08 SDA - 16 0/0 2/0 0/0 1/2 + 9 PA09 SCL 9 17 0/1 2/1 0/1 1/3 + 6 PA06 MOSI 6 6 - 0/2 1/0 - + 9 PA09 MISO 9 17 0/1 2/1 0/1 1/3 + 7 PA07 SCK 7 7 - 0/3 1/1 - 10 PA10 LED 10 18 0/2 2/2 1/0 0/2 30 PA30 SWCLK 10 - - 1/2 1/0 - 31 PA31 SWDIO 11 - - 1/3 1/1 - @@ -716,8 +740,10 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM 34 PB02 DOTSTAR_CLK 2 14 - - 5/0 6/0 2/2 - 35 PB03 DOTSTAR_DATA 9 15 - - 5/1 6/1 - - 15 PA15 LED 15 - - 2/3 4/3 3/1 2/1 1/3 - 55 PB23 MISO 7 - - 1/3 5/3 7/1 - - - 54 PB22 MOSI 22 - - 1/2 5/2 7/0 - - + 16 PA16 RX 0 - - 1/0 3/1 2/0 1/0 0/4 + 17 PA17 TX 1 - - 1/1 3/0 2/1 1/1 0/5 + 55 PB23 MOSI 7 - - 1/3 5/3 7/1 - - + 54 PB22 MISO 22 - - 1/2 5/2 7/0 - - 43 PB11 QSPI_CS 12 - - - 4/3 5/1 0/5 1/1 8 PA08 QSPI_D0 - 8 2 0/0 2/1 0/0 0/0 1/4 9 PA09 QSPI_D1 9 9 3 0/1 2/0 0/1 0/1 1/5 @@ -893,6 +919,8 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM 11 PA11 FLASH_MISO 11 11 - 0/3 2/3 1/1 0/3 1/7 8 PA08 FLASH_MOSI - 8 2 0/0 2/1 0/0 0/0 1/4 9 PA09 FLASH_SCK 9 9 3 0/1 2/0 0/1 0/1 1/5 + 13 PA13 RX 13 - - 2/1 4/0 2/1 0/7 1/3 + 12 PA12 TX 12 - - 2/0 4/1 2/0 0/6 1/2 43 PB11 MISO 12 - - - 4/3 5/1 0/5 1/1 44 PB12 MOSI 12 - - 4/0 - 4/0 3/0 0/0 55 PB23 RXD 7 - - 1/3 5/3 7/1 - - @@ -931,7 +959,7 @@ Adafruit ItsyBitsy M4 Express :ref:`samd51_pinout_table`. The default devices at the board are: - UART 2 at pins PA13/PA12, labelled RXD/TXD -- I2C 5 at pins PA22/PA23, labelled SDA/SCL +- I2C 3 at pins PA22/PA23, labelled SDA/SCL - SPI 4 at pins PB12/PB11/PB13, labelled MOSI, MISO and SCK - DAC output on pins PA02 and PA05, labelled A0 and A4 @@ -1163,22 +1191,22 @@ The tables shown above were created with small a Python script running on the ta else: return "zzzzzzz%03d" % i[0] - def table(num=127, sort=True): - pintbl = [] - inv_bd = {v: k for k, v in Pin.board.__dict__.items()} - for i in range(num): - try: - p = Pin(i) - pi = pininfo(p) - if p in inv_bd.keys(): - name = inv_bd[p] - else: - name = "" - pintbl.append((i, name, pininfo(i))) - except: - pass - # print("not defined") + def pinnum(p): + return (ord(p[1]) - ord("A")) * 32 + int(p[2:]) + def table(num = 127, sort=True): + pintbl = [] + pinlist = [] + for name in Pin.board.__dict__.keys(): + p = Pin(name) + pi = pininfo(p) + pintbl.append((pinnum(pi[0]), name, pi)) + pinlist.append(p) + for pc in Pin.cpu.__dict__.keys(): + p = Pin(pc) + pi = pininfo(p) + if not p in pinlist: + pintbl.append((pinnum(pi[0]), "", pi)) if sort: pintbl.sort(key=tblkey) for item in pintbl: diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst index d57dc67908398..781686d2f60e6 100644 --- a/docs/samd/quickref.rst +++ b/docs/samd/quickref.rst @@ -163,10 +163,15 @@ See :ref:`machine.UART `. :: uart3.write('hello') # write 5 bytes uart3.read(5) # read up to 5 bytes + uart = UART() # Use the default values for id, rx and tx. + uart = UART(baudrate=9600) # Use the default UART and set the baudrate + The SAMD21/SAMD51 MCUs have up to eight hardware so called SERCOM devices, which can be used as UART, SPI or I2C device, but not every MCU variant and board exposes all TX and RX pins for users. For the assignment of Pins to devices and UART signals, -refer to the :ref:`SAMD pinout `. +refer to the :ref:`SAMD pinout `. If the id, rx or tx pins are not specified, +the default values are used. The first positional argument (if given) is assumed to be the UART id. +If the baudrate is changed and the UART id is omitted, it must be set using the baudrate keyword. PWM (pulse width modulation) ---------------------------- @@ -373,8 +378,18 @@ signal pins for users. Hardware SPI is accessed via the spi = SPI(1, sck=Pin("SCK"), mosi=Pin("MOSI"), miso=Pin("MISO"), baudrate=10000000) spi.write('Hello World') -If miso is not specified, it is not used. For the assignment of Pins to SPI devices and signals, refer to -:ref:`SAMD pinout `. +For the assignment of Pins to SPI devices and signals, refer to +:ref:`SAMD pinout `. If the id, miso, mosi or sck pins are not specified, +the default values are used. So it is possible to create the SPI object as:: + + from machine import SPI + spi = SPI() # Use the default device and default baudrate + spi = SPI(baudrate=12_000_000) # Use the default device and change the baudrate + +If the MISO signal shall be omitted, it must be defined as miso=None. +The first positional argument (if given) is assumed to be the SPI id. +If the baudrate is changed while the SPI id is omitted, it must be +set using the baudrate keyword. Note: Even if the highest reliable baud rate at the moment is about 24 Mhz, setting a baud rate will not always result in exactly that frequency, especially @@ -407,6 +422,7 @@ The SAMD21/SAMD51 MCUs have up to eight hardware so called SERCOM devices, which can be used as UART, SPI or I2C device, but not every MCU variant and board exposes all signal pins for users. For the assignment of Pins to devices and I2C signals, refer to :ref:`SAMD pinout `. +If the id, scl or sda pins are not specified, the default values are used. Hardware I2C is accessed via the :ref:`machine.I2C ` class and has the same methods as software SPI above:: @@ -416,6 +432,13 @@ has the same methods as software SPI above:: i2c = I2C(2, scl=Pin("SCL"), sda=Pin("SDA"), freq=400_000) i2c.writeto(0x76, b"Hello World") + i2c2 = I2C() # Use the default values for id, scl and sda. + i2c2 = I2C(freq=100_000) # Use the default device and set freq. + +The first positional argument (if given) is assumed to be the I2C id. +If the freq is changed and the I2C id is omitted, it must be set using +the freq keyword. + OneWire driver -------------- From 5f01232dd7c0a211e8beff24dc88efa48598ef36 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 26 Feb 2025 10:58:37 +1100 Subject: [PATCH 345/546] tests/run-tests: Remove any 'expected' file from a unittest run. This won't be generated normally, but a failed run (for example, from a unittest with an error or which doesn't call unittest.main()) will generate one. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/run-tests.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run-tests.py b/tests/run-tests.py index 1af3dfc2973fc..38f1161360272 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -996,6 +996,8 @@ def run_one_test(test_file): if output_expected is not None: with open(filename_expected, "wb") as f: f.write(output_expected) + else: + rm_f(filename_expected) # in case left over from previous failed run with open(filename_mupy, "wb") as f: f.write(output_mupy) failed_tests.append((test_name, test_file)) From b4cf82b2d62c60c5e6e10d8d23c6b099b96c6f75 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 26 Feb 2025 11:00:53 +1100 Subject: [PATCH 346/546] test/run-tests: Print a note if it looks like unittest.main() missing. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/run-tests.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/run-tests.py b/tests/run-tests.py index 38f1161360272..50adb6b4d3bea 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -1004,6 +1004,16 @@ def run_one_test(test_file): test_count.increment() + # Print a note if this looks like it might have been a misfired unittest + if not uses_unittest and not test_passed: + with open(test_file, "r") as f: + if any(re.match("^import.+unittest", l) for l in f.readlines()): + print( + "NOTE: {} may be a unittest that doesn't run unittest.main()".format( + test_file + ) + ) + if pyb: num_threads = 1 From f5b4545761dbbce0c00dec6d90d180663a61eaa4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 2 Mar 2025 23:52:13 +1100 Subject: [PATCH 347/546] py/modsys: Add sys.implementation._build entry. For a given MicroPython firmware/executable it can be sometimes important to know how it was built, which variant/board configuration it came from. This commit adds a new field `sys.implementation._build` that can help identify the configuration that MicroPython was built with. For now it's either: * for unix, webassembly and windows ports * - for microcontroller ports (the variant is optional) In the future additional elements may be added to this string, separated by a hyphen. Resolves issue #16498. Signed-off-by: Damien George --- docs/library/sys.rst | 12 ++++++++++++ py/mkrules.cmake | 13 +++++++++++++ py/mkrules.mk | 11 +++++++++++ py/modsys.c | 23 +++++++++++++++++++---- tests/basics/sys1.py | 6 ++++++ 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/docs/library/sys.rst b/docs/library/sys.rst index c72214c13108e..baefd927051d2 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -75,6 +75,8 @@ Constants * *version* - tuple (major, minor, micro, releaselevel), e.g. (1, 22, 0, '') * *_machine* - string describing the underlying machine * *_mpy* - supported mpy file-format version (optional attribute) + * *_build* - string that can help identify the configuration that + MicroPython was built with This object is the recommended way to distinguish MicroPython from other Python implementations (note that it still may not exist in the very @@ -83,6 +85,16 @@ Constants Starting with version 1.22.0-preview, the fourth node *releaselevel* in *implementation.version* is either an empty string or ``"preview"``. + The *_build* entry was added in version 1.25.0 and is a hyphen-separated + set of elements. New elements may be appended in the future so it's best to + access this field using ``sys.implementation._build.split("-")``. The + elements that are currently used are: + + * On the unix, webassembly and windows ports the first element is the variant + name, for example ``'standard'``. + * On microcontroller targets, the first element is the board name and the second + element (if present) is the board variant, for example ``'RPI_PICO2-RISCV'`` + .. admonition:: Difference to CPython :class: attention diff --git a/py/mkrules.cmake b/py/mkrules.cmake index 907500dd67534..cafcbce5681d8 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -19,6 +19,19 @@ if(NOT MICROPY_PREVIEW_VERSION_2) set(MICROPY_PREVIEW_VERSION_2 0) endif() +# Set the board name. +if(MICROPY_BOARD) + if(MICROPY_BOARD_VARIANT) + set(MICROPY_BOARD_BUILD_NAME ${MICROPY_BOARD}-${MICROPY_BOARD_VARIANT}) + else() + set(MICROPY_BOARD_BUILD_NAME ${MICROPY_BOARD}) + endif() + + target_compile_definitions(${MICROPY_TARGET} PRIVATE + MICROPY_BOARD_BUILD_NAME="${MICROPY_BOARD_BUILD_NAME}" + ) +endif() + # Need to do this before extracting MICROPY_CPP_DEF below. Rest of frozen # manifest handling is at the end of this file. if(MICROPY_FROZEN_MANIFEST) diff --git a/py/mkrules.mk b/py/mkrules.mk index e9504ce39f839..ad855c0d7fc45 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -27,6 +27,17 @@ OBJ_EXTRA_ORDER_DEPS += $(HEADER_BUILD)/compressed.data.h CFLAGS += -DMICROPY_ROM_TEXT_COMPRESSION=1 endif +# Set the variant or board name. +ifneq ($(VARIANT),) +CFLAGS += -DMICROPY_BOARD_BUILD_NAME=\"$(VARIANT)\" +else ifneq ($(BOARD),) +ifeq ($(BOARD_VARIANT),) +CFLAGS += -DMICROPY_BOARD_BUILD_NAME=\"$(BOARD)\" +else +CFLAGS += -DMICROPY_BOARD_BUILD_NAME=\"$(BOARD)-$(BOARD_VARIANT)\" +endif +endif + # QSTR generation uses the same CFLAGS, with these modifications. QSTR_GEN_FLAGS = -DNO_QSTR # Note: := to force evaluation immediately. diff --git a/py/modsys.c b/py/modsys.c index e90ea2233a732..9ab02293b9063 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -92,6 +92,17 @@ static const MP_DEFINE_STR_OBJ(mp_sys_implementation_machine_obj, MICROPY_BANNER #endif #if MICROPY_PY_ATTRTUPLE + +#if defined(MICROPY_BOARD_BUILD_NAME) +static const MP_DEFINE_STR_OBJ(mp_sys_implementation__build_obj, MICROPY_BOARD_BUILD_NAME); +#define MICROPY_BOARD_BUILD (1) +#define SYS_IMPLEMENTATION_ELEMS__BUILD \ + , MP_ROM_PTR(&mp_sys_implementation__build_obj) +#else +#define MICROPY_BOARD_BUILD (0) +#define SYS_IMPLEMENTATION_ELEMS__BUILD +#endif + #if MICROPY_PREVIEW_VERSION_2 #define SYS_IMPLEMENTATION_ELEMS__V2 \ , MP_ROM_TRUE @@ -106,6 +117,9 @@ static const qstr impl_fields[] = { #if MICROPY_PERSISTENT_CODE_LOAD MP_QSTR__mpy, #endif + #if defined(MICROPY_BOARD_BUILD_NAME) + MP_QSTR__build, + #endif #if MICROPY_PREVIEW_VERSION_2 MP_QSTR__v2, #endif @@ -113,19 +127,20 @@ static const qstr impl_fields[] = { static MP_DEFINE_ATTRTUPLE( mp_sys_implementation_obj, impl_fields, - 3 + MICROPY_PERSISTENT_CODE_LOAD + MICROPY_PREVIEW_VERSION_2, + 3 + MICROPY_PERSISTENT_CODE_LOAD + MICROPY_BOARD_BUILD + MICROPY_PREVIEW_VERSION_2, SYS_IMPLEMENTATION_ELEMS_BASE SYS_IMPLEMENTATION_ELEMS__MPY + SYS_IMPLEMENTATION_ELEMS__BUILD SYS_IMPLEMENTATION_ELEMS__V2 ); #else static const mp_rom_obj_tuple_t mp_sys_implementation_obj = { {&mp_type_tuple}, 3 + MICROPY_PERSISTENT_CODE_LOAD, - // Do not include SYS_IMPLEMENTATION_ELEMS__V2 because - // SYS_IMPLEMENTATION_ELEMS__MPY may be empty if + // Do not include SYS_IMPLEMENTATION_ELEMS__BUILD or SYS_IMPLEMENTATION_ELEMS__V2 + // because SYS_IMPLEMENTATION_ELEMS__MPY may be empty if // MICROPY_PERSISTENT_CODE_LOAD is disabled, which means they'll share - // the same index. Cannot query _v2 if MICROPY_PY_ATTRTUPLE is + // the same index. Cannot query _build or _v2 if MICROPY_PY_ATTRTUPLE is // disabled. { SYS_IMPLEMENTATION_ELEMS_BASE diff --git a/tests/basics/sys1.py b/tests/basics/sys1.py index 95d8905d135c7..94220fe4a60c6 100644 --- a/tests/basics/sys1.py +++ b/tests/basics/sys1.py @@ -24,6 +24,12 @@ # Effectively skip subtests print(int) +if hasattr(sys.implementation, '_build'): + print(type(sys.implementation._build)) +else: + # Effectively skip subtests + print(str) + try: print(sys.intern('micropython') == 'micropython') has_intern = True From 859b6efce52b0ca605681e3b10e2304577942a9a Mon Sep 17 00:00:00 2001 From: danicampora Date: Fri, 21 Feb 2025 14:41:03 +0100 Subject: [PATCH 348/546] zephyr/machine_wdt: Add watchdog timer implementation. Simple implementation in-line with the rest of the MicroPython ports Tested on the nRF52832 and the nRF5340. Signed-off-by: danicampora --- ports/zephyr/machine_wdt.c | 92 +++++++++++++++++++++++++++++++++++++ ports/zephyr/mpconfigport.h | 4 ++ ports/zephyr/prj.conf | 3 ++ 3 files changed, 99 insertions(+) create mode 100644 ports/zephyr/machine_wdt.c diff --git a/ports/zephyr/machine_wdt.c b/ports/zephyr/machine_wdt.c new file mode 100644 index 0000000000000..7c5e6337ca33a --- /dev/null +++ b/ports/zephyr/machine_wdt.c @@ -0,0 +1,92 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Daniel Campora on behalf of REMOTE TECH LTD + * + * 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. + */ + +// This file is never compiled standalone, it's included directly from +// extmod/machine_wdt.c via MICROPY_PY_MACHINE_WDT_INCLUDEFILE. + +#include +#include +#include +#include +#include + +#include "py/mperrno.h" + +typedef struct _machine_wdt_obj_t { + mp_obj_base_t base; + struct device *wdt; + int32_t channel_id; +} machine_wdt_obj_t; + +static machine_wdt_obj_t wdt_default = { + {&machine_wdt_type}, NULL, -1 +}; + +static machine_wdt_obj_t *mp_machine_wdt_make_new_instance(mp_int_t id, mp_int_t timeout_ms) { + if (id != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid WDT id")); + } + + if (timeout_ms <= 0) { + mp_raise_ValueError(MP_ERROR_TEXT("watchdog timeout too short")); + } + + wdt_default.wdt = (struct device *)DEVICE_DT_GET(DT_ALIAS(watchdog0)); + + if (!device_is_ready(wdt_default.wdt)) { + mp_raise_OSError(MP_ENODEV); + } + + struct wdt_timeout_cfg wdt_config = { + /* Reset SoC when watchdog timer expires. */ + .flags = WDT_FLAG_RESET_SOC, + + /* Expire watchdog after max window */ + .window.min = 0, + .window.max = timeout_ms, + }; + + wdt_default.channel_id = wdt_install_timeout(wdt_default.wdt, &wdt_config); + + if (wdt_default.channel_id < 0) { + mp_raise_OSError(-wdt_default.channel_id); + } + + mp_int_t rs_code = wdt_setup(wdt_default.wdt, WDT_OPT_PAUSE_IN_SLEEP | WDT_OPT_PAUSE_HALTED_BY_DBG); + + if (rs_code < 0) { + mp_raise_OSError(-rs_code); + } + + return &wdt_default; +} + +static void mp_machine_wdt_feed(machine_wdt_obj_t *self) { + mp_int_t rs_code = wdt_feed(self->wdt, self->channel_id); + if (rs_code < 0) { + mp_raise_OSError(-rs_code); + } +} diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index a2e74e6928984..c4fa6c5b95439 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -68,6 +68,10 @@ #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_PY_MACHINE_UART (1) #define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/zephyr/machine_uart.c" +#ifdef CONFIG_WATCHDOG +#define MICROPY_PY_MACHINE_WDT (1) +#define MICROPY_PY_MACHINE_WDT_INCLUDEFILE "ports/zephyr/machine_wdt.c" +#endif #define MICROPY_PY_STRUCT (0) #ifdef CONFIG_NETWORKING // If we have networking, we likely want errno comfort diff --git a/ports/zephyr/prj.conf b/ports/zephyr/prj.conf index eac04216c7fac..ae37c3ff07614 100644 --- a/ports/zephyr/prj.conf +++ b/ports/zephyr/prj.conf @@ -79,3 +79,6 @@ CONFIG_NET_BUF_POOL_USAGE=y CONFIG_MICROPY_CONFIGFILE="mpconfigport.h" CONFIG_MICROPY_VFS_FAT=y CONFIG_MICROPY_VFS_LFS2=y + +CONFIG_WATCHDOG=y +CONFIG_WDT_DISABLE_AT_BOOT=y From fc71f7832f202acf8ab45ab36ebffd706cb0ab59 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 18 Oct 2024 12:08:11 +0300 Subject: [PATCH 349/546] py/makeqstrdata.py: Implement MicroPython compatibility. This allows running `py/makeqstrdata.py` with MicroPython itself. Signed-off-by: Volodymyr Shymanskyy Signed-off-by: Angus Gratton --- py/makeqstrdata.py | 64 ++++++++++++++++++++++++++++++++++++++-------- pyproject.toml | 2 +- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py index 480344180a88a..3a9c7aff52536 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -9,17 +9,54 @@ import re import sys -# Python 2/3 compatibility: +# Python 2/3/MicroPython compatibility: # - iterating through bytes is different -# - codepoint2name lives in a different module -import platform - -if platform.python_version_tuple()[0] == "2": +# - codepoint2name from html.entities is hard-coded +if sys.version_info[0] == 2: bytes_cons = lambda val, enc=None: bytearray(val) - from htmlentitydefs import codepoint2name -elif platform.python_version_tuple()[0] == "3": +elif sys.version_info[0] == 3: # Also handles MicroPython bytes_cons = bytes - from html.entities import codepoint2name + +# fmt: off +codepoint2name = { + 198: "AElig", 193: "Aacute", 194: "Acirc", 192: "Agrave", 913: "Alpha", 197: "Aring", 195: "Atilde", + 196: "Auml", 914: "Beta", 199: "Ccedil", 935: "Chi", 8225: "Dagger", 916: "Delta", 208: "ETH", + 201: "Eacute", 202: "Ecirc", 200: "Egrave", 917: "Epsilon", 919: "Eta", 203: "Euml", 915: "Gamma", + 205: "Iacute", 206: "Icirc", 204: "Igrave", 921: "Iota", 207: "Iuml", 922: "Kappa", 923: "Lambda", + 924: "Mu", 209: "Ntilde", 925: "Nu", 338: "OElig", 211: "Oacute", 212: "Ocirc", 210: "Ograve", + 937: "Omega", 927: "Omicron", 216: "Oslash", 213: "Otilde", 214: "Ouml", 934: "Phi", 928: "Pi", + 8243: "Prime", 936: "Psi", 929: "Rho", 352: "Scaron", 931: "Sigma", 222: "THORN", 932: "Tau", + 920: "Theta", 218: "Uacute", 219: "Ucirc", 217: "Ugrave", 933: "Upsilon", 220: "Uuml", 926: "Xi", + 221: "Yacute", 376: "Yuml", 918: "Zeta", 225: "aacute", 226: "acirc", 180: "acute", 230: "aelig", + 224: "agrave", 8501: "alefsym", 945: "alpha", 38: "amp", 8743: "and", 8736: "ang", 229: "aring", + 8776: "asymp", 227: "atilde", 228: "auml", 8222: "bdquo", 946: "beta", 166: "brvbar", 8226: "bull", + 8745: "cap", 231: "ccedil", 184: "cedil", 162: "cent", 967: "chi", 710: "circ", 9827: "clubs", + 8773: "cong", 169: "copy", 8629: "crarr", 8746: "cup", 164: "curren", 8659: "dArr", 8224: "dagger", + 8595: "darr", 176: "deg", 948: "delta", 9830: "diams", 247: "divide", 233: "eacute", 234: "ecirc", + 232: "egrave", 8709: "empty", 8195: "emsp", 8194: "ensp", 949: "epsilon", 8801: "equiv", 951: "eta", + 240: "eth", 235: "euml", 8364: "euro", 8707: "exist", 402: "fnof", 8704: "forall", 189: "frac12", + 188: "frac14", 190: "frac34", 8260: "frasl", 947: "gamma", 8805: "ge", 62: "gt", 8660: "hArr", + 8596: "harr", 9829: "hearts", 8230: "hellip", 237: "iacute", 238: "icirc", 161: "iexcl", 236: "igrave", + 8465: "image", 8734: "infin", 8747: "int", 953: "iota", 191: "iquest", 8712: "isin", 239: "iuml", + 954: "kappa", 8656: "lArr", 955: "lambda", 9001: "lang", 171: "laquo", 8592: "larr", 8968: "lceil", + 8220: "ldquo", 8804: "le", 8970: "lfloor", 8727: "lowast", 9674: "loz", 8206: "lrm", 8249: "lsaquo", + 8216: "lsquo", 60: "lt", 175: "macr", 8212: "mdash", 181: "micro", 183: "middot", 8722: "minus", + 956: "mu", 8711: "nabla", 160: "nbsp", 8211: "ndash", 8800: "ne", 8715: "ni", 172: "not", 8713: "notin", + 8836: "nsub", 241: "ntilde", 957: "nu", 243: "oacute", 244: "ocirc", 339: "oelig", 242: "ograve", + 8254: "oline", 969: "omega", 959: "omicron", 8853: "oplus", 8744: "or", 170: "ordf", 186: "ordm", + 248: "oslash", 245: "otilde", 8855: "otimes", 246: "ouml", 182: "para", 8706: "part", 8240: "permil", + 8869: "perp", 966: "phi", 960: "pi", 982: "piv", 177: "plusmn", 163: "pound", 8242: "prime", + 8719: "prod", 8733: "prop", 968: "psi", 34: "quot", 8658: "rArr", 8730: "radic", 9002: "rang", + 187: "raquo", 8594: "rarr", 8969: "rceil", 8221: "rdquo", 8476: "real", 174: "reg", 8971: "rfloor", + 961: "rho", 8207: "rlm", 8250: "rsaquo", 8217: "rsquo", 8218: "sbquo", 353: "scaron", 8901: "sdot", + 167: "sect", 173: "shy", 963: "sigma", 962: "sigmaf", 8764: "sim", 9824: "spades", 8834: "sub", + 8838: "sube", 8721: "sum", 8835: "sup", 185: "sup1", 178: "sup2", 179: "sup3", 8839: "supe", + 223: "szlig", 964: "tau", 8756: "there4", 952: "theta", 977: "thetasym", 8201: "thinsp", 254: "thorn", + 732: "tilde", 215: "times", 8482: "trade", 8657: "uArr", 250: "uacute", 8593: "uarr", 251: "ucirc", + 249: "ugrave", 168: "uml", 978: "upsih", 965: "upsilon", 252: "uuml", 8472: "weierp", 958: "xi", + 253: "yacute", 165: "yen", 255: "yuml", 950: "zeta", 8205: "zwj", 8204: "zwnj" +} +# fmt: on # end compatibility code codepoint2name[ord("-")] = "hyphen" @@ -295,6 +332,9 @@ "", } +# Matches any string that needs no escaping (alphanum + _ only) +RE_NO_ESCAPE = re.compile(r"^[a-zA-Z0-9_]$") + # this must match the equivalent function in qstr.c def compute_hash(qstr, bytes_hash): @@ -307,15 +347,17 @@ def compute_hash(qstr, bytes_hash): def qstr_escape(qst): - def esc_char(m): - c = ord(m.group(0)) + def esc_char(c): + if RE_NO_ESCAPE.match(c): + return c + c = ord(c) try: name = codepoint2name[c] except KeyError: name = "0x%02x" % c return "_" + name + "_" - return re.sub(r"[^A-Za-z0-9_]", esc_char, qst) + return "".join(map(esc_char, qst)) static_qstr_list_ident = list(map(qstr_escape, static_qstr_list)) diff --git a/pyproject.toml b/pyproject.toml index d5e3d0e847464..8053df24dd797 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.codespell] count = "" ignore-regex = '\b[A-Z]{3}\b' -ignore-words-list = "ans,asend,deques,dout,emac,extint,hsi,iput,mis,numer,shft,synopsys,technic,ure" +ignore-words-list = "ans,asend,deques,dout,emac,extint,hsi,iput,mis,notin,numer,shft,synopsys,technic,ure,curren" quiet-level = 3 skip = """ */build*,\ From e1b2f2e078727999148a71ab969c1685173d25c0 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 18 Oct 2024 12:08:11 +0300 Subject: [PATCH 350/546] tools/mpy-tool.py: Add support for self-hosting of mpy-tool. This allows running mpy-tool using MicroPython itself. An appropriate test is added to CI to make sure it continues to work. Signed-off-by: Volodymyr Shymanskyy Signed-off-by: Angus Gratton --- tools/ci.sh | 10 ++++++++++ tools/mpy-tool.py | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tools/ci.sh b/tools/ci.sh index 5b108d620258c..682c3ae507a6d 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -118,9 +118,19 @@ function ci_mpy_format_test { python2.7 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy python3 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy + # Build MicroPython + ci_unix_standard_build + micropython=./ports/unix/build-standard/micropython + $micropython -m mip install --target . argparse __future__ + export MICROPYPATH=. + + # Test mpy-tool.py running under MicroPython + $micropython ./tools/mpy-tool.py -x -d tests/frozen/frozentest.mpy + # Test mpy-tool.py dump feature on native code make -C examples/natmod/features1 ./tools/mpy-tool.py -xd examples/natmod/features1/features1.mpy + $micropython ./tools/mpy-tool.py -x -d examples/natmod/features1/features1.mpy } ######################################################################################## diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index cdceae3746638..0094c12177d78 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -24,11 +24,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -# Python 2/3 compatibility code +# Python 2/3/MicroPython compatibility code from __future__ import print_function -import platform +import sys -if platform.python_version_tuple()[0] == "2": +if sys.version_info[0] == 2: from binascii import hexlify as hexlify_py2 str_cons = lambda val, enc=None: str(val) @@ -41,7 +41,7 @@ def hexlify_to_str(b): x = hexlify_py2(b) return ":".join(x[i : i + 2] for i in range(0, len(x), 2)) -else: +elif sys.version_info[0] == 3: # Also handles MicroPython from binascii import hexlify str_cons = str From 6fba1e406bde7ddf00f7cf52bd8c44764c8a1a97 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 18 Oct 2024 12:08:11 +0300 Subject: [PATCH 351/546] tools/mpy-tool.py: Support calling main() from an external script. Signed-off-by: Volodymyr Shymanskyy Signed-off-by: Angus Gratton --- 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 0094c12177d78..69d3a6c06f6b3 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -1765,7 +1765,7 @@ def copy_section(file, offset, offset2): f.write(merged_mpy) -def main(): +def main(args=None): global global_qstrs import argparse @@ -1797,7 +1797,7 @@ def main(): ) cmd_parser.add_argument("-o", "--output", default=None, help="output file") cmd_parser.add_argument("files", nargs="+", help="input .mpy files") - args = cmd_parser.parse_args() + args = cmd_parser.parse_args(args) # set config values relevant to target machine config.MICROPY_LONGINT_IMPL = { From 4d034f817c861b90cbe7ab0cb59d85036031750f Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 15 Feb 2025 00:58:01 +0100 Subject: [PATCH 352/546] esp8266/network_wlan: Allow enumerating connected stations in AP mode. This commit introduces the ability to obtain a list of stations connected to the device when in soft-AP mode. A new parameter ("stations") to pass to WLAN.status is supported, returning a tuple of (bssid, ipv4) entries, one per connected station. An empty tuple is returned if no stations are connected, and an exception is raised if an error occurred whilst building the python objects to return to the interpreter. Documentation is also updated to cover the new parameter. This fixes #5395. Signed-off-by: Alessandro Gatti --- docs/library/network.WLAN.rst | 13 ++++++++++++- ports/esp8266/network_wlan.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/docs/library/network.WLAN.rst b/docs/library/network.WLAN.rst index 09fefc5929f07..ee0ef490fda84 100644 --- a/docs/library/network.WLAN.rst +++ b/docs/library/network.WLAN.rst @@ -85,7 +85,18 @@ Methods * ``STAT_GOT_IP`` -- connection successful. When called with one argument *param* should be a string naming the status - parameter to retrieve. Supported parameters in WiFI STA mode are: ``'rssi'``. + parameter to retrieve, and different parameters are supported depending on the + mode the WiFi is in. + + In STA mode, passing ``'rssi'`` returns a signal strength indicator value, whose + format varies depending on the port (this is available on all ports that support + WiFi network interfaces, except for CC3200). + + In AP mode, passing ``'stations'`` returns a list of connected WiFi stations + (this is available on all ports that support WiFi network interfaces, except for + CC3200). The format of the station information entries varies across ports, + providing either the raw BSSID of the connected station, the IP address of the + connected station, or both. .. method:: WLAN.isconnected() diff --git a/ports/esp8266/network_wlan.c b/ports/esp8266/network_wlan.c index 7131b88bac9ab..9084f246feaae 100644 --- a/ports/esp8266/network_wlan.c +++ b/ports/esp8266/network_wlan.c @@ -170,6 +170,29 @@ static mp_obj_t esp_disconnect(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(esp_disconnect_obj, esp_disconnect); +static void build_stations_list_cleanup_callback(void *station_info) { + if (station_info != NULL) { + wifi_softap_free_station_info(); + } +} + +static mp_obj_t build_stations_list(void) { + struct station_info *info = wifi_softap_get_station_info(); + MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, build_stations_list_cleanup_callback, (void *)info); + nlr_push_jump_callback(&ctx.callback, mp_call_function_1_from_nlr_jump_callback); + mp_obj_list_t *stations = MP_OBJ_TO_PTR(mp_obj_new_list(0, NULL)); + struct station_info *current = info; + mp_obj_t entry[2] = { 0 }; + while (current != NULL) { + entry[0] = mp_obj_new_bytes(current->bssid, sizeof(current->bssid)); + entry[1] = netutils_format_ipv4_addr((uint8_t *)¤t->ip.addr, NETUTILS_BIG); + mp_obj_list_append(stations, mp_obj_new_tuple(2, entry)); + current = STAILQ_NEXT(current, next); + } + nlr_pop_jump_callback(true); + return MP_OBJ_FROM_PTR(stations); +} + static mp_obj_t esp_status(size_t n_args, const mp_obj_t *args) { wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (n_args == 1) { @@ -185,6 +208,12 @@ static mp_obj_t esp_status(size_t n_args, const mp_obj_t *args) { if (self->if_id == STATION_IF) { return MP_OBJ_NEW_SMALL_INT(wifi_station_get_rssi()); } + break; + case MP_QSTR_stations: + if (self->if_id == SOFTAP_IF) { + return build_stations_list(); + } + break; } mp_raise_ValueError(MP_ERROR_TEXT("unknown status param")); } From 6be7570219c05f5dd4a5e0bc10be3cff851da8c8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 4 Mar 2025 10:48:55 +1100 Subject: [PATCH 353/546] nrf/modules: Fix access of read-only buffer in Flash.writeblocks. When writing to flash, the source buffer only needs to be read-only, not writable. This fix allows passing in `bytes` and other read-only buffer objects. Signed-off-by: Damien George --- ports/nrf/modules/nrf/flashbdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/nrf/modules/nrf/flashbdev.c b/ports/nrf/modules/nrf/flashbdev.c index e490dc53dd677..41833b228e2ff 100644 --- a/ports/nrf/modules/nrf/flashbdev.c +++ b/ports/nrf/modules/nrf/flashbdev.c @@ -80,7 +80,7 @@ mp_obj_t nrf_flashbdev_writeblocks(size_t n_args, const mp_obj_t *args) { nrf_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); uint32_t block_num = mp_obj_get_int(args[1]); mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); mp_int_t address = self->start + (block_num * FLASH_PAGESIZE); From 9dd4cef81416711ad05d3ee874f2813b42926541 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 3 Jan 2025 11:53:00 +1100 Subject: [PATCH 354/546] py/objarray: Add MP_DEFINE_MEMORYVIEW_OBJ convenience macro. This allows defining a `memoryview` instance, either statically or on the C stack. Signed-off-by: Damien George --- py/objarray.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/py/objarray.h b/py/objarray.h index 4a0e8a983fe77..bb7a514b97913 100644 --- a/py/objarray.h +++ b/py/objarray.h @@ -53,6 +53,10 @@ typedef struct _mp_obj_array_t { } mp_obj_array_t; #if MICROPY_PY_BUILTINS_MEMORYVIEW + +#define MP_DEFINE_MEMORYVIEW_OBJ(obj_name, typecode, offset, len, ptr) \ + mp_obj_array_t obj_name = {{&mp_type_memoryview}, (typecode), (offset), (len), (ptr)} + static inline void mp_obj_memoryview_init(mp_obj_array_t *self, size_t typecode, size_t offset, size_t len, void *items) { self->base.type = &mp_type_memoryview; self->typecode = typecode; @@ -60,6 +64,7 @@ static inline void mp_obj_memoryview_init(mp_obj_array_t *self, size_t typecode, self->len = len; self->items = items; } + #endif #if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY From 89e6c58c8007e2628741e73c32866c23e8478ae1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 15 Nov 2024 12:57:34 +1100 Subject: [PATCH 355/546] extmod/modvfs: Add vfs.rom_ioctl function and its ioctl constants. This is a generic interface to allow querying and modifying the read-only memory area of a device, if it has such an area. Signed-off-by: Damien George --- extmod/modvfs.c | 7 +++++++ extmod/vfs.h | 15 +++++++++++++++ ports/qemu/mpconfigport.h | 1 + ports/unix/variants/mpconfigvariant_common.h | 1 + py/mpconfig.h | 5 +++++ 5 files changed, 29 insertions(+) diff --git a/extmod/modvfs.c b/extmod/modvfs.c index df422365b75ef..41841f05569a2 100644 --- a/extmod/modvfs.c +++ b/extmod/modvfs.c @@ -38,11 +38,18 @@ #error "MICROPY_PY_VFS requires MICROPY_VFS" #endif +#if MICROPY_VFS_ROM_IOCTL +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_rom_ioctl_obj, 1, 4, mp_vfs_rom_ioctl); +#endif + static const mp_rom_map_elem_t vfs_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_vfs) }, { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, + #if MICROPY_VFS_ROM_IOCTL + { MP_ROM_QSTR(MP_QSTR_rom_ioctl), MP_ROM_PTR(&mp_vfs_rom_ioctl_obj) }, + #endif #if MICROPY_VFS_FAT { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, #endif diff --git a/extmod/vfs.h b/extmod/vfs.h index 626e25a351123..cc9c50d290051 100644 --- a/extmod/vfs.h +++ b/extmod/vfs.h @@ -52,6 +52,13 @@ #define MP_BLOCKDEV_IOCTL_BLOCK_SIZE (5) #define MP_BLOCKDEV_IOCTL_BLOCK_ERASE (6) +// Constants for vfs.rom_ioctl() function. +#define MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS (1) // rom_ioctl(1) +#define MP_VFS_ROM_IOCTL_GET_SEGMENT (2) // rom_ioctl(2, ) +#define MP_VFS_ROM_IOCTL_WRITE_PREPARE (3) // rom_ioctl(3, , ) +#define MP_VFS_ROM_IOCTL_WRITE (4) // rom_ioctl(4, , , ) +#define MP_VFS_ROM_IOCTL_WRITE_COMPLETE (5) // rom_ioctl(5, ) + // At the moment the VFS protocol just has import_stat, but could be extended to other methods typedef struct _mp_vfs_proto_t { mp_import_stat_t (*import_stat)(void *self, const char *path); @@ -122,4 +129,12 @@ MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_stat_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj); +#if MICROPY_VFS_ROM_IOCTL +// When MICROPY_VFS_ROM_IOCTL is enabled a port must define the following function. +// This is a generic interface to allow querying and modifying the user-accessible, +// read-only memory area of a device, if it is configured with such an area. +// Supported ioctl commands are given by MP_VFS_ROM_IOCTL_xxx. +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args); +#endif + #endif // MICROPY_INCLUDED_EXTMOD_VFS_H diff --git a/ports/qemu/mpconfigport.h b/ports/qemu/mpconfigport.h index 0a25df4d0ac10..b02507277323e 100644 --- a/ports/qemu/mpconfigport.h +++ b/ports/qemu/mpconfigport.h @@ -64,6 +64,7 @@ #define MICROPY_PY_MACHINE_PIN_BASE (1) #define MICROPY_VFS (1) #define MICROPY_VFS_ROM (1) +#define MICROPY_VFS_ROM_IOCTL (0) // type definitions for the specific machine diff --git a/ports/unix/variants/mpconfigvariant_common.h b/ports/unix/variants/mpconfigvariant_common.h index 093477af0aded..9eeed8797366c 100644 --- a/ports/unix/variants/mpconfigvariant_common.h +++ b/ports/unix/variants/mpconfigvariant_common.h @@ -123,3 +123,4 @@ #define MICROPY_PY_MACHINE_PIN_BASE (1) #define MICROPY_VFS_ROM (1) +#define MICROPY_VFS_ROM_IOCTL (0) diff --git a/py/mpconfig.h b/py/mpconfig.h index 66b3d125e78de..05f39b3605bf3 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1006,6 +1006,11 @@ typedef double mp_float_t; #define MICROPY_VFS_WRITABLE (1) #endif +// Whether to enable the mp_vfs_rom_ioctl C function, and vfs.rom_ioctl Python function +#ifndef MICROPY_VFS_ROM_IOCTL +#define MICROPY_VFS_ROM_IOCTL (MICROPY_VFS_ROM) +#endif + // Support for VFS POSIX component, to mount a POSIX filesystem within VFS #ifndef MICROPY_VFS_POSIX #define MICROPY_VFS_POSIX (0) From d4b8ca2ffc3967007297d17e995d955ca39b1d6d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 21 Feb 2025 00:37:49 +1100 Subject: [PATCH 356/546] extmod/vfs: Add mp_vfs_mount_romfs_protected() helper. This function will attempt to create a `VfsRom` instance and mount it at location "/rom" in the filesystem. Signed-off-by: Damien George --- extmod/vfs.c | 30 ++++++++++++++++++++++++++++++ extmod/vfs.h | 3 +++ py/qstrdefs.h | 5 +++++ 3 files changed, 38 insertions(+) diff --git a/extmod/vfs.c b/extmod/vfs.c index b9c5ab0fc3a8c..c83b8ce7d563e 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -46,6 +46,10 @@ #include "extmod/vfs_posix.h" #endif +#if MICROPY_VFS_ROM && MICROPY_VFS_ROM_IOCTL +#include "extmod/vfs_rom.h" +#endif + // For mp_vfs_proxy_call, the maximum number of additional args that can be passed. // A fixed maximum size is used to avoid the need for a costly variable array. #define PROXY_MAX_ARGS (2) @@ -552,6 +556,32 @@ int mp_vfs_mount_and_chdir_protected(mp_obj_t bdev, mp_obj_t mount_point) { return ret; } +#if MICROPY_VFS_ROM && MICROPY_VFS_ROM_IOCTL + +int mp_vfs_mount_romfs_protected(void) { + int ret; + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(MP_VFS_ROM_IOCTL_GET_SEGMENT), MP_OBJ_NEW_SMALL_INT(0) }; + mp_obj_t rom = mp_vfs_rom_ioctl(2, args); + mp_obj_t romfs = mp_call_function_1(MP_OBJ_FROM_PTR(&mp_type_vfs_rom), rom); + mp_obj_t mount_point = MP_OBJ_NEW_QSTR(MP_QSTR__slash_rom); + mp_call_function_2(MP_OBJ_FROM_PTR(&mp_vfs_mount_obj), romfs, mount_point); + #if MICROPY_PY_SYS_PATH_ARGV_DEFAULTS + // Add "/rom" and "/rom/lib" to `sys.path`. + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_rom)); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_rom_slash_lib)); + #endif + ret = 0; // success + nlr_pop(); + } else { + ret = -MP_EIO; + } + return ret; +} + +#endif + MP_REGISTER_ROOT_POINTER(struct _mp_vfs_mount_t *vfs_cur); MP_REGISTER_ROOT_POINTER(struct _mp_vfs_mount_t *vfs_mount_table); diff --git a/extmod/vfs.h b/extmod/vfs.h index cc9c50d290051..3acc09db12283 100644 --- a/extmod/vfs.h +++ b/extmod/vfs.h @@ -112,6 +112,9 @@ mp_obj_t mp_vfs_stat(mp_obj_t path_in); mp_obj_t mp_vfs_statvfs(mp_obj_t path_in); int mp_vfs_mount_and_chdir_protected(mp_obj_t bdev, mp_obj_t mount_point); +#if MICROPY_VFS_ROM && MICROPY_VFS_ROM_IOCTL +int mp_vfs_mount_romfs_protected(void); +#endif MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_umount_obj); diff --git a/py/qstrdefs.h b/py/qstrdefs.h index 5003636df3d4b..0b50d279f9dbf 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -68,6 +68,11 @@ Q(utf-8) Q(.frozen) #endif +#if MICROPY_VFS_ROM && MICROPY_VFS_ROM_IOCTL +Q(/rom) +Q(/rom/lib) +#endif + #if MICROPY_ENABLE_PYSTACK Q(pystack exhausted) #endif From 840b641024cffd0b4e3f941e53c699c699c21811 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 21 Feb 2025 11:34:09 +1100 Subject: [PATCH 357/546] py/runtime: Automatically mount ROMFS as part of mp_init. This is put in `mp_init()` to make it consistent across all ports. Signed-off-by: Damien George --- py/runtime.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/py/runtime.c b/py/runtime.c index bf26921f9ab9f..58819819ad025 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -46,6 +46,10 @@ #include "py/cstack.h" #include "py/gc.h" +#if MICROPY_VFS_ROM && MICROPY_VFS_ROM_IOCTL +#include "extmod/vfs.h" +#endif + #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) #define DEBUG_printf DEBUG_printf @@ -185,6 +189,11 @@ void mp_init(void) { #endif MP_THREAD_GIL_ENTER(); + + #if MICROPY_VFS_ROM && MICROPY_VFS_ROM_IOCTL + // Mount ROMFS if it exists. + mp_vfs_mount_romfs_protected(); + #endif } void mp_deinit(void) { From 0c98c60b68bf338c695d43e41b15def59949eb39 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Mar 2022 10:57:46 +1100 Subject: [PATCH 358/546] tools/mpremote: Add romfs query, build and deploy commands. These commands use the `vfs.rom_ioctl()` function to manage the ROM partitions on a device, and create and deploy ROMFS images. Signed-off-by: Damien George --- docs/reference/mpremote.rst | 24 ++++ tools/mpremote/mpremote/commands.py | 183 +++++++++++++++++++++++++++- tools/mpremote/mpremote/main.py | 31 +++++ tools/mpremote/mpremote/romfs.py | 148 ++++++++++++++++++++++ 4 files changed, 385 insertions(+), 1 deletion(-) create mode 100644 tools/mpremote/mpremote/romfs.py diff --git a/docs/reference/mpremote.rst b/docs/reference/mpremote.rst index d4c6983595fec..ef23cd85c21a3 100644 --- a/docs/reference/mpremote.rst +++ b/docs/reference/mpremote.rst @@ -78,6 +78,7 @@ The full list of supported commands are: - `mip ` - `mount ` - `unmount ` +- `romfs ` - `rtc ` - `sleep ` - `reset ` @@ -347,6 +348,29 @@ The full list of supported commands are: This happens automatically when ``mpremote`` terminates, but it can be used in a sequence to unmount an earlier mount before subsequent command are run. +.. _mpremote_command_romfs: + +- **romfs** -- manage ROMFS partitions on the device: + + .. code-block:: bash + + $ mpremote romfs + + ```` may be: + + - ``romfs query`` to list all the available ROMFS partitions and their size + - ``romfs [-o ] build `` to create a ROMFS image from the given + source directory; the default output file is the source appended by ``.romfs`` + - ``romfs [-p ] deploy `` to deploy a ROMFS image to the device; + will also create a temporary ROMFS image if the source is a directory + + The ``build`` and ``deploy`` sub-commands both support the ``-m``/``--mpy`` option + to automatically compile ``.py`` files to ``.mpy`` when creating the ROMFS image. + This option is enabled by default, but only works if the ``mpy_cross`` Python + package has been installed (eg via ``pip install mpy_cross``). If the package is + not installed then a warning is printed and ``.py`` files remain as is. Compiling + of ``.py`` files can be disabled with the ``--no-mpy`` option. + .. _mpremote_command_rtc: - **rtc** -- set/get the device clock (RTC): diff --git a/tools/mpremote/mpremote/commands.py b/tools/mpremote/mpremote/commands.py index f86befd080349..7dd448c8b771c 100644 --- a/tools/mpremote/mpremote/commands.py +++ b/tools/mpremote/mpremote/commands.py @@ -1,12 +1,15 @@ +import binascii import hashlib import os import sys import tempfile +import zlib import serial.tools.list_ports -from .transport import TransportError, stdout_write_bytes +from .transport import TransportError, TransportExecError, stdout_write_bytes from .transport_serial import SerialTransport +from .romfs import make_romfs class CommandError(Exception): @@ -478,3 +481,181 @@ def do_rtc(state, args): state.transport.exec("machine.RTC().datetime({})".format(timetuple)) else: print(state.transport.eval("machine.RTC().datetime()")) + + +def _do_romfs_query(state, args): + state.ensure_raw_repl() + state.did_action() + + # Detect the romfs and get its associated device. + state.transport.exec("import vfs") + if not state.transport.eval("hasattr(vfs,'rom_ioctl')"): + print("ROMFS is not enabled on this device") + return + num_rom_partitions = state.transport.eval("vfs.rom_ioctl(1)") + if num_rom_partitions <= 0: + print("No ROMFS partitions available") + return + + for rom_id in range(num_rom_partitions): + state.transport.exec(f"dev=vfs.rom_ioctl(2,{rom_id})") + has_object = state.transport.eval("hasattr(dev,'ioctl')") + if has_object: + rom_block_count = state.transport.eval("dev.ioctl(4,0)") + rom_block_size = state.transport.eval("dev.ioctl(5,0)") + rom_size = rom_block_count * rom_block_size + print( + f"ROMFS{rom_id} partition has size {rom_size} bytes ({rom_block_count} blocks of {rom_block_size} bytes each)" + ) + else: + rom_size = state.transport.eval("len(dev)") + print(f"ROMFS{rom_id} partition has size {rom_size} bytes") + romfs = state.transport.eval("bytes(memoryview(dev)[:12])") + print(f" Raw contents: {romfs.hex(':')} ...") + if not romfs.startswith(b"\xd2\xcd\x31"): + print(" Not a valid ROMFS") + else: + size = 0 + for value in romfs[3:]: + size = (size << 7) | (value & 0x7F) + if not value & 0x80: + break + print(f" ROMFS image size: {size}") + + +def _do_romfs_build(state, args): + state.did_action() + + if args.path is None: + raise CommandError("romfs build: source path not given") + + input_directory = args.path + + if args.output is None: + output_file = input_directory + ".romfs" + else: + output_file = args.output + + romfs = make_romfs(input_directory, mpy_cross=args.mpy) + + print(f"Writing {len(romfs)} bytes to output file {output_file}") + with open(output_file, "wb") as f: + f.write(romfs) + + +def _do_romfs_deploy(state, args): + state.ensure_raw_repl() + state.did_action() + transport = state.transport + + if args.path is None: + raise CommandError("romfs deploy: source path not given") + + rom_id = args.partition + romfs_filename = args.path + + # Read in or create the ROMFS filesystem image. + if romfs_filename.endswith(".romfs"): + with open(romfs_filename, "rb") as f: + romfs = f.read() + else: + romfs = make_romfs(romfs_filename, mpy_cross=args.mpy) + print(f"Image size is {len(romfs)} bytes") + + # Detect the ROMFS partition and get its associated device. + state.transport.exec("import vfs") + if not state.transport.eval("hasattr(vfs,'rom_ioctl')"): + raise CommandError("ROMFS is not enabled on this device") + transport.exec(f"dev=vfs.rom_ioctl(2,{rom_id})") + if transport.eval("isinstance(dev,int) and dev<0"): + raise CommandError(f"ROMFS{rom_id} partition not found on device") + has_object = transport.eval("hasattr(dev,'ioctl')") + if has_object: + rom_block_count = transport.eval("dev.ioctl(4,0)") + rom_block_size = transport.eval("dev.ioctl(5,0)") + rom_size = rom_block_count * rom_block_size + print( + f"ROMFS{rom_id} partition has size {rom_size} bytes ({rom_block_count} blocks of {rom_block_size} bytes each)" + ) + else: + rom_size = transport.eval("len(dev)") + print(f"ROMFS{rom_id} partition has size {rom_size} bytes") + + # Check if ROMFS filesystem image will fit in the target partition. + if len(romfs) > rom_size: + print("ROMFS image is too big for the target partition") + sys.exit(1) + + # Prepare ROMFS partition for writing. + print(f"Preparing ROMFS{rom_id} partition for writing") + transport.exec("import vfs\ntry:\n vfs.umount('/rom')\nexcept:\n pass") + chunk_size = 4096 + if has_object: + for offset in range(0, len(romfs), rom_block_size): + transport.exec(f"dev.ioctl(6,{offset // rom_block_size})") + chunk_size = min(chunk_size, rom_block_size) + else: + rom_min_write = transport.eval(f"vfs.rom_ioctl(3,{rom_id},{len(romfs)})") + chunk_size = max(chunk_size, rom_min_write) + + # Detect capabilities of the device to use the fastest method of transfer. + has_bytes_fromhex = transport.eval("hasattr(bytes,'fromhex')") + try: + transport.exec("from binascii import a2b_base64") + has_a2b_base64 = True + except TransportExecError: + has_a2b_base64 = False + try: + transport.exec("from io import BytesIO") + transport.exec("from deflate import DeflateIO,RAW") + has_deflate_io = True + except TransportExecError: + has_deflate_io = False + + # Deploy the ROMFS filesystem image to the device. + for offset in range(0, len(romfs), chunk_size): + romfs_chunk = romfs[offset : offset + chunk_size] + romfs_chunk += bytes(chunk_size - len(romfs_chunk)) + if has_deflate_io: + # Needs: binascii.a2b_base64, io.BytesIO, deflate.DeflateIO. + romfs_chunk_compressed = zlib.compress(romfs_chunk, wbits=-9) + buf = binascii.b2a_base64(romfs_chunk_compressed).strip() + transport.exec(f"buf=DeflateIO(BytesIO(a2b_base64({buf})),RAW,9).read()") + elif has_a2b_base64: + # Needs: binascii.a2b_base64. + buf = binascii.b2a_base64(romfs_chunk) + transport.exec(f"buf=a2b_base64({buf})") + elif has_bytes_fromhex: + # Needs: bytes.fromhex. + buf = romfs_chunk.hex() + transport.exec(f"buf=bytes.fromhex('{buf}')") + else: + # Needs nothing special. + transport.exec("buf=" + repr(romfs_chunk)) + print(f"\rWriting at offset {offset}", end="") + if has_object: + transport.exec( + f"dev.writeblocks({offset // rom_block_size},buf,{offset % rom_block_size})" + ) + else: + transport.exec(f"vfs.rom_ioctl(4,{rom_id},{offset},buf)") + + # Complete writing. + if not has_object: + transport.eval(f"vfs.rom_ioctl(5,{rom_id})") + + print() + print("ROMFS image deployed") + + +def do_romfs(state, args): + if args.command[0] == "query": + _do_romfs_query(state, args) + elif args.command[0] == "build": + _do_romfs_build(state, args) + elif args.command[0] == "deploy": + _do_romfs_deploy(state, args) + else: + raise CommandError( + f"romfs: '{args.command[0]}' is not a command; pass romfs --help for a list" + ) diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index e6e397020fe85..bdae8f163656b 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -36,6 +36,7 @@ do_resume, do_rtc, do_soft_reset, + do_romfs, ) from .mip import do_mip from .repl import do_repl @@ -228,6 +229,32 @@ def argparse_mip(): return cmd_parser +def argparse_romfs(): + cmd_parser = argparse.ArgumentParser(description="manage ROM partitions") + _bool_flag( + cmd_parser, + "mpy", + "m", + True, + "automatically compile .py files to .mpy when building the ROMFS image (default)", + ) + cmd_parser.add_argument( + "--partition", + "-p", + type=int, + default=0, + help="ROMFS partition to use", + ) + cmd_parser.add_argument( + "--output", + "-o", + help="output file", + ) + cmd_parser.add_argument("command", nargs=1, help="romfs command, one of: query, build, deploy") + cmd_parser.add_argument("path", nargs="?", help="path to directory to deploy") + return cmd_parser + + def argparse_none(description): return lambda: argparse.ArgumentParser(description=description) @@ -302,6 +329,10 @@ def argparse_none(description): do_version, argparse_none("print version and exit"), ), + "romfs": ( + do_romfs, + argparse_romfs, + ), } # Additional commands aliases. diff --git a/tools/mpremote/mpremote/romfs.py b/tools/mpremote/mpremote/romfs.py new file mode 100644 index 0000000000000..ae781a36dfe62 --- /dev/null +++ b/tools/mpremote/mpremote/romfs.py @@ -0,0 +1,148 @@ +# MIT license; Copyright (c) 2022 Damien P. George + +import struct, sys, os + +try: + from mpy_cross import run as mpy_cross_run +except ImportError: + mpy_cross_run = None + + +class VfsRomWriter: + ROMFS_HEADER = b"\xd2\xcd\x31" + + ROMFS_RECORD_KIND_UNUSED = 0 + ROMFS_RECORD_KIND_PADDING = 1 + ROMFS_RECORD_KIND_DATA_VERBATIM = 2 + ROMFS_RECORD_KIND_DATA_POINTER = 3 + ROMFS_RECORD_KIND_DIRECTORY = 4 + ROMFS_RECORD_KIND_FILE = 5 + + def __init__(self): + self._dir_stack = [(None, bytearray())] + + def _encode_uint(self, value): + encoded = [value & 0x7F] + value >>= 7 + while value != 0: + encoded.insert(0, 0x80 | (value & 0x7F)) + value >>= 7 + return bytes(encoded) + + def _pack(self, kind, payload): + return self._encode_uint(kind) + self._encode_uint(len(payload)) + payload + + def _extend(self, data): + buf = self._dir_stack[-1][1] + buf.extend(data) + return len(buf) + + def finalise(self): + _, data = self._dir_stack.pop() + encoded_kind = VfsRomWriter.ROMFS_HEADER + encoded_len = self._encode_uint(len(data)) + if (len(encoded_kind) + len(encoded_len) + len(data)) % 2 == 1: + encoded_len = b"\x80" + encoded_len + data = encoded_kind + encoded_len + data + return data + + def opendir(self, dirname): + self._dir_stack.append((dirname, bytearray())) + + def closedir(self): + dirname, dirdata = self._dir_stack.pop() + dirdata = self._encode_uint(len(dirname)) + bytes(dirname, "ascii") + dirdata + self._extend(self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DIRECTORY, dirdata)) + + def mkdata(self, data): + assert len(self._dir_stack) == 1 + return self._extend(self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DATA_VERBATIM, data)) - len( + data + ) + + def mkfile(self, filename, filedata): + filename = bytes(filename, "ascii") + payload = self._encode_uint(len(filename)) + payload += filename + if isinstance(filedata, tuple): + sub_payload = self._encode_uint(filedata[0]) + sub_payload += self._encode_uint(filedata[1]) + payload += self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DATA_POINTER, sub_payload) + else: + payload += self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DATA_VERBATIM, filedata) + self._dir_stack[-1][1].extend(self._pack(VfsRomWriter.ROMFS_RECORD_KIND_FILE, payload)) + + +def copy_recursively(vfs, src_dir, print_prefix, mpy_cross): + assert src_dir.endswith("/") + DIR = 1 << 14 + mpy_cross_missed = 0 + dir_contents = sorted(os.listdir(src_dir)) + for name in dir_contents: + src_name = src_dir + name + st = os.stat(src_name) + + if name == dir_contents[-1]: + # Last entry in the directory listing. + print_entry = "\\--" + print_recurse = " " + else: + # Not the last entry in the directory listing. + print_entry = "|--" + print_recurse = "| " + + if st[0] & DIR: + # A directory, enter it and copy its contents recursively. + print(print_prefix + print_entry, name + "/") + vfs.opendir(name) + mpy_cross_missed += copy_recursively( + vfs, src_name + "/", print_prefix + print_recurse, mpy_cross + ) + vfs.closedir() + else: + # A file. + did_mpy = False + name_extra = "" + if mpy_cross and name.endswith(".py"): + name_mpy = name[:-3] + ".mpy" + src_name_mpy = src_dir + name_mpy + if not os.path.isfile(src_name_mpy): + if mpy_cross_run is not None: + did_mpy = True + proc = mpy_cross_run(src_name) + proc.wait() + else: + mpy_cross_missed += 1 + if did_mpy: + name_extra = " -> .mpy" + print(print_prefix + print_entry, name + name_extra) + if did_mpy: + name = name_mpy + src_name = src_name_mpy + with open(src_name, "rb") as src: + vfs.mkfile(name, src.read()) + if did_mpy: + os.remove(src_name_mpy) + return mpy_cross_missed + + +def make_romfs(src_dir, *, mpy_cross): + if not src_dir.endswith("/"): + src_dir += "/" + + vfs = VfsRomWriter() + + # Build the filesystem recursively. + print("Building romfs filesystem, source directory: {}".format(src_dir)) + print("/") + try: + mpy_cross_missed = copy_recursively(vfs, src_dir, "", mpy_cross) + except OSError as er: + print("Error: OSError {}".format(er), file=sys.stderr) + sys.exit(1) + + if mpy_cross_missed: + print("Warning: `mpy_cross` module not found, .py files were not precompiled") + mpy_cross = False + + return vfs.finalise() From bea7645b2e55881c4f42e6cfbe2a6433c5986794 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Mar 2022 10:56:50 +1100 Subject: [PATCH 359/546] stm32: Implement vfs.rom_ioctl with support for internal/external flash. This commit implements `vfs.rom_ioctl()` to query, erase and write both internal and external flash, depending on how the board configures its flash memory. A board can configure ROM as follows. To use internal flash memory: #define MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH (1) To use external flash memory (QSPI memory mapped): #define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) #define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_obj) Then the partition must be defined as symbols in the linker script: _micropy_hw_romfs_part1_start _micropy_hw_romfs_part1_size And finally the partition needs to be enabled: #define MICROPY_HW_ROMFS_ENABLE_PART1 (1) There's support for a second, optional partition via: _micropy_hw_romfs_part2_start _micropy_hw_romfs_part2_size #define MICROPY_HW_ROMFS_ENABLE_PART1 (1) Signed-off-by: Damien George --- ports/stm32/Makefile | 1 + ports/stm32/mpconfigboard_common.h | 20 ++++ ports/stm32/mpconfigport.h | 3 + ports/stm32/qspi.c | 2 - ports/stm32/qspi.h | 7 ++ ports/stm32/vfs_rom_ioctl.c | 161 +++++++++++++++++++++++++++++ 6 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 ports/stm32/vfs_rom_ioctl.c diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index a9612f1a5792e..8ac9a8af03d1a 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -291,6 +291,7 @@ SRC_C += \ storage.c \ sdcard.c \ sdram.c \ + vfs_rom_ioctl.c \ fatfs_port.c \ lcd.c \ accel.c \ diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 4ce0a75b87e2b..2a650077f3a45 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -67,6 +67,26 @@ #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (1) #endif +// Whether to enable ROMFS on the internal flash. +#ifndef MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH +#define MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH (0) +#endif + +// Whether to enable ROMFS on external QSPI flash. +#ifndef MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (0) +#endif + +// Whether to enable ROMFS partition 1. +#ifndef MICROPY_HW_ROMFS_ENABLE_PART1 +#define MICROPY_HW_ROMFS_ENABLE_PART1 (0) +#endif + +// Whether to enable ROMFS partition 2. +#ifndef MICROPY_HW_ROMFS_ENABLE_PART2 +#define MICROPY_HW_ROMFS_ENABLE_PART2 (0) +#endif + // Whether to enable storage on the internal flash of the MCU #ifndef MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE #define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1) diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 2b57446ee8233..da86fa641623a 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -79,6 +79,9 @@ #define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_VFS (1) +#ifndef MICROPY_VFS_ROM +#define MICROPY_VFS_ROM (MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH || MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI) +#endif // control over Python builtins #ifndef MICROPY_PY_BUILTINS_HELP_TEXT diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c index 98364db41d4fb..34359a1ccf838 100644 --- a/ports/stm32/qspi.c +++ b/ports/stm32/qspi.c @@ -34,8 +34,6 @@ #if defined(MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2) -#define QSPI_MAP_ADDR (0x90000000) - #ifndef MICROPY_HW_QSPI_PRESCALER #define MICROPY_HW_QSPI_PRESCALER 3 // F_CLK = F_AHB/3 (72MHz when CPU is 216MHz) #endif diff --git a/ports/stm32/qspi.h b/ports/stm32/qspi.h index c774b12582be6..6fe91168cfdba 100644 --- a/ports/stm32/qspi.h +++ b/ports/stm32/qspi.h @@ -28,9 +28,16 @@ #include "drivers/bus/qspi.h" +#define QSPI_MAP_ADDR (0x90000000) +#define QSPI_MAP_ADDR_MAX (0xa0000000) + extern const mp_qspi_proto_t qspi_proto; void qspi_init(void); void qspi_memory_map(void); +static inline bool qspi_is_valid_addr(uint32_t addr) { + return QSPI_MAP_ADDR <= addr && addr < QSPI_MAP_ADDR_MAX; +} + #endif // MICROPY_INCLUDED_STM32_QSPI_H diff --git a/ports/stm32/vfs_rom_ioctl.c b/ports/stm32/vfs_rom_ioctl.c new file mode 100644 index 0000000000000..5024d44e5adc8 --- /dev/null +++ b/ports/stm32/vfs_rom_ioctl.c @@ -0,0 +1,161 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" +#include "py/objarray.h" +#include "py/mperrno.h" +#include "extmod/vfs.h" +#include "drivers/memory/spiflash.h" + +#include "flash.h" +#include "qspi.h" +#include "storage.h" + +#if MICROPY_VFS_ROM_IOCTL + +#if MICROPY_HW_ROMFS_ENABLE_PART1 && !defined(MICROPY_HW_ROMFS_PART1_START) +#define MICROPY_HW_ROMFS_PART1_START (uintptr_t)(&_micropy_hw_romfs_part1_start) +#define MICROPY_HW_ROMFS_PART1_SIZE (uintptr_t)(&_micropy_hw_romfs_part1_size) +extern uint8_t _micropy_hw_romfs_part1_start; +extern uint8_t _micropy_hw_romfs_part1_size; +#endif + +#if MICROPY_HW_ROMFS_ENABLE_PART2 && !defined(MICROPY_HW_ROMFS_PART2_START) +#define MICROPY_HW_ROMFS_PART2_START (uintptr_t)(&_micropy_hw_romfs_part2_start) +#define MICROPY_HW_ROMFS_PART2_SIZE (uintptr_t)(&_micropy_hw_romfs_part2_size) +extern uint8_t _micropy_hw_romfs_part2_start; +extern uint8_t _micropy_hw_romfs_part2_size; +#endif + +#define ROMFS_MEMORYVIEW(base, size) {{&mp_type_memoryview}, 'B', 0, (size), (void *)(base)} + +static const mp_obj_array_t romfs_obj_table[] = { + #if MICROPY_HW_ROMFS_ENABLE_PART1 + ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART1_START, MICROPY_HW_ROMFS_PART1_SIZE), + #endif + #if MICROPY_HW_ROMFS_ENABLE_PART2 + ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART2_START, MICROPY_HW_ROMFS_PART2_SIZE), + #endif +}; + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + mp_int_t cmd = mp_obj_get_int(args[0]); + if (cmd == MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS) { + return MP_OBJ_NEW_SMALL_INT(MP_ARRAY_SIZE(romfs_obj_table)); + } + + if (n_args < 2) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + mp_int_t romfs_id = mp_obj_get_int(args[1]); + if (!(0 <= romfs_id && romfs_id < MP_ARRAY_SIZE(romfs_obj_table))) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + const mp_obj_array_t *romfs_obj = &romfs_obj_table[romfs_id]; + uintptr_t romfs_base = (uintptr_t)romfs_obj->items; + uintptr_t romfs_len = romfs_obj->len; + + if (cmd == MP_VFS_ROM_IOCTL_GET_SEGMENT) { + // Return the ROMFS memoryview object. + return MP_OBJ_FROM_PTR(romfs_obj); + } + + if (cmd == MP_VFS_ROM_IOCTL_WRITE_PREPARE) { + // Erase sectors in given range. + if (n_args < 3) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = romfs_base; + uint32_t dest_max = dest + mp_obj_get_int(args[2]); + if (dest_max > romfs_base + romfs_len) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + #if MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH + if (flash_is_valid_addr(dest)) { + while (dest < dest_max) { + int ret = flash_erase(dest); + if (ret < 0) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + uint32_t sector_size = 0; + flash_get_sector_info(dest, NULL, §or_size); + dest += sector_size; + } + return MP_OBJ_NEW_SMALL_INT(16); + } + #endif + + #if MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI + if (qspi_is_valid_addr(dest)) { + dest -= QSPI_MAP_ADDR; + dest_max -= QSPI_MAP_ADDR; + while (dest < dest_max) { + int ret = mp_spiflash_erase_block(MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ, dest); + if (ret < 0) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + dest += MP_SPIFLASH_ERASE_BLOCK_SIZE; + } + return MP_OBJ_NEW_SMALL_INT(4); + } + #endif + } + + if (cmd == MP_VFS_ROM_IOCTL_WRITE) { + // Write data to flash. + if (n_args < 4) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = romfs_base + mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + if (dest + bufinfo.len > romfs_base + romfs_len) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + #if MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH + if (flash_is_valid_addr(dest)) { + int ret = flash_write(dest, bufinfo.buf, bufinfo.len / 4); + return MP_OBJ_NEW_SMALL_INT(ret); + } + #endif + + #if MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI + if (qspi_is_valid_addr(dest)) { + dest -= QSPI_MAP_ADDR; + int ret = mp_spiflash_write(MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ, dest, bufinfo.len, bufinfo.buf); + return MP_OBJ_NEW_SMALL_INT(ret); + } + #endif + } + + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); +} + +#endif // MICROPY_VFS_ROM_IOCTL From 45c36f87ea025386c7d2f9b3c5d8916e54b70318 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 21 Feb 2025 00:45:13 +1100 Subject: [PATCH 360/546] stm32/boards: Enable ROMFS partitions on PYBD_SFx boards. Using unused and previously inaccessible external QSPI flash. Signed-off-by: Damien George --- ports/stm32/boards/PYBD_SF2/f722_qspi.ld | 7 ++++++- ports/stm32/boards/PYBD_SF2/mpconfigboard.h | 5 +++++ ports/stm32/boards/PYBD_SF6/f767.ld | 6 +++++- ports/stm32/boards/PYBD_SF6/mpconfigboard.h | 5 +++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/PYBD_SF2/f722_qspi.ld b/ports/stm32/boards/PYBD_SF2/f722_qspi.ld index 70d7f9cc4b8e4..05126233ec7ca 100644 --- a/ports/stm32/boards/PYBD_SF2/f722_qspi.ld +++ b/ports/stm32/boards/PYBD_SF2/f722_qspi.ld @@ -20,7 +20,8 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 480K /* sectors 2-7 */ - FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 2048K /* external QSPI */ + FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 1024K /* external QSPI */ + FLASH_ROMFS (rx): ORIGIN = 0x90100000, LENGTH = 1024K /* external QSPI */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K /* DTCM+SRAM1+SRAM2 */ } @@ -39,6 +40,10 @@ _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; +/* ROMFS location */ +_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); + /* Define output sections */ SECTIONS { diff --git a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h index f0ef67e77e604..e8923bf9b66e5 100644 --- a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h +++ b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h @@ -64,6 +64,11 @@ void board_sleep(int value); #define MICROPY_HW_RTC_USE_US (1) #define MICROPY_HW_RTC_USE_CALOUT (1) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) +#define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev2.spiflash) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // SPI flash #1, for R/W storage #define MICROPY_HW_SOFTQSPI_SCK_LOW(self) (GPIOE->BSRR = (0x10000 << 11)) #define MICROPY_HW_SOFTQSPI_SCK_HIGH(self) (GPIOE->BSRR = (1 << 11)) diff --git a/ports/stm32/boards/PYBD_SF6/f767.ld b/ports/stm32/boards/PYBD_SF6/f767.ld index 4262a48a9962c..68da1f19d40dc 100644 --- a/ports/stm32/boards/PYBD_SF6/f767.ld +++ b/ports/stm32/boards/PYBD_SF6/f767.ld @@ -18,7 +18,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 2016K /* sectors 1-11 3x32K 1*128K 7*256K */ - FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 2048K /* external QSPI */ + FLASH_ROMFS (rx): ORIGIN = 0x90000000, LENGTH = 2048K /* external QSPI */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 512K /* DTCM=128k, SRAM1=368K, SRAM2=16K */ } @@ -37,4 +37,8 @@ _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; +/* ROMFS location */ +_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); + INCLUDE common_bl.ld diff --git a/ports/stm32/boards/PYBD_SF6/mpconfigboard.h b/ports/stm32/boards/PYBD_SF6/mpconfigboard.h index 8c11d7b2eec96..526a111177987 100644 --- a/ports/stm32/boards/PYBD_SF6/mpconfigboard.h +++ b/ports/stm32/boards/PYBD_SF6/mpconfigboard.h @@ -45,6 +45,11 @@ #define MICROPY_HW_CLK_PLLQ (6) #define MICROPY_HW_FLASH_LATENCY (FLASH_LATENCY_4) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) +#define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev2.spiflash) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // Extra UART config #define MICROPY_HW_UART7_TX (pyb_pin_W16) #define MICROPY_HW_UART7_RX (pyb_pin_W22B) From 50a7362b3eff211a5051eeaecc88bdde045c90d1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Mar 2022 10:57:38 +1100 Subject: [PATCH 361/546] rp2: Implement vfs.rom_ioctl with support for external flash. Not enabled by default on any board. A board can enable a ROMFS partition by defining `MICROPY_HW_ROMFS_BYTES` in its `mpconfigboard.h` file. For example: #define MICROPY_HW_ROMFS_BYTES (128 * 1024) The ROMFS partition is placed at the end of the flash allocated for the firmware, giving less space for the firmware. It then lives between the firmware and the read/write filesystem. Signed-off-by: Damien George --- ports/rp2/mpconfigport.h | 11 ++++++++++ ports/rp2/rp2_flash.c | 45 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index fe8287ba1d6e5..38dcad1dae6e1 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -67,6 +67,16 @@ #endif #endif +// Number of bytes of flash to allocate to the ROMFS partition. +#ifndef MICROPY_HW_ROMFS_BYTES +#define MICROPY_HW_ROMFS_BYTES (0) +#endif + +// Number of bytes of flash to allocate to read/write filesystem storage. +#ifndef MICROPY_HW_FLASH_STORAGE_BYTES +#define MICROPY_HW_FLASH_STORAGE_BYTES (1408 * 1024) +#endif + #ifndef MICROPY_CONFIG_ROM_LEVEL #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) #endif @@ -170,6 +180,7 @@ #define MICROPY_VFS (1) #define MICROPY_VFS_LFS2 (1) #define MICROPY_VFS_FAT (1) +#define MICROPY_VFS_ROM (MICROPY_HW_ROMFS_BYTES > 0) #define MICROPY_SSL_MBEDTLS (1) #define MICROPY_PY_LWIP_PPP (MICROPY_PY_NETWORK_PPP_LWIP) #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c index a487fb1633b21..f3f154a1402d9 100644 --- a/ports/rp2/rp2_flash.c +++ b/ports/rp2/rp2_flash.c @@ -28,6 +28,7 @@ #include "py/mphal.h" #include "py/runtime.h" +#include "py/mperrno.h" #include "extmod/vfs.h" #include "modrp2.h" #include "hardware/flash.h" @@ -35,15 +36,16 @@ #define BLOCK_SIZE_BYTES (FLASH_SECTOR_SIZE) -#ifndef MICROPY_HW_FLASH_STORAGE_BYTES -#define MICROPY_HW_FLASH_STORAGE_BYTES (1408 * 1024) -#endif +static_assert(MICROPY_HW_ROMFS_BYTES % 4096 == 0, "ROMFS size must be a multiple of 4K"); static_assert(MICROPY_HW_FLASH_STORAGE_BYTES % 4096 == 0, "Flash storage size must be a multiple of 4K"); #ifndef MICROPY_HW_FLASH_STORAGE_BASE #define MICROPY_HW_FLASH_STORAGE_BASE (PICO_FLASH_SIZE_BYTES - MICROPY_HW_FLASH_STORAGE_BYTES) #endif +// Put ROMFS at the upper end of the code space. +#define MICROPY_HW_ROMFS_BASE (MICROPY_HW_FLASH_STORAGE_BASE - MICROPY_HW_ROMFS_BYTES) + static_assert(MICROPY_HW_FLASH_STORAGE_BYTES <= PICO_FLASH_SIZE_BYTES, "MICROPY_HW_FLASH_STORAGE_BYTES too big"); static_assert(MICROPY_HW_FLASH_STORAGE_BASE + MICROPY_HW_FLASH_STORAGE_BYTES <= PICO_FLASH_SIZE_BYTES, "MICROPY_HW_FLASH_STORAGE_BYTES too big"); @@ -53,6 +55,14 @@ typedef struct _rp2_flash_obj_t { uint32_t flash_size; } rp2_flash_obj_t; +#if MICROPY_HW_ROMFS_BYTES > 0 +static rp2_flash_obj_t rp2_flash_romfs_obj = { + .base = { &rp2_flash_type }, + .flash_base = MICROPY_HW_ROMFS_BASE, + .flash_size = MICROPY_HW_ROMFS_BYTES, +}; +#endif + static rp2_flash_obj_t rp2_flash_obj = { .base = { &rp2_flash_type }, .flash_base = MICROPY_HW_FLASH_STORAGE_BASE, @@ -138,6 +148,19 @@ static mp_obj_t rp2_flash_make_new(const mp_obj_type_t *type, size_t n_args, siz return MP_OBJ_FROM_PTR(self); } +static mp_int_t rp2_flash_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + rp2_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)(XIP_BASE + self->flash_base); + bufinfo->len = self->flash_size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Write unsupported. + return 1; + } +} + static mp_obj_t rp2_flash_readblocks(size_t n_args, const mp_obj_t *args) { rp2_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); uint32_t offset = mp_obj_get_int(args[1]) * BLOCK_SIZE_BYTES; @@ -218,5 +241,21 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_QSTR_Flash, MP_TYPE_FLAG_NONE, make_new, rp2_flash_make_new, + buffer, rp2_flash_get_buffer, locals_dict, &rp2_flash_locals_dict ); + +#if MICROPY_VFS_ROM_IOCTL +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + switch (mp_obj_get_int(args[0])) { + #if MICROPY_HW_ROMFS_BYTES > 0 + case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS: + return MP_OBJ_NEW_SMALL_INT(1); + case MP_VFS_ROM_IOCTL_GET_SEGMENT: + return MP_OBJ_FROM_PTR(&rp2_flash_romfs_obj); + #endif + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} +#endif From 0255cb77ccbdf09894e59523aecff24f2caa6352 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Mar 2022 18:05:06 +1100 Subject: [PATCH 362/546] esp32: Implement vfs.rom_ioctl with support for external flash. Not enabled by default on any board. For a board to enable ROMFS it must: - Add `#define MICROPY_VFS_ROM (1)` to its `mpconfigboard.h` file. - Use `partitions-4MiB-romfs.csv` as its partitions file (or a similar partitions definition that has an entry labelled "romfs"). Signed-off-by: Damien George --- ports/esp32/esp32_partition.c | 76 +++++++++++++++++++++++++++ ports/esp32/partitions-4MiB-romfs.csv | 8 +++ 2 files changed, 84 insertions(+) create mode 100644 ports/esp32/partitions-4MiB-romfs.csv diff --git a/ports/esp32/esp32_partition.c b/ports/esp32/esp32_partition.c index 55830a285b0b6..f33e9da671b8c 100644 --- a/ports/esp32/esp32_partition.c +++ b/ports/esp32/esp32_partition.c @@ -53,6 +53,20 @@ typedef struct _esp32_partition_obj_t { uint16_t block_size; } esp32_partition_obj_t; +#if MICROPY_VFS_ROM_IOCTL + +static esp32_partition_obj_t esp32_partition_romfs_obj = { + .base = { .type = NULL }, + .part = NULL, + .cache = NULL, + .block_size = NATIVE_BLOCK_SIZE_BYTES, +}; + +static const void *esp32_partition_romfs_ptr = NULL; +static esp_partition_mmap_handle_t esp32_partition_romfs_handle; + +#endif + static esp32_partition_obj_t *esp32_partition_new(const esp_partition_t *part, uint16_t block_size) { if (part == NULL) { mp_raise_OSError(MP_ENOENT); @@ -114,6 +128,24 @@ static mp_obj_t esp32_partition_make_new(const mp_obj_type_t *type, size_t n_arg return MP_OBJ_FROM_PTR(esp32_partition_new(part, block_size)); } +#if MICROPY_VFS_ROM_IOCTL +static mp_int_t esp32_partition_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self == &esp32_partition_romfs_obj && flags == MP_BUFFER_READ) { + if (esp32_partition_romfs_ptr == NULL) { + check_esp_err(esp_partition_mmap(self->part, 0, self->part->size, ESP_PARTITION_MMAP_DATA, &esp32_partition_romfs_ptr, &esp32_partition_romfs_handle)); + } + bufinfo->buf = (void *)esp32_partition_romfs_ptr; + bufinfo->len = self->part->size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Unsupported. + return 1; + } +} +#endif + static mp_obj_t esp32_partition_find(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // Parse args enum { ARG_type, ARG_subtype, ARG_label, ARG_block_size }; @@ -284,11 +316,55 @@ static const mp_rom_map_elem_t esp32_partition_locals_dict_table[] = { }; static MP_DEFINE_CONST_DICT(esp32_partition_locals_dict, esp32_partition_locals_dict_table); +#if MICROPY_VFS_ROM_IOCTL +#define ESP32_PARTITION_GET_BUFFER buffer, esp32_partition_get_buffer, +#else +#define ESP32_PARTITION_GET_BUFFER +#endif + MP_DEFINE_CONST_OBJ_TYPE( esp32_partition_type, MP_QSTR_Partition, MP_TYPE_FLAG_NONE, make_new, esp32_partition_make_new, print, esp32_partition_print, + ESP32_PARTITION_GET_BUFFER locals_dict, &esp32_partition_locals_dict ); + +/******************************************************************************/ +// romfs partition + +#if MICROPY_VFS_ROM_IOCTL + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + if (esp32_partition_romfs_obj.base.type == NULL) { + esp32_partition_romfs_obj.base.type = &esp32_partition_type; + // Get the romfs partition. + // TODO: number of segments ioctl can be used if there is more than one romfs. + esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "romfs"); + if (iter != NULL) { + esp32_partition_romfs_obj.part = esp_partition_get(iter); + } + esp_partition_iterator_release(iter); + } + + switch (mp_obj_get_int(args[0])) { + case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS: + if (esp32_partition_romfs_obj.part == NULL) { + return MP_OBJ_NEW_SMALL_INT(0); + } else { + return MP_OBJ_NEW_SMALL_INT(1); + } + case MP_VFS_ROM_IOCTL_GET_SEGMENT: + if (esp32_partition_romfs_obj.part == NULL) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } else { + return MP_OBJ_FROM_PTR(&esp32_partition_romfs_obj); + } + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} + +#endif // MICROPY_VFS_ROM_IOCTL diff --git a/ports/esp32/partitions-4MiB-romfs.csv b/ports/esp32/partitions-4MiB-romfs.csv new file mode 100644 index 0000000000000..dd02506e54684 --- /dev/null +++ b/ports/esp32/partitions-4MiB-romfs.csv @@ -0,0 +1,8 @@ +# Notes: the offset of the partition table itself is set in +# $IDF_PATH/components/partition_table/Kconfig.projbuild. +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 0x1D0000, +romfs, data, 0x8f, 0x1E0000, 0x20000, +vfs, data, fat, 0x200000, 0x200000, From 75ff8e5465c766bad0c79d1a5d98170f27f7070b Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 15 Nov 2024 11:18:09 +1100 Subject: [PATCH 363/546] esp8266: Implement vfs.rom_ioctl with support for external flash. Not enabled by default on any board. For a board to enable ROMFS it must: - Add `#define MICROPY_VFS_ROM (1)` to its `mpconfigboard.h` file. - Add a FLASH_ROMFS partition to the linker script and expose the partition with: _micropy_hw_romfs_start = ORIGIN(FLASH_ROMFS); _micropy_hw_romfs_size = LENGTH(FLASH_ROMFS); Signed-off-by: Damien George --- ports/esp8266/Makefile | 1 + ports/esp8266/boards/esp8266_common.ld | 1 + ports/esp8266/modesp.c | 9 ++- ports/esp8266/vfs_rom_ioctl.c | 89 ++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 ports/esp8266/vfs_rom_ioctl.c diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index 1721075eae172..1c9de01503abd 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -134,6 +134,7 @@ SRC_C = \ fatfs_port.c \ posix_helpers.c \ hspi.c \ + vfs_rom_ioctl.c \ $(wildcard $(BOARD_DIR)/*.c) \ ifeq ($(MICROPY_PY_ESPNOW),1) diff --git a/ports/esp8266/boards/esp8266_common.ld b/ports/esp8266/boards/esp8266_common.ld index cf4883acc9d20..206c3f14b430e 100644 --- a/ports/esp8266/boards/esp8266_common.ld +++ b/ports/esp8266/boards/esp8266_common.ld @@ -170,6 +170,7 @@ SECTIONS *modonewire.o(.literal* .text*) *network_wlan.o(.literal* .text*) *esp_mphal.o(.literal* .text*) + *vfs_rom_ioctl.o(.literal* .text*) /* we put as much rodata as possible in this section */ /* note that only rodata accessed as a machine word is allowed here */ diff --git a/ports/esp8266/modesp.c b/ports/esp8266/modesp.c index f303da1a33289..c88022945cfe6 100644 --- a/ports/esp8266/modesp.c +++ b/ports/esp8266/modesp.c @@ -164,9 +164,16 @@ static MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_size_obj, esp_flash_size); #define IS_OTA_FIRMWARE() ((*(uint32_t *)0x40200000 & 0xff00) == 0x100) extern byte _firmware_size[]; +#if MICROPY_VFS_ROM_IOCTL +extern uint8_t _micropy_hw_romfs_size; +#endif static mp_obj_t esp_flash_user_start(void) { - return MP_OBJ_NEW_SMALL_INT((uint32_t)_firmware_size); + uint32_t flash_user_start = (uint32_t)_firmware_size; + #if MICROPY_VFS_ROM_IOCTL + flash_user_start += (uint32_t)&_micropy_hw_romfs_size; + #endif + return MP_OBJ_NEW_SMALL_INT(flash_user_start); } static MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_user_start_obj, esp_flash_user_start); diff --git a/ports/esp8266/vfs_rom_ioctl.c b/ports/esp8266/vfs_rom_ioctl.c new file mode 100644 index 0000000000000..c7528375becd0 --- /dev/null +++ b/ports/esp8266/vfs_rom_ioctl.c @@ -0,0 +1,89 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mperrno.h" +#include "py/objarray.h" +#include "extmod/vfs.h" +#include "ets_alt_task.h" +#include "user_interface.h" + +#if MICROPY_VFS_ROM_IOCTL + +#define FLASH_MEM_BASE (0x40200000) +#define FLASH_PAGE_SIZE (4096) + +#define MICROPY_HW_ROMFS_BASE (uintptr_t)(&_micropy_hw_romfs_start) +#define MICROPY_HW_ROMFS_BYTES (uintptr_t)(&_micropy_hw_romfs_size) + +#define ROMFS_SPI_FLASH_OFFSET (MICROPY_HW_ROMFS_BASE - FLASH_MEM_BASE) + +extern uint8_t _micropy_hw_romfs_start; +extern uint8_t _micropy_hw_romfs_size; + +static const MP_DEFINE_MEMORYVIEW_OBJ(romfs_obj, 'B', 0, MICROPY_HW_ROMFS_BYTES, (void *)MICROPY_HW_ROMFS_BASE); + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + switch (mp_obj_get_int(args[0])) { + case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS: + return MP_OBJ_NEW_SMALL_INT(1); + + case MP_VFS_ROM_IOCTL_GET_SEGMENT: + return MP_OBJ_FROM_PTR(&romfs_obj); + + case MP_VFS_ROM_IOCTL_WRITE_PREPARE: { + uint32_t dest = ROMFS_SPI_FLASH_OFFSET; + uint32_t dest_max = dest + mp_obj_get_int(args[2]); + while (dest < dest_max) { + ets_loop_iter(); // flash access takes time so run any pending tasks + SpiFlashOpResult res = spi_flash_erase_sector(dest / FLASH_PAGE_SIZE); + ets_loop_iter(); + if (res != SPI_FLASH_RESULT_OK) { + return MP_OBJ_NEW_SMALL_INT(res == SPI_FLASH_RESULT_TIMEOUT ? -MP_ETIMEDOUT : -MP_EIO); + } + dest += FLASH_PAGE_SIZE; + } + return MP_OBJ_NEW_SMALL_INT(4); // minimum write size + } + + case MP_VFS_ROM_IOCTL_WRITE: { + mp_int_t offset = ROMFS_SPI_FLASH_OFFSET + mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + ets_loop_iter(); // flash access takes time so run any pending tasks + SpiFlashOpResult res = spi_flash_write(offset, bufinfo.buf, (bufinfo.len + 3) & ~3); + ets_loop_iter(); + if (res == SPI_FLASH_RESULT_OK) { + return MP_OBJ_NEW_SMALL_INT(0); + } + return MP_OBJ_NEW_SMALL_INT(res == SPI_FLASH_RESULT_TIMEOUT ? -MP_ETIMEDOUT : -MP_EIO); + } + + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} + +#endif // MICROPY_VFS_ROM_IOCTL From 6bec36a4eec999eccc6aadf0de782ebf69454ced Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 1 Mar 2025 17:22:03 +1100 Subject: [PATCH 364/546] esp8266/boards: Add FLASH_2M_ROMFS variant with 320k ROM partition. The same as the 2M flash variant but with a 320KiB ROM partition. Signed-off-by: Damien George --- .../esp8266/boards/ESP8266_GENERIC/board.json | 3 ++- .../mpconfigvariant_FLASH_2M_ROMFS.mk | 12 ++++++++++ ports/esp8266/boards/esp8266_2MiB_ROMFS.ld | 24 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_2M_ROMFS.mk create mode 100644 ports/esp8266/boards/esp8266_2MiB_ROMFS.ld diff --git a/ports/esp8266/boards/ESP8266_GENERIC/board.json b/ports/esp8266/boards/ESP8266_GENERIC/board.json index 2b3a6b5507db8..8f2aa1240f280 100644 --- a/ports/esp8266/boards/ESP8266_GENERIC/board.json +++ b/ports/esp8266/boards/ESP8266_GENERIC/board.json @@ -17,7 +17,8 @@ "variants": { "OTA": "OTA compatible", "FLASH_1M": "1MiB flash", - "FLASH_512K": "512kiB flash" + "FLASH_512K": "512kiB flash", + "FLASH_2M_ROMFS": "2MiB flash with ROMFS" }, "vendor": "Espressif" } diff --git a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_2M_ROMFS.mk b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_2M_ROMFS.mk new file mode 100644 index 0000000000000..9ee3566d8bf35 --- /dev/null +++ b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_FLASH_2M_ROMFS.mk @@ -0,0 +1,12 @@ +LD_FILES = boards/esp8266_2MiB_ROMFS.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 -DMICROPY_VFS_ROM=1 diff --git a/ports/esp8266/boards/esp8266_2MiB_ROMFS.ld b/ports/esp8266/boards/esp8266_2MiB_ROMFS.ld new file mode 100644 index 0000000000000..6020c3d485da6 --- /dev/null +++ b/ports/esp8266/boards/esp8266_2MiB_ROMFS.ld @@ -0,0 +1,24 @@ +/* GNU linker script for ESP8266 with 2M or more flash, and includes a ROMFS partition + + Flash layout: + 0x40200000 36k header + iram/dram init + 0x40209000 668k firmware (irom0) + 0x402c0000 320k ROMFS + 0x40300000 1M+ filesystem (not memory mapped) +*/ + +MEMORY +{ + dport0_0_seg : org = 0x3ff00000, len = 16 + dram0_0_seg : org = 0x3ffe8000, len = 80K + iram1_0_seg : org = 0x40100000, len = 32K + irom0_0_seg : org = 0x40209000, len = 1M - 36K - 320K + FLASH_ROMFS : org = 0x402b0000, len = 320K +} + +/* define ROMFS extents */ +_micropy_hw_romfs_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_size = LENGTH(FLASH_ROMFS); + +/* define common sections and symbols */ +INCLUDE boards/esp8266_common.ld From be0fce942996af576acd2ac639a4be92f6c4eeb7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 4 Mar 2025 22:46:33 +1100 Subject: [PATCH 365/546] unix/main: Add coverage test for mounting ROMFS filesystem at startup. Signed-off-by: Damien George --- ports/unix/main.c | 27 +++++++++++++++++++ .../unix/variants/coverage/mpconfigvariant.h | 2 ++ 2 files changed, 29 insertions(+) diff --git a/ports/unix/main.c b/ports/unix/main.c index 58fa3ff589a55..0d137fe1b58ea 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -45,6 +45,7 @@ #include "py/gc.h" #include "py/objstr.h" #include "py/cstack.h" +#include "py/mperrno.h" #include "py/mphal.h" #include "py/mpthread.h" #include "extmod/misc.h" @@ -548,7 +549,14 @@ MP_NOINLINE int main_(int argc, char **argv) { MP_OBJ_NEW_QSTR(MP_QSTR__slash_), }; mp_vfs_mount(2, args, (mp_map_t *)&mp_const_empty_map); + + // Make sure the root that was just mounted is the current VFS (it's always at + // the end of the linked list). Can't use chdir('/') because that will change + // the current path within the VfsPosix object. MP_STATE_VM(vfs_cur) = MP_STATE_VM(vfs_mount_table); + while (MP_STATE_VM(vfs_cur)->next != NULL) { + MP_STATE_VM(vfs_cur) = MP_STATE_VM(vfs_cur)->next; + } } #endif @@ -803,3 +811,22 @@ void nlr_jump_fail(void *val) { fprintf(stderr, "FATAL: uncaught NLR %p\n", val); exit(1); } + +#if MICROPY_VFS_ROM_IOCTL + +static uint8_t romfs_buf[4] = { 0xd2, 0xcd, 0x31, 0x00 }; // empty ROMFS +static const MP_DEFINE_MEMORYVIEW_OBJ(romfs_obj, 'B', 0, sizeof(romfs_buf), romfs_buf); + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + switch (mp_obj_get_int(args[0])) { + case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS: + return MP_OBJ_NEW_SMALL_INT(1); + + case MP_VFS_ROM_IOCTL_GET_SEGMENT: + return MP_OBJ_FROM_PTR(&romfs_obj); + } + + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); +} + +#endif diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index e24e0f1b14ade..a72e533e74c4a 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -41,4 +41,6 @@ #define MICROPY_DEBUG_PARSE_RULE_NAME (1) #define MICROPY_TRACKED_ALLOC (1) #define MICROPY_WARNINGS_CATEGORY (1) +#undef MICROPY_VFS_ROM_IOCTL +#define MICROPY_VFS_ROM_IOCTL (1) #define MICROPY_PY_CRYPTOLIB_CTR (1) From 3823aeb0f14c04084eba6566164d9b7dbd9e7ced Mon Sep 17 00:00:00 2001 From: danicampora Date: Fri, 21 Feb 2025 14:51:45 +0100 Subject: [PATCH 366/546] zephyr/machine_timer: Add machine.Timer class implementation. Simple `machine.Timer` implementation in-line with the rest of the MicroPython ports. Note: Only virtual timers are supported (not linked to any particular hardware peripheral). Tested with the nRF5340 and the nRF52840. Signed-off-by: danicampora --- ports/zephyr/CMakeLists.txt | 1 + ports/zephyr/machine_timer.c | 201 +++++++++++++++++++++++++++++++++++ ports/zephyr/modmachine.c | 1 + 3 files changed, 203 insertions(+) create mode 100644 ports/zephyr/machine_timer.c diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index 68b6c0700b546..b955089066749 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -40,6 +40,7 @@ set(MICROPY_SOURCE_PORT machine_i2c.c machine_spi.c machine_pin.c + machine_timer.c modbluetooth_zephyr.c modsocket.c modzephyr.c diff --git a/ports/zephyr/machine_timer.c b/ports/zephyr/machine_timer.c new file mode 100644 index 0000000000000..8ab2f629131c0 --- /dev/null +++ b/ports/zephyr/machine_timer.c @@ -0,0 +1,201 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Daniel Campora on behalf of REMOTE TECH LTD + * + * 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/mperrno.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "modmachine.h" +#include +#include +#include + +#define TIMER_MS_PER_TICK (1000) + +typedef enum _machine_timer_mode_t { + TIMER_MODE_ONE_SHOT = 0, + TIMER_MODE_PERIODIC +} machine_timer_mode_t; + +typedef struct _machine_timer_obj_t { + mp_obj_base_t base; + + struct k_timer my_timer; + + machine_timer_mode_t mode; + uint32_t period_ms; + + mp_obj_t callback; + + struct _machine_timer_obj_t *next; +} machine_timer_obj_t; + +const mp_obj_type_t machine_timer_type; + +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); +static mp_obj_t machine_timer_deinit(mp_obj_t self_in); + +static void machine_timer_callback(struct k_timer *timer) { + machine_timer_obj_t *self = (machine_timer_obj_t *)k_timer_user_data_get(timer); + if (self->mode == TIMER_MODE_ONE_SHOT) { + machine_timer_deinit(self); + } + if (self->callback != mp_const_none) { + mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self)); + } +} + +static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_timer_obj_t *self = self_in; + qstr mode_str = (self->mode == TIMER_MODE_PERIODIC) ? MP_QSTR_PERIODIC : MP_QSTR_ONE_SHOT; + mp_printf(print, "Timer(-1, mode=%q, period=%lu)", mode_str, self->period_ms); +} + +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 (mp_obj_get_int(args[0]) != -1) { + mp_raise_ValueError(MP_ERROR_TEXT("only virtual timers are supported")); + } + + // Create the new timer. + machine_timer_obj_t *self = mp_obj_malloc_with_finaliser(machine_timer_obj_t, &machine_timer_type); + + // Add the timer to the linked-list of timers + self->next = MP_STATE_PORT(machine_timer_obj_head); + MP_STATE_PORT(machine_timer_obj_head) = self; + + if (n_args > 1 || n_kw > 0) { + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_timer_init_helper(self, n_args - 1, args + 1, &kw_args); + } + return 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) { + enum { + ARG_mode, + ARG_callback, + ARG_period, + ARG_freq, + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIMER_MODE_PERIODIC} }, + { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, + #if MICROPY_PY_BUILTINS_FLOAT + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + #else + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, + #endif + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + #if MICROPY_PY_BUILTINS_FLOAT + if (args[ARG_freq].u_obj != mp_const_none) { + self->period_ms = (uint32_t)(TIMER_MS_PER_TICK / mp_obj_get_float(args[ARG_freq].u_obj)); + } + #else + if (args[ARG_freq].u_int != 0xffffffff) { + self->period_ms = TIMER_MS_PER_TICK / ((uint64_t)args[ARG_freq].u_int); + } + #endif + else { + self->period_ms = args[ARG_period].u_int; + } + + self->mode = args[ARG_mode].u_int; + self->callback = args[ARG_callback].u_obj; + + k_timer_init(&self->my_timer, machine_timer_callback, NULL); + k_timer_user_data_set(&self->my_timer, self); + k_timer_start(&self->my_timer, K_MSEC(self->period_ms), K_MSEC(self->period_ms)); + + return mp_const_none; +} + +static mp_obj_t machine_timer_deinit(mp_obj_t self_in) { + machine_timer_obj_t *self = self_in; + machine_timer_obj_t *prev = NULL; + + k_timer_stop(&self->my_timer); + + // remove it from the linked list + for (machine_timer_obj_t *_timer = MP_STATE_PORT(machine_timer_obj_head); _timer != NULL; _timer = _timer->next) { + if (_timer == self) { + if (prev != NULL) { + prev->next = _timer->next; + } else { + // move the start pointer + MP_STATE_PORT(machine_timer_obj_head) = _timer->next; + } + break; + } else { + prev = _timer; + } + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_deinit_obj, machine_timer_deinit); + +static mp_obj_t machine_timer_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return machine_timer_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init); + +static mp_obj_t machine_timer_value(mp_obj_t self_in) { + machine_timer_obj_t *self = self_in; + k_ticks_t ticks = k_timer_remaining_ticks(&self->my_timer); + return MP_OBJ_NEW_SMALL_INT(k_ticks_to_ms_near32(ticks)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_value_obj, machine_timer_value); + +static const mp_rom_map_elem_t machine_timer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_timer_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_timer_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_timer_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_timer_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_ONE_SHOT), MP_ROM_INT(TIMER_MODE_ONE_SHOT) }, + { MP_ROM_QSTR(MP_QSTR_PERIODIC), MP_ROM_INT(TIMER_MODE_PERIODIC) }, +}; +static MP_DEFINE_CONST_DICT(machine_timer_locals_dict, machine_timer_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_timer_type, + MP_QSTR_Timer, + MP_TYPE_FLAG_NONE, + make_new, machine_timer_make_new, + print, machine_timer_print, + locals_dict, &machine_timer_locals_dict + ); + +MP_REGISTER_ROOT_POINTER(struct _machine_timer_obj_t *machine_timer_obj_head); diff --git a/ports/zephyr/modmachine.c b/ports/zephyr/modmachine.c index bbb9280a5dcb9..89ad12f185daa 100644 --- a/ports/zephyr/modmachine.c +++ b/ports/zephyr/modmachine.c @@ -44,6 +44,7 @@ MICROPY_PY_MACHINE_RESET_ENTRY \ { MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) }, \ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ + { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, \ static mp_obj_t machine_reset(void) { sys_reboot(SYS_REBOOT_COLD); From 0ec496a98ca7ba0d81c877505fa7673bab5be75c Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 12 Mar 2025 13:07:25 +1100 Subject: [PATCH 367/546] lib/cyw43-driver: Update driver to latest version v1.1.0. Includes various fixes and improvements to the WLAN driver, in particular: - Add WPA3 STA and AP support. - Attempt to reconnect to AP in response to validation error. - Update 43439 BT firmware for Data Length Extension fix. Signed-off-by: Damien George --- lib/cyw43-driver | 2 +- ports/mimxrt/cyw43_configport.h | 2 +- ports/rp2/cyw43_configport.h | 1 + ports/stm32/cyw43_configport.h | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/cyw43-driver b/lib/cyw43-driver index 9f6405f0b3260..dd7568229f3bf 160000 --- a/lib/cyw43-driver +++ b/lib/cyw43-driver @@ -1 +1 @@ -Subproject commit 9f6405f0b3260968306d782e1c5ac275a46dc65d +Subproject commit dd7568229f3bf7a37737b9e1ef250c26efe75b23 diff --git a/ports/mimxrt/cyw43_configport.h b/ports/mimxrt/cyw43_configport.h index 619db24b1b579..ab535a477763d 100644 --- a/ports/mimxrt/cyw43_configport.h +++ b/ports/mimxrt/cyw43_configport.h @@ -107,7 +107,7 @@ #endif #if MICROPY_HW_ENABLE_RF_SWITCH -#define CYW43_PIN_WL_RFSW_VDD pin_WL_RFSW_VDD +#define CYW43_PIN_RFSW_VDD pin_WL_RFSW_VDD #endif #define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) diff --git a/ports/rp2/cyw43_configport.h b/ports/rp2/cyw43_configport.h index 4294691c66657..bb99bb2976fee 100644 --- a/ports/rp2/cyw43_configport.h +++ b/ports/rp2/cyw43_configport.h @@ -35,6 +35,7 @@ #include "extmod/modnetwork.h" #include "pendsv.h" +#define CYW43_INCLUDE_LEGACY_F1_OVERFLOW_WORKAROUND_VARIABLES (1) #define CYW43_WIFI_NVRAM_INCLUDE_FILE "wifi_nvram_43439.h" #define CYW43_IOCTL_TIMEOUT_US (1000000) #define CYW43_SLEEP_MAX (10) diff --git a/ports/stm32/cyw43_configport.h b/ports/stm32/cyw43_configport.h index 1ea1ebde17ea2..595051180336e 100644 --- a/ports/stm32/cyw43_configport.h +++ b/ports/stm32/cyw43_configport.h @@ -102,7 +102,7 @@ #define CYW43_PIN_BT_CTS pyb_pin_BT_CTS #if MICROPY_HW_ENABLE_RF_SWITCH -#define CYW43_PIN_WL_RFSW_VDD pyb_pin_WL_RFSW_VDD +#define CYW43_PIN_RFSW_VDD pyb_pin_WL_RFSW_VDD #endif #define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) From dafff1fd0e37514b230081b1b34288d1d4b25867 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 12 Mar 2025 13:15:34 +1100 Subject: [PATCH 368/546] extmod/network_cyw43: Add WPA3 security constants. These are now supported by cyw43-driver. Signed-off-by: Damien George --- extmod/network_cyw43.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extmod/network_cyw43.c b/extmod/network_cyw43.c index 02b022cb81327..1b1b10b40741a 100644 --- a/extmod/network_cyw43.c +++ b/extmod/network_cyw43.c @@ -583,6 +583,8 @@ static const mp_rom_map_elem_t network_cyw43_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_IF_AP), MP_ROM_INT(MOD_NETWORK_AP_IF) }, { MP_ROM_QSTR(MP_QSTR_SEC_OPEN), MP_ROM_INT(CYW43_AUTH_OPEN) }, { MP_ROM_QSTR(MP_QSTR_SEC_WPA_WPA2), MP_ROM_INT(CYW43_AUTH_WPA2_MIXED_PSK) }, + { MP_ROM_QSTR(MP_QSTR_SEC_WPA3), MP_ROM_INT(CYW43_AUTH_WPA3_SAE_AES_PSK) }, + { MP_ROM_QSTR(MP_QSTR_SEC_WPA2_WPA3), MP_ROM_INT(CYW43_AUTH_WPA3_WPA2_AES_PSK) }, { MP_ROM_QSTR(MP_QSTR_PM_NONE), MP_ROM_INT(PM_NONE) }, { MP_ROM_QSTR(MP_QSTR_PM_PERFORMANCE), MP_ROM_INT(PM_PERFORMANCE) }, From 4b1c666c28ef9adb5a9b5156624bccd6a3d129b6 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 4 Mar 2025 12:20:02 +1100 Subject: [PATCH 369/546] esp32: Merge the per-SoC "main" components back together. Removes redundant metadata from each, shouldn't otherwise change any build output. Reverts the split originally added in e4650125. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/CMakeLists.txt | 3 -- ports/esp32/esp32_common.cmake | 14 ++++++- .../esp32/{main_esp32 => main}/CMakeLists.txt | 0 .../{main_esp32 => main}/idf_component.yml | 4 ++ ports/esp32/main/linker_esp32.lf | 41 +++++++++++++++++++ ports/esp32/main_esp32/linker.lf | 39 ------------------ ports/esp32/main_esp32c3/CMakeLists.txt | 14 ------- ports/esp32/main_esp32c3/idf_component.yml | 5 --- ports/esp32/main_esp32c3/linker.lf | 1 - ports/esp32/main_esp32c6/CMakeLists.txt | 14 ------- ports/esp32/main_esp32c6/idf_component.yml | 5 --- ports/esp32/main_esp32c6/linker.lf | 1 - ports/esp32/main_esp32s2/CMakeLists.txt | 13 ------ ports/esp32/main_esp32s2/idf_component.yml | 6 --- ports/esp32/main_esp32s2/linker.lf | 1 - ports/esp32/main_esp32s3/CMakeLists.txt | 13 ------ ports/esp32/main_esp32s3/idf_component.yml | 6 --- ports/esp32/main_esp32s3/linker.lf | 1 - 18 files changed, 58 insertions(+), 123 deletions(-) rename ports/esp32/{main_esp32 => main}/CMakeLists.txt (100%) rename ports/esp32/{main_esp32 => main}/idf_component.yml (52%) create mode 100644 ports/esp32/main/linker_esp32.lf delete mode 100644 ports/esp32/main_esp32/linker.lf delete mode 100644 ports/esp32/main_esp32c3/CMakeLists.txt delete mode 100644 ports/esp32/main_esp32c3/idf_component.yml delete mode 100644 ports/esp32/main_esp32c3/linker.lf delete mode 100644 ports/esp32/main_esp32c6/CMakeLists.txt delete mode 100644 ports/esp32/main_esp32c6/idf_component.yml delete mode 100644 ports/esp32/main_esp32c6/linker.lf delete mode 100644 ports/esp32/main_esp32s2/CMakeLists.txt delete mode 100644 ports/esp32/main_esp32s2/idf_component.yml delete mode 100644 ports/esp32/main_esp32s2/linker.lf delete mode 100644 ports/esp32/main_esp32s3/CMakeLists.txt delete mode 100644 ports/esp32/main_esp32s3/idf_component.yml delete mode 100644 ports/esp32/main_esp32s3/linker.lf diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt index c3a675eb2b78f..1db374b40a431 100644 --- a/ports/esp32/CMakeLists.txt +++ b/ports/esp32/CMakeLists.txt @@ -61,8 +61,5 @@ set(SDKCONFIG_DEFAULTS ${CMAKE_BINARY_DIR}/sdkconfig.combined) # Include main IDF cmake file. include($ENV{IDF_PATH}/tools/cmake/project.cmake) -# Set the location of the main component for the project (one per target). -list(APPEND EXTRA_COMPONENT_DIRS main_${IDF_TARGET}) - # Define the project. project(micropython) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 059989c10a4db..09952363d6d50 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -14,6 +14,18 @@ if(NOT MICROPY_PORT_DIR) get_filename_component(MICROPY_PORT_DIR ${MICROPY_DIR}/ports/esp32 ABSOLUTE) endif() +# RISC-V specific inclusions +if(CONFIG_IDF_TARGET_ARCH_RISCV) + list(APPEND MICROPY_SOURCE_LIB ${MICROPY_DIR}/shared/runtime/gchelper_generic.c) + list(APPEND IDF_COMPONENTS riscv) +endif() + +if(NOT DEFINED MICROPY_PY_TINYUSB) + if(CONFIG_IDF_TARGET_ESP32S2 OR CONFIG_IDF_TARGET_ESP32S3) + set(MICROPY_PY_TINYUSB ON) + endif() +endif() + # Include core source components. include(${MICROPY_DIR}/py/py.cmake) @@ -182,7 +194,7 @@ list(APPEND IDF_COMPONENTS if (MICROPY_USER_LDFRAGMENTS) set(MICROPY_LDFRAGMENTS ${MICROPY_USER_LDFRAGMENTS}) else() - set(MICROPY_LDFRAGMENTS linker.lf) + set(MICROPY_LDFRAGMENTS linker_esp32.lf) endif() # Register the main IDF component. diff --git a/ports/esp32/main_esp32/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt similarity index 100% rename from ports/esp32/main_esp32/CMakeLists.txt rename to ports/esp32/main/CMakeLists.txt diff --git a/ports/esp32/main_esp32/idf_component.yml b/ports/esp32/main/idf_component.yml similarity index 52% rename from ports/esp32/main_esp32/idf_component.yml rename to ports/esp32/main/idf_component.yml index 11f078f69f761..ccbde1f27e412 100644 --- a/ports/esp32/main_esp32/idf_component.yml +++ b/ports/esp32/main/idf_component.yml @@ -1,5 +1,9 @@ ## IDF Component Manager Manifest File dependencies: espressif/mdns: "~1.1.0" + espressif/esp_tinyusb: + rules: + - if: "target in [esp32s2, esp32s3]" + version: "~1.0.0" idf: version: ">=5.2.0" diff --git a/ports/esp32/main/linker_esp32.lf b/ports/esp32/main/linker_esp32.lf new file mode 100644 index 0000000000000..27e4ac2194c1b --- /dev/null +++ b/ports/esp32/main/linker_esp32.lf @@ -0,0 +1,41 @@ +# This fixes components/esp_ringbuf/linker.lf for ESP32 only to allow us to put +# non-ISR ringbuf functions in flash. + +# Requires that both RINGBUF_PLACE_FUNCTIONS_INTO_FLASH and RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH +# are set to "n" (which is the default), otherwise this would result in duplicate section config +# when resolving the linker fragments. + +# The effect of this file is to leave the ISR functions in RAM (which we require), but apply a fixed +# version of RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y (leaving out prvGetFreeSize and prvGetCurMaxSizeByteBuf) +# See https://github.com/espressif/esp-idf/issues/13378 + +[mapping:esp_ringbuf_fix] +archive: libesp_ringbuf.a +entries: + if IDF_TARGET_ESP32 = y: + # This is exactly the list of functions from RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y, + # but with prvGetFreeSize and prvGetCurMaxSizeByteBuf removed. + ringbuf: prvGetCurMaxSizeNoSplit (default) + ringbuf: prvGetCurMaxSizeAllowSplit (default) + ringbuf: prvInitializeNewRingbuffer (default) + ringbuf: prvReceiveGeneric (default) + ringbuf: vRingbufferDelete (default) + ringbuf: vRingbufferGetInfo (default) + ringbuf: vRingbufferReturnItem (default) + ringbuf: xRingbufferAddToQueueSetRead (default) + ringbuf: xRingbufferCanRead (default) + ringbuf: xRingbufferCreate (default) + ringbuf: xRingbufferCreateStatic (default) + ringbuf: xRingbufferCreateNoSplit (default) + ringbuf: xRingbufferReceive (default) + ringbuf: xRingbufferReceiveSplit (default) + ringbuf: xRingbufferReceiveUpTo (default) + ringbuf: xRingbufferRemoveFromQueueSetRead (default) + ringbuf: xRingbufferSend (default) + ringbuf: xRingbufferSendAcquire (default) + ringbuf: xRingbufferSendComplete (default) + ringbuf: xRingbufferPrintInfo (default) + ringbuf: xRingbufferGetMaxItemSize (default) + ringbuf: xRingbufferGetCurFreeSize (default) + + # Everything else will have the default rule already applied (i.e. noflash_text). diff --git a/ports/esp32/main_esp32/linker.lf b/ports/esp32/main_esp32/linker.lf deleted file mode 100644 index e00cd63f5548d..0000000000000 --- a/ports/esp32/main_esp32/linker.lf +++ /dev/null @@ -1,39 +0,0 @@ -# This fixes components/esp_ringbuf/linker.lf to allow us to put non-ISR ringbuf functions in flash. - -# Requires that both RINGBUF_PLACE_FUNCTIONS_INTO_FLASH and RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH -# are set to "n" (which is the default), otherwise this would result in duplicate section config -# when resolving the linker fragments. - -# The effect of this file is to leave the ISR functions in RAM (which we require), but apply a fixed -# version of RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y (leaving out prvGetFreeSize and prvGetCurMaxSizeByteBuf) -# See https://github.com/espressif/esp-idf/issues/13378 - -[mapping:esp_ringbuf_fix] -archive: libesp_ringbuf.a -entries: - # This is exactly the list of functions from RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y, - # but with prvGetFreeSize and prvGetCurMaxSizeByteBuf removed. - ringbuf: prvGetCurMaxSizeNoSplit (default) - ringbuf: prvGetCurMaxSizeAllowSplit (default) - ringbuf: prvInitializeNewRingbuffer (default) - ringbuf: prvReceiveGeneric (default) - ringbuf: vRingbufferDelete (default) - ringbuf: vRingbufferGetInfo (default) - ringbuf: vRingbufferReturnItem (default) - ringbuf: xRingbufferAddToQueueSetRead (default) - ringbuf: xRingbufferCanRead (default) - ringbuf: xRingbufferCreate (default) - ringbuf: xRingbufferCreateStatic (default) - ringbuf: xRingbufferCreateNoSplit (default) - ringbuf: xRingbufferReceive (default) - ringbuf: xRingbufferReceiveSplit (default) - ringbuf: xRingbufferReceiveUpTo (default) - ringbuf: xRingbufferRemoveFromQueueSetRead (default) - ringbuf: xRingbufferSend (default) - ringbuf: xRingbufferSendAcquire (default) - ringbuf: xRingbufferSendComplete (default) - ringbuf: xRingbufferPrintInfo (default) - ringbuf: xRingbufferGetMaxItemSize (default) - ringbuf: xRingbufferGetCurFreeSize (default) - - # Everything else will have the default rule already applied (i.e. noflash_text). diff --git a/ports/esp32/main_esp32c3/CMakeLists.txt b/ports/esp32/main_esp32c3/CMakeLists.txt deleted file mode 100644 index 307c0f32183a9..0000000000000 --- a/ports/esp32/main_esp32c3/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Set location of base MicroPython directory. -if(NOT MICROPY_DIR) - get_filename_component(MICROPY_DIR ${CMAKE_CURRENT_LIST_DIR}/../../.. ABSOLUTE) -endif() - -# Set location of the ESP32 port directory. -if(NOT MICROPY_PORT_DIR) - get_filename_component(MICROPY_PORT_DIR ${MICROPY_DIR}/ports/esp32 ABSOLUTE) -endif() - -list(APPEND MICROPY_SOURCE_LIB ${MICROPY_DIR}/shared/runtime/gchelper_generic.c) -list(APPEND IDF_COMPONENTS riscv) - -include(${MICROPY_PORT_DIR}/esp32_common.cmake) diff --git a/ports/esp32/main_esp32c3/idf_component.yml b/ports/esp32/main_esp32c3/idf_component.yml deleted file mode 100644 index 11f078f69f761..0000000000000 --- a/ports/esp32/main_esp32c3/idf_component.yml +++ /dev/null @@ -1,5 +0,0 @@ -## IDF Component Manager Manifest File -dependencies: - espressif/mdns: "~1.1.0" - idf: - version: ">=5.2.0" diff --git a/ports/esp32/main_esp32c3/linker.lf b/ports/esp32/main_esp32c3/linker.lf deleted file mode 100644 index 31c5b4563ca9a..0000000000000 --- a/ports/esp32/main_esp32c3/linker.lf +++ /dev/null @@ -1 +0,0 @@ -# Empty linker fragment (no workaround required for C3, see main_esp32/linker.lf). diff --git a/ports/esp32/main_esp32c6/CMakeLists.txt b/ports/esp32/main_esp32c6/CMakeLists.txt deleted file mode 100644 index 307c0f32183a9..0000000000000 --- a/ports/esp32/main_esp32c6/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Set location of base MicroPython directory. -if(NOT MICROPY_DIR) - get_filename_component(MICROPY_DIR ${CMAKE_CURRENT_LIST_DIR}/../../.. ABSOLUTE) -endif() - -# Set location of the ESP32 port directory. -if(NOT MICROPY_PORT_DIR) - get_filename_component(MICROPY_PORT_DIR ${MICROPY_DIR}/ports/esp32 ABSOLUTE) -endif() - -list(APPEND MICROPY_SOURCE_LIB ${MICROPY_DIR}/shared/runtime/gchelper_generic.c) -list(APPEND IDF_COMPONENTS riscv) - -include(${MICROPY_PORT_DIR}/esp32_common.cmake) diff --git a/ports/esp32/main_esp32c6/idf_component.yml b/ports/esp32/main_esp32c6/idf_component.yml deleted file mode 100644 index 11f078f69f761..0000000000000 --- a/ports/esp32/main_esp32c6/idf_component.yml +++ /dev/null @@ -1,5 +0,0 @@ -## IDF Component Manager Manifest File -dependencies: - espressif/mdns: "~1.1.0" - idf: - version: ">=5.2.0" diff --git a/ports/esp32/main_esp32c6/linker.lf b/ports/esp32/main_esp32c6/linker.lf deleted file mode 100644 index cedabcf979350..0000000000000 --- a/ports/esp32/main_esp32c6/linker.lf +++ /dev/null @@ -1 +0,0 @@ -# Empty linker fragment (no workaround required for C6, see main_esp32/linker.lf). diff --git a/ports/esp32/main_esp32s2/CMakeLists.txt b/ports/esp32/main_esp32s2/CMakeLists.txt deleted file mode 100644 index bc5ab939c3ce3..0000000000000 --- a/ports/esp32/main_esp32s2/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Set location of base MicroPython directory. -if(NOT MICROPY_DIR) - get_filename_component(MICROPY_DIR ${CMAKE_CURRENT_LIST_DIR}/../../.. ABSOLUTE) -endif() - -# Set location of the ESP32 port directory. -if(NOT MICROPY_PORT_DIR) - get_filename_component(MICROPY_PORT_DIR ${MICROPY_DIR}/ports/esp32 ABSOLUTE) -endif() - -set(MICROPY_PY_TINYUSB ON) - -include(${MICROPY_PORT_DIR}/esp32_common.cmake) diff --git a/ports/esp32/main_esp32s2/idf_component.yml b/ports/esp32/main_esp32s2/idf_component.yml deleted file mode 100644 index 2ee00b287786b..0000000000000 --- a/ports/esp32/main_esp32s2/idf_component.yml +++ /dev/null @@ -1,6 +0,0 @@ -## IDF Component Manager Manifest File -dependencies: - espressif/mdns: "~1.1.0" - espressif/esp_tinyusb: "~1.0.0" - idf: - version: ">=5.2.0" diff --git a/ports/esp32/main_esp32s2/linker.lf b/ports/esp32/main_esp32s2/linker.lf deleted file mode 100644 index 3c496fa878b3f..0000000000000 --- a/ports/esp32/main_esp32s2/linker.lf +++ /dev/null @@ -1 +0,0 @@ -# Empty linker fragment (no workaround required for S2, see main_esp32/linker.lf). diff --git a/ports/esp32/main_esp32s3/CMakeLists.txt b/ports/esp32/main_esp32s3/CMakeLists.txt deleted file mode 100644 index bc5ab939c3ce3..0000000000000 --- a/ports/esp32/main_esp32s3/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Set location of base MicroPython directory. -if(NOT MICROPY_DIR) - get_filename_component(MICROPY_DIR ${CMAKE_CURRENT_LIST_DIR}/../../.. ABSOLUTE) -endif() - -# Set location of the ESP32 port directory. -if(NOT MICROPY_PORT_DIR) - get_filename_component(MICROPY_PORT_DIR ${MICROPY_DIR}/ports/esp32 ABSOLUTE) -endif() - -set(MICROPY_PY_TINYUSB ON) - -include(${MICROPY_PORT_DIR}/esp32_common.cmake) diff --git a/ports/esp32/main_esp32s3/idf_component.yml b/ports/esp32/main_esp32s3/idf_component.yml deleted file mode 100644 index 2ee00b287786b..0000000000000 --- a/ports/esp32/main_esp32s3/idf_component.yml +++ /dev/null @@ -1,6 +0,0 @@ -## IDF Component Manager Manifest File -dependencies: - espressif/mdns: "~1.1.0" - espressif/esp_tinyusb: "~1.0.0" - idf: - version: ">=5.2.0" diff --git a/ports/esp32/main_esp32s3/linker.lf b/ports/esp32/main_esp32s3/linker.lf deleted file mode 100644 index 81d27906be0e4..0000000000000 --- a/ports/esp32/main_esp32s3/linker.lf +++ /dev/null @@ -1 +0,0 @@ -# Empty linker fragment (no workaround required for S3, see main_esp32/linker.lf). From 4d65b4e26119ab19de362174ec62cc3ea68a836a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 4 Mar 2025 12:30:10 +1100 Subject: [PATCH 370/546] esp32: Remove the ESP32 ringbuffer linker workaround. Reverts workaround added in acbdbcd9. According to the linked ESP-IDF issue this was only a problem for ESP-IDF V5.0.x, and support for versions older than V5.2 was dropped in 6e5d8d009. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/boards/sdkconfig.spiram | 2 +- ports/esp32/esp32_common.cmake | 2 -- ports/esp32/main/linker_esp32.lf | 41 ----------------------------- 3 files changed, 1 insertion(+), 44 deletions(-) delete mode 100644 ports/esp32/main/linker_esp32.lf diff --git a/ports/esp32/boards/sdkconfig.spiram b/ports/esp32/boards/sdkconfig.spiram index 35fe3c676ddf4..f5503d554149c 100644 --- a/ports/esp32/boards/sdkconfig.spiram +++ b/ports/esp32/boards/sdkconfig.spiram @@ -13,6 +13,6 @@ CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=8192 # to PSRAM bug workarounds. Apply some options to reduce the firmware size. CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y -# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y # Workaround required: see main_esp32/linker.lf +CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 09952363d6d50..6473f04a53500 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -193,8 +193,6 @@ list(APPEND IDF_COMPONENTS # Provide the default LD fragment if not set if (MICROPY_USER_LDFRAGMENTS) set(MICROPY_LDFRAGMENTS ${MICROPY_USER_LDFRAGMENTS}) -else() - set(MICROPY_LDFRAGMENTS linker_esp32.lf) endif() # Register the main IDF component. diff --git a/ports/esp32/main/linker_esp32.lf b/ports/esp32/main/linker_esp32.lf deleted file mode 100644 index 27e4ac2194c1b..0000000000000 --- a/ports/esp32/main/linker_esp32.lf +++ /dev/null @@ -1,41 +0,0 @@ -# This fixes components/esp_ringbuf/linker.lf for ESP32 only to allow us to put -# non-ISR ringbuf functions in flash. - -# Requires that both RINGBUF_PLACE_FUNCTIONS_INTO_FLASH and RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH -# are set to "n" (which is the default), otherwise this would result in duplicate section config -# when resolving the linker fragments. - -# The effect of this file is to leave the ISR functions in RAM (which we require), but apply a fixed -# version of RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y (leaving out prvGetFreeSize and prvGetCurMaxSizeByteBuf) -# See https://github.com/espressif/esp-idf/issues/13378 - -[mapping:esp_ringbuf_fix] -archive: libesp_ringbuf.a -entries: - if IDF_TARGET_ESP32 = y: - # This is exactly the list of functions from RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y, - # but with prvGetFreeSize and prvGetCurMaxSizeByteBuf removed. - ringbuf: prvGetCurMaxSizeNoSplit (default) - ringbuf: prvGetCurMaxSizeAllowSplit (default) - ringbuf: prvInitializeNewRingbuffer (default) - ringbuf: prvReceiveGeneric (default) - ringbuf: vRingbufferDelete (default) - ringbuf: vRingbufferGetInfo (default) - ringbuf: vRingbufferReturnItem (default) - ringbuf: xRingbufferAddToQueueSetRead (default) - ringbuf: xRingbufferCanRead (default) - ringbuf: xRingbufferCreate (default) - ringbuf: xRingbufferCreateStatic (default) - ringbuf: xRingbufferCreateNoSplit (default) - ringbuf: xRingbufferReceive (default) - ringbuf: xRingbufferReceiveSplit (default) - ringbuf: xRingbufferReceiveUpTo (default) - ringbuf: xRingbufferRemoveFromQueueSetRead (default) - ringbuf: xRingbufferSend (default) - ringbuf: xRingbufferSendAcquire (default) - ringbuf: xRingbufferSendComplete (default) - ringbuf: xRingbufferPrintInfo (default) - ringbuf: xRingbufferGetMaxItemSize (default) - ringbuf: xRingbufferGetCurFreeSize (default) - - # Everything else will have the default rule already applied (i.e. noflash_text). From 79fb5aa8789e71c9bcd8430b58267d96552be94f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 5 Feb 2025 15:16:47 +1100 Subject: [PATCH 371/546] esp32/machine_sdcard: Add SDCard pin assignments for ESP32-S3 support. Previously ESP32-S3 SDMMC could only use fixed pin assignments, however the ESP-IDF defaults don't match common boards. The chip also supports using GPIO Matrix to assign any pin. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/esp32/quickref.rst | 2 +- docs/library/machine.SDCard.rst | 161 ++++++++++++++++++++++---------- ports/esp32/machine_sdcard.c | 47 ++++++++++ 3 files changed, 160 insertions(+), 50 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 0780e01300801..d65782e501ac7 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -747,7 +747,7 @@ See :ref:`machine.SDCard `. :: import machine, os, vfs - # Slot 2 uses pins sck=18, cs=5, miso=19, mosi=23 + # On original ESP32, slot 2 uses pins sck=18, cs=5, miso=19, mosi=23 sd = machine.SDCard(slot=2) vfs.mount(sd, '/sd') # mount diff --git a/docs/library/machine.SDCard.rst b/docs/library/machine.SDCard.rst index e4bb25dfc0377..62da8c3c2ea61 100644 --- a/docs/library/machine.SDCard.rst +++ b/docs/library/machine.SDCard.rst @@ -23,7 +23,8 @@ arguments that might need to be set in order to use either a non-standard slot or a non-standard pin assignment. The exact subset of arguments supported will vary from platform to platform. -.. class:: SDCard(slot=1, width=1, cd=None, wp=None, sck=None, miso=None, mosi=None, cs=None, freq=20000000) +.. class:: SDCard(slot=1, width=1, cd=None, wp=None, sck=None, miso=None, mosi=None, + cs=None, cmd=None, data=None, freq=20000000) This class provides access to SD or MMC storage cards using either a dedicated SD/MMC interface hardware or through an SPI channel. @@ -37,7 +38,8 @@ vary from platform to platform. - *slot* selects which of the available interfaces to use. Leaving this unset will select the default interface. - - *width* selects the bus width for the SD/MMC interface. + - *width* selects the bus width for the SD/MMC interface. This many data + pins must be connected to the SD card. - *cd* can be used to specify a card-detect pin. @@ -51,7 +53,14 @@ vary from platform to platform. - *cs* can be used to specify an SPI chip select pin. - - *freq* selects the SD/MMC interface frequency in Hz (only supported on the ESP32). + The following additional parameters are only present on ESP32 port: + + - *cmd* can be used to specify the SD CMD pin (ESP32-S3 only). + + - *data* can be used to specify a list or tuple of SD data bus pins + (ESP32-S3 only). + + - *freq* selects the SD/MMC interface frequency in Hz. Implementation-specific details ------------------------------- @@ -67,52 +76,106 @@ The standard PyBoard has just one slot. No arguments are necessary or supported. ESP32 ````` -The ESP32 provides two channels of SD/MMC hardware and also supports -access to SD Cards through either of the two SPI ports that are -generally available to the user. As a result the *slot* argument can -take a value between 0 and 3, inclusive. Slots 0 and 1 use the -built-in SD/MMC hardware while slots 2 and 3 use the SPI ports. Slot 0 -supports 1, 4 or 8-bit wide access while slot 1 supports 1 or 4-bit -access; the SPI slots only support 1-bit access. - - .. note:: Slot 0 is used to communicate with on-board flash memory - on most ESP32 modules and so will be unavailable to the - user. - - .. note:: Most ESP32 modules that provide an SD card slot using the - dedicated hardware only wire up 1 data pin, so the default - value for *width* is 1. - -The pins used by the dedicated SD/MMC hardware are fixed. The pins -used by the SPI hardware can be reassigned. - - .. note:: If any of the SPI signals are remapped then all of the SPI - signals will pass through a GPIO multiplexer unit which - can limit the performance of high frequency signals. Since - the normal operating speed for SD cards is 40MHz this can - cause problems on some cards. - -The default (and preferred) pin assignment are as follows: - - ====== ====== ====== ====== ====== - Slot 0 1 2 3 - ------ ------ ------ ------ ------ - Signal Pin Pin Pin Pin - ====== ====== ====== ====== ====== - sck 6 14 18 14 - cmd 11 15 - cs 5 15 - miso 19 12 - mosi 23 13 - D0 7 2 - D1 8 4 - D2 9 12 - D3 10 13 - D4 16 - D5 17 - D6 5 - D7 18 - ====== ====== ====== ====== ====== +SD cards support access in both SD/MMC mode and the simpler (but slower) SPI +mode. ESP32 and ESP32-S3 chips can access SD cards using either mode. SPI mode +makes use of a `SPI` host peripheral, which cannot concurrently be used for +something else. + +The ``slot`` argument determines which mode is used. Different values are +available on different chips: + +====== ================= ============ ======================== +Slot Supported chips Mode Supported data width +====== ================= ============ ======================== +0 ESP32-S3 SD/MMC 1, 4, or 8 bits. +1 ESP32, ESP32-S3 SD/MMC 1 or 4 bits. +2 ESP32, ESP32-S3 `SPI` (id=1) 1 bit. +3 ESP32, ESP32-S3 `SPI` (id=0) 1 bit. +====== ================= ============ ======================== + +.. note:: On the original ESP32, SDMMC slot 0 is unavailable as its pins are + used to communicate with on-board flash memory. + +.. note:: Most ESP32 modules that provide an SD card slot using the + dedicated hardware only wire up 1 data pin, so the default + value for ``width`` is 1. + +Additional details depend on which ESP32 family chip is in use: + +Original ESP32 +~~~~~~~~~~~~~~ + +Pin assignments in SD/MMC mode are fixed on the original ESP32. When accessing a +card in SPI mode, pins can be set to different values in the constructor. + +The default pin assignments are as follows: + + ====== ====== ====== ====== ============ + Slot 1 2 3 Can be set + ------ ------ ------ ------ ------------ + Signal Pin Pin Pin + ====== ====== ====== ====== ============ + CLK 14 No + CMD 15 No + D0 2 No + D1 4 No + D2 12 No + D3 13 No + sck 18 14 Yes + cs 5 15 Yes + miso 19 12 Yes + mosi 23 13 Yes + ====== ====== ====== ====== ============ + +The ``cd`` and ``wp`` pins are not fixed in either mode and default to disabled, unless set. + +ESP32-S3 +~~~~~~~~ + +The ESP32-S3 chip allows pins to be set to different values for both SD/MMC and +SPI mode access. + +If not set, default pin assignments are as follows: + + ======== ====== ====== ====== ====== + Slot 0 1 2 3 + -------- ------ ------ ------ ------ + Signal Pin Pin Pin Pin + ======== ====== ====== ====== ====== + CLK 14 14 + CMD 15 15 + D0 2 2 + D1 4 4 + D2 12 12 + D3 13 13 + D4 33* + D5 34* + D6 35* + D7 36* + sck 37* 14 + cs 34* 13 + miso 37* 2 + mosi 35* 15 + ======== ====== ====== ====== ====== + +.. note:: Slots 0 and 1 cannot both be in use at the same time. + +.. note:: Pins marked with an asterisk * in the table must be changed from the + default if the ESP32-S3 board is configured for Octal SPI Flash or + PSRAM. + +To access a card in SD/MMC mode, set ``slot`` parameter value 0 or 1 and +parameters ``sck`` (for CLK), ``cmd`` and ``data`` as needed to assign pins. If +the ``data`` argument is passed then it should be a list or tuple of data pins +or pin numbers with length equal to the ``width`` argument. For example:: + + sd = SDCard(slot=0, width=4, sck=8, cmd=9, data=(10, 11, 12, 13)) + +To access a card in SPI mode, set ``slot`` parameter value 2 or 3 and pass +parameters ``sck``, ``cs``, ``miso``, ``mosi`` as needed to assign pins. + +In either mode the ``cd`` and ``wp`` pins default to disabled, unless set in the +constructor. cc3200 `````` diff --git a/ports/esp32/machine_sdcard.c b/ports/esp32/machine_sdcard.c index da6b0df282caf..48fd96c6d2991 100644 --- a/ports/esp32/machine_sdcard.c +++ b/ports/esp32/machine_sdcard.c @@ -177,6 +177,10 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args ARG_mosi, ARG_sck, ARG_cs, + #if SOC_SDMMC_USE_GPIO_MATRIX + ARG_cmd, + ARG_data, + #endif ARG_freq, }; static const mp_arg_t allowed_args[] = { @@ -189,6 +193,11 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_cs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + // Optional assignment of SDMMC interface pins, if host supports this + #if SOC_SDMMC_USE_GPIO_MATRIX + { MP_QSTR_cmd, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_data, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + #endif // freq is valid for both SPI and SDMMC interfaces { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 20000000} }, }; @@ -211,6 +220,11 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args arg_vals[ARG_miso].u_obj, arg_vals[ARG_mosi].u_obj, arg_vals[ARG_sck].u_obj, arg_vals[ARG_cs].u_obj); + #if SOC_SDMMC_USE_GPIO_MATRIX + DEBUG_printf(" cmd=%p, data=%p", + arg_vals[ARG_cmd].u_obj, arg_vals[ARG_data].u_obj); + #endif + int slot_num = arg_vals[ARG_slot].u_int; if (slot_num < 0 || slot_num > 3) { mp_raise_ValueError(MP_ERROR_TEXT("slot number must be between 0 and 3 inclusive")); @@ -221,6 +235,13 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args if (is_spi) { slot_num -= 2; } + // Verify valid argument combinations + #if SOC_SDMMC_USE_GPIO_MATRIX + if (is_spi && (arg_vals[ARG_cmd].u_obj != mp_const_none + || arg_vals[ARG_data].u_obj != mp_const_none)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid config: SPI slot with SDMMC pin arguments")); + } + #endif DEBUG_printf(" Setting up host configuration"); @@ -307,6 +328,32 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args mp_raise_ValueError(MP_ERROR_TEXT("width must be 1 or 4 (or 8 on slot 0)")); } + #if SOC_SDMMC_USE_GPIO_MATRIX + // Optionally configure all the SDMMC pins, if chip supports this + SET_CONFIG_PIN(slot_config, clk, ARG_sck); // reuse SPI SCK for CLK + SET_CONFIG_PIN(slot_config, cmd, ARG_cmd); + if (arg_vals[ARG_data].u_obj != mp_const_none) { + mp_obj_t *data_vals; + size_t data_len; + mp_obj_get_array(arg_vals[ARG_data].u_obj, &data_len, &data_vals); + if (data_len != width) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("data argument length must match width %d"), width); + } + slot_config.d0 = machine_pin_get_id(data_vals[0]); + if (width > 1) { + slot_config.d1 = machine_pin_get_id(data_vals[1]); + slot_config.d2 = machine_pin_get_id(data_vals[2]); + slot_config.d3 = machine_pin_get_id(data_vals[3]); + } + if (width == 8) { + slot_config.d4 = machine_pin_get_id(data_vals[4]); + slot_config.d5 = machine_pin_get_id(data_vals[5]); + slot_config.d6 = machine_pin_get_id(data_vals[6]); + slot_config.d7 = machine_pin_get_id(data_vals[7]); + } + } + #endif + DEBUG_printf(" Calling init_slot()"); check_esp_err(sdmmc_host_init_slot(self->host.slot, &slot_config)); } From c85eefc55b3cc8a460230129e11fb490a0a15f29 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 5 Feb 2025 15:17:27 +1100 Subject: [PATCH 372/546] esp32/machine_sdcard: Add SDCard SPI mode support for ESP32-S2,C3,C6. These micros don't have full SDMMC host support, but they can initialise the SDCard in SPI mode. A bit limited on C3 and C6 as they only have one host SPI peripheral. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/library/machine.SDCard.rst | 60 ++++++++++++------ ports/esp32/machine_sdcard.c | 104 ++++++++++++++++++++++++-------- 2 files changed, 121 insertions(+), 43 deletions(-) diff --git a/docs/library/machine.SDCard.rst b/docs/library/machine.SDCard.rst index 62da8c3c2ea61..c4a0d5d172b30 100644 --- a/docs/library/machine.SDCard.rst +++ b/docs/library/machine.SDCard.rst @@ -77,24 +77,34 @@ ESP32 ````` SD cards support access in both SD/MMC mode and the simpler (but slower) SPI -mode. ESP32 and ESP32-S3 chips can access SD cards using either mode. SPI mode -makes use of a `SPI` host peripheral, which cannot concurrently be used for -something else. +mode. -The ``slot`` argument determines which mode is used. Different values are -available on different chips: - -====== ================= ============ ======================== -Slot Supported chips Mode Supported data width -====== ================= ============ ======================== -0 ESP32-S3 SD/MMC 1, 4, or 8 bits. -1 ESP32, ESP32-S3 SD/MMC 1 or 4 bits. -2 ESP32, ESP32-S3 `SPI` (id=1) 1 bit. -3 ESP32, ESP32-S3 `SPI` (id=0) 1 bit. -====== ================= ============ ======================== +SPI mode makes use of a `SPI` host peripheral, which cannot concurrently be used +for other SPI interactions. -.. note:: On the original ESP32, SDMMC slot 0 is unavailable as its pins are - used to communicate with on-board flash memory. +The ``slot`` argument determines which mode is used. Different values are +supported on different chips: + +========== ======== ======== ============ ============ +Chip Slot 0 Slot 1 Slot 2 Slot 3 +========== ======== ======== ============ ============ +ESP32 SD/MMC SPI (id=1) SPI (id=0) +ESP32-C3 SPI (id=0) +ESP32-C6 SPI (id=0) +ESP32-S2 SPI (id=1) SPI (id=0) +ESP32-S3 SD/MMC SD/MMC SPI (id=1) SPI (id=0) +========== ======== ======== ============ ============ + +Different slots support different data bus widths (number of data pins): + +========== ========== ===================== +Slot Type Supported data widths +========== ========== ===================== +0 SD/MMC 1, 4, 8 +1 SD/MMC 1, 4 +2 SPI 1 +3 SPI 1 +========== ========== ===================== .. note:: Most ESP32 modules that provide an SD card slot using the dedicated hardware only wire up 1 data pin, so the default @@ -105,8 +115,9 @@ Additional details depend on which ESP32 family chip is in use: Original ESP32 ~~~~~~~~~~~~~~ -Pin assignments in SD/MMC mode are fixed on the original ESP32. When accessing a -card in SPI mode, pins can be set to different values in the constructor. +In SD/MMC mode (slot 1), pin assignments in SD/MMC mode are fixed on the +original ESP32. The SPI mode slots (2 & 3) allow pins to be set to different +values in the constructor. The default pin assignments are as follows: @@ -177,6 +188,19 @@ parameters ``sck``, ``cs``, ``miso``, ``mosi`` as needed to assign pins. In either mode the ``cd`` and ``wp`` pins default to disabled, unless set in the constructor. +Other ESP32 chips +~~~~~~~~~~~~~~~~~ + +Other ESP32 family chips do not have hardware SD/MMC host controllers and can +only access SD cards in SPI mode. + +To access a card in SPI mode, set ``slot`` parameter value 2 or 3 and pass +parameters ``sck``, ``cs``, ``miso``, ``mosi`` to assign pins. + +.. note:: ESP32-C3 and ESP32-C6 only have one available `SPI` bus, so the only + valid ``slot`` parameter value is 2. Using this bus for the SD card + will prevent also using it for :class:`machine.SPI`. + cc3200 `````` diff --git a/ports/esp32/machine_sdcard.c b/ports/esp32/machine_sdcard.c index 48fd96c6d2991..0f8bd844692b4 100644 --- a/ports/esp32/machine_sdcard.c +++ b/ports/esp32/machine_sdcard.c @@ -33,7 +33,9 @@ #if MICROPY_HW_ENABLE_SDCARD +#if SOC_SDMMC_HOST_SUPPORTED #include "driver/sdmmc_host.h" +#endif #include "driver/sdspi_host.h" #include "sdmmc_cmd.h" #include "esp_log.h" @@ -69,18 +71,34 @@ typedef struct _sdcard_obj_t { #define _SECTOR_SIZE(self) (self->card.csd.sector_size) +// Number SPI buses available for firmware app (including for SD) +#define NUM_SD_SPI_BUS (SOC_SPI_PERIPH_NUM - 1) + +#if CONFIG_IDF_TARGET_ESP32 +#define SD_SLOT_MIN 1 +#elif SOC_SDMMC_HOST_SUPPORTED +#define SD_SLOT_MIN 0 +#else +#define SD_SLOT_MIN 2 +#endif +#define SD_SLOT_MAX (NUM_SD_SPI_BUS + 1) // Inclusive + // SPI bus default bus and device configuration. -static const spi_bus_config_t spi_bus_defaults[2] = { +static const spi_bus_config_t spi_bus_defaults[NUM_SD_SPI_BUS] = { { #if CONFIG_IDF_TARGET_ESP32 .miso_io_num = GPIO_NUM_19, .mosi_io_num = GPIO_NUM_23, .sclk_io_num = GPIO_NUM_18, - #else + #elif CONFIG_IDF_TARGET_ESP32S3 .miso_io_num = GPIO_NUM_36, .mosi_io_num = GPIO_NUM_35, .sclk_io_num = GPIO_NUM_37, + #else + .miso_io_num = GPIO_NUM_NC, + .mosi_io_num = GPIO_NUM_NC, + .sclk_io_num = GPIO_NUM_NC, #endif .data2_io_num = GPIO_NUM_NC, .data3_io_num = GPIO_NUM_NC, @@ -92,6 +110,7 @@ static const spi_bus_config_t spi_bus_defaults[2] = { .flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_MOSI, .intr_flags = 0, }, + #if NUM_SD_SPI_BUS > 1 { .miso_io_num = GPIO_NUM_2, .mosi_io_num = GPIO_NUM_15, @@ -106,28 +125,34 @@ static const spi_bus_config_t spi_bus_defaults[2] = { .flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_MOSI, .intr_flags = 0, }, + #endif }; #if CONFIG_IDF_TARGET_ESP32 -static const uint8_t spi_dma_channel_defaults[2] = { +static const uint8_t spi_dma_channel_defaults[NUM_SD_SPI_BUS] = { 2, 1, }; #endif -static const sdspi_device_config_t spi_dev_defaults[2] = { +static const sdspi_device_config_t spi_dev_defaults[NUM_SD_SPI_BUS] = { + #if NUM_SD_SPI_BUS > 1 { #if CONFIG_IDF_TARGET_ESP32 .host_id = VSPI_HOST, .gpio_cs = GPIO_NUM_5, - #else + #elif CONFIG_IDF_TARGET_ESP32S3 .host_id = SPI3_HOST, .gpio_cs = GPIO_NUM_34, + #else + .host_id = SPI3_HOST, + .gpio_cs = GPIO_NUM_NC, #endif .gpio_cd = SDSPI_SLOT_NO_CD, .gpio_wp = SDSPI_SLOT_NO_WP, .gpio_int = SDSPI_SLOT_NO_INT, }, + #endif SDSPI_DEVICE_CONFIG_DEFAULT(), // HSPI (ESP32) / SPI2 (ESP32S3) }; @@ -159,12 +184,15 @@ static esp_err_t sdcard_ensure_card_init(sdcard_card_obj_t *self, bool force) { // Expose the SD card or MMC as an object with the block protocol. // Create a new SDCard object -// The driver supports either the host SD/MMC controller (default) or SPI mode -// In both cases there are two "slots". Slot 0 on the SD/MMC controller is -// typically tied up with the flash interface in most ESP32 modules but in -// theory supports 1, 4 or 8-bit transfers. Slot 1 supports only 1 and 4-bit -// transfers. Only 1-bit is supported on the SPI interfaces. -// card = SDCard(slot=1, width=None, present_pin=None, wp_pin=None) +// +// SD/MMC or SPI mode is determined by the slot argument +// 0,1 is SD/MMC mode where supported. +// 2,3 is SPI mode where supported (1-bit only) +// +// Original ESP32 can't use 0 +// ESP32-C3/C6/etc can only use 2 (only one SPI bus, no SD/MMC controller) +// +// Consult machine.SDCard docs for more details. static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check arguments @@ -183,8 +211,13 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args #endif ARG_freq, }; + #if SOC_SDMMC_HOST_SUPPORTED + static const int DEFAULT_SLOT = 1; + #else + static const int DEFAULT_SLOT = SD_SLOT_MAX; + #endif static const mp_arg_t allowed_args[] = { - { MP_QSTR_slot, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_slot, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SLOT} }, { MP_QSTR_width, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, { MP_QSTR_cd, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_wp, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, @@ -226,15 +259,21 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args #endif int slot_num = arg_vals[ARG_slot].u_int; - if (slot_num < 0 || slot_num > 3) { - mp_raise_ValueError(MP_ERROR_TEXT("slot number must be between 0 and 3 inclusive")); + if (slot_num < SD_SLOT_MIN || slot_num > SD_SLOT_MAX) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid slot number")); } + #if SOC_SDMMC_HOST_SUPPORTED // Slots 0 and 1 are native SD/MMC, slots 2 and 3 are SPI bool is_spi = (slot_num >= 2); + #else + bool is_spi = true; + #endif if (is_spi) { slot_num -= 2; + assert(slot_num < NUM_SD_SPI_BUS); } + // Verify valid argument combinations #if SOC_SDMMC_USE_GPIO_MATRIX if (is_spi && (arg_vals[ARG_cmd].u_obj != mp_const_none @@ -242,6 +281,13 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args mp_raise_ValueError(MP_ERROR_TEXT("invalid config: SPI slot with SDMMC pin arguments")); } #endif + #if SOC_SDMMC_HOST_SUPPORTED + if (!is_spi && (arg_vals[ARG_miso].u_obj != mp_const_none + || arg_vals[ARG_mosi].u_obj != mp_const_none + || arg_vals[ARG_cs].u_obj != mp_const_none)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid config: SDMMC slot with SPI pin arguments")); + } + #endif DEBUG_printf(" Setting up host configuration"); @@ -253,21 +299,17 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args if (is_spi) { sdmmc_host_t _temp_host = SDSPI_HOST_DEFAULT(); _temp_host.max_freq_khz = freq / 1000; + // SPI SDMMC sets the slot to the SPI host ID + _temp_host.slot = spi_dev_defaults[slot_num].host_id; self->host = _temp_host; - } else { + } + #if SOC_SDMMC_HOST_SUPPORTED + else { sdmmc_host_t _temp_host = SDMMC_HOST_DEFAULT(); _temp_host.max_freq_khz = freq / 1000; self->host = _temp_host; } - - if (is_spi) { - // Needs to match spi_dev_defaults above. - #if CONFIG_IDF_TARGET_ESP32 - self->host.slot = slot_num ? HSPI_HOST : VSPI_HOST; - #else - self->host.slot = slot_num ? SPI2_HOST : SPI3_HOST; - #endif - } + #endif DEBUG_printf(" Calling host.init()"); @@ -294,6 +336,15 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args SET_CONFIG_PIN(dev_config, gpio_cd, ARG_cd); SET_CONFIG_PIN(dev_config, gpio_wp, ARG_wp); + // On chips other than original ESP32 and S3, there are not + // always default SPI pins assigned + if (dev_config.gpio_cs == GPIO_NUM_NC + || bus_config.miso_io_num == GPIO_NUM_NC + || bus_config.mosi_io_num == GPIO_NUM_NC + || bus_config.sclk_io_num == GPIO_NUM_NC) { + mp_raise_ValueError(MP_ERROR_TEXT("SPI pin values required")); + } + DEBUG_printf(" Calling spi_bus_initialize()"); check_esp_err(spi_bus_initialize(spi_host_id, &bus_config, dma_channel)); @@ -309,7 +360,9 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args spi_bus_free(spi_host_id); mp_raise_ValueError(MP_ERROR_TEXT("SPI bus already in use")); } - } else { + } + #if SOC_SDMMC_HOST_SUPPORTED + else { // SD/MMC interface DEBUG_printf(" Setting up SDMMC slot configuration"); sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); @@ -357,6 +410,7 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args DEBUG_printf(" Calling init_slot()"); check_esp_err(sdmmc_host_init_slot(self->host.slot, &slot_config)); } + #endif // SOC_SDMMC_HOST_SUPPORTED DEBUG_printf(" Returning new card object: %p", self); return MP_OBJ_FROM_PTR(self); From 464121f30158e769b4bcd4e0187f9fc58b6e09d9 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 5 Feb 2025 18:17:02 +1100 Subject: [PATCH 373/546] esp32/boards: Enable machine.SDCard on all boards. This increases binary size by about 4KB on C3, probably a bit less on S2. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h | 2 -- ports/esp32/boards/ESP32_GENERIC_C6/mpconfigboard.h | 1 - ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.h | 1 - ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h | 2 -- ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.h | 1 - ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.h | 1 - ports/esp32/boards/M5STACK_NANOC6/mpconfigboard.h | 1 - ports/esp32/boards/UM_FEATHERS2/mpconfigboard.h | 1 - ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.h | 1 - ports/esp32/boards/UM_TINYC6/mpconfigboard.h | 1 - ports/esp32/boards/UM_TINYS2/mpconfigboard.h | 1 - 11 files changed, 13 deletions(-) diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h b/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h index a6cebdf6ee64f..988c7db8a144b 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h +++ b/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h @@ -3,7 +3,5 @@ #define MICROPY_HW_BOARD_NAME "ESP32C3 module" #define MICROPY_HW_MCU_NAME "ESP32C3" -#define MICROPY_HW_ENABLE_SDCARD (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) diff --git a/ports/esp32/boards/ESP32_GENERIC_C6/mpconfigboard.h b/ports/esp32/boards/ESP32_GENERIC_C6/mpconfigboard.h index be530ad3a87ba..d6cc9a6f67855 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C6/mpconfigboard.h +++ b/ports/esp32/boards/ESP32_GENERIC_C6/mpconfigboard.h @@ -3,7 +3,6 @@ #define MICROPY_HW_BOARD_NAME "ESP32C6 module" #define MICROPY_HW_MCU_NAME "ESP32C6" -#define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_PY_MACHINE_I2S (0) // Enable UART REPL for modules that have an external USB-UART and don't use native USB. diff --git a/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.h b/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.h index aaea41bcdec2b..9e03e8269b1b8 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.h +++ b/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.h @@ -2,7 +2,6 @@ #define MICROPY_HW_MCU_NAME "ESP32S2" #define MICROPY_PY_BLUETOOTH (0) -#define MICROPY_HW_ENABLE_SDCARD (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) diff --git a/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h b/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h index 46d720edc758f..9eb0bada1ee59 100644 --- a/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h +++ b/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h @@ -2,8 +2,6 @@ #define MICROPY_HW_MCU_NAME "ESP32-C3FH4" #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-c3-mini" -#define MICROPY_HW_ENABLE_SDCARD (0) - #define MICROPY_HW_I2C0_SCL (10) #define MICROPY_HW_I2C0_SDA (8) diff --git a/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.h b/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.h index 9776b7b478aa1..08badc3845737 100644 --- a/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.h +++ b/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.h @@ -3,7 +3,6 @@ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-s2-mini" #define MICROPY_PY_BLUETOOTH (0) -#define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_HW_I2C0_SCL (35) #define MICROPY_HW_I2C0_SDA (33) diff --git a/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.h b/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.h index 9241280dec6a3..07db116c28471 100644 --- a/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.h +++ b/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.h @@ -3,7 +3,6 @@ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-s2-pico" #define MICROPY_PY_BLUETOOTH (0) -#define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_HW_I2C0_SCL (9) #define MICROPY_HW_I2C0_SDA (8) diff --git a/ports/esp32/boards/M5STACK_NANOC6/mpconfigboard.h b/ports/esp32/boards/M5STACK_NANOC6/mpconfigboard.h index 9acab7096853b..656ec0bc6c3c5 100644 --- a/ports/esp32/boards/M5STACK_NANOC6/mpconfigboard.h +++ b/ports/esp32/boards/M5STACK_NANOC6/mpconfigboard.h @@ -1,7 +1,6 @@ #define MICROPY_HW_BOARD_NAME "M5Stack NanoC6" #define MICROPY_HW_MCU_NAME "ESP32C6" -#define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_PY_MACHINE_I2S (0) #define MICROPY_HW_I2C0_SCL (1) diff --git a/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.h b/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.h index d8529b6342fe2..a2a00c0e46c0e 100644 --- a/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.h +++ b/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.h @@ -3,7 +3,6 @@ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "FeatherS2" #define MICROPY_PY_BLUETOOTH (0) -#define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_HW_I2C0_SCL (9) #define MICROPY_HW_I2C0_SDA (8) diff --git a/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.h b/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.h index e7e4d37ece95a..bcc809895d12f 100644 --- a/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.h +++ b/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.h @@ -3,7 +3,6 @@ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "FeatherS2-Neo" #define MICROPY_PY_BLUETOOTH (0) -#define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_HW_I2C0_SCL (9) #define MICROPY_HW_I2C0_SDA (8) diff --git a/ports/esp32/boards/UM_TINYC6/mpconfigboard.h b/ports/esp32/boards/UM_TINYC6/mpconfigboard.h index 1a3f1c904a799..09392d55594a0 100644 --- a/ports/esp32/boards/UM_TINYC6/mpconfigboard.h +++ b/ports/esp32/boards/UM_TINYC6/mpconfigboard.h @@ -1,7 +1,6 @@ #define MICROPY_HW_BOARD_NAME "Unexpected Maker TinyC6" #define MICROPY_HW_MCU_NAME "ESP32C6" -#define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_PY_MACHINE_I2S (0) #define MICROPY_HW_I2C0_SCL (7) diff --git a/ports/esp32/boards/UM_TINYS2/mpconfigboard.h b/ports/esp32/boards/UM_TINYS2/mpconfigboard.h index e0bde417c8a2a..ecfb0056b43b0 100644 --- a/ports/esp32/boards/UM_TINYS2/mpconfigboard.h +++ b/ports/esp32/boards/UM_TINYS2/mpconfigboard.h @@ -3,7 +3,6 @@ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "TinyS2" #define MICROPY_PY_BLUETOOTH (0) -#define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_HW_I2C0_SCL (9) #define MICROPY_HW_I2C0_SDA (8) From bfd974d65823c5ab4be1a2d0934d7392a5fa3069 Mon Sep 17 00:00:00 2001 From: garywill Date: Fri, 6 Dec 2024 13:20:34 +0800 Subject: [PATCH 374/546] esp32/machine_pwm: Correctly stop LEDC timer. The ESP32 PWM (LEDC) timer wasn't correctly stopped. `ledc_timer_rst()` is for resetting the timer counter to zero, not for stopping the timer. The correct way to stop a pwm timer is to pause it, then configure it with `deconfigure = true`. Signed-off-by: garywill --- ports/esp32/machine_pwm.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 6e3610b156679..7826769556c98 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -156,7 +156,13 @@ static void pwm_deinit(int channel_idx) { int timer_idx = chans[channel_idx].timer_idx; if (timer_idx != -1) { if (!is_timer_in_use(channel_idx, timer_idx)) { - check_esp_err(ledc_timer_rst(TIMER_IDX_TO_MODE(timer_idx), TIMER_IDX_TO_TIMER(timer_idx))); + check_esp_err(ledc_timer_pause(TIMER_IDX_TO_MODE(timer_idx), TIMER_IDX_TO_TIMER(timer_idx))); + ledc_timer_config_t timer_config = { + .deconfigure = true, + .speed_mode = TIMER_IDX_TO_MODE(timer_idx), + .timer_num = TIMER_IDX_TO_TIMER(timer_idx), + }; + check_esp_err(ledc_timer_config(&timer_config)); // Flag it unused timers[chans[channel_idx].timer_idx].freq_hz = -1; } @@ -635,7 +641,13 @@ static void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { if (!current_in_use) { // Free the old timer - check_esp_err(ledc_timer_rst(self->mode, self->timer)); + check_esp_err(ledc_timer_pause(self->mode, self->timer)); + ledc_timer_config_t timer_config = { + .deconfigure = true, + .speed_mode = self->mode, + .timer_num = self->timer, + }; + check_esp_err(ledc_timer_config(&timer_config)); // Flag it unused timers[current_timer_idx].freq_hz = -1; } From 4d2d60d6e1422dc5a66f15f7e48e3ebad2dfc41c Mon Sep 17 00:00:00 2001 From: Garry W <32130780+garywill@users.noreply.github.com> Date: Thu, 14 Nov 2024 04:19:31 +0000 Subject: [PATCH 375/546] esp32/machine_pin: Fix logic clearing USB_SERIAL_JTAG_USB_PAD_ENABLE. When we don't use USB JTAG, we want to use the two USB pins (D+/D-) as GPIO. So, do clear USB_SERIAL_JTAG_USB_PAD_ENABLE when USB SERIAL JTAG is not enabled Signed-off-by: Garry W <32130780+garywill@users.noreply.github.com> Signed-off-by: Angus Gratton --- ports/esp32/machine_pin.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index b821abf1fc18a..bb0d40c4d82ed 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -158,12 +158,12 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ } } - #if CONFIG_IDF_TARGET_ESP32C3 && MICROPY_HW_ESP_USB_SERIAL_JTAG + #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); } #endif - #if CONFIG_IDF_TARGET_ESP32C6 && CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED + #if CONFIG_IDF_TARGET_ESP32C6 && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED if (index == 12 || index == 13) { CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE); } From 1d5dc723b40d978b84da2f9b1a48e880777385ea Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 15 Nov 2024 15:48:52 +1100 Subject: [PATCH 376/546] esp32/machine_pin: Fix availability of USB Serial/JTAG pins on ESP32-C6. Similar to parent commit, allow using USB Serial/JTAG pins for other purposes but only if this feature is disabled in the build config. Signed-off-by: Angus Gratton --- ports/esp32/machine_pin.c | 2 +- ports/esp32/machine_pin.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index bb0d40c4d82ed..d0c4ee1a7eca7 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -163,7 +163,7 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE); } #endif - #if CONFIG_IDF_TARGET_ESP32C6 && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED + #if CONFIG_IDF_TARGET_ESP32C6 && !MICROPY_HW_ESP_USB_SERIAL_JTAG if (index == 12 || index == 13) { 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 3f94508e015f8..47f1ddebebf44 100644 --- a/ports/esp32/machine_pin.h +++ b/ports/esp32/machine_pin.h @@ -108,7 +108,7 @@ #define MICROPY_HW_ENABLE_GPIO9 (1) #define MICROPY_HW_ENABLE_GPIO10 (1) #define MICROPY_HW_ENABLE_GPIO11 (1) -#if !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG +#if !MICROPY_HW_ESP_USB_SERIAL_JTAG #define MICROPY_HW_ENABLE_GPIO12 (1) #define MICROPY_HW_ENABLE_GPIO13 (1) #endif From e75ffc3bccb8b213704f0ddd2ce82731ab2069ba Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 11 Mar 2025 10:50:22 +0000 Subject: [PATCH 377/546] rp2/modrp2: Fix rp2.bootsel_button() function for RP2350. Co-authored-by: graham sanderson Signed-off-by: Phil Howard --- ports/rp2/modrp2.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ports/rp2/modrp2.c b/ports/rp2/modrp2.c index c90b6d5840208..5a43c11e71928 100644 --- a/ports/rp2/modrp2.c +++ b/ports/rp2/modrp2.c @@ -42,11 +42,17 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_network_country_obj); #endif +#define CS_PIN_INDEX 1 + +#if PICO_RP2040 +#define CS_BIT (1u << CS_PIN_INDEX) +#else +#define CS_BIT SIO_GPIO_HI_IN_QSPI_CSN_BITS +#endif + // Improved version of // https://github.com/raspberrypi/pico-examples/blob/master/picoboard/button/button.c static bool __no_inline_not_in_flash_func(bootsel_button)(void) { - const uint CS_PIN_INDEX = 1; - // Disable interrupts and the other core since they might be // executing code from flash and we are about to temporarily // disable flash access. @@ -65,7 +71,7 @@ static bool __no_inline_not_in_flash_func(bootsel_button)(void) { // The HI GPIO registers in SIO can observe and control the 6 QSPI pins. // The button pulls the QSPI_SS pin *low* when pressed. - bool button_state = !(sio_hw->gpio_hi_in & (1 << CS_PIN_INDEX)); + bool button_state = !(sio_hw->gpio_hi_in & CS_BIT); // Restore the QSPI_SS pin so we can use flash again. hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl, From 9a070fee343bb1f96b908ce3bb3032a8ec86c904 Mon Sep 17 00:00:00 2001 From: Alex Brudner Date: Mon, 3 Mar 2025 17:09:56 -0700 Subject: [PATCH 378/546] rp2/boards/SPARKFUN_IOTREDBOARD_RP2350: Add support for IoT RedBoard. Signed-off-by: Alex Brudner Signed-off-by: Malcolm McKellips --- .../SPARKFUN_IOTREDBOARD_RP2350/board.json | 26 +++ .../SPARKFUN_IOTREDBOARD_RP2350/manifest.py | 7 + .../mpconfigboard.cmake | 20 ++ .../mpconfigboard.h | 61 ++++++ .../mpconfigvariant.cmake | 1 + .../mpconfigvariant_RISCV.cmake | 1 + .../SPARKFUN_IOTREDBOARD_RP2350/pins.csv | 70 +++++++ .../sparkfun_iotredboard_rp2350.h | 183 ++++++++++++++++++ 8 files changed, 369 insertions(+) create mode 100644 ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/board.json create mode 100644 ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/manifest.py create mode 100644 ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigboard.cmake create mode 100644 ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigboard.h create mode 100644 ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigvariant.cmake create mode 100644 ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigvariant_RISCV.cmake create mode 100644 ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/pins.csv create mode 100644 ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/sparkfun_iotredboard_rp2350.h diff --git a/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/board.json b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/board.json new file mode 100644 index 0000000000000..a0dd4d835a1bc --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/board.json @@ -0,0 +1,26 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "BLE", + "Dual-core", + "External Flash", + "USB-C", + "WiFi", + "External RAM", + "microSD", + "RGB LED", + "JST-SH", + "Battery Charging" + ], + "images": [ + "27708-IoT-RedBoard-RP2350-Feature.jpg" + ], + "mcu": "rp2350", + "product": "SparkFun IoT RedBoard RP2350", + "thumbnail": "", + "url": "https://www.sparkfun.com/sparkfun-iot-redboard-rp2350.html", + "vendor": "SparkFun" +} diff --git a/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/manifest.py b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/manifest.py new file mode 100644 index 0000000000000..b1670d6804495 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/manifest.py @@ -0,0 +1,7 @@ +include("$(PORT_DIR)/boards/manifest.py") + +require("bundle-networking") + +require("aioble") + +require("sdcard") diff --git a/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigboard.cmake b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigboard.cmake new file mode 100644 index 0000000000000..beefc90db6557 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigboard.cmake @@ -0,0 +1,20 @@ +# cmake file for SparkFun IoT RedBoard RP2350 + +# TODO: DELETE THIS LINE WHEN SUBMODULED PICO-SDK INCLUDES THIS BOARD +set(PICO_BOARD_HEADER_DIRS ${MICROPY_PORT_DIR}/boards/${MICROPY_BOARD}) + +set(PICO_BOARD "sparkfun_iotredboard_rp2350") +set(PICO_PLATFORM "rp2350") + +set(PICO_NUM_GPIOS 48) + +set(MICROPY_PY_LWIP ON) +set(MICROPY_PY_NETWORK_CYW43 ON) + +# Bluetooth +set(MICROPY_PY_BLUETOOTH ON) +set(MICROPY_BLUETOOTH_BTSTACK ON) +set(MICROPY_PY_BLUETOOTH_CYW43 ON) + +# Board specific version of the frozen manifest +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigboard.h new file mode 100644 index 0000000000000..34d08b0bec4b3 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigboard.h @@ -0,0 +1,61 @@ +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "SparkFun IoT RedBoard RP2350" +#define MICROPY_HW_FLASH_STORAGE_BYTES (PICO_FLASH_SIZE_BYTES - 1536 * 1024) + +// Enable networking. +#define MICROPY_PY_NETWORK 1 +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "IoTRedBoardRP2350" + +#define CYW43_USE_SPI (1) +#define CYW43_LWIP (1) +#define CYW43_GPIO (1) +#define CYW43_SPI_PIO (1) + +// USB VID/PID +#define MICROPY_HW_USB_VID (0x1B4F) +#define MICROPY_HW_USB_PID (0x0047) + +// UART0 +#define MICROPY_HW_UART0_TX (0) +#define MICROPY_HW_UART0_RX (1) +#define MICROPY_HW_UART0_CTS (30) +#define MICROPY_HW_UART0_RTS (31) + +// UART1 +#define MICROPY_HW_UART1_TX (40) +#define MICROPY_HW_UART1_RX (41) +#define MICROPY_HW_UART1_CTS (42) +#define MICROPY_HW_UART1_RTS (43) + +// I2C0 +#define MICROPY_HW_I2C0_SCL (5) +#define MICROPY_HW_I2C0_SDA (4) + +// I2C1 +#define MICROPY_HW_I2C1_SCL (31) +#define MICROPY_HW_I2C1_SDA (30) + +// SPI0 +#define MICROPY_HW_SPI0_SCK (22) +#define MICROPY_HW_SPI0_MOSI (23) +#define MICROPY_HW_SPI0_MISO (20) + +// SD Card/SPI1 +#define MICROPY_HW_SPI1_SCK (10) +#define MICROPY_HW_SPI1_MOSI (11) +#define MICROPY_HW_SPI1_MISO (8) + +// PSRAM +#define MICROPY_HW_PSRAM_CS_PIN (47) +#define MICROPY_HW_ENABLE_PSRAM (1) + +// #include "enable_cyw43.h" + +// For debugging mbedtls - also set +// Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose +// #define MODUSSL_MBEDTLS_DEBUG_LEVEL 1 + +#define MICROPY_HW_PIN_EXT_COUNT CYW43_WL_GPIO_COUNT + +int mp_hal_is_pin_reserved(int n); +#define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i) diff --git a/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigvariant.cmake b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigvariant.cmake new file mode 100644 index 0000000000000..6fe039ba51bba --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigvariant.cmake @@ -0,0 +1 @@ +set(PICO_PLATFORM "rp2350") diff --git a/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigvariant_RISCV.cmake b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigvariant_RISCV.cmake new file mode 100644 index 0000000000000..65a97fc3350d1 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/mpconfigvariant_RISCV.cmake @@ -0,0 +1 @@ +set(PICO_PLATFORM "rp2350-riscv") diff --git a/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/pins.csv b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/pins.csv new file mode 100644 index 0000000000000..d8d98e1ada9ef --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/pins.csv @@ -0,0 +1,70 @@ +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 +GP23,GPIO23 +GP25,GPIO25 +GP26,GPIO26 +GP27,GPIO27 +GP28,GPIO28 +GP29,GPIO29 +GP30,GPIO30 +GP31,GPIO31 +GP32,GPIO32 +GP33,GPIO33 +GP34,GPIO34 +GP35,GPIO35 +GP39,GPIO39 +GP40,GPIO40 +GP41,GPIO41 +GP42,GPIO42 +GP43,GPIO43 +GP44,GPIO44 +GP45,GPIO45 +WL_GPIO0,EXT_GPIO0 +WL_GPIO1,EXT_GPIO1 +WL_GPIO2,EXT_GPIO2 +WL_LED,EXT_GPIO0 +SD_DET,GPIO2 +RGB_LED,GPIO3 +NEOPIXEL,GPIO3 +B_ALRT,GPIO6 +PERIPH_POWER,GPIO7 +HSTX0,GPIO12 +HSTX1,GPIO13 +HSTX2,GPIO14 +HSTX3,GPIO15 +HSTX4,GPIO16 +HSTX5,GPIO17 +HSTX6,GPIO18 +HSTX7,GPIO19 +LED,GPIO25 +STAT,GPIO25 +SRC_5V,GPIO26 +BATT_PWR,GPIO27 +USER_SW,GPIO39 +A0,GPIO40 +A1,GPIO41 +A2,GPIO42 +A3,GPIO43 +A4,GPIO44 +A5,GPIO45 diff --git a/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/sparkfun_iotredboard_rp2350.h b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/sparkfun_iotredboard_rp2350.h new file mode 100644 index 0000000000000..ac0972eb226a6 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTREDBOARD_RP2350/sparkfun_iotredboard_rp2350.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2024 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// ----------------------------------------------------- +// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO +// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES +// ----------------------------------------------------- +// Board definition for the SparkFun IoT RedBoard - RP2350 +// +// This header may be included by other board headers as "boards/sparkfun_iotredboard_rp2350.h" + +// pico_cmake_set PICO_PLATFORM=rp2350 +// pico_cmake_set PICO_CYW43_SUPPORTED = 1 + +#ifndef _BOARDS_SPARKFUN_IOTREDBOARD_RP2350_H +#define _BOARDS_SPARKFUN_IOTREDBOARD_RP2350_H + +// For board detection +#define SPARKFUN_IOTREDBOARD_RP2350 + +// --- RP2350 VARIANT --- +#define PICO_RP2350A 0 // 1 for RP2350A, 0 for RP2350B + +// --- BOARD SPECIFIC --- +#define SPARKFUN_IOTREDBOARD_RP2350_USER_SW_PIN 39 +#define SPARKFUN_IOTREDBOARD_RP2350_PSRAM_CS_PIN 47 + + +// --- UART --- +#ifndef PICO_DEFAULT_UART +#define PICO_DEFAULT_UART 0 +#endif +#ifndef PICO_DEFAULT_UART_TX_PIN +#define PICO_DEFAULT_UART_TX_PIN 0 +#endif +#ifndef PICO_DEFAULT_UART_RX_PIN +#define PICO_DEFAULT_UART_RX_PIN 1 +#endif + +// --- LED --- +#ifndef PICO_DEFAULT_LED_PIN +#define PICO_DEFAULT_LED_PIN 25 +#endif + +#ifndef PICO_DEFAULT_WS2812_PIN +#define PICO_DEFAULT_WS2812_PIN 3 +#endif + +// --- I2C --- Qwiic connector is on these pins +#ifndef PICO_DEFAULT_I2C +#define PICO_DEFAULT_I2C 0 +#endif +#ifndef PICO_DEFAULT_I2C_SDA_PIN +#define PICO_DEFAULT_I2C_SDA_PIN 4 +#endif +#ifndef PICO_DEFAULT_I2C_SCL_PIN +#define PICO_DEFAULT_I2C_SCL_PIN 5 +#endif + +// --- SPI --- +#ifndef PICO_DEFAULT_SPI +#define PICO_DEFAULT_SPI 0 +#endif +#ifndef PICO_DEFAULT_SPI_SCK_PIN +#define PICO_DEFAULT_SPI_SCK_PIN 22 +#endif +#ifndef PICO_DEFAULT_SPI_TX_PIN +#define PICO_DEFAULT_SPI_TX_PIN 23 +#endif +#ifndef PICO_DEFAULT_SPI_RX_PIN +#define PICO_DEFAULT_SPI_RX_PIN 20 +#endif +#ifndef PICO_DEFAULT_SPI_CSN_PIN +#define PICO_DEFAULT_SPI_CSN_PIN 21 +#endif + +// --- FLASH --- + +#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1 + +#ifndef PICO_FLASH_SPI_CLKDIV +#define PICO_FLASH_SPI_CLKDIV 2 +#endif + +// pico_cmake_set_default PICO_FLASH_SIZE_BYTES = (16 * 1024 * 1024) +#ifndef PICO_FLASH_SIZE_BYTES +#define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) +#endif + +// The IoT RedBoard has an SD Card. +#ifndef PICO_SD_CLK_PIN +#define PICO_SD_CLK_PIN 10 +#endif +#ifndef PICO_SD_CMD_PIN +#define PICO_SD_CMD_PIN 11 +#endif +#ifndef PICO_SD_DAT0_PIN +#define PICO_SD_DAT0_PIN 8 +#endif +#ifndef PICO_SD_DAT3_PIN +#define PICO_SD_DAT3_PIN 9 // DAT3 of the SD card is the chip select pin +#endif +#ifndef PICO_SD_DAT_PIN_COUNT +#define PICO_SD_DAT_PIN_COUNT 1 +#endif + +// The GPIO Pin used to monitor VSYS. Typically you would use this with ADC. +// There is an example in adc/read_vsys in pico-examples. +#ifndef PICO_VSYS_PIN +#define PICO_VSYS_PIN 46 +#endif + +// pico_cmake_set_default PICO_RP2350_A2_SUPPORTED = 1 +#ifndef PICO_RP2350_A2_SUPPORTED +#define PICO_RP2350_A2_SUPPORTED 1 +#endif + +// Bootloader activity LED in double reset mode. +#ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED +#define PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED PICO_DEFAULT_LED_PIN +#endif + +// Bootloader activity LED in USB reset mode. +#ifndef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED +#define PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED PICO_DEFAULT_LED_PIN +#endif + +// --- CYW43 --- + +#ifndef CYW43_WL_GPIO_COUNT +#define CYW43_WL_GPIO_COUNT 3 +#endif + +#ifndef CYW43_WL_GPIO_LED_PIN +#define CYW43_WL_GPIO_LED_PIN 0 +#endif + +// If CYW43_WL_GPIO_VBUS_PIN is defined then a CYW43 GPIO has to be used to read VBUS. +// This can be passed to cyw43_arch_gpio_get to determine if the device is battery powered. +// PICO_VBUS_PIN and CYW43_WL_GPIO_VBUS_PIN should not both be defined. +#ifndef CYW43_WL_GPIO_VBUS_PIN +#define CYW43_WL_GPIO_VBUS_PIN 2 +#endif + +// cyw43 SPI pins can't be changed at runtime +#ifndef CYW43_PIN_WL_DYNAMIC +#define CYW43_PIN_WL_DYNAMIC 0 +#endif + +// gpio pin to power up the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_REG_ON +#define CYW43_DEFAULT_PIN_WL_REG_ON 24u +#endif + +// gpio pin for spi data out to the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_DATA_OUT +#define CYW43_DEFAULT_PIN_WL_DATA_OUT 38u +#endif + +// gpio pin for spi data in from the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_DATA_IN +#define CYW43_DEFAULT_PIN_WL_DATA_IN 38u +#endif + +// gpio (irq) pin for the irq line from the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_HOST_WAKE +#define CYW43_DEFAULT_PIN_WL_HOST_WAKE 38u +#endif + +// gpio pin for the spi clock line to the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_CLOCK +#define CYW43_DEFAULT_PIN_WL_CLOCK 37u +#endif + +// gpio pin for the spi chip select to the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_CS +#define CYW43_DEFAULT_PIN_WL_CS 36u +#endif + +#endif // _BOARDS_SPARKFUN_IOTREDBOARD_RP2350_H From 8dcf9290f85c98695866e6656bf9a2653c490828 Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Sat, 8 Mar 2025 19:24:09 +1100 Subject: [PATCH 379/546] rp2/boards/WEACTSTUDIO_RP2350B_CORE: Add WeAct Studio RP2350B Core. Signed-off-by: Matt Trentini --- .../WEACTSTUDIO_RP2350B_CORE/board.json | 23 ++++++++ .../WEACTSTUDIO_RP2350B_CORE/manifest.py | 1 + .../mpconfigboard.cmake | 13 +++++ .../WEACTSTUDIO_RP2350B_CORE/mpconfigboard.h | 16 +++++ .../mpconfigvariant.cmake | 2 + .../mpconfigvariant_RISCV.cmake | 2 + .../boards/WEACTSTUDIO_RP2350B_CORE/pins.csv | 48 +++++++++++++++ .../weactstudio_rp2350b_core.h | 58 +++++++++++++++++++ 8 files changed, 163 insertions(+) create mode 100644 ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/board.json create mode 100644 ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/manifest.py create mode 100644 ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigboard.cmake create mode 100644 ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigboard.h create mode 100644 ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigvariant.cmake create mode 100644 ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigvariant_RISCV.cmake create mode 100644 ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/pins.csv create mode 100644 ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/weactstudio_rp2350b_core.h diff --git a/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/board.json b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/board.json new file mode 100644 index 0000000000000..bd280820fd3bb --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/board.json @@ -0,0 +1,23 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Dual-core", + "External Flash", + "External RAM", + "USB-C" + ], + "images": [ + "weactstudio_rp2350_core.png" + ], + "mcu": "rp2350", + "product": "RP2350B Core", + "thumbnail": "", + "url": "https://github.com/WeActStudio/WeActStudio.RP2350BCoreBoard", + "variants": { + "RISCV": "RISC V" + }, + "vendor": "WeAct Studio" +} diff --git a/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/manifest.py b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/manifest.py new file mode 100644 index 0000000000000..832942f052606 --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/manifest.py @@ -0,0 +1 @@ +include("$(PORT_DIR)/boards/manifest.py") diff --git a/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigboard.cmake b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigboard.cmake new file mode 100644 index 0000000000000..f03452c6273dc --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigboard.cmake @@ -0,0 +1,13 @@ +# CMake file for WeAct Studio RP2350B Core + +# The Core is powered by an RP2350B with 48 GPIOs +set(PICO_NUM_GPIOS 48) + +# The WeAct Studio boards don't have official pico-sdk support. +# So, add this board directory to the header search path and define PICO_BOARD +# which will instruct pico-sdk to look for weactstudio_rp2350b_core.h +list(APPEND PICO_BOARD_HEADER_DIRS ${MICROPY_BOARD_DIR}) +set(PICO_BOARD "weactstudio_rp2350b_core") + +# Freeze manifest and modules +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigboard.h b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigboard.h new file mode 100644 index 0000000000000..1f0963db85c47 --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigboard.h @@ -0,0 +1,16 @@ +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "WeAct Studio RP2350B Core" +#define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_BYTES (PICO_FLASH_SIZE_BYTES - (2 * 1024 * 1024)) + +// TODO: Split PSRAM option off as a variant +#define MICROPY_HW_PSRAM_CS_PIN (0) +#define MICROPY_HW_ENABLE_PSRAM (0) + +// Override machine_uart.c definitions. +// See weactstudio_rp2350b.h and note that the PICO_DEFAULT_UART configuration +// is not currently referenced in machine_uart.c. +#define MICROPY_HW_UART0_TX (16) +#define MICROPY_HW_UART0_RX (17) +#define MICROPY_HW_UART0_CTS (-1) +#define MICROPY_HW_UART0_RTS (-1) diff --git a/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigvariant.cmake b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigvariant.cmake new file mode 100644 index 0000000000000..531c91524402b --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigvariant.cmake @@ -0,0 +1,2 @@ +# Set the ARM-based RP2350 platform +set(PICO_PLATFORM "rp2350") diff --git a/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigvariant_RISCV.cmake b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigvariant_RISCV.cmake new file mode 100644 index 0000000000000..9f62e459aa053 --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/mpconfigvariant_RISCV.cmake @@ -0,0 +1,2 @@ +# Set the RISC-V-based RP2350 platform +set(PICO_PLATFORM "rp2350-riscv") diff --git a/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/pins.csv b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/pins.csv new file mode 100644 index 0000000000000..50d0de8560a33 --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/pins.csv @@ -0,0 +1,48 @@ +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 +GP29,GPIO29 +GP30,GPIO30 +GP31,GPIO31 +GP32,GPIO32 +GP33,GPIO33 +GP34,GPIO34 +GP35,GPIO35 +GP36,GPIO36 +GP37,GPIO37 +GP38,GPIO38 +GP39,GPIO39 +GP40,GPIO40 +GP41,GPIO41 +GP42,GPIO42 +GP43,GPIO43 +GP44,GPIO44 +GP45,GPIO45 +GP46,GPIO46 +GP47,GPIO47 +KEY,GPIO23 +LED,GPIO25 diff --git a/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/weactstudio_rp2350b_core.h b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/weactstudio_rp2350b_core.h new file mode 100644 index 0000000000000..bb00ff2827518 --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/weactstudio_rp2350b_core.h @@ -0,0 +1,58 @@ +#ifndef _BOARDS_WEACTSTUDIO_RP2350B_CORE_COMMON_H +#define _BOARDS_WEACTSTUDIO_RP2350B_CORE_COMMON_H + +// --- LED --- +#ifndef PICO_DEFAULT_LED_PIN +#define PICO_DEFAULT_LED_PIN 25 +#endif + +// Note: Avoid using Pin 0 for any default peripheral since it can be used for +// extra PSRAM/flash + +// --- UART --- +#ifndef PICO_DEFAULT_UART +#define PICO_DEFAULT_UART 0 +#endif +#ifndef PICO_DEFAULT_UART_TX_PIN +#define PICO_DEFAULT_UART_TX_PIN 12 +#endif +#ifndef PICO_DEFAULT_UART_RX_PIN +#define PICO_DEFAULT_UART_RX_PIN 13 +#endif + +// --- I2C --- +#ifndef PICO_DEFAULT_I2C +#define PICO_DEFAULT_I2C 0 +#endif +#ifndef PICO_DEFAULT_I2C_SDA_PIN +#define PICO_DEFAULT_I2C_SDA_PIN 8 +#endif +#ifndef PICO_DEFAULT_I2C_SCL_PIN +#define PICO_DEFAULT_I2C_SCL_PIN 9 +#endif + +// --- SPI --- +#ifndef PICO_DEFAULT_SPI +#define PICO_DEFAULT_SPI 0 +#endif +#ifndef PICO_DEFAULT_SPI_SCK_PIN +#define PICO_DEFAULT_SPI_SCK_PIN 18 +#endif +#ifndef PICO_DEFAULT_SPI_TX_PIN +#define PICO_DEFAULT_SPI_TX_PIN 19 +#endif +#ifndef PICO_DEFAULT_SPI_RX_PIN +#define PICO_DEFAULT_SPI_RX_PIN 16 +#endif +#ifndef PICO_DEFAULT_SPI_CSN_PIN +#define PICO_DEFAULT_SPI_CSN_PIN 17 +#endif + +// Flash configuration +#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1 + +#ifndef PICO_FLASH_SPI_CLKDIV +#define PICO_FLASH_SPI_CLKDIV 2 +#endif + +#endif From ac30dcb20cbe3cab73133623adc3a0dfff77dd20 Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Mon, 10 Mar 2025 17:34:47 -0600 Subject: [PATCH 380/546] rp2/boards/SPARKFUN_XRP_CONTROLLER: Add SparkFun XRP Controller. Signed-off-by: Dryw Wade --- .../boards/SPARKFUN_XRP_CONTROLLER/board.json | 25 ++++ .../SPARKFUN_XRP_CONTROLLER/manifest.py | 6 + .../mpconfigboard.cmake | 20 +++ .../SPARKFUN_XRP_CONTROLLER/mpconfigboard.h | 52 +++++++ .../boards/SPARKFUN_XRP_CONTROLLER/pins.csv | 84 +++++++++++ .../sparkfun_xrp_controller.h | 133 ++++++++++++++++++ 6 files changed, 320 insertions(+) create mode 100644 ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/board.json create mode 100644 ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/manifest.py create mode 100644 ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/mpconfigboard.cmake create mode 100644 ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/mpconfigboard.h create mode 100644 ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/pins.csv create mode 100644 ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/sparkfun_xrp_controller.h diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/board.json b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/board.json new file mode 100644 index 0000000000000..92486564b1067 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/board.json @@ -0,0 +1,25 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "BLE", + "Dual-core", + "External Flash", + "External RAM", + "IMU", + "JST-SH", + "RGB LED", + "USB-C", + "WiFi" + ], + "images": [ + "26619-XRP-Controller-Board-Feature.jpg" + ], + "mcu": "rp2350", + "product": "XRP Controller", + "thumbnail": "", + "url": "https://www.sparkfun.com/sparkfun-experiential-robotics-platform-xrp-controller.html", + "vendor": "Sparkfun" +} diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/manifest.py b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/manifest.py new file mode 100644 index 0000000000000..4e38f09cdee4f --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/manifest.py @@ -0,0 +1,6 @@ +include("$(PORT_DIR)/boards/manifest.py") + +require("bundle-networking") + +# Bluetooth +require("aioble") diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/mpconfigboard.cmake b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/mpconfigboard.cmake new file mode 100644 index 0000000000000..2af12f81382d0 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/mpconfigboard.cmake @@ -0,0 +1,20 @@ +# cmake file for SparkFun XRP Controller + +# TODO: DELETE THIS LINE WHEN SUBMODULED PICO-SDK INCLUDES THIS BOARD +set(PICO_BOARD_HEADER_DIRS ${MICROPY_PORT_DIR}/boards/${MICROPY_BOARD}) + +set(PICO_BOARD "sparkfun_xrp_controller") +set(PICO_PLATFORM "rp2350") + +set(PICO_NUM_GPIOS 48) + +set(MICROPY_PY_LWIP ON) +set(MICROPY_PY_NETWORK_CYW43 ON) + +# Bluetooth +set(MICROPY_PY_BLUETOOTH ON) +set(MICROPY_BLUETOOTH_BTSTACK ON) +set(MICROPY_PY_BLUETOOTH_CYW43 ON) + +# Board specific version of the frozen manifest +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/mpconfigboard.h new file mode 100644 index 0000000000000..13f04b92d7965 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/mpconfigboard.h @@ -0,0 +1,52 @@ +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "SparkFun XRP Controller" +#define MICROPY_HW_FLASH_STORAGE_BYTES (PICO_FLASH_SIZE_BYTES - 1536 * 1024) + +// Enable networking. +#define MICROPY_PY_NETWORK 1 +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "XRP" + +// CYW43 driver configuration. +#define CYW43_USE_SPI (1) +#define CYW43_LWIP (1) +#define CYW43_GPIO (1) +#define CYW43_SPI_PIO (1) + +// For debugging mbedtls - also set +// Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose +// #define MODUSSL_MBEDTLS_DEBUG_LEVEL 1 + +#define MICROPY_HW_PIN_EXT_COUNT CYW43_WL_GPIO_COUNT + +int mp_hal_is_pin_reserved(int n); +#define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i) + +#define MICROPY_HW_USB_VID (0x1B4F) +#define MICROPY_HW_USB_PID (0x0046) + +#define MICROPY_HW_I2C0_SDA (4) +#define MICROPY_HW_I2C0_SCL (5) + +#define MICROPY_HW_I2C1_SDA (38) +#define MICROPY_HW_I2C1_SCL (39) + +#define MICROPY_HW_SPI0_SCK (18) +#define MICROPY_HW_SPI0_MOSI (19) +#define MICROPY_HW_SPI0_MISO (16) + +#define MICROPY_HW_SPI1_SCK (14) +#define MICROPY_HW_SPI1_MOSI (15) +#define MICROPY_HW_SPI1_MISO (12) + +#define MICROPY_HW_UART0_TX (12) +#define MICROPY_HW_UART0_RX (13) +#define MICROPY_HW_UART0_CTS (14) +#define MICROPY_HW_UART0_RTS (15) + +#define MICROPY_HW_UART1_TX (8) +#define MICROPY_HW_UART1_RX (9) +#define MICROPY_HW_UART1_CTS (10) +#define MICROPY_HW_UART1_RTS (11) + +#define MICROPY_HW_PSRAM_CS_PIN (47) +#define MICROPY_HW_ENABLE_PSRAM (1) diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/pins.csv b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/pins.csv new file mode 100644 index 0000000000000..abf77af56c031 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/pins.csv @@ -0,0 +1,84 @@ +# XRP default pin names +MOTOR_L_IN_1,GPIO34 +MOTOR_L_IN_2,GPIO35 +MOTOR_R_IN_1,GPIO32 +MOTOR_R_IN_2,GPIO33 +MOTOR_3_IN_1,GPIO20 +MOTOR_3_IN_2,GPIO21 +MOTOR_4_IN_1,GPIO10 +MOTOR_4_IN_2,GPIO11 +MOTOR_L_ENCODER_A,GPIO30 +MOTOR_L_ENCODER_B,GPIO31 +MOTOR_R_ENCODER_A,GPIO24 +MOTOR_R_ENCODER_B,GPIO25 +MOTOR_3_ENCODER_A,GPIO22 +MOTOR_3_ENCODER_B,GPIO23 +MOTOR_4_ENCODER_A,GPIO2 +MOTOR_4_ENCODER_B,GPIO3 +MOTOR_L_CURRENT,GPIO40 +MOTOR_R_CURRENT,GPIO43 +MOTOR_3_CURRENT,GPIO41 +MOTOR_4_CURRENT,GPIO42 +SERVO_1,GPIO6 +SERVO_2,GPIO9 +SERVO_3,GPIO7 +SERVO_4,GPIO8 +I2C_SDA_0,GPIO4 +I2C_SCL_0,GPIO5 +I2C_SDA_1,GPIO38 +I2C_SCL_1,GPIO39 +DISTANCE_TRIGGER,GPIO0 +DISTANCE_ECHO,GPIO1 +LINE_L,GPIO44 +LINE_R,GPIO45 +BOARD_VIN_MEASURE,GPIO46 +BOARD_USER_BUTTON,GPIO36 +BOARD_NEOPIXEL,GPIO37 +BOARD_LED,EXT_GPIO0 + +# XRP alternate pin names +ML_IN_1,GPIO34 +ML_IN_2,GPIO35 +MR_IN_1,GPIO32 +MR_IN_2,GPIO33 +M3_IN_1,GPIO20 +M3_IN_2,GPIO21 +M4_IN_1,GPIO10 +M4_IN_2,GPIO11 +ML_ENC_A,GPIO30 +ML_ENC_B,GPIO31 +MR_ENC_A,GPIO24 +MR_ENC_B,GPIO25 +M3_ENC_A,GPIO22 +M3_ENC_B,GPIO23 +M4_ENC_A,GPIO2 +M4_ENC_B,GPIO3 +ML_CUR,GPIO40 +MR_CUR,GPIO43 +M3_CUR,GPIO41 +M4_CUR,GPIO42 +S1,GPIO6 +S2,GPIO9 +S3,GPIO7 +S4,GPIO8 +SDA_0,GPIO4 +SCL_0,GPIO5 +SDA_1,GPIO38 +SCL_1,GPIO39 +RANGE_TRIGGER,GPIO0 +RANGE_ECHO,GPIO1 +REFLECTANCE_L,GPIO44 +REFLECTANCE_R,GPIO45 +BRD_VIN,GPIO46 +BRD_USR_BTN,GPIO36 +BRD_RGB_LED,GPIO37 +BRD_LED,EXT_GPIO0 + +# LED default names +LED,EXT_GPIO0 +NEOPIXEL,GPIO37 + +# Radio GPIO pins +WL_GPIO0,EXT_GPIO0 +WL_GPIO1,EXT_GPIO1 +WL_GPIO2,EXT_GPIO2 diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/sparkfun_xrp_controller.h b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/sparkfun_xrp_controller.h new file mode 100644 index 0000000000000..70c2f13c753fe --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/sparkfun_xrp_controller.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2024 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// ----------------------------------------------------- +// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO +// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES +// ----------------------------------------------------- +// Board definition for the SparkFun XRP Controller +// +// This header may be included by other board headers as "boards/sparkfun_xrp_controller.h" + +// pico_cmake_set PICO_PLATFORM=rp2350 +// pico_cmake_set PICO_CYW43_SUPPORTED = 1 + +#ifndef _BOARDS_SPARKFUN_XRP_CONTROLLER_H +#define _BOARDS_SPARKFUN_XRP_CONTROLLER_H + +// For board detection +#define SPARKFUN_XRP_CONTROLLER + +// --- RP2350 VARIANT --- +#define PICO_RP2350A 0 + +// --- UART --- +#ifndef PICO_DEFAULT_UART +#define PICO_DEFAULT_UART 0 +#endif +#ifndef PICO_DEFAULT_UART_TX_PIN +#define PICO_DEFAULT_UART_TX_PIN 12 +#endif +#ifndef PICO_DEFAULT_UART_RX_PIN +#define PICO_DEFAULT_UART_RX_PIN 13 +#endif + +// --- LED --- +// no PICO_DEFAULT_LED_PIN - LED is on Wireless chip +#ifndef PICO_DEFAULT_WS2812_PIN +#define PICO_DEFAULT_WS2812_PIN 37 +#endif + +// --- I2C --- Qwiic connector is on these pins +#ifndef PICO_DEFAULT_I2C +#define PICO_DEFAULT_I2C 0 +#endif +#ifndef PICO_DEFAULT_I2C_SDA_PIN +#define PICO_DEFAULT_I2C_SDA_PIN 4 +#endif +#ifndef PICO_DEFAULT_I2C_SCL_PIN +#define PICO_DEFAULT_I2C_SCL_PIN 5 +#endif + +// --- SPI --- +#ifndef PICO_DEFAULT_SPI +#define PICO_DEFAULT_SPI 0 +#endif +#ifndef PICO_DEFAULT_SPI_SCK_PIN +#define PICO_DEFAULT_SPI_SCK_PIN 18 +#endif +#ifndef PICO_DEFAULT_SPI_TX_PIN +#define PICO_DEFAULT_SPI_TX_PIN 19 +#endif +#ifndef PICO_DEFAULT_SPI_RX_PIN +#define PICO_DEFAULT_SPI_RX_PIN 16 +#endif +#ifndef PICO_DEFAULT_SPI_CSN_PIN +#define PICO_DEFAULT_SPI_CSN_PIN 17 +#endif + +// --- FLASH --- + +#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1 + +#ifndef PICO_FLASH_SPI_CLKDIV +#define PICO_FLASH_SPI_CLKDIV 2 +#endif + +// pico_cmake_set_default PICO_FLASH_SIZE_BYTES = (16 * 1024 * 1024) +#ifndef PICO_FLASH_SIZE_BYTES +#define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) +#endif + +#ifndef CYW43_WL_GPIO_COUNT +#define CYW43_WL_GPIO_COUNT 3 +#endif + +#ifndef CYW43_WL_GPIO_LED_PIN +#define CYW43_WL_GPIO_LED_PIN 0 +#endif + +// pico_cmake_set_default PICO_RP2350_A2_SUPPORTED = 1 +#ifndef PICO_RP2350_A2_SUPPORTED +#define PICO_RP2350_A2_SUPPORTED 1 +#endif + +// cyw43 SPI pins can't be changed at runtime +#ifndef CYW43_PIN_WL_DYNAMIC +#define CYW43_PIN_WL_DYNAMIC 0 +#endif + +// gpio pin to power up the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_REG_ON +#define CYW43_DEFAULT_PIN_WL_REG_ON 26u +#endif + +// gpio pin for spi data out to the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_DATA_OUT +#define CYW43_DEFAULT_PIN_WL_DATA_OUT 29u +#endif + +// gpio pin for spi data in from the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_DATA_IN +#define CYW43_DEFAULT_PIN_WL_DATA_IN 29u +#endif + +// gpio (irq) pin for the irq line from the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_HOST_WAKE +#define CYW43_DEFAULT_PIN_WL_HOST_WAKE 29u +#endif + +// gpio pin for the spi clock line to the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_CLOCK +#define CYW43_DEFAULT_PIN_WL_CLOCK 28u +#endif + +// gpio pin for the spi chip select to the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_CS +#define CYW43_DEFAULT_PIN_WL_CS 27u +#endif + +#endif From 2264340559ed9139658e8d78a6a714ec27c92142 Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Mon, 10 Mar 2025 17:35:58 -0600 Subject: [PATCH 381/546] rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA: Fix XRP Controller Beta URL. Signed-off-by: Dryw Wade --- ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json index cece574326503..b7dbd016007d7 100644 --- a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json @@ -18,6 +18,6 @@ "mcu": "rp2040", "product": "XRP Controller (Beta)", "thumbnail": "", - "url": "https://www.sparkfun.com/sparkfun-experiential-robotics-platform-xrp-controller.html", + "url": "https://www.sparkfun.com/sparkfun-experiential-robotics-platform-xrp-controller-beta.html", "vendor": "Sparkfun" } From 416c6cf0c861da94d679f5aa739acf167165c2a4 Mon Sep 17 00:00:00 2001 From: Lesords <2385342343@qq.com> Date: Tue, 26 Nov 2024 02:25:37 +0000 Subject: [PATCH 382/546] rp2/boards/SEEED_XIAO_RP2350: Add new Seeed XIAO board definition. Signed-off-by: Lesords <2385342343@qq.com> --- ports/rp2/boards/SEEED_XIAO_RP2350/board.json | 24 +++++++++++++++++++ .../SEEED_XIAO_RP2350/mpconfigboard.cmake | 5 ++++ .../boards/SEEED_XIAO_RP2350/mpconfigboard.h | 3 +++ .../SEEED_XIAO_RP2350/mpconfigvariant.cmake | 1 + .../mpconfigvariant_RISCV.cmake | 1 + ports/rp2/boards/SEEED_XIAO_RP2350/pins.csv | 24 +++++++++++++++++++ 6 files changed, 58 insertions(+) create mode 100644 ports/rp2/boards/SEEED_XIAO_RP2350/board.json create mode 100644 ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigboard.cmake create mode 100644 ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigboard.h create mode 100644 ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigvariant.cmake create mode 100644 ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigvariant_RISCV.cmake create mode 100644 ports/rp2/boards/SEEED_XIAO_RP2350/pins.csv diff --git a/ports/rp2/boards/SEEED_XIAO_RP2350/board.json b/ports/rp2/boards/SEEED_XIAO_RP2350/board.json new file mode 100644 index 0000000000000..9198b17dbc95e --- /dev/null +++ b/ports/rp2/boards/SEEED_XIAO_RP2350/board.json @@ -0,0 +1,24 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Battery Charging", + "Dual-core", + "External Flash", + "RGB LED", + "USB" + ], + "images": [ + "xiao_rp2350-font.jpg" + ], + "mcu": "rp2350", + "product": "XIAO RP2350", + "thumbnail": "", + "url": "https://www.seeedstudio.com/Seeed-XIAO-RP2350-p-5944.html", + "variants": { + "RISCV": "RISC-V CPU mode" + }, + "vendor": "Seeed Studio" +} diff --git a/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigboard.cmake b/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigboard.cmake new file mode 100644 index 0000000000000..a22ea4a5c9df5 --- /dev/null +++ b/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigboard.cmake @@ -0,0 +1,5 @@ +# cmake file for Seeed XIAO RP2350 +set(PICO_BOARD "seeed_xiao_rp2350") + +# To change the gpio count for QFN-80 +# set(PICO_NUM_GPIOS 48) diff --git a/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigboard.h b/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigboard.h new file mode 100644 index 0000000000000..ea7e7437567e1 --- /dev/null +++ b/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigboard.h @@ -0,0 +1,3 @@ +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "Seeed XIAO RP2350" +#define MICROPY_HW_FLASH_STORAGE_BYTES (PICO_FLASH_SIZE_BYTES - 1024 * 1024) diff --git a/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigvariant.cmake b/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigvariant.cmake new file mode 100644 index 0000000000000..6fe039ba51bba --- /dev/null +++ b/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigvariant.cmake @@ -0,0 +1 @@ +set(PICO_PLATFORM "rp2350") diff --git a/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigvariant_RISCV.cmake b/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigvariant_RISCV.cmake new file mode 100644 index 0000000000000..65a97fc3350d1 --- /dev/null +++ b/ports/rp2/boards/SEEED_XIAO_RP2350/mpconfigvariant_RISCV.cmake @@ -0,0 +1 @@ +set(PICO_PLATFORM "rp2350-riscv") diff --git a/ports/rp2/boards/SEEED_XIAO_RP2350/pins.csv b/ports/rp2/boards/SEEED_XIAO_RP2350/pins.csv new file mode 100644 index 0000000000000..34b71a57c620e --- /dev/null +++ b/ports/rp2/boards/SEEED_XIAO_RP2350/pins.csv @@ -0,0 +1,24 @@ +D0,GPIO26 +D1,GPIO27 +D2,GPIO28 +D3,GPIO5 +D4,GPIO6 +D5,GPIO7 +D6,GPIO0 +D7,GPIO1 +D8,GPIO2 +D9,GPIO4 +D10,GPIO3 +D11,GPIO21 +D12,GPIO20 +D13,GPIO17 +D14,GPIO16 +D15,GPIO11 +D16,GPIO12 +D17,GPIO10 +D18,GPIO9 +LED,GPIO25 +NEOPIXEL_POWER,GPIO23 +NEOPIXEL,GPIO22 +BAT_ADC_EN,GPIO19 +BAT_ADC,GPIO29 From bf9cdd21891d30807ad6e0e229774d589cd5709f Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Mar 2025 13:23:13 +1100 Subject: [PATCH 383/546] stm32: Rename ROMFS partition config variables to start at index 0. Change ROMFS partition configuration variables to use index 0 as the starting partition number (instead of index 1). Reasons to do this: - `vfs.rom_ioctl()` numbers the partitions starting from 0 - `mpremote romfs -p ` numbers the partitions starting from 0 Signed-off-by: Damien George --- ports/stm32/boards/PYBD_SF2/f722_qspi.ld | 4 ++-- ports/stm32/boards/PYBD_SF2/mpconfigboard.h | 2 +- ports/stm32/boards/PYBD_SF6/f767.ld | 4 ++-- ports/stm32/boards/PYBD_SF6/mpconfigboard.h | 2 +- ports/stm32/mpconfigboard_common.h | 10 +++++----- ports/stm32/vfs_rom_ioctl.c | 20 ++++++++++---------- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ports/stm32/boards/PYBD_SF2/f722_qspi.ld b/ports/stm32/boards/PYBD_SF2/f722_qspi.ld index 05126233ec7ca..f5e6769834a09 100644 --- a/ports/stm32/boards/PYBD_SF2/f722_qspi.ld +++ b/ports/stm32/boards/PYBD_SF2/f722_qspi.ld @@ -41,8 +41,8 @@ _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; /* ROMFS location */ -_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); -_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); /* Define output sections */ SECTIONS diff --git a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h index e8923bf9b66e5..3ae11125d8b1b 100644 --- a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h +++ b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h @@ -67,7 +67,7 @@ void board_sleep(int value); // ROMFS config #define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) #define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev2.spiflash) -#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) // SPI flash #1, for R/W storage #define MICROPY_HW_SOFTQSPI_SCK_LOW(self) (GPIOE->BSRR = (0x10000 << 11)) diff --git a/ports/stm32/boards/PYBD_SF6/f767.ld b/ports/stm32/boards/PYBD_SF6/f767.ld index 68da1f19d40dc..0ee6cf76355fe 100644 --- a/ports/stm32/boards/PYBD_SF6/f767.ld +++ b/ports/stm32/boards/PYBD_SF6/f767.ld @@ -38,7 +38,7 @@ _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; /* ROMFS location */ -_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); -_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); INCLUDE common_bl.ld diff --git a/ports/stm32/boards/PYBD_SF6/mpconfigboard.h b/ports/stm32/boards/PYBD_SF6/mpconfigboard.h index 526a111177987..45261523bdaa0 100644 --- a/ports/stm32/boards/PYBD_SF6/mpconfigboard.h +++ b/ports/stm32/boards/PYBD_SF6/mpconfigboard.h @@ -48,7 +48,7 @@ // ROMFS config #define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) #define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev2.spiflash) -#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) // Extra UART config #define MICROPY_HW_UART7_TX (pyb_pin_W16) diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 2a650077f3a45..e01a4d4b87e0b 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -77,16 +77,16 @@ #define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (0) #endif +// Whether to enable ROMFS partition 0. +#ifndef MICROPY_HW_ROMFS_ENABLE_PART0 +#define MICROPY_HW_ROMFS_ENABLE_PART0 (0) +#endif + // Whether to enable ROMFS partition 1. #ifndef MICROPY_HW_ROMFS_ENABLE_PART1 #define MICROPY_HW_ROMFS_ENABLE_PART1 (0) #endif -// Whether to enable ROMFS partition 2. -#ifndef MICROPY_HW_ROMFS_ENABLE_PART2 -#define MICROPY_HW_ROMFS_ENABLE_PART2 (0) -#endif - // Whether to enable storage on the internal flash of the MCU #ifndef MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE #define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1) diff --git a/ports/stm32/vfs_rom_ioctl.c b/ports/stm32/vfs_rom_ioctl.c index 5024d44e5adc8..1fa4408c53796 100644 --- a/ports/stm32/vfs_rom_ioctl.c +++ b/ports/stm32/vfs_rom_ioctl.c @@ -36,6 +36,13 @@ #if MICROPY_VFS_ROM_IOCTL +#if MICROPY_HW_ROMFS_ENABLE_PART0 && !defined(MICROPY_HW_ROMFS_PART0_START) +#define MICROPY_HW_ROMFS_PART0_START (uintptr_t)(&_micropy_hw_romfs_part0_start) +#define MICROPY_HW_ROMFS_PART0_SIZE (uintptr_t)(&_micropy_hw_romfs_part0_size) +extern uint8_t _micropy_hw_romfs_part0_start; +extern uint8_t _micropy_hw_romfs_part0_size; +#endif + #if MICROPY_HW_ROMFS_ENABLE_PART1 && !defined(MICROPY_HW_ROMFS_PART1_START) #define MICROPY_HW_ROMFS_PART1_START (uintptr_t)(&_micropy_hw_romfs_part1_start) #define MICROPY_HW_ROMFS_PART1_SIZE (uintptr_t)(&_micropy_hw_romfs_part1_size) @@ -43,22 +50,15 @@ extern uint8_t _micropy_hw_romfs_part1_start; extern uint8_t _micropy_hw_romfs_part1_size; #endif -#if MICROPY_HW_ROMFS_ENABLE_PART2 && !defined(MICROPY_HW_ROMFS_PART2_START) -#define MICROPY_HW_ROMFS_PART2_START (uintptr_t)(&_micropy_hw_romfs_part2_start) -#define MICROPY_HW_ROMFS_PART2_SIZE (uintptr_t)(&_micropy_hw_romfs_part2_size) -extern uint8_t _micropy_hw_romfs_part2_start; -extern uint8_t _micropy_hw_romfs_part2_size; -#endif - #define ROMFS_MEMORYVIEW(base, size) {{&mp_type_memoryview}, 'B', 0, (size), (void *)(base)} static const mp_obj_array_t romfs_obj_table[] = { + #if MICROPY_HW_ROMFS_ENABLE_PART0 + ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART0_START, MICROPY_HW_ROMFS_PART0_SIZE), + #endif #if MICROPY_HW_ROMFS_ENABLE_PART1 ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART1_START, MICROPY_HW_ROMFS_PART1_SIZE), #endif - #if MICROPY_HW_ROMFS_ENABLE_PART2 - ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART2_START, MICROPY_HW_ROMFS_PART2_SIZE), - #endif }; mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { From d5aeca2e83a03657ee2b5b21225d5a10aba10f56 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Mar 2025 13:23:50 +1100 Subject: [PATCH 384/546] esp8266: Rename ROMFS partition config variables to include "part0". For consistency with the stm32 port. Signed-off-by: Damien George --- ports/esp8266/boards/esp8266_2MiB_ROMFS.ld | 4 ++-- ports/esp8266/modesp.c | 4 ++-- ports/esp8266/vfs_rom_ioctl.c | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ports/esp8266/boards/esp8266_2MiB_ROMFS.ld b/ports/esp8266/boards/esp8266_2MiB_ROMFS.ld index 6020c3d485da6..ce057981aba88 100644 --- a/ports/esp8266/boards/esp8266_2MiB_ROMFS.ld +++ b/ports/esp8266/boards/esp8266_2MiB_ROMFS.ld @@ -17,8 +17,8 @@ MEMORY } /* define ROMFS extents */ -_micropy_hw_romfs_start = ORIGIN(FLASH_ROMFS); -_micropy_hw_romfs_size = LENGTH(FLASH_ROMFS); +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); /* define common sections and symbols */ INCLUDE boards/esp8266_common.ld diff --git a/ports/esp8266/modesp.c b/ports/esp8266/modesp.c index c88022945cfe6..e1f9d39687593 100644 --- a/ports/esp8266/modesp.c +++ b/ports/esp8266/modesp.c @@ -165,13 +165,13 @@ static MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_size_obj, esp_flash_size); extern byte _firmware_size[]; #if MICROPY_VFS_ROM_IOCTL -extern uint8_t _micropy_hw_romfs_size; +extern uint8_t _micropy_hw_romfs_part0_size; #endif static mp_obj_t esp_flash_user_start(void) { uint32_t flash_user_start = (uint32_t)_firmware_size; #if MICROPY_VFS_ROM_IOCTL - flash_user_start += (uint32_t)&_micropy_hw_romfs_size; + flash_user_start += (uint32_t)&_micropy_hw_romfs_part0_size; #endif return MP_OBJ_NEW_SMALL_INT(flash_user_start); } diff --git a/ports/esp8266/vfs_rom_ioctl.c b/ports/esp8266/vfs_rom_ioctl.c index c7528375becd0..eb260cb50f471 100644 --- a/ports/esp8266/vfs_rom_ioctl.c +++ b/ports/esp8266/vfs_rom_ioctl.c @@ -35,13 +35,13 @@ #define FLASH_MEM_BASE (0x40200000) #define FLASH_PAGE_SIZE (4096) -#define MICROPY_HW_ROMFS_BASE (uintptr_t)(&_micropy_hw_romfs_start) -#define MICROPY_HW_ROMFS_BYTES (uintptr_t)(&_micropy_hw_romfs_size) +#define MICROPY_HW_ROMFS_BASE (uintptr_t)(&_micropy_hw_romfs_part0_start) +#define MICROPY_HW_ROMFS_BYTES (uintptr_t)(&_micropy_hw_romfs_part0_size) #define ROMFS_SPI_FLASH_OFFSET (MICROPY_HW_ROMFS_BASE - FLASH_MEM_BASE) -extern uint8_t _micropy_hw_romfs_start; -extern uint8_t _micropy_hw_romfs_size; +extern uint8_t _micropy_hw_romfs_part0_start; +extern uint8_t _micropy_hw_romfs_part0_size; static const MP_DEFINE_MEMORYVIEW_OBJ(romfs_obj, 'B', 0, MICROPY_HW_ROMFS_BYTES, (void *)MICROPY_HW_ROMFS_BASE); From 043dc7959206e13d044fda5d590e1452f4a226df Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 14 Jan 2025 11:34:21 +0100 Subject: [PATCH 385/546] stm32/boards/ARDUINO_GIGA: Enable 4MiB ROMFS partition in ext flash. Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h | 9 +++++++-- ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h index fdf3128a3606d..cef45d730cc20 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h @@ -32,6 +32,11 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) #define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) +#define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev.spiflash) +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) + // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) #define MICROPY_HW_SPIFLASH_SOFT_RESET (1) @@ -125,8 +130,8 @@ void GIGA_board_low_power(int mode); // QSPI flash #1 for storage #define MICROPY_HW_QSPI_PRESCALER (2) // 100MHz #define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (27) -// Reserve 1MiB at the end for compatibility with alternate firmware that places WiFi blob here. -#define MICROPY_HW_SPIFLASH_SIZE_BITS (120 * 1024 * 1024) +// Reserve 4MiB for romfs and 1MiB for WiFi/BT firmware. +#define MICROPY_HW_SPIFLASH_SIZE_BITS (88 * 1024 * 1024) #define MICROPY_HW_QSPIFLASH_CS (pyb_pin_QSPI2_CS) #define MICROPY_HW_QSPIFLASH_SCK (pyb_pin_QSPI2_CLK) #define MICROPY_HW_QSPIFLASH_IO0 (pyb_pin_QSPI2_D0) diff --git a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld index 793a76b970292..e7bb950dbe6ce 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld +++ b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld @@ -13,11 +13,11 @@ MEMORY SRAM3 (xrw) : ORIGIN = 0x30040000, LENGTH = 32K /* SRAM3 D2 */ SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */ - FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */ FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */ FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */ FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */ FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */ + FLASH_ROMFS (rx) : ORIGIN = 0x90B00000, LENGTH = 4096K /* romfs partition in QSPI flash */ } /* produce a link error if there is not this amount of RAM for these sections */ @@ -44,6 +44,10 @@ _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); +/* Location of romfs filesystem */ +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); + /* OpenAMP shared memory region */ _openamp_shm_region_start = ORIGIN(SRAM4); _openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4); From edc927a1852dba9d444c842f7bcf11bbaeeaf092 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 14 Jan 2025 11:35:45 +0100 Subject: [PATCH 386/546] stm32/boards/ARDUINO_NICLA_VISION: Enable 4MiB ROMFS part in ext flash. Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h | 9 +++++++-- ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h index 505f8b6890169..47bf1be23d460 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h @@ -32,6 +32,11 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) #define MICROPY_HW_TIM_IS_RESERVED(id) (id == 3) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) +#define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev.spiflash) +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) + // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) #define MICROPY_HW_SPIFLASH_SOFT_RESET (1) @@ -130,8 +135,8 @@ void NICLAV_board_osc_enable(int enable); // QSPI flash for storage #define MICROPY_HW_QSPI_PRESCALER (2) // 100MHz #define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (27) -// Reserve 1MiB at the end for compatibility with alternate firmware that places WiFi blob here. -#define MICROPY_HW_SPIFLASH_SIZE_BITS (120 * 1024 * 1024) +// Reserve 4MiB for romfs and 1MiB for WiFi/BT firmware. +#define MICROPY_HW_SPIFLASH_SIZE_BITS (88 * 1024 * 1024) #define MICROPY_HW_QSPIFLASH_CS (pyb_pin_QSPI2_CS) #define MICROPY_HW_QSPIFLASH_SCK (pyb_pin_QSPI2_CLK) #define MICROPY_HW_QSPIFLASH_IO0 (pyb_pin_QSPI2_D0) diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld b/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld index 6d6ce279f299c..9bd7f49a8ecaf 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld @@ -13,11 +13,11 @@ MEMORY SRAM3 (xrw) : ORIGIN = 0x30040000, LENGTH = 32K /* SRAM3 D2 */ SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */ - FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */ FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */ FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */ FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */ FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */ + FLASH_ROMFS (rx) : ORIGIN = 0x90B00000, LENGTH = 4096K /* romfs partition in QSPI flash */ } _cm4_ram_start = ORIGIN(SRAM4); @@ -46,6 +46,10 @@ _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); +/* Location of romfs filesystem */ +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); + /* OpenAMP shared memory region */ _openamp_shm_region_start = ORIGIN(SRAM4); _openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4); From 96ce08e498a2c62b38aad78f84f2a8f438dc7e3d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 14 Jan 2025 11:36:22 +0100 Subject: [PATCH 387/546] stm32/boards/ARDUINO_PORTENTA_H7: Enable 4MiB ROMFS part in ext flash. Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h | 9 +++++++-- ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h index 900bf18673728..90a1469056634 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h @@ -32,6 +32,11 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) #define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) +#define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev.spiflash) +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) + // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) #define MICROPY_HW_SPIFLASH_SOFT_RESET (1) @@ -129,8 +134,8 @@ void PORTENTA_board_osc_enable(int enable); // QSPI flash #1 for storage #define MICROPY_HW_QSPI_PRESCALER (2) // 100MHz #define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (27) -// Reserve 1MiB at the end for compatibility with alternate firmware that places WiFi blob here. -#define MICROPY_HW_SPIFLASH_SIZE_BITS (120 * 1024 * 1024) +// Reserve 4MiB for romfs and 1MiB for WiFi/BT firmware. +#define MICROPY_HW_SPIFLASH_SIZE_BITS (88 * 1024 * 1024) #define MICROPY_HW_QSPIFLASH_CS (pyb_pin_QSPI2_CS) #define MICROPY_HW_QSPIFLASH_SCK (pyb_pin_QSPI2_CLK) #define MICROPY_HW_QSPIFLASH_IO0 (pyb_pin_QSPI2_D0) diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld b/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld index 793a76b970292..e7bb950dbe6ce 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld @@ -13,11 +13,11 @@ MEMORY SRAM3 (xrw) : ORIGIN = 0x30040000, LENGTH = 32K /* SRAM3 D2 */ SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */ - FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */ FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */ FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */ FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */ FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */ + FLASH_ROMFS (rx) : ORIGIN = 0x90B00000, LENGTH = 4096K /* romfs partition in QSPI flash */ } /* produce a link error if there is not this amount of RAM for these sections */ @@ -44,6 +44,10 @@ _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); +/* Location of romfs filesystem */ +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); + /* OpenAMP shared memory region */ _openamp_shm_region_start = ORIGIN(SRAM4); _openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4); From 9db2398009ebb156fc6790f87be337c29f83b5d0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Oct 2024 15:55:46 +1100 Subject: [PATCH 388/546] stm32/can: Refactor can.h API to not depend on pyboard can types. This is necessary for the machine.CAN implementation to use the same low-level functions. Includes some refactoring around FIFO selection as there was a footgun where CAN_FIFO0/1 are 0/1 but FDCAN_RX_FIFO0/1 are not. Added an explicit type for non-hardware-specific FIFO numbering. Also moved responsibility for re-enabling CAN receive interrupts into the higher layer (pyb_can.c layer) after calling can_receive(). Also includes this behaviour change for FDCAN boards: - Fix for boards with FDCAN not updating error status counters (num_error_warning, num_error_passive, num_bus_off). These are now updated the same as on boards with CAN Classic controllers, as documented. - Previously FDCAN boards would trigger the RX callback function on error events instead (passing undocumented irq numbers 3, 4, 5). This behaviour has been removed in favour of the documented behaviour of updating the status counters. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/stm32/can.c | 198 +++++++++++++++++----------------- ports/stm32/can.h | 84 ++++++++++----- ports/stm32/fdcan.c | 243 +++++++++++++++++++----------------------- ports/stm32/main.c | 6 +- ports/stm32/modpyb.c | 2 +- ports/stm32/pyb_can.c | 202 ++++++++++++++++++----------------- ports/stm32/pyb_can.h | 58 ++++++++++ 7 files changed, 431 insertions(+), 362 deletions(-) create mode 100644 ports/stm32/pyb_can.h diff --git a/ports/stm32/can.c b/ports/stm32/can.c index 490faeba709a2..47385e0fa7b5b 100644 --- a/ports/stm32/can.c +++ b/ports/stm32/can.c @@ -28,29 +28,15 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "can.h" +#include "pyb_can.h" #include "irq.h" #if MICROPY_HW_ENABLE_CAN -void can_init0(void) { - for (uint i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_can_obj_all)); i++) { - MP_STATE_PORT(pyb_can_obj_all)[i] = NULL; - } -} - -void can_deinit_all(void) { - for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_can_obj_all)); i++) { - pyb_can_obj_t *can_obj = MP_STATE_PORT(pyb_can_obj_all)[i]; - if (can_obj != NULL) { - can_deinit(can_obj); - } - } -} - #if !MICROPY_HW_ENABLE_FDCAN -bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart) { - CAN_InitTypeDef *init = &can_obj->can.Init; +bool can_init(CAN_HandleTypeDef *can, int can_id, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart) { + CAN_InitTypeDef *init = &can->Init; init->Mode = mode << 4; // shift-left so modes fit in a small-int init->Prescaler = prescaler; init->SJW = ((sjw - 1) & 3) << 24; @@ -67,7 +53,7 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ uint32_t sce_irq = 0; const machine_pin_obj_t *pins[2]; - switch (can_obj->can_id) { + switch (can_id) { #if defined(MICROPY_HW_CAN1_TX) case PYB_CAN_1: CANx = CAN1; @@ -107,21 +93,16 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ uint32_t pin_mode = MP_HAL_PIN_MODE_ALT; uint32_t pin_pull = MP_HAL_PIN_PULL_UP; for (int i = 0; i < 2; i++) { - if (!mp_hal_pin_config_alt(pins[i], pin_mode, pin_pull, AF_FN_CAN, can_obj->can_id)) { + if (!mp_hal_pin_config_alt(pins[i], pin_mode, pin_pull, AF_FN_CAN, can_id)) { return false; } } // init CANx - can_obj->can.Instance = CANx; - HAL_CAN_Init(&can_obj->can); + can->Instance = CANx; + HAL_CAN_Init(can); - can_obj->is_enabled = true; - can_obj->num_error_warning = 0; - can_obj->num_error_passive = 0; - can_obj->num_bus_off = 0; - - __HAL_CAN_ENABLE_IT(&can_obj->can, CAN_IT_ERR | CAN_IT_BOF | CAN_IT_EPV | CAN_IT_EWG); + __HAL_CAN_ENABLE_IT(can, CAN_IT_ERR | CAN_IT_BOF | CAN_IT_EPV | CAN_IT_EWG); NVIC_SetPriority(sce_irq, IRQ_PRI_CAN); HAL_NVIC_EnableIRQ(sce_irq); @@ -129,10 +110,9 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ return true; } -void can_deinit(pyb_can_obj_t *self) { - self->is_enabled = false; - HAL_CAN_DeInit(&self->can); - if (self->can.Instance == CAN1) { +void can_deinit(CAN_HandleTypeDef *can) { + HAL_CAN_DeInit(can); + if (can->Instance == CAN1) { HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn); HAL_NVIC_DisableIRQ(CAN1_RX1_IRQn); HAL_NVIC_DisableIRQ(CAN1_SCE_IRQn); @@ -140,7 +120,7 @@ void can_deinit(pyb_can_obj_t *self) { __HAL_RCC_CAN1_RELEASE_RESET(); __HAL_RCC_CAN1_CLK_DISABLE(); #if defined(CAN2) - } else if (self->can.Instance == CAN2) { + } else if (can->Instance == CAN2) { HAL_NVIC_DisableIRQ(CAN2_RX0_IRQn); HAL_NVIC_DisableIRQ(CAN2_RX1_IRQn); HAL_NVIC_DisableIRQ(CAN2_SCE_IRQn); @@ -149,7 +129,7 @@ void can_deinit(pyb_can_obj_t *self) { __HAL_RCC_CAN2_CLK_DISABLE(); #endif #if defined(CAN3) - } else if (self->can.Instance == CAN3) { + } else if (can->Instance == CAN3) { HAL_NVIC_DisableIRQ(CAN3_RX0_IRQn); HAL_NVIC_DisableIRQ(CAN3_RX1_IRQn); HAL_NVIC_DisableIRQ(CAN3_SCE_IRQn); @@ -160,7 +140,19 @@ void can_deinit(pyb_can_obj_t *self) { } } -void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t bank) { +void can_disable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo) { + __HAL_CAN_DISABLE_IT(can, ((fifo == CAN_RX_FIFO0) ? + (CAN_IT_FMP0 | CAN_IT_FF0 | CAN_IT_FOV0) : + (CAN_IT_FMP1 | CAN_IT_FF1 | CAN_IT_FOV1))); +} + +void can_enable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo, bool enable_msg_received) { + __HAL_CAN_ENABLE_IT(can, ((fifo == CAN_RX_FIFO0) ? + ((enable_msg_received ? CAN_IT_FMP0 : 0) | CAN_IT_FF0 | CAN_IT_FOV0) : + ((enable_msg_received ? CAN_IT_FMP1 : 0) | CAN_IT_FF1 | CAN_IT_FOV1))); +} + +void can_clearfilter(CAN_HandleTypeDef *can, uint32_t filter_num, uint8_t bank) { CAN_FilterConfTypeDef filter; filter.FilterIdHigh = 0; @@ -168,18 +160,18 @@ void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t bank) { filter.FilterMaskIdHigh = 0; filter.FilterMaskIdLow = 0; filter.FilterFIFOAssignment = CAN_FILTER_FIFO0; - filter.FilterNumber = f; + filter.FilterNumber = filter_num; filter.FilterMode = CAN_FILTERMODE_IDMASK; filter.FilterScale = CAN_FILTERSCALE_16BIT; filter.FilterActivation = DISABLE; filter.BankNumber = bank; - HAL_CAN_ConfigFilter(&self->can, &filter); + HAL_CAN_ConfigFilter(can, &filter); } -int can_receive(CAN_HandleTypeDef *can, int fifo, CanRxMsgTypeDef *msg, uint8_t *data, uint32_t timeout_ms) { +int can_receive(CAN_HandleTypeDef *can, can_rx_fifo_t fifo, CanRxMsgTypeDef *msg, uint8_t *data, uint32_t timeout_ms) { volatile uint32_t *rfr; - if (fifo == CAN_FIFO0) { + if (fifo == CAN_RX_FIFO0) { rfr = &can->Instance->RF0R; } else { rfr = &can->Instance->RF1R; @@ -222,13 +214,16 @@ int can_receive(CAN_HandleTypeDef *can, int fifo, CanRxMsgTypeDef *msg, uint8_t return 0; // success } -// We have our own version of CAN transmit so we can handle Timeout=0 correctly. -HAL_StatusTypeDef CAN_Transmit(CAN_HandleTypeDef *hcan, uint32_t Timeout) { +// Lightly modified version of HAL CAN_Transmit to handle Timeout=0 correctly +HAL_StatusTypeDef can_transmit(CAN_HandleTypeDef *hcan, CanTxMsgTypeDef *txmsg, uint8_t *data, uint32_t Timeout) { uint32_t transmitmailbox; uint32_t tickstart; uint32_t rqcpflag = 0; uint32_t txokflag = 0; + hcan->pTxMsg = txmsg; + (void)data; // Not needed here, caller has set it up as &tx_msg->Data + // Check the parameters assert_param(IS_CAN_IDTYPE(hcan->pTxMsg->IDE)); assert_param(IS_CAN_RTR(hcan->pTxMsg->RTR)); @@ -312,79 +307,90 @@ HAL_StatusTypeDef CAN_Transmit(CAN_HandleTypeDef *hcan, uint32_t Timeout) { } } -static void can_rx_irq_handler(uint can_id, uint fifo_id) { - mp_obj_t callback; - pyb_can_obj_t *self; - mp_obj_t irq_reason = MP_OBJ_NEW_SMALL_INT(0); - byte *state; - - self = MP_STATE_PORT(pyb_can_obj_all)[can_id - 1]; - - if (fifo_id == CAN_FIFO0) { - callback = self->rxcallback0; - state = &self->rx_state0; +// Workaround for the __HAL_CAN macros expecting a CAN_HandleTypeDef which we +// don't have in the ISR. Using this "fake" struct instead of CAN_HandleTypeDef +// so it's not possible to accidentally call an API that uses one of the other +// fields in the structure. +typedef struct { + CAN_TypeDef *Instance; +} fake_handle_t; + +static void can_rx_irq_handler(uint can_id, CAN_TypeDef *instance, can_rx_fifo_t fifo) { + uint32_t full_flag, full_int, overrun_flag, overrun_int, pending_int; + + const fake_handle_t handle = { + .Instance = instance, + }; + + if (fifo == CAN_RX_FIFO0) { + full_flag = CAN_FLAG_FF0; + full_int = CAN_IT_FF0; + overrun_flag = CAN_FLAG_FOV0; + overrun_int = CAN_IT_FOV0; + pending_int = CAN_IT_FMP0; } else { - callback = self->rxcallback1; - state = &self->rx_state1; + full_flag = CAN_FLAG_FF1; + full_int = CAN_IT_FF1; + overrun_flag = CAN_FLAG_FOV1; + overrun_int = CAN_IT_FOV1; + pending_int = CAN_IT_FMP1; } - switch (*state) { - case RX_STATE_FIFO_EMPTY: - __HAL_CAN_DISABLE_IT(&self->can, (fifo_id == CAN_FIFO0) ? CAN_IT_FMP0 : CAN_IT_FMP1); - irq_reason = MP_OBJ_NEW_SMALL_INT(0); - *state = RX_STATE_MESSAGE_PENDING; - break; - case RX_STATE_MESSAGE_PENDING: - __HAL_CAN_DISABLE_IT(&self->can, (fifo_id == CAN_FIFO0) ? CAN_IT_FF0 : CAN_IT_FF1); - __HAL_CAN_CLEAR_FLAG(&self->can, (fifo_id == CAN_FIFO0) ? CAN_FLAG_FF0 : CAN_FLAG_FF1); - irq_reason = MP_OBJ_NEW_SMALL_INT(1); - *state = RX_STATE_FIFO_FULL; - break; - case RX_STATE_FIFO_FULL: - __HAL_CAN_DISABLE_IT(&self->can, (fifo_id == CAN_FIFO0) ? CAN_IT_FOV0 : CAN_IT_FOV1); - __HAL_CAN_CLEAR_FLAG(&self->can, (fifo_id == CAN_FIFO0) ? CAN_FLAG_FOV0 : CAN_FLAG_FOV1); - irq_reason = MP_OBJ_NEW_SMALL_INT(2); - *state = RX_STATE_FIFO_OVERFLOW; - break; - case RX_STATE_FIFO_OVERFLOW: - // This should never happen - break; + bool full = __HAL_CAN_GET_FLAG(&handle, full_flag); + bool overrun = __HAL_CAN_GET_FLAG(&handle, overrun_flag); + + // Note: receive interrupt bits are disabled below, and re-enabled by the + // higher layer after calling can_receive() + + if (full) { + __HAL_CAN_DISABLE_IT(&handle, full_int); + __HAL_CAN_CLEAR_FLAG(&handle, full_flag); + if (!overrun) { + can_irq_handler(can_id, CAN_INT_FIFO_FULL, fifo); + } + } + if (overrun) { + __HAL_CAN_DISABLE_IT(&handle, overrun_int); + __HAL_CAN_CLEAR_FLAG(&handle, overrun_flag); + can_irq_handler(can_id, CAN_INT_FIFO_OVERFLOW, fifo); } - pyb_can_handle_callback(self, fifo_id, callback, irq_reason); + if (!(full || overrun)) { + // Process of elimination, if neither of the above + // FIFO status flags are set then message pending interrupt is what fired. + __HAL_CAN_DISABLE_IT(&handle, pending_int); + can_irq_handler(can_id, CAN_INT_MESSAGE_RECEIVED, fifo); + } } -static void can_sce_irq_handler(uint can_id) { - pyb_can_obj_t *self = MP_STATE_PORT(pyb_can_obj_all)[can_id - 1]; - if (self) { - self->can.Instance->MSR = CAN_MSR_ERRI; - uint32_t esr = self->can.Instance->ESR; - if (esr & CAN_ESR_BOFF) { - ++self->num_bus_off; - } else if (esr & CAN_ESR_EPVF) { - ++self->num_error_passive; - } else if (esr & CAN_ESR_EWGF) { - ++self->num_error_warning; - } +static void can_sce_irq_handler(uint can_id, CAN_TypeDef *instance) { + instance->MSR = CAN_MSR_ERRI; // Write to clear ERRIE interrupt + uint32_t esr = instance->ESR; + if (esr & CAN_ESR_BOFF) { + can_irq_handler(can_id, CAN_INT_ERR_BUS_OFF, 0); + } else if (esr & CAN_ESR_EPVF) { + can_irq_handler(can_id, CAN_INT_ERR_PASSIVE, 0); + } else if (esr & CAN_ESR_EWGF) { + can_irq_handler(can_id, CAN_INT_ERR_WARNING, 0); } } #if defined(MICROPY_HW_CAN1_TX) void CAN1_RX0_IRQHandler(void) { IRQ_ENTER(CAN1_RX0_IRQn); - can_rx_irq_handler(PYB_CAN_1, CAN_FIFO0); + can_rx_irq_handler(PYB_CAN_1, CAN1, CAN_RX_FIFO0); IRQ_EXIT(CAN1_RX0_IRQn); } void CAN1_RX1_IRQHandler(void) { IRQ_ENTER(CAN1_RX1_IRQn); - can_rx_irq_handler(PYB_CAN_1, CAN_FIFO1); + can_rx_irq_handler(PYB_CAN_1, CAN1, CAN_RX_FIFO1); IRQ_EXIT(CAN1_RX1_IRQn); } void CAN1_SCE_IRQHandler(void) { IRQ_ENTER(CAN1_SCE_IRQn); - can_sce_irq_handler(PYB_CAN_1); + can_sce_irq_handler(PYB_CAN_1, CAN1); IRQ_EXIT(CAN1_SCE_IRQn); } #endif @@ -392,19 +398,19 @@ void CAN1_SCE_IRQHandler(void) { #if defined(MICROPY_HW_CAN2_TX) void CAN2_RX0_IRQHandler(void) { IRQ_ENTER(CAN2_RX0_IRQn); - can_rx_irq_handler(PYB_CAN_2, CAN_FIFO0); + can_rx_irq_handler(PYB_CAN_2, CAN2, CAN_RX_FIFO0); IRQ_EXIT(CAN2_RX0_IRQn); } void CAN2_RX1_IRQHandler(void) { IRQ_ENTER(CAN2_RX1_IRQn); - can_rx_irq_handler(PYB_CAN_2, CAN_FIFO1); + can_rx_irq_handler(PYB_CAN_2, CAN2, CAN_RX_FIFO1); IRQ_EXIT(CAN2_RX1_IRQn); } void CAN2_SCE_IRQHandler(void) { IRQ_ENTER(CAN2_SCE_IRQn); - can_sce_irq_handler(PYB_CAN_2); + can_sce_irq_handler(PYB_CAN_2, CAN2); IRQ_EXIT(CAN2_SCE_IRQn); } #endif @@ -412,19 +418,19 @@ void CAN2_SCE_IRQHandler(void) { #if defined(MICROPY_HW_CAN3_TX) void CAN3_RX0_IRQHandler(void) { IRQ_ENTER(CAN3_RX0_IRQn); - can_rx_irq_handler(PYB_CAN_3, CAN_FIFO0); + can_rx_irq_handler(PYB_CAN_3, CAN3, CAN_RX_FIFO0); IRQ_EXIT(CAN3_RX0_IRQn); } void CAN3_RX1_IRQHandler(void) { IRQ_ENTER(CAN3_RX1_IRQn); - can_rx_irq_handler(PYB_CAN_3, CAN_FIFO1); + can_rx_irq_handler(PYB_CAN_3, CAN3, CAN_RX_FIFO1); IRQ_EXIT(CAN3_RX1_IRQn); } void CAN3_SCE_IRQHandler(void) { IRQ_ENTER(CAN3_SCE_IRQn); - can_sce_irq_handler(PYB_CAN_3); + can_sce_irq_handler(PYB_CAN_3, CAN3); IRQ_EXIT(CAN3_SCE_IRQn); } #endif diff --git a/ports/stm32/can.h b/ports/stm32/can.h index 45ac7187e0040..3422f4180d6df 100644 --- a/ports/stm32/can.h +++ b/ports/stm32/can.h @@ -26,10 +26,17 @@ #ifndef MICROPY_INCLUDED_STM32_CAN_H #define MICROPY_INCLUDED_STM32_CAN_H -#include "py/obj.h" +#include "py/mphal.h" #if MICROPY_HW_ENABLE_CAN +// This API provides a higher abstraction layer over the two STM32 +// CAN controller APIs provided by the ST HAL (one for their classic +// CAN controller, one for their FD CAN controller.) + +// There are two implementations, can.c and fdcan.c - only one is included in +// the build for a given board. + #define PYB_CAN_1 (1) #define PYB_CAN_2 (2) #define PYB_CAN_3 (3) @@ -61,31 +68,56 @@ typedef enum _rx_state_t { RX_STATE_FIFO_OVERFLOW, } rx_state_t; -typedef struct _pyb_can_obj_t { - mp_obj_base_t base; - mp_obj_t rxcallback0; - mp_obj_t rxcallback1; - mp_uint_t can_id : 8; - bool is_enabled : 1; - byte rx_state0; - byte rx_state1; - uint16_t num_error_warning; - uint16_t num_error_passive; - uint16_t num_bus_off; - CAN_HandleTypeDef can; -} pyb_can_obj_t; - -extern const mp_obj_type_t pyb_can_type; - -void can_init0(void); -void can_deinit_all(void); -bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart); -void can_deinit(pyb_can_obj_t *self); - -void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t bank); -int can_receive(CAN_HandleTypeDef *can, int fifo, CanRxMsgTypeDef *msg, uint8_t *data, uint32_t timeout_ms); -HAL_StatusTypeDef CAN_Transmit(CAN_HandleTypeDef *hcan, uint32_t Timeout); -void pyb_can_handle_callback(pyb_can_obj_t *self, uint fifo_id, mp_obj_t callback, mp_obj_t irq_reason); +typedef enum { + CAN_INT_MESSAGE_RECEIVED, + CAN_INT_FIFO_FULL, + CAN_INT_FIFO_OVERFLOW, // These first 3 correspond to pyb.CAN.rxcallback args + + CAN_INT_ERR_BUS_OFF, + CAN_INT_ERR_PASSIVE, + CAN_INT_ERR_WARNING, +} can_int_t; + +// RX FIFO numbering +// +// Note: For traditional CAN peripheral, the values of CAN_FIFO0 and CAN_FIFO1 are the same +// as this enum. FDCAN macros FDCAN_RX_FIFO0 and FDCAN_RX_FIFO1 are hardware offsets and not the same. +typedef enum { + CAN_RX_FIFO0, + CAN_RX_FIFO1, +} can_rx_fifo_t; + +bool can_init(CAN_HandleTypeDef *can, int can_id, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart); +void can_deinit(CAN_HandleTypeDef *can); + +void can_clearfilter(CAN_HandleTypeDef *can, uint32_t filter_num, uint8_t bank); +int can_receive(CAN_HandleTypeDef *can, can_rx_fifo_t fifo, CanRxMsgTypeDef *msg, uint8_t *data, uint32_t timeout_ms); +HAL_StatusTypeDef can_transmit(CAN_HandleTypeDef *hcan, CanTxMsgTypeDef *txmsg, uint8_t *data, uint32_t Timeout); + +// Disable all CAN receive interrupts for a FIFO +void can_disable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo); + +// Enable CAN receive interrupts for a FIFO. +// +// Interrupt for CAN_INT_MESSAGE_RECEIVED is only enabled if enable_msg_received is set. +void can_enable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo, bool enable_msg_received); + +// Implemented in pyb_can.c, called from lower layer +extern void can_irq_handler(uint can_id, can_int_t interrupt, can_rx_fifo_t fifo); + +#if MICROPY_HW_ENABLE_FDCAN + +static inline unsigned can_rx_pending(CAN_HandleTypeDef *can, can_rx_fifo_t fifo) { + return HAL_FDCAN_GetRxFifoFillLevel(can, fifo == CAN_RX_FIFO0 ? FDCAN_RX_FIFO0 : FDCAN_RX_FIFO1); +} + +#else + +static inline unsigned can_rx_pending(CAN_HandleTypeDef *can, can_rx_fifo_t fifo) { + return __HAL_CAN_MSG_PENDING(can, fifo == CAN_RX_FIFO0 ? CAN_FIFO0 : CAN_FIFO1); +} + +#endif // MICROPY_HW_ENABLE_FDCAN #endif // MICROPY_HW_ENABLE_CAN diff --git a/ports/stm32/fdcan.c b/ports/stm32/fdcan.c index 04d8135c081b3..b87ab48f1ccb7 100644 --- a/ports/stm32/fdcan.c +++ b/ports/stm32/fdcan.c @@ -28,6 +28,7 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "can.h" +#include "pyb_can.h" #include "irq.h" #if MICROPY_HW_ENABLE_CAN && MICROPY_HW_ENABLE_FDCAN @@ -44,9 +45,13 @@ #define FDCAN_ELEMENT_MASK_FIDX (0x7f000000) // Filter Index #define FDCAN_ELEMENT_MASK_ANMF (0x80000000) // Accepted Non-matching Frame -#define FDCAN_RX_FIFO0_MASK (FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST | FDCAN_FLAG_RX_FIFO0_FULL | FDCAN_FLAG_RX_FIFO0_NEW_MESSAGE) -#define FDCAN_RX_FIFO1_MASK (FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST | FDCAN_FLAG_RX_FIFO1_FULL | FDCAN_FLAG_RX_FIFO1_NEW_MESSAGE) -#define FDCAN_ERROR_STATUS_MASK (FDCAN_FLAG_ERROR_PASSIVE | FDCAN_FLAG_ERROR_WARNING | FDCAN_FLAG_BUS_OFF) +#define FDCAN_IT_RX_FIFO0_MASK (FDCAN_IT_RX_FIFO0_MESSAGE_LOST | FDCAN_IT_RX_FIFO0_FULL | FDCAN_IT_RX_FIFO0_NEW_MESSAGE) +#define FDCAN_IT_RX_FIFO1_MASK (FDCAN_IT_RX_FIFO1_MESSAGE_LOST | FDCAN_IT_RX_FIFO1_FULL | FDCAN_IT_RX_FIFO1_NEW_MESSAGE) +#define FDCAN_IT_ERROR_STATUS_MASK (FDCAN_IT_ERROR_PASSIVE | FDCAN_IT_ERROR_WARNING | FDCAN_IT_BUS_OFF) + +#define FDCAN_IT_RX_NEW_MESSAGE_MASK (FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_RX_FIFO1_NEW_MESSAGE) +#define FDCAN_IT_RX_FULL_MASK (FDCAN_IT_RX_FIFO0_FULL | FDCAN_IT_RX_FIFO1_FULL) +#define FDCAN_IT_RX_MESSAGE_LOST_MASK (FDCAN_IT_RX_FIFO0_MESSAGE_LOST | FDCAN_IT_RX_FIFO1_MESSAGE_LOST) #if defined(STM32H7) // adaptations for H7 to G4 naming convention in HAL @@ -63,10 +68,10 @@ // also defined in _hal_fdcan.c, but not able to declare extern and reach the variable const uint8_t DLCtoBytes[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64}; -bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart) { +bool can_init(CAN_HandleTypeDef *can, int can_id, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart) { (void)auto_restart; - FDCAN_InitTypeDef *init = &can_obj->can.Init; + FDCAN_InitTypeDef *init = &can->Init; // Configure FDCAN with FD frame and BRS support. init->FrameFormat = FDCAN_FRAME_FD_BRS; init->Mode = mode; @@ -94,7 +99,7 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ // To support 2 FDCAN instances simultaneously, the Message RAM is divided in half by // setting the second FDCAN memory offset to half the RAM size. With this configuration, // the maximum words per FDCAN instance is 1280 32-bit words. - if (can_obj->can_id == PYB_CAN_1) { + if (can_id == PYB_CAN_1) { init->MessageRAMOffset = 0; } else { init->MessageRAMOffset = FDCAN_MESSAGE_RAM_SIZE / 2; @@ -139,7 +144,7 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ FDCAN_GlobalTypeDef *CANx = NULL; const machine_pin_obj_t *pins[2]; - switch (can_obj->can_id) { + switch (can_id) { #if defined(MICROPY_HW_CAN1_TX) case PYB_CAN_1: CANx = FDCAN1; @@ -167,39 +172,34 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ uint32_t pin_mode = MP_HAL_PIN_MODE_ALT; uint32_t pin_pull = MP_HAL_PIN_PULL_UP; for (int i = 0; i < 2; ++i) { - if (!mp_hal_pin_config_alt(pins[i], pin_mode, pin_pull, AF_FN_CAN, can_obj->can_id)) { + if (!mp_hal_pin_config_alt(pins[i], pin_mode, pin_pull, AF_FN_CAN, can_id)) { return false; } } // init CANx - can_obj->can.Instance = CANx; + can->Instance = CANx; // catch bad configuration errors. - if (HAL_FDCAN_Init(&can_obj->can) != HAL_OK) { + if (HAL_FDCAN_Init(can) != HAL_OK) { return false; } // Disable acceptance of non-matching frames (enabled by default) - HAL_FDCAN_ConfigGlobalFilter(&can_obj->can, FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE); + HAL_FDCAN_ConfigGlobalFilter(can, FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE); // The configuration registers are locked after CAN is started. - HAL_FDCAN_Start(&can_obj->can); + HAL_FDCAN_Start(can); // Reset all filters for (int f = 0; f < init->StdFiltersNbr; ++f) { - can_clearfilter(can_obj, f, false); + can_clearfilter(can, f, false); } for (int f = 0; f < init->ExtFiltersNbr; ++f) { - can_clearfilter(can_obj, f, true); + can_clearfilter(can, f, true); } - can_obj->is_enabled = true; - can_obj->num_error_warning = 0; - can_obj->num_error_passive = 0; - can_obj->num_bus_off = 0; - - switch (can_obj->can_id) { + switch (can_id) { case PYB_CAN_1: NVIC_SetPriority(FDCAN1_IT0_IRQn, IRQ_PRI_CAN); HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn); @@ -218,22 +218,18 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ return false; } // FDCAN IT 0 - HAL_FDCAN_ConfigInterruptLines(&can_obj->can, FDCAN_IT_GROUP_RX_FIFO0 | FDCAN_IT_GROUP_BIT_LINE_ERROR | FDCAN_IT_GROUP_PROTOCOL_ERROR, FDCAN_INTERRUPT_LINE0); + HAL_FDCAN_ConfigInterruptLines(can, FDCAN_IT_GROUP_RX_FIFO0 | FDCAN_IT_GROUP_BIT_LINE_ERROR | FDCAN_IT_GROUP_PROTOCOL_ERROR, FDCAN_INTERRUPT_LINE0); // FDCAN IT 1 - HAL_FDCAN_ConfigInterruptLines(&can_obj->can, FDCAN_IT_GROUP_RX_FIFO1, FDCAN_INTERRUPT_LINE1); + HAL_FDCAN_ConfigInterruptLines(can, FDCAN_IT_GROUP_RX_FIFO1, FDCAN_INTERRUPT_LINE1); - uint32_t ActiveITs = FDCAN_IT_BUS_OFF | FDCAN_IT_ERROR_WARNING | FDCAN_IT_ERROR_PASSIVE; - ActiveITs |= FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_RX_FIFO1_NEW_MESSAGE; - ActiveITs |= FDCAN_IT_RX_FIFO0_MESSAGE_LOST | FDCAN_IT_RX_FIFO1_MESSAGE_LOST; - ActiveITs |= FDCAN_IT_RX_FIFO0_FULL | FDCAN_IT_RX_FIFO1_FULL; - HAL_FDCAN_ActivateNotification(&can_obj->can, ActiveITs, 0); + // Enable error interrupts. RX-related interrupts are enabled via can_enable_rx_interrupts() + HAL_FDCAN_ActivateNotification(can, FDCAN_IT_BUS_OFF | FDCAN_IT_ERROR_WARNING | FDCAN_IT_ERROR_PASSIVE, 0); return true; } -void can_deinit(pyb_can_obj_t *self) { - self->is_enabled = false; - HAL_FDCAN_DeInit(&self->can); - if (self->can.Instance == FDCAN1) { +void can_deinit(FDCAN_HandleTypeDef *can) { + HAL_FDCAN_DeInit(can); + if (can->Instance == FDCAN1) { HAL_NVIC_DisableIRQ(FDCAN1_IT0_IRQn); HAL_NVIC_DisableIRQ(FDCAN1_IT1_IRQn); // TODO check if FDCAN2 is used. @@ -241,7 +237,7 @@ void can_deinit(pyb_can_obj_t *self) { __HAL_RCC_FDCAN_RELEASE_RESET(); __HAL_RCC_FDCAN_CLK_DISABLE(); #if defined(MICROPY_HW_CAN2_TX) - } else if (self->can.Instance == FDCAN2) { + } else if (can->Instance == FDCAN2) { HAL_NVIC_DisableIRQ(FDCAN2_IT0_IRQn); HAL_NVIC_DisableIRQ(FDCAN2_IT1_IRQn); // TODO check if FDCAN2 is used. @@ -252,25 +248,52 @@ void can_deinit(pyb_can_obj_t *self) { } } -void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t extid) { - if (self && self->can.Instance) { - FDCAN_FilterTypeDef filter = {0}; - if (extid == 1) { - filter.IdType = FDCAN_EXTENDED_ID; - } else { - filter.IdType = FDCAN_STANDARD_ID; +void can_clearfilter(FDCAN_HandleTypeDef *can, uint32_t f, uint8_t extid) { + FDCAN_FilterTypeDef filter = {0}; + if (extid == 1) { + filter.IdType = FDCAN_EXTENDED_ID; + } else { + filter.IdType = FDCAN_STANDARD_ID; + } + filter.FilterIndex = f; + filter.FilterConfig = FDCAN_FILTER_DISABLE; + HAL_FDCAN_ConfigFilter(can, &filter); +} + +void can_disable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo) { + HAL_FDCAN_DeactivateNotification(can, (fifo == CAN_RX_FIFO0) ? FDCAN_IT_RX_FIFO0_MASK : FDCAN_IT_RX_FIFO1_MASK); +} + +void can_enable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo, bool enable_msg_received) { + uint32_t ints = (fifo == CAN_RX_FIFO0) ? FDCAN_IT_RX_FIFO0_MASK : FDCAN_IT_RX_FIFO1_MASK; + if (!enable_msg_received) { + ints &= FDCAN_IT_RX_NEW_MESSAGE_MASK; + } + HAL_FDCAN_ActivateNotification(can, ints, 0); +} + +HAL_StatusTypeDef can_transmit(CAN_HandleTypeDef *can, CanTxMsgTypeDef *txmsg, uint8_t *data, uint32_t timeout_ms) { + uint32_t start = HAL_GetTick(); + while (HAL_FDCAN_GetTxFifoFreeLevel(can) == 0) { + if (timeout_ms == 0) { + mp_raise_OSError(MP_ETIMEDOUT); + } + // Check for the Timeout + if (timeout_ms != HAL_MAX_DELAY) { + if (HAL_GetTick() - start >= timeout_ms) { + mp_raise_OSError(MP_ETIMEDOUT); + } } - filter.FilterIndex = f; - filter.FilterConfig = FDCAN_FILTER_DISABLE; - HAL_FDCAN_ConfigFilter(&self->can, &filter); + MICROPY_EVENT_POLL_HOOK } + return HAL_FDCAN_AddMessageToTxFifoQ(can, txmsg, data); } -int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr, uint8_t *data, uint32_t timeout_ms) { +int can_receive(FDCAN_HandleTypeDef *can, can_rx_fifo_t fifo, FDCAN_RxHeaderTypeDef *hdr, uint8_t *data, uint32_t timeout_ms) { volatile uint32_t *rxf, *rxa; uint32_t fl; - if (fifo == FDCAN_RX_FIFO0) { + if (fifo == CAN_RX_FIFO0) { rxf = &can->Instance->RXF0S; rxa = &can->Instance->RXF0A; fl = FDCAN_RXF0S_F0FL; @@ -293,7 +316,7 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr, // Get pointer to incoming message uint32_t index, *address; - if (fifo == FDCAN_RX_FIFO0) { + if (fifo == CAN_RX_FIFO0) { index = (*rxf & FDCAN_RXF0S_F0GI) >> FDCAN_RXF0S_F0GI_Pos; #if defined(STM32G4) address = (uint32_t *)(can->msgRam.RxFIFO0SA + (index * (18U * 4U))); // SRAMCAN_RF0_SIZE bytes, size not configurable @@ -303,7 +326,6 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr, } else { index = (*rxf & FDCAN_RXF1S_F1GI) >> FDCAN_RXF1S_F1GI_Pos; #if defined(STM32G4) - // ToDo: test FIFO1, FIFO 0 is ok address = (uint32_t *)(can->msgRam.RxFIFO1SA + (index * (18U * 4U))); // SRAMCAN_RF1_SIZE bytes, size not configurable #else address = (uint32_t *)(can->msgRam.RxFIFO1SA + (index * can->Init.RxFifo1ElmtSize * 4)); @@ -340,117 +362,66 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr, return 0; // success } -static void can_rx_irq_handler(uint can_id, uint fifo_id) { - mp_obj_t callback; - pyb_can_obj_t *self; - mp_obj_t irq_reason = MP_OBJ_NEW_SMALL_INT(0); - byte *state; - - self = MP_STATE_PORT(pyb_can_obj_all)[can_id - 1]; - - CAN_TypeDef *can = self->can.Instance; - - uint32_t RxFifo0ITs; - uint32_t RxFifo1ITs; - // uint32_t Errors; - uint32_t ErrorStatusITs; - uint32_t Psr; - - RxFifo0ITs = can->IR & FDCAN_RX_FIFO0_MASK; - RxFifo0ITs &= can->IE; - RxFifo1ITs = can->IR & FDCAN_RX_FIFO1_MASK; - RxFifo1ITs &= can->IE; - // Errors = (&self->can)->Instance->IR & FDCAN_ERROR_MASK; - // Errors &= (&self->can)->Instance->IE; - ErrorStatusITs = can->IR & FDCAN_ERROR_STATUS_MASK; - ErrorStatusITs &= can->IE; - Psr = can->PSR; - - if (fifo_id == FDCAN_RX_FIFO0) { - callback = self->rxcallback0; - state = &self->rx_state0; - if (RxFifo0ITs & FDCAN_FLAG_RX_FIFO0_NEW_MESSAGE) { - __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO0_NEW_MESSAGE); - __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO0_NEW_MESSAGE); - irq_reason = MP_OBJ_NEW_SMALL_INT(0); - *state = RX_STATE_MESSAGE_PENDING; - - } - if (RxFifo0ITs & FDCAN_FLAG_RX_FIFO0_FULL) { - __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO0_FULL); - __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO0_FULL); - irq_reason = MP_OBJ_NEW_SMALL_INT(1); - *state = RX_STATE_FIFO_FULL; +static void can_rx_irq_handler(uint can_id, CAN_TypeDef *instance, can_rx_fifo_t fifo) { + uint32_t ints, rx_fifo_ints, error_ints; - } - if (RxFifo0ITs & FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST) { - __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO0_MESSAGE_LOST); - __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST); - irq_reason = MP_OBJ_NEW_SMALL_INT(2); - *state = RX_STATE_FIFO_OVERFLOW; - } + ints = instance->IR & instance->IE; + if (fifo == CAN_RX_FIFO0) { + rx_fifo_ints = ints & FDCAN_IT_RX_FIFO0_MASK; } else { - callback = self->rxcallback1; - state = &self->rx_state1; - if (RxFifo1ITs & FDCAN_FLAG_RX_FIFO1_NEW_MESSAGE) { - __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO1_NEW_MESSAGE); - __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO1_NEW_MESSAGE); - irq_reason = MP_OBJ_NEW_SMALL_INT(0); - *state = RX_STATE_MESSAGE_PENDING; + rx_fifo_ints = ints & FDCAN_IT_RX_FIFO1_MASK; + } + error_ints = ints & FDCAN_IT_ERROR_STATUS_MASK; - } - if (RxFifo1ITs & FDCAN_FLAG_RX_FIFO1_FULL) { - __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO1_FULL); - __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO1_FULL); - irq_reason = MP_OBJ_NEW_SMALL_INT(1); - *state = RX_STATE_FIFO_FULL; + // Disable receive interrupts, re-enabled by higher layer after calling can_receive() + // (Note: can't use __HAL_CAN API as only have a CAN_TypeDef, not CAN_HandleTypeDef) + instance->IE &= ~rx_fifo_ints; + instance->IR = rx_fifo_ints | error_ints; + if (rx_fifo_ints) { + if (rx_fifo_ints & FDCAN_IT_RX_NEW_MESSAGE_MASK) { + can_irq_handler(can_id, CAN_INT_MESSAGE_RECEIVED, fifo); + } + if (rx_fifo_ints & FDCAN_IT_RX_FULL_MASK) { + can_irq_handler(can_id, CAN_INT_FIFO_FULL, fifo); } - if (RxFifo1ITs & FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST) { - __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO1_MESSAGE_LOST); - __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST); - irq_reason = MP_OBJ_NEW_SMALL_INT(2); - *state = RX_STATE_FIFO_OVERFLOW; + if (rx_fifo_ints & FDCAN_IT_RX_MESSAGE_LOST_MASK) { + can_irq_handler(can_id, CAN_INT_FIFO_OVERFLOW, fifo); } } - if (ErrorStatusITs & FDCAN_FLAG_ERROR_WARNING) { - __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_ERROR_WARNING); - if (Psr & FDCAN_PSR_EW) { - irq_reason = MP_OBJ_NEW_SMALL_INT(3); - // mp_printf(MICROPY_ERROR_PRINTER, "clear warning %08x\n", (can->IR & FDCAN_ERROR_STATUS_MASK)); + if (error_ints) { + uint32_t Psr = instance->PSR; + + if (error_ints & FDCAN_IT_ERROR_WARNING) { + if (Psr & FDCAN_PSR_EW) { + can_irq_handler(can_id, CAN_INT_ERR_WARNING, 0); + } } - } - if (ErrorStatusITs & FDCAN_FLAG_ERROR_PASSIVE) { - __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_ERROR_PASSIVE); - if (Psr & FDCAN_PSR_EP) { - irq_reason = MP_OBJ_NEW_SMALL_INT(4); - // mp_printf(MICROPY_ERROR_PRINTER, "clear passive %08x\n", (can->IR & FDCAN_ERROR_STATUS_MASK)); + if (error_ints & FDCAN_IT_ERROR_PASSIVE) { + if (Psr & FDCAN_PSR_EP) { + can_irq_handler(can_id, CAN_INT_ERR_PASSIVE, 0); + } } - } - if (ErrorStatusITs & FDCAN_FLAG_BUS_OFF) { - __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_BUS_OFF); - if (Psr & FDCAN_PSR_BO) { - irq_reason = MP_OBJ_NEW_SMALL_INT(5); - // mp_printf(MICROPY_ERROR_PRINTER, "bus off %08x\n", (can->IR & FDCAN_ERROR_STATUS_MASK)); + if (error_ints & FDCAN_IT_BUS_OFF) { + if (Psr & FDCAN_PSR_BO) { + can_irq_handler(can_id, CAN_INT_ERR_BUS_OFF, 0); + } } } - - pyb_can_handle_callback(self, fifo_id, callback, irq_reason); - // mp_printf(MICROPY_ERROR_PRINTER, "Ints: %08x, %08x, %08x\n", RxFifo0ITs, RxFifo1ITs, ErrorStatusITs); } #if defined(MICROPY_HW_CAN1_TX) void FDCAN1_IT0_IRQHandler(void) { IRQ_ENTER(FDCAN1_IT0_IRQn); - can_rx_irq_handler(PYB_CAN_1, FDCAN_RX_FIFO0); + can_rx_irq_handler(PYB_CAN_1, FDCAN1, CAN_RX_FIFO0); IRQ_EXIT(FDCAN1_IT0_IRQn); } void FDCAN1_IT1_IRQHandler(void) { IRQ_ENTER(FDCAN1_IT1_IRQn); - can_rx_irq_handler(PYB_CAN_1, FDCAN_RX_FIFO1); + can_rx_irq_handler(PYB_CAN_1, FDCAN1, CAN_RX_FIFO1); IRQ_EXIT(FDCAN1_IT1_IRQn); } #endif @@ -458,13 +429,13 @@ void FDCAN1_IT1_IRQHandler(void) { #if defined(MICROPY_HW_CAN2_TX) void FDCAN2_IT0_IRQHandler(void) { IRQ_ENTER(FDCAN2_IT0_IRQn); - can_rx_irq_handler(PYB_CAN_2, FDCAN_RX_FIFO0); + can_rx_irq_handler(PYB_CAN_2, FDCAN2, CAN_RX_FIFO0); IRQ_EXIT(FDCAN2_IT0_IRQn); } void FDCAN2_IT1_IRQHandler(void) { IRQ_ENTER(FDCAN2_IT1_IRQn); - can_rx_irq_handler(PYB_CAN_2, FDCAN_RX_FIFO1); + can_rx_irq_handler(PYB_CAN_2, FDCAN2, CAN_RX_FIFO1); IRQ_EXIT(FDCAN2_IT1_IRQn); } #endif diff --git a/ports/stm32/main.c b/ports/stm32/main.c index ea870ef0da7ca..55dbeed98246f 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -86,7 +86,7 @@ #include "accel.h" #include "servo.h" #include "dac.h" -#include "can.h" +#include "pyb_can.h" #include "subghz.h" #if MICROPY_PY_THREAD @@ -536,7 +536,7 @@ void stm32_main(uint32_t reset_mode) { timer_init0(); #if MICROPY_HW_ENABLE_CAN - can_init0(); + pyb_can_init0(); #endif #if MICROPY_HW_ENABLE_USB @@ -683,7 +683,7 @@ void stm32_main(uint32_t reset_mode) { pyb_i2c_deinit_all(); #endif #if MICROPY_HW_ENABLE_CAN - can_deinit_all(); + pyb_can_deinit_all(); #endif #if MICROPY_HW_ENABLE_DAC dac_deinit_all(); diff --git a/ports/stm32/modpyb.c b/ports/stm32/modpyb.c index 176010a7e5107..3d8696e3c426e 100644 --- a/ports/stm32/modpyb.c +++ b/ports/stm32/modpyb.c @@ -41,7 +41,7 @@ #include "i2c.h" #include "spi.h" #include "uart.h" -#include "can.h" +#include "pyb_can.h" #include "adc.h" #include "storage.h" #include "sdcard.h" diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index 181b3173866c3..0d004ecfcb5e4 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -27,6 +27,7 @@ #include #include +#include "py/obj.h" #include "py/objarray.h" #include "py/runtime.h" #include "py/gc.h" @@ -35,6 +36,7 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "bufhelper.h" +#include "pyb_can.h" #include "can.h" #include "irq.h" @@ -44,8 +46,6 @@ #define CAN_MAX_DATA_FRAME (64) -#define CAN_FIFO0 FDCAN_RX_FIFO0 -#define CAN_FIFO1 FDCAN_RX_FIFO1 #define CAN_FILTER_FIFO0 (0) // Default timings; 125Kbps @@ -82,21 +82,6 @@ #define CAN2_RX1_IRQn FDCAN2_IT1_IRQn #endif -#define CAN_IT_FIFO0_FULL FDCAN_IT_RX_FIFO0_FULL -#define CAN_IT_FIFO1_FULL FDCAN_IT_RX_FIFO1_FULL -#define CAN_IT_FIFO0_OVRF FDCAN_IT_RX_FIFO0_MESSAGE_LOST -#define CAN_IT_FIFO1_OVRF FDCAN_IT_RX_FIFO1_MESSAGE_LOST -#define CAN_IT_FIFO0_PENDING FDCAN_IT_RX_FIFO0_NEW_MESSAGE -#define CAN_IT_FIFO1_PENDING FDCAN_IT_RX_FIFO1_NEW_MESSAGE -#define CAN_FLAG_FIFO0_FULL FDCAN_FLAG_RX_FIFO0_FULL -#define CAN_FLAG_FIFO1_FULL FDCAN_FLAG_RX_FIFO1_FULL -#define CAN_FLAG_FIFO0_OVRF FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST -#define CAN_FLAG_FIFO1_OVRF FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST - -#define __HAL_CAN_ENABLE_IT __HAL_FDCAN_ENABLE_IT -#define __HAL_CAN_DISABLE_IT __HAL_FDCAN_DISABLE_IT -#define __HAL_CAN_CLEAR_FLAG __HAL_FDCAN_CLEAR_FLAG -#define __HAL_CAN_MSG_PENDING HAL_FDCAN_GetRxFifoFillLevel extern const uint8_t DLCtoBytes[16]; #else @@ -113,21 +98,27 @@ extern const uint8_t DLCtoBytes[16]; #define CAN_MAXIMUM_NBS2 (8) #define CAN_MINIMUM_TSEG (1) -#define CAN_IT_FIFO0_FULL CAN_IT_FF0 -#define CAN_IT_FIFO1_FULL CAN_IT_FF1 -#define CAN_IT_FIFO0_OVRF CAN_IT_FOV0 -#define CAN_IT_FIFO1_OVRF CAN_IT_FOV1 -#define CAN_IT_FIFO0_PENDING CAN_IT_FMP0 -#define CAN_IT_FIFO1_PENDING CAN_IT_FMP1 -#define CAN_FLAG_FIFO0_FULL CAN_FLAG_FF0 -#define CAN_FLAG_FIFO1_FULL CAN_FLAG_FF1 -#define CAN_FLAG_FIFO0_OVRF CAN_FLAG_FOV0 -#define CAN_FLAG_FIFO1_OVRF CAN_FLAG_FOV1 - static uint8_t can2_start_bank = 14; #endif +static mp_obj_t pyb_can_deinit(mp_obj_t self_in); + +void pyb_can_init0(void) { + for (uint i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_can_obj_all)); i++) { + MP_STATE_PORT(pyb_can_obj_all)[i] = NULL; + } +} + +void pyb_can_deinit_all(void) { + for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_can_obj_all)); i++) { + pyb_can_obj_t *can_obj = MP_STATE_PORT(pyb_can_obj_all)[i]; + if (can_obj != NULL) { + pyb_can_deinit(MP_OBJ_FROM_PTR(can_obj)); + } + } +} + static void pyb_can_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!self->is_enabled) { @@ -283,15 +274,20 @@ static mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp // Init filter banks for classic CAN. can2_start_bank = args[ARG_num_filter_banks].u_int; for (int f = 0; f < CAN_MAX_FILTER; f++) { - can_clearfilter(self, f, can2_start_bank); + can_clearfilter(&self->can, f, can2_start_bank); } #endif - if (!can_init(self, args[ARG_mode].u_int, args[ARG_prescaler].u_int, args[ARG_sjw].u_int, + if (!can_init(&self->can, self->can_id, args[ARG_mode].u_int, args[ARG_prescaler].u_int, args[ARG_sjw].u_int, args[ARG_bs1].u_int, args[ARG_bs2].u_int, args[ARG_auto_restart].u_bool)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("CAN(%d) init failure"), self->can_id); } + self->is_enabled = true; + self->num_error_warning = 0; + self->num_error_passive = 0; + self->num_bus_off = 0; + return mp_const_none; } @@ -346,7 +342,7 @@ static mp_obj_t pyb_can_make_new(const mp_obj_type_t *type, size_t n_args, size_ if (self->is_enabled) { // The caller is requesting a reconfiguration of the hardware // this can only be done if the hardware is in init mode - can_deinit(self); + pyb_can_deinit(MP_OBJ_FROM_PTR(self)); } self->rxcallback0 = mp_const_none; @@ -373,7 +369,8 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(pyb_can_init_obj, 1, pyb_can_init); // deinit() static mp_obj_t pyb_can_deinit(mp_obj_t self_in) { pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in); - can_deinit(self); + can_deinit(&self->can); + self->is_enabled = false; return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(pyb_can_deinit_obj, pyb_can_deinit); @@ -489,17 +486,8 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_can_info_obj, 1, 2, pyb_can_info) // any(fifo) - return `True` if any message waiting on the FIFO, else `False` static mp_obj_t pyb_can_any(mp_obj_t self_in, mp_obj_t fifo_in) { pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_int_t fifo = mp_obj_get_int(fifo_in); - if (fifo == 0) { - if (__HAL_CAN_MSG_PENDING(&self->can, CAN_FIFO0) != 0) { - return mp_const_true; - } - } else { - if (__HAL_CAN_MSG_PENDING(&self->can, CAN_FIFO1) != 0) { - return mp_const_true; - } - } - return mp_const_false; + can_rx_fifo_t fifo = mp_obj_get_int(fifo_in); + return mp_obj_new_bool(can_rx_pending(&self->can, fifo) != 0); } static MP_DEFINE_CONST_FUN_OBJ_2(pyb_can_any_obj, pyb_can_any); @@ -533,6 +521,7 @@ static mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t * } // send the data + HAL_StatusTypeDef status; CanTxMsgTypeDef tx_msg; #if MICROPY_HW_ENABLE_FDCAN @@ -594,27 +583,7 @@ static mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t * tx_data[i] = ((byte *)bufinfo.buf)[i]; } - HAL_StatusTypeDef status; - #if MICROPY_HW_ENABLE_FDCAN - uint32_t timeout_ms = args[ARG_timeout].u_int; - uint32_t start = HAL_GetTick(); - while (HAL_FDCAN_GetTxFifoFreeLevel(&self->can) == 0) { - if (timeout_ms == 0) { - mp_raise_OSError(MP_ETIMEDOUT); - } - // Check for the Timeout - if (timeout_ms != HAL_MAX_DELAY) { - if (HAL_GetTick() - start >= timeout_ms) { - mp_raise_OSError(MP_ETIMEDOUT); - } - } - MICROPY_EVENT_POLL_HOOK - } - status = HAL_FDCAN_AddMessageToTxFifoQ(&self->can, &tx_msg, tx_data); - #else - self->can.pTxMsg = &tx_msg; - status = CAN_Transmit(&self->can, args[ARG_timeout].u_int); - #endif + status = can_transmit(&self->can, &tx_msg, tx_data, args[ARG_timeout].u_int); if (status != HAL_OK) { mp_hal_raise(status); @@ -646,12 +615,8 @@ static mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t * uint8_t *rx_data = rx_msg.Data; #endif - mp_uint_t fifo = args[ARG_fifo].u_int; - if (fifo == 0) { - fifo = CAN_FIFO0; - } else if (fifo == 1) { - fifo = CAN_FIFO1; - } else { + can_rx_fifo_t fifo = args[ARG_fifo].u_int; + if (fifo != CAN_RX_FIFO0 && fifo != CAN_RX_FIFO1) { mp_raise_TypeError(NULL); } @@ -667,30 +632,28 @@ static mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t * #endif // Manage the rx state machine - if ((fifo == CAN_FIFO0 && self->rxcallback0 != mp_const_none) || - (fifo == CAN_FIFO1 && self->rxcallback1 != mp_const_none)) { - byte *state = (fifo == CAN_FIFO0) ? &self->rx_state0 : &self->rx_state1; - + if ((fifo == CAN_RX_FIFO0 && self->rxcallback0 != mp_const_none) || + (fifo == CAN_RX_FIFO1 && self->rxcallback1 != mp_const_none)) { + bool fifo_empty = can_rx_pending(&self->can, fifo) == 0; + byte *state = (fifo == CAN_RX_FIFO0) ? &self->rx_state0 : &self->rx_state1; switch (*state) { case RX_STATE_FIFO_EMPTY: break; case RX_STATE_MESSAGE_PENDING: - if (__HAL_CAN_MSG_PENDING(&self->can, fifo) == 0) { - // Fifo is empty - __HAL_CAN_ENABLE_IT(&self->can, (fifo == CAN_FIFO0) ? CAN_IT_FIFO0_PENDING : CAN_IT_FIFO1_PENDING); + if (fifo_empty) { *state = RX_STATE_FIFO_EMPTY; } break; case RX_STATE_FIFO_FULL: - __HAL_CAN_ENABLE_IT(&self->can, (fifo == CAN_FIFO0) ? CAN_IT_FIFO0_FULL : CAN_IT_FIFO1_FULL); *state = RX_STATE_MESSAGE_PENDING; break; case RX_STATE_FIFO_OVERFLOW: - __HAL_CAN_ENABLE_IT(&self->can, (fifo == CAN_FIFO0) ? CAN_IT_FIFO0_OVRF : CAN_IT_FIFO1_OVRF); - __HAL_CAN_ENABLE_IT(&self->can, (fifo == CAN_FIFO0) ? CAN_IT_FIFO0_FULL : CAN_IT_FIFO1_FULL); *state = RX_STATE_MESSAGE_PENDING; break; } + + // Re-enable any interrupts that were disabled in RX IRQ handlers + can_enable_rx_interrupts(&self->can, fifo, fifo_empty); } // Create the tuple, or get the list, that will hold the return values @@ -756,12 +719,12 @@ static mp_obj_t pyb_can_clearfilter(size_t n_args, const mp_obj_t *pos_args, mp_ mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); #if MICROPY_HW_ENABLE_FDCAN - can_clearfilter(self, f, args[ARG_extframe].u_bool); + can_clearfilter(&self->can, f, args[ARG_extframe].u_bool); #else if (self->can_id == 2) { f += can2_start_bank; } - can_clearfilter(self, f, can2_start_bank); + can_clearfilter(&self->can, f, can2_start_bank); #endif return mp_const_none; } @@ -945,16 +908,12 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(pyb_can_setfilter_obj, 1, pyb_can_setfilter); static mp_obj_t pyb_can_rxcallback(mp_obj_t self_in, mp_obj_t fifo_in, mp_obj_t callback_in) { pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_int_t fifo = mp_obj_get_int(fifo_in); + can_rx_fifo_t fifo = mp_obj_get_int(fifo_in); mp_obj_t *callback; - callback = (fifo == 0) ? &self->rxcallback0 : &self->rxcallback1; + callback = (fifo == CAN_RX_FIFO0) ? &self->rxcallback0 : &self->rxcallback1; if (callback_in == mp_const_none) { - __HAL_CAN_DISABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_PENDING : CAN_IT_FIFO1_PENDING); - __HAL_CAN_DISABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_FULL : CAN_IT_FIFO1_FULL); - __HAL_CAN_DISABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_OVRF : CAN_IT_FIFO1_OVRF); - __HAL_CAN_CLEAR_FLAG(&self->can, (fifo == CAN_FIFO0) ? CAN_FLAG_FIFO0_FULL : CAN_FLAG_FIFO1_FULL); - __HAL_CAN_CLEAR_FLAG(&self->can, (fifo == CAN_FIFO0) ? CAN_FLAG_FIFO0_OVRF : CAN_FLAG_FIFO1_OVRF); + can_disable_rx_interrupts(&self->can, fifo); *callback = mp_const_none; } else if (*callback != mp_const_none) { // Rx call backs has already been initialized @@ -964,21 +923,19 @@ static mp_obj_t pyb_can_rxcallback(mp_obj_t self_in, mp_obj_t fifo_in, mp_obj_t *callback = callback_in; uint32_t irq = 0; if (self->can_id == PYB_CAN_1) { - irq = (fifo == 0) ? CAN1_RX0_IRQn : CAN1_RX1_IRQn; + irq = (fifo == CAN_RX_FIFO0) ? CAN1_RX0_IRQn : CAN1_RX1_IRQn; #if defined(CAN2) } else if (self->can_id == PYB_CAN_2) { - irq = (fifo == 0) ? CAN2_RX0_IRQn : CAN2_RX1_IRQn; + irq = (fifo == CAN_RX_FIFO0) ? CAN2_RX0_IRQn : CAN2_RX1_IRQn; #endif #if defined(CAN3) } else { - irq = (fifo == 0) ? CAN3_RX0_IRQn : CAN3_RX1_IRQn; + irq = (fifo == CAN_RX_FIFO0) ? CAN3_RX0_IRQn : CAN3_RX1_IRQn; #endif } NVIC_SetPriority(irq, IRQ_PRI_CAN); HAL_NVIC_EnableIRQ(irq); - __HAL_CAN_ENABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_PENDING : CAN_IT_FIFO1_PENDING); - __HAL_CAN_ENABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_FULL : CAN_IT_FIFO1_FULL); - __HAL_CAN_ENABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_OVRF : CAN_IT_FIFO1_OVRF); + can_enable_rx_interrupts(&self->can, fifo, true); } return mp_const_none; } @@ -1038,8 +995,8 @@ static mp_uint_t can_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, i uintptr_t flags = arg; ret = 0; if ((flags & MP_STREAM_POLL_RD) - && ((__HAL_CAN_MSG_PENDING(&self->can, CAN_FIFO0) != 0) - || (__HAL_CAN_MSG_PENDING(&self->can, CAN_FIFO1) != 0))) { + && ((can_rx_pending(&self->can, 0) != 0) + || (can_rx_pending(&self->can, 1) != 0))) { ret |= MP_STREAM_POLL_RD; } #if MICROPY_HW_ENABLE_FDCAN @@ -1057,17 +1014,62 @@ static mp_uint_t can_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, i return ret; } -void pyb_can_handle_callback(pyb_can_obj_t *self, uint fifo_id, mp_obj_t callback, mp_obj_t irq_reason) { +// IRQ handler, called from lower layer can.c or fdcan.c in ISR context + +void can_irq_handler(uint can_id, can_int_t interrupt, can_rx_fifo_t fifo) { + mp_obj_t callback; + pyb_can_obj_t *self; + byte *state; + + self = MP_STATE_PORT(pyb_can_obj_all)[can_id - 1]; + + if (fifo == CAN_RX_FIFO0) { + callback = self->rxcallback0; + state = &self->rx_state0; + } else { + callback = self->rxcallback1; + state = &self->rx_state1; + } + + switch (interrupt) { + // These interrupts go on to run a Python callback, interrupt arg is the + // 'interrupt' enum value as an int. + case CAN_INT_MESSAGE_RECEIVED: + *state = RX_STATE_MESSAGE_PENDING; + break; + case CAN_INT_FIFO_FULL: + *state = RX_STATE_FIFO_FULL; + break; + case CAN_INT_FIFO_OVERFLOW: + *state = RX_STATE_FIFO_OVERFLOW; + break; + + // These interrupts do not run a Python callback + case CAN_INT_ERR_BUS_OFF: + self->num_bus_off++; + return; + case CAN_INT_ERR_PASSIVE: + self->num_error_passive++; + return; + case CAN_INT_ERR_WARNING: + self->num_error_warning++; + return; + + default: + return; // Should be unreachable + } + + // Run the callback if (callback != mp_const_none) { mp_sched_lock(); gc_lock(); nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - mp_call_function_2(callback, MP_OBJ_FROM_PTR(self), irq_reason); + mp_call_function_2(callback, MP_OBJ_FROM_PTR(self), MP_OBJ_NEW_SMALL_INT(interrupt)); nlr_pop(); } else { // Uncaught exception; disable the callback so it doesn't run again. - pyb_can_rxcallback(MP_OBJ_FROM_PTR(self), MP_OBJ_NEW_SMALL_INT(fifo_id), mp_const_none); + pyb_can_rxcallback(MP_OBJ_FROM_PTR(self), MP_OBJ_NEW_SMALL_INT(fifo), mp_const_none); mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in CAN(%u) rx interrupt handler\n", self->can_id); mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); } diff --git a/ports/stm32/pyb_can.h b/ports/stm32/pyb_can.h new file mode 100644 index 0000000000000..a82043d7863ed --- /dev/null +++ b/ports/stm32/pyb_can.h @@ -0,0 +1,58 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 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_STM32_PYB_CAN_H +#define MICROPY_INCLUDED_STM32_PYB_CAN_H + +#include "py/mphal.h" + +#if MICROPY_HW_ENABLE_CAN + +#include "py/obj.h" +#include "can.h" + +typedef struct _pyb_can_obj_t { + mp_obj_base_t base; + mp_obj_t rxcallback0; + mp_obj_t rxcallback1; + mp_uint_t can_id : 8; + bool is_enabled : 1; + byte rx_state0; + byte rx_state1; + uint16_t num_error_warning; + uint16_t num_error_passive; + uint16_t num_bus_off; + CAN_HandleTypeDef can; +} pyb_can_obj_t; + +extern const mp_obj_type_t pyb_can_type; + +void pyb_can_deinit_all(void); +void pyb_can_init0(void); + +void pyb_can_irq_handler(uint can_id, can_rx_fifo_t fifo, can_int_t interrupt); + +#endif +#endif From dbda43b9e14ac1aa8a293840d3242d3baa489b18 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 23 May 2024 17:48:31 +1000 Subject: [PATCH 389/546] tests/multi_pyb_can: Add multitests for pyboard CAN controller. Currently only classic CAN, but tests run on both the stm32 classic CAN controller and the FD-CAN controller with the same results. Signed-off-by: Angus Gratton --- tests/multi_pyb_can/rx_callback.py | 98 ++++++++++++++++++++++++++ tests/multi_pyb_can/rx_callback.py.exp | 9 +++ tests/multi_pyb_can/rx_filters.py | 50 +++++++++++++ tests/multi_pyb_can/rx_filters.py.exp | 15 ++++ 4 files changed, 172 insertions(+) create mode 100644 tests/multi_pyb_can/rx_callback.py create mode 100644 tests/multi_pyb_can/rx_callback.py.exp create mode 100644 tests/multi_pyb_can/rx_filters.py create mode 100644 tests/multi_pyb_can/rx_filters.py.exp diff --git a/tests/multi_pyb_can/rx_callback.py b/tests/multi_pyb_can/rx_callback.py new file mode 100644 index 0000000000000..960a225c93f28 --- /dev/null +++ b/tests/multi_pyb_can/rx_callback.py @@ -0,0 +1,98 @@ +from pyb import CAN +import time +import errno + +# Test the various receive IRQs, including overflow + +rx_overflow = False + +REASONS = ["received", "full", "overflow"] + +# CAN IDs +ID_SPAM = 0x345 # messages spammed into the receive FIFO +ID_ACK_OFLOW = 0x055 # message the receiver sends after it's seen an overflow +ID_AFTER = 0x100 # message the sender sends after the ACK + + +def cb0(bus, reason): + global rx_overflow + if reason != 0 and not rx_overflow: + # exact timing of 'received' callbacks depends on controller type, + # so only log the other two + print("rx0 reason", REASONS[reason]) + if reason == 2: + rx_overflow = True + + +# Accept all standard IDs on FIFO 0 +def _enable_accept_all(): + if hasattr(CAN, "MASK"): # FD-CAN controller + can.setfilter(0, CAN.RANGE, 0, (0x0, 0x7FF), extframe=False) + else: + can.setfilter(0, CAN.MASK16, 0, (0, 0, 0, 0), extframe=False) + + +# Receiver +def instance0(): + _enable_accept_all() + can.rxcallback(0, cb0) + + multitest.next() + multitest.wait("sender ready") + multitest.broadcast("receiver ready") + + while not rx_overflow: + pass # Resume ASAP after FIFO0 overflows + + can.send(b"overflow", ID_ACK_OFLOW) + + # drain the receive FIFO, making sure we read at least on ID_SPAM message + rxed_spam = False + while can.any(0): + msg = can.recv(0, timeout=0) + assert msg[0] == ID_SPAM + rxed_spam = True + print("rxed_spam", rxed_spam) + + # This should be the one message with ID_AFTER, there may be one or two spam messages as well + for _ in range(10): + msg = can.recv(0, timeout=500) + if msg[0] == ID_AFTER: + print(msg) + break + + # RX FIFO should be empty now + print("any", can.any(0)) + + +# Sender +def instance1(): + _enable_accept_all() + multitest.next() + multitest.broadcast("sender ready") + multitest.wait("receiver ready") + + # Spam out messages until the receiver tells us its RX FIFO is full. + # + # The RX FIFO on the receiver can vary from 3 deep (BXCAN) to 25 deep (STM32H7), + # so we keep sending to it until we see a CAN message on ID_ACK_OFLOW indicating + # the receiver's FIFO has overflowed + for i in range(255): + can.send(bytes([i] * 8), ID_SPAM, timeout=25) + if can.any(0): + print(can.recv(0)) # should be ID_ACK_OFLOW + break + # on boards like STM32H7 the TX FIFO is really deep, so don't fill it too quickly... + time.sleep_ms(1) + + # give the receiver some time to make space in the FIFO + time.sleep_ms(200) + + # send the final message, the receiver should get this one + can.send(b"aaaaa", ID_AFTER) + + # Sender's RX FIFO should also be empty at this point + print("any", can.any(0)) + + +can = CAN(1, CAN.NORMAL, baudrate=500_000, sample_point=75) diff --git a/tests/multi_pyb_can/rx_callback.py.exp b/tests/multi_pyb_can/rx_callback.py.exp new file mode 100644 index 0000000000000..3ab197cc10319 --- /dev/null +++ b/tests/multi_pyb_can/rx_callback.py.exp @@ -0,0 +1,9 @@ +--- instance0 --- +rx0 reason full +rx0 reason overflow +rxed_spam True +(256, False, False, 0, b'aaaaa') +any False +--- instance1 --- +(85, False, False, 0, b'overflow') +any False diff --git a/tests/multi_pyb_can/rx_filters.py b/tests/multi_pyb_can/rx_filters.py new file mode 100644 index 0000000000000..22e5eb0364748 --- /dev/null +++ b/tests/multi_pyb_can/rx_filters.py @@ -0,0 +1,50 @@ +from pyb import CAN +import time +import errno + +# Test for the filtering capabilities for RX FIFO 0 and 1. + + +# Receiver +def instance0(): + # Configure to receive standard frames (in a range) on FIFO 0 + # and extended frames (in a range) on FIFO 1. + if hasattr(CAN, "MASK"): + # FD-CAN has independent filter banks for standard and extended IDs + can.setfilter(0, CAN.MASK, 0, (0x300, 0x700), extframe=False) + can.setfilter(0, CAN.MASK, 1, (0x3000, 0x7000), extframe=True) + else: + # pyb.CAN only supports MASK32 for extended ids + can.setfilter(0, CAN.MASK16, 0, (0x300, 0x700, 0x300, 0x700), extframe=False) + can.setfilter(1, CAN.MASK32, 1, (0x3000, 0x7000), extframe=True) + + multitest.next() + multitest.wait("sender ready") + multitest.broadcast("receiver ready") + for i in range(3): + print(i) + print("fifo0", can.recv(0, timeout=200)) + print("fifo1", can.recv(1, timeout=200)) + + try: + can.recv(0, timeout=100) # should time out + except OSError as e: + assert e.errno == errno.ETIMEDOUT + print("Timed out as expected") + + +# Sender +def instance1(): + multitest.next() + multitest.broadcast("sender ready") + multitest.wait("receiver ready") + + for i in range(3): + print(i) + can.send(bytes([i, 3] * i), 0x345) + can.send(bytes([0xEE] * i), 0x3700 + i, extframe=True) + can.send(b"abcdef", 0x123) # matches no filter, expect ACKed but not received + time.sleep_ms(5) # avoid flooding either our or the receiver's FIFO + + +can = CAN(1, CAN.NORMAL, baudrate=500_000, sample_point=75) diff --git a/tests/multi_pyb_can/rx_filters.py.exp b/tests/multi_pyb_can/rx_filters.py.exp new file mode 100644 index 0000000000000..65fbdf4b1b22b --- /dev/null +++ b/tests/multi_pyb_can/rx_filters.py.exp @@ -0,0 +1,15 @@ +--- instance0 --- +0 +fifo0 (837, False, False, 0, b'') +fifo1 (14080, True, False, 0, b'') +1 +fifo0 (837, False, False, 0, b'\x01\x03') +fifo1 (14081, True, False, 0, b'\xee') +2 +fifo0 (837, False, False, 0, b'\x02\x03\x02\x03') +fifo1 (14082, True, False, 0, b'\xee\xee') +Timed out as expected +--- instance1 --- +0 +1 +2 From f187c77da8b0ff51927b62cd1f4efd78c03bdb7f Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 27 Feb 2025 09:03:17 +0100 Subject: [PATCH 390/546] shared/runtime/pyexec: Add helper function to execute a vstr. Add `pyexec_vstr()` to execute Python code from a vstr source. Signed-off-by: iabdalkader --- shared/runtime/pyexec.c | 5 +++++ shared/runtime/pyexec.h | 1 + 2 files changed, 6 insertions(+) diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 9dc4446ed4d66..c828c75817940 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -720,6 +720,11 @@ int pyexec_frozen_module(const char *name, bool allow_keyboard_interrupt) { } #endif +int pyexec_vstr(vstr_t *str, bool allow_keyboard_interrupt) { + mp_uint_t exec_flags = allow_keyboard_interrupt ? 0 : EXEC_FLAG_NO_INTERRUPT; + return parse_compile_execute(str, MP_PARSE_FILE_INPUT, exec_flags | EXEC_FLAG_SOURCE_IS_VSTR); +} + #if MICROPY_REPL_INFO mp_obj_t pyb_set_repl_info(mp_obj_t o_value) { repl_display_debugging_info = mp_obj_get_int(o_value); diff --git a/shared/runtime/pyexec.h b/shared/runtime/pyexec.h index 5779d3e09a8c4..95f4481626611 100644 --- a/shared/runtime/pyexec.h +++ b/shared/runtime/pyexec.h @@ -42,6 +42,7 @@ int pyexec_friendly_repl(void); int pyexec_file(const char *filename); int pyexec_file_if_exists(const char *filename); int pyexec_frozen_module(const char *name, bool allow_keyboard_interrupt); +int pyexec_vstr(vstr_t *str, bool allow_keyboard_interrupt); void pyexec_event_repl_init(void); int pyexec_event_repl_process_char(int c); extern uint8_t pyexec_repl_active; From 51976110e2da32b7a5b7035b7d5f17ab49bbd40e Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 12 Sep 2024 14:39:59 +0300 Subject: [PATCH 391/546] tools/mpy_ld.py: Allow linking static libraries. This commit introduces an additional symbol resolution mechanism to the natmod linking process. This allows the build scripts to look for required symbols into selected libraries that are provided by the compiler installation (libgcc and libm at the moment). For example, using soft-float code in natmods, whilst technically possible, was not an easy process and required some additional work to pull it off. With this addition all the manual (and error-prone) operations have been automated and folded into `tools/mpy_ld.py`. Both newlib and picolibc toolchains are supported, albeit the latter may require a bit of extra configuration depending on the environment the build process runs on. Picolibc's soft-float functions aren't in libm - in fact the shipped libm is nothing but a stub - but they are inside libc. This is usually not a problem as these changes cater for that configuration quirk, but on certain compilers the include paths used to find libraries in may not be updated to take Picolibc's library directory into account. The bare metal RISC-V compiler shipped with the CI OS image (GCC 10.2.0 on Ubuntu 22.04LTS) happens to exhibit this very problem. To work around that for CI builds, the Picolibc libraries' path is hardcoded in the Makefile directives used by the linker, but this can be changed by setting the PICOLIBC_ROOT environment library when building natmods. Signed-off-by: Volodymyr Shymanskyy Co-authored-by: Alessandro Gatti --- docs/develop/natmod.rst | 7 + examples/natmod/features2/Makefile | 3 + examples/natmod/features2/main.c | 11 ++ py/dynruntime.mk | 81 +++++++--- tools/ar_util.py | 236 +++++++++++++++++++++++++++++ tools/ci.sh | 44 +++++- tools/mpy_ld.py | 140 ++++++++++------- 7 files changed, 440 insertions(+), 82 deletions(-) create mode 100644 tools/ar_util.py diff --git a/docs/develop/natmod.rst b/docs/develop/natmod.rst index ba45e4305c66d..18678eaefbf11 100644 --- a/docs/develop/natmod.rst +++ b/docs/develop/natmod.rst @@ -70,6 +70,13 @@ The known limitations are: So, if your C code has writable data, make sure the data is defined globally, without an initialiser, and only written to within functions. +The native module is not automatically linked against the standard static libraries +like ``libm.a`` and ``libgcc.a``, which can lead to ``undefined symbol`` errors. +You can link the runtime libraries by setting ``LINK_RUNTIME = 1`` +in your Makefile. Custom static libraries can also be linked by adding +``MPY_LD_FLAGS += -l path/to/library.a``. Note that these are linked into +the native module and will not be shared with other modules or the system. + Linker limitation: the native module is not linked against the symbol table of the full MicroPython firmware. Rather, it is linked against an explicit table of exported symbols found in ``mp_fun_table`` (in ``py/nativeglue.h``), that is fixed at firmware diff --git a/examples/natmod/features2/Makefile b/examples/natmod/features2/Makefile index 4fd23c6879d47..5ddb74087b71a 100644 --- a/examples/natmod/features2/Makefile +++ b/examples/natmod/features2/Makefile @@ -10,5 +10,8 @@ SRC = main.c prod.c test.py # Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) ARCH = x64 +# Link with libm.a and libgcc.a from the toolchain +LINK_RUNTIME = 1 + # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk diff --git a/examples/natmod/features2/main.c b/examples/natmod/features2/main.c index 22961aa494f5f..5ce8e7b013020 100644 --- a/examples/natmod/features2/main.c +++ b/examples/natmod/features2/main.c @@ -1,5 +1,6 @@ /* This example demonstrates the following features in a native module: - using floats + - calling math functions from libm.a - defining additional code in Python (see test.py) - have extra C code in a separate file (see prod.c) */ @@ -10,6 +11,9 @@ // Include the header for auxiliary C code for this module #include "prod.h" +// Include standard library header +#include + // Automatically detect if this module should include double-precision code. // If double precision is supported by the target architecture then it can // be used in native module regardless of what float setting the target @@ -41,6 +45,12 @@ static mp_obj_t add_d(mp_obj_t x, mp_obj_t y) { static MP_DEFINE_CONST_FUN_OBJ_2(add_d_obj, add_d); #endif +// A function that uses libm +static mp_obj_t call_round(mp_obj_t x) { + return mp_obj_new_float_from_f(roundf(mp_obj_get_float_to_f(x))); +} +static MP_DEFINE_CONST_FUN_OBJ_1(round_obj, call_round); + // A function that computes the product of floats in an array. // This function uses the most general C argument interface, which is more difficult // to use but has access to the globals dict of the module via self->globals. @@ -74,6 +84,7 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a #if USE_DOUBLE mp_store_global(MP_QSTR_add_d, MP_OBJ_FROM_PTR(&add_d_obj)); #endif + mp_store_global(MP_QSTR_round, MP_OBJ_FROM_PTR(&round_obj)); // The productf function uses the most general C argument interface mp_store_global(MP_QSTR_productf, MP_DYNRUNTIME_MAKE_FUNCTION(productf)); diff --git a/py/dynruntime.mk b/py/dynruntime.mk index 68d5e2e268f5b..807befb464a84 100644 --- a/py/dynruntime.mk +++ b/py/dynruntime.mk @@ -29,16 +29,17 @@ CFLAGS += -Wall -Werror -DNDEBUG CFLAGS += -DNO_QSTR CFLAGS += -DMICROPY_ENABLE_DYNRUNTIME CFLAGS += -DMP_CONFIGFILE='<$(CONFIG_H)>' -CFLAGS += -fpic -fno-common -CFLAGS += -U _FORTIFY_SOURCE # prevent use of __*_chk libc functions -#CFLAGS += -fdata-sections -ffunction-sections + +CFLAGS_ARCH += -fpic -fno-common +CFLAGS_ARCH += -U_FORTIFY_SOURCE # prevent use of __*_chk libc functions +#CFLAGS_ARCH += -fdata-sections -ffunction-sections MPY_CROSS_FLAGS += -march=$(ARCH) SRC_O += $(addprefix $(BUILD)/, $(patsubst %.c,%.o,$(filter %.c,$(SRC))) $(patsubst %.S,%.o,$(filter %.S,$(SRC)))) SRC_MPY += $(addprefix $(BUILD)/, $(patsubst %.py,%.mpy,$(filter %.py,$(SRC)))) -CLEAN_EXTRA += $(MOD).mpy +CLEAN_EXTRA += $(MOD).mpy .mpy_ld_cache ################################################################################ # Architecture configuration @@ -47,72 +48,74 @@ ifeq ($(ARCH),x86) # x86 CROSS = -CFLAGS += -m32 -fno-stack-protector +CFLAGS_ARCH += -m32 -fno-stack-protector MICROPY_FLOAT_IMPL ?= double else ifeq ($(ARCH),x64) # x64 CROSS = -CFLAGS += -fno-stack-protector +CFLAGS_ARCH += -fno-stack-protector MICROPY_FLOAT_IMPL ?= double else ifeq ($(ARCH),armv6m) # thumb CROSS = arm-none-eabi- -CFLAGS += -mthumb -mcpu=cortex-m0 +CFLAGS_ARCH += -mthumb -mcpu=cortex-m0 MICROPY_FLOAT_IMPL ?= none else ifeq ($(ARCH),armv7m) # thumb CROSS = arm-none-eabi- -CFLAGS += -mthumb -mcpu=cortex-m3 +CFLAGS_ARCH += -mthumb -mcpu=cortex-m3 MICROPY_FLOAT_IMPL ?= none else ifeq ($(ARCH),armv7emsp) # thumb CROSS = arm-none-eabi- -CFLAGS += -mthumb -mcpu=cortex-m4 -CFLAGS += -mfpu=fpv4-sp-d16 -mfloat-abi=hard +CFLAGS_ARCH += -mthumb -mcpu=cortex-m4 +CFLAGS_ARCH += -mfpu=fpv4-sp-d16 -mfloat-abi=hard MICROPY_FLOAT_IMPL ?= float else ifeq ($(ARCH),armv7emdp) # thumb CROSS = arm-none-eabi- -CFLAGS += -mthumb -mcpu=cortex-m7 -CFLAGS += -mfpu=fpv5-d16 -mfloat-abi=hard +CFLAGS_ARCH += -mthumb -mcpu=cortex-m7 +CFLAGS_ARCH += -mfpu=fpv5-d16 -mfloat-abi=hard MICROPY_FLOAT_IMPL ?= double else ifeq ($(ARCH),xtensa) # xtensa CROSS = xtensa-lx106-elf- -CFLAGS += -mforce-l32 +CFLAGS_ARCH += -mforce-l32 MICROPY_FLOAT_IMPL ?= none else ifeq ($(ARCH),xtensawin) # xtensawin CROSS = xtensa-esp32-elf- -CFLAGS += MICROPY_FLOAT_IMPL ?= float else ifeq ($(ARCH),rv32imc) # rv32imc CROSS = riscv64-unknown-elf- -CFLAGS += -march=rv32imac -mabi=ilp32 -mno-relax +CFLAGS_ARCH += -march=rv32imac -mabi=ilp32 -mno-relax # 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 $(CROSS)gcc --print-file-name=picolibc.specs) +PICOLIBC_SPECS := $(shell $(CROSS)gcc --print-file-name=picolibc.specs) ifneq ($(PICOLIBC_SPECS),picolibc.specs) -CFLAGS += --specs=$(PICOLIBC_SPECS) +CFLAGS_ARCH += -specs=$(PICOLIBC_SPECS) +USE_PICOLIBC := 1 +PICOLIBC_ARCH := rv32imac +PICOLIBC_ABI := ilp32 endif MICROPY_FLOAT_IMPL ?= none @@ -122,7 +125,47 @@ $(error architecture '$(ARCH)' not supported) endif MICROPY_FLOAT_IMPL_UPPER = $(shell echo $(MICROPY_FLOAT_IMPL) | tr '[:lower:]' '[:upper:]') -CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_$(MICROPY_FLOAT_IMPL_UPPER) +CFLAGS += $(CFLAGS_ARCH) -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_$(MICROPY_FLOAT_IMPL_UPPER) + +ifeq ($(LINK_RUNTIME),1) +# All of these picolibc-specific directives are here to work around a +# limitation of Ubuntu 22.04's RISC-V bare metal toolchain. In short, the +# specific version of GCC in use (10.2.0) does not seem to take into account +# extra paths provided by an explicitly passed specs file when performing name +# resolution via `--print-file-name`. +# +# If Picolibc is used and libc.a fails to resolve, then said file's path will +# be computed by searching the Picolibc libraries root for a libc.a file in a +# subdirectory whose path is built using the current `-march` and `-mabi` +# flags that are passed to GCC. The `PICOLIBC_ROOT` environment variable is +# checked to override the starting point for the library file search, and if +# it is not set then the default value is used, assuming that this is running +# on an Ubuntu 22.04 machine. +# +# This should be revised when the CI base image is updated to a newer Ubuntu +# version (that hopefully contains a newer RISC-V compiler) or to another Linux +# distribution. +ifeq ($(USE_PICOLIBC),1) +LIBM_NAME := libc.a +else +LIBM_NAME := libm.a +endif +LIBGCC_PATH := $(realpath $(shell $(CROSS)gcc $(CFLAGS) --print-libgcc-file-name)) +LIBM_PATH := $(realpath $(shell $(CROSS)gcc $(CFLAGS) --print-file-name=$(LIBM_NAME))) +ifeq ($(USE_PICOLIBC),1) +ifeq ($(LIBM_PATH),) +# The CROSS toolchain prefix usually ends with a dash, but that may not be +# always the case. If the prefix ends with a dash it has to be taken out as +# Picolibc's architecture directory won't have it in its name. GNU Make does +# not have any facility to perform character-level text manipulation so we +# shell out to sed. +CROSS_PREFIX := $(shell echo $(CROSS) | sed -e 's/-$$//') +PICOLIBC_ROOT ?= /usr/lib/picolibc/$(CROSS_PREFIX)/lib +LIBM_PATH := $(PICOLIBC_ROOT)/$(PICOLIBC_ARCH)/$(PICOLIBC_ABI)/$(LIBM_NAME) +endif +endif +MPY_LD_FLAGS += $(addprefix -l, $(LIBGCC_PATH) $(LIBM_PATH)) +endif CFLAGS += $(CFLAGS_EXTRA) @@ -165,7 +208,7 @@ $(BUILD)/%.mpy: %.py # Build native .mpy from object files $(BUILD)/$(MOD).native.mpy: $(SRC_O) $(ECHO) "LINK $<" - $(Q)$(MPY_LD) --arch $(ARCH) --qstrs $(CONFIG_H) -o $@ $^ + $(Q)$(MPY_LD) --arch $(ARCH) --qstrs $(CONFIG_H) $(MPY_LD_FLAGS) -o $@ $^ # Build final .mpy from all intermediate .mpy files $(MOD).mpy: $(BUILD)/$(MOD).native.mpy $(SRC_MPY) diff --git a/tools/ar_util.py b/tools/ar_util.py new file mode 100644 index 0000000000000..b90d379031467 --- /dev/null +++ b/tools/ar_util.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +# +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2024 Volodymyr Shymanskyy +# +# 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. + +import os +import re +import hashlib +import functools +import pickle + +from elftools.elf import elffile +from collections import defaultdict + +try: + from ar import Archive +except: + Archive = None + + +class PickleCache: + def __init__(self, path, prefix=""): + self.path = path + self._get_fn = lambda key: os.path.join(path, prefix + key[:24]) + + def store(self, key, data): + os.makedirs(self.path, exist_ok=True) + # See also https://bford.info/cachedir/ + cachedir_tag_path = os.path.join(self.path, "CACHEDIR.TAG") + if not os.path.exists(cachedir_tag_path): + with open(cachedir_tag_path, "w") as f: + f.write( + "Signature: 8a477f597d28d172789f06886806bc55\n" + "# This file is a cache directory tag created by MicroPython.\n" + "# For information about cache directory tags see https://bford.info/cachedir/\n" + ) + with open(self._get_fn(key), "wb") as f: + pickle.dump(data, f) + + def load(self, key): + with open(self._get_fn(key), "rb") as f: + return pickle.load(f) + + +def cached(key, cache): + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + cache_key = key(*args, **kwargs) + try: + d = cache.load(cache_key) + if d["key"] != cache_key: + raise Exception("Cache key mismatch") + return d["data"] + except Exception: + res = func(*args, **kwargs) + try: + cache.store( + cache_key, + { + "key": cache_key, + "data": res, + }, + ) + except Exception: + pass + return res + + return wrapper + + return decorator + + +class CachedArFile: + def __init__(self, fn): + if not Archive: + raise RuntimeError("Please run 'pip install ar' to link .a files") + self.fn = fn + self._archive = Archive(open(fn, "rb")) + info = self.load_symbols() + self.objs = info["objs"] + self.symbols = info["symbols"] + + def open(self, obj): + return self._archive.open(obj, "rb") + + def _cache_key(self): + sha = hashlib.sha256() + with open(self.fn, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + sha.update(chunk) + # Change this salt if the cache data format changes + sha.update(bytes.fromhex("00000000000000000000000000000001")) + return sha.hexdigest() + + @cached(key=_cache_key, cache=PickleCache(path=".mpy_ld_cache", prefix="ar_")) + def load_symbols(self): + print("Loading", self.fn) + objs = defaultdict(lambda: {"def": set(), "undef": set(), "weak": set()}) + symbols = {} + for entry in self._archive: + obj_name = entry.name + elf = elffile.ELFFile(self.open(obj_name)) + symtab = elf.get_section_by_name(".symtab") + if not symtab: + continue + + obj = objs[obj_name] + + for symbol in symtab.iter_symbols(): + sym_name = symbol.name + sym_bind = symbol["st_info"]["bind"] + + if sym_bind in ("STB_GLOBAL", "STB_WEAK"): + if symbol.entry["st_shndx"] != "SHN_UNDEF": + obj["def"].add(sym_name) + symbols[sym_name] = obj_name + else: + obj["undef"].add(sym_name) + + if sym_bind == "STB_WEAK": + obj["weak"].add(sym_name) + + return {"objs": dict(objs), "symbols": symbols} + + +def resolve(archives, symbols): + resolved_objs = [] # Object files needed to resolve symbols + unresolved_symbols = set() + provided_symbols = {} # Which symbol is provided by which object + symbol_stack = list(symbols) + + # A helper function to handle symbol resolution from a particular object + def add_obj(archive, symbol): + obj_name = archive.symbols[symbol] + obj_info = archive.objs[obj_name] + + obj_tuple = (archive, obj_name) + if obj_tuple in resolved_objs: + return # Already processed this object + + resolved_objs.append(obj_tuple) + + # Add the symbols this object defines + for defined_symbol in obj_info["def"]: + if defined_symbol in provided_symbols and not defined_symbol.startswith( + "__x86.get_pc_thunk." + ): + if defined_symbol in obj_info["weak"]: + continue + else: + raise RuntimeError(f"Multiple definitions for {defined_symbol}") + provided_symbols[defined_symbol] = obj_name # TODO: mark weak if needed + + # Recursively add undefined symbols from this object + for undef_symbol in obj_info["undef"]: + if undef_symbol in obj_info["weak"]: + print(f"Skippping weak dependency: {undef_symbol}") + continue + if undef_symbol not in provided_symbols: + symbol_stack.append(undef_symbol) # Add undefined symbol to resolve + + while symbol_stack: + symbol = symbol_stack.pop(0) + + if symbol in provided_symbols: + continue # Symbol is already resolved + + found = False + for archive in archives: + if symbol in archive.symbols: + add_obj(archive, symbol) + found = True + break + + if not found: + unresolved_symbols.add(symbol) + + return resolved_objs, list(unresolved_symbols) + + +def expand_ld_script(fn): + # This function parses a subset of ld scripts + # Typically these are just groups of static lib references + group_pattern = re.compile(r"GROUP\s*\(\s*([^\)]+)\s*\)", re.MULTILINE) + output_format_pattern = re.compile(r"OUTPUT_FORMAT\s*\(\s*([^\)]+)\s*\)", re.MULTILINE) + comment_pattern = re.compile(r"/\*.*?\*/", re.MULTILINE | re.DOTALL) + + with open(fn, "r") as f: + content = f.read() + content = comment_pattern.sub("", content).strip() + + # Ensure no unrecognized instructions + leftovers = content + for pattern in (group_pattern, output_format_pattern): + leftovers = pattern.sub("", leftovers) + if leftovers.strip(): + raise ValueError("Invalid instruction found in the ld script:" + leftovers) + + # Extract files from GROUP instructions + files = [] + for match in group_pattern.findall(content): + files.extend([file.strip() for file in re.split(r"[,\s]+", match) if file.strip()]) + + return files + + +def load_archive(fn): + ar_header = b"!\012" + with open(fn, "rb") as f: + is_ar_file = f.read(len(ar_header)) == ar_header + if is_ar_file: + return [CachedArFile(fn)] + else: + return [CachedArFile(item) for item in expand_ld_script(fn)] diff --git a/tools/ci.sh b/tools/ci.sh index 682c3ae507a6d..5d57175c80e94 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -155,12 +155,15 @@ PYTHON_VER=$(python --version | cut -d' ' -f2) export IDF_CCACHE_ENABLE=1 function ci_esp32_idf_setup { - pip3 install pyelftools git clone --depth 1 --branch $IDF_VER https://github.com/espressif/esp-idf.git # doing a treeless clone isn't quite as good as --shallow-submodules, but it # is smaller than full clones and works when the submodule commit isn't a head. git -C esp-idf submodule update --init --recursive --filter=tree:0 ./esp-idf/install.sh + # Install additional packages for mpy_ld into the IDF env + source esp-idf/export.sh + pip3 install pyelftools + pip3 install ar } function ci_esp32_build_common { @@ -287,6 +290,7 @@ function ci_qemu_setup_arm { sudo apt-get update sudo apt-get install qemu-system sudo pip3 install pyelftools + sudo pip3 install ar qemu-system-arm --version } @@ -295,6 +299,7 @@ function ci_qemu_setup_rv32 { sudo apt-get update sudo apt-get install qemu-system sudo pip3 install pyelftools + sudo pip3 install ar qemu-system-riscv32 --version } @@ -385,6 +390,7 @@ function ci_samd_build { function ci_stm32_setup { ci_gcc_arm_setup pip3 install pyelftools + pip3 install ar pip3 install pyhy } @@ -503,18 +509,40 @@ function ci_native_mpy_modules_build { else arch=$1 fi - for natmod in features1 features3 features4 deflate framebuf heapq random re + for natmod in features1 features3 features4 heapq re do + make -C examples/natmod/$natmod clean make -C examples/natmod/$natmod ARCH=$arch done - # btree requires thread local storage support on rv32imc. - if [ $arch != rv32imc ]; then - make -C examples/natmod/btree ARCH=$arch + + # deflate, framebuf, and random currently cannot build on xtensa due to + # some symbols that have been removed from the compiler's runtime, in + # favour of being provided from ROM. + if [ $arch != "xtensa" ]; then + for natmod in deflate framebuf random + do + make -C examples/natmod/$natmod clean + make -C examples/natmod/$natmod ARCH=$arch + done fi - # features2 requires soft-float on armv7m and rv32imc. - if [ $arch != rv32imc ] && [ $arch != armv7m ]; then + + # features2 requires soft-float on armv7m, rv32imc, and xtensa. On armv6m + # the compiler generates absolute relocations in the object file + # referencing soft-float functions, which is not supported at the moment. + make -C examples/natmod/features2 clean + if [ $arch = "rv32imc" ] || [ $arch = "armv7m" ] || [ $arch = "xtensa" ]; then + make -C examples/natmod/features2 ARCH=$arch MICROPY_FLOAT_IMPL=float + elif [ $arch != "armv6m" ]; then make -C examples/natmod/features2 ARCH=$arch fi + + # btree requires thread local storage support on rv32imc, whilst on xtensa + # it relies on symbols that are provided from ROM but not exposed to + # natmods at the moment. + if [ $arch != "rv32imc" ] && [ $arch != "xtensa" ]; then + make -C examples/natmod/btree clean + make -C examples/natmod/btree ARCH=$arch + fi } function ci_native_mpy_modules_32bit_build { @@ -550,6 +578,7 @@ function ci_unix_standard_v2_run_tests { function ci_unix_coverage_setup { sudo pip3 install setuptools sudo pip3 install pyelftools + sudo pip3 install ar gcc --version python3 --version } @@ -598,6 +627,7 @@ function ci_unix_32bit_setup { sudo apt-get install gcc-multilib g++-multilib libffi-dev:i386 python2.7 sudo pip3 install setuptools sudo pip3 install pyelftools + sudo pip3 install ar gcc --version python2.7 --version python3 --version diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 54295208f1a29..44a76bdee6bf0 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -30,6 +30,7 @@ import sys, os, struct, re from elftools.elf import elffile +import ar_util sys.path.append(os.path.dirname(__file__) + "/../py") import makeqstrdata as qstrutil @@ -664,7 +665,7 @@ def do_relocation_text(env, text_addr, r): R_XTENSA_PDIFF32, R_XTENSA_ASM_EXPAND, ): - if s.section.name.startswith(".text"): + if not hasattr(s, "section") or s.section.name.startswith(".text"): # it looks like R_XTENSA_[P]DIFF32 into .text is already correctly relocated, # and expand relaxations cannot occur in non-executable sections. return @@ -1075,59 +1076,59 @@ def process_riscv32_relocation(env, text_addr, r): return addr, value -def load_object_file(env, felf): - with open(felf, "rb") as f: - elf = elffile.ELFFile(f) - env.check_arch(elf["e_machine"]) - - # Get symbol table - symtab = list(elf.get_section_by_name(".symtab").iter_symbols()) - - # Load needed sections from ELF file - sections_shndx = {} # maps elf shndx to Section object - for idx, s in enumerate(elf.iter_sections()): - if s.header.sh_type in ("SHT_PROGBITS", "SHT_NOBITS"): - if s.data_size == 0: - # Ignore empty sections - pass - elif s.name.startswith((".literal", ".text", ".rodata", ".data.rel.ro", ".bss")): - sec = Section.from_elfsec(s, felf) - sections_shndx[idx] = sec - if s.name.startswith(".literal"): - env.literal_sections.append(sec) - else: - env.sections.append(sec) - elif s.name.startswith(".data"): - raise LinkError("{}: {} non-empty".format(felf, s.name)) +def load_object_file(env, f, felf): + elf = elffile.ELFFile(f) + env.check_arch(elf["e_machine"]) + + # Get symbol table + symtab = list(elf.get_section_by_name(".symtab").iter_symbols()) + + # Load needed sections from ELF file + sections_shndx = {} # maps elf shndx to Section object + for idx, s in enumerate(elf.iter_sections()): + if s.header.sh_type in ("SHT_PROGBITS", "SHT_NOBITS"): + if s.data_size == 0: + # Ignore empty sections + pass + elif s.name.startswith((".literal", ".text", ".rodata", ".data.rel.ro", ".bss")): + sec = Section.from_elfsec(s, felf) + sections_shndx[idx] = sec + if s.name.startswith(".literal"): + env.literal_sections.append(sec) else: - # Ignore section - pass - elif s.header.sh_type in ("SHT_REL", "SHT_RELA"): - shndx = s.header.sh_info - if shndx in sections_shndx: - sec = sections_shndx[shndx] - sec.reloc_name = s.name - sec.reloc = list(s.iter_relocations()) - for r in sec.reloc: - r.sym = symtab[r["r_info_sym"]] - - # Link symbols to their sections, and update known and unresolved symbols - for sym in symtab: - sym.filename = felf - shndx = sym.entry["st_shndx"] + env.sections.append(sec) + elif s.name.startswith(".data"): + raise LinkError("{}: {} non-empty".format(felf, s.name)) + else: + # Ignore section + pass + elif s.header.sh_type in ("SHT_REL", "SHT_RELA"): + shndx = s.header.sh_info if shndx in sections_shndx: - # Symbol with associated section - sym.section = sections_shndx[shndx] - if sym["st_info"]["bind"] in ("STB_GLOBAL", "STB_WEAK"): - # Defined global symbol - if sym.name in env.known_syms and not sym.name.startswith( - "__x86.get_pc_thunk." - ): - raise LinkError("duplicate symbol: {}".format(sym.name)) - env.known_syms[sym.name] = sym - elif sym.entry["st_shndx"] == "SHN_UNDEF" and sym["st_info"]["bind"] == "STB_GLOBAL": - # Undefined global symbol, needs resolving - env.unresolved_syms.append(sym) + sec = sections_shndx[shndx] + sec.reloc_name = s.name + sec.reloc = list(s.iter_relocations()) + for r in sec.reloc: + r.sym = symtab[r["r_info_sym"]] + + # Link symbols to their sections, and update known and unresolved symbols + dup_errors = [] + for sym in symtab: + sym.filename = felf + shndx = sym.entry["st_shndx"] + if shndx in sections_shndx: + # Symbol with associated section + sym.section = sections_shndx[shndx] + if sym["st_info"]["bind"] in ("STB_GLOBAL", "STB_WEAK"): + # Defined global symbol + if sym.name in env.known_syms and not sym.name.startswith("__x86.get_pc_thunk."): + dup_errors.append("duplicate symbol: {}".format(sym.name)) + env.known_syms[sym.name] = sym + elif sym.entry["st_shndx"] == "SHN_UNDEF" and sym["st_info"]["bind"] == "STB_GLOBAL": + # Undefined global symbol, needs resolving + env.unresolved_syms.append(sym) + if len(dup_errors): + raise LinkError("\n".join(dup_errors)) def link_objects(env, native_qstr_vals_len): @@ -1188,6 +1189,8 @@ def link_objects(env, native_qstr_vals_len): ] ) } + + undef_errors = [] for sym in env.unresolved_syms: assert sym["st_value"] == 0 if sym.name == "_GLOBAL_OFFSET_TABLE_": @@ -1205,7 +1208,10 @@ def link_objects(env, native_qstr_vals_len): sym.section = mp_fun_table_sec sym.mp_fun_table_offset = fun_table[sym.name] else: - raise LinkError("{}: undefined symbol: {}".format(sym.filename, sym.name)) + undef_errors.append("{}: undefined symbol: {}".format(sym.filename, sym.name)) + + if len(undef_errors): + raise LinkError("\n".join(undef_errors)) # Align sections, assign their addresses, and create full_text env.full_text = bytearray(env.arch.asm_jump(8)) # dummy, to be filled in later @@ -1446,8 +1452,27 @@ def do_link(args): log(LOG_LEVEL_2, "qstr vals: " + ", ".join(native_qstr_vals)) env = LinkEnv(args.arch) try: - for file in args.files: - load_object_file(env, file) + # Load object files + for fn in args.files: + with open(fn, "rb") as f: + load_object_file(env, f, fn) + + if args.libs: + # Load archive info + archives = [] + for item in args.libs: + archives.extend(ar_util.load_archive(item)) + # List symbols to look for + syms = set(sym.name for sym in env.unresolved_syms) + # Resolve symbols from libs + lib_objs, _ = ar_util.resolve(archives, syms) + # Load extra object files from libs + for ar, obj in lib_objs: + obj_name = ar.fn + ":" + obj + log(LOG_LEVEL_2, "using " + obj_name) + with ar.open(obj) as f: + load_object_file(env, f, obj_name) + link_objects(env, len(native_qstr_vals)) build_mpy(env, env.find_addr("mpy_init"), args.output, native_qstr_vals) except LinkError as er: @@ -1458,13 +1483,16 @@ def do_link(args): def main(): import argparse - cmd_parser = argparse.ArgumentParser(description="Run scripts on the pyboard.") + cmd_parser = argparse.ArgumentParser(description="Link native object files into a MPY bundle.") cmd_parser.add_argument( "--verbose", "-v", action="count", default=1, help="increase verbosity" ) cmd_parser.add_argument("--arch", default="x64", help="architecture") cmd_parser.add_argument("--preprocess", action="store_true", help="preprocess source files") cmd_parser.add_argument("--qstrs", default=None, help="file defining additional qstrs") + cmd_parser.add_argument( + "--libs", "-l", dest="libs", action="append", help="static .a libraries to link" + ) cmd_parser.add_argument( "--output", "-o", default=None, help="output .mpy file (default to input with .o->.mpy)" ) From 1a7cbac879fa578cd992a3baf10950180e89340d Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 7 Mar 2025 00:04:14 +0100 Subject: [PATCH 392/546] tools/ci.sh: Build Xtensa natmods as part of the CI process. This commit expands the CI tests by checking whether the example native modules are able to be built for the Xtensa architecture. This was made possible by the changes to mpy_ld that allow symbol resolution across standard compiler-provided libraries. Signed-off-by: Alessandro Gatti --- tools/ci.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/ci.sh b/tools/ci.sh index 5d57175c80e94..7105b2460b787 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -197,7 +197,7 @@ function ci_esp32_build_s3_c3 { # ports/esp8266 function ci_esp8266_setup { - sudo pip install pyserial esptool==3.3.1 + sudo pip3 install pyserial esptool==3.3.1 pyelftools ar wget https://github.com/jepler/esp-open-sdk/releases/download/2018-06-10/xtensa-lx106-elf-standalone.tar.gz zcat xtensa-lx106-elf-standalone.tar.gz | tar x # Remove this esptool.py so pip version is used instead @@ -214,6 +214,9 @@ function ci_esp8266_build { make ${MAKEOPTS} -C ports/esp8266 BOARD=ESP8266_GENERIC make ${MAKEOPTS} -C ports/esp8266 BOARD=ESP8266_GENERIC BOARD_VARIANT=FLASH_512K make ${MAKEOPTS} -C ports/esp8266 BOARD=ESP8266_GENERIC BOARD_VARIANT=FLASH_1M + + # Test building native .mpy with xtensa architecture. + ci_native_mpy_modules_build xtensa } ######################################################################################## From 31a008c6e26eccc3798a9ab4169019a02eadb830 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 7 Mar 2025 00:07:11 +0100 Subject: [PATCH 393/546] tools/ci.sh: Do not assume the Python interpreter is called "python". This commit removes the assumption made by the CI scripts that the system-provided python executable is simply named "python". The scripts will now look for a binary called "python3" first, and then fall back to "python" if that is not found. Whilst this is currently the case for the CI environment, there are no guarantees for this going forward. For example minimal CI environments set up by some developers, using the same base OS, have their python executable called "python3". Signed-off-by: Alessandro Gatti --- tools/ci.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/ci.sh b/tools/ci.sh index 7105b2460b787..c7a6db79d6ce9 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -150,7 +150,8 @@ function ci_cc3200_build { # GitHub tag of ESP-IDF to use for CI (note: must be a tag or a branch) IDF_VER=v5.2.2 -PYTHON_VER=$(python --version | cut -d' ' -f2) +PYTHON=$(command -v python3 2> /dev/null) +PYTHON_VER=$(${PYTHON:-python} --version | cut -d' ' -f2) export IDF_CCACHE_ENABLE=1 From f1018ee5c2dd0eabcc0cfe7e02c607594e489788 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 3 Mar 2025 13:48:41 +0100 Subject: [PATCH 394/546] mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT: Add new Makerdiary board. This is a board based on the i.MX RT1011 in breadboard-friendly shape. A good basic board with 16M flash and regular pinout, providing access to many GPIO_nn and GPIO_AD_nn Pins. Signed-off-by: robert-hh --- docs/mimxrt/pinout.rst | 67 +++++++++----- .../MAKERDIARY_RT1011_NANO_KIT/board.json | 19 ++++ .../deploy_makerdiary.md | 34 ++++++++ .../mpconfigboard.h | 87 +++++++++++++++++++ .../mpconfigboard.mk | 16 ++++ .../MAKERDIARY_RT1011_NANO_KIT/pins.csv | 42 +++++++++ 6 files changed, 245 insertions(+), 20 deletions(-) create mode 100644 ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/board.json create mode 100644 ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/deploy_makerdiary.md create mode 100644 ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/mpconfigboard.h create mode 100644 ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/mpconfigboard.mk create mode 100644 ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/pins.csv diff --git a/docs/mimxrt/pinout.rst b/docs/mimxrt/pinout.rst index 3ff892c64d688..9aeb85401be8d 100644 --- a/docs/mimxrt/pinout.rst +++ b/docs/mimxrt/pinout.rst @@ -30,26 +30,28 @@ MIMXRT1170-EVK Debug USB D0/D1 D12/D11 D10/D13 Adafruit Metro M7 - D0/D1 D7/D3 A1/A0 Olimex RT1010Py - RxD/TxD D7/D8 D5/D6 Seeed ARCH MIX - J3_19/J3_20 J4_16/J4_17 J4_06/J4_07 +Makerdiary RT1011 - D9/D10 D13/A0 D11/D12 ================= =========== =========== =========== =========== | -================ =========== =========== ======= ======= ===== -Board / Pin UART4 UART5 UART6 UART7 UART8 -================ =========== =========== ======= ======= ===== -Teensy 4.0 16/17 21/20 25/24 28/29 - -Teensy 4.1 16/17 21/20 25/24 28/29 34/35 -MIMXRT1010-EVK - - - - - -MIMXRT1015-EVK - - - - - -MIMXRT1020-EVK D15/D14 A1/A0 - - - -MIMXRT1050-EVK A1/A0 - - - - -MIMXRT1050-EVKB A1/A0 - - - - -MIMXRT1060-EVK A1/A0 - - - - -MIMXRT1064-EVK A1/A0 - - - - -MIMXRT1170-EVK D15/D14 D25/D26 D33/D34 D35/D36 - -Olimex RT1010Py - - - - - -Seeed ARCH MIX J4_10/J4_11 J5_08/J5_12 - - - -================ =========== =========== ======= ======= ===== +================= =========== =========== ======= ======= ===== +Board / Pin UART4 UART5 UART6 UART7 UART8 +================= =========== =========== ======= ======= ===== +Teensy 4.0 16/17 21/20 25/24 28/29 - +Teensy 4.1 16/17 21/20 25/24 28/29 34/35 +MIMXRT1010-EVK - - - - - +MIMXRT1015-EVK - - - - - +MIMXRT1020-EVK D15/D14 A1/A0 - - - +MIMXRT1050-EVK A1/A0 - - - - +MIMXRT1050-EVKB A1/A0 - - - - +MIMXRT1060-EVK A1/A0 - - - - +MIMXRT1064-EVK A1/A0 - - - - +MIMXRT1170-EVK D15/D14 D25/D26 D33/D34 D35/D36 - +Olimex RT1010Py - - - - - +Seeed ARCH MIX J4_10/J4_11 J5_08/J5_12 - - - +Makerdiary RT1011 A1/A2 - - - - +================= =========== =========== ======= ======= ===== .. _mimxrt_pwm_pinout: @@ -188,7 +190,6 @@ LED_BLUE F1/3/B ========= =============== Pin Olimex RT1010PY ========= =============== -D0 - D1 F1/0/B D2 F1/0/A D3 F1/1/B @@ -197,13 +198,10 @@ D5 F1/2/B D6 F1/2/A D7 F1/3/B D8 F1/3/A -D9 - D10 F1/0/B D11 F1/0/A D12 F1/1/B D13 F1/1/A -D14 - -A0 - A1 F1/2/B A2 F1/2/A A3 F1/3/B @@ -214,6 +212,32 @@ CS0 F1/1/X SCK F1/0/X ========= =============== +| + +========= ================= +Pin Makerdiary RT1011 +========= ================= +D1 F1/0/B +D2 F1/0/A +D3 F1/1/B +D4 F1/1/A +D5 F1/2/B +D6 F1/2/A +D7 F1/3/B +D8 F1/3/A +A3 F1/2/B +A4 F1/2/A +A5 F1/3/B +A6 F1/3/A +A9 F1/3/X +A10 F1/2/X +A11 F1/1/X +SD1 F1/0/B +SD2 F1/0/A +LED F1/1/B +DIO F1/0/X +========= ================= + Legend: * Qm/n: QTMR module m, channel n @@ -322,6 +346,7 @@ MIXMXRT1170-EVK D10/-/D11/D12/D13 D28/-/D25/D24/D26 -/-/D14/D Adafruit Metro M7 -/-/MOSI/MISO/SCK - - Olimex RT1010Py - CS0/-/SDO/SDI/SCK SDCARD with CS1 Seeed ARCH MIX J4_12/-/J4_14/J4_13/J4_15 J3_09/J3_05/J3_08_J3_11 +Makerdiary RT1011 A5/A2/A4/A3/A6 A11/A1/A10/A9/CLK ================= ========================= ======================= =============== Pins denoted with (*) are by default not wired at the board. The CS0 and CS1 signals @@ -355,6 +380,7 @@ MIXMXRT1170-EVK D14/D15 D1/D0 A4/A5 D26/D25 D19/D18 Adafruit Metro M7 D14/D15 D0/D1 Olimex RT1010Py - SDA1/SCL1 SDA2/SCL2 - - Seeed ARCH MIX J3_17/J3_16 J4_06/J4_07 J5_05/J5_04 - - +Makerdiary RT1011 D1/D2 A7/A8 ================= =========== =========== =========== ======= ======= .. _mimxrt_i2s_pinout: @@ -379,6 +405,7 @@ Adafruit Metro M7 1 D8 D10 D9 D12 D14 D15 D13 Olimex RT1010Py 1 D8 D6 D7 D4 D1 D2 D3 Olimex RT1010Py 3 - D10 D9 D11 - - - MIMXRT_DEV 1 "MCK" "SCK_TX" "WS_TX" "SD_TX" "SCK_RX" "WS_RX" "SD_RX" +Makerdiary RT1011 1 D8 SD1 D7 D4 D1 D2 D3 ================= == ===== ======== ======= ======= ======== ======= ======= Symbolic pin names are provided for the MIMXRT_10xx_DEV boards. diff --git a/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/board.json b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/board.json new file mode 100644 index 0000000000000..ee04a5965fed7 --- /dev/null +++ b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/board.json @@ -0,0 +1,19 @@ +{ + "deploy": [ + "deploy_makerdiary.md" + ], + "docs": "", + "features": [ + "External Flash", + "USB", + "microSD" + ], + "images": [ + "MAKERDIARY_RT1011_NANO_KIT.jpg" + ], + "mcu": "mimxrt", + "product": "iMX RT1011 Nano Kit", + "thumbnail": "", + "url": "https://makerdiary.com/products/imxrt1011-nanokit", + "vendor": "Makerdiary" +} diff --git a/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/deploy_makerdiary.md b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/deploy_makerdiary.md new file mode 100644 index 0000000000000..fde3381592214 --- /dev/null +++ b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/deploy_makerdiary.md @@ -0,0 +1,34 @@ +## 1. Deploy the MicroPython firmware to the iMX RT1011 Nano Kit. + +The iMX RT1011 Nano Kit comes pre-installed with a UF2 bootloader. +It can be started by pushing reset twice. Then the bootloader drive will +appear. If that does not happen or the bootloader was lost, you can +reinstall the bootloader using the procedure below. + +Once the bootloader is installed and started, you can install MicroPython +by copying the .uf2 version of the firmware file to the bootloader +drive. When the firmware is installed, the drive will disappear. + +## 2. Reinstall the bootloader iMX RT1011 Nano Kit. + +1. Get the NXP program sdphost for your operating system, e.g. from +https://github.com/adafruit/tinyuf2/tree/master/ports/mimxrt10xx/sdphost. +You can also get them from the NXP web sites. + +2. Get the UF2 boot-loader package https://github.com/adafruit/tinyuf2/releases/tag/0.21.0 +and extract the file tinyuf2-makerdiary_rt1011-0.21.0.bin. You may as +well go for a newer release. + +Now you have all files at hand that you will need for updating. + +1. Push and hold the "USR/BT" button, then press "RST", and release both buttons. + +2. Run the commands: + +``` +sudo ./sdphost -u 0x1fc9,0x0145 -- write-file 0x20206400 tinyuf2-makerdiary_rt1011-0.21.0.bin +sudo ./sdphost -u 0x1fc9,0x0145 -- jump-address 0x20207000 +``` +Wait until a drive icon appears on the computer (or mount it explicitly). +At this point the bootloader is installed. You can now copy the +MicroPython .uf2 file to the board. diff --git a/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/mpconfigboard.h b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/mpconfigboard.h new file mode 100644 index 0000000000000..28085294f1410 --- /dev/null +++ b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/mpconfigboard.h @@ -0,0 +1,87 @@ +#define MICROPY_HW_BOARD_NAME "iMX_RT1011_Nano_Kit" +#define MICROPY_HW_MCU_NAME "MIMXRT1011DAE5A" +#define MICROPY_HW_USB_MANUFACTURER_STRING "Makerdiary" +#define MICROPY_HW_USB_VID 0xf055 +#define MICROPY_HW_USB_PID 0x9802 +#define MICROPY_PY_OS_DUPTERM_BUILTIN_STREAM (0) + +// RT1011-Nanokit has 1 board LED +#define MICROPY_HW_LED1_PIN (pin_GPIO_SD_04) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) + +#define MICROPY_HW_NUM_PIN_IRQS (2 * 32) +#define MICROPY_PY_MACHINE_SDCARD (0) + + +#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0]) +#define MICROPY_HW_UART_INDEX { 0, 1, 2, 3, 4 } + +#define IOMUX_TABLE_UART \ + { IOMUXC_GPIO_10_LPUART1_TXD }, { IOMUXC_GPIO_09_LPUART1_RXD }, \ + { IOMUXC_GPIO_AD_00_LPUART2_TXD }, { IOMUXC_GPIO_13_LPUART2_RXD }, \ + { IOMUXC_GPIO_12_LPUART3_TXD }, { IOMUXC_GPIO_11_LPUART3_RXD }, \ + { IOMUXC_GPIO_AD_02_LPUART4_TXD }, { IOMUXC_GPIO_AD_01_LPUART4_RXD }, + +#define IOMUX_TABLE_UART_CTS_RTS \ + { IOMUXC_GPIO_08_LPUART1_CTS_B }, { IOMUXC_GPIO_07_LPUART1_RTS_B }, \ + { IOMUXC_GPIO_AD_08_LPUART2_CTS_B }, { IOMUXC_GPIO_AD_07_LPUART2_RTS_B }, \ + { IOMUXC_GPIO_AD_14_LPUART3_CTS_B }, { IOMUXC_GPIO_AD_13_LPUART3_RTS_B }, \ + { IOMUXC_GPIO_AD_14_LPUART4_CTS_B }, { IOMUXC_GPIO_AD_13_LPUART4_RTS_B }, + +#define MICROPY_HW_SPI_INDEX { 1, 2 } + +#define IOMUX_TABLE_SPI \ + { IOMUXC_GPIO_AD_06_LPSPI1_SCK }, { IOMUXC_GPIO_AD_05_LPSPI1_PCS0 }, \ + { IOMUXC_GPIO_AD_04_LPSPI1_SDO }, { IOMUXC_GPIO_AD_03_LPSPI1_SDI }, \ + { IOMUXC_GPIO_AD_02_LPSPI1_PCS1 }, \ + { IOMUXC_GPIO_AD_12_LPSPI2_SCK }, { IOMUXC_GPIO_AD_11_LPSPI2_PCS0 }, \ + { IOMUXC_GPIO_AD_10_LPSPI2_SDO }, { IOMUXC_GPIO_AD_09_LPSPI2_SDI }, \ + { IOMUXC_GPIO_AD_01_LPSPI2_PCS1 } + +#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx } +#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx } + +// Define mapping hardware I2C # to logical I2C # +// SDA/SCL HW-I2C Logical I2C +// SDA1/SCL1 LPI2C1 -> 0 +// SDA2/SCL2 LPI2C2 -> 1 + +#define MICROPY_HW_I2C_INDEX { 1, 2 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_02_LPI2C1_SCL }, { IOMUXC_GPIO_01_LPI2C1_SDA }, \ + { IOMUXC_GPIO_AD_08_LPI2C2_SCL }, { IOMUXC_GPIO_AD_07_LPI2C2_SDA }, + +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (3) +#define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, 0, kCLOCK_Sai3Mux } +#define I2S_CLOCK_PRE_DIV { 0, kCLOCK_Sai1PreDiv, 0, kCLOCK_Sai3PreDiv } +#define I2S_CLOCK_DIV { 0, kCLOCK_Sai1Div, 0, kCLOCK_Sai3Div } +#define I2S_IOMUXC_GPR_MODE { 0, kIOMUXC_GPR_SAI1MClkOutputDir, 0, kIOMUXC_GPR_SAI3MClkOutputDir } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx, 0, kDmaRequestMuxSai3Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx, 0, kDmaRequestMuxSai3Tx } +#define I2S_AUDIO_PLL_CLOCK (2U) + +#define I2S_GPIO(_hwid, _fn, _mode, _pin, _iomux) \ + { \ + .hw_id = _hwid, \ + .fn = _fn, \ + .mode = _mode, \ + .name = MP_QSTR_##_pin, \ + .iomux = {_iomux}, \ + } + +#define I2S_GPIO_MAP \ + { \ + I2S_GPIO(1, MCK, TX, GPIO_08, IOMUXC_GPIO_08_SAI1_MCLK), /* pin D8 */ \ + I2S_GPIO(1, SCK, RX, GPIO_01, IOMUXC_GPIO_01_SAI1_RX_BCLK), /* pin D1 */ \ + I2S_GPIO(1, WS, RX, GPIO_02, IOMUXC_GPIO_02_SAI1_RX_SYNC), /* pin D2 */ \ + I2S_GPIO(1, SD, RX, GPIO_03, IOMUXC_GPIO_03_SAI1_RX_DATA00), /* pin D3 */ \ + I2S_GPIO(1, SCK, TX, GPIO_06, IOMUXC_GPIO_06_SAI1_TX_BCLK), /* pin D6 */ \ + I2S_GPIO(1, WS, TX, GPIO_07, IOMUXC_GPIO_07_SAI1_TX_SYNC), /* pin D7 */ \ + I2S_GPIO(1, SD, TX, GPIO_04, IOMUXC_GPIO_04_SAI1_TX_DATA00), /* pin D4 */ \ + I2S_GPIO(3, SCK, TX, GPIO_SD_01, IOMUXC_GPIO_SD_01_SAI3_TX_BCLK), /* pin SD1 */ \ + I2S_GPIO(3, WS, TX, GPIO_SD_00, IOMUXC_GPIO_SD_00_SAI3_TX_SYNC), /* pin SD0 */ \ + I2S_GPIO(3, SD, TX, GPIO_SD_02, IOMUXC_GPIO_SD_02_SAI3_TX_DATA) /* pin SD2 */ \ + } diff --git a/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/mpconfigboard.mk b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/mpconfigboard.mk new file mode 100644 index 0000000000000..942012157dd22 --- /dev/null +++ b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/mpconfigboard.mk @@ -0,0 +1,16 @@ +MCU_SERIES = MIMXRT1011 +MCU_VARIANT = MIMXRT1011DAE5A + +MICROPY_FLOAT_IMPL = single +MICROPY_HW_FLASH_TYPE = qspi_nor_flash +MICROPY_HW_FLASH_SIZE = 0x1000000 # 16MB +MICROPY_HW_FLASH_CLK = kFlexSpiSerialClk_100MHz +MICROPY_HW_FLASH_QE_CMD = 0x31 +MICROPY_HW_FLASH_QE_ARG = 0x02 + +USE_UF2_BOOTLOADER = 1 + +CFLAGS += -DMICROPY_HW_FLASH_SAMPLE_CLK=kFLEXSPIReadSampleClk_LoopbackInternally + +SRC_C += \ + hal/flexspi_nor_flash.c \ diff --git a/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/pins.csv b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/pins.csv new file mode 100644 index 0000000000000..c3f432e59f733 --- /dev/null +++ b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/pins.csv @@ -0,0 +1,42 @@ +A0,GPIO_AD_00 +A1,GPIO_AD_01 +A2,GPIO_AD_02 +A3,GPIO_AD_03 +A4,GPIO_AD_04 +A5,GPIO_AD_05 +A6,GPIO_AD_06 +A7,GPIO_AD_07 +A8,GPIO_AD_08 +A9,GPIO_AD_09 +A10,GPIO_AD_10 +A11,GPIO_AD_11 +A14,GPIO_AD_14 + +D0,GPIO_00 +D1,GPIO_01 +D2,GPIO_02 +D3,GPIO_03 +D4,GPIO_04 +D5,GPIO_05 +D6,GPIO_06 +D7,GPIO_07 +D8,GPIO_08 +D9,GPIO_09 +D10,GPIO_10 +D11,GPIO_11 +D12,GPIO_12 +D13,GPIO_13 + +SD0,GPIO_SD_00 +SD1,GPIO_SD_01 +SD2,GPIO_SD_02 +SD5,GPIO_SD_05 +# Test pads at the bottom side +SD3,GPIO_SD_03 +SD4,GPIO_SD_04 +SD12,GPIO_SD_12 +SD13,GPIO_SD_13 + +LED,GPIO_SD_04 +CLK,GPIO_AD_12 +DIO,GPIO_AD_13 From 58f1ade318ecd72def23e9169a7a25d65d7967fb Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 6 Dec 2024 20:38:19 +0100 Subject: [PATCH 395/546] mimxrt/machine_adc: Add ADC.read_uv() method. Matching the generic documentation. Signed-off-by: robert-hh --- ports/mimxrt/machine_adc.c | 19 ++++++++++--------- ports/mimxrt/mpconfigport.h | 1 + 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ports/mimxrt/machine_adc.c b/ports/mimxrt/machine_adc.c index 29b73a20652ce..c332bd703128a 100644 --- a/ports/mimxrt/machine_adc.c +++ b/ports/mimxrt/machine_adc.c @@ -41,6 +41,12 @@ // The ADC class doesn't have any constants for this port. #define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS +#if defined(MIMXRT117x_SERIES) +#define ADC_UV_FULL_RANGE (3840000) +#else +#define ADC_UV_FULL_RANGE (3300000) +#endif + typedef struct _machine_adc_obj_t { mp_obj_base_t base; ADC_Type *adc; @@ -78,15 +84,6 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args ADC_Type *adc_instance = pin->adc_list[0].instance; // NOTE: we only use the first ADC assignment - multiple assignments are not supported for now uint8_t channel = pin->adc_list[0].channel; - #if 0 // done in adc_read_u16 - // Configure ADC peripheral channel - adc_channel_config_t channel_config = { - .channelNumber = (uint32_t)channel, - .enableInterruptOnConversionCompleted = false, - }; - ADC_SetChannelConfig(adc_instance, 0UL, &channel_config); // NOTE: we always choose channel group '0' since we only perform software triggered conversion - #endif - // Create ADC Instance machine_adc_obj_t *o = mp_obj_malloc(machine_adc_obj_t, &machine_adc_type); o->adc = adc_instance; @@ -169,3 +166,7 @@ void machine_adc_init(void) { } } #endif + +static mp_int_t mp_machine_adc_read_uv(machine_adc_obj_t *self) { + return (uint64_t)ADC_UV_FULL_RANGE * mp_machine_adc_read_u16(self) / 65536; +} diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 7da85f1aee579..b9fb87e050b83 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -84,6 +84,7 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1) #define MICROPY_PY_MACHINE_ADC (1) #define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/mimxrt/machine_adc.c" +#define MICROPY_PY_MACHINE_ADC_READ_UV (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_PY_MACHINE_BITSTREAM (1) #define MICROPY_PY_MACHINE_DHT_READINTO (1) From d8edae040fb194c95e9569a44968d4c3858e0ac0 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 17 Jan 2025 09:40:59 +0100 Subject: [PATCH 396/546] mimxrt/mpconfigport: Enable support for exFAT. There is plenty of room in the MIMXRT board flash, so it can be enabled. Tested with: - MIMXRT1176_EVK - MIMXRT1061 (Teensy 4.1) - MIMXRT1010 (Olimex RT1010) Signed-off-by: robert-hh --- ports/mimxrt/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index b9fb87e050b83..728354239c46f 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -125,6 +125,7 @@ uint32_t trng_random_u32(void); #define MICROPY_FATFS_RPATH (2) #define MICROPY_FATFS_MULTI_PARTITION (1) #define MICROPY_FATFS_MAX_SS (4096) +#define MICROPY_FATFS_EXFAT (1) #ifndef MICROPY_PY_NETWORK #define MICROPY_PY_NETWORK (1) From 2a5b97beaea2db3c7b8c6a77cf0ca854fdb16a4b Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 15 Feb 2025 12:42:46 +0100 Subject: [PATCH 397/546] mimxrt/mpconfigport: Enable PPP for boards with lwIP. PPP is now enabled on all boards with Ethernet support. PPP could be enabled for other boards without Ethernet like the Teensy 4.0 as well in a second step. Enabling for MIMXRT101x boards is hardly possible due to the large RAM demand of lwIP. Tested with a Teensy 4.1 board and a SimCom A7608 GPRS/LTE modem. Signed-off-by: robert-hh --- ports/mimxrt/mpconfigport.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 728354239c46f..96ffc15379358 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -139,6 +139,10 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_HASHLIB_MD5 (MICROPY_PY_SSL) #define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL) #define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL) +#ifndef MICROPY_PY_NETWORK_PPP_LWIP +#define MICROPY_PY_NETWORK_PPP_LWIP (MICROPY_PY_LWIP) +#endif +#define MICROPY_PY_LWIP_PPP (MICROPY_PY_NETWORK_PPP_LWIP) #ifndef MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1) From 2d20dbce2c4e45123349051dec16e6565fec54ef Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 22 Feb 2025 16:20:50 +0100 Subject: [PATCH 398/546] mimxrt/machine_uart: Remove duplicate init and make IRQ optional. Changes: - The duplicate LPUART_Init call was not needed, just an edit fail. - Allow a port to disable UART.irq(). Some code for configuration stays, but the respective UART IRQ is not enabled. Calling uart.irq() will cause an exception by extmod/machine_uart.c. Signed-off-by: robert-hh --- ports/mimxrt/machine_uart.c | 10 +++++++--- ports/mimxrt/mpconfigport.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ports/mimxrt/machine_uart.c b/ports/mimxrt/machine_uart.c index 9f9d6c8fd256f..1c601b559d08e 100644 --- a/ports/mimxrt/machine_uart.c +++ b/ports/mimxrt/machine_uart.c @@ -326,22 +326,23 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, self->timeout_char = min_timeout_char; } + self->config.rxIdleType = kLPUART_IdleTypeStartBit; + self->config.rxIdleConfig = kLPUART_IdleCharacter4; #if defined(MIMXRT117x_SERIES) // Use the Lpuart1 clock value, which is set for All UART devices. LPUART_Init(self->lpuart, &self->config, CLOCK_GetRootClockFreq(kCLOCK_Root_Lpuart1)); #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; + #if MICROPY_PY_MACHINE_UART_IRQ LPUART_EnableInterrupts(self->lpuart, kLPUART_IdleLineInterruptEnable); + #endif // The Uart supports inverting, but not the fsl API, so it has to coded directly // And it has to be done after LPUART_Init. @@ -381,6 +382,7 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->timeout_char = 1; self->new = true; self->mp_irq_obj = NULL; + self->mp_irq_trigger = 0; LPUART_GetDefaultConfig(&self->config); @@ -427,6 +429,7 @@ void machine_uart_deinit_all(void) { } } +#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; @@ -475,6 +478,7 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args 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); diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 96ffc15379358..9a7dfc630f32a 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -114,7 +114,9 @@ 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) +#ifndef MICROPY_PY_MACHINE_UART_IRQ #define MICROPY_PY_MACHINE_UART_IRQ (1) +#endif #define MICROPY_PY_ONEWIRE (1) #define MICROPY_PY_MACHINE_BOOTLOADER (1) From 1398e7fd20ef399a003e4e476eb7726126f42d66 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 23 Feb 2025 16:11:56 +0100 Subject: [PATCH 399/546] mimxrt/hal/qspi_nor_flash_config: Use a safe common CS timing. The flash devices used by the MIMXRT board are specified either with 3ns or 5 ns CS setup and hold time. Since a single configuration file is used for all boards, use 5ns instead of 3ns to be safe, even if there were no problems so far. Signed-off-by: robert-hh --- ports/mimxrt/hal/qspi_nor_flash_config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/mimxrt/hal/qspi_nor_flash_config.c b/ports/mimxrt/hal/qspi_nor_flash_config.c index 36ccc30fc1922..0ed1b5032bb88 100644 --- a/ports/mimxrt/hal/qspi_nor_flash_config.c +++ b/ports/mimxrt/hal/qspi_nor_flash_config.c @@ -38,8 +38,8 @@ const flexspi_nor_config_t qspiflash_config = { .tag = FLEXSPI_CFG_BLK_TAG, .version = FLEXSPI_CFG_BLK_VERSION, .readSampleClkSrc = MICROPY_HW_FLASH_DQS, - .csHoldTime = 3u, - .csSetupTime = 3u, + .csHoldTime = 5u, // safe time for all flash devices + .csSetupTime = 5u, .busyOffset = FLASH_BUSY_STATUS_OFFSET, // Status bit 0 indicates busy. .busyBitPolarity = FLASH_BUSY_STATUS_POL, // Busy when the bit is 1. .deviceModeCfgEnable = 1u, From b85ad4bd4128876908ed0493a1b1d3df3d972cf1 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 9 Mar 2025 09:08:01 +0100 Subject: [PATCH 400/546] mimxrt/machine_uart: Fix rx/tx buffer allocation bug. The buffer would be reset on every call to `uart.init()`. If no sizes were given, the buffer would be set to the default size 256. That made problems e.g. with PPP. Also, the RX buffer was not stored at the UART object and not visible to GC as being in use. Then a `gc.collect()` would eventually free the buffer. This commit fixes those issues, keeping the buffer size if not deliberately changed and allocating new buffers only if the size was changed. Signed-off-by: robert-hh --- ports/mimxrt/machine_uart.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/ports/mimxrt/machine_uart.c b/ports/mimxrt/machine_uart.c index 1c601b559d08e..0d682a8a4d10a 100644 --- a/ports/mimxrt/machine_uart.c +++ b/ports/mimxrt/machine_uart.c @@ -66,6 +66,8 @@ typedef struct _machine_uart_obj_t { uint16_t tx_status; uint8_t *txbuf; uint16_t txbuf_len; + uint8_t *rxbuf; + uint16_t rxbuf_len; bool new; uint16_t mp_irq_trigger; // user IRQ trigger mask uint16_t mp_irq_flags; // user IRQ active IRQ flags @@ -197,7 +199,7 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ 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, + self->rxbuf_len, self->txbuf_len, self->timeout, self->timeout_char, _invert_name[self->invert], self->mp_irq_trigger); } @@ -291,25 +293,33 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, self->config.enableRx = true; // Set the RX buffer size if configured. - size_t rxbuf_len = DEFAULT_BUFFER_SIZE; if (args[ARG_rxbuf].u_int > 0) { - rxbuf_len = args[ARG_rxbuf].u_int; + size_t rxbuf_len = args[ARG_rxbuf].u_int; if (rxbuf_len < MIN_BUFFER_SIZE) { rxbuf_len = MIN_BUFFER_SIZE; } else if (rxbuf_len > MAX_BUFFER_SIZE) { mp_raise_ValueError(MP_ERROR_TEXT("rxbuf too large")); } + // Force re-allocting of the buffer if the size changed + if (rxbuf_len != self->rxbuf_len) { + self->rxbuf = NULL; + self->rxbuf_len = rxbuf_len; + } } // Set the TX buffer size if configured. - size_t txbuf_len = DEFAULT_BUFFER_SIZE; if (args[ARG_txbuf].u_int > 0) { - txbuf_len = args[ARG_txbuf].u_int; + size_t txbuf_len = args[ARG_txbuf].u_int; if (txbuf_len < MIN_BUFFER_SIZE) { txbuf_len = MIN_BUFFER_SIZE; } else if (txbuf_len > MAX_BUFFER_SIZE) { mp_raise_ValueError(MP_ERROR_TEXT("txbuf too large")); } + // Force re-allocting of the buffer if the size is changed + if (txbuf_len != self->txbuf_len) { + self->txbuf = NULL; + self->txbuf_len = txbuf_len; + } } // Initialise the UART peripheral if any arguments given, or it was not initialised previously. @@ -335,10 +345,13 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, LPUART_Init(self->lpuart, &self->config, CLOCK_GetClockRootFreq(kCLOCK_UartClkRoot)); #endif 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; + if (self->rxbuf == NULL) { + self->rxbuf = m_new(uint8_t, self->rxbuf_len + 1); + } + LPUART_TransferStartRingBuffer(self->lpuart, &self->handle, self->rxbuf, self->rxbuf_len); + if (self->txbuf == NULL) { + self->txbuf = m_new(uint8_t, self->txbuf_len); // Allocate the TX buffer. + } #if MICROPY_PY_MACHINE_UART_IRQ LPUART_EnableInterrupts(self->lpuart, kLPUART_IdleLineInterruptEnable); @@ -380,6 +393,10 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->invert = false; self->timeout = 1; self->timeout_char = 1; + self->rxbuf = NULL; + self->rxbuf_len = DEFAULT_BUFFER_SIZE; + self->txbuf = NULL; + self->txbuf_len = DEFAULT_BUFFER_SIZE; self->new = true; self->mp_irq_obj = NULL; self->mp_irq_trigger = 0; From 1e7328ca28de201d6cd1ef31fb64e37613c0f9fb Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 13 Mar 2025 15:47:48 +0100 Subject: [PATCH 401/546] mimxrt/machine_i2c: Support the timeout keyword argument. Set the default timeout to 50000 us. The default used to be 0, causing the NXP I2C driver to silently stop working in case of a non-responding device. Signed-off-by: robert-hh --- ports/mimxrt/machine_i2c.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ports/mimxrt/machine_i2c.c b/ports/mimxrt/machine_i2c.c index 89fe47dbf60c1..b9d23a23c9bab 100644 --- a/ports/mimxrt/machine_i2c.c +++ b/ports/mimxrt/machine_i2c.c @@ -36,6 +36,7 @@ #define DEFAULT_I2C_FREQ (400000) #define DEFAULT_I2C_DRIVE (6) +#define DEFAULT_I2C_TIMEOUT (50000) typedef struct _machine_i2c_obj_t { mp_obj_base_t base; @@ -82,16 +83,18 @@ bool lpi2c_set_iomux(int8_t hw_i2c, uint8_t drive) { static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "I2C(%u, freq=%u)", - self->i2c_id, self->master_config->baudRate_Hz); + mp_printf(print, "I2C(%u, freq=%u, timeout=%u)", + self->i2c_id, self->master_config->baudRate_Hz, + self->master_config->pinLowTimeout_ns / 1000); } mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_id, ARG_freq, ARG_drive}; + enum { ARG_id, ARG_freq, ARG_drive, ARG_timeout}; static const mp_arg_t allowed_args[] = { { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_freq, MP_ARG_INT, {.u_int = DEFAULT_I2C_FREQ} }, { MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_DRIVE} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_TIMEOUT} }, }; // Parse args. @@ -121,6 +124,9 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n LPI2C_MasterGetDefaultConfig(self->master_config); // Initialise the I2C peripheral. self->master_config->baudRate_Hz = args[ARG_freq].u_int; + if (args[ARG_timeout].u_int >= 0) { + self->master_config->pinLowTimeout_ns = args[ARG_timeout].u_int * 1000; // to be set as ns + } LPI2C_MasterInit(self->i2c_inst, self->master_config, BOARD_BOOTCLOCKRUN_LPI2C_CLK_ROOT); return MP_OBJ_FROM_PTR(self); From cdcc70d4f86b2948db4003c7ed55092b628394a8 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 13 Mar 2025 16:50:27 +0100 Subject: [PATCH 402/546] mimxrt: Enable default devices for I2C, SPI and UART. Since all boards are configured to have a I2C(0), SPI(0) and UART(1), these can be set as default devices, allowing the instantiation of I2C(), SPI(), UART() without an id argument. Signed-off-by: robert-hh --- docs/mimxrt/quickref.rst | 12 +++++++++++- ports/mimxrt/machine_i2c.c | 5 +++-- ports/mimxrt/machine_spi.c | 5 +++-- ports/mimxrt/machine_uart.c | 14 +++++++++++--- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index 49d7befc7452b..9f1efd4ffcad2 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -122,10 +122,13 @@ See :ref:`machine.UART `. :: uart1 = UART(1, baudrate=115200) uart1.write('hello') # write 5 bytes uart1.read(5) # read up to 5 bytes + uart1 = UART(baudrate=19200) # open UART 1 at 19200 baud The i.MXRT has up to eight hardware UARTs, but not every board exposes all TX and RX pins for users. For the assignment of Pins to UART signals, -refer to the :ref:`UART pinout `. +refer to the :ref:`UART pinout `. If the UART ID is +omitted, UART(1) is selected. Then, the keyword +option for baudrate must be used to change it from the default value. PWM (pulse width modulation) ---------------------------- @@ -305,12 +308,15 @@ rates (up to 30Mhz). Hardware SPI is accessed via the cs_pin(0) spi.write('Hello World') cs_pin(1) + spi = SPI(baudrate=4_000_000) # Use SPI(0) at a baudrate of 4 MHz For the assignment of Pins to SPI signals, refer to :ref:`Hardware SPI pinout `. The keyword option cs=n can be used to enable the cs pin 0 or 1 for an automatic cs signal. The default is cs=-1. Using cs=-1 the automatic cs signal is not created. In that case, cs has to be set by the script. Clearing that assignment requires a power cycle. +If the SPI ID is omitted, SPI(0) is selected. Then, the keyword +option for baudrate must be used to change it from the default value. Notes: @@ -355,6 +361,10 @@ has the same methods as software SPI above:: i2c = I2C(0, 400_000) i2c.writeto(0x76, b"Hello World") + i2c = I2C(freq=100_000) # use I2C(0) at 100kHz + +If the I2C ID is omitted, I2C(0) is selected. Then, the keyword +option for freq must be used to change the freq from the default value. I2S bus ------- diff --git a/ports/mimxrt/machine_i2c.c b/ports/mimxrt/machine_i2c.c index b9d23a23c9bab..d170804f4f058 100644 --- a/ports/mimxrt/machine_i2c.c +++ b/ports/mimxrt/machine_i2c.c @@ -34,6 +34,7 @@ #include "fsl_iomuxc.h" #include "fsl_lpi2c.h" +#define DEFAULT_I2C_ID (0) #define DEFAULT_I2C_FREQ (400000) #define DEFAULT_I2C_DRIVE (6) #define DEFAULT_I2C_TIMEOUT (50000) @@ -91,7 +92,7 @@ static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_prin mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_id, ARG_freq, ARG_drive, ARG_timeout}; static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_id, MP_ARG_INT, {.u_int = DEFAULT_I2C_ID} }, { MP_QSTR_freq, MP_ARG_INT, {.u_int = DEFAULT_I2C_FREQ} }, { MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_DRIVE} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_TIMEOUT} }, @@ -102,7 +103,7 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // Get I2C bus. - int i2c_id = mp_obj_get_int(args[ARG_id].u_obj); + int i2c_id = args[ARG_id].u_int; if (i2c_id < 0 || i2c_id >= MICROPY_HW_I2C_NUM || i2c_index_table[i2c_id] == 0) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id); } diff --git a/ports/mimxrt/machine_spi.c b/ports/mimxrt/machine_spi.c index 56e155ff22f4b..4f6480607aa36 100644 --- a/ports/mimxrt/machine_spi.c +++ b/ports/mimxrt/machine_spi.c @@ -37,6 +37,7 @@ #include "fsl_lpspi.h" #include "fsl_lpspi_edma.h" +#define DEFAULT_SPI_ID (0) #define DEFAULT_SPI_BAUDRATE (1000000) #define DEFAULT_SPI_POLARITY (0) #define DEFAULT_SPI_PHASE (0) @@ -130,7 +131,7 @@ static void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_prin mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_gap_ns, ARG_drive, ARG_cs }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_id, MP_ARG_INT, {.u_int = DEFAULT_SPI_ID} }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = DEFAULT_SPI_BAUDRATE} }, { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_POLARITY} }, { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_PHASE} }, @@ -146,7 +147,7 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // Get the SPI bus id. - int spi_id = mp_obj_get_int(args[ARG_id].u_obj); + int spi_id = args[ARG_id].u_int; if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(spi_index_table) || spi_index_table[spi_id] == 0) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); } diff --git a/ports/mimxrt/machine_uart.c b/ports/mimxrt/machine_uart.c index 0d682a8a4d10a..4dcaf72f9862a 100644 --- a/ports/mimxrt/machine_uart.c +++ b/ports/mimxrt/machine_uart.c @@ -37,6 +37,7 @@ #include "modmachine.h" #include "pin.h" +#define DEFAULT_UART_ID (1) #define DEFAULT_UART_BAUDRATE (115200) #define DEFAULT_BUFFER_SIZE (256) #define MIN_BUFFER_SIZE (32) @@ -377,10 +378,17 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, } static mp_obj_t mp_machine_uart_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); + mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, true); // Get UART bus. - int uart_id = mp_obj_get_int(args[0]); + int uart_id; + if (n_args > 0) { + uart_id = mp_obj_get_int(args[0]); + n_args--; + args++; + } else { + uart_id = DEFAULT_UART_ID; + } if (uart_id < 0 || uart_id > MICROPY_HW_UART_NUM || uart_index_table[uart_id] == 0) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) doesn't exist"), uart_id); } @@ -409,7 +417,7 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg if (uart_present) { mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); - mp_machine_uart_init_helper(self, n_args - 1, args + 1, &kw_args); + mp_machine_uart_init_helper(self, n_args, args, &kw_args); return MP_OBJ_FROM_PTR(self); } else { return mp_const_none; From 274c8c419c97bdb54fa14de37a38b08dcefd1493 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 23 Mar 2025 09:28:06 +0100 Subject: [PATCH 403/546] mimxrt/boards: Update deploy instructions. Make the final step of the deploy more detailed. Signed-off-by: robert-hh --- .../boards/ADAFRUIT_METRO_M7/deploy_metro_m7.md | 6 +++++- .../MAKERDIARY_RT1011_NANO_KIT/deploy_makerdiary.md | 7 ++++++- ports/mimxrt/boards/OLIMEX_RT1010/deploy_olimex.md | 12 +++++++++--- ports/mimxrt/boards/deploy_mimxrt.md | 7 +++++-- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/deploy_metro_m7.md b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/deploy_metro_m7.md index c5bfd1a28a79f..a2d6f5c824f0b 100644 --- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/deploy_metro_m7.md +++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/deploy_metro_m7.md @@ -8,4 +8,8 @@ here: https://learn.adafruit.com/adafruit-metro-m7-microsd/installing-the-bootlo Once the bootloader is installed and started, you can install MicroPython by copying the .uf2 version of the firmware file to the bootloader -drive. When the firmware is installed, the drive will disappear. +drive. A LED on the board will start flickering, indicating that the +upload is ongoing. Once the upload is complete, the drive icon will +disappear. Wait until the LED stops flickering. In rare cases there +may be an error message coming up, especially when there are only few +or no changes in the firmware file. Then just repeat the upload. \ No newline at end of file diff --git a/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/deploy_makerdiary.md b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/deploy_makerdiary.md index fde3381592214..49922183b1248 100644 --- a/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/deploy_makerdiary.md +++ b/ports/mimxrt/boards/MAKERDIARY_RT1011_NANO_KIT/deploy_makerdiary.md @@ -31,4 +31,9 @@ sudo ./sdphost -u 0x1fc9,0x0145 -- jump-address 0x20207000 ``` Wait until a drive icon appears on the computer (or mount it explicitly). At this point the bootloader is installed. You can now copy the -MicroPython .uf2 file to the board. +MicroPython .uf2 file to the board to start the firmware upload. +A LED on the board will start flickering, indicating that the +upload is ongoing. Once the upload is complete, the drive icon will +disappear. Wait until the LED stops flickering. In rare cases there +may be an error message coming up, especially when there are only few +or no changes in the firmware file. Then just repeat the upload. diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/deploy_olimex.md b/ports/mimxrt/boards/OLIMEX_RT1010/deploy_olimex.md index 7abba784b940b..4db0b6f611942 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/deploy_olimex.md +++ b/ports/mimxrt/boards/OLIMEX_RT1010/deploy_olimex.md @@ -33,10 +33,16 @@ and you will not need it for Windows. 4. Once the upload of the bootloader is finished, push Reset twice. -The bootloader should start and show a drive icon. Copy the .uf2 version of MicroPython -to this drive to install or update MicroPython. +The bootloader should start and show a drive icon. -Once the UF2 bootloader is installed, only step 4 is required to deploy MicroPython. If +5. Copy the .uf2 version of MicroPython to this drive to install or update MicroPython. + +A LED on the board will start flickering, indicating that the upload is ongoing. Once the upload +is complete, the drive icon will disappear. Wait until the LED stops flickering. In rare cases there +may be an error message coming up, especially when there are only few or no changes in +the firmware file. Then just repeat the copy. + +Once the UF2 bootloader is installed, only steps 4 and 5 are required to deploy MicroPython. If MicroPython is already installed, the bootloader can as well be invoked by calling `machine.bootloader()`. diff --git a/ports/mimxrt/boards/deploy_mimxrt.md b/ports/mimxrt/boards/deploy_mimxrt.md index 7ad21fe64ca17..2c7f3500979c3 100644 --- a/ports/mimxrt/boards/deploy_mimxrt.md +++ b/ports/mimxrt/boards/deploy_mimxrt.md @@ -41,5 +41,8 @@ installed. If there is no valid Firmware on the device, the bootloader will start automatically. Once it's started, a drive ICON will appear. The MicroPython firmware file with .uf2 -extension must then be copied to that drive. When the file is copied and MicroPython -is installed, the drive disappears and MicroPython starts. +extension must then be copied to that drive. A LED on the board may start flickering, +indicating that the upload is ongoing. Once the upload is complete, the drive icon +will disappear. Wait until the LED stops flickering. In rare cases there +may be an error message coming up, especially when there are only few or +no changes in the firmware file. Then just repeat the copy. From c61e85910879d5db120d00f7790ed36f4dcda44d Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 19 Mar 2025 15:47:35 +1100 Subject: [PATCH 404/546] drivers: Add MP_QSPI_IOCTL_MEMORY_MODIFIED to indicate flash changed. Signed-off-by: Damien George --- drivers/bus/qspi.h | 3 ++- drivers/bus/softqspi.c | 3 ++- drivers/memory/spiflash.c | 21 ++++++++++++++++++--- drivers/memory/spiflash.h | 2 ++ ports/stm32/octospi.c | 3 ++- ports/stm32/qspi.c | 2 +- 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/drivers/bus/qspi.h b/drivers/bus/qspi.h index 009f55b159d41..7ba2e750943c5 100644 --- a/drivers/bus/qspi.h +++ b/drivers/bus/qspi.h @@ -37,10 +37,11 @@ enum { MP_QSPI_IOCTL_DEINIT, MP_QSPI_IOCTL_BUS_ACQUIRE, MP_QSPI_IOCTL_BUS_RELEASE, + MP_QSPI_IOCTL_MEMORY_MODIFIED, }; typedef struct _mp_qspi_proto_t { - int (*ioctl)(void *self, uint32_t cmd); + int (*ioctl)(void *self, uint32_t cmd, uintptr_t arg); int (*write_cmd_data)(void *self, uint8_t cmd, size_t len, uint32_t data); int (*write_cmd_addr_data)(void *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src); int (*read_cmd)(void *self, uint8_t cmd, size_t len, uint32_t *dest); diff --git a/drivers/bus/softqspi.c b/drivers/bus/softqspi.c index 65a504a739d25..5cfc4db5e3383 100644 --- a/drivers/bus/softqspi.c +++ b/drivers/bus/softqspi.c @@ -56,8 +56,9 @@ static void nibble_write(mp_soft_qspi_obj_t *self, uint8_t v) { mp_hal_pin_write(self->io3, (v >> 3) & 1); } -static int mp_soft_qspi_ioctl(void *self_in, uint32_t cmd) { +static int mp_soft_qspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) { mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in; + (void)arg; switch (cmd) { case MP_QSPI_IOCTL_INIT: diff --git a/drivers/memory/spiflash.c b/drivers/memory/spiflash.c index 773334e167ae3..09ab7157b88eb 100644 --- a/drivers/memory/spiflash.c +++ b/drivers/memory/spiflash.c @@ -61,14 +61,22 @@ static void mp_spiflash_acquire_bus(mp_spiflash_t *self) { const mp_spiflash_config_t *c = self->config; if (c->bus_kind == MP_SPIFLASH_BUS_QSPI) { - c->bus.u_qspi.proto->ioctl(c->bus.u_qspi.data, MP_QSPI_IOCTL_BUS_ACQUIRE); + c->bus.u_qspi.proto->ioctl(c->bus.u_qspi.data, MP_QSPI_IOCTL_BUS_ACQUIRE, 0); } } static void mp_spiflash_release_bus(mp_spiflash_t *self) { const mp_spiflash_config_t *c = self->config; if (c->bus_kind == MP_SPIFLASH_BUS_QSPI) { - c->bus.u_qspi.proto->ioctl(c->bus.u_qspi.data, MP_QSPI_IOCTL_BUS_RELEASE); + c->bus.u_qspi.proto->ioctl(c->bus.u_qspi.data, MP_QSPI_IOCTL_BUS_RELEASE, 0); + } +} + +static void mp_spiflash_notify_modified(mp_spiflash_t *self, uint32_t addr, uint32_t len) { + const mp_spiflash_config_t *c = self->config; + if (c->bus_kind == MP_SPIFLASH_BUS_QSPI) { + uintptr_t arg[2] = { addr, len }; + c->bus.u_qspi.proto->ioctl(c->bus.u_qspi.data, MP_QSPI_IOCTL_MEMORY_MODIFIED, (uintptr_t)&arg[0]); } } @@ -174,7 +182,7 @@ void mp_spiflash_init(mp_spiflash_t *self) { mp_hal_pin_output(self->config->bus.u_spi.cs); self->config->bus.u_spi.proto->ioctl(self->config->bus.u_spi.data, MP_SPI_IOCTL_INIT); } else { - self->config->bus.u_qspi.proto->ioctl(self->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT); + self->config->bus.u_qspi.proto->ioctl(self->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT, 0); } mp_spiflash_acquire_bus(self); @@ -285,6 +293,7 @@ static int mp_spiflash_write_page(mp_spiflash_t *self, uint32_t addr, size_t len int mp_spiflash_erase_block(mp_spiflash_t *self, uint32_t addr) { mp_spiflash_acquire_bus(self); int ret = mp_spiflash_erase_block_internal(self, addr); + mp_spiflash_notify_modified(self, addr, SECTOR_SIZE); mp_spiflash_release_bus(self); return ret; } @@ -300,6 +309,8 @@ int mp_spiflash_read(mp_spiflash_t *self, uint32_t addr, size_t len, uint8_t *de } int mp_spiflash_write(mp_spiflash_t *self, uint32_t addr, size_t len, const uint8_t *src) { + uint32_t orig_addr = addr; + uint32_t orig_len = len; mp_spiflash_acquire_bus(self); int ret = 0; uint32_t offset = addr & (PAGE_SIZE - 1); @@ -317,12 +328,16 @@ int mp_spiflash_write(mp_spiflash_t *self, uint32_t addr, size_t len, const uint src += rest; offset = 0; } + mp_spiflash_notify_modified(self, orig_addr, orig_len); mp_spiflash_release_bus(self); return ret; } /******************************************************************************/ // Interface functions that use the cache +// +// These functions do not call mp_spiflash_notify_modified(), so shouldn't be +// used for memory-mapped flash (for example). #if MICROPY_HW_SPIFLASH_ENABLE_CACHE diff --git a/drivers/memory/spiflash.h b/drivers/memory/spiflash.h index 5ccf7d44c97de..edd7d49330da1 100644 --- a/drivers/memory/spiflash.h +++ b/drivers/memory/spiflash.h @@ -81,6 +81,8 @@ int mp_spiflash_write(mp_spiflash_t *self, uint32_t addr, size_t len, const uint #if MICROPY_HW_SPIFLASH_ENABLE_CACHE // These functions use the cache (which must already be configured) +// Note: don't use these functions in combination with memory-mapped +// flash, because MP_QSPI_IOCTL_MEMORY_MODIFIED is not called. int mp_spiflash_cache_flush(mp_spiflash_t *self); int mp_spiflash_cached_read(mp_spiflash_t *self, uint32_t addr, size_t len, uint8_t *dest); int mp_spiflash_cached_write(mp_spiflash_t *self, uint32_t addr, size_t len, const uint8_t *src); diff --git a/ports/stm32/octospi.c b/ports/stm32/octospi.c index 407f485664c6b..345c4a2374128 100644 --- a/ports/stm32/octospi.c +++ b/ports/stm32/octospi.c @@ -105,8 +105,9 @@ void octospi_init(void) { OCTOSPI1->CR |= OCTOSPI_CR_EN; } -static int octospi_ioctl(void *self_in, uint32_t cmd) { +static int octospi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) { (void)self_in; + (void)arg; switch (cmd) { case MP_QSPI_IOCTL_INIT: octospi_init(); diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c index 34359a1ccf838..1d3239b0d26d2 100644 --- a/ports/stm32/qspi.c +++ b/ports/stm32/qspi.c @@ -170,7 +170,7 @@ void qspi_memory_map(void) { qspi_mpu_enable_mapped(); } -static int qspi_ioctl(void *self_in, uint32_t cmd) { +static int qspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) { (void)self_in; switch (cmd) { case MP_QSPI_IOCTL_INIT: From 396ab268df8a0b52e25108e41370ec6ab64fd337 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 19 Mar 2025 15:59:22 +1100 Subject: [PATCH 405/546] stm32/qspi: Implement MP_QSPI_IOCTL_MEMORY_MODIFIED ioctl. stm32's QSPI driver supports memory-mapped mode. The memory-mapped flash can also be erased/written to. To support both these modes, it switches in and out of memory-mapped mode during an erase/write. If the flash is erased/written and then switched back to memory mapped mode, the cache related to the memory-mapped region that changed must be invalidated. Otherwise subsequent code may end up reading old data. That cache invalidation is currently not being done, and this commit fixes that. This bug has been around ever since QSPI memory-mapped mode existed, but it's never really been observed because it's not common to use flash in memory-mapped mode and also erase/write it. Eg PYBD_SF2 uses the memory-mapped flash in read-only mode to store additional firmware. But since the introduction of ROMFS, things changed. The `vfs.rom_ioctl()` command can erase/write memory-mapped flash. Signed-off-by: Damien George --- ports/stm32/qspi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c index 1d3239b0d26d2..1311c27d1d46e 100644 --- a/ports/stm32/qspi.c +++ b/ports/stm32/qspi.c @@ -190,6 +190,14 @@ static int qspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) { // Switch to memory-map mode when bus is idle qspi_memory_map(); break; + case MP_QSPI_IOCTL_MEMORY_MODIFIED: { + uintptr_t *addr_len = (uintptr_t *)arg; + volatile void *addr = (volatile void *)(QSPI_MAP_ADDR + addr_len[0]); + size_t len = addr_len[1]; + SCB_InvalidateICache_by_Addr(addr, len); + SCB_InvalidateDCache_by_Addr(addr, len); + break; + } } return 0; // success } From 1e92bdd206f6f87ba65ea05c5b2623fca0b926cd Mon Sep 17 00:00:00 2001 From: Malcolm McKellips Date: Mon, 17 Mar 2025 08:41:31 -0600 Subject: [PATCH 406/546] rp2/boards: Fix SparkFun vendor name. The preferred/correct spelling is "SparkFun" so this commit updates all of the existing SparkFun board definitions with that spelling. --- ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/board.json | 2 +- ports/rp2/boards/SPARKFUN_PROMICRO/board.json | 2 +- ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json | 2 +- ports/rp2/boards/SPARKFUN_THINGPLUS/board.json | 2 +- ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/board.json | 2 +- ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/board.json | 2 +- ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/board.json b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/board.json index 4ddcd999fe5f8..e65a9462c73c3 100644 --- a/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/board.json +++ b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/board.json @@ -21,5 +21,5 @@ "product": "IoT Node LoRaWAN RP2350", "thumbnail": "", "url": "https://www.sparkfun.com/products/26060", - "vendor": "Sparkfun" + "vendor": "SparkFun" } diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json index b8c8afc914eac..66cea79ddeea8 100644 --- a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json +++ b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json @@ -17,5 +17,5 @@ "product": "Pro Micro RP2040", "thumbnail": "", "url": "https://www.sparkfun.com/products/18288", - "vendor": "Sparkfun" + "vendor": "SparkFun" } diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json index d2bfa80a348d7..8e8b6319007ab 100644 --- a/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json +++ b/ports/rp2/boards/SPARKFUN_PROMICRO_RP2350/board.json @@ -18,5 +18,5 @@ "product": "Pro Micro RP2350", "thumbnail": "", "url": "https://www.sparkfun.com/products/24870", - "vendor": "Sparkfun" + "vendor": "SparkFun" } diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json index 3eeb397265572..e756c9bff445a 100644 --- a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json +++ b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json @@ -20,5 +20,5 @@ "product": "Thing Plus RP2040", "thumbnail": "", "url": "https://www.sparkfun.com/products/17745", - "vendor": "Sparkfun" + "vendor": "SparkFun" } diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/board.json b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/board.json index 09bbefe04dc1e..a6222d7a0def0 100644 --- a/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/board.json +++ b/ports/rp2/boards/SPARKFUN_THINGPLUS_RP2350/board.json @@ -23,5 +23,5 @@ "product": "Thing Plus RP2350", "thumbnail": "", "url": "https://www.sparkfun.com/products/25134", - "vendor": "Sparkfun" + "vendor": "SparkFun" } diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/board.json b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/board.json index 92486564b1067..6a06c0875bde0 100644 --- a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/board.json +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER/board.json @@ -21,5 +21,5 @@ "product": "XRP Controller", "thumbnail": "", "url": "https://www.sparkfun.com/sparkfun-experiential-robotics-platform-xrp-controller.html", - "vendor": "Sparkfun" + "vendor": "SparkFun" } diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json index b7dbd016007d7..b0566459a05e8 100644 --- a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/board.json @@ -19,5 +19,5 @@ "product": "XRP Controller (Beta)", "thumbnail": "", "url": "https://www.sparkfun.com/sparkfun-experiential-robotics-platform-xrp-controller-beta.html", - "vendor": "Sparkfun" + "vendor": "SparkFun" } From 93a8c53d64e63da4b965265a24ccdd33806055db Mon Sep 17 00:00:00 2001 From: Malcolm McKellips Date: Mon, 17 Mar 2025 14:33:59 -0600 Subject: [PATCH 407/546] rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350: Add SD card support. The IOTNODE_LORAWAN_RP2350 has an SD card and we want users to be able to `import sdcard` without copying `sdcard.py` over to their board. Signed-off-by: Malcolm McKellips --- ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/manifest.py | 3 +++ .../boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.cmake | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/manifest.py diff --git a/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/manifest.py b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/manifest.py new file mode 100644 index 0000000000000..3f5fa79bf7460 --- /dev/null +++ b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/manifest.py @@ -0,0 +1,3 @@ +include("$(PORT_DIR)/boards/manifest.py") + +require("sdcard") diff --git a/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.cmake b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.cmake index 11fe1ee28fce2..8a36724599d4a 100644 --- a/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.cmake +++ b/ports/rp2/boards/SPARKFUN_IOTNODE_LORAWAN_RP2350/mpconfigboard.cmake @@ -5,3 +5,6 @@ set(PICO_BOARD_HEADER_DIRS ${MICROPY_PORT_DIR}/boards/${MICROPY_BOARD}) set(PICO_BOARD "sparkfun_iotnode_lorawan_rp2350") set(PICO_PLATFORM "rp2350") + +# Board specific version of the frozen manifest +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) From 39452dbeed27423fd207d3cc48c52b4e591deea5 Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Sun, 22 Dec 2024 20:08:54 +1100 Subject: [PATCH 408/546] docs/rp2: Add network information to the rp2 quickref. Some rp2 boards include WiFi, at least with the very popular Pico W and Pico 2 W. New users frequently ask how to set up WiFi and are confused because it's not covered in the quickref. This commit adds the wlan section, copied and modified with notes from the ESP32 quickref. Signed-off-by: Matt Trentini --- docs/esp32/quickref.rst | 24 +++++++++---------- docs/rp2/quickref.rst | 51 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index d65782e501ac7..ccc01099d17f9 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -83,30 +83,30 @@ The :class:`network.WLAN` class in the :mod:`network` module:: import network - wlan = network.WLAN(network.WLAN.IF_STA) # create station interface - wlan.active(True) # activate the interface - wlan.scan() # scan for access points - wlan.isconnected() # check if the station is connected to an AP + wlan = network.WLAN() # create station interface (the default, see below for an access point interface) + wlan.active(True) # activate the interface + wlan.scan() # scan for access points + 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.ipconfig('addr4') # get the interface's IPv4 addresses + wlan.config('mac') # get the interface's MAC address + wlan.ipconfig('addr4') # get the interface's IPv4 addresses ap = network.WLAN(network.WLAN.IF_AP) # create access-point interface - ap.config(ssid='ESP-AP') # set the SSID of the access point - ap.config(max_clients=10) # set how many clients can connect to the network - ap.active(True) # activate the interface + ap.config(ssid='ESP-AP') # set the SSID of the access point + ap.config(max_clients=10) # set how many clients can connect to the network + ap.active(True) # activate the interface A useful function for connecting to your local WiFi network is:: def do_connect(): - import network - wlan = network.WLAN(network.WLAN.IF_STA) + import machine, network + wlan = network.WLAN() wlan.active(True) if not wlan.isconnected(): print('connecting to network...') wlan.connect('ssid', 'key') while not wlan.isconnected(): - pass + machine.idle() print('network config:', wlan.ipconfig('addr4')) Once the network is established the :mod:`socket ` module can be used diff --git a/docs/rp2/quickref.rst b/docs/rp2/quickref.rst index 6be3180500746..23071d77215d5 100644 --- a/docs/rp2/quickref.rst +++ b/docs/rp2/quickref.rst @@ -55,6 +55,57 @@ The :mod:`rp2` module:: import rp2 +Networking +---------- + +WLAN +^^^^ + +.. note:: + This section applies only to devices that include WiFi support, such as the `Pico W`_ and `Pico 2 W`_. + +The :class:`network.WLAN` class in the :mod:`network` module:: + + import network + + wlan = network.WLAN() # create station interface (the default, see below for an access point interface) + wlan.active(True) # activate the interface + wlan.scan() # scan for access points + 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.ipconfig('addr4') # get the interface's IPv4 addresses + + ap = network.WLAN(network.WLAN.IF_AP) # create access-point interface + ap.config(ssid='RP2-AP') # set the SSID of the access point + ap.config(max_clients=10) # set how many clients can connect to the network + ap.active(True) # activate the interface + +A useful function for connecting to your local WiFi network is:: + + def do_connect(): + import machine, network + wlan = network.WLAN() + wlan.active(True) + if not wlan.isconnected(): + print('connecting to network...') + wlan.connect('ssid', 'key') + while not wlan.isconnected(): + machine.idle() + 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 +convenient HTTP requests. + +After a call to ``wlan.connect()``, the device will by default retry to connect +**forever**, even when the authentication failed or no AP is in range. +``wlan.status()`` will return ``network.STAT_CONNECTING`` in this state until a +connection succeeds or the interface gets disabled. + +.. _Pico W: https://www.raspberrypi.com/documentation/microcontrollers/pico-series.html#picow-technical-specification +.. _Pico 2 W: https://www.raspberrypi.com/documentation/microcontrollers/pico-series.html#pico2w-technical-specification + Delay and timing ---------------- From f315a376b616fb195a18700a724715f0e7d250e3 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 13 Mar 2025 13:02:11 +0000 Subject: [PATCH 409/546] rp2/machine_i2c: Require an I2C bus ID when no default is available. When PICO_DEFAULT_I2C is not set require an I2C bus ID instead of using -1 as a default, which would fail with a cryptic: "I2C(-1) doesn't exist" Signed-off-by: Phil Howard --- ports/rp2/machine_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/rp2/machine_i2c.c b/ports/rp2/machine_i2c.c index e97a852b24305..94212fb487043 100644 --- a/ports/rp2/machine_i2c.c +++ b/ports/rp2/machine_i2c.c @@ -100,7 +100,7 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n #ifdef PICO_DEFAULT_I2C { MP_QSTR_id, MP_ARG_INT, {.u_int = PICO_DEFAULT_I2C} }, #else - { MP_QSTR_id, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_id, MP_ARG_INT | MP_ARG_REQUIRED }, #endif { MP_QSTR_freq, MP_ARG_INT, {.u_int = DEFAULT_I2C_FREQ} }, { MP_QSTR_scl, MICROPY_I2C_PINS_ARG_OPTS | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, From a86122396d0cdb98ed0e72a4c208d7491581e3fa Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 12 Mar 2025 10:42:48 +0000 Subject: [PATCH 410/546] rp2/machine_spi: Make SPI ID optional. If the "spi_id" arg is not supplied and then the board default specified by PICO_DEFAULT_SPI will be used. Signed-off-by: Phil Howard --- ports/rp2/machine_spi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ports/rp2/machine_spi.c b/ports/rp2/machine_spi.c index abf0a70bd0ebc..940e31b3b6ef6 100644 --- a/ports/rp2/machine_spi.c +++ b/ports/rp2/machine_spi.c @@ -128,7 +128,11 @@ static void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_prin mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + #ifdef PICO_DEFAULT_SPI + { MP_QSTR_id, MP_ARG_INT, {.u_int = PICO_DEFAULT_SPI} }, + #else + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, }, + #endif { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = DEFAULT_SPI_BAUDRATE} }, { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_POLARITY} }, { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_PHASE} }, @@ -144,7 +148,7 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // Get the SPI bus id. - int spi_id = mp_obj_get_int(args[ARG_id].u_obj); + int spi_id = args[ARG_id].u_int; if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(machine_spi_obj)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); } From dd7a950bbc27713df8cc461958757ec793189d72 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 12 Mar 2025 11:32:16 +0000 Subject: [PATCH 411/546] rp2/machine_spi: Allow MISO to be unspecified. It's common with write-only SPI displays for MISO to be repurposed as a register select or data/command pin. While that was possible by setting up the pin after a call to `machine.SPI()` this change makes `machine.SPI(miso=None)` explicit. Signed-off-by: Phil Howard --- ports/rp2/machine_spi.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/ports/rp2/machine_spi.c b/ports/rp2/machine_spi.c index 940e31b3b6ef6..680a3df287850 100644 --- a/ports/rp2/machine_spi.c +++ b/ports/rp2/machine_spi.c @@ -80,6 +80,9 @@ #endif +// Assume we won't get an RP2-compatible with 255 GPIO pins +#define MICROPY_HW_SPI_PIN_UNUSED UINT8_MAX + // SPI0 can be GP{0..7,16..23}, SPI1 can be GP{8..15,24..29}. #define IS_VALID_PERIPH(spi, pin) ((((pin) & 8) >> 3) == (spi)) // GP{2,6,10,14,...} @@ -120,9 +123,14 @@ static machine_spi_obj_t machine_spi_obj[] = { static void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "SPI(%u, baudrate=%u, polarity=%u, phase=%u, bits=%u, sck=%u, mosi=%u, miso=%u)", + mp_printf(print, "SPI(%u, baudrate=%u, polarity=%u, phase=%u, bits=%u, sck=%u, mosi=%u, miso=", self->spi_id, self->baudrate, self->polarity, self->phase, self->bits, - self->sck, self->mosi, self->miso); + self->sck, self->mosi); + if (self->miso == MICROPY_HW_SPI_PIN_UNUSED) { + mp_printf(print, "None)"); + } else { + mp_printf(print, "%u)", self->miso); + } } mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { @@ -140,7 +148,7 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_FIRSTBIT} }, { MP_QSTR_sck, MICROPY_SPI_PINS_ARG_OPTS | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_mosi, MICROPY_SPI_PINS_ARG_OPTS | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, - { MP_QSTR_miso, MICROPY_SPI_PINS_ARG_OPTS | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_miso, MICROPY_SPI_PINS_ARG_OPTS | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, }; // Parse the arguments. @@ -171,7 +179,10 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n } self->mosi = mosi; } - if (args[ARG_miso].u_obj != mp_const_none) { + + if (args[ARG_miso].u_obj == mp_const_none) { + self->miso = MICROPY_HW_SPI_PIN_UNUSED; + } else if (args[ARG_miso].u_obj != MP_OBJ_NEW_SMALL_INT(-1)) { int miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj); if (!IS_VALID_MISO(self->spi_id, miso)) { mp_raise_ValueError(MP_ERROR_TEXT("bad MISO pin")); @@ -194,7 +205,9 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n self->baudrate = spi_set_baudrate(self->spi_inst, self->baudrate); spi_set_format(self->spi_inst, self->bits, self->polarity, self->phase, self->firstbit); gpio_set_function(self->sck, GPIO_FUNC_SPI); - gpio_set_function(self->miso, GPIO_FUNC_SPI); + if (self->miso != MICROPY_HW_SPI_PIN_UNUSED) { + gpio_set_function(self->miso, GPIO_FUNC_SPI); + } gpio_set_function(self->mosi, GPIO_FUNC_SPI); } From 6fa498cba1a622723d3dc13e15331085aea60d3c Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 12 Mar 2025 16:24:55 +1100 Subject: [PATCH 412/546] rp2/mpnetworkport: Fix lost CYW43 WiFi events when using both cores. There's a very odd but predictable sequence of events that breaks Wi-Fi when using both cores: 1) CPU1 calls pendsv_suspend() - for example sleep() causes a softtimer node to be inserted, which calls pendsv_suspend(). 2) CYW43 sends wakeup IRQ. CPU0 GPIO IRQ handler schedules PendSV and disables the GPIO IRQ on CPU0, to re-enable after cyw43_poll() runs and completes. 3) CPU0 PendSV_Handler runs, sees pendsv is suspended, exits. 4) CPU1 calls pendsv_resume() and pendsv_resume() sees PendSV is pending and triggers it on CPU1. 5) CPU1 runs PendSV_Handler, runs cyw43_poll(), and at the end it re-enables the IRQ *but now on CPU1*. However CPU1 has GPIO IRQs disabled, so the CYW43 interrupt never runs again... The fix in this commit is to always enable/disable the interrupt on CPU0. This isn't supported by the pico-sdk, but it is supported by the hardware. Fixes issue #16779. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/mpnetworkport.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c index af2cabb3bff0f..2687a56874996 100644 --- a/ports/rp2/mpnetworkport.c +++ b/ports/rp2/mpnetworkport.c @@ -59,13 +59,36 @@ static soft_timer_entry_t mp_network_soft_timer; volatile int cyw43_has_pending = 0; +// The Pico SDK only lets us set GPIO wake on the current running CPU, but the +// hardware doesn't have this limit. We need to always enable/disable the pin +// interrupt on CPU0, regardless of which CPU runs PendSV and +// cyw43_post_poll_hook(). See feature request at https://github.com/raspberrypi/pico-sdk/issues/2354 +static void gpio_set_cpu0_host_wake_irq_enabled(bool enable) { + // This is a re-implementation of gpio_set_irq_enabled() and _gpio_set_irq_enabled() + // from the pico-sdk, but with the core, gpio, and event type hardcoded to shrink + // code size. + io_bank0_irq_ctrl_hw_t *irq_ctrl_base = &io_bank0_hw->proc0_irq_ctrl; + uint32_t gpio = CYW43_PIN_WL_HOST_WAKE; + uint32_t events = CYW43_IRQ_LEVEL; + io_rw_32 *en_reg = &irq_ctrl_base->inte[gpio / 8]; + events <<= 4 * (gpio % 8); + if (enable) { + hw_set_bits(en_reg, events); + } else { + hw_clear_bits(en_reg, events); + } +} + +// GPIO IRQ always runs on CPU0 static void gpio_irq_handler(void) { uint32_t events = gpio_get_irq_event_mask(CYW43_PIN_WL_HOST_WAKE); if (events & CYW43_IRQ_LEVEL) { - // As we use a high level interrupt, it will go off forever until it's serviced. - // So disable the interrupt until this is done. It's re-enabled again by - // 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); + // As we use a level interrupt (and can't use an edge interrupt + // as CYW43_PIN_WL_HOST_WAKE is also a SPI data pin), we need to disable + // the interrupt to stop it re-triggering until after PendSV run + // cyw43_poll(). It is re-enabled in cyw43_post_poll_hook(), implemented + // below. + gpio_set_cpu0_host_wake_irq_enabled(false); cyw43_has_pending = 1; __sev(); pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, cyw43_poll); @@ -81,9 +104,10 @@ void cyw43_irq_init(void) { #endif } +// This hook will run on whichever CPU serviced the PendSV interrupt void cyw43_post_poll_hook(void) { cyw43_has_pending = 0; - gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, CYW43_IRQ_LEVEL, true); + gpio_set_cpu0_host_wake_irq_enabled(true); } #endif From 23fb171b808a54eb51304ced9504ce66261bcc54 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 18 Mar 2025 16:05:41 +1100 Subject: [PATCH 413/546] rp2/mpnetworkport: Refactor out cyw43_has_pending global variable. A better indication of whether a cyw43 event is pending is the actual flag in the PendSV handler table. (If this fails, could also use the GPIO interrupt enabled register bit). This commit was needed of a previous version of the fix in the parent commit, but it turned out not strictly necessary for the current version. However, it's still a good clean up. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/cyw43_configport.h | 6 ++++-- ports/rp2/modmachine.c | 2 +- ports/rp2/mpnetworkport.c | 5 +---- ports/rp2/pendsv.c | 4 ++++ ports/rp2/pendsv.h | 1 + 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ports/rp2/cyw43_configport.h b/ports/rp2/cyw43_configport.h index bb99bb2976fee..ead1a3953d97e 100644 --- a/ports/rp2/cyw43_configport.h +++ b/ports/rp2/cyw43_configport.h @@ -140,10 +140,12 @@ uint cyw43_get_pin_wl(cyw43_pin_index_t pin_id); #endif void cyw43_post_poll_hook(void); -extern volatile int cyw43_has_pending; +static inline bool cyw43_poll_is_pending(void) { + return pendsv_is_pending(PENDSV_DISPATCH_CYW43); +} static inline void cyw43_yield(void) { - if (!cyw43_has_pending) { + if (!cyw43_poll_is_pending()) { best_effort_wfe_or_timeout(make_timeout_time_ms(1)); } } diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 954ea216497fe..31665a7640d9c 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -145,7 +145,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { uint32_t my_interrupts = MICROPY_BEGIN_ATOMIC_SECTION(); #if MICROPY_PY_NETWORK_CYW43 - if (cyw43_has_pending && cyw43_poll != NULL) { + if (cyw43_poll_is_pending()) { MICROPY_END_ATOMIC_SECTION(my_interrupts); return; } diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c index 2687a56874996..7a6df7f6e5b4d 100644 --- a/ports/rp2/mpnetworkport.c +++ b/ports/rp2/mpnetworkport.c @@ -57,7 +57,6 @@ static soft_timer_entry_t mp_network_soft_timer; #define CYW43_IRQ_LEVEL GPIO_IRQ_LEVEL_HIGH #define CYW43_SHARED_IRQ_HANDLER_PRIORITY PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY -volatile int cyw43_has_pending = 0; // The Pico SDK only lets us set GPIO wake on the current running CPU, but the // hardware doesn't have this limit. We need to always enable/disable the pin @@ -89,9 +88,8 @@ static void gpio_irq_handler(void) { // cyw43_poll(). It is re-enabled in cyw43_post_poll_hook(), implemented // below. gpio_set_cpu0_host_wake_irq_enabled(false); - cyw43_has_pending = 1; - __sev(); pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, cyw43_poll); + __sev(); CYW43_STAT_INC(IRQ_COUNT); } } @@ -106,7 +104,6 @@ void cyw43_irq_init(void) { // This hook will run on whichever CPU serviced the PendSV interrupt void cyw43_post_poll_hook(void) { - cyw43_has_pending = 0; gpio_set_cpu0_host_wake_irq_enabled(true); } diff --git a/ports/rp2/pendsv.c b/ports/rp2/pendsv.c index 2c086f89429be..0bf64d0ae440d 100644 --- a/ports/rp2/pendsv.c +++ b/ports/rp2/pendsv.c @@ -99,6 +99,10 @@ static inline int pendsv_suspend_count(void) { #endif +bool pendsv_is_pending(size_t slot) { + return pendsv_dispatch_table[slot] != NULL; +} + static inline void pendsv_resume_run_dispatch(void) { // Run pendsv if needed. Find an entry with a dispatch and call pendsv dispatch // with it. If pendsv runs it will service all slots. diff --git a/ports/rp2/pendsv.h b/ports/rp2/pendsv.h index a5d9440211a10..6abe950df632e 100644 --- a/ports/rp2/pendsv.h +++ b/ports/rp2/pendsv.h @@ -51,5 +51,6 @@ void pendsv_init(void); void pendsv_suspend(void); void pendsv_resume(void); void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f); +bool pendsv_is_pending(size_t slot); #endif // MICROPY_INCLUDED_RP2_PENDSV_H From 35d4d2d06bf2776070c0635e4e3093ef7a6c8f39 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 12 Mar 2025 16:57:19 +1100 Subject: [PATCH 414/546] rp2/pendsv: Account for PendSV running on both cores, and without CYW43. Changes: - Move setting of PendSV priority to pendsv_init(). - Call pendsv_init() from CPU1 as well, to ensure priority is the same. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/mpnetworkport.c | 4 ---- ports/rp2/mpthreadport.c | 3 +++ ports/rp2/pendsv.c | 30 +++++++++++++++++++++++------- ports/rp2/pendsv.h | 3 --- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c index 7a6df7f6e5b4d..e1e1567828bfb 100644 --- a/ports/rp2/mpnetworkport.c +++ b/ports/rp2/mpnetworkport.c @@ -42,7 +42,6 @@ static soft_timer_entry_t mp_network_soft_timer; #if MICROPY_PY_NETWORK_CYW43 #include "lib/cyw43-driver/src/cyw43.h" #include "lib/cyw43-driver/src/cyw43_stats.h" -#include "hardware/irq.h" #if !defined(__riscv) #if PICO_RP2040 @@ -97,9 +96,6 @@ 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); - #if !defined(__riscv) - NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_PENDSV); - #endif } // This hook will run on whichever CPU serviced the PendSV interrupt diff --git a/ports/rp2/mpthreadport.c b/ports/rp2/mpthreadport.c index 8fd5e5d790277..043a878c89b06 100644 --- a/ports/rp2/mpthreadport.c +++ b/ports/rp2/mpthreadport.c @@ -106,6 +106,9 @@ static void core1_entry_wrapper(void) { // Allow MICROPY_BEGIN_ATOMIC_SECTION to be invoked from core0. multicore_lockout_victim_init(); + // Set PendSV interrupt priority correctly for CPU1 + pendsv_init(); + if (core1_entry) { core1_entry(core1_arg); } diff --git a/ports/rp2/pendsv.c b/ports/rp2/pendsv.c index 0bf64d0ae440d..05b521fde2607 100644 --- a/ports/rp2/pendsv.c +++ b/ports/rp2/pendsv.c @@ -28,6 +28,7 @@ #include "py/mpconfig.h" #include "py/mpthread.h" #include "pendsv.h" +#include "hardware/irq.h" #if PICO_RP2040 #include "RP2040.h" @@ -45,6 +46,9 @@ static pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS]; static inline void pendsv_resume_run_dispatch(void); +// PendSV IRQ priority, to run system-level tasks that preempt the main thread. +#define IRQ_PRI_PENDSV PICO_LOWEST_IRQ_PRIORITY + void PendSV_Handler(void); #if MICROPY_PY_THREAD @@ -53,8 +57,14 @@ void PendSV_Handler(void); // loop of mp_wfe_or_timeout(), where we don't want the CPU event bit to be set. static mp_thread_recursive_mutex_t pendsv_mutex; +// Called from CPU0 during boot, but may be called later when CPU1 wakes up void pendsv_init(void) { - mp_thread_recursive_mutex_init(&pendsv_mutex); + if (get_core_num() == 0) { + mp_thread_recursive_mutex_init(&pendsv_mutex); + } + #if !defined(__riscv) + NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_PENDSV); + #endif } void pendsv_suspend(void) { @@ -117,11 +127,13 @@ static inline void pendsv_resume_run_dispatch(void) { void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { pendsv_dispatch_table[slot] = f; + // There is a race here where other core calls pendsv_suspend() before ISR + // can execute so this check fails, but dispatch will happen later when + // other core calls pendsv_resume(). if (pendsv_suspend_count() == 0) { #if PICO_ARM - // 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(). + // Note this register is part of each CPU core, so setting it on CPUx + // will set the IRQ and run PendSV_Handler on CPUx only. SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; #elif PICO_RISCV struct timespec ts; @@ -136,15 +148,19 @@ void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { } // PendSV interrupt handler to perform background processing. +// +// Handler can execute on either CPU if MICROPY_PY_THREAD is set (no code on +// CPU1 calls pendsv_schedule_dispatch(), but CPU1 can call pendsv_resume() +// which will trigger it). void PendSV_Handler(void) { #if MICROPY_PY_THREAD if (!mp_thread_recursive_mutex_lock(&pendsv_mutex, 0)) { - // Failure here means core 1 holds pendsv_mutex. ISR will - // run again after core 1 calls pendsv_resume(). + // Failure here means other core holds pendsv_mutex. ISR will + // run again after that core calls pendsv_resume(). return; } - // Core 0 should not already have locked pendsv_mutex + // This core should not already have locked pendsv_mutex assert(pendsv_mutex.mutex.enter_count == 1); #else assert(pendsv_suspend_count() == 0); diff --git a/ports/rp2/pendsv.h b/ports/rp2/pendsv.h index 6abe950df632e..de89f0473e7fa 100644 --- a/ports/rp2/pendsv.h +++ b/ports/rp2/pendsv.h @@ -42,9 +42,6 @@ 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_init(void); From 2b2a43187890c47a90ef28b0874691d4df176d14 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 9 Mar 2025 11:26:53 +0100 Subject: [PATCH 415/546] rp2/machine_uart: Fix unintended UART buffer allocation on init(). The buffer was be reset on every call to uart.init(). If no sizes were given, the buffer was set to the default size 256. That made problems e.g. with PPP. This commit fixes it, keeping the buffer size if not deliberately changed and allocating new buffers only if the size was changed. Signed-off-by: robert-hh --- ports/rp2/machine_uart.c | 41 ++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index 334c6fda328f3..9a4507ada69c4 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -116,8 +116,10 @@ typedef struct _machine_uart_obj_t { uint16_t timeout_char; // timeout waiting between chars (in ms) uint8_t invert; uint8_t flow; + uint16_t rxbuf_len; ringbuf_t read_buffer; mutex_t *read_mutex; + uint16_t txbuf_len; ringbuf_t write_buffer; mutex_t *write_mutex; uint16_t mp_irq_trigger; // user IRQ trigger mask @@ -128,10 +130,10 @@ typedef struct _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, NULL}, + 0, 0, 0, 0, 0, {NULL, 1, 0, 0}, &read_mutex_0, 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}, + 0, 0, 0, 0, 0, {NULL, 1, 0, 0}, &read_mutex_1, 0, {NULL, 1, 0, 0}, &write_mutex_1, 0, 0, NULL}, }; static const char *_parity_name[] = {"None", "0", "1"}; @@ -252,7 +254,7 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ 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, 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->stop, self->tx, self->rx, self->txbuf_len, self->rxbuf_len, self->timeout, self->timeout_char, _invert_name[self->invert], self->mp_irq_trigger); } @@ -365,25 +367,33 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, } // Set the RX buffer size if configured. - size_t rxbuf_len = DEFAULT_BUFFER_SIZE; if (args[ARG_rxbuf].u_int > 0) { - rxbuf_len = args[ARG_rxbuf].u_int; + size_t rxbuf_len = args[ARG_rxbuf].u_int; if (rxbuf_len < MIN_BUFFER_SIZE) { rxbuf_len = MIN_BUFFER_SIZE; } else if (rxbuf_len > MAX_BUFFER_SIZE) { mp_raise_ValueError(MP_ERROR_TEXT("rxbuf too large")); } + // Force re-allocting of the buffer if the size changed + if (rxbuf_len != self->rxbuf_len) { + self->read_buffer.buf = NULL; + self->rxbuf_len = rxbuf_len; + } } // Set the TX buffer size if configured. - size_t txbuf_len = DEFAULT_BUFFER_SIZE; if (args[ARG_txbuf].u_int > 0) { - txbuf_len = args[ARG_txbuf].u_int; + size_t txbuf_len = args[ARG_txbuf].u_int; if (txbuf_len < MIN_BUFFER_SIZE) { txbuf_len = MIN_BUFFER_SIZE; } else if (txbuf_len > MAX_BUFFER_SIZE) { mp_raise_ValueError(MP_ERROR_TEXT("txbuf too large")); } + // Force re-allocting of the buffer if the size changed + if (txbuf_len != self->txbuf_len) { + self->write_buffer.buf = NULL; + self->txbuf_len = txbuf_len; + } } // Initialise the UART peripheral if any arguments given, or it was not initialised previously. @@ -423,11 +433,14 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, uart_set_hw_flow(self->uart, self->flow & UART_HWCONTROL_CTS, self->flow & UART_HWCONTROL_RTS); // Allocate the RX/TX buffers. - ringbuf_alloc(&(self->read_buffer), rxbuf_len + 1); - MP_STATE_PORT(rp2_uart_rx_buffer[self->uart_id]) = self->read_buffer.buf; - - ringbuf_alloc(&(self->write_buffer), txbuf_len + 1); - MP_STATE_PORT(rp2_uart_tx_buffer[self->uart_id]) = self->write_buffer.buf; + if (self->read_buffer.buf == NULL) { + ringbuf_alloc(&(self->read_buffer), self->rxbuf_len + 1); + MP_STATE_PORT(rp2_uart_rx_buffer[self->uart_id]) = self->read_buffer.buf; + } + if (self->write_buffer.buf == NULL) { + ringbuf_alloc(&(self->write_buffer), self->txbuf_len + 1); + MP_STATE_PORT(rp2_uart_tx_buffer[self->uart_id]) = self->write_buffer.buf; + } // Set the irq handler. if (self->uart_id == 0) { @@ -454,6 +467,10 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg // Get static peripheral object. machine_uart_obj_t *self = (machine_uart_obj_t *)&machine_uart_obj[uart_id]; + self->rxbuf_len = DEFAULT_BUFFER_SIZE; + self->read_buffer.buf = NULL; + self->txbuf_len = DEFAULT_BUFFER_SIZE; + self->write_buffer.buf = NULL; // Initialise the UART peripheral. mp_map_t kw_args; From 6db7b47ab931db3e95d2d97ad3c383a6ba035bb0 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 9 Mar 2025 11:39:12 +0100 Subject: [PATCH 416/546] samd/machine_uart: Fix unintended UART buffer allocation on init(). The buffer was be reset on every call to uart.init(). If no sizes were given, the buffer was set to the default size 256. That made problems e.g. with PPP. This commit fixes it, keeping the buffer size if not deliberately changed and allocating new buffers only if the size was changed. Cater for changes of the bits value, which requires a change to the buffer size. Signed-off-by: robert-hh --- ports/samd/machine_uart.c | 49 ++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c index 45bfc3ddd5002..85a81660ca05f 100644 --- a/ports/samd/machine_uart.c +++ b/ports/samd/machine_uart.c @@ -88,8 +88,10 @@ 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) bool new; + uint16_t rxbuf_len; ringbuf_t read_buffer; #if MICROPY_HW_UART_TXBUF + uint16_t txbuf_len; ringbuf_t write_buffer; #endif #if MICROPY_PY_MACHINE_UART_IRQ @@ -287,16 +289,6 @@ void machine_uart_set_baudrate(mp_obj_t self_in, uint32_t baudrate) { 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); - size_t rxbuf_len = self->read_buffer.size - 1; - #if MICROPY_HW_UART_TXBUF - size_t txbuf_len = self->write_buffer.size - 1; - #endif - if (self->bits > 8) { - rxbuf_len /= 2; - #if MICROPY_HW_UART_TXBUF - txbuf_len /= 2; - #endif - } mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, " "tx=\"%q\", rx=\"%q\", timeout=%u, timeout_char=%u, rxbuf=%d" @@ -312,9 +304,9 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ ")", self->id, self->baudrate, self->bits, _parity_name[self->parity], self->stop + 1, pin_find_by_id(self->tx)->name, pin_find_by_id(self->rx)->name, - self->timeout, self->timeout_char, rxbuf_len + self->timeout, self->timeout_char, self->rxbuf_len #if MICROPY_HW_UART_TXBUF - , txbuf_len + , self->txbuf_len #endif #if MICROPY_HW_UART_RTSCTS , self->rts != 0xff ? pin_find_by_id(self->rts)->name : MP_QSTR_None @@ -358,6 +350,11 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, // Set bits if configured. if (args[ARG_bits].u_int > 0) { self->bits = args[ARG_bits].u_int; + // Invalidate the buffers since the size may have to be changed + self->read_buffer.buf = NULL; + #if MICROPY_HW_UART_TXBUF + self->write_buffer.buf = NULL; + #endif } // Set parity if configured. @@ -414,7 +411,7 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, } // Set the RX buffer size if configured. - size_t rxbuf_len = DEFAULT_BUFFER_SIZE; + size_t rxbuf_len = self->rxbuf_len; if (args[ARG_rxbuf].u_int > 0) { rxbuf_len = args[ARG_rxbuf].u_int; if (rxbuf_len < MIN_BUFFER_SIZE) { @@ -422,11 +419,16 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, } else if (rxbuf_len > MAX_BUFFER_SIZE) { mp_raise_ValueError(MP_ERROR_TEXT("rxbuf too large")); } + // Force re-allocting of the buffer if the size changed + if (rxbuf_len != self->rxbuf_len) { + self->read_buffer.buf = NULL; + self->rxbuf_len = rxbuf_len; + } } #if MICROPY_HW_UART_TXBUF // Set the TX buffer size if configured. - size_t txbuf_len = DEFAULT_BUFFER_SIZE; + size_t txbuf_len = self->txbuf_len; if (args[ARG_txbuf].u_int > 0) { txbuf_len = args[ARG_txbuf].u_int; if (txbuf_len < MIN_BUFFER_SIZE) { @@ -434,6 +436,11 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, } else if (txbuf_len > MAX_BUFFER_SIZE) { mp_raise_ValueError(MP_ERROR_TEXT("txbuf too large")); } + // Force re-allocting of the buffer if the size changed + if (txbuf_len != self->txbuf_len) { + self->write_buffer.buf = NULL; + self->txbuf_len = txbuf_len; + } } #endif @@ -462,10 +469,14 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, } // Allocate the RX/TX buffers. - ringbuf_alloc(&(self->read_buffer), rxbuf_len + 1); + if (self->read_buffer.buf == NULL) { + ringbuf_alloc(&(self->read_buffer), rxbuf_len + 1); + } #if MICROPY_HW_UART_TXBUF - ringbuf_alloc(&(self->write_buffer), txbuf_len + 1); + if (self->write_buffer.buf == NULL) { + ringbuf_alloc(&(self->write_buffer), txbuf_len + 1); + } #endif // Step 1: Configure the Pin mux. @@ -503,6 +514,12 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->stop = 0; self->timeout = 1; self->timeout_char = 1; + self->rxbuf_len = DEFAULT_BUFFER_SIZE; + self->read_buffer.buf = NULL; + #if MICROPY_HW_UART_TXBUF + self->txbuf_len = DEFAULT_BUFFER_SIZE; + self->write_buffer.buf = NULL; + #endif #if defined(pin_TX) && defined(pin_RX) // Initialize with the default pins self->tx = mp_hal_get_pin_obj((mp_obj_t)pin_TX); From 4dfee50a0be8d61c79928f7bb11a4b77c485aea1 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 9 Mar 2025 11:39:12 +0100 Subject: [PATCH 417/546] samd/machine_uart: Fix lock-up in loopback mode if read buffer is full. Signed-off-by: robert-hh --- ports/samd/machine_uart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c index 85a81660ca05f..5be38e961c121 100644 --- a/ports/samd/machine_uart.c +++ b/ports/samd/machine_uart.c @@ -156,7 +156,8 @@ void common_uart_irq_handler(int uart_id) { } } #endif - } else if (uart->USART.INTFLAG.bit.DRE != 0) { + } + if (uart->USART.INTFLAG.bit.DRE != 0) { #if MICROPY_HW_UART_TXBUF // handle the outgoing data if (ringbuf_avail(&self->write_buffer) > 0) { From 56e90cb60b028f0a306e6253ad183e6cd4a2b45e Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Mar 2025 13:14:46 +1100 Subject: [PATCH 418/546] py/mpconfig: Enable 2-argument built-in next() at basic feature level. This is a pretty fundamental built-in and having CPython-compatible behaviour is beneficial. The code size increase is not much, and ports/boards can still disable it if needed to save space. Addresses issue #5384. Signed-off-by: Damien George --- py/mpconfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 05f39b3605bf3..dfa32ebf7614a 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1215,7 +1215,7 @@ typedef double mp_float_t; // Support for calling next() with second argument #ifndef MICROPY_PY_BUILTINS_NEXT2 -#define MICROPY_PY_BUILTINS_NEXT2 (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#define MICROPY_PY_BUILTINS_NEXT2 (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES) #endif // Whether to support rounding of integers (incl bignum); eg round(123,-1)=120 From 994751c25139496eed9c0eb90949ae8fd681e734 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 18 Mar 2025 10:58:43 +1100 Subject: [PATCH 419/546] tests/cpydiff: Remove builtin_next_arg2.py difference. Because 2-arg `next()` is implemented, and now enabled at the basic feature level. Signed-off-by: Damien George --- tests/cpydiff/builtin_next_arg2.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 tests/cpydiff/builtin_next_arg2.py diff --git a/tests/cpydiff/builtin_next_arg2.py b/tests/cpydiff/builtin_next_arg2.py deleted file mode 100644 index ed9565fe0fe77..0000000000000 --- a/tests/cpydiff/builtin_next_arg2.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -categories: Modules,builtins -description: Second argument to next() is not implemented -cause: MicroPython is optimised for code space. -workaround: Instead of ``val = next(it, deflt)`` use:: - - try: - val = next(it) - except StopIteration: - val = deflt -""" - -print(next(iter(range(0)), 42)) From fdc0c6f8f673cf5e6d2b02ff6820bcc14afe9c94 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 18 Mar 2025 10:24:58 +1100 Subject: [PATCH 420/546] py/dynruntime: Make malloc functions raise MemoryError on failure. Addresses some TODOs in this file. Signed-off-by: Damien George --- py/dynruntime.h | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/py/dynruntime.h b/py/dynruntime.h index d360d824ac39c..c93111bbd4d6f 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -28,6 +28,14 @@ // This header file contains definitions to dynamically implement the static // MicroPython runtime API defined in py/obj.h and py/runtime.h. +// +// All of the symbols made available in this header are overriding those defined +// in py/obj.h and py/runtime.h. This is done with macros. For macros that +// would be too complicated (usually more than a single expression), they call a +// static-inline function for the implementation. This function has the same +// name as the macro (hence the same name as a public API function) but with +// "_dyn" appended. For example, the m_malloc() macro calls the m_malloc_dyn() +// static-inline function. #include "py/binary.h" #include "py/nativeglue.h" @@ -39,6 +47,10 @@ #error "dynruntime.h included in non-dynamic-module build." #endif +#if MICROPY_MALLOC_USES_ALLOCATED_SIZE +#error "MICROPY_MALLOC_USES_ALLOCATED_SIZE must be disable in a dynamic-module build." +#endif + #undef MP_ROM_QSTR #undef MP_OBJ_QSTR_VALUE #undef MP_OBJ_NEW_QSTR @@ -52,13 +64,32 @@ /******************************************************************************/ // Memory allocation +#define m_malloc_fail(num_bytes) (m_malloc_fail_dyn((num_bytes))) #define m_malloc(n) (m_malloc_dyn((n))) #define m_free(ptr) (m_free_dyn((ptr))) #define m_realloc(ptr, new_num_bytes) (m_realloc_dyn((ptr), (new_num_bytes))) +#define m_realloc_maybe(ptr, new_num_bytes, allow_move) (m_realloc_maybe_dyn((ptr), (new_num_bytes), (allow_move))) + +static NORETURN inline void m_malloc_fail_dyn(size_t num_bytes) { + mp_fun_table.raise_msg( + mp_fun_table.load_global(MP_QSTR_MemoryError), + "memory allocation failed"); +} + +static inline void *m_realloc_maybe_dyn(void *ptr, size_t new_num_bytes, bool allow_move) { + return mp_fun_table.realloc_(ptr, new_num_bytes, allow_move); +} + +static inline void *m_realloc_checked_dyn(void *ptr, size_t new_num_bytes, bool allow_move) { + ptr = m_realloc_maybe(ptr, new_num_bytes, allow_move); + if (ptr == NULL && new_num_bytes != 0) { + m_malloc_fail(new_num_bytes); + } + return ptr; +} static inline void *m_malloc_dyn(size_t n) { - // TODO won't raise on OOM - return mp_fun_table.realloc_(NULL, n, false); + return m_realloc_checked_dyn(NULL, n, false); } static inline void m_free_dyn(void *ptr) { @@ -66,8 +97,7 @@ static inline void m_free_dyn(void *ptr) { } static inline void *m_realloc_dyn(void *ptr, size_t new_num_bytes) { - // TODO won't raise on OOM - return mp_fun_table.realloc_(ptr, new_num_bytes, true); + return m_realloc_checked_dyn(ptr, new_num_bytes, true); } /******************************************************************************/ From fa42487e45620534440256c9b29e4526f3137de9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 18 Mar 2025 00:34:20 +1100 Subject: [PATCH 421/546] extmod/moddeflate: Keep DeflateIO state consistent on window alloc fail. Allocation of a large compression window may fail, and in that case keep the `DeflateIO` state consistent so its other methods (such as `close()`) still work. Consistency is kept by only updating the `self->write` member if the window allocation succeeds. Thanks to @jimmo for finding the bug. Signed-off-by: Damien George --- extmod/moddeflate.c | 12 ++++-- tests/extmod/deflate_compress_memory_error.py | 39 +++++++++++++++++++ .../deflate_compress_memory_error.py.exp | 2 + tests/run-tests.py | 1 + 4 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 tests/extmod/deflate_compress_memory_error.py create mode 100644 tests/extmod/deflate_compress_memory_error.py.exp diff --git a/extmod/moddeflate.c b/extmod/moddeflate.c index 4afad01618c4d..920b898b2ccc2 100644 --- a/extmod/moddeflate.c +++ b/extmod/moddeflate.c @@ -168,16 +168,20 @@ static bool deflateio_init_write(mp_obj_deflateio_t *self) { const mp_stream_p_t *stream = mp_get_stream_raise(self->stream, MP_STREAM_OP_WRITE); - self->write = m_new_obj(mp_obj_deflateio_write_t); - self->write->input_len = 0; - int wbits = self->window_bits; if (wbits == 0) { // Same default wbits for all formats. wbits = DEFLATEIO_DEFAULT_WBITS; } + + // Allocate the large window before allocating the mp_obj_deflateio_write_t, in case the + // window allocation fails the mp_obj_deflateio_t object will remain in a consistent state. size_t window_len = 1 << wbits; - self->write->window = m_new(uint8_t, window_len); + uint8_t *window = m_new(uint8_t, window_len); + + self->write = m_new_obj(mp_obj_deflateio_write_t); + self->write->window = window; + self->write->input_len = 0; uzlib_lz77_init(&self->write->lz77, self->write->window, window_len); self->write->lz77.dest_write_data = self; diff --git a/tests/extmod/deflate_compress_memory_error.py b/tests/extmod/deflate_compress_memory_error.py new file mode 100644 index 0000000000000..56ce0603081d4 --- /dev/null +++ b/tests/extmod/deflate_compress_memory_error.py @@ -0,0 +1,39 @@ +# Test deflate.DeflateIO compression, with out-of-memory errors. + +try: + # Check if deflate is available. + import deflate + import io +except ImportError: + print("SKIP") + raise SystemExit + +# Check if compression is enabled. +if not hasattr(deflate.DeflateIO, "write"): + print("SKIP") + raise SystemExit + +# Create a compressor object. +b = io.BytesIO() +g = deflate.DeflateIO(b, deflate.RAW, 15) + +# Then, use up most of the heap. +l = [] +while True: + try: + l.append(bytearray(1000)) + except: + break +l.pop() + +# Try to compress. This will try to allocate a large window and fail. +try: + g.write('test') +except MemoryError: + print("MemoryError") + +# Should still be able to close the stream. +g.close() + +# The underlying output stream should be unchanged. +print(b.getvalue()) diff --git a/tests/extmod/deflate_compress_memory_error.py.exp b/tests/extmod/deflate_compress_memory_error.py.exp new file mode 100644 index 0000000000000..606315c14604c --- /dev/null +++ b/tests/extmod/deflate_compress_memory_error.py.exp @@ -0,0 +1,2 @@ +MemoryError +b'' diff --git a/tests/run-tests.py b/tests/run-tests.py index 50adb6b4d3bea..9e7cab4689a8d 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -163,6 +163,7 @@ def open(self, path, mode): "extmod/asyncio_threadsafeflag.py", "extmod/asyncio_wait_for_fwd.py", "extmod/binascii_a2b_base64.py", + "extmod/deflate_compress_memory_error.py", # tries to allocate unlimited memory "extmod/re_stack_overflow.py", "extmod/time_res.py", "extmod/vfs_posix.py", From 458a8f2e15c8932f83331ce316db3a71551c7a9a Mon Sep 17 00:00:00 2001 From: Anson Mansfield Date: Fri, 14 Mar 2025 13:16:49 -0400 Subject: [PATCH 422/546] extmod/vfs: Refactor mp_vfs_mount to enable no-args mount overload. Signed-off-by: Anson Mansfield --- extmod/vfs.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/extmod/vfs.c b/extmod/vfs.c index c83b8ce7d563e..88a32c3bc800d 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -206,22 +206,24 @@ static mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) { } mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_readonly, ARG_mkfs }; + enum { ARG_fsobj, ARG_mount_point, ARG_readonly, ARG_mkfs }; static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_FALSE} }, { MP_QSTR_mkfs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_FALSE} }, }; // parse args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // get the mount point size_t mnt_len; - const char *mnt_str = mp_obj_str_get_data(pos_args[1], &mnt_len); + const char *mnt_str = mp_obj_str_get_data(args[ARG_mount_point].u_obj, &mnt_len); // see if we need to auto-detect and create the filesystem - mp_obj_t vfs_obj = pos_args[0]; + mp_obj_t vfs_obj = args[ARG_fsobj].u_obj; mp_obj_t dest[2]; mp_load_method_maybe(vfs_obj, MP_QSTR_mount, dest); if (dest[0] == MP_OBJ_NULL) { @@ -238,11 +240,13 @@ mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args vfs->next = NULL; // call the underlying object to do any mounting operation - mp_vfs_proxy_call(vfs, MP_QSTR_mount, 2, (mp_obj_t *)&args); + mp_arg_val_t *proxy_args = &args[ARG_readonly]; + size_t proxy_args_len = MP_ARRAY_SIZE(args) - ARG_readonly; + mp_vfs_proxy_call(vfs, MP_QSTR_mount, proxy_args_len, (mp_obj_t *)proxy_args); // check that the destination mount point is unused const char *path_out; - mp_vfs_mount_t *existing_mount = mp_vfs_lookup_path(mp_obj_str_get_str(pos_args[1]), &path_out); + mp_vfs_mount_t *existing_mount = mp_vfs_lookup_path(mp_obj_str_get_str(args[ARG_mount_point].u_obj), &path_out); if (existing_mount != MP_VFS_NONE && existing_mount != MP_VFS_ROOT) { if (vfs->len != 1 && existing_mount->len == 1) { // if root dir is mounted, still allow to mount something within a subdir of root @@ -266,7 +270,7 @@ mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj, 2, mp_vfs_mount); +MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj, 0, mp_vfs_mount); mp_obj_t mp_vfs_umount(mp_obj_t mnt_in) { // remove vfs from the mount table From 1487a13079ec5e33cac8d273e6d2fa805bca4b1e Mon Sep 17 00:00:00 2001 From: Anson Mansfield Date: Fri, 14 Mar 2025 13:19:06 -0400 Subject: [PATCH 423/546] extmod/vfs: Return mount table from no-args vfs.mount call. This extends the existing `vfs.mount()` function to accept zero arguments, in which case it returns a list of tuples of mounted filesystem objects and their mount location. Signed-off-by: Anson Mansfield --- extmod/vfs.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/extmod/vfs.c b/extmod/vfs.c index 88a32c3bc800d..aebf5ed295b3b 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -206,6 +206,18 @@ static mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) { } mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + if (n_args == 0) { + // zero-args, output a table of all current mountpoints + mp_obj_t mount_list = mp_obj_new_list(0, NULL); + mp_vfs_mount_t *vfsp = MP_STATE_VM(vfs_mount_table); + while (vfsp != NULL) { + mp_obj_t items[] = { vfsp->obj, mp_obj_new_str(vfsp->str, vfsp->len) }; + mp_obj_list_append(mount_list, mp_obj_new_tuple(MP_ARRAY_SIZE(items), items)); + vfsp = vfsp->next; + } + return mount_list; + } + enum { ARG_fsobj, ARG_mount_point, ARG_readonly, ARG_mkfs }; static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, From 9fcc25b9d7e6246f9c1dda3c07f3e718f751e74c Mon Sep 17 00:00:00 2001 From: Anson Mansfield Date: Mon, 3 Mar 2025 14:05:31 -0500 Subject: [PATCH 424/546] tests/extmod/vfs_mountinfo.py: Add test for no-args mount output. Signed-off-by: Anson Mansfield --- tests/extmod/vfs_mountinfo.py | 66 +++++++++++++++++++++++++++++++ tests/extmod/vfs_mountinfo.py.exp | 11 ++++++ 2 files changed, 77 insertions(+) create mode 100644 tests/extmod/vfs_mountinfo.py create mode 100644 tests/extmod/vfs_mountinfo.py.exp diff --git a/tests/extmod/vfs_mountinfo.py b/tests/extmod/vfs_mountinfo.py new file mode 100644 index 0000000000000..f674e80763409 --- /dev/null +++ b/tests/extmod/vfs_mountinfo.py @@ -0,0 +1,66 @@ +# test VFS functionality without any particular filesystem type + +try: + import os, vfs +except ImportError: + print("SKIP") + raise SystemExit +import errno + + +class Filesystem: + def __init__(self, id, paths=[]): + self.id = id + self.paths = paths + + def mount(self, readonly, mksfs): + print("mount", self) + + def umount(self): + print("umount", self) + + def stat(self, path): + if path in self.paths: + return (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + else: + raise OSError + + statvfs = stat + + def open(self, path, mode): + pass + + def __repr__(self): + return "Filesystem(%d)" % self.id + + +# first we umount any existing mount points the target may have +try: + vfs.umount("/") +except OSError: + pass +for path in os.listdir("/"): + vfs.umount("/" + path) + + +print(vfs.mount()) + +vfs.mount(Filesystem(1), "/foo") + +print(vfs.mount()) + +vfs.mount(Filesystem(2), "/bar/baz") + +print(vfs.mount()) + +vfs.mount(Filesystem(3), "/bar") + +print(vfs.mount()) + +vfs.umount("/bar/baz") + +print(vfs.mount()) + +vfs.mount(Filesystem(4), "/") + +print(vfs.mount()) diff --git a/tests/extmod/vfs_mountinfo.py.exp b/tests/extmod/vfs_mountinfo.py.exp new file mode 100644 index 0000000000000..4ddf06c8c976f --- /dev/null +++ b/tests/extmod/vfs_mountinfo.py.exp @@ -0,0 +1,11 @@ +[] +mount Filesystem(1) +[(Filesystem(1), '/foo')] +mount Filesystem(2) +[(Filesystem(1), '/foo'), (Filesystem(2), '/bar/baz')] +mount Filesystem(3) +[(Filesystem(1), '/foo'), (Filesystem(2), '/bar/baz'), (Filesystem(3), '/bar')] +umount Filesystem(2) +[(Filesystem(1), '/foo'), (Filesystem(3), '/bar')] +mount Filesystem(4) +[(Filesystem(1), '/foo'), (Filesystem(3), '/bar'), (Filesystem(4), '/')] From c68a40ac94ea8a8dd6031dff6f21706977893bef Mon Sep 17 00:00:00 2001 From: Anson Mansfield Date: Mon, 3 Mar 2025 14:25:14 -0500 Subject: [PATCH 425/546] docs/library/vfs: Document no-args mount output. Signed-off-by: Anson Mansfield --- docs/library/vfs.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/library/vfs.rst b/docs/library/vfs.rst index fcd06eb4353b1..1fa1e3060cb5b 100644 --- a/docs/library/vfs.rst +++ b/docs/library/vfs.rst @@ -34,6 +34,14 @@ represented by VFS classes. Will raise ``OSError(EPERM)`` if *mount_point* is already mounted. +.. function:: mount() + :noindex: + + With no arguments to :func:`mount`, return a list of tuples representing + all active mountpoints. + + The returned list has the form *[(fsobj, mount_point), ...]*. + .. function:: umount(mount_point) Unmount a filesystem. *mount_point* can be a string naming the mount location, From e4051a1ca66703090383678a1747c3f99e993309 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Mar 2025 14:39:41 +1100 Subject: [PATCH 426/546] extmod/vfs_rom: Implement minimal VfsRom.getcwd() method. This is needed if you chdir to a ROMFS and want to query your current directory. Prior to this change, using `os.getcwd()` when in a ROMFS would raise: AttributeError: 'VfsRom' object has no attribute 'getcwd' Signed-off-by: Damien George --- extmod/vfs_rom.c | 8 ++++++++ tests/extmod/vfs_rom.py | 2 ++ 2 files changed, 10 insertions(+) diff --git a/extmod/vfs_rom.c b/extmod/vfs_rom.c index ff3652d2ce3a2..7d814cb980524 100644 --- a/extmod/vfs_rom.c +++ b/extmod/vfs_rom.c @@ -300,6 +300,13 @@ static mp_obj_t vfs_rom_chdir(mp_obj_t self_in, mp_obj_t path_in) { } static MP_DEFINE_CONST_FUN_OBJ_2(vfs_rom_chdir_obj, vfs_rom_chdir); +static mp_obj_t vfs_rom_getcwd(mp_obj_t self_in) { + (void)self_in; + // The current directory is always the root of the ROMFS. + return MP_OBJ_NEW_QSTR(MP_QSTR_); +} +static MP_DEFINE_CONST_FUN_OBJ_1(vfs_rom_getcwd_obj, vfs_rom_getcwd); + typedef struct _vfs_rom_ilistdir_it_t { mp_obj_base_t base; mp_fun_1_t iternext; @@ -436,6 +443,7 @@ static const mp_rom_map_elem_t vfs_rom_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&vfs_rom_open_obj) }, { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&vfs_rom_chdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&vfs_rom_getcwd_obj) }, { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&vfs_rom_ilistdir_obj) }, { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&vfs_rom_stat_obj) }, { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&vfs_rom_statvfs_obj) }, diff --git a/tests/extmod/vfs_rom.py b/tests/extmod/vfs_rom.py index dc88481c028ac..770b6863b9c43 100644 --- a/tests/extmod/vfs_rom.py +++ b/tests/extmod/vfs_rom.py @@ -408,9 +408,11 @@ def test_listdir(self): def test_chdir(self): os.chdir("/test_rom") + self.assertEqual(os.getcwd(), "/test_rom") self.assertEqual(os.listdir(), self.romfs_listdir) os.chdir("/test_rom/") + self.assertEqual(os.getcwd(), "/test_rom") self.assertEqual(os.listdir(), self.romfs_listdir) # chdir within the romfs is not implemented. From 2db0c0225f41292f85b2cf49257d663856d5c8db Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Mar 2025 14:52:15 +1100 Subject: [PATCH 427/546] tools/mpremote: Make mip install skip /rom*/lib directories. If a ROMFS is mounted then "/rom/lib" is usually in `sys.path` before the writable filesystem's "lib" entry. The ROMFS directory cannot be installed to, so skip it if found. Signed-off-by: Damien George --- tools/mpremote/mpremote/mip.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/mpremote/mpremote/mip.py b/tools/mpremote/mpremote/mip.py index 858b7933d11d8..26ae8bec5ec6c 100644 --- a/tools/mpremote/mpremote/mip.py +++ b/tools/mpremote/mpremote/mip.py @@ -175,7 +175,11 @@ def do_mip(state, args): if args.target is None: state.transport.exec("import sys") - lib_paths = [p for p in state.transport.eval("sys.path") if p.endswith("/lib")] + lib_paths = [ + p + for p in state.transport.eval("sys.path") + if not p.startswith("/rom") and p.endswith("/lib") + ] if lib_paths and lib_paths[0]: args.target = lib_paths[0] else: From cccac2cc0127eb43de4524c74424ecaaa594c80e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 11 Mar 2025 17:17:39 +1100 Subject: [PATCH 428/546] rp2,esp32,extmod: Implement UPDATE_SUBMODULES in CMake. Rather than having Make calling CMake to generate a list of submodules and then run a Make target (which is complex and prone to masking other errors), implement the submodule update logic in CMake itself. Internal CMake-side changes are that GIT_SUBMODULES is now a CMake list, and the trigger variable name is changed from ECHO_SUBMODULES to UPDATE_SUBMODULES. The run is otherwise 100% a normal CMake run now, so most of the other special casing can be removed. Signed-off-by: Angus Gratton --- extmod/extmod.cmake | 89 ++++++++++++++++------------------ ports/esp32/Makefile | 14 +++--- ports/esp32/esp32_common.cmake | 15 +++++- ports/rp2/CMakeLists.txt | 18 +++---- ports/rp2/Makefile | 16 +++--- py/mkrules.cmake | 42 ++++++++++------ py/mkrules.mk | 9 +--- 7 files changed, 105 insertions(+), 98 deletions(-) diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index 3643f1aee7bf5..7cacd0a43329e 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -107,62 +107,57 @@ set(MICROPY_SOURCE_LIB_LIBM_SQRT_HW ${MICROPY_DIR}/lib/libm/thumb_vfp_sqrtf.c) if(MICROPY_PY_BTREE) set(MICROPY_LIB_BERKELEY_DIR "${MICROPY_DIR}/lib/berkeley-db-1.xx") - string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/berkeley-db-1.xx) + list(APPEND GIT_SUBMODULES lib/berkeley-db-1.xx) - if(ECHO_SUBMODULES) - # No-op, we're just doing submodule/variant discovery. - # Cannot run the add_library/target_include_directories rules (even though - # the build won't run) because IDF will attempt verify the files exist. - elseif(NOT EXISTS ${MICROPY_LIB_BERKELEY_DIR}/README) + if(NOT UPDATE_SUBMODULES AND NOT EXISTS ${MICROPY_LIB_BERKELEY_DIR}/README) # Regular build, submodule not initialised -- fail with a clear error. message(FATAL_ERROR " MICROPY_PY_BTREE is enabled but the berkeley-db submodule is not initialised.\n Run 'make BOARD=${MICROPY_BOARD} submodules'") - else() - # Regular build, we have the submodule. - add_library(micropy_extmod_btree OBJECT - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_close.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_conv.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_debug.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_delete.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_get.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_open.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_overflow.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_page.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_put.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_search.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_seq.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_split.c - ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_utils.c - ${MICROPY_LIB_BERKELEY_DIR}/mpool/mpool.c - ) + endif() - target_include_directories(micropy_extmod_btree PRIVATE - ${MICROPY_LIB_BERKELEY_DIR}/include - ) + add_library(micropy_extmod_btree OBJECT + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_close.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_conv.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_debug.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_delete.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_get.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_open.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_overflow.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_page.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_put.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_search.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_seq.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_split.c + ${MICROPY_LIB_BERKELEY_DIR}/btree/bt_utils.c + ${MICROPY_LIB_BERKELEY_DIR}/mpool/mpool.c + ) - if(NOT BERKELEY_DB_CONFIG_FILE) - set(BERKELEY_DB_CONFIG_FILE "${MICROPY_DIR}/extmod/berkeley-db/berkeley_db_config_port.h") - endif() + target_include_directories(micropy_extmod_btree PRIVATE + ${MICROPY_LIB_BERKELEY_DIR}/include + ) - target_compile_definitions(micropy_extmod_btree PRIVATE - BERKELEY_DB_CONFIG_FILE="${BERKELEY_DB_CONFIG_FILE}" - ) + if(NOT BERKELEY_DB_CONFIG_FILE) + set(BERKELEY_DB_CONFIG_FILE "${MICROPY_DIR}/extmod/berkeley-db/berkeley_db_config_port.h") + endif() - # The include directories and compile definitions below are needed to build - # modbtree.c and should be added to the main MicroPython target. + target_compile_definitions(micropy_extmod_btree PRIVATE + BERKELEY_DB_CONFIG_FILE="${BERKELEY_DB_CONFIG_FILE}" + ) - list(APPEND MICROPY_INC_CORE - "${MICROPY_LIB_BERKELEY_DIR}/include" - ) + # The include directories and compile definitions below are needed to build + # modbtree.c and should be added to the main MicroPython target. - list(APPEND MICROPY_DEF_CORE - MICROPY_PY_BTREE=1 - BERKELEY_DB_CONFIG_FILE="${BERKELEY_DB_CONFIG_FILE}" - ) + list(APPEND MICROPY_INC_CORE + "${MICROPY_LIB_BERKELEY_DIR}/include" + ) - list(APPEND MICROPY_SOURCE_EXTMOD - ${MICROPY_EXTMOD_DIR}/modbtree.c - ) - endif() + list(APPEND MICROPY_DEF_CORE + MICROPY_PY_BTREE=1 + BERKELEY_DB_CONFIG_FILE="${BERKELEY_DB_CONFIG_FILE}" + ) + + list(APPEND MICROPY_SOURCE_EXTMOD + ${MICROPY_EXTMOD_DIR}/modbtree.c + ) endif() # Library for mbedtls @@ -350,5 +345,5 @@ if(MICROPY_PY_LWIP) ${MICROPY_LIB_LWIP_DIR}/include ) - string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/lwip) + list(APPEND GIT_SUBMODULES lib/lwip) endif() diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 1ce4d97208da7..a4e5531850c7d 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -106,12 +106,10 @@ size-components: size-files: $(call RUN_IDF_PY,size-files) -# Running the build with ECHO_SUBMODULES set will trigger py/mkrules.cmake to -# print out the value of the GIT_SUBMODULES variable, prefixed with -# "GIT_SUBMODULES", and then abort. This extracts out that line from the idf.py -# output and passes the list of submodules to py/mkrules.mk which does the -# `git submodule init` on each. +# Run idf.py with the UPDATE_SUBMODULES flag to update +# necessary submodules for this board. +# +# This is done in a dedicated build directory as some CMake cache values are not +# set correctly if not all submodules are loaded yet. submodules: - @GIT_SUBMODULES=$$(IDF_COMPONENT_MANAGER=0 idf.py $(IDFPY_FLAGS) -B $(BUILD)/submodules -D ECHO_SUBMODULES=1 build 2>&1 | \ - grep '^GIT_SUBMODULES=' | cut -d= -f2); \ - $(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="$${GIT_SUBMODULES}" GIT_SUBMODULES_FAIL_IF_EMPTY=1 submodules + IDF_COMPONENT_MANAGER=0 idf.py $(IDFPY_FLAGS) -B $(BUILD)/submodules -D UPDATE_SUBMODULES=1 reconfigure diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 6473f04a53500..f7b00900a0388 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -71,8 +71,8 @@ list(APPEND MICROPY_SOURCE_DRIVERS ${MICROPY_DIR}/drivers/dht/dht.c ) -string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/tinyusb) -if(MICROPY_PY_TINYUSB AND NOT ECHO_SUBMODULES) +list(APPEND GIT_SUBMODULES lib/tinyusb) +if(MICROPY_PY_TINYUSB) set(TINYUSB_SRC "${MICROPY_DIR}/lib/tinyusb/src") string(TOUPPER OPT_MCU_${IDF_TARGET} tusb_mcu) @@ -195,6 +195,17 @@ if (MICROPY_USER_LDFRAGMENTS) set(MICROPY_LDFRAGMENTS ${MICROPY_USER_LDFRAGMENTS}) endif() +if (UPDATE_SUBMODULES) + # ESP-IDF checks if some paths exist before CMake does. Some paths don't + # yet exist if this is an UPDATE_SUBMODULES pass on a brand new checkout, so remove + # any path which might not exist yet. A "real" build will not set UPDATE_SUBMODULES. + unset(MICROPY_SOURCE_TINYUSB) + unset(MICROPY_SOURCE_EXTMOD) + unset(MICROPY_SOURCE_LIB) + unset(MICROPY_INC_TINYUSB) + unset(MICROPY_INC_CORE) +endif() + # Register the main IDF component. idf_component_register( SRCS diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 7002ad8769ea9..f89e2792c6479 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -76,8 +76,8 @@ if (MICROPY_PY_NETWORK_CYW43) endif() # Necessary submodules for all boards. -string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/mbedtls) -string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/tinyusb) +list(APPEND GIT_SUBMODULES lib/mbedtls) +list(APPEND GIT_SUBMODULES lib/tinyusb) # Include component cmake fragments include(${MICROPY_DIR}/py/py.cmake) @@ -340,7 +340,7 @@ if (MICROPY_PY_BLUETOOTH_CYW43) endif() if (MICROPY_BLUETOOTH_BTSTACK) - string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/btstack) + list(APPEND GIT_SUBMODULES lib/btstack) list(APPEND MICROPY_SOURCE_PORT mpbtstackport.c) @@ -358,8 +358,8 @@ if (MICROPY_BLUETOOTH_BTSTACK) endif() if(MICROPY_BLUETOOTH_NIMBLE) - string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/mynewt-nimble) - if(NOT (${ECHO_SUBMODULES}) AND NOT EXISTS ${MICROPY_DIR}/lib/mynewt-nimble/nimble/host/include/host/ble_hs.h) + list(APPEND GIT_SUBMODULES lib/mynewt-nimble) + if(NOT UPDATE_SUBMODULES AND NOT EXISTS ${MICROPY_DIR}/lib/mynewt-nimble/nimble/host/include/host/ble_hs.h) message(FATAL_ERROR " mynewt-nimble not initialized.\n Run 'make BOARD=${MICROPY_BOARD} submodules'") endif() @@ -386,8 +386,8 @@ target_include_directories(${MICROPY_TARGET} PRIVATE ) if (MICROPY_PY_NETWORK_CYW43) - string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/cyw43-driver) - if((NOT (${ECHO_SUBMODULES})) AND NOT EXISTS ${MICROPY_DIR}/lib/cyw43-driver/src/cyw43.h) + list(APPEND GIT_SUBMODULES lib/cyw43-driver) + if(NOT UPDATE_SUBMODULES AND NOT EXISTS ${MICROPY_DIR}/lib/cyw43-driver/src/cyw43.h) message(FATAL_ERROR " cyw43-driver not initialized.\n Run 'make BOARD=${MICROPY_BOARD} submodules'") endif() @@ -433,8 +433,8 @@ if (MICROPY_PY_NETWORK_NINAW10) endif() if (MICROPY_PY_NETWORK_WIZNET5K) - string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/wiznet5k) - if((NOT (${ECHO_SUBMODULES})) AND NOT EXISTS ${MICROPY_DIR}/lib/wiznet5k/README.md) + list(APPEND GIT_SUBMODULES lib/wiznet5k) + if(NOT UPDATE_SUBMODULES AND NOT EXISTS ${MICROPY_DIR}/lib/wiznet5k/README.md) message(FATAL_ERROR " wiznet5k not initialized.\n Run 'make BOARD=${MICROPY_BOARD} submodules'") endif() diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile index 200899d338e6a..bfc85f3715e48 100644 --- a/ports/rp2/Makefile +++ b/ports/rp2/Makefile @@ -65,15 +65,11 @@ all: clean: $(RM) -rf $(BUILD) -# First ensure that pico-sdk is initialised, then use cmake to pick everything -# else (including board-specific dependencies). -# Running the build with ECHO_SUBMODULES set will trigger py/mkrules.cmake to -# print out the value of the GIT_SUBMODULES variable, prefixed with -# "GIT_SUBMODULES", and then abort. This extracts out that line from the cmake -# output and passes the list of submodules to py/mkrules.mk which does the -# `git submodule init` on each. +# First ensure that pico-sdk is initialised, then run CMake with the +# UPDATE_SUBMODULES flag to update necessary submodules for this board. +# +# This is done in a dedicated build directory as some CMake cache values are not +# set correctly if not all submodules are loaded yet. submodules: $(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="lib/pico-sdk" submodules - @GIT_SUBMODULES=$$(cmake -B $(BUILD)/submodules -DECHO_SUBMODULES=1 ${CMAKE_ARGS} -S . 2>&1 | \ - grep '^GIT_SUBMODULES=' | cut -d= -f2); \ - $(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="$${GIT_SUBMODULES}" GIT_SUBMODULES_FAIL_IF_EMPTY=1 submodules + cmake -S . -B $(BUILD)/submodules -DUPDATE_SUBMODULES=1 ${CMAKE_ARGS} diff --git a/py/mkrules.cmake b/py/mkrules.cmake index cafcbce5681d8..4374b8b4da3cb 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -209,16 +209,11 @@ if(MICROPY_FROZEN_MANIFEST) # Note: target_compile_definitions already added earlier. if(NOT MICROPY_LIB_DIR) - string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/micropython-lib) + list(APPEND GIT_SUBMODULES lib/micropython-lib) set(MICROPY_LIB_DIR ${MICROPY_DIR}/lib/micropython-lib) endif() - if(ECHO_SUBMODULES) - # No-op, we're just doing submodule/variant discovery. - # Note: All the following rules are safe to run in discovery mode even - # though the submodule might not be available as they do not directly depend - # on anything from the submodule. - elseif(NOT EXISTS ${MICROPY_LIB_DIR}/README.md) + if(NOT UPDATE_SUBMODULES AND NOT EXISTS ${MICROPY_LIB_DIR}/README.md) message(FATAL_ERROR " micropython-lib not initialized.\n Run 'make BOARD=${MICROPY_BOARD} submodules'") endif() @@ -272,12 +267,29 @@ if(MICROPY_FROZEN_MANIFEST) ) endif() -# Update submodules -if(ECHO_SUBMODULES) - # If cmake is run with GIT_SUBMODULES defined on command line, process the port / board - # settings then print the final GIT_SUBMODULES variable and exit. - # Note: the GIT_SUBMODULES is done via echo rather than message, as message splits - # the output onto multiple lines - execute_process(COMMAND ${CMAKE_COMMAND} -E echo "GIT_SUBMODULES=${GIT_SUBMODULES}") - message(FATAL_ERROR "Done") +# Update submodules, this is invoked on some ports via 'make submodules'. +# +# Note: This logic has a Makefile equivalent in py/mkrules.mk +if(UPDATE_SUBMODULES AND GIT_SUBMODULES) + macro(run_git) + execute_process(COMMAND git ${ARGV} WORKING_DIRECTORY ${MICROPY_DIR} + RESULT_VARIABLE RES) + endmacro() + + list(JOIN GIT_SUBMODULES " " GIT_SUBMODULES_MSG) + message("Updating submodules: ${GIT_SUBMODULES_MSG}") + run_git(submodule sync ${GIT_SUBMODULES}) + if(RES EQUAL 0) + # If available, do blobless partial clones of submodules to save time and space. + # A blobless partial clone lazily fetches data as needed, but has all the metadata available (tags, etc.). + run_git(submodule update --init --filter=blob:none ${GIT_SUBMODULES}) + # Fallback to standard submodule update if blobless isn't available (earlier than git 2.36.0) + if (NOT RES EQUAL 0) + run_git(submodule update --init ${GIT_SUBMODULES}) + endif() + endif() + + if (NOT RES EQUAL 0) + message(FATAL_ERROR "Submodule update failed") + endif() endif() diff --git a/py/mkrules.mk b/py/mkrules.mk index ad855c0d7fc45..495d8d48bd2fc 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -262,19 +262,14 @@ endif # If available, do blobless partial clones of submodules to save time and space. # A blobless partial clone lazily fetches data as needed, but has all the metadata available (tags, etc.). # Fallback to standard submodule update if blobless isn't available (earlier than 2.36.0) +# +# Note: This target has a CMake equivalent in py/mkrules.cmake submodules: $(ECHO) "Updating submodules: $(GIT_SUBMODULES)" ifneq ($(GIT_SUBMODULES),) $(Q)cd $(TOP) && git submodule sync $(GIT_SUBMODULES) $(Q)cd $(TOP) && git submodule update --init --filter=blob:none $(GIT_SUBMODULES) || \ git submodule update --init $(GIT_SUBMODULES) -else -ifeq ($(GIT_SUBMODULES_FAIL_IF_EMPTY),1) - # If you see this error, it may mean the internal step run by the port's build - # system to find git submodules has failed. Double-check dependencies are set correctly. - $(ECHO) "Internal build error: The submodule list should not be empty." - exit 1 -endif endif .PHONY: submodules From 50da085d93de55719198fb4fb1272f467e7a16a7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 11 Mar 2025 17:34:24 +1100 Subject: [PATCH 429/546] rp2: Print an error message if pico-sdk submodule is missing. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile index bfc85f3715e48..8cba521b2b201 100644 --- a/ports/rp2/Makefile +++ b/ports/rp2/Makefile @@ -56,9 +56,11 @@ ifdef MICROPY_PREVIEW_VERSION_2 CMAKE_ARGS += -DMICROPY_PREVIEW_VERSION_2=1 endif +HELP_PICO_SDK_SUBMODULE ?= "\033[1;31mError: pico-sdk submodule is not initialized.\033[0m Run 'make submodules'" HELP_BUILD_ERROR ?= "See \033[1;31mhttps://github.com/micropython/micropython/wiki/Build-Troubleshooting\033[0m" all: + [ -f ../../lib/pico-sdk/README.md ] || (echo -e $(HELP_PICO_SDK_SUBMODULE); false) [ -e $(BUILD)/Makefile ] || cmake -S . -B $(BUILD) -DPICO_BUILD_DOCS=0 ${CMAKE_ARGS} $(MAKE) $(MAKE_ARGS) -C $(BUILD) || (echo -e $(HELP_BUILD_ERROR); false) From a828b99cffa97cc04c393a6fdec96bf9e39c07f5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 28 Mar 2025 11:46:39 +1100 Subject: [PATCH 430/546] esp32/Makefile: Use $(Q) prefix on all commands. This prevents printing the lengthy command and makes the build output a little cleaner. Signed-off-by: Damien George --- ports/esp32/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index a4e5531850c7d..7f31ea69192ab 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -70,12 +70,12 @@ endif HELP_BUILD_ERROR ?= "See \033[1;31mhttps://github.com/micropython/micropython/wiki/Build-Troubleshooting\033[0m" define RUN_IDF_PY - idf.py $(IDFPY_FLAGS) -B $(BUILD) $(1) + $(Q)idf.py $(IDFPY_FLAGS) -B $(BUILD) $(1) endef all: - idf.py $(IDFPY_FLAGS) -B $(BUILD) build || (echo -e $(HELP_BUILD_ERROR); false) - @$(PYTHON) makeimg.py \ + $(Q)idf.py $(IDFPY_FLAGS) -B $(BUILD) build || (echo -e $(HELP_BUILD_ERROR); false) + $(Q)$(PYTHON) makeimg.py \ $(BUILD)/sdkconfig \ $(BUILD)/bootloader/bootloader.bin \ $(BUILD)/partition_table/partition-table.bin \ @@ -112,4 +112,4 @@ size-files: # This is done in a dedicated build directory as some CMake cache values are not # set correctly if not all submodules are loaded yet. submodules: - IDF_COMPONENT_MANAGER=0 idf.py $(IDFPY_FLAGS) -B $(BUILD)/submodules -D UPDATE_SUBMODULES=1 reconfigure + $(Q)IDF_COMPONENT_MANAGER=0 idf.py $(IDFPY_FLAGS) -B $(BUILD)/submodules -D UPDATE_SUBMODULES=1 reconfigure From 5eee5a67dc69f6b701e9a79b6fd1e047dfbd55a9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 28 Mar 2025 11:47:55 +1100 Subject: [PATCH 431/546] rp2/Makefile: Use $(Q) prefix on all commands. This prevents printing the lengthy command and makes the build output a little cleaner. Signed-off-by: Damien George --- ports/rp2/Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile index 8cba521b2b201..76b5698d2c8ec 100644 --- a/ports/rp2/Makefile +++ b/ports/rp2/Makefile @@ -60,9 +60,9 @@ HELP_PICO_SDK_SUBMODULE ?= "\033[1;31mError: pico-sdk submodule is not initializ HELP_BUILD_ERROR ?= "See \033[1;31mhttps://github.com/micropython/micropython/wiki/Build-Troubleshooting\033[0m" all: - [ -f ../../lib/pico-sdk/README.md ] || (echo -e $(HELP_PICO_SDK_SUBMODULE); false) - [ -e $(BUILD)/Makefile ] || cmake -S . -B $(BUILD) -DPICO_BUILD_DOCS=0 ${CMAKE_ARGS} - $(MAKE) $(MAKE_ARGS) -C $(BUILD) || (echo -e $(HELP_BUILD_ERROR); false) + $(Q)[ -f ../../lib/pico-sdk/README.md ] || (echo -e $(HELP_PICO_SDK_SUBMODULE); false) + $(Q)[ -e $(BUILD)/Makefile ] || cmake -S . -B $(BUILD) -DPICO_BUILD_DOCS=0 ${CMAKE_ARGS} + $(Q)$(MAKE) $(MAKE_ARGS) -C $(BUILD) || (echo -e $(HELP_BUILD_ERROR); false) clean: $(RM) -rf $(BUILD) @@ -73,5 +73,5 @@ clean: # This is done in a dedicated build directory as some CMake cache values are not # set correctly if not all submodules are loaded yet. submodules: - $(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="lib/pico-sdk" submodules - cmake -S . -B $(BUILD)/submodules -DUPDATE_SUBMODULES=1 ${CMAKE_ARGS} + $(Q)$(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="lib/pico-sdk" submodules + $(Q)cmake -S . -B $(BUILD)/submodules -DUPDATE_SUBMODULES=1 ${CMAKE_ARGS} From f96417dbf28617c533e4f2e65c65d1ed11f089fa Mon Sep 17 00:00:00 2001 From: Mark Seminatore Date: Mon, 31 Mar 2025 11:38:36 -0700 Subject: [PATCH 432/546] rp2/cyw43_configport: Fix cyw43 mDNS by properly starting mDNS on netif. The rp2 port has an incomplete mDNS implementation. The code in `main.c` calls `mdns_resp_init()` which opens the UDP socket for mDNS. However, no code in the cyw43 driver makes the proper calls to `mdns_resp_add_netif()` and `mdns_resp_remove_netif()` to send the announce packets. The wiznet5k driver does make these calls and was used as a model for these changes. This commit attempts to address this by very small changes to the `ports/rp2/cyw43_configport.h` file. The change uses new cyw43 driver hooks to map the driver macros `CYW43_CB_TCPIP_INIT_EXTRA` and `CYW43_CB_TCPIP_DEINIT_EXTRA` to the appropriate lwIP mDNS calls. Fixes issue #15297. Signed-off-by: Mark Seminatore --- ports/rp2/cyw43_configport.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ports/rp2/cyw43_configport.h b/ports/rp2/cyw43_configport.h index ead1a3953d97e..5523305742325 100644 --- a/ports/rp2/cyw43_configport.h +++ b/ports/rp2/cyw43_configport.h @@ -33,6 +33,7 @@ #include "py/mphal.h" #include "py/runtime.h" #include "extmod/modnetwork.h" +#include "lwip/apps/mdns.h" #include "pendsv.h" #define CYW43_INCLUDE_LEGACY_F1_OVERFLOW_WORKAROUND_VARIABLES (1) @@ -167,4 +168,19 @@ static inline void cyw43_delay_ms(uint32_t ms) { #define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait() +#if LWIP_MDNS_RESPONDER == 1 + +// Hook for any additional TCP/IP initialization than needs to be done. +// Called after the netif specified by `itf` has been set up. +#ifndef CYW43_CB_TCPIP_INIT_EXTRA +#define CYW43_CB_TCPIP_INIT_EXTRA(self, itf) mdns_resp_add_netif(&self->netif[itf], mod_network_hostname_data) +#endif + +// Hook for any additional TCP/IP deinitialization than needs to be done. +// Called before the netif specified by `itf` is removed. +#ifndef CYW43_CB_TCPIP_DEINIT_EXTRA +#define CYW43_CB_TCPIP_DEINIT_EXTRA(self, itf) mdns_resp_remove_netif(&self->netif[itf]) +#endif + +#endif #endif // MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H From b33b9f81217bb1521138a8064d1d1bee3eb49e0f Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 24 Jan 2024 09:08:29 +1100 Subject: [PATCH 433/546] stm32/main: Catch and report corrupted lfs filesystem at startup. On stm32, the startup code attempts to mount the configured filesystem. If there is an existing littlefs filesystem that's suitable corrupted it's possible for the reported blocksize to be incorrect here: uint32_t block_size = lfs2_fromle32(superblock->block_size); This `block_size` (which is read from the filesystem iteself) is used to create the len argument passed to `pyb_flash_make_new()`. In that function the len arg is validated to be a mutliple of the underlying hardware block size, as well as not bigger than the physical flash. Any failure is raised as a ValueError. This exception is not caught currently in main, it flows up to the high level assert / startup failure. As this occurs before `boot.py` is run, the users (potentially frozen) application code doesn't have any opportunity to detect and handle the issue. This commit adds a helper function which attempts to create a block device, and on error returns `None` instead of raising an exception. Using this in main means that a potentially corrupt filesystem will simply remain unmounted, and the application can handle the issue safely. The fix here also handles the case where the littlefs filesystem is valid but the autodetection code (which detects the filesystem size) does not work correctly. In that case it will retry mounting the filesystem using the whole size of the block device. Signed-off-by: Andrew Leech --- ports/stm32/main.c | 11 ++++++++-- ports/stm32/storage.c | 48 +++++++++++++++++++++++++------------------ ports/stm32/storage.h | 4 ++++ 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 55dbeed98246f..0f904a8ba958f 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -185,8 +185,15 @@ MP_NOINLINE static bool init_flash_fs(uint reset_mode) { if (len != -1) { // Detected a littlefs filesystem so create correct block device for it - mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR_len), MP_OBJ_NEW_SMALL_INT(len) }; - bdev = MP_OBJ_TYPE_GET_SLOT(&pyb_flash_type, make_new)(&pyb_flash_type, 0, 1, args); + mp_obj_t lfs_bdev = pyb_flash_new_obj(0, len); + if (lfs_bdev == mp_const_none) { + // Invalid len detected, filesystem header block likely corrupted. + // Create bdev with default size to attempt to mount the whole flash or + // let user attempt recovery if desired. + bdev = pyb_flash_new_obj(0, -1); + } else { + bdev = lfs_bdev; + } } #endif diff --git a/ports/stm32/storage.c b/ports/stm32/storage.c index a6594fd4d98c9..d810261fbcedf 100644 --- a/ports/stm32/storage.c +++ b/ports/stm32/storage.c @@ -273,6 +273,29 @@ const pyb_flash_obj_t pyb_flash_obj = { 0, // actual size handled in ioctl, MP_BLOCKDEV_IOCTL_BLOCK_COUNT case }; +mp_obj_t pyb_flash_new_obj(mp_int_t start, mp_int_t len) { + uint32_t bl_len = (storage_get_block_count() - FLASH_PART1_START_BLOCK) * FLASH_BLOCK_SIZE; + + if (start == -1) { + start = 0; + } else if (!(0 <= start && start < bl_len && start % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) { + return mp_const_none; + } + + if (len == -1) { + len = bl_len - start; + } else if (!(0 < len && start + len <= bl_len && len % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) { + return mp_const_none; + } + + pyb_flash_obj_t *self = mp_obj_malloc(pyb_flash_obj_t, &pyb_flash_type); + self->use_native_block_size = false; + self->start = start; + self->len = len; + + return MP_OBJ_FROM_PTR(self); +} + static void pyb_flash_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { pyb_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self == &pyb_flash_obj) { @@ -296,30 +319,15 @@ static mp_obj_t pyb_flash_make_new(const mp_obj_type_t *type, size_t n_args, siz // Default singleton object that accesses entire flash, including virtual partition table return MP_OBJ_FROM_PTR(&pyb_flash_obj); } - - pyb_flash_obj_t *self = mp_obj_malloc(pyb_flash_obj_t, &pyb_flash_type); - self->use_native_block_size = false; - - uint32_t bl_len = (storage_get_block_count() - FLASH_PART1_START_BLOCK) * FLASH_BLOCK_SIZE; - mp_int_t start = args[ARG_start].u_int; - if (start == -1) { - start = 0; - } else if (!(0 <= start && start < bl_len && start % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) { - mp_raise_ValueError(NULL); - } - mp_int_t len = args[ARG_len].u_int; - if (len == -1) { - len = bl_len - start; - } else if (!(0 < len && start + len <= bl_len && len % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) { + + mp_obj_t self = pyb_flash_new_obj(start, len); + if (self == mp_const_none) { + // Invalid start or end arg mp_raise_ValueError(NULL); } - - self->start = start; - self->len = len; - - return MP_OBJ_FROM_PTR(self); + return self; } static mp_obj_t pyb_flash_readblocks(size_t n_args, const mp_obj_t *args) { diff --git a/ports/stm32/storage.h b/ports/stm32/storage.h index 05654855aa0e7..accf6c3904383 100644 --- a/ports/stm32/storage.h +++ b/ports/stm32/storage.h @@ -77,4 +77,8 @@ extern const struct _pyb_flash_obj_t pyb_flash_obj; struct _fs_user_mount_t; void pyb_flash_init_vfs(struct _fs_user_mount_t *vfs); +#if !BUILDING_MBOOT +mp_obj_t pyb_flash_new_obj(mp_int_t start, mp_int_t len); +#endif + #endif // MICROPY_INCLUDED_STM32_STORAGE_H From 1a47379dd64ab0dab557092293cc9a9e27b5a418 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 15 Jan 2025 08:39:16 +0100 Subject: [PATCH 434/546] stm32/boards: Add F427 AF CSV file. Signed-off-by: iabdalkader --- ports/stm32/boards/stm32f427_af.csv | 170 ++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 ports/stm32/boards/stm32f427_af.csv diff --git a/ports/stm32/boards/stm32f427_af.csv b/ports/stm32/boards/stm32f427_af.csv new file mode 100644 index 0000000000000..34d5a94cb8fd3 --- /dev/null +++ b/ports/stm32/boards/stm32f427_af.csv @@ -0,0 +1,170 @@ +Port ,Pin ,AF0 ,AF1 ,AF2 ,AF3 ,AF4 ,AF5 ,AF6 ,AF7 ,AF8 ,AF9 ,AF10 ,AF11 ,AF12 ,AF13 ,AF14,AF15 ,ADC + , ,SYS ,TIM1/2 ,TIM3/4/5,TIM8/9/10/11,I2C1/2/3 ,SPI1/2/3/4/5/6 ,SPI2/3/SAI1 ,SPI3/USART1/2/3,USART6/UART4/5/7/8,CAN1/2/TIM12/13/14/LCD,OTG2_HS/OTG1_FS,ETH ,FMC/SDIO/OTG2_FS ,DCMI ,LCD ,SYS , +PortA,PA0 , ,TIM2_CH1/TIM2_ETR,TIM5_CH1,TIM8_ETR , , , ,USART2_CTS ,UART4_TX , , ,ETH_MII_CRS , , , ,EVENTOUT, +PortA,PA1 , ,TIM2_CH2 ,TIM5_CH2, , , , ,USART2_RTS ,UART4_RX , , ,ETH_MII_RX_CLK/ETH_RMII_REF_CLK, , , ,EVENTOUT,ADC123_IN1 +PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3,TIM9_CH1 , , , ,USART2_TX , , , ,ETH_MDIO , , , ,EVENTOUT,ADC123_IN2 +PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4,TIM9_CH2 , , , ,USART2_RX , , ,OTG_HS_ULPI_D0 ,ETH_MII_COL , , , ,EVENTOUT,ADC123_IN3 +PortA,PA4 , , , , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_CK , , , , ,OTG_HS_SOF ,DCMI_HSYNC , ,EVENTOUT,ADC12_IN4 +PortA,PA5 , ,TIM2_CH1/TIM2_ETR, ,TIM8_CH1N , ,SPI1_SCK , , , , ,OTG_HS_ULPI_CK , , , , ,EVENTOUT,ADC12_IN5 +PortA,PA6 , ,TIM1_BKIN ,TIM3_CH1,TIM8_BKIN , ,SPI1_MISO , , , ,TIM13_CH1 , , , ,DCMI_PIXCLK, ,EVENTOUT,ADC12_IN6 +PortA,PA7 , ,TIM1_CH1N ,TIM3_CH2,TIM8_CH1N , ,SPI1_MOSI , , , ,TIM14_CH1 , ,ETH_MII_RX_DV/ETH_RMII_CRS_DV , , , ,EVENTOUT,ADC12_IN7 +PortA,PA8 ,MCO1 ,TIM1_CH1 , , ,I2C3_SCL , , ,USART1_CK , , ,OTG_FS_SOF , , , , ,EVENTOUT, +PortA,PA9 , ,TIM1_CH2 , , ,I2C3_SMBA, , ,USART1_TX , , , , , ,DCMI_D0 , ,EVENTOUT, +PortA,PA10, ,TIM1_CH3 , , , , , ,USART1_RX , , ,OTG_FS_ID , , ,DCMI_D1 , ,EVENTOUT, +PortA,PA11, ,TIM1_CH4 , , , , , ,USART1_CTS , ,CAN1_RX ,OTG_FS_DM , , , , ,EVENTOUT, +PortA,PA12, ,TIM1_ETR , , , , , ,USART1_RTS , ,CAN1_TX ,OTG_FS_DP , , , , ,EVENTOUT, +PortA,PA13,JTMS/SWDIO , , , , , , , , , , , , , , ,EVENTOUT, +PortA,PA14,JTCK/SWCLK , , , , , , , , , , , , , , ,EVENTOUT, +PortA,PA15,JTDI ,TIM2_CH1/TIM2_ETR, , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS , , , , , , , , ,EVENTOUT, +PortB,PB0 , ,TIM1_CH2N ,TIM3_CH3,TIM8_CH2N , , , , , , ,OTG_HS_ULPI_D1 ,ETH_MII_RXD2 , , , ,EVENTOUT,ADC12_IN8 +PortB,PB1 , ,TIM1_CH3N ,TIM3_CH4,TIM8_CH3N , , , , , , ,OTG_HS_ULPI_D2 ,ETH_MII_RXD3 , , , ,EVENTOUT,ADC12_IN9 +PortB,PB2 , , , , , , , , , , , , , , , ,EVENTOUT, +PortB,PB3 ,JTDO/TRACESWO,TIM2_CH2 , , , ,SPI1_SCK ,SPI3_SCK/I2S3_CK , , , , , , , , ,EVENTOUT, +PortB,PB4 ,NJTRST , ,TIM3_CH1, , ,SPI1_MISO ,SPI3_MISO ,I2S3ext_SD , , , , , , , ,EVENTOUT, +PortB,PB5 , , ,TIM3_CH2, ,I2C1_SMBA,SPI1_MOSI ,SPI3_MOSI/I2S3_SD, , ,CAN2_RX ,OTG_HS_ULPI_D7 ,ETH_PPS_OUT ,FMC_SDCKE1 ,DCMI_D10 , ,EVENTOUT, +PortB,PB6 , , ,TIM4_CH1, ,I2C1_SCL , , ,USART1_TX , ,CAN2_TX , , ,FMC_SDNE1 ,DCMI_D5 , ,EVENTOUT, +PortB,PB7 , , ,TIM4_CH2, ,I2C1_SDA , , ,USART1_RX , , , , ,FMC_NL ,DCMI_VSYNC , ,EVENTOUT, +PortB,PB8 , , ,TIM4_CH3,TIM10_CH1 ,I2C1_SCL , , , , ,CAN1_RX , ,ETH_MII_TXD3 ,SDIO_D4 ,DCMI_D6 , ,EVENTOUT, +PortB,PB9 , , ,TIM4_CH4,TIM11_CH1 ,I2C1_SDA ,SPI2_NSS/I2S2_WS , , , ,CAN1_TX , , ,SDIO_D5 ,DCMI_D7 , ,EVENTOUT, +PortB,PB10, ,TIM2_CH3 , , ,I2C2_SCL ,SPI2_SCK/I2S2_CK , ,USART3_TX , , ,OTG_HS_ULPI_D3 ,ETH_MII_RX_ER , , , ,EVENTOUT, +PortB,PB11, ,TIM2_CH4 , , ,I2C2_SDA , , ,USART3_RX , , ,OTG_HS_ULPI_D4 ,ETH_MII_TX_EN/ETH_RMII_TX_EN , , , ,EVENTOUT, +PortB,PB12, ,TIM1_BKIN , , ,I2C2_SMBA,SPI2_NSS/I2S2_WS , ,USART3_CK , ,CAN2_RX ,OTG_HS_ULPI_D5 ,ETH_MII_TXD0/ETH_RMII_TXD0 ,OTG_HS_ID , , ,EVENTOUT, +PortB,PB13, ,TIM1_CH1N , , , ,SPI2_SCK/I2S2_CK , ,USART3_CTS , ,CAN2_TX ,OTG_HS_ULPI_D6 ,ETH_MII_TXD1/ETH_RMII_TXD1 , , , ,EVENTOUT, +PortB,PB14, ,TIM1_CH2N , ,TIM8_CH2N , ,SPI2_MISO ,I2S2ext_SD ,USART3_RTS , ,TIM12_CH1 , , ,OTG_HS_DM , , ,EVENTOUT, +PortB,PB15,RTC_REFIN ,TIM1_CH3N , ,TIM8_CH3N , ,SPI2_MOSI/I2S2_SD, , , ,TIM12_CH2 , , ,OTG_HS_DP , , ,EVENTOUT, +PortC,PC0 , , , , , , , , , , ,OTG_HS_ULPI_STP, ,FMC_SDNWE , , ,EVENTOUT,ADC123_IN10 +PortC,PC1 , , , , , , , , , , , ,ETH_MDC , , , ,EVENTOUT,ADC123_IN11 +PortC,PC2 , , , , , ,SPI2_MISO ,I2S2ext_SD , , , ,OTG_HS_ULPI_DIR,ETH_MII_TXD2 ,FMC_SDNE0 , , ,EVENTOUT,ADC123_IN12 +PortC,PC3 , , , , , ,SPI2_MOSI/I2S2_SD, , , , ,OTG_HS_ULPI_NXT,ETH_MII_TX_CLK ,FMC_SDCKE0 , , ,EVENTOUT,ADC123_IN13 +PortC,PC4 , , , , , , , , , , , ,ETH_MII_RXD0/ETH_RMII_RXD0 , , , ,EVENTOUT,ADC12_IN14 +PortC,PC5 , , , , , , , , , , , ,ETH_MII_RXD1/ETH_RMII_RXD1 , , , ,EVENTOUT,ADC12_IN15 +PortC,PC6 , , ,TIM3_CH1,TIM8_CH1 , ,I2S2_MCK , , ,USART6_TX , , , ,SDIO_D6 ,DCMI_D0 , ,EVENTOUT, +PortC,PC7 , , ,TIM3_CH2,TIM8_CH2 , , ,I2S3_MCK , ,USART6_RX , , , ,SDIO_D7 ,DCMI_D1 , ,EVENTOUT, +PortC,PC8 , , ,TIM3_CH3,TIM8_CH3 , , , , ,USART6_CK , , , ,SDIO_D0 ,DCMI_D2 , ,EVENTOUT, +PortC,PC9 ,MCO2 , ,TIM3_CH4,TIM8_CH4 ,I2C3_SDA ,I2S_CKIN , , , , , , ,SDIO_D1 ,DCMI_D3 , ,EVENTOUT, +PortC,PC10, , , , , , ,SPI3_SCK/I2S3_CK ,USART3_TX ,UART4_TX , , , ,SDIO_D2 ,DCMI_D8 , ,EVENTOUT, +PortC,PC11, , , , , ,I2S3ext_SD ,SPI3_MISO ,USART3_RX ,UART4_RX , , , ,SDIO_D3 ,DCMI_D4 , ,EVENTOUT, +PortC,PC12, , , , , , ,SPI3_MOSI/I2S3_SD,USART3_CK ,UART5_TX , , , ,SDIO_CK ,DCMI_D9 , ,EVENTOUT, +PortC,PC13, , , , , , , , , , , , , , , ,EVENTOUT, +PortC,PC14, , , , , , , , , , , , , , , ,EVENTOUT, +PortC,PC15, , , , , , , , , , , , , , , ,EVENTOUT, +PortD,PD0 , , , , , , , , , ,CAN1_RX , , ,FMC_D2 , , ,EVENTOUT, +PortD,PD1 , , , , , , , , , ,CAN1_TX , , ,FMC_D3 , , ,EVENTOUT, +PortD,PD2 , , ,TIM3_ETR, , , , , ,UART5_RX , , , ,SDIO_CMD ,DCMI_D11 , ,EVENTOUT, +PortD,PD3 , , , , , ,SPI2_SCK/I2S2_CK , ,USART2_CTS , , , , ,FMC_CLK ,DCMI_D5 , ,EVENTOUT, +PortD,PD4 , , , , , , , ,USART2_RTS , , , , ,FMC_NOE , , ,EVENTOUT, +PortD,PD5 , , , , , , , ,USART2_TX , , , , ,FMC_NWE , , ,EVENTOUT, +PortD,PD6 , , , , , ,SPI3_MOSI/I2S3_SD,SAI1_SD_A ,USART2_RX , , , , ,FMC_NWAIT ,DCMI_D10 , ,EVENTOUT, +PortD,PD7 , , , , , , , ,USART2_CK , , , , ,FMC_NE1/FMC_NCE2 , , ,EVENTOUT, +PortD,PD8 , , , , , , , ,USART3_TX , , , , ,FMC_D13 , , ,EVENTOUT, +PortD,PD9 , , , , , , , ,USART3_RX , , , , ,FMC_D14 , , ,EVENTOUT, +PortD,PD10, , , , , , , ,USART3_CK , , , , ,FMC_D15 , , ,EVENTOUT, +PortD,PD11, , , , , , , ,USART3_CTS , , , , ,FMC_A16 , , ,EVENTOUT, +PortD,PD12, , ,TIM4_CH1, , , , ,USART3_RTS , , , , ,FMC_A17 , , ,EVENTOUT, +PortD,PD13, , ,TIM4_CH2, , , , , , , , , ,FMC_A18 , , ,EVENTOUT, +PortD,PD14, , ,TIM4_CH3, , , , , , , , , ,FMC_D0 , , ,EVENTOUT, +PortD,PD15, , ,TIM4_CH4, , , , , , , , , ,FMC_D1 , , ,EVENTOUT, +PortE,PE0 , , ,TIM4_ETR, , , , , ,UART8_RX , , , ,FMC_NBL0 ,DCMI_D2 , ,EVENTOUT, +PortE,PE1 , , , , , , , , ,UART8_TX , , , ,FMC_NBL1 ,DCMI_D3 , ,EVENTOUT, +PortE,PE2 ,TRACECLK , , , , ,SPI4_SCK ,SAI1_MCLK_A , , , , ,ETH_MII_TXD3 ,FMC_A23 , , ,EVENTOUT, +PortE,PE3 ,TRACED0 , , , , , ,SAI1_SD_B , , , , , ,FMC_A19 , , ,EVENTOUT, +PortE,PE4 ,TRACED1 , , , , ,SPI4_NSS ,SAI1_FS_A , , , , , ,FMC_A20 ,DCMI_D4 , ,EVENTOUT, +PortE,PE5 ,TRACED2 , , ,TIM9_CH1 , ,SPI4_MISO ,SAI1_SCK_A , , , , , ,FMC_A21 ,DCMI_D6 , ,EVENTOUT, +PortE,PE6 ,TRACED3 , , ,TIM9_CH2 , ,SPI4_MOSI ,SAI1_SD_A , , , , , ,FMC_A22 ,DCMI_D7 , ,EVENTOUT, +PortE,PE7 , ,TIM1_ETR , , , , , , ,UART7_RX , , , ,FMC_D4 , , ,EVENTOUT, +PortE,PE8 , ,TIM1_CH1N , , , , , , ,UART7_TX , , , ,FMC_D5 , , ,EVENTOUT, +PortE,PE9 , ,TIM1_CH1 , , , , , , , , , , ,FMC_D6 , , ,EVENTOUT, +PortE,PE10, ,TIM1_CH2N , , , , , , , , , , ,FMC_D7 , , ,EVENTOUT, +PortE,PE11, ,TIM1_CH2 , , , ,SPI4_NSS , , , , , , ,FMC_D8 , , ,EVENTOUT, +PortE,PE12, ,TIM1_CH3N , , , ,SPI4_SCK , , , , , , ,FMC_D9 , , ,EVENTOUT, +PortE,PE13, ,TIM1_CH3 , , , ,SPI4_MISO , , , , , , ,FMC_D10 , , ,EVENTOUT, +PortE,PE14, ,TIM1_CH4 , , , ,SPI4_MOSI , , , , , , ,FMC_D11 , , ,EVENTOUT, +PortE,PE15, ,TIM1_BKIN , , , , , , , , , , ,FMC_D12 , , ,EVENTOUT, +PortF,PF0 , , , , ,I2C2_SDA , , , , , , , ,FMC_A0 , , ,EVENTOUT, +PortF,PF1 , , , , ,I2C2_SCL , , , , , , , ,FMC_A1 , , ,EVENTOUT, +PortF,PF2 , , , , ,I2C2_SMBA, , , , , , , ,FMC_A2 , , ,EVENTOUT, +PortF,PF3 , , , , , , , , , , , , ,FMC_A3 , , ,EVENTOUT,ADC3_IN9 +PortF,PF4 , , , , , , , , , , , , ,FMC_A4 , , ,EVENTOUT,ADC3_IN14 +PortF,PF5 , , , , , , , , , , , , ,FMC_A5 , , ,EVENTOUT,ADC3_IN15 +PortF,PF6 , , , ,TIM10_CH1 , ,SPI5_NSS ,SAI1_SD_B , ,UART7_RX , , , ,FMC_NIORD , , ,EVENTOUT,ADC3_IN4 +PortF,PF7 , , , ,TIM11_CH1 , ,SPI5_SCK ,SAI1_MCLK_B , ,UART7_TX , , , ,FMC_NREG , , ,EVENTOUT,ADC3_IN5 +PortF,PF8 , , , , , ,SPI5_MISO ,SAI1_SCK_B , , ,TIM13_CH1 , , ,FMC_NIOWR , , ,EVENTOUT,ADC3_IN6 +PortF,PF9 , , , , , ,SPI5_MOSI ,SAI1_FS_B , , ,TIM14_CH1 , , ,FMC_CD , , ,EVENTOUT,ADC3_IN7 +PortF,PF10, , , , , , , , , , , , ,FMC_INTR ,DCMI_D11 , ,EVENTOUT,ADC3_IN8 +PortF,PF11, , , , , ,SPI5_MOSI , , , , , , ,FMC_SDNRAS ,DCMI_D12 , ,EVENTOUT, +PortF,PF12, , , , , , , , , , , , ,FMC_A6 , , ,EVENTOUT, +PortF,PF13, , , , , , , , , , , , ,FMC_A7 , , ,EVENTOUT, +PortF,PF14, , , , , , , , , , , , ,FMC_A8 , , ,EVENTOUT, +PortF,PF15, , , , , , , , , , , , ,FMC_A9 , , ,EVENTOUT, +PortG,PG0 , , , , , , , , , , , , ,FMC_A10 , , ,EVENTOUT, +PortG,PG1 , , , , , , , , , , , , ,FMC_A11 , , ,EVENTOUT, +PortG,PG2 , , , , , , , , , , , , ,FMC_A12 , , ,EVENTOUT, +PortG,PG3 , , , , , , , , , , , , ,FMC_A13 , , ,EVENTOUT, +PortG,PG4 , , , , , , , , , , , , ,FMC_A14/FMC_BA0 , , ,EVENTOUT, +PortG,PG5 , , , , , , , , , , , , ,FMC_A15/FMC_BA1 , , ,EVENTOUT, +PortG,PG6 , , , , , , , , , , , , ,FMC_INT2 ,DCMI_D12 , ,EVENTOUT, +PortG,PG7 , , , , , , , , ,USART6_CK , , , ,FMC_INT3 ,DCMI_D13 , ,EVENTOUT, +PortG,PG8 , , , , , ,SPI6_NSS , , ,USART6_RTS , , ,ETH_PPS_OUT ,FMC_SDCLK , , ,EVENTOUT, +PortG,PG9 , , , , , , , , ,USART6_RX , , , ,FMC_NE2/FMC_NCE3 ,DCMI_VSYNC , ,EVENTOUT, +PortG,PG10, , , , , , , , , , , , ,FMC_NCE4_1/FMC_NE3,DCMI_D2 , ,EVENTOUT, +PortG,PG11, , , , , , , , , , , ,ETH_MII_TX_EN/ETH_RMII_TX_EN ,FMC_NCE4_2 ,DCMI_D3 , ,EVENTOUT, +PortG,PG12, , , , , ,SPI6_MISO , , ,USART6_RTS , , , ,FMC_NE4 , , ,EVENTOUT, +PortG,PG13, , , , , ,SPI6_SCK , , ,USART6_CTS , , ,ETH_MII_TXD0/ETH_RMII_TXD0 ,FMC_A24 , , ,EVENTOUT, +PortG,PG14, , , , , ,SPI6_MOSI , , ,USART6_TX , , ,ETH_MII_TXD1/ETH_RMII_TXD1 ,FMC_A25 , , ,EVENTOUT, +PortG,PG15, , , , , , , , ,USART6_CTS , , , ,FMC_SDNCAS ,DCMI_D13 , ,EVENTOUT, +PortH,PH0 , , , , , , , , , , , , , , , ,EVENTOUT, +PortH,PH1 , , , , , , , , , , , , , , , ,EVENTOUT, +PortH,PH2 , , , , , , , , , , , ,ETH_MII_CRS ,FMC_SDCKE0 , , ,EVENTOUT, +PortH,PH3 , , , , , , , , , , , ,ETH_MII_COL ,FMC_SDNE0 , , ,EVENTOUT, +PortH,PH4 , , , , ,I2C2_SCL , , , , , ,OTG_HS_ULPI_NXT, , , , ,EVENTOUT, +PortH,PH5 , , , , ,I2C2_SDA ,SPI5_NSS , , , , , , ,FMC_SDNWE , , ,EVENTOUT, +PortH,PH6 , , , , ,I2C2_SMBA,SPI5_SCK , , , ,TIM12_CH1 , , ,FMC_SDNE1 ,DCMI_D8 , , , +PortH,PH7 , , , , ,I2C3_SCL ,SPI5_MISO , , , , , ,ETH_MII_RXD3 ,FMC_SDCKE1 ,DCMI_D9 , , , +PortH,PH8 , , , , ,I2C3_SDA , , , , , , , ,FMC_D16 ,DCMI_HSYNC , ,EVENTOUT, +PortH,PH9 , , , , ,I2C3_SMBA, , , , ,TIM12_CH2 , , ,FMC_D17 ,DCMI_D0 , ,EVENTOUT, +PortH,PH10, , ,TIM5_CH1, , , , , , , , , ,FMC_D18 ,DCMI_D1 , ,EVENTOUT, +PortH,PH11, , ,TIM5_CH2, , , , , , , , , ,FMC_D19 ,DCMI_D2 , ,EVENTOUT, +PortH,PH12, , ,TIM5_CH3, , , , , , , , , ,FMC_D20 ,DCMI_D3 , ,EVENTOUT, +PortH,PH13, , , ,TIM8_CH1N , , , , , ,CAN1_TX , , ,FMC_D21 , , ,EVENTOUT, +PortH,PH14, , , ,TIM8_CH2N , , , , , , , , ,FMC_D22 ,DCMI_D4 , ,EVENTOUT, +PortH,PH15, , , ,TIM8_CH3N , , , , , , , , ,FMC_D23 ,DCMI_D11 , ,EVENTOUT, +PortI,PI0 , , ,TIM5_CH4, , ,SPI2_NSS/I2S2_WS , , , , , , ,FMC_D24 ,DCMI_D13 , ,EVENTOUT, +PortI,PI1 , , , , , ,SPI2_SCK/I2S2_CK , , , , , , ,FMC_D25 ,DCMI_D8 , ,EVENTOUT, +PortI,PI2 , , , ,TIM8_CH4 , ,SPI2_MISO ,I2S2ext_SD , , , , , ,FMC_D26 ,DCMI_D9 , ,EVENTOUT, +PortI,PI3 , , , ,TIM8_ETR , ,SPI2_MOSI/I2S2_SD, , , , , , ,FMC_D27 ,DCMI_D10 , ,EVENTOUT, +PortI,PI4 , , , ,TIM8_BKIN , , , , , , , , ,FMC_NBL2 ,DCMI_D5 , ,EVENTOUT, +PortI,PI5 , , , ,TIM8_CH1 , , , , , , , , ,FMC_NBL3 ,DCMI_VSYNC , ,EVENTOUT, +PortI,PI6 , , , ,TIM8_CH2 , , , , , , , , ,FMC_D28 ,DCMI_D6 , ,EVENTOUT, +PortI,PI7 , , , ,TIM8_CH3 , , , , , , , , ,FMC_D29 ,DCMI_D7 , ,EVENTOUT, +PortI,PI8 , , , , , , , , , , , , , , , ,EVENTOUT, +PortI,PI9 , , , , , , , , , ,CAN1_RX , , ,FMC_D30 , , ,EVENTOUT, +PortI,PI10, , , , , , , , , , , ,ETH_MII_RX_ER ,FMC_D31 , , ,EVENTOUT, +PortI,PI11, , , , , , , , , , ,OTG_HS_ULPI_DIR, , , , ,EVENTOUT, +PortI,PI12, , , , , , , , , , , , , , , ,EVENTOUT, +PortI,PI13, , , , , , , , , , , , , , , ,EVENTOUT, +PortI,PI14, , , , , , , , , , , , , , , ,EVENTOUT, +PortI,PI15, , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ0 , , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ1 , , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ2 , , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ3 , , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ4 , , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ5 , , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ6 , , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ7 , , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ8 , , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ9 , , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ10, , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ11, , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ12, , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ13, , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ14, , , , , , , , , , , , , , , ,EVENTOUT, +PortJ,PJ15, , , , , , , , , , , , , , , ,EVENTOUT, +PortK,PK0 , , , , , , , , , , , , , , , ,EVENTOUT, +PortK,PK1 , , , , , , , , , , , , , , , ,EVENTOUT, +PortK,PK2 , , , , , , , , , , , , , , , ,EVENTOUT, +PortK,PK3 , , , , , , , , , , , , , , , ,EVENTOUT, +PortK,PK4 , , , , , , , , , , , , , , , ,EVENTOUT, +PortK,PK5 , , , , , , , , , , , , , , , ,EVENTOUT, +PortK,PK6 , , , , , , , , , , , , , , , ,EVENTOUT, +PortK,PK7 , , , , , , , , , , , , , , , ,EVENTOUT, From 3b948893d827c39c967b0ca17da3e8159eaef176 Mon Sep 17 00:00:00 2001 From: Herwin Grobben Date: Wed, 5 Feb 2025 11:08:09 +0100 Subject: [PATCH 435/546] stm32/stm32_it: Add handler for timer 20 interrupt. Signed-off-by: Herwin Grobben --- ports/stm32/stm32_it.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index 2e625a5894dd9..4bf509bb9462e 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -775,6 +775,14 @@ void TIM17_FDCAN_IT1_IRQHandler(void) { } #endif +#if defined(STM32G4) +void TIM20_UP_IRQHandler(void) { + IRQ_ENTER(TIM20_UP_IRQn); + timer_irq_handler(20); + IRQ_EXIT(TIM20_UP_IRQn); +} +#endif + #if defined(STM32H7) void TIM15_IRQHandler(void) { IRQ_ENTER(TIM15_IRQn); From 91386b3d56b4ef3bb27af89846c1cc2712f6065f Mon Sep 17 00:00:00 2001 From: Herwin Grobben Date: Wed, 5 Feb 2025 13:58:57 +0100 Subject: [PATCH 436/546] stm32/timer: Use APB2 to calculate timer 20 source frequency. Signed-off-by: Herwin Grobben --- ports/stm32/timer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c index acfdbd84eb2b6..5c333eb942a08 100644 --- a/ports/stm32/timer.c +++ b/ports/stm32/timer.c @@ -264,8 +264,8 @@ uint32_t timer_get_source_freq(uint32_t tim_id) { #else uint32_t source, clk_div; - if (tim_id == 1 || (8 <= tim_id && tim_id <= 11)) { - // TIM{1,8,9,10,11} are on APB2 + if (tim_id == 1 || (8 <= tim_id && tim_id <= 11) || tim_id == 20) { + // TIM{1,8,9,10,11,20} are on APB2 #if defined(STM32F0) || defined(STM32G0) source = HAL_RCC_GetPCLK1Freq(); clk_div = RCC->CFGR & RCC_CFGR_PPRE; From c18e925431c57384a8f28c8699fe46c8b22aaa3a Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Mon, 24 Mar 2025 00:28:43 +1100 Subject: [PATCH 437/546] stm32/timer: Add support for STM32H5 Timer 1. Signed-off-by: Matt Trentini --- ports/stm32/timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c index 5c333eb942a08..9d65b484cd196 100644 --- a/ports/stm32/timer.c +++ b/ports/stm32/timer.c @@ -840,7 +840,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { TIM_ENTRY(1, TIM1_BRK_UP_TRG_COM_IRQn), #elif defined(STM32F4) || defined(STM32F7) TIM_ENTRY(1, TIM1_UP_TIM10_IRQn), - #elif defined(STM32H7) + #elif defined(STM32H7) || defined(STM32H5) TIM_ENTRY(1, TIM1_UP_IRQn), #elif defined(STM32G4) || defined(STM32L4) || defined(STM32WB) TIM_ENTRY(1, TIM1_UP_TIM16_IRQn), From ac1cbef36661625dabc47bc4aa0aa7a0d076dec1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 19 Mar 2025 14:18:04 +1100 Subject: [PATCH 438/546] stm32/qspi: Add qspi_memory_map_exit and restart. Signed-off-by: Damien George --- ports/stm32/qspi.c | 27 ++++++++++++++++++++------- ports/stm32/qspi.h | 2 ++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c index 1311c27d1d46e..7334ece4f2181 100644 --- a/ports/stm32/qspi.c +++ b/ports/stm32/qspi.c @@ -170,6 +170,25 @@ void qspi_memory_map(void) { qspi_mpu_enable_mapped(); } +void qspi_memory_map_exit(void) { + // Prevent access to QSPI memory-mapped region. + qspi_mpu_disable_all(); + + // Abort any ongoing transfer if peripheral is busy + if (QUADSPI->SR & QUADSPI_SR_BUSY) { + QUADSPI->CR |= QUADSPI_CR_ABORT; + while (QUADSPI->CR & QUADSPI_CR_ABORT) { + } + } +} + +// Needed on F7 due to errata 2.4.3: "Memory-mapped read operations may fail when timeout counter is enabled". +// Call this function to disable then re-enable memory-mapped mode, which resets the CS pin to inactive. +void qspi_memory_map_restart(void) { + qspi_memory_map_exit(); + qspi_memory_map(); +} + static int qspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) { (void)self_in; switch (cmd) { @@ -178,13 +197,7 @@ static int qspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) { break; case MP_QSPI_IOCTL_BUS_ACQUIRE: // Disable memory-mapped region during bus access - qspi_mpu_disable_all(); - // Abort any ongoing transfer if peripheral is busy - if (QUADSPI->SR & QUADSPI_SR_BUSY) { - QUADSPI->CR |= QUADSPI_CR_ABORT; - while (QUADSPI->CR & QUADSPI_CR_ABORT) { - } - } + qspi_memory_map_exit(); break; case MP_QSPI_IOCTL_BUS_RELEASE: // Switch to memory-map mode when bus is idle diff --git a/ports/stm32/qspi.h b/ports/stm32/qspi.h index 6fe91168cfdba..a2d6b9f328db2 100644 --- a/ports/stm32/qspi.h +++ b/ports/stm32/qspi.h @@ -35,6 +35,8 @@ extern const mp_qspi_proto_t qspi_proto; void qspi_init(void); void qspi_memory_map(void); +void qspi_memory_map_exit(void); +void qspi_memory_map_restart(void); static inline bool qspi_is_valid_addr(uint32_t addr) { return QSPI_MAP_ADDR <= addr && addr < QSPI_MAP_ADDR_MAX; From 1660faacf6c8a5837a76dfa2b12f90e87347863c Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 19 Mar 2025 22:01:56 +1100 Subject: [PATCH 439/546] stm32/boards/PYBD_SF2: Restart qspi memory-mapped mode during startup. The PYBD boards use an F7xx which has an errata 2.4.3: Memory-mapped read operations may fail when timeout counter is enabled This is unfortunate because it means that once QSPI memory-mapped flash is accessed the QSPI peripheral will leave the CS pin active (low) forever, which increases power consumption of the SPI flash chip (because it's active and waiting for commands). The exact amount of power increase depends on the flash, but the PYBD_SFx increase by about 2.5mA. Previously this increase in power only happened when QSPI flash was needed, eg on PYBD_SF2 when mbedtls or nimble libraries were used. On PYBD_SF6 it's actually never used. But with the introduction of ROMFS which lives in the QSPI flash, the memory is always access on start up to see if the ROMFS contains a valid image (it must read the memory to find out). That means these boards always consume about 2.5mA more after starting up (compared to when ROMFS is disabled). The fix in this commit is to explicitly restart the QSPI memory mapped mode during the start up process. More precisely, the restart is done after querying the ROMFS and just before trying to execute `boot.py`. That's the right location to keep power consumption permanently down if the QSPI is never used (eg ROMFS image doesn't exist). Signed-off-by: Damien George --- ports/stm32/boards/PYBD_SF2/board_init.c | 10 ++++++++++ ports/stm32/boards/PYBD_SF2/mpconfigboard.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/ports/stm32/boards/PYBD_SF2/board_init.c b/ports/stm32/boards/PYBD_SF2/board_init.c index b39da97c4584e..2f277d1cce737 100644 --- a/ports/stm32/boards/PYBD_SF2/board_init.c +++ b/ports/stm32/boards/PYBD_SF2/board_init.c @@ -61,6 +61,16 @@ void board_early_init(void) { #if !BUILDING_MBOOT +#include "boardctrl.h" +#include "qspi.h" + +int board_run_boot_py(boardctrl_state_t *state) { + // Due to errata 2.4.3, restart memory-mapped mode to deactivate the CS line and save power. + qspi_memory_map_restart(); + + return boardctrl_run_boot_py(state); +} + void board_sleep(int value) { mp_spiflash_deepsleep(&spi_bdev.spiflash, value); mp_spiflash_deepsleep(&spi_bdev2.spiflash, value); diff --git a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h index 3ae11125d8b1b..da5ab9e7fa1eb 100644 --- a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h +++ b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h @@ -43,11 +43,14 @@ #define MICROPY_HW_ENABLE_RF_SWITCH (1) #define MICROPY_BOARD_EARLY_INIT board_early_init +#define MICROPY_BOARD_RUN_BOOT_PY board_run_boot_py #define MICROPY_BOARD_ENTER_STOP board_sleep(1); #define MICROPY_BOARD_LEAVE_STOP board_sleep(0); #define MICROPY_BOARD_ENTER_STANDBY board_sleep(1); #define MICROPY_BOARD_SDCARD_POWER mp_hal_pin_high(pyb_pin_EN_3V3); +struct _boardctrl_state_t; void board_early_init(void); +int board_run_boot_py(struct _boardctrl_state_t *state); void board_sleep(int value); // HSE is 25MHz, run SYS at 120MHz From 9ab6906f50fe41b6168ce64ae792e3750fd311f9 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 22 Mar 2025 02:35:32 +0100 Subject: [PATCH 440/546] esp32/esp32_common.cmake: Use native gchelper for RISC-V. This commit changes the gchelper implementation in use for RV32-based targets (ESP32C3, ESP32C6) from the generic one written in C to the one written in assembler that is specific to the CPU in question. The native implementation is already exercised on most CI builds as it is used by the QEMU port to compile and test the RV32 target. Signed-off-by: Alessandro Gatti --- ports/esp32/esp32_common.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index f7b00900a0388..d026a64b0ce21 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -16,7 +16,10 @@ endif() # RISC-V specific inclusions if(CONFIG_IDF_TARGET_ARCH_RISCV) - list(APPEND MICROPY_SOURCE_LIB ${MICROPY_DIR}/shared/runtime/gchelper_generic.c) + list(APPEND MICROPY_SOURCE_LIB + ${MICROPY_DIR}/shared/runtime/gchelper_native.c + ${MICROPY_DIR}/shared/runtime/gchelper_rv32i.s + ) list(APPEND IDF_COMPONENTS riscv) endif() From fda9bf491767603b762926fde35325480b5b14ee Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 22 Mar 2025 02:40:59 +0100 Subject: [PATCH 441/546] esp32/esp32_common.cmake: Clean up RISC-V directives. This commit cleans up a couple of RISC-V specific directives in the build script. Namely, removes the forced inclusion of the "riscv" component and introduces proper mpy-cross flags. The "riscv" component is already included by the ESP-IDF build framework, as certain low-level components would not build otherwise, so there is no need to add it to the required components list. The architecture flag for mpy-cross is now set for RISC-V targets, as it was previously set only for Xtensa targets (and it relied on a string comparison rather than using the appropriate configuration variable). Signed-off-by: Alessandro Gatti --- ports/esp32/esp32_common.cmake | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index d026a64b0ce21..99fcebac32697 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -20,7 +20,6 @@ if(CONFIG_IDF_TARGET_ARCH_RISCV) ${MICROPY_DIR}/shared/runtime/gchelper_native.c ${MICROPY_DIR}/shared/runtime/gchelper_rv32i.s ) - list(APPEND IDF_COMPONENTS riscv) endif() if(NOT DEFINED MICROPY_PY_TINYUSB) @@ -237,8 +236,10 @@ idf_component_register( set(MICROPY_TARGET ${COMPONENT_TARGET}) # Define mpy-cross flags, for use with frozen code. -if(CONFIG_IDF_TARGET_ARCH STREQUAL "xtensa") -set(MICROPY_CROSS_FLAGS -march=xtensawin) +if(CONFIG_IDF_TARGET_ARCH_XTENSA) + set(MICROPY_CROSS_FLAGS -march=xtensawin) +elseif(CONFIG_IDF_TARGET_ARCH_RISCV) + set(MICROPY_CROSS_FLAGS -march=rv32imc) endif() # Set compile options for this port. From 6bb586619dc1b9022ca191016a3c32a6f89f3caa Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 22 Mar 2025 02:45:10 +0100 Subject: [PATCH 442/546] esp32/esp32_common.cmake: Remove obsolete definition. This commit removes a definition used back when ESP-IDF v4 was supported by MicroPython. Those times are now long gone, and so is the need for that particular definition to be set in the first place. Signed-off-by: Alessandro Gatti --- ports/esp32/esp32_common.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 99fcebac32697..dedef6d782cae 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -247,7 +247,6 @@ target_compile_definitions(${MICROPY_TARGET} PUBLIC ${MICROPY_DEF_CORE} ${MICROPY_DEF_BOARD} ${MICROPY_DEF_TINYUSB} - MICROPY_ESP_IDF_4=1 MICROPY_VFS_FAT=1 MICROPY_VFS_LFS2=1 FFCONF_H=\"${MICROPY_OOFATFS_DIR}/ffconf.h\" From 3805e65ed3b7306329bf0305d5b46f08d7619a11 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 29 Mar 2025 00:38:14 +0100 Subject: [PATCH 443/546] tools/mpy_ld.py: Give better error for unsupported ARM absolute relocs. This is a known limitation, so better to give a clear warning than a catch-all AssertionError. Happens for example when trying to use soft-float on ARCH=armv6m Also give more details on the assertion for unknown relocations, such that one can see which symbol it affects etc, to aid in debugging. References issue #14430. Signed-off-by: Jon Nordby --- tools/mpy_ld.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 44a76bdee6bf0..70cab2b894ba5 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -702,9 +702,13 @@ def do_relocation_text(env, text_addr, r): elif env.arch.name == "EM_RISCV": (addr, value) = process_riscv32_relocation(env, text_addr, r) + elif env.arch.name == "EM_ARM" and r_info_type == R_ARM_ABS32: + # happens for soft-float on armv6m + raise ValueError("Absolute relocations not supported on ARM") + else: # Unknown/unsupported relocation - assert 0, r_info_type + assert 0, (r_info_type, s.name, s.entry, env.arch.name) # Write relocation if env.arch.name == "EM_RISCV": From e34412f0f49032fcd3a08317c8f2e01c7bdae24f Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 5 Apr 2025 22:53:25 +1100 Subject: [PATCH 444/546] tools/ci.sh: Manually install picotool for rp2 builds. If picotool is not installed, it's fetched and built when compiling each rp2 board. And the "develop" branch of picotool is used instead of a release. Installing it manually using the "master" branch means the latest released version is used (instead of a possibly unstable development version), and also makes building each rp2 board a little faster. Signed-off-by: Damien George --- tools/ci.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/ci.sh b/tools/ci.sh index c7a6db79d6ce9..948eeeaef2d22 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -22,6 +22,15 @@ function ci_gcc_riscv_setup { riscv64-unknown-elf-gcc --version } +function ci_picotool_setup { + # Manually installing picotool ensures we use a release version, and speeds up the build. + git clone https://github.com/raspberrypi/pico-sdk.git + (cd pico-sdk && git submodule update --init lib/mbedtls) + git clone https://github.com/raspberrypi/picotool.git + (cd picotool && mkdir build && cd build && cmake -DPICO_SDK_PATH=../../pico-sdk .. && make && sudo make install) + picotool version +} + ######################################################################################## # c code formatting @@ -62,6 +71,7 @@ function ci_code_size_setup { gcc --version ci_gcc_arm_setup ci_gcc_riscv_setup + ci_picotool_setup } function ci_code_size_build { @@ -355,6 +365,7 @@ function ci_renesas_ra_board_build { function ci_rp2_setup { ci_gcc_arm_setup + ci_picotool_setup } function ci_rp2_build { From 57f1e60dd05a58b6d78c72af31ee4d706b1c47a1 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 1 Apr 2025 12:09:41 +1100 Subject: [PATCH 445/546] tests/cpydiff: Update CPy diff for assign expr in nested comprehensions. Since 7c1584aef1 MicroPython matches CPython in most cases, aside from nested comprehensions. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/cpydiff/syntax_assign_expr.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/cpydiff/syntax_assign_expr.py b/tests/cpydiff/syntax_assign_expr.py index 58f57ca1fbe02..704c5c3ecab37 100644 --- a/tests/cpydiff/syntax_assign_expr.py +++ b/tests/cpydiff/syntax_assign_expr.py @@ -1,8 +1,8 @@ """ categories: Syntax,Operators -description: MicroPython allows using := to assign to the variable of a comprehension, CPython raises a SyntaxError. -cause: MicroPython is optimised for code size and doesn't check this case. -workaround: Do not rely on this behaviour if writing CPython compatible code. +description: MicroPython allows := to assign to the iteration variable in nested comprehensions, CPython does not. +cause: MicroPython is optimised for code size. Although it is a syntax error to assign to the iteration variable in a standard comprehension (same as CPython), it doesn't check if an inner nested comprehension assigns to the iteration variable of the outer comprehension. +workaround: Do not use := to assign to the iteration variable of a comprehension. """ -print([i := -1 for i in range(4)]) +print([[(j := i) for i in range(2)] for j in range(2)]) From e9a80fc9a0a13a6afe43973c526ea25b7bcdb9e7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 1 Apr 2025 11:53:31 +1100 Subject: [PATCH 446/546] tests/cpydiff: Remove types_str_endswith. MicroPython support for this behaviour was added in eb45d97898a. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tests/cpydiff/types_str_endswith.py | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 tests/cpydiff/types_str_endswith.py diff --git a/tests/cpydiff/types_str_endswith.py b/tests/cpydiff/types_str_endswith.py deleted file mode 100644 index 890c7ba5ef47f..0000000000000 --- a/tests/cpydiff/types_str_endswith.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -categories: Types,str -description: Start/end indices such as str.endswith(s, start) not implemented -cause: Unknown -workaround: Unknown -""" - -print("abc".endswith("c", 1)) From 74a5bf94c187d41d0458814312d57d2105233726 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 1 Apr 2025 11:52:04 +1100 Subject: [PATCH 447/546] tools/gen-cpydiff.py: Fail CPython diff generation if output matches. Previously this information was recorded in a "status" field of the result, but nothing ever parsed this result which led to non-differences not being removed. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tools/gen-cpydiff.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/tools/gen-cpydiff.py b/tools/gen-cpydiff.py index c0b5734389cdf..2f9394deeae00 100644 --- a/tools/gen-cpydiff.py +++ b/tools/gen-cpydiff.py @@ -69,7 +69,6 @@ "code", "output_cpy", "output_upy", - "status", ], ) @@ -98,7 +97,7 @@ def readfiles(): if not re.match(r"\s*# fmt: (on|off)\s*", x) ) - output = Output(test, class_, desc, cause, workaround, code, "", "", "") + output = Output(test, class_, desc, cause, workaround, code, "", "") files.append(output) except IndexError: print("Incorrect format in file " + test_fullpath) @@ -108,6 +107,7 @@ def readfiles(): def run_tests(tests): """executes all tests""" + same_results = False results = [] for test in tests: test_fullpath = os.path.join(TESTPATH, test.name) @@ -133,23 +133,26 @@ def run_tests(tests): output_upy = [com.decode("utf8") for com in process.communicate(input_py)] if output_cpy[0] == output_upy[0] and output_cpy[1] == output_upy[1]: - status = "Supported" - print("Supported operation!\nFile: " + test_fullpath) + print("Error: Test has same output in CPython vs MicroPython: " + test_fullpath) + same_results = True else: - status = "Unsupported" - - output = Output( - test.name, - test.class_, - test.desc, - test.cause, - test.workaround, - test.code, - output_cpy, - output_upy, - status, + output = Output( + test.name, + test.class_, + test.desc, + test.cause, + test.workaround, + test.code, + output_cpy, + output_upy, + ) + results.append(output) + + if same_results: + raise SystemExit( + "Failing due to non-differences in results. If MicroPython behaviour has changed " + "to match CPython, please remove the file(s) mentioned above." ) - results.append(output) results.sort(key=lambda x: x.class_) return results From 9e9be83fd69a36bf82019d1ecfcafa3b317ae899 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 29 Mar 2025 10:11:38 +0100 Subject: [PATCH 448/546] tools/mpremote: Allow .img for ROMFS file and validate ROMFS image. Currently the tool allows writing an invalid ROMFS image, with a bad header or images smaller than minimum size, and only checks the image extension. This commit allows deploying a ROMFS with either a ".img" or ".romfs" extension (in the future support may be added for other extensions that have different semantics, eg a manifest), and validates the image header before writing. Signed-off-by: iabdalkader --- tools/mpremote/mpremote/commands.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/mpremote/mpremote/commands.py b/tools/mpremote/mpremote/commands.py index 7dd448c8b771c..e385d050927f2 100644 --- a/tools/mpremote/mpremote/commands.py +++ b/tools/mpremote/mpremote/commands.py @@ -9,7 +9,7 @@ from .transport import TransportError, TransportExecError, stdout_write_bytes from .transport_serial import SerialTransport -from .romfs import make_romfs +from .romfs import make_romfs, VfsRomWriter class CommandError(Exception): @@ -555,7 +555,7 @@ def _do_romfs_deploy(state, args): romfs_filename = args.path # Read in or create the ROMFS filesystem image. - if romfs_filename.endswith(".romfs"): + if os.path.isfile(romfs_filename) and romfs_filename.endswith((".img", ".romfs")): with open(romfs_filename, "rb") as f: romfs = f.read() else: @@ -581,6 +581,11 @@ def _do_romfs_deploy(state, args): rom_size = transport.eval("len(dev)") print(f"ROMFS{rom_id} partition has size {rom_size} bytes") + # Check if ROMFS image is valid + if not romfs.startswith(VfsRomWriter.ROMFS_HEADER): + print("Invalid ROMFS image") + sys.exit(1) + # Check if ROMFS filesystem image will fit in the target partition. if len(romfs) > rom_size: print("ROMFS image is too big for the target partition") From 11f057dd9a5801e23b39822b3a57cbfa8cd10f00 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 6 Aug 2024 19:49:40 +0100 Subject: [PATCH 449/546] rp2: Add support for PSRAM with auto-detection. Performs a best-effort attempt to detect attached PSRAM, configure it and *add* it to the MicroPython heap. If PSRAM is not present, should fall back to use internal RAM. Introduce two new port/board defines: - MICROPY_HW_ENABLE_PSRAM to enable PSRAM. - MICROPY_HW_PSRAM_CS_PIN to define the chip-select pin (required). Changes are: - ports/rp2/rp2_psram.[ch]: Add new PSRAM module. - ports/rp2/main.c: Add optional PSRAM support. - ports/rp2/CMakeLists.txt: Include rp2_psram.c. - ports/rp2/mpconfigport.h: Add MICROPY_HW_ENABLE_PSRAM. - ports/rp2/modmachine.c: Reconfigure PSRAM on freq change. Co-authored-by: Kirk Benell Co-authored-by: Mike Bell Signed-off-by: Phil Howard --- ports/rp2/CMakeLists.txt | 1 + ports/rp2/main.c | 19 ++++ ports/rp2/modmachine.c | 4 + ports/rp2/mpconfigport.h | 4 + ports/rp2/rp2_psram.c | 198 +++++++++++++++++++++++++++++++++++++++ ports/rp2/rp2_psram.h | 44 +++++++++ 6 files changed, 270 insertions(+) create mode 100644 ports/rp2/rp2_psram.c create mode 100644 ports/rp2/rp2_psram.h diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index f89e2792c6479..1a5029c150417 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -165,6 +165,7 @@ set(MICROPY_SOURCE_PORT pendsv.c rp2_flash.c rp2_pio.c + rp2_psram.c rp2_dma.c uart.c usbd.c diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 58da63c06f12d..764af6b7466cf 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -46,6 +46,7 @@ #include "mpnetworkport.h" #include "genhdr/mpversion.h" #include "mp_usbd.h" +#include "rp2_psram.h" #include "pico/stdlib.h" #include "pico/binary_info.h" @@ -93,6 +94,10 @@ int main(int argc, char **argv) { // Hook for setting up anything that needs to be super early in the boot-up process. MICROPY_BOARD_STARTUP(); + #if MICROPY_HW_ENABLE_PSRAM + size_t psram_size = psram_init(MICROPY_HW_PSRAM_CS_PIN); + #endif + #if MICROPY_HW_ENABLE_UART_REPL bi_decl(bi_program_feature("UART REPL")) setup_default_uart(); @@ -120,7 +125,21 @@ int main(int argc, char **argv) { // Initialise stack extents and GC heap. mp_cstack_init_with_top(&__StackTop, &__StackTop - &__StackBottom); + + #if MICROPY_HW_ENABLE_PSRAM + if (psram_size) { + #if MICROPY_GC_SPLIT_HEAP + gc_init(&__GcHeapStart, &__GcHeapEnd); + gc_add((void *)PSRAM_BASE, (void *)(PSRAM_BASE + psram_size)); + #else + gc_init((void *)PSRAM_BASE, (void *)(PSRAM_BASE + psram_size)); + #endif + } else { + gc_init(&__GcHeapStart, &__GcHeapEnd); + } + #else gc_init(&__GcHeapStart, &__GcHeapEnd); + #endif #if MICROPY_PY_LWIP // lwIP doesn't allow to reinitialise itself by subsequent calls to this function diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 31665a7640d9c..bf4e43a613c90 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -31,6 +31,7 @@ #include "mp_usbd.h" #include "modmachine.h" #include "uart.h" +#include "rp2_psram.h" #include "clocks_extra.h" #include "hardware/pll.h" #include "hardware/structs/rosc.h" @@ -115,6 +116,9 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { setup_default_uart(); mp_uart_init(); #endif + #if MICROPY_HW_ENABLE_PSRAM + psram_init(MICROPY_HW_PSRAM_CS_PIN); + #endif } static void mp_machine_idle(void) { diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 38dcad1dae6e1..28022ba420942 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -81,6 +81,10 @@ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) #endif +#ifndef MICROPY_HW_ENABLE_PSRAM +#define MICROPY_HW_ENABLE_PSRAM (0) +#endif + // Memory allocation policies #define MICROPY_GC_STACK_ENTRY_TYPE uint16_t #define MICROPY_ALLOC_PATH_MAX (128) diff --git a/ports/rp2/rp2_psram.c b/ports/rp2/rp2_psram.c new file mode 100644 index 0000000000000..bb063f4af7a06 --- /dev/null +++ b/ports/rp2/rp2_psram.c @@ -0,0 +1,198 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Phil Howard + * Mike Bell + * Kirk D. Benell + * + * 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" + +#if MICROPY_HW_ENABLE_PSRAM + +#include "hardware/structs/ioqspi.h" +#include "hardware/structs/qmi.h" +#include "hardware/structs/xip_ctrl.h" +#include "hardware/clocks.h" +#include "hardware/sync.h" +#include "rp2_psram.h" + +size_t __no_inline_not_in_flash_func(psram_detect)(void) { + int psram_size = 0; + + // Try and read the PSRAM ID via direct_csr. + qmi_hw->direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | QMI_DIRECT_CSR_EN_BITS; + + // Need to poll for the cooldown on the last XIP transfer to expire + // (via direct-mode BUSY flag) before it is safe to perform the first + // direct-mode operation + while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) { + } + + // Exit out of QMI in case we've inited already + qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS; + + // Transmit as quad. + qmi_hw->direct_tx = QMI_DIRECT_TX_OE_BITS | QMI_DIRECT_TX_IWIDTH_VALUE_Q << QMI_DIRECT_TX_IWIDTH_LSB | 0xf5; + + while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) { + } + + (void)qmi_hw->direct_rx; + + qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS); + + // Read the id + qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS; + uint8_t kgd = 0; + uint8_t eid = 0; + + for (size_t i = 0; i < 7; i++) { + if (i == 0) { + qmi_hw->direct_tx = 0x9f; + } else { + qmi_hw->direct_tx = 0xff; + } + + while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_TXEMPTY_BITS) == 0) { + } + + while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) { + } + + if (i == 5) { + kgd = qmi_hw->direct_rx; + } else if (i == 6) { + eid = qmi_hw->direct_rx; + } else { + (void)qmi_hw->direct_rx; + } + } + + // Disable direct csr. + qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS); + + if (kgd == 0x5D) { + psram_size = 1024 * 1024; // 1 MiB + uint8_t size_id = eid >> 5; + if (eid == 0x26 || size_id == 2) { + psram_size *= 8; // 8 MiB + } else if (size_id == 0) { + psram_size *= 2; // 2 MiB + } else if (size_id == 1) { + psram_size *= 4; // 4 MiB + } + } + + return psram_size; +} + +size_t __no_inline_not_in_flash_func(psram_init)(uint cs_pin) { + gpio_set_function(cs_pin, GPIO_FUNC_XIP_CS1); + + uint32_t intr_stash = save_and_disable_interrupts(); + + size_t psram_size = psram_detect(); + + if (!psram_size) { + restore_interrupts(intr_stash); + return 0; + } + + // Enable direct mode, PSRAM CS, clkdiv of 10. + qmi_hw->direct_csr = 10 << QMI_DIRECT_CSR_CLKDIV_LSB | \ + QMI_DIRECT_CSR_EN_BITS | \ + QMI_DIRECT_CSR_AUTO_CS1N_BITS; + while (qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) { + } + + // Enable QPI mode on the PSRAM + const uint CMD_QPI_EN = 0x35; + qmi_hw->direct_tx = QMI_DIRECT_TX_NOPUSH_BITS | CMD_QPI_EN; + + while (qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) { + } + + // Set PSRAM timing for APS6404 + // + // Using an rxdelay equal to the divisor isn't enough when running the APS6404 close to 133MHz. + // So: don't allow running at divisor 1 above 100MHz (because delay of 2 would be too late), + // and add an extra 1 to the rxdelay if the divided clock is > 100MHz (i.e. sys clock > 200MHz). + const int max_psram_freq = 133000000; + const int clock_hz = clock_get_hz(clk_sys); + int divisor = (clock_hz + max_psram_freq - 1) / max_psram_freq; + if (divisor == 1 && clock_hz > 100000000) { + divisor = 2; + } + int rxdelay = divisor; + if (clock_hz / divisor > 100000000) { + rxdelay += 1; + } + + // - Max select must be <= 8us. The value is given in multiples of 64 system clocks. + // - Min deselect must be >= 18ns. The value is given in system clock cycles - ceil(divisor / 2). + const int clock_period_fs = 1000000000000000ll / clock_hz; + const int max_select = (125 * 1000000) / clock_period_fs; // 125 = 8000ns / 64 + const int min_deselect = (18 * 1000000 + (clock_period_fs - 1)) / clock_period_fs - (divisor + 1) / 2; + + qmi_hw->m[1].timing = 1 << QMI_M1_TIMING_COOLDOWN_LSB | + QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB | + max_select << QMI_M1_TIMING_MAX_SELECT_LSB | + min_deselect << QMI_M1_TIMING_MIN_DESELECT_LSB | + rxdelay << QMI_M1_TIMING_RXDELAY_LSB | + divisor << QMI_M1_TIMING_CLKDIV_LSB; + + // Set PSRAM commands and formats + qmi_hw->m[1].rfmt = + QMI_M0_RFMT_PREFIX_WIDTH_VALUE_Q << QMI_M0_RFMT_PREFIX_WIDTH_LSB | \ + QMI_M0_RFMT_ADDR_WIDTH_VALUE_Q << QMI_M0_RFMT_ADDR_WIDTH_LSB | \ + QMI_M0_RFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M0_RFMT_SUFFIX_WIDTH_LSB | \ + QMI_M0_RFMT_DUMMY_WIDTH_VALUE_Q << QMI_M0_RFMT_DUMMY_WIDTH_LSB | \ + QMI_M0_RFMT_DATA_WIDTH_VALUE_Q << QMI_M0_RFMT_DATA_WIDTH_LSB | \ + QMI_M0_RFMT_PREFIX_LEN_VALUE_8 << QMI_M0_RFMT_PREFIX_LEN_LSB | \ + 6 << QMI_M0_RFMT_DUMMY_LEN_LSB; + + qmi_hw->m[1].rcmd = 0xEB; + + qmi_hw->m[1].wfmt = + QMI_M0_WFMT_PREFIX_WIDTH_VALUE_Q << QMI_M0_WFMT_PREFIX_WIDTH_LSB | \ + QMI_M0_WFMT_ADDR_WIDTH_VALUE_Q << QMI_M0_WFMT_ADDR_WIDTH_LSB | \ + QMI_M0_WFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M0_WFMT_SUFFIX_WIDTH_LSB | \ + QMI_M0_WFMT_DUMMY_WIDTH_VALUE_Q << QMI_M0_WFMT_DUMMY_WIDTH_LSB | \ + QMI_M0_WFMT_DATA_WIDTH_VALUE_Q << QMI_M0_WFMT_DATA_WIDTH_LSB | \ + QMI_M0_WFMT_PREFIX_LEN_VALUE_8 << QMI_M0_WFMT_PREFIX_LEN_LSB; + + qmi_hw->m[1].wcmd = 0x38; + + // Disable direct mode + qmi_hw->direct_csr = 0; + + // Enable writes to PSRAM + hw_set_bits(&xip_ctrl_hw->ctrl, XIP_CTRL_WRITABLE_M1_BITS); + + restore_interrupts(intr_stash); + + return psram_size; +} + +#endif diff --git a/ports/rp2/rp2_psram.h b/ports/rp2/rp2_psram.h new file mode 100644 index 0000000000000..0eddf6341592b --- /dev/null +++ b/ports/rp2/rp2_psram.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Phil Howard + * Mike Bell + * Kirk D. Benell + * + * 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 "pico/stdlib.h" + +#ifndef MICROPY_INCLUDED_RP2_RP2_PSRAM_H +#define MICROPY_INCLUDED_RP2_RP2_PSRAM_H + +#if MICROPY_HW_ENABLE_PSRAM +#ifndef MICROPY_HW_PSRAM_CS_PIN +#error "MICROPY_HW_ENABLE_PSRAM requires MICROPY_HW_PSRAM_CS_PIN" +#endif + +#define PSRAM_BASE _u(0x11000000) + +extern size_t psram_init(uint cs_pin); +#endif + +#endif From b7d5caf2a3e08fa90bbb09b43b29455e2c20eaf6 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 9 Aug 2024 14:15:33 +0100 Subject: [PATCH 450/546] rp2/mpconfigport: Configure heap for PSRAM. PSRAM will be used exclusively if MICROPY_GC_SPLIT_HEAP == 0, it will be added to RAM if MICROPY_GC_SPLIT_HEAP == 1, and the system will fall back to RAM only if it's not detected. Due to the size of PSRAM, GC stack was overflowing and causing the GC to scan through the entire memory pool. This caused noticable slowdowns during GC. Increase the stack from 256 to 4096 bytes to avoid overflow and increase the stack entry type size to accomodate 8MB+ PSRAM. Changes are: - ports/rp2/mpconfigport.h: Make split-heap optional and enable by default. - ports/rp2/mpconfigport.h: Increase GC stack entry type to uint32_t. - ports/rp2/mpconfigport.h: Raise GC stack size. Co-authored-by: Kirk Benell Signed-off-by: Phil Howard --- ports/rp2/mpconfigport.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 28022ba420942..3d65737266b49 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -86,7 +86,15 @@ #endif // Memory allocation policies +#if MICROPY_HW_ENABLE_PSRAM +#define MICROPY_GC_STACK_ENTRY_TYPE uint32_t +#define MICROPY_ALLOC_GC_STACK_SIZE (1024) // Avoid slowdown when GC stack overflow causes a full sweep of PSRAM-backed heap +#else #define MICROPY_GC_STACK_ENTRY_TYPE uint16_t +#endif +#ifndef MICROPY_GC_SPLIT_HEAP +#define MICROPY_GC_SPLIT_HEAP MICROPY_HW_ENABLE_PSRAM // whether PSRAM is added to or replaces the heap +#endif #define MICROPY_ALLOC_PATH_MAX (128) #define MICROPY_QSTR_BYTES_IN_HASH (1) From 89eea0f5e8d7fe2a1c0ca7c3bc1578b417d8fb8c Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Sun, 11 Aug 2024 21:39:05 +0100 Subject: [PATCH 451/546] rp2/rp2_flash: Support flash writes from PSRAM. Add a 256 byte (FLASH_PAGE_SIZE) SRAM copy buffer to allow copies from PSRAM to flash. This would otherwise hardfault since PSRAM is disabled when doing a write to flash. Changes are: - ports/rp2/rp2_flash.c: Add 256 byte (flash page size) SRAM copy buffer for PSRAM to flash copies. - ports/rp2/rp2_flash.c: Invalidate the XIP cache to purge any PSRAM data before critical flash operations. Co-authored-by: Phil Howard Co-authored-by: Angus Gratton Signed-off-by: Phil Howard --- ports/rp2/rp2_flash.c | 64 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c index f3f154a1402d9..8739f17a4a548 100644 --- a/ports/rp2/rp2_flash.c +++ b/ports/rp2/rp2_flash.c @@ -33,9 +33,13 @@ #include "modrp2.h" #include "hardware/flash.h" #include "pico/binary_info.h" +#include "rp2_psram.h" #define BLOCK_SIZE_BYTES (FLASH_SECTOR_SIZE) +// Size of buffer for flash writes from PSRAM, since they are mutually exclusive +#define COPY_BUFFER_SIZE_BYTES (FLASH_PAGE_SIZE) + static_assert(MICROPY_HW_ROMFS_BYTES % 4096 == 0, "ROMFS size must be a multiple of 4K"); static_assert(MICROPY_HW_FLASH_STORAGE_BYTES % 4096 == 0, "Flash storage size must be a multiple of 4K"); @@ -96,10 +100,27 @@ static uint32_t begin_critical_flash_section(void) { if (use_multicore_lockout()) { multicore_lockout_start_blocking(); } - return save_and_disable_interrupts(); + uint32_t state = save_and_disable_interrupts(); + + #if MICROPY_HW_ENABLE_PSRAM + // We're about to invalidate the XIP cache, clean it first to commit any dirty writes to PSRAM + // Use the upper 16k of the maintenance space (0x1bffc000 through 0x1bffffff) to workaround + // incorrect behaviour of the XIP clean operation, where it also alters the tag of the associated + // cache line: https://forums.raspberrypi.com/viewtopic.php?t=378249#p2263677 + volatile uint8_t *maintenance_ptr = (volatile uint8_t *)(XIP_SRAM_BASE + (XIP_MAINTENANCE_BASE - XIP_BASE)); + for (int i = 1; i < 16 * 1024; i += 8) { + maintenance_ptr[i] = 0; + } + #endif + + return state; } static void end_critical_flash_section(uint32_t state) { + #if MICROPY_HW_ENABLE_PSRAM + // The ROM function to program flash will reset PSRAM timings to defaults + psram_init(MICROPY_HW_PSRAM_CS_PIN); + #endif restore_interrupts(state); if (use_multicore_lockout()) { multicore_lockout_end_blocking(); @@ -192,10 +213,43 @@ static mp_obj_t rp2_flash_writeblocks(size_t n_args, const mp_obj_t *args) { } else { offset += mp_obj_get_int(args[3]); } - mp_uint_t atomic_state = begin_critical_flash_section(); - flash_range_program(self->flash_base + offset, bufinfo.buf, bufinfo.len); - end_critical_flash_section(atomic_state); - mp_event_handle_nowait(); + + // If copying from SRAM, can write direct to flash. + // If copying from PSRAM/flash, use an SRAM buffer and write in chunks. + #if MICROPY_HW_ENABLE_PSRAM + bool write_direct = (uintptr_t)bufinfo.buf >= SRAM_BASE; + #else + bool write_direct = true; + #endif + + if (write_direct) { + // If copying from SRAM, write direct + mp_uint_t atomic_state = begin_critical_flash_section(); + flash_range_program(self->flash_base + offset, bufinfo.buf, bufinfo.len); + end_critical_flash_section(atomic_state); + mp_event_handle_nowait(); + } + #if MICROPY_HW_ENABLE_PSRAM + else { + size_t bytes_left = bufinfo.len; + size_t bytes_offset = 0; + static uint8_t copy_buffer[COPY_BUFFER_SIZE_BYTES] = {0}; + + while (bytes_left) { + memcpy(copy_buffer, bufinfo.buf + bytes_offset, MIN(bytes_left, COPY_BUFFER_SIZE_BYTES)); + mp_uint_t atomic_state = begin_critical_flash_section(); + flash_range_program(self->flash_base + offset + bytes_offset, copy_buffer, MIN(bytes_left, COPY_BUFFER_SIZE_BYTES)); + end_critical_flash_section(atomic_state); + bytes_offset += COPY_BUFFER_SIZE_BYTES; + if (bytes_left <= COPY_BUFFER_SIZE_BYTES) { + break; + } + bytes_left -= COPY_BUFFER_SIZE_BYTES; + mp_event_handle_nowait(); + } + } + #endif + // TODO check return value return mp_const_none; } From 91cff8e4f10ea665c5f3f4b16d62c98d6ca22037 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Tue, 25 Mar 2025 11:32:06 +0000 Subject: [PATCH 452/546] rp2/rp2_flash: Configure optimal flash timings. Configure flash timings dynamically to match the system clock. Reconfigure timings after flash writes. Changes are: - ports/rp2/main.c: Set default flash timings. - ports/rp2/modmachine.c: Configure optimal flash timings on freq change. - ports/rp2/rp2_flash.c: Reconfigure flash when leaving critical section. Signed-off-by: Phil Howard --- ports/rp2/main.c | 4 +++ ports/rp2/modmachine.c | 12 +++++++ ports/rp2/rp2_flash.c | 71 +++++++++++++++++++++++++++++++++++++++++- ports/rp2/rp2_flash.h | 34 ++++++++++++++++++++ 4 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 ports/rp2/rp2_flash.h diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 764af6b7466cf..0f10f63c6d296 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -26,6 +26,7 @@ #include +#include "rp2_flash.h" #include "py/compile.h" #include "py/cstack.h" #include "py/runtime.h" @@ -94,6 +95,9 @@ int main(int argc, char **argv) { // Hook for setting up anything that needs to be super early in the boot-up process. MICROPY_BOARD_STARTUP(); + // Set the flash divisor to an appropriate value + rp2_flash_set_timing(); + #if MICROPY_HW_ENABLE_PSRAM size_t psram_size = psram_init(MICROPY_HW_PSRAM_CS_PIN); #endif diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index bf4e43a613c90..58a3a8ae4d87d 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -32,6 +32,7 @@ #include "modmachine.h" #include "uart.h" #include "rp2_psram.h" +#include "rp2_flash.h" #include "clocks_extra.h" #include "hardware/pll.h" #include "hardware/structs/rosc.h" @@ -95,6 +96,11 @@ static mp_obj_t mp_machine_get_freq(void) { static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { mp_int_t freq = mp_obj_get_int(args[0]); + + // If necessary, increase the flash divider before increasing the clock speed + const int old_freq = clock_get_hz(clk_sys); + rp2_flash_set_timing_for_freq(MAX(freq, old_freq)); + if (!set_sys_clock_khz(freq / 1000, false)) { mp_raise_ValueError(MP_ERROR_TEXT("cannot change frequency")); } @@ -112,6 +118,12 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { } } } + + // If clock speed was reduced, maybe we can reduce the flash divider + if (freq < old_freq) { + rp2_flash_set_timing_for_freq(freq); + } + #if MICROPY_HW_ENABLE_UART_REPL setup_default_uart(); mp_uart_init(); diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c index 8739f17a4a548..a9beabf051c2d 100644 --- a/ports/rp2/rp2_flash.c +++ b/ports/rp2/rp2_flash.c @@ -34,6 +34,12 @@ #include "hardware/flash.h" #include "pico/binary_info.h" #include "rp2_psram.h" +#ifdef PICO_RP2350 +#include "hardware/structs/ioqspi.h" +#include "hardware/structs/qmi.h" +#else +#include "hardware/structs/ssi.h" +#endif #define BLOCK_SIZE_BYTES (FLASH_SECTOR_SIZE) @@ -94,6 +100,48 @@ static bool use_multicore_lockout(void) { ; } +// Function to set the flash divisor to the correct divisor, assumes interrupts disabled +// and core1 locked out if relevant. +static void __no_inline_not_in_flash_func(rp2_flash_set_timing_internal)(int clock_hz) { + + // Use the minimum divisor assuming a 133MHz flash. + const int max_flash_freq = 133000000; + int divisor = (clock_hz + max_flash_freq - 1) / max_flash_freq; + + #if PICO_RP2350 + // Make sure flash is deselected - QMI doesn't appear to have a busy flag(!) + while ((ioqspi_hw->io[1].status & IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) != IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) { + ; + } + + // RX delay equal to the divisor means sampling at the same time as the next falling edge of SCK after the + // falling edge that generated the data. This is pretty tight at 133MHz but seems to work with the Winbond flash chips. + const int rxdelay = divisor; + qmi_hw->m[0].timing = (1 << QMI_M0_TIMING_COOLDOWN_LSB) | + rxdelay << QMI_M1_TIMING_RXDELAY_LSB | + divisor << QMI_M1_TIMING_CLKDIV_LSB; + + // Force a read through XIP to ensure the timing is applied + volatile uint32_t *ptr = (volatile uint32_t *)0x14000000; + (void)*ptr; + #else + // RP2040 SSI hardware only supports even divisors + if (divisor & 1) { + divisor += 1; + } + + // Wait for SSI not busy + while (ssi_hw->sr & SSI_SR_BUSY_BITS) { + ; + } + + // Disable, set the new divisor, and re-enable + hw_clear_bits(&ssi_hw->ssienr, SSI_SSIENR_SSI_EN_BITS); + ssi_hw->baudr = divisor; + hw_set_bits(&ssi_hw->ssienr, SSI_SSIENR_SSI_EN_BITS); + #endif +} + // Flash erase and write must run with interrupts disabled and the other core suspended, // because the XIP bit gets disabled. static uint32_t begin_critical_flash_section(void) { @@ -117,8 +165,9 @@ static uint32_t begin_critical_flash_section(void) { } static void end_critical_flash_section(uint32_t state) { + // The ROM function to program flash will have reset flash and PSRAM timings to defaults + rp2_flash_set_timing_internal(clock_get_hz(clk_sys)); #if MICROPY_HW_ENABLE_PSRAM - // The ROM function to program flash will reset PSRAM timings to defaults psram_init(MICROPY_HW_PSRAM_CS_PIN); #endif restore_interrupts(state); @@ -313,3 +362,23 @@ mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { } } #endif + +// Modify the flash timing. Ensure flash access is suspended while +// the timings are altered. +void rp2_flash_set_timing_for_freq(int clock_hz) { + if (multicore_lockout_victim_is_initialized(1 - get_core_num())) { + multicore_lockout_start_blocking(); + } + uint32_t state = save_and_disable_interrupts(); + + rp2_flash_set_timing_internal(clock_hz); + + restore_interrupts(state); + if (multicore_lockout_victim_is_initialized(1 - get_core_num())) { + multicore_lockout_end_blocking(); + } +} + +void rp2_flash_set_timing(void) { + rp2_flash_set_timing_for_freq(clock_get_hz(clk_sys)); +} diff --git a/ports/rp2/rp2_flash.h b/ports/rp2/rp2_flash.h new file mode 100644 index 0000000000000..3c1fbbff2f9cd --- /dev/null +++ b/ports/rp2/rp2_flash.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Mike Bell + * Phil Howard + * + * 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_RP2_FLASH_H +#define MICROPY_INCLUDED_RP2_RP2_FLASH_H + +extern void rp2_flash_set_timing_for_freq(int clock_hz); +extern void rp2_flash_set_timing(void); + +#endif From 0ee160e7c06ddc6641257d5ebb88d006e731a3aa Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 18 Dec 2024 14:49:56 +1100 Subject: [PATCH 453/546] extmod/extmod.mk: Add cyw43_spi.c to list of sources. This file is part of the updated cyw43-driver. It will only be used if `CYW43_USE_SPI` is 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 a510f3c54c25c..d2fe2acbad95c 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -458,6 +458,7 @@ SRC_THIRDPARTY_C += $(addprefix $(CYW43_DIR)/src/,\ cyw43_lwip.c \ cyw43_ll.c \ cyw43_sdio.c \ + cyw43_spi.c \ cyw43_stats.c \ ) ifeq ($(MICROPY_PY_BLUETOOTH),1) From a9384c71c50a7bc4aaae1f88ddee5ebacc56ba81 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 28 Feb 2025 23:19:31 +1100 Subject: [PATCH 454/546] extmod/extmod.mk: Switch from drivers/cyw43/cywbt to lib/cyw43-drivers. The cyw43-driver now provides the Bluetooth initialisation code, making `drivers/cyw43/cywbt.c` obsolete. To use the new code a port must enable the `CYW43_ENABLE_BLUETOOTH_OVER_UART` option. Some ports have yet to migrate to the new code, so in the meantime they can explicitly add the old source to their source list and continue to use it without change. Signed-off-by: Damien George --- extmod/extmod.mk | 4 +--- ports/mimxrt/Makefile | 1 + ports/stm32/Makefile | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/extmod.mk b/extmod/extmod.mk index d2fe2acbad95c..859f610c90ac1 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -454,6 +454,7 @@ CYW43_DIR = lib/cyw43-driver GIT_SUBMODULES += $(CYW43_DIR) CFLAGS_EXTMOD += -DMICROPY_PY_NETWORK_CYW43=1 SRC_THIRDPARTY_C += $(addprefix $(CYW43_DIR)/src/,\ + cyw43_bthci_uart.c \ cyw43_ctrl.c \ cyw43_lwip.c \ cyw43_ll.c \ @@ -461,9 +462,6 @@ SRC_THIRDPARTY_C += $(addprefix $(CYW43_DIR)/src/,\ cyw43_spi.c \ cyw43_stats.c \ ) -ifeq ($(MICROPY_PY_BLUETOOTH),1) -DRIVERS_SRC_C += drivers/cyw43/cywbt.c -endif $(BUILD)/$(CYW43_DIR)/src/cyw43_%.o: CFLAGS += -std=c11 endif # MICROPY_PY_NETWORK_CYW43 diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 8106ab4f3ff03..5c54295028083 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -280,6 +280,7 @@ endif ifeq ($(MICROPY_PY_BLUETOOTH),1) SRC_C += mpbthciport.c +DRIVERS_SRC_C += drivers/cyw43/cywbt.c endif # MICROPY_PY_BLUETOOTH ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 8ac9a8af03d1a..c938dcbda7d6d 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -432,6 +432,7 @@ endif ifeq ($(MICROPY_PY_BLUETOOTH),1) SRC_C += mpbthciport.c +DRIVERS_SRC_C += drivers/cyw43/cywbt.c ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) SRC_C += mpnimbleport.c From 7268034d56d353e90992f0d688eb4a35ae86e09a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2025 23:35:07 +1100 Subject: [PATCH 455/546] top: Add "ser" to codespell exclusion list. This word appears in the upcoming alif port. Signed-off-by: Damien George --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8053df24dd797..263b120d3fea4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.codespell] count = "" ignore-regex = '\b[A-Z]{3}\b' -ignore-words-list = "ans,asend,deques,dout,emac,extint,hsi,iput,mis,notin,numer,shft,synopsys,technic,ure,curren" +ignore-words-list = "ans,asend,deques,dout,emac,extint,hsi,iput,mis,notin,numer,ser,shft,synopsys,technic,ure,curren" quiet-level = 3 skip = """ */build*,\ From c5102a7858e9e23aaaa99e70bb0d196bd7e25ba4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Dec 2023 00:11:32 +1100 Subject: [PATCH 456/546] lib/alif_ensemble-cmsis-dfp: Add new submodule for Alif SDK v1.3.2. Signed-off-by: Damien George --- .gitmodules | 3 +++ lib/alif_ensemble-cmsis-dfp | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/alif_ensemble-cmsis-dfp diff --git a/.gitmodules b/.gitmodules index 6338f0e66d63d..7c5a517259269 100644 --- a/.gitmodules +++ b/.gitmodules @@ -68,3 +68,6 @@ [submodule "lib/arduino-lib"] path = lib/arduino-lib url = https://github.com/arduino/arduino-lib-mpy.git +[submodule "lib/alif_ensemble-cmsis-dfp"] + path = lib/alif_ensemble-cmsis-dfp + url = https://github.com/alifsemi/alif_ensemble-cmsis-dfp.git diff --git a/lib/alif_ensemble-cmsis-dfp b/lib/alif_ensemble-cmsis-dfp new file mode 160000 index 0000000000000..04b3176a0031c --- /dev/null +++ b/lib/alif_ensemble-cmsis-dfp @@ -0,0 +1 @@ +Subproject commit 04b3176a0031c945d4306db00663379e9b431e08 From 1356860e2258cf2ecef43a65f40d83ddcbf98d0b Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Mar 2025 16:47:39 +1100 Subject: [PATCH 457/546] lib/alif-security-toolkit: Add new submodule for Alif Security Toolkit. Signed-off-by: Damien George --- .gitmodules | 3 +++ lib/alif-security-toolkit | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/alif-security-toolkit diff --git a/.gitmodules b/.gitmodules index 7c5a517259269..02849ec9bdd11 100644 --- a/.gitmodules +++ b/.gitmodules @@ -71,3 +71,6 @@ [submodule "lib/alif_ensemble-cmsis-dfp"] path = lib/alif_ensemble-cmsis-dfp url = https://github.com/alifsemi/alif_ensemble-cmsis-dfp.git +[submodule "lib/alif-security-toolkit"] + path = lib/alif-security-toolkit + url = https://github.com/micropython/alif-security-toolkit.git diff --git a/lib/alif-security-toolkit b/lib/alif-security-toolkit new file mode 160000 index 0000000000000..63698efe8567e --- /dev/null +++ b/lib/alif-security-toolkit @@ -0,0 +1 @@ +Subproject commit 63698efe8567eed115fe620d5e5de75f460310d7 From 092d85557cc901a2840ac4abce3d12eb520c9765 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Dec 2023 00:11:41 +1100 Subject: [PATCH 458/546] alif/tinyusb_port: Add Alif TinyUSB DCD driver. From https://github.com/alifsemi/alif_vscode-tinyusb.git, commit c79f39361d334ee44f44fed30c56e70dbb368649 Signed-off-by: Damien George Signed-off-by: iabdalkader --- ports/alif/tinyusb_port/alif_dcd_reg.h | 691 ++++++++++++++++++++++++ ports/alif/tinyusb_port/tusb_alif_dcd.c | 688 +++++++++++++++++++++++ 2 files changed, 1379 insertions(+) create mode 100644 ports/alif/tinyusb_port/alif_dcd_reg.h create mode 100644 ports/alif/tinyusb_port/tusb_alif_dcd.c diff --git a/ports/alif/tinyusb_port/alif_dcd_reg.h b/ports/alif/tinyusb_port/alif_dcd_reg.h new file mode 100644 index 0000000000000..84220f6be4ce4 --- /dev/null +++ b/ports/alif/tinyusb_port/alif_dcd_reg.h @@ -0,0 +1,691 @@ +// *FORMAT-OFF* +///------------------------------------------------------------------------------------------------- +/// @file alif_dcd_reg.h +/// @author karol.saja@alifsemi.com +/// @version 0.0.1 +/// @date 2023-09-08 +/// @brief Low Level SPI driver +///------------------------------------------------------------------------------------------------- + +#ifndef __ALIF_DCD_REG_H__ +#define __ALIF_DCD_REG_H__ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +//-------------------------------------------------------------------------------------------------- + +#define _rw volatile uint32_t +#define __w volatile uint32_t +#define __r volatile const uint32_t + +volatile struct { + union { + _rw gsbuscfg0; + struct { + _rw incrbrstena : 1; + _rw incr4brstena : 1; + _rw incr8brstena : 1; + _rw incr16brstena : 1; + _rw incr32brstena : 1; + _rw incr64brstena : 1; + _rw incr128brstena : 1; + _rw incr256brstena : 1; + __r : 2; + _rw desbigend : 1; + _rw datbigend : 1; + __r : 4; + _rw deswrreqinfo : 4; + _rw datwrreqinfo : 4; + _rw desrdreqinfo : 4; + _rw datrdreqinfo : 4; + } gsbuscfg0_b; + }; + + union { + _rw gsbuscfg1; + struct { + __r : 8; + _rw pipetranslimit : 4; + _rw en1kpage : 1; + __r : 19; + } gsbuscfg1_b; + }; + + __r : 32; __r : 32; + + union { + _rw gctl; + struct { + _rw dsblclkgtng : 1; + _rw gblhibernationen : 1; + __r : 1; + _rw disscramble : 1; + _rw scaledown : 2; + _rw ramclksel : 2; + __r : 2; + _rw sofitpsync : 1; + _rw coresoftreset : 1; + _rw prtcapdir : 2; + _rw frmscldwn : 2; + __r : 1; + _rw bypssetaddr : 1; + __r : 14; + } gctl_b; + }; + + __r : 32; + + union { + _rw gsts; + struct { + __r curmod : 2; + __r : 2; + _rw buserraddrvld : 1; + _rw csrtimeout : 1; + __r device_ip : 1; + __r host_ip : 1; + __r adp_ip : 1; + __r bc_ip : 1; + __r otg_ip : 1; + __r ssic_ip : 1; + __r : 8; + __r cbelt : 12; + } gsts_b; + }; + + union { + _rw guctl1; + struct { + _rw loa_filter_en : 1; + _rw ovrld_l1_susp_com : 1; + _rw hc_parchk_disable : 1; + _rw hc_errata_enable : 1; + _rw l1_susp_thrld_for_host : 4; + _rw l1_susp_thrld_en_for_host : 1; + _rw dev_hs_nyet_bulk_spr : 1; + _rw resume_opmode_hs_host : 1; + __r : 1; + _rw disusb2refclkgtng : 1; + __r : 2; + _rw parkmode_disable_fsls : 1; + _rw parkmode_disable_hs : 1; + __r : 1; + _rw nak_per_enh_hs : 1; + _rw nak_per_enh_fs : 1; + _rw dev_lsp_tail_lock_dis : 1; + _rw ip_gap_add_on : 2; + _rw dev_l1_exit_by_hw : 1; + __r : 2; + _rw dev_trb_out_spr_ind : 1; + _rw tx_ipgap_linecheck_dis : 1; + _rw filter_se0_fsls_eop : 1; + __r : 1; + _rw dev_decouple_l1l2_evt : 1; + } guctl1_b; + }; + + union { + _rw gsnpsid; + }; + + __r : 32; + + union { + _rw guid; + struct { + _rw userid : 32; + } guid_b; + }; + + union { // <- host only, leaving unimplemented for now [FIXME] + _rw guctl; + struct { + } guctl_b; + }; + + union { + _rw gbuserraddrlo; + struct { + _rw buserraddr : 32; + } gbuserraddrlo_b; + }; + + union { + _rw gbuserraddrhi; + struct { + _rw buserraddr : 32; + } gbuserraddrhi_b; + }; + + __r : 32; __r : 32; + + __r ghwparams[8]; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; + + // base + 0x9C here + + union { + _rw guctl2; + struct { + __r : 11; + _rw disablecfc : 1; + _rw enableepcacheevict : 1; + __r : 1; + _rw rst_actbitlater : 1; + __r : 4; + _rw en_hp_pm_timer : 7; + __r : 6; + } guctl2_b; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x100 here + + union { + _rw gusb2phycfg0; + struct { + _rw toutcal : 3; + _rw phyif : 1; + __r ulpi_utmi_sel : 1; + __r fsintf : 1; + _rw suspendusb20 : 1; + _rw physel : 1; + _rw enblslpm : 1; + _rw xcvrdly : 1; + _rw usbtrdtim : 4; + __r : 5; + _rw lsipd : 3; + _rw lstrd : 3; + _rw ovrd_fsls_disc_time : 1; + __r inv_sel_hsic : 1; + __r hsic_con_width_adj : 2; + _rw ulpi_lpm_with_opmode_chk : 1; + _rw u2_freeclk_exists : 1; + _rw physoftrst : 1; + } gusb2phycfg0_b; + }; + + __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x200 here + + union { + _rw gtxfifosiz[4]; + struct { + _rw txfdep : 16; + _rw txfstaddr : 16; + } gtxfifosiz_b[4]; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x280 here + + union { + _rw grxfifosiz[4]; + struct { + _rw rxfdep : 16; + _rw rxfstaddr : 16; + } grxfifosiz_b[4]; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x300 here + + union { + _rw gevntadrlo0; + struct { + _rw evntadrlo : 32; + } gevntadrlo0_b; + }; + + union { + _rw gevntadrhi0; + struct { + _rw evntadrhi : 32; + } gevntadrhi0_b; + }; + + union { + _rw gevntsiz0; + struct { + _rw eventsiz : 16; + __r : 15; + _rw evntintrptmask : 1; + } gevntsiz0_b; + }; + + union { + _rw gevntcount0; + struct { + _rw evntcount : 16; + __r : 15; + _rw evnt_handler_busy : 1; + } gevntcount0_b; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x500 here + + __r ghwparams8; + + __r : 32; __r : 32; __r : 32; + + // base + 0x510 here + + union { + _rw gtxfifopridev; + struct { + _rw gtxfifopridev : 4; + __r : 28; + } gtxfifopridev_b; + }; + + __r : 32; + + union { + _rw gtxfifoprihst; + struct { + _rw gtxfifoprihst : 2; + __r : 30; + } gtxfifoprihst_b; + }; + + union { + _rw grxfifoprihst; + struct { + _rw grxfifoprihst : 2; + __r : 30; + } grxfifoprihst_b; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x530 here + + union { + _rw gfladj; + struct { + _rw gfladj_30mhz : 6; + __r : 1; + _rw gfladj_30mhz_sdbnd_sel : 1; + _rw gfladj_refclk_fladj : 14; + __r : 1; + _rw gfladj_refclk_lpm_sel : 1; + _rw gfladj_refclk_240mhz_decr : 7; + _rw gfladj_refclk_240mhzdecr_pls1 : 1; + } gfladj_b; + }; + + __r : 32; __r : 32; __r : 32; + + // base + 0x540 here + + union { + _rw gusb2rhbctl0; + struct { + _rw ovrd_l1timeout : 4; + __r : 28; + } gusb2rhbctl0_b; + }; + +} *ugbl = (void *) (USB_BASE + 0xC100); + +volatile struct { + union { + _rw dcfg; + struct { + _rw devspd : 3; + _rw devaddr : 7; + __r : 2; + _rw intrnum : 5; + _rw nump : 5; + _rw lpmcap : 1; + _rw ignstrmpp : 1; + __r : 8; + } dcfg_b; + }; + + union { + _rw dctl; + struct { + __r : 1; + _rw tstctl : 4; + __w ulstchngreq : 4; + __r : 7; + _rw css : 1; + _rw crs : 1; + _rw l1hibernationen : 1; + _rw keepconnect : 1; + _rw lpm_nyet_thres : 4; + _rw hirdthres : 5; + __r : 1; + _rw csftrst : 1; + _rw run_stop : 1; + } dctl_b; + }; + + union { + _rw devten; + struct { + _rw dissconnevten : 1; + _rw usbrstevten : 1; + _rw connectdoneevten : 1; + _rw ulstcngen : 1; + _rw wkupevten : 1; + _rw hibernationreqevten : 1; + _rw u3l2l1suspen : 1; + _rw softevten : 1; + _rw l1suspen : 1; + _rw errticerrevten : 1; + __r : 2; + _rw vendevtstrcvden : 1; + __r : 1; + _rw l1wkupevten : 1; + __r : 1; + _rw eccerren : 1; + __r : 15; + } devten_b; + }; + + union { + _rw dsts; + struct { + __r connectspd : 3; + __r soffn : 14; + __r rxfifoempty : 1; + __r usblnkst : 4; + __r devctrlhlt : 1; + __r coreidle : 1; + __r sss : 1; + __r rss : 1; + __r : 2; + _rw sre : 1; + __r dcnrd : 1; + __r : 2; + } dsts_b; + }; + + union { + _rw dgcmdpar; + struct { + _rw parameter : 32; + } dgcmdpar_b; + }; + + union { + _rw dgcmd; + struct { + _rw cmdtyp : 8; + _rw cmdioc : 1; + __r : 1; + _rw cmdact : 1; + __r : 1; + __r cmdstatus : 4; + __r : 16; + } dgcmd_b; + }; + + __r : 32; __r : 32; + + union { + _rw dalepena; + struct { + _rw usbactep : 32; + } dalepena_b; + }; + + __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x100 here + + struct { + union { + _rw par2; + struct { + _rw parameter : 32; + } par2_b; + }; + + union { + _rw par1; + struct { + _rw parameter : 32; + } par1_b; + }; + + union { + _rw par0; + struct { + _rw parameter : 32; + } par0_b; + }; + + union { + _rw depcmd; + struct { + _rw cmdtyp : 4; + __r : 4; + _rw cmdioc : 1; + __r : 1; + _rw cmdact : 1; + _rw hipri_forcerm : 1; + _rw cmdstatus : 4; + _rw commandparam : 16; + } depcmd_b; + }; + } depcmd[8]; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x300 here + + union { + _rw dev_imod0; + struct { + _rw device_imodi : 16; + _rw device_imocd : 16; + } dev_imod0_b; + }; +} *udev = (void *) (USB_BASE + 0xC700); + +//-------------------------------------------------------------------------------------------------- + +typedef union { + _rw val : 32; + struct { + _rw sig : 8; + _rw evt : 5; + __r : 3; + _rw info : 9; + __r : 7; + } devt; + struct { + _rw sig : 1; + _rw ep : 5; + _rw evt : 4; + __r : 2; + _rw sts : 4; + _rw par : 16; + } depevt; +} evt_t; + +//-------------------------------------------------------------------------------------------------- + +enum { + CMDTYP_RESERVED = 0, + CMDTYP_DEPCFG, + CMDTYP_DEPXFERCFG, + CMDTYP_DEPGETSTATE, + CMDTYP_DEPSSTALL, + CMDTYP_DEPCSTALL, + CMDTYP_DEPSTRTXFER, + CMDTYP_DEPUPDXFER, + CMDTYP_DEPENDXFER, + CMDTYP_DEPSTARTCFG +} DEPCMD_CMDTYP; + +enum { + DEVT_DISCONNEVT = 0, + DEVT_USBRST, + DEVT_CONNECTDONE, + DEVT_ULSTCHNG, + DEVT_WKUPEVT, + // DEVT_HIBRQ not implemented + DEVT_USBSUSP = 6, + DEVT_SOF, + DEVT_L1SUSP, + DEVT_ERRTICERR, + DEVT_CMDCMPLT, + DEVT_EVNTOVERFLOW, + DEVT_VNDDEVTSTRCVED, + // reserved + DEVT_L1RESM = 14, + // reserved + DEVT_ECCERR = 16 +} DEVT; + +enum { + // reserved + DEPEVT_XFERCOMPLETE = 1, + DEPEVT_XFERINPROGRESS, + DEPEVT_XFERNOTREADY, + // not implemented + DEPEVT_STREAMEVT = 6, + DEPEVT_EPCMDCMPLT +} DEPEVT; + +enum { + // reserved + TRBCTL_NORMAL = 1, + TRBCTL_CTL_SETUP, + TRBCTL_CTL_STAT2, + TRBCTL_CTL_STAT3, + TRBCTL_CTL_DATA, + TRBCTL_ISO_FIRST, + TRBCTL_ISO, + TRBCTL_LINK, + TRBCTL_NORMAL_ZLP +} TRBCTL; + +//-------------------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------------------- + +#ifdef __cplusplus +} +#endif // __cplusplus + + +#endif // __ALIF_DCD_REG_H__ diff --git a/ports/alif/tinyusb_port/tusb_alif_dcd.c b/ports/alif/tinyusb_port/tusb_alif_dcd.c new file mode 100644 index 0000000000000..510f0e9c13a9b --- /dev/null +++ b/ports/alif/tinyusb_port/tusb_alif_dcd.c @@ -0,0 +1,688 @@ +// *FORMAT-OFF* +/* Copyright (C) 2024 Alif Semiconductor - All Rights Reserved. + * Use, distribution and modification of this code is permitted under the + * terms stated in the Alif Semiconductor Software License Agreement + * + * You should have received a copy of the Alif Semiconductor Software + * License Agreement with this file. If not, please write to: + * contact@alifsemi.com, or visit: https://alifsemi.com/license + * + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED + +#if defined(CORE_M55_HE) +#include "M55_HE.h" +#elif defined(CORE_M55_HP) +#include "M55_HP.h" +#else +#error "Unsupported core!" +#endif + +#include "device/dcd.h" + +#include "alif_dcd_reg.h" + +#include "clk.h" +#include "power.h" + +// #define TUSB_ALIF_DEBUG +// #define TUSB_ALIF_DEBUG_DEPTH (2048) + +#if defined(TUSB_ALIF_DEBUG) +#if (1 < TUSB_ALIF_DEBUG_DEPTH) +#define LOG(...) memset(logbuf[bi % TUSB_ALIF_DEBUG_DEPTH], ' ', 48);\ + snprintf(logbuf[(bi++) % TUSB_ALIF_DEBUG_DEPTH], 48, __VA_ARGS__); +char logbuf[TUSB_ALIF_DEBUG_DEPTH][48]; +int bi = 0; +#else +#define LOG(...) memset(logbuf, ' ', 48);\ + snprintf(logbuf, 48, __VA_ARGS__) +char logbuf[48]; +#endif +#else +#define LOG(...) +#endif + +/// Structs and Buffers -------------------------------------------------------- + +static uint32_t _evnt_buf[1024] CFG_TUSB_MEM_SECTION __attribute__((aligned(4096))); // [TODO] runtime alloc +static volatile uint32_t* _evnt_tail; + +static uint8_t _ctrl_buf[64] CFG_TUSB_MEM_SECTION __attribute__((aligned(32))); // [TODO] runtime alloc +static uint32_t _xfer_trb[8][4] CFG_TUSB_MEM_SECTION __attribute__((aligned(32))); // [TODO] runtime alloc +static uint16_t _xfer_bytes[8]; +static bool _ctrl_long_data = false; +static bool _xfer_cfgd = false; +// static bool _addr_req = false; +static uint32_t _sts_stage = 0; + +/// Private Functions ---------------------------------------------------------- + +static uint8_t _dcd_cmd_wait(uint8_t ep, uint8_t typ, uint16_t param); +static uint8_t _dcd_start_xfer(uint8_t ep, void* buf, uint32_t size, uint8_t type); + +static void _dcd_handle_depevt(uint8_t ep, uint8_t evt, uint8_t sts, uint16_t par); +static void _dcd_handle_devt(uint8_t evt, uint16_t info); + +/// API Extension -------------------------------------------------------------- + +void dcd_uninit(void); + +/// Device Setup --------------------------------------------------------------- + +// Initializes the USB peripheral for device mode and enables it. +// This function should enable internal D+/D- pull-up for enumeration. +void dcd_init(uint8_t rhport) +{ + // enable 20mhz clock + enable_cgu_clk20m(); + // enable usb peripheral clock + enable_usb_periph_clk(); + // power up usb phy + enable_usb_phy_power(); + // disable usb phy isolation + disable_usb_phy_isolation(); + // clear usb phy power-on-reset signal + CLKCTL_PER_MST->USB_CTRL2 &= ~(1 << 8); + + // force stop/disconnect + udev->dctl_b.run_stop = 0; + + // dctl set csftrst to 1 and wait for 0 + udev->dctl_b.csftrst = 1; + while(0 != udev->dctl_b.csftrst); + + sys_busy_loop_us(50000); + ugbl->gctl_b.coresoftreset = 1; + ugbl->gusb2phycfg0_b.physoftrst = 1; + sys_busy_loop_us(50000); + ugbl->gusb2phycfg0_b.physoftrst = 0; + sys_busy_loop_us(50000); + ugbl->gctl_b.coresoftreset = 0; + sys_busy_loop_us(50000); + + ugbl->gsbuscfg0 = 0x00000009; + // ugbl->gusb2phycfg0 = 0x4000154F; // [TODO] document as bits + uint32_t reg = ugbl->gusb2phycfg0; + reg &= ~((1 << 3) | (1 << 4) | (0xF << 10)); // clear phyif, ulpi_utmi_sel and usbtrdtim + reg |= ((1 << 3) | (5 << 10)); + ugbl->gusb2phycfg0 = reg; + + // set device speed (USBHS only) + udev->dcfg_b.devspd = 0x0; // HS, this will need #if condition [TODO] + + // allocate ring buffer for events + memset(_evnt_buf, 0, sizeof(_evnt_buf)); + RTSS_CleanDCache_by_Addr(_evnt_buf, sizeof(_evnt_buf)); + _evnt_tail = _evnt_buf; + ugbl->gevntadrlo0 = (uint32_t) LocalToGlobal(_evnt_buf); + ugbl->gevntsiz0_b.eventsiz = (uint32_t) sizeof(_evnt_buf); + ugbl->gevntcount0_b.evntcount = 0; + + // write devten to enable usb reset, conn done, link state change... + udev->devten_b.dissconnevten = 1; + udev->devten_b.usbrstevten = 1; + udev->devten_b.connectdoneevten = 1; + udev->devten_b.ulstcngen = 1; + + // begin endpoint setup + _dcd_cmd_wait(0, CMDTYP_DEPSTARTCFG, 0); + + // configure CONTROL IN and OUT eps + udev->depcmd[0].par1 = (0 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[0].par0 = (0 << 22) | (0 << 17) | (512 << 3) | (0 << 1); + _dcd_cmd_wait(0, CMDTYP_DEPCFG, 0); + + udev->depcmd[1].par1 = (1 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[1].par0 = (0 << 22) | (0 << 17) | (512 << 3) | (0 << 1); + _dcd_cmd_wait(1, CMDTYP_DEPCFG, 0); + + // set initial xfer configuration for CONTROL eps + udev->depcmd[0].par0 = 1; + _dcd_cmd_wait(0, CMDTYP_DEPXFERCFG, 0); + + udev->depcmd[1].par0 = 1; + _dcd_cmd_wait(1, CMDTYP_DEPXFERCFG, 0); + + // enable pull-ups + dcd_connect(rhport); + + // prepare trb for the first setup packet + memset(_ctrl_buf, 0, sizeof(_ctrl_buf)); + _xfer_trb[0][0] = (uint32_t) LocalToGlobal(_ctrl_buf); + _xfer_trb[0][1] = 0; + _xfer_trb[0][2] = 8; + _xfer_trb[0][3] = (1 << 11) | (1 << 10) | (TRBCTL_CTL_SETUP << 4) | (1 << 1) | (1 << 0); + RTSS_CleanDCache_by_Addr(_xfer_trb[0], sizeof(_xfer_trb[0])); + + // send trb to the usb dma + udev->depcmd[0].par1 = (uint32_t) LocalToGlobal(_xfer_trb[0]); + udev->depcmd[0].par0 = 0; + _dcd_cmd_wait(0, CMDTYP_DEPSTRTXFER, 0); + + // enable ep event interrupts for CONTROL OUT and IN + udev->dalepena_b.usbactep = (1 << 1) | (1 << 0); + + // enable interrupts in the NVIC +#if !defined(TUSB_ALIF_NO_IRQ_CFG) + NVIC_ClearPendingIRQ(USB_IRQ_IRQn); + NVIC_SetPriority(USB_IRQ_IRQn, 5); +#endif + dcd_int_enable(rhport); +} + + +// Processes all the hardware generated events e.g bus reset, new data packet +// from host... It will be called by application in the MCU USB interrupt handler. +void dcd_int_handler(uint8_t rhport) +{ + LOG("%010u IRQ enter, evntcount %u", DWT->CYCCNT, ugbl->gevntcount0_b.evntcount); + + // process failures first + if (ugbl->gsts_b.device_ip) { + if (ugbl->gsts_b.csrtimeout || ugbl->gsts_b.buserraddrvld) { + // buserraddrvld is usually set when USB tries to write to protected + // memory region. Check linker script to ensure USB event buffer and + // TRBs reside in bulk memory or other NS-allowed region + __BKPT(0); + } + } + + // cycle through event queue + // evaluate on every iteration to prevent unnecessary isr exit/reentry + while (0 < ugbl->gevntcount0_b.evntcount) { + RTSS_InvalidateDCache_by_Addr(_evnt_buf, sizeof(_evnt_buf)); + volatile evt_t e = {.val = *_evnt_tail++}; + + LOG("%010u IRQ loop, evntcount %u evnt %08x", DWT->CYCCNT, + ugbl->gevntcount0_b.evntcount, e.val); + + // wrap around + if (_evnt_tail >= (_evnt_buf + 1024)) _evnt_tail = _evnt_buf; + + // dispatch the right handler for the event type + if (0 == e.depevt.sig) { // DEPEVT + _dcd_handle_depevt(e.depevt.ep, e.depevt.evt, e.depevt.sts, e.depevt.par); + } else if (1 == e.devt.sig) { // DEVT + _dcd_handle_devt(e.devt.evt, e.devt.info); + } else { + // bad event?? + LOG("Unknown event %u", e.val); + __BKPT(0); + } + + // consume one event + ugbl->gevntcount0 = 4; + } + + LOG("%010u IRQ exit, evntcount %u", DWT->CYCCNT, ugbl->gevntcount0_b.evntcount); +} + + +// Enables the USB device interrupt. +// May be used to prevent concurrency issues when mutating data structures +// shared between main code and the interrupt handler. +void dcd_int_enable (uint8_t rhport) +{ + NVIC_EnableIRQ(USB_IRQ_IRQn); + + (void) rhport; +} + +// Disables the USB device interrupt. +// May be used to prevent concurrency issues when mutating data structures +// shared between main code and the interrupt handler. +void dcd_int_disable(uint8_t rhport) +{ + NVIC_DisableIRQ(USB_IRQ_IRQn); + + (void) rhport; +} + +// Receive Set Address request, mcu port must also include status IN response. +// If your peripheral automatically changes address during enumeration you may +// leave this empty and also no queue an event for the corresponding SETUP packet. +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); + + udev->dcfg_b.devaddr = dev_addr; + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); +} + +// Called to remote wake up host when suspended (e.g hid keyboard) +void dcd_remote_wakeup(uint8_t rhport) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); +} + +// Connect by enabling internal pull-up resistor on D+/D- +void dcd_connect(uint8_t rhport) +{ + udev->dctl_b.run_stop = 1; + + (void) rhport; +} + +// Disconnect by disabling internal pull-up resistor on D+/D- +void dcd_disconnect(uint8_t rhport) +{ + // [TODO] clear all xfers and eps first + + udev->dctl_b.run_stop = 0; + + (void) rhport; +} + +// Enable/Disable Start-of-frame interrupt. Default is disabled +void dcd_sof_enable(uint8_t rhport, bool en) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); +} + + +/// Endpoint Management -------------------------------------------------------- + +// Invoked when a control transfer's status stage is complete. +// May help DCD to prepare for next control transfer, this API is optional. +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); + + _ctrl_long_data = false; + _dcd_start_xfer(TUSB_DIR_OUT, _ctrl_buf, 8, TRBCTL_CTL_SETUP); +} + +// Opening an endpoint is done for all non-control endpoints once the host picks +// a configuration that the device should use. +// At this point, the endpoint should be enabled in the peripheral and +// configured to match the endpoint descriptor. +// Pay special attention to the direction of the endpoint you can get from the +// helper methods above. It will likely change what registers you are setting. +// Also make sure to enable endpoint specific interrupts. +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) +{ + LOG("%010u >%s %u %s %u %u", DWT->CYCCNT, __func__, desc_ep->bEndpointAddress, + desc_ep->bmAttributes.xfer == TUSB_XFER_BULK ? "bulk" : "int", + desc_ep->wMaxPacketSize, desc_ep->bInterval); + + if (TUSB_XFER_ISOCHRONOUS == desc_ep->bmAttributes.xfer) + return false; + + uint8_t ep = (tu_edpt_number(desc_ep->bEndpointAddress) << 1) | + tu_edpt_dir(desc_ep->bEndpointAddress); + + // [TODO] verify that the num doesn't exceed hw max + + if (false == _xfer_cfgd) { + _dcd_cmd_wait(0, CMDTYP_DEPSTARTCFG, 2); + _xfer_cfgd = true; + } + + uint8_t fifo_num = TUSB_DIR_IN == tu_edpt_dir(desc_ep->bEndpointAddress) ? + tu_edpt_number(desc_ep->bEndpointAddress) : 0; + uint8_t interval = 0 < desc_ep->bInterval ? (desc_ep->bInterval - 1) : 0; + + udev->depcmd[ep].par1 = (ep << 25) | (interval << 16) | + (1 << 10) | (1 << 8); + udev->depcmd[ep].par0 = (0 << 30) | (0 << 22) | (fifo_num << 17) | + ((desc_ep->wMaxPacketSize & 0x7FF) << 3) | + (desc_ep->bmAttributes.xfer << 1); + + _dcd_cmd_wait(ep, CMDTYP_DEPCFG, 0); + udev->depcmd[ep].par0 = 1; + _dcd_cmd_wait(ep, CMDTYP_DEPXFERCFG, 0); + + udev->dalepena_b.usbactep |= (1 << ep); + + return true; +} + +// Close all non-control endpoints, cancel all pending transfers if any. +// Invoked when switching from a non-zero Configuration by SET_CONFIGURE therefore +// required for multiple configuration support. +void dcd_edpt_close_all(uint8_t rhport) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); +} + +// Close an endpoint. his function is used for implementing alternate settings. +// After calling this, the device should not respond to any packets directed +// towards this endpoint. When called, this function must abort any transfers in +// progress through this endpoint, before returning. +// Implementation is optional. Must be called from the USB task. +// Interrupts could be disabled or enabled during the call. +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK; + +// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to +// notify the stack +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + // DEPSTRTXFER command + LOG("%010u >%s %u %x %u", DWT->CYCCNT, __func__, ep_addr, (uint32_t) buffer, total_bytes); + + uint8_t ep = (tu_edpt_number(ep_addr) << 1) | tu_edpt_dir(ep_addr); + + switch (ep) { + case 0: { // CONTROL OUT + if (0 < total_bytes) { + // DATA OUT request + _xfer_bytes[0] = total_bytes; + _dcd_start_xfer(0, buffer, total_bytes, TRBCTL_CTL_STAT3); + } else { + // TinyUSB explicitly requests STATUS OUT fetch after DATA IN + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_OUT), + 0, XFER_RESULT_SUCCESS, true); + } + } + } break; + case 1: { // CONTROL IN + _xfer_bytes[1] = total_bytes; + + if (0 < total_bytes) { + RTSS_CleanDCache_by_Addr(buffer, total_bytes); + uint8_t type = _ctrl_long_data ? TRBCTL_NORMAL : TRBCTL_CTL_DATA; + if (64 == total_bytes) { + _ctrl_long_data = true; + } + _dcd_start_xfer(1, buffer, total_bytes, type); + } else { + // status events are handled directly from the ISR when USB + // controller triggers XferNotReady event for status stage + + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_IN), + 0, XFER_RESULT_SUCCESS, true); + } + // dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN), + // 0, XFER_RESULT_SUCCESS, false); + } + } break; + default: { // DATA EPs (BULK & INTERRUPT only) + _xfer_bytes[ep] = total_bytes; + if (TUSB_DIR_IN == tu_edpt_dir(ep_addr)) { + RTSS_CleanDCache_by_Addr(buffer, total_bytes); + } else { + total_bytes = 512; // temporary hack, controller requires max + // size requests on OUT endpoints [FIXME] + } + uint8_t ret = _dcd_start_xfer(ep, buffer, total_bytes, + total_bytes ? TRBCTL_NORMAL : TRBCTL_NORMAL_ZLP); + LOG("start xfer sts %u", ret); + } + } + + return true; +} + +// Submit a transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack +// This API is optional, may be useful for register-based for transferring data. +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) TU_ATTR_WEAK; + +// Stall endpoint, any queuing transfer should be removed from endpoint +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + // DEPSSTALL command + LOG(">%s", __func__); + + uint8_t ep = (tu_edpt_number(ep_addr) << 1) | tu_edpt_dir(ep_addr); + + _dcd_cmd_wait(ep, CMDTYP_DEPSSTALL, 0); + + if (0 == tu_edpt_number(ep_addr)) { + _ctrl_long_data = false; + _dcd_start_xfer(TUSB_DIR_OUT, _ctrl_buf, 8, TRBCTL_CTL_SETUP); + } +} + +// clear stall, data toggle is also reset to DATA0 +// This API never calls with control endpoints, since it is auto cleared when +// receiving setup packet +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + // DEPCSTALL command + LOG(">%s", __func__); + + uint8_t ep = (tu_edpt_number(ep_addr) << 1) | tu_edpt_dir(ep_addr); + + _dcd_cmd_wait(ep, CMDTYP_DEPCSTALL, 0); +} + +void dcd_uninit(void) +{ + CLKCTL_PER_MST->USB_CTRL2 |= 1 << 8; // set usb phy power-on-reset signal + enable_usb_phy_isolation(); // enable usb phy isolation + disable_usb_phy_power(); // power down usb phy + disable_usb_periph_clk(); // disable usb peripheral clock +} + +void USB_IRQHandler(void) +{ + dcd_int_handler(TUD_OPT_RHPORT); +} + + +static uint8_t _dcd_cmd_wait(uint8_t ep, uint8_t typ, uint16_t param) +{ + // capture phy state and disable lpm and suspend + uint32_t phycfg = ugbl->gusb2phycfg0; + ugbl->gusb2phycfg0_b.enblslpm = 0; + ugbl->gusb2phycfg0_b.suspendusb20 = 0; + + // set up command in depcmd register + udev->depcmd[ep].depcmd_b.cmdtyp = typ; + udev->depcmd[ep].depcmd_b.cmdioc = 0; + udev->depcmd[ep].depcmd_b.commandparam = param; + + // dispatch command and wait for completion + udev->depcmd[ep].depcmd_b.cmdact = 1; + while(0 != udev->depcmd[ep].depcmd_b.cmdact); + + // restore phy state + ugbl->gusb2phycfg0 = phycfg; + + return udev->depcmd[ep].depcmd_b.cmdstatus; +} + + +static void _dcd_handle_depevt(uint8_t ep, uint8_t evt, uint8_t sts, uint16_t par) +{ + LOG("%010u DEPEVT ep%u evt%u sts%u par%u", DWT->CYCCNT, ep, evt, sts, par); + + switch (evt) { + case DEPEVT_XFERCOMPLETE: { + LOG("Transfer complete"); + RTSS_InvalidateDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[0])); + if (0 == ep) { + uint8_t trbctl = (_xfer_trb[0][3] >> 4) & 0x3F; + LOG("ep0 xfer trb3 = %08x", _xfer_trb[0][3]); + if (TRBCTL_CTL_SETUP == trbctl) { + RTSS_InvalidateDCache_by_Addr(_ctrl_buf, sizeof(_ctrl_buf)); + LOG("%02x %02x %02x %02x %02x %02x %02x %02x", + _ctrl_buf[0], _ctrl_buf[1], _ctrl_buf[2], _ctrl_buf[3], + _ctrl_buf[4], _ctrl_buf[5], _ctrl_buf[6], _ctrl_buf[7]); + dcd_event_setup_received(TUD_OPT_RHPORT, _ctrl_buf, true); + } else if (TRBCTL_CTL_STAT3 == trbctl) { + if (0 < _xfer_bytes[0]) { + RTSS_InvalidateDCache_by_Addr((void*) _xfer_trb[0][0], _xfer_bytes[0]); + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_OUT), + _xfer_bytes[0] - (_xfer_trb[0][2] & 0xFFFFFF), + XFER_RESULT_SUCCESS, true); + } else { + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_OUT), + 0, XFER_RESULT_SUCCESS, true); + + // *(volatile uint32_t*) 0x4900C000 ^= 8; // [TEMP] + } + } + } else { + // invalid TRBCTL value + __BKPT(0); + } + } else if (1 == ep) { + uint8_t trbctl = (_xfer_trb[1][3] >> 4) & 0x3F; + LOG("ep1 xfer trb3 = %08x trb2 = %08x", _xfer_trb[1][3], _xfer_trb[1][2]); + if (TRBCTL_CTL_STAT2 != trbctl) { // STATUS IN notification is done at xfer request + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_IN), + _xfer_bytes[1] - (_xfer_trb[1][2] & 0xFFFFFF), + XFER_RESULT_SUCCESS, true); + } else { + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_IN), + 0, XFER_RESULT_SUCCESS, true); + + // *(volatile uint32_t*) 0x4900C000 ^= 8; // [TEMP] + } + } + } else { + // [TODO] check if ep is open + LOG("ep%u xfer trb3 = %08x trb2 = %08x", ep, _xfer_trb[ep][3], _xfer_trb[ep][2]); + if (TUSB_DIR_OUT == tu_edpt_dir(tu_edpt_addr(ep >> 1, ep & 1))) { + RTSS_InvalidateDCache_by_Addr((void*) _xfer_trb[ep][0], + 512 - _xfer_trb[ep][2]); + // _xfer_bytes[ep] - _xfer_trb[ep][2]); + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(ep >> 1, ep & 1), + 512 - _xfer_trb[ep][2], + XFER_RESULT_SUCCESS, true); + } else + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(ep >> 1, ep & 1), + _xfer_bytes[ep] - _xfer_trb[ep][2], + XFER_RESULT_SUCCESS, true); + } + } break; + case DEPEVT_XFERINPROGRESS: { + LOG("Transfer in progress"); + } break; + case DEPEVT_XFERNOTREADY: { + LOG("Transfer not ready: %s", sts & 8 ? "no TRB" : "no XFER"); + + // XferNotReady NotActive for status stage + if ((1 == ep) && (0b0010 == (sts & 0b1011))) { + _dcd_start_xfer(1, NULL, 0, TRBCTL_CTL_STAT2); + break; + } + + if ((0 == ep) && (0b0010 == (sts & 0b1011))) { + _xfer_bytes[0] = 0; + _dcd_start_xfer(0, _ctrl_buf, 64, TRBCTL_CTL_STAT3); + break; + } + + if ((1 > ep) && (sts & (1 << 3))) { + if (_xfer_trb[ep][3] & (1 << 0)) { // transfer was configured + // dependxfer can only block when actbitlater is set + ugbl->guctl2_b.rst_actbitlater = 1; + _dcd_cmd_wait(ep, CMDTYP_DEPENDXFER, 0); + ugbl->guctl2_b.rst_actbitlater = 0; + + // reset the trb byte count and clean the cache + RTSS_InvalidateDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[0])); + _xfer_trb[ep][2] = _xfer_bytes[ep]; + RTSS_CleanDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[ep])); + + // prepare ep command + udev->depcmd[ep].par1 = (uint32_t) LocalToGlobal(_xfer_trb[ep]); + udev->depcmd[ep].par0 = 0; + + // issue the block command and pass the status + _dcd_cmd_wait(ep, CMDTYP_DEPSTRTXFER, 0); + + *(volatile uint32_t*) 0x49007000 ^= 16; // [TEMP] + } + } + } break; + case DEPEVT_EPCMDCMPLT: { + // redundant, currently no commands are issued with IOC bit set + } break; + } +} + + +static void _dcd_handle_devt(uint8_t evt, uint16_t info) +{ + LOG("%010u DEVT evt%u info%u", DWT->CYCCNT, evt, info); + switch (evt) { + case DEVT_USBRST: { + _xfer_cfgd = false; + + // [TODO] issue depcstall for any ep in stall mode + udev->dcfg_b.devaddr = 0; + LOG("USB reset"); + dcd_event_bus_reset(TUD_OPT_RHPORT, TUSB_SPEED_HIGH, true); // [TODO] actual speed + } break; + case DEVT_CONNECTDONE: { + // read conn speed from dsts + // program ramclksel in gctl if needed + LOG("Connect done"); + + udev->depcmd[0].par1 = (0 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[0].par0 = (2 << 30) | (0 << 22) | (0 << 17) | (64 << 3) | (0 << 1); + _dcd_cmd_wait(0, CMDTYP_DEPCFG, 0); + + udev->depcmd[1].par1 = (1 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[1].par0 = (2 << 30) | (0 << 22) | (0 << 17) | (64 << 3) | (0 << 1); + _dcd_cmd_wait(1, CMDTYP_DEPCFG, 0); + } break; + case DEVT_ULSTCHNG: { + LOG("Link status change"); + switch (info) { + case 0x3: { // suspend (L2) + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_SUSPEND, true); + } break; + case 0x4: { // disconnected + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_UNPLUGGED, true); + } break; + case 0xF: { // resume + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_RESUME, true); + } break; + default: {} + } + // 0x0: ON state + // 0x2: L1 state (sleep) + // 0x3: L2 state (suspend) + // 0x4: disconnected state + // 0x5: early suspend + // 0xE: reset + // 0xF: resume + } break; + case DEVT_ERRTICERR: { + __BKPT(0); + } break; + default: { + LOG("Unknown DEVT event"); + } + } +} + + +static uint8_t _dcd_start_xfer(uint8_t ep, void* buf, uint32_t size, uint8_t type) +{ + dcd_int_disable(TUD_OPT_RHPORT); // prevent race conditions + + // program the trb and clean the cache + _xfer_trb[ep][0] = buf ? (uint32_t) LocalToGlobal(buf) : 0; + _xfer_trb[ep][1] = 0; + _xfer_trb[ep][2] = size; + _xfer_trb[ep][3] = (1 << 11) | (1 << 10) | (type << 4) | (1 << 1) | (1 << 0); + RTSS_CleanDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[ep])); + + // prepare ep command + udev->depcmd[ep].par1 = (uint32_t) LocalToGlobal(_xfer_trb[ep]); + udev->depcmd[ep].par0 = 0; + + dcd_int_enable(TUD_OPT_RHPORT); + + // issue the block command and pass the status + return _dcd_cmd_wait(ep, CMDTYP_DEPSTRTXFER, 0); +} + +#endif // CFG_TUD_ENABLED From ebecd6d101f0ca73baa8a7452ae946aac8b42817 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 22 Oct 2024 08:48:25 +0200 Subject: [PATCH 459/546] alif/tinyusb_port: Disable USB IRQ on deinit. Signed-off-by: iabdalkader --- ports/alif/tinyusb_port/tusb_alif_dcd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/alif/tinyusb_port/tusb_alif_dcd.c b/ports/alif/tinyusb_port/tusb_alif_dcd.c index 510f0e9c13a9b..5ede591478645 100644 --- a/ports/alif/tinyusb_port/tusb_alif_dcd.c +++ b/ports/alif/tinyusb_port/tusb_alif_dcd.c @@ -460,6 +460,8 @@ void dcd_uninit(void) enable_usb_phy_isolation(); // enable usb phy isolation disable_usb_phy_power(); // power down usb phy disable_usb_periph_clk(); // disable usb peripheral clock + dcd_int_disable(TUD_OPT_RHPORT); + NVIC_ClearPendingIRQ(USB_IRQ_IRQn); } void USB_IRQHandler(void) From b8a9cdf0673916457ca2abf8a69ad8e22afaea11 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 28 Mar 2025 00:29:06 +1100 Subject: [PATCH 460/546] alif/tinyusb_port: Implement SOF event. Signed-off-by: Damien George --- ports/alif/tinyusb_port/tusb_alif_dcd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ports/alif/tinyusb_port/tusb_alif_dcd.c b/ports/alif/tinyusb_port/tusb_alif_dcd.c index 5ede591478645..9a990cedbe387 100644 --- a/ports/alif/tinyusb_port/tusb_alif_dcd.c +++ b/ports/alif/tinyusb_port/tusb_alif_dcd.c @@ -281,6 +281,8 @@ void dcd_disconnect(uint8_t rhport) void dcd_sof_enable(uint8_t rhport, bool en) { LOG("%010u >%s", DWT->CYCCNT, __func__); + + udev->devten_b.softevten = en; } @@ -656,6 +658,9 @@ static void _dcd_handle_devt(uint8_t evt, uint16_t info) // 0xE: reset // 0xF: resume } break; + case DEVT_SOF: { + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_SOF, true); + } break; case DEVT_ERRTICERR: { __BKPT(0); } break; From ccc5935234bbef026db776ebeb83a94f353e7d6b Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Dec 2023 00:11:46 +1100 Subject: [PATCH 461/546] alif: Add initial port to Alif Ensemble MCUs. This commit adds the beginning of a new alif port with support for Alif Ensemble MCUs. See https://alifsemi.com/ Supported features of this port added by this commit: - UART REPL. - TinyUSB support, for REPL and MSC. - Octal SPI flash support, for filesystem. - machine.Pin support. General notes about the port: - It uses make, similar to other bare-metal ports here. - The toolchain is the standard arm-none-eabi- toolchain. - Flashing a board can be done using either the built-in serial bootloader, or JLink (both supported here). - There are two required submodules (one for drivers/SDK, one for security tools), both of which are open source and on GitHub. - No special hardware or software is needed for development, just a board connected over USB. OpenMV have generously sponsored the development of this port. Signed-off-by: Damien George Signed-off-by: iabdalkader --- ports/alif/Makefile | 261 ++++++++++++++++++++++ ports/alif/README.md | 21 ++ ports/alif/alif_flash.c | 160 ++++++++++++++ ports/alif/boards/manifest.py | 5 + ports/alif/fatfs_port.c | 38 ++++ ports/alif/irq.h | 84 ++++++++ ports/alif/machine_pin.c | 298 ++++++++++++++++++++++++++ ports/alif/main.c | 145 +++++++++++++ ports/alif/mcu/M55_HP_cfg.json | 14 ++ ports/alif/mcu/ensemble.ld.S | 165 ++++++++++++++ ports/alif/mcu/make-pins.py | 64 ++++++ ports/alif/mcu/pins_prefix.c | 2 + ports/alif/modalif.c | 46 ++++ ports/alif/modalif.h | 33 +++ ports/alif/modmachine.c | 79 +++++++ ports/alif/modules/_boot.py | 23 ++ ports/alif/mpconfigport.h | 155 ++++++++++++++ ports/alif/mpconfigport.mk | 12 ++ ports/alif/mphalport.c | 152 +++++++++++++ ports/alif/mphalport.h | 152 +++++++++++++ ports/alif/mpuart.c | 113 ++++++++++ ports/alif/mpuart.h | 32 +++ ports/alif/msc_disk.c | 151 +++++++++++++ ports/alif/ospi_flash.c | 265 +++++++++++++++++++++++ ports/alif/ospi_flash.h | 52 +++++ ports/alif/ospi_xip_user.h | 5 + ports/alif/pendsv.c | 49 +++++ ports/alif/pendsv.h | 48 +++++ ports/alif/qstrdefsport.h | 2 + ports/alif/tinyusb_port/tusb_config.h | 73 +++++++ ports/alif/usbd.c | 41 ++++ 31 files changed, 2740 insertions(+) create mode 100644 ports/alif/Makefile create mode 100644 ports/alif/README.md create mode 100644 ports/alif/alif_flash.c create mode 100644 ports/alif/boards/manifest.py create mode 100644 ports/alif/fatfs_port.c create mode 100644 ports/alif/irq.h create mode 100644 ports/alif/machine_pin.c create mode 100644 ports/alif/main.c create mode 100644 ports/alif/mcu/M55_HP_cfg.json create mode 100644 ports/alif/mcu/ensemble.ld.S create mode 100755 ports/alif/mcu/make-pins.py create mode 100644 ports/alif/mcu/pins_prefix.c create mode 100644 ports/alif/modalif.c create mode 100644 ports/alif/modalif.h create mode 100644 ports/alif/modmachine.c create mode 100644 ports/alif/modules/_boot.py create mode 100644 ports/alif/mpconfigport.h create mode 100644 ports/alif/mpconfigport.mk create mode 100644 ports/alif/mphalport.c create mode 100644 ports/alif/mphalport.h create mode 100644 ports/alif/mpuart.c create mode 100644 ports/alif/mpuart.h create mode 100644 ports/alif/msc_disk.c create mode 100644 ports/alif/ospi_flash.c create mode 100644 ports/alif/ospi_flash.h create mode 100644 ports/alif/ospi_xip_user.h create mode 100644 ports/alif/pendsv.c create mode 100644 ports/alif/pendsv.h create mode 100644 ports/alif/qstrdefsport.h create mode 100644 ports/alif/tinyusb_port/tusb_config.h create mode 100644 ports/alif/usbd.c diff --git a/ports/alif/Makefile b/ports/alif/Makefile new file mode 100644 index 0000000000000..2863ebe2c4d61 --- /dev/null +++ b/ports/alif/Makefile @@ -0,0 +1,261 @@ +################################################################################ +# Initial setup of Makefile environment + +BOARD ?= ALIF_ENSEMBLE +BOARD_DIR ?= boards/$(BOARD) +BUILD ?= build-$(BOARD) + +ifeq ($(wildcard $(BOARD_DIR)/.),) +$(error Invalid BOARD specified: $(BOARD_DIR)) +endif + +include ../../py/mkenv.mk +include mpconfigport.mk +include $(BOARD_DIR)/mpconfigboard.mk + +# qstr definitions (must come before including py.mk) +QSTR_DEFS += qstrdefsport.h + +# include py core make definitions +include $(TOP)/py/py.mk +include $(TOP)/extmod/extmod.mk + +################################################################################ +# Project specific settings and compiler/linker flags + +CROSS_COMPILE ?= arm-none-eabi- +GIT_SUBMODULES += lib/tinyusb lib/alif_ensemble-cmsis-dfp lib/alif-security-toolkit +PORT ?= /dev/ttyACM0 + +ALIF_TOOLS ?= ../../lib/alif-security-toolkit/toolkit +ALIF_DFP_REL_TOP ?= lib/alif_ensemble-cmsis-dfp +ALIF_DFP_REL_HERE ?= $(TOP)/lib/alif_ensemble-cmsis-dfp +CMSIS_DIR ?= $(TOP)/lib/cmsis/inc + +MCU_CORE ?= M55_HP +ALIF_CONFIG ?= mcu/$(MCU_CORE)_cfg.json +LD_FILE ?= mcu/ensemble.ld.S + +INC += -I. +INC += -I$(TOP) +INC += -I$(BUILD) +INC += -I$(BOARD_DIR) +INC += -I$(CMSIS_DIR) +INC += -I$(ALIF_DFP_REL_HERE)/drivers/include/ +INC += -I$(ALIF_DFP_REL_HERE)/ospi_xip/source/ospi +INC += -I$(ALIF_DFP_REL_HERE)/Device/common/config/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/common/include/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/config/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/include/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/$(MCU_SERIES)/$(MCU_VARIANT)/ +INC += -I$(TOP)/lib/tinyusb/src +INC += -Itinyusb_port + +GEN_PIN_MKPINS = mcu/make-pins.py +GEN_PIN_PREFIX = mcu/pins_prefix.c +GEN_PINS_BOARD_CSV = $(BOARD_DIR)/pins.csv +GEN_PINS_SRC = $(BUILD)/pins_board.c +GEN_PINS_HDR = $(HEADER_BUILD)/pins_board.h + +CFLAGS_FPU += -mfloat-abi=hard -mfpu=fpv5-d16 +CFLAGS_CORTEX_M55 += -mthumb -mcpu=cortex-m55 -mtune=cortex-m55 $(CFLAGS_FPU) + +CFLAGS += $(INC) -Wall -Werror -std=c99 $(CFLAGS_CORTEX_M55) -nostdlib +CFLAGS += -Wdouble-promotion -Wfloat-conversion +CFLAGS += -fdata-sections -ffunction-sections +CFLAGS += -D$(MCU_CORE) -DCORE_$(MCU_CORE) -DALIF_CMSIS_H="\"$(MCU_CORE).h\"" + +ifeq ($(MICROPY_FLOAT_IMPL),float) +CFLAGS += -fsingle-precision-constant +CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT +else +CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_DOUBLE +endif + +AFLAGS = -mthumb -march=armv8.1-m.main $(CFLAGS_FPU) + +LDFLAGS += -nostdlib +LDFLAGS += -T$(BUILD)/ensemble.ld -Map=$@.map --cref --gc-sections +LDFLAGS += --wrap=dcd_event_handler + +# Tune for Debugging or Optimization +ifeq ($(DEBUG), 1) +CFLAGS += -Og -ggdb3 +# Disable text compression in debug builds +MICROPY_ROM_TEXT_COMPRESSION = 0 +else +CFLAGS += -O2 -DNDEBUG +endif + +LIBS += "$(shell $(CC) $(CFLAGS) -print-libgcc-file-name)" + +JLINK_CMD = '\ +ExitOnError 1\n\ +Device $(JLINK_DEV)\n\ +SelectInterface SWD\n\ +Speed auto\n\ +Connect\n\ +Reset\n\ +ShowHWStatus\n\ +LoadFile "$(BUILD)/firmware_toc.bin",0x8057f1c0\n\ +LoadFile "$(BUILD)/firmware.bin",0x80000000\n\ +Reset\n\ +Exit' + +################################################################################ +# Source files and libraries + +SRC_O += \ + shared/runtime/gchelper_thumb2.o + +SRC_C = \ + alif_flash.c \ + fatfs_port.c \ + machine_pin.c \ + main.c \ + modalif.c \ + mphalport.c \ + mpuart.c \ + msc_disk.c \ + ospi_flash.c \ + pendsv.c \ + usbd.c \ + $(wildcard $(BOARD_DIR)/*.c) + +ifeq ($(MICROPY_FLOAT_IMPL),float) +LIBM_SRC_C += $(SRC_LIB_LIBM_C) +LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C) +$(BUILD)/lib/libm/%.o: CFLAGS += -Wno-maybe-uninitialized +else +LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_C) +LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_SQRT_HW_C) +$(BUILD)/lib/libm_dbl/%.o: CFLAGS += -Wno-maybe-uninitialized +endif + +SHARED_SRC_C += $(addprefix shared/,\ + libc/string0.c \ + netutils/dhcpserver.c \ + netutils/netutils.c \ + netutils/trace.c \ + readline/readline.c \ + runtime/gchelper_native.c \ + runtime/interrupt_char.c \ + runtime/mpirq.c \ + runtime/pyexec.c \ + runtime/softtimer.c \ + runtime/stdout_helpers.c \ + runtime/sys_stdio_mphal.c \ + timeutils/timeutils.c \ + tinyusb/mp_usbd.c \ + tinyusb/mp_usbd_cdc.c \ + tinyusb/mp_usbd_descriptor.c \ + ) + +DRIVERS_SRC_C += $(addprefix drivers/,\ + bus/softspi.c \ + bus/softqspi.c \ + memory/spiflash.c \ + dht/dht.c \ + ) + +TINYUSB_SRC_C += \ + lib/tinyusb/src/tusb.c \ + lib/tinyusb/src/class/cdc/cdc_device.c \ + lib/tinyusb/src/class/msc/msc_device.c \ + lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/usbd.c \ + lib/tinyusb/src/device/usbd_control.c \ + tinyusb_port/tusb_alif_dcd.c \ + +ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ + Device/common/source/clk.c \ + Device/common/source/mpu_M55.c \ + Device/common/source/system_M55.c \ + Device/common/source/system_utils.c \ + Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ + drivers/source/pinconf.c \ + drivers/source/uart.c \ + ospi_xip/source/ospi/ospi_drv.c \ + ) + +$(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 + +# List of sources for qstr extraction +SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) $(GEN_PINS_SRC) + +OBJ += $(PY_O) +OBJ += $(addprefix $(BUILD)/, $(SRC_O)) +OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(LIBM_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(TINYUSB_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(ALIF_SRC_C:.c=.o)) +OBJ += $(GEN_PINS_SRC:.c=.o) + +################################################################################ +# Main targets + +.DELETE_ON_ERROR: + +.PHONY: all +all: $(BUILD)/firmware_toc.bin + +$(BUILD)/ensemble.ld: $(LD_FILE) + $(ECHO) "Preprocess linker script $@" + $(Q)$(CPP) -P -E $(CFLAGS) $^ > $@ + +$(BUILD)/firmware.elf: $(OBJ) $(BUILD)/ensemble.ld + $(ECHO) "Link $@" + $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + $(Q)$(SIZE) $@ + +$(BUILD)/firmware.bin: $(BUILD)/firmware.elf + $(Q)$(OBJCOPY) -Obinary $^ $(BUILD)/firmware.bin + +$(BUILD)/firmware_toc.bin: $(BUILD)/firmware.bin + $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \ + --filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \ + --output-dir $(BUILD) \ + --firmware-dir $(BUILD) \ + --output $@ + +.PHONY: deploy +deploy: $(BUILD)/firmware_toc.bin + $(ECHO) "Writing $< to the board" + $(Q)python $(ALIF_TOOLS)/app-write-mram.py \ + --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ + --port $(PORT) \ + --pad \ + --images file:$(BUILD)/application_package.ds + +.PHONY: deploy-jlink +deploy-jlink: $(BUILD)/firmware_toc.bin + $(Q)echo -e $(JLINK_CMD) | $(JLINK_EXE) + +.PHONY: maintenance +maintenance: + $(Q)python $(ALIF_TOOLS)/maintenance.py \ + --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ + --port $(PORT) + +.PHONY: update-system-package +update-system-package: + $(Q)python $(ALIF_TOOLS)/updateSystemPackage.py \ + --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ + --port $(PORT) + +################################################################################ +# Remaining make rules + +# Use a pattern rule here so that make will only call make-pins.py once to make +# both pins_board.c and pins_board.h +$(BUILD)/%_board.c $(HEADER_BUILD)/%_board.h: $(BOARD_DIR)/%.csv $(GEN_PIN_MKPINS) $(GEN_PIN_PREFIX) | $(HEADER_BUILD) + $(ECHO) "GEN $@" + $(Q)$(PYTHON) $(GEN_PIN_MKPINS) \ + --board-csv $(GEN_PINS_BOARD_CSV) \ + --prefix $(GEN_PIN_PREFIX) \ + --output-source $(GEN_PINS_SRC) \ + --output-header $(GEN_PINS_HDR) + +include $(TOP)/py/mkrules.mk diff --git a/ports/alif/README.md b/ports/alif/README.md new file mode 100644 index 0000000000000..824a63da186d2 --- /dev/null +++ b/ports/alif/README.md @@ -0,0 +1,21 @@ +MicroPython port to Alif Ensemble MCUs +====================================== + +This is a port of MicroPython to the Alif Ensemble series of microcontrollers. + +Initial development of this Alif port was sponsored by OpenMV LLC. + +Features currently supported: +- UART REPL. +- TinyUSB with CDC and MSC device support. +- Octal SPI flash with XIP mode. +- machine.Pin support with named pins. +- machine.UART, machine.SPI, machine.I2C, machine.RTC peripherals. +- WiFi and Bluetooth using cyw43. +- Dual core support of the HE and HP cores using Open-AMP. +- Low power modes. + +The following more advanced features will follow later: +- Ethernet support. +- SDRAM support. +- Other machine modules. diff --git a/ports/alif/alif_flash.c b/ports/alif/alif_flash.c new file mode 100644 index 0000000000000..e3b2125800ea2 --- /dev/null +++ b/ports/alif/alif_flash.c @@ -0,0 +1,160 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "py/runtime.h" +#include "extmod/vfs.h" +#include "modalif.h" +#include "ospi_flash.h" + +typedef struct _alif_flash_obj_t { + mp_obj_base_t base; + uint32_t flash_base_addr; + uint32_t flash_size; +} alif_flash_obj_t; + +static alif_flash_obj_t alif_flash_obj = { + .base = { &alif_flash_type }, + .flash_base_addr = MICROPY_HW_FLASH_STORAGE_BASE_ADDR, + .flash_size = MICROPY_HW_FLASH_STORAGE_BYTES, +}; + +static mp_obj_t alif_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Parse arguments + enum { ARG_start, ARG_len }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (args[ARG_start].u_int == -1 && args[ARG_len].u_int == -1) { + // Default singleton object that accesses entire flash + return MP_OBJ_FROM_PTR(&alif_flash_obj); + } + + alif_flash_obj_t *self = mp_obj_malloc(alif_flash_obj_t, &alif_flash_type); + + mp_int_t start = args[ARG_start].u_int; + if (start == -1) { + start = 0; + } else if (!(0 <= start && start < MICROPY_HW_FLASH_STORAGE_BYTES && start % MICROPY_HW_FLASH_BLOCK_SIZE_BYTES == 0)) { + mp_raise_ValueError(NULL); + } + + mp_int_t len = args[ARG_len].u_int; + if (len == -1) { + len = MICROPY_HW_FLASH_STORAGE_BYTES - start; + } else if (!(0 < len && start + len <= MICROPY_HW_FLASH_STORAGE_BYTES && len % MICROPY_HW_FLASH_BLOCK_SIZE_BYTES == 0)) { + mp_raise_ValueError(NULL); + } + + self->flash_base_addr = MICROPY_HW_FLASH_STORAGE_BASE_ADDR + start; + self->flash_size = len; + + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t alif_flash_readblocks(size_t n_args, const mp_obj_t *args) { + alif_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = mp_obj_get_int(args[1]) * MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); + if (n_args == 4) { + offset += mp_obj_get_int(args[3]); + } + int ret = ospi_flash_read(self->flash_base_addr + offset, bufinfo.len, bufinfo.buf); + mp_event_handle_nowait(); + return MP_OBJ_NEW_SMALL_INT(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alif_flash_readblocks_obj, 3, 4, alif_flash_readblocks); + +static mp_obj_t alif_flash_writeblocks(size_t n_args, const mp_obj_t *args) { + alif_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = mp_obj_get_int(args[1]) * MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + if (n_args == 3) { + uint32_t addr = self->flash_base_addr + offset; + int32_t len = bufinfo.len; + while (len > 0) { + int ret = ospi_flash_erase_sector(addr); + mp_event_handle_nowait(); + if (ret < 0) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + addr += MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + len -= MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + } + } else { + offset += mp_obj_get_int(args[3]); + } + int ret = ospi_flash_write(self->flash_base_addr + offset, bufinfo.len, bufinfo.buf); + mp_event_handle_nowait(); + return MP_OBJ_NEW_SMALL_INT(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alif_flash_writeblocks_obj, 3, 4, alif_flash_writeblocks); + +static mp_obj_t alif_flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) { + alif_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t cmd = mp_obj_get_int(cmd_in); + switch (cmd) { + case MP_BLOCKDEV_IOCTL_INIT: + return MP_OBJ_NEW_SMALL_INT(0); + case MP_BLOCKDEV_IOCTL_DEINIT: + return MP_OBJ_NEW_SMALL_INT(0); + case MP_BLOCKDEV_IOCTL_SYNC: + return MP_OBJ_NEW_SMALL_INT(0); + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + return MP_OBJ_NEW_SMALL_INT(self->flash_size / MICROPY_HW_FLASH_BLOCK_SIZE_BYTES); + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + return MP_OBJ_NEW_SMALL_INT(MICROPY_HW_FLASH_BLOCK_SIZE_BYTES); + case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: { + uint32_t offset = mp_obj_get_int(arg_in) * MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + int ret = ospi_flash_erase_sector(self->flash_base_addr + offset); + return MP_OBJ_NEW_SMALL_INT(ret); + } + default: + return mp_const_none; + } +} +static MP_DEFINE_CONST_FUN_OBJ_3(alif_flash_ioctl_obj, alif_flash_ioctl); + +static const mp_rom_map_elem_t alif_flash_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&alif_flash_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&alif_flash_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&alif_flash_ioctl_obj) }, +}; +static MP_DEFINE_CONST_DICT(alif_flash_locals_dict, alif_flash_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + alif_flash_type, + MP_QSTR_Flash, + MP_TYPE_FLAG_NONE, + make_new, alif_flash_make_new, + locals_dict, &alif_flash_locals_dict + ); diff --git a/ports/alif/boards/manifest.py b/ports/alif/boards/manifest.py new file mode 100644 index 0000000000000..148f1d6a6eb5e --- /dev/null +++ b/ports/alif/boards/manifest.py @@ -0,0 +1,5 @@ +freeze("$(PORT_DIR)/modules") +include("$(MPY_DIR)/extmod/asyncio") +require("dht") +require("neopixel") +require("onewire") diff --git a/ports/alif/fatfs_port.c b/ports/alif/fatfs_port.c new file mode 100644 index 0000000000000..5883c9f3b9447 --- /dev/null +++ b/ports/alif/fatfs_port.c @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "lib/oofatfs/ff.h" + +DWORD get_fattime(void) { + // TODO + int year = 2024; + int month = 1; + int day = 1; + int hour = 0; + int min = 0; + int sec = 0; + return ((year - 1980) << 25) | (month << 21) | (day << 16) | (hour << 11) | (min << 5) | (sec / 2); +} diff --git a/ports/alif/irq.h b/ports/alif/irq.h new file mode 100644 index 0000000000000..723f89f1d907e --- /dev/null +++ b/ports/alif/irq.h @@ -0,0 +1,84 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2023 Damien P. George + * Copyright (c) 2024 OpenMV LLC. + * + * 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_ALIF_IRQ_H +#define MICROPY_INCLUDED_ALIF_IRQ_H + +#include +#include ALIF_CMSIS_H + +// IRQ priority definitions. +// +// The M55-HP CPU has __NVIC_PRIO_BITS==8 bits for setting the IRQ priority. +// It uses NVIC_SetPriorityGrouping(0) which is 7 bits for preempt priority +// and 1 bit for the sub-priority. +// +// Lower number implies higher interrupt priority. + +#define NVIC_PRIORITYGROUP_7 ((uint32_t)0x00000000U) +#define IRQ_PRI_SYSTEM_TICK NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 0, 0) +#define IRQ_PRI_QUIET_TIMING NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) +#define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) +#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) +#define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) + +// these states correspond to values from query_irq, enable_irq and disable_irq +#define IRQ_STATE_DISABLED (0x00000001) +#define IRQ_STATE_ENABLED (0x00000000) + +static inline uint32_t query_irq(void) { + return __get_PRIMASK(); +} + +static inline void enable_irq(uint32_t state) { + __set_PRIMASK(state); +} + +static inline uint32_t disable_irq(void) { + uint32_t state = __get_PRIMASK(); + __disable_irq(); + return state; +} + +// irqs with a priority value greater or equal to "pri" will be disabled +static inline uint32_t raise_irq_pri(uint32_t pri) { + uint32_t basepri = __get_BASEPRI(); + // If non-zero, the processor does not process any exception with a + // priority value greater than or equal to BASEPRI. + // When writing to BASEPRI_MAX the write goes to BASEPRI only if either: + // - Rn is non-zero and the current BASEPRI value is 0 + // - Rn is non-zero and less than the current BASEPRI value + pri <<= (8 - __NVIC_PRIO_BITS); + __ASM volatile ("msr basepri_max, %0" : : "r" (pri) : "memory"); + return basepri; +} + +// "basepri" should be the value returned from raise_irq_pri +static inline void restore_irq_pri(uint32_t basepri) { + __set_BASEPRI(basepri); +} + +#endif // MICROPY_INCLUDED_ALIF_IRQ_H diff --git a/ports/alif/machine_pin.c b/ports/alif/machine_pin.c new file mode 100644 index 0000000000000..02c5e482adb89 --- /dev/null +++ b/ports/alif/machine_pin.c @@ -0,0 +1,298 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "extmod/modmachine.h" +#include "extmod/virtpin.h" +#include "shared/runtime/mpirq.h" + +extern const mp_obj_dict_t machine_pin_cpu_pins_locals_dict; +extern const mp_obj_dict_t machine_pin_board_pins_locals_dict; + +static const machine_pin_obj_t *machine_pin_find_named(const mp_obj_dict_t *named_pins, mp_obj_t name) { + const mp_map_t *named_map = &named_pins->map; + mp_map_elem_t *named_elem = mp_map_lookup((mp_map_t *)named_map, name, MP_MAP_LOOKUP); + if (named_elem != NULL && named_elem->value != NULL) { + return MP_OBJ_TO_PTR(named_elem->value); + } + return NULL; +} + +const machine_pin_obj_t *machine_pin_find(mp_obj_t pin) { + // Is already a object of the proper type + if (mp_obj_is_type(pin, &machine_pin_type)) { + return MP_OBJ_TO_PTR(pin); + } + if (mp_obj_is_str(pin)) { + // Try to find the pin in the board pins first. + const machine_pin_obj_t *self = machine_pin_find_named(&machine_pin_board_pins_locals_dict, pin); + if (self != NULL) { + return self; + } + + // If not found, try to find the pin in the cpu pins. + self = machine_pin_find_named(&machine_pin_cpu_pins_locals_dict, pin); + if (self != NULL) { + return self; + } + + // Pin name not found. + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unknown named pin \"%s\""), mp_obj_str_get_str(pin)); + } + mp_raise_ValueError(MP_ERROR_TEXT("invalid pin")); +} + +static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_pin_obj_t *self = self_in; + + uint8_t alt_func, pad_ctrl; + pinconf_get(self->port, self->pin, &alt_func, &pad_ctrl); + + qstr mode_qst; + if (gpio_get_direction(self->gpio, self->pin) == GPIO_PIN_DIR_INPUT) { + mode_qst = MP_QSTR_IN; + } else { + if (pad_ctrl & PADCTRL_DRIVER_OPEN_DRAIN) { + mode_qst = MP_QSTR_OPEN_DRAIN; + } else { + mode_qst = MP_QSTR_OUT; + } + } + mp_printf(print, "Pin(%q, mode=%q", self->name, mode_qst); + uint8_t pad_ctrl_pull = pad_ctrl & (PADCTRL_DRIVER_DISABLED_PULL_DOWN | PADCTRL_DRIVER_DISABLED_PULL_UP); + if (pad_ctrl_pull == PADCTRL_DRIVER_DISABLED_PULL_UP) { + mp_printf(print, ", pull=%q", MP_QSTR_PULL_UP); + } else if (pad_ctrl_pull == PADCTRL_DRIVER_DISABLED_PULL_DOWN) { + mp_printf(print, ", pull=%q", MP_QSTR_PULL_DOWN); + } + if (alt_func != PINMUX_ALTERNATE_FUNCTION_0) { + mp_printf(print, ", alt=%u", alt_func); + } + mp_printf(print, ")"); +} + +enum { + ARG_mode, ARG_pull, ARG_value, ARG_alt +}; +static const mp_arg_t allowed_args[] = { + {MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + {MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + {MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + {MP_QSTR_alt, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0}}, +}; + +static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // get initial value of pin (only valid for OUT and OPEN_DRAIN modes) + int value = -1; + if (args[ARG_value].u_obj != mp_const_none) { + value = mp_obj_is_true(args[ARG_value].u_obj); + } + + // configure mode + if (args[ARG_mode].u_obj != mp_const_none) { + mp_int_t mode = mp_obj_get_int(args[ARG_mode].u_obj); + if (mode == MP_HAL_PIN_MODE_INPUT) { + mp_hal_pin_input(self); + } else if (mode == MP_HAL_PIN_MODE_OUTPUT) { + if (value != -1) { + // set initial output value before configuring mode + mp_hal_pin_write(self, value); + } + mp_hal_pin_output(self); + } else if (mode == MP_HAL_PIN_MODE_OPEN_DRAIN) { + if (value != -1) { + // set initial output value before configuring mode + mp_hal_pin_write(self, value); + } + mp_hal_pin_open_drain(self); + } + } + + // Configure pull (unconditionally because None means no-pull). + uint32_t pull = 0; + if (args[ARG_pull].u_obj != mp_const_none) { + pull = mp_obj_get_int(args[ARG_pull].u_obj); + } + uint8_t alt_func; + uint8_t pad_ctrl; + pinconf_get(self->port, self->pin, &alt_func, &pad_ctrl); + alt_func = PINMUX_ALTERNATE_FUNCTION_0; + pad_ctrl |= PADCTRL_READ_ENABLE; + pad_ctrl &= ~(PADCTRL_DRIVER_DISABLED_PULL_DOWN | PADCTRL_DRIVER_DISABLED_PULL_UP); + if (pull & MP_HAL_PIN_PULL_UP) { + pad_ctrl |= PADCTRL_DRIVER_DISABLED_PULL_UP; + } + if (pull & MP_HAL_PIN_PULL_DOWN) { + pad_ctrl |= PADCTRL_DRIVER_DISABLED_PULL_DOWN; + } + pinconf_set(self->port, self->pin, alt_func, pad_ctrl); + + return mp_const_none; +} + +// constructor(id, ...) +mp_obj_t mp_pin_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); + + const machine_pin_obj_t *self = machine_pin_find(args[0]); + + if (n_args > 1 || n_kw > 0) { + // pin mode given, so configure this GPIO + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args); + } + return MP_OBJ_FROM_PTR(self); +} + +// fast method for getting/setting pin value +static mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + machine_pin_obj_t *self = self_in; + if (n_args == 0) { + // get pin + return MP_OBJ_NEW_SMALL_INT(mp_hal_pin_read(self)); + } else { + // set pin + bool value = mp_obj_is_true(args[0]); + mp_hal_pin_write(self, value); + return mp_const_none; + } +} + +// pin.init(mode, pull) +static mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init); + +// pin.value([value]) +static mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) { + return machine_pin_call(args[0], n_args - 1, 0, args + 1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value); + +// pin.low() +static mp_obj_t machine_pin_low(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_hal_pin_low(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_low_obj, machine_pin_low); + +// pin.high() +static mp_obj_t machine_pin_high(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_hal_pin_high(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_high_obj, machine_pin_high); + +// pin.toggle() +static mp_obj_t machine_pin_toggle(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + gpio_toggle_value(self->gpio, self->pin); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle); + +static MP_DEFINE_CONST_OBJ_TYPE( + pin_cpu_pins_obj_type, + MP_QSTR_cpu, + MP_TYPE_FLAG_NONE, + locals_dict, &machine_pin_cpu_pins_locals_dict + ); + +static MP_DEFINE_CONST_OBJ_TYPE( + pin_board_pins_obj_type, + MP_QSTR_board, + MP_TYPE_FLAG_NONE, + locals_dict, &machine_pin_board_pins_locals_dict + ); + +static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_low), MP_ROM_PTR(&machine_pin_low_obj) }, + { MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&machine_pin_high_obj) }, + { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_low_obj) }, + { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_high_obj) }, + { MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) }, + + // class attributes + { MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&pin_board_pins_obj_type) }, + { MP_ROM_QSTR(MP_QSTR_cpu), MP_ROM_PTR(&pin_cpu_pins_obj_type) }, + + // class constants + { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(MP_HAL_PIN_MODE_INPUT) }, + { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(MP_HAL_PIN_MODE_OUTPUT) }, + { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(MP_HAL_PIN_MODE_OPEN_DRAIN) }, + { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(MP_HAL_PIN_PULL_UP) }, + { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(MP_HAL_PIN_PULL_DOWN) }, +}; +static MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table); + +static mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + (void)errcode; + machine_pin_obj_t *self = self_in; + + switch (request) { + case MP_PIN_READ: { + return mp_hal_pin_read(self); + } + case MP_PIN_WRITE: { + mp_hal_pin_write(self, arg); + return 0; + } + } + return -1; +} + +static const mp_pin_p_t pin_pin_p = { + .ioctl = pin_ioctl, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_pin_type, + MP_QSTR_Pin, + MP_TYPE_FLAG_NONE, + make_new, mp_pin_make_new, + print, machine_pin_print, + call, machine_pin_call, + protocol, &pin_pin_p, + locals_dict, &machine_pin_locals_dict + ); + +mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) { + return machine_pin_find(obj); +} diff --git a/ports/alif/main.c b/ports/alif/main.c new file mode 100644 index 0000000000000..0eb43a38268a6 --- /dev/null +++ b/ports/alif/main.c @@ -0,0 +1,145 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/compile.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/stackctrl.h" +#include "shared/readline/readline.h" +#include "shared/runtime/gchelper.h" +#include "shared/runtime/pyexec.h" +#include "shared/tinyusb/mp_usbd.h" +#include "tusb.h" +#include "mpuart.h" +#include "ospi_flash.h" +#include "pendsv.h" + +extern uint8_t __StackTop, __StackLimit; +extern uint8_t __GcHeapStart, __GcHeapEnd; + +NORETURN void panic(const char *msg) { + mp_hal_stdout_tx_strn("\nFATAL ERROR:\n", 14); + mp_hal_stdout_tx_strn(msg, strlen(msg)); + for (;;) { + *(volatile uint32_t *)0x4900C000 ^= 9; + for (volatile uint delay = 0; delay < 10000000; delay++) { + } + } +} + +void _start(void) { + SysTick_Config(SystemCoreClock / 1000); + + MICROPY_BOARD_STARTUP(); + + pendsv_init(); + + MICROPY_BOARD_EARLY_INIT(); + + #if MICROPY_HW_ENABLE_UART_REPL + mp_uart_init(); + #endif + + if (ospi_flash_init() != 0) { + MICROPY_BOARD_FATAL_ERROR("ospi_init failed"); + } + + #if MICROPY_HW_ENABLE_USBDEV + NVIC_ClearPendingIRQ(USB_IRQ_IRQn); + NVIC_SetPriority(USB_IRQ_IRQn, IRQ_PRI_USB); + #endif + + // Initialise stack extents and GC heap. + mp_stack_set_top(&__StackTop); + mp_stack_set_limit(&__StackTop - &__StackLimit - 1024); + gc_init(&__GcHeapStart, &__GcHeapEnd); + + for (;;) { + // Initialise MicroPython runtime. + mp_init(); + + // Initialise sub-systems. + readline_init0(); + + // Execute _boot.py to set up the filesystem. + pyexec_frozen_module("_boot.py", false); + + // Execute user scripts. + int ret = pyexec_file_if_exists("boot.py"); + #if MICROPY_HW_ENABLE_USBDEV + mp_usbd_init(); + #endif + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL && ret != 0) { + ret = pyexec_file_if_exists("main.py"); + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + } + + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } + + soft_reset_exit: + mp_printf(MP_PYTHON_PRINTER, "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(); +} + +void nlr_jump_fail(void *val) { + mp_printf(&mp_plat_print, "FATAL: uncaught exception %p\n", val); + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(val)); + for (;;) { + __WFE(); + } +} + +#ifndef NDEBUG +void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) { + printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); + panic("Assertion failed"); +} +#endif diff --git a/ports/alif/mcu/M55_HP_cfg.json b/ports/alif/mcu/M55_HP_cfg.json new file mode 100644 index 0000000000000..228ddbf6c9464 --- /dev/null +++ b/ports/alif/mcu/M55_HP_cfg.json @@ -0,0 +1,14 @@ +{ + "USER_APP": { + "binary": "firmware.bin", + "mramAddress": "0x80000000", + "version": "1.0.0", + "cpu_id": "M55_HP", + "flags": ["boot"], + "signed": false + }, + "DEVICE": { + "binary": "app-device-config.json", + "version" : "0.5.00" + } +} diff --git a/ports/alif/mcu/ensemble.ld.S b/ports/alif/mcu/ensemble.ld.S new file mode 100644 index 0000000000000..198641ba3dcba --- /dev/null +++ b/ports/alif/mcu/ensemble.ld.S @@ -0,0 +1,165 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 OpenMV LLC. + * + * 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. + */ + +// Entry Point +ENTRY(Reset_Handler) + +MEMORY +{ + ROM (rx) : ORIGIN = 0x80000000, LENGTH = 0x0057F000 + ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 + #ifdef CORE_M55_HP + DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00100000 + #else + DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00040000 + #endif + SRAM0 (rwx) : ORIGIN = 0x02000000, LENGTH = 0x00400000 + SRAM1 (rwx) : ORIGIN = 0x08000000, LENGTH = 0x00280000 +} + +__STACK_SIZE = 0x00004000; +__HEAP_SIZE = 0x00004000; +__MP_HEAP_SIZE = 0x00040000; + +SECTIONS +{ + .text : ALIGN(16) + { + KEEP(*(.vectors)) + . = ALIGN(4); + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(16); + } > ROM + + .copy.table : ALIGN(4) + { + __copy_table_start__ = .; + LONG ( LOADADDR(.data) ) + LONG ( ADDR(.data) ) + LONG ( SIZEOF(.data)/4 ) + __copy_table_end__ = .; + . = ALIGN(16); + } > ROM + + .zero.table : ALIGN(4) + { + __zero_table_start__ = .; + LONG (ADDR(.bss)) + LONG (SIZEOF(.bss)/4) + LONG (ADDR(.bss.sram0)) + LONG (SIZEOF(.bss.sram0)/4) + __zero_table_end__ = .; + . = ALIGN(16); + } > ROM + + .data : ALIGN(8) + { + *(.data) + . = ALIGN(8); + *(.data.*) + . = ALIGN(16); + } > DTCM AT > ROM + + /* Peripherals in expansion master 0 (USB, Ethernet, SD/MMC) + are by default configured as non-secure, so they don't + have access to DTCMs. This can be fixed in the ToC by allowing + access to DTCMs to all bus masters, for now these peripherals + should place buffers in regular SRAM */ + .bss.sram0 (NOLOAD) : ALIGN(4) + { + * (.bss.sram0*) + } > SRAM0 + + .bss : ALIGN(4) + { + __bss_start__ = .; + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > DTCM + + .heap (NOLOAD) : ALIGN(4) + { + __end__ = .; + PROVIDE(end = .); + . = . + __HEAP_SIZE; + . = ALIGN(4); + __HeapLimit = .; + + /* MicroPython GC heap */ + . = ALIGN(16); + __GcHeapStart = .; + . = . + __MP_HEAP_SIZE; + __GcHeapEnd = .; + } > DTCM + + .stack (NOLOAD) : ALIGN(4) + { + __StackLimit = .; + . = . + __STACK_SIZE; + . = ALIGN(4); + __StackTop = .; + } > DTCM + PROVIDE(__stack = __StackTop); + + .init_fini_arrays : ALIGN(16) + { + KEEP(*(.init)) + KEEP(*(.fini)) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.eh_frame*)) + . = ALIGN(16); + } > ROM + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/ports/alif/mcu/make-pins.py b/ports/alif/mcu/make-pins.py new file mode 100755 index 0000000000000..b49db4cfe49bb --- /dev/null +++ b/ports/alif/mcu/make-pins.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +import os +import re +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../../tools")) +import boardgen + +NUM_PORTS = 16 +NUM_PINS_PER_PORT = 8 + + +class AlifPin(boardgen.Pin): + # Emit the struct which contains the pin instance. + def definition(self): + port, pin = self.name()[1:].split("_") + base = "LPGPIO_BASE" if port == "15" else "GPIO{}_BASE".format(port) + return ( + "{{ " + ".base = {{ .type = &machine_pin_type }}, " + ".gpio = (GPIO_Type *){base}, " + ".port = PORT_{port}, " + ".pin = PIN_{pin}, " + ".name = MP_QSTR_P{port}_{pin} " + "}}".format(port=port, pin=pin, base=base) + ) + + # Alif cpu names must be "Pn_m". + @staticmethod + def validate_cpu_pin_name(cpu_pin_name): + boardgen.Pin.validate_cpu_pin_name(cpu_pin_name) + + if not (m := re.match("P([0-9]){1,2}_([0-9])", cpu_pin_name)): + raise boardgen.PinGeneratorError( + "Invalid cpu pin name '{}', must be 'Pn_m'".format(cpu_pin_name) + ) + + port = int(m.group(1)) + pin = int(m.group(2)) + if not (0 <= port < NUM_PORTS and 0 <= pin < NUM_PINS_PER_PORT): + raise boardgen.PinGeneratorError("Unknown cpu pin '{}'".format(cpu_pin_name)) + + +class AlifPinGenerator(boardgen.PinGenerator): + def __init__(self): + # Use custom pin type above. + super().__init__(pin_type=AlifPin) + + # Pre-define the pins (i.e. don't require them to be listed in pins.csv). + for i in range(NUM_PORTS): + for j in range(NUM_PINS_PER_PORT): + self.add_cpu_pin("P{}_{}".format(i, j)) + + # Only use pre-defined cpu pins (do not let board.csv create them). + def find_pin_by_cpu_pin_name(self, cpu_pin_name, create=True): + return super().find_pin_by_cpu_pin_name(cpu_pin_name, create=False) + + def cpu_table_size(self): + return "{} * {}".format(NUM_PORTS, NUM_PINS_PER_PORT) + + +if __name__ == "__main__": + AlifPinGenerator().main() diff --git a/ports/alif/mcu/pins_prefix.c b/ports/alif/mcu/pins_prefix.c new file mode 100644 index 0000000000000..99fef5939a0ce --- /dev/null +++ b/ports/alif/mcu/pins_prefix.c @@ -0,0 +1,2 @@ +#include "py/mphal.h" +#include "extmod/modmachine.h" diff --git a/ports/alif/modalif.c b/ports/alif/modalif.c new file mode 100644 index 0000000000000..69b7e6c5c8e87 --- /dev/null +++ b/ports/alif/modalif.c @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "py/runtime.h" +#include "modalif.h" + +static const mp_rom_map_elem_t alif_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_alif) }, + { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&alif_flash_type) }, + #if MICROPY_HW_USB_MSC + // Attribute to indicate USB MSC is enabled. + { MP_ROM_QSTR(MP_QSTR_usb_msc), MP_ROM_TRUE }, + #endif +}; +static MP_DEFINE_CONST_DICT(alif_module_globals, alif_module_globals_table); + +const mp_obj_module_t mp_module_alif = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&alif_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_alif, mp_module_alif); diff --git a/ports/alif/modalif.h b/ports/alif/modalif.h new file mode 100644 index 0000000000000..5812d6ba54597 --- /dev/null +++ b/ports/alif/modalif.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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_ALIF_MODALIF_H +#define MICROPY_INCLUDED_ALIF_MODALIF_H + +#include "py/obj.h" + +extern const mp_obj_type_t alif_flash_type; + +#endif // MICROPY_INCLUDED_ALIF_MODALIF_H diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c new file mode 100644 index 0000000000000..c4102225dabfc --- /dev/null +++ b/ports/alif/modmachine.c @@ -0,0 +1,79 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ + +// This file is never compiled standalone, it's included directly from +// extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE. + +#define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ + { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ + +static void mp_machine_idle(void) { + mp_event_wait_indefinite(); +} + +static mp_obj_t mp_machine_unique_id(void) { + return mp_obj_new_bytes((const uint8_t *)"ABCD", 4); +} + +NORETURN static void mp_machine_reset(void) { + NVIC_SystemReset(); +} + +NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { + __disable_irq(); + + MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); + + while (1) { + ; + } +} + +static mp_int_t mp_machine_reset_cause(void) { + // TODO + return 0; +} + +static mp_obj_t mp_machine_get_freq(void) { + return MP_OBJ_NEW_SMALL_INT(SystemCoreClock); +} + +static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { + mp_raise_NotImplementedError(NULL); +} + +static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { + mp_int_t delay = -1; + if (n_args == 1) { + delay = mp_obj_get_int(args[0]); + } + mp_hal_delay_ms(delay); +} + +NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { + mp_machine_lightsleep(n_args, args); + mp_machine_reset(); +} diff --git a/ports/alif/modules/_boot.py b/ports/alif/modules/_boot.py new file mode 100644 index 0000000000000..36044b461e662 --- /dev/null +++ b/ports/alif/modules/_boot.py @@ -0,0 +1,23 @@ +import sys, os, alif + + +bdev = alif.Flash() +if hasattr(alif, "usb_msc"): + try: + # This may fail on VfsFat construction, or mount. + os.mount(os.VfsFat(bdev), "/flash") + except: + os.VfsFat.mkfs(bdev) + os.mount(os.VfsFat(bdev), "/flash") +else: + try: + os.mount(os.VfsLfs2(bdev, progsize=256), "/flash") + except: + os.VfsLfs2.mkfs(bdev, progsize=256) + os.mount(os.VfsLfs2(bdev, progsize=256), "/flash") + +sys.path.append("/flash") +sys.path.append("/flash/lib") +os.chdir("/flash") + +del sys, os, alif, bdev diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h new file mode 100644 index 0000000000000..2d8e3b2a09dbe --- /dev/null +++ b/ports/alif/mpconfigport.h @@ -0,0 +1,155 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ + +// Options controlling how MicroPython is built, overriding defaults in py/mpconfig.h + +#include +#include // for alloca() + +// board specific definitions +#include "mpconfigboard.h" + +#ifndef MICROPY_CONFIG_ROM_LEVEL +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) +#endif + +#define MICROPY_HW_ENABLE_UART_REPL (1) // useful if there is no USB +#define MICROPY_HW_ENABLE_USBDEV (1) + +#ifndef MICROPY_HW_USB_PRODUCT_FS_STRING +#define MICROPY_HW_USB_PRODUCT_FS_STRING "Board in HS mode" +#endif +#define MICROPY_HW_USB_CDC (1) +#define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) +#ifndef MICROPY_HW_USB_MSC +#define MICROPY_HW_USB_MSC (0) +#endif +#ifndef MICROPY_HW_USB_VID +#define MICROPY_HW_USB_VID (0xf055) +#endif +#ifndef MICROPY_HW_USB_PID +#define MICROPY_HW_USB_PID (0x9802) // interface has CDC only +#endif + +#define MICROPY_HW_FLASH_BLOCK_SIZE_BYTES (4096) + +// Memory allocation policies +#ifndef MICROPY_ALLOC_GC_STACK_SIZE +#define MICROPY_ALLOC_GC_STACK_SIZE (128) +#endif +#define MICROPY_ALLOC_PATH_MAX (128) +#define MICROPY_QSTR_BYTES_IN_HASH (1) + +// MicroPython emitters +#define MICROPY_PERSISTENT_CODE_LOAD (1) +#define MICROPY_EMIT_THUMB (1) +#define MICROPY_EMIT_THUMB_ARMV7M (1) +#define MICROPY_EMIT_INLINE_THUMB (1) +#define MICROPY_EMIT_INLINE_THUMB_FLOAT (1) + +// Optimisations +#define MICROPY_OPT_COMPUTED_GOTO (1) + +// Python internal features +#define MICROPY_READER_VFS (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_SCHEDULER_DEPTH (8) +#define MICROPY_SCHEDULER_STATIC_NODES (1) +#define MICROPY_USE_INTERNAL_ERRNO (1) + +// Fine control over Python builtins, classes, modules, etc +#define MICROPY_PY_SYS_PLATFORM "alif" + +// Extended modules +#define MICROPY_EPOCH_IS_1970 (1) +#define MICROPY_PY_OS_DUPTERM (1) +#define MICROPY_PY_OS_SEP (1) +#define MICROPY_PY_OS_SYNC (1) +#define MICROPY_PY_OS_UNAME (1) +#define MICROPY_PY_TIME (1) +#define MICROPY_PY_MACHINE (1) +#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/alif/modmachine.c" +#define MICROPY_PY_MACHINE_RESET (1) +#define MICROPY_PY_MACHINE_BOOTLOADER (1) +#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1) +#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1) +#define MICROPY_PY_MACHINE_DHT_READINTO (1) +#define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_SOFTI2C (1) +#define MICROPY_PY_MACHINE_SOFTSPI (1) +#define MICROPY_PY_MACHINE_TIMER (1) +#define MICROPY_VFS (1) + +// fatfs configuration +#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ +#define MICROPY_FATFS_RPATH (2) +#if MICROPY_HW_USB_MSC +#define MICROPY_FATFS_USE_LABEL (1) +#define MICROPY_FATFS_MULTI_PARTITION (1) +// Set FatFS block size to flash sector size to avoid caching +// the flash sector in memory to support smaller block sizes. +#define MICROPY_FATFS_MAX_SS (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES) +#endif + +#define MP_STATE_PORT MP_STATE_VM + +// Miscellaneous settings + +// We need an implementation of the log2 function which is not a macro +#define MP_NEED_LOG2 (1) + +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) + +#define MP_SSIZE_MAX (0x7fffffff) + +// Assume that if we already defined the obj repr then we also defined these items +#ifndef MICROPY_OBJ_REPR +typedef intptr_t mp_int_t; // must be pointer size +typedef uintptr_t mp_uint_t; // must be pointer size +typedef intptr_t mp_off_t; +#endif + +// Board configuration settings. + +#ifndef MICROPY_BOARD_STARTUP +#define MICROPY_BOARD_STARTUP() +#endif + +#ifndef MICROPY_BOARD_EARLY_INIT +#define MICROPY_BOARD_EARLY_INIT() +#endif + +#ifndef MICROPY_BOARD_FATAL_ERROR +extern void panic(const char *); +#define MICROPY_BOARD_FATAL_ERROR panic +#endif + +#ifndef MICROPY_BOARD_ENTER_BOOTLOADER +#define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) +#endif diff --git a/ports/alif/mpconfigport.mk b/ports/alif/mpconfigport.mk new file mode 100644 index 0000000000000..3d04450a60fa7 --- /dev/null +++ b/ports/alif/mpconfigport.mk @@ -0,0 +1,12 @@ +# Enable/disable extra modules and features + +# MicroPython feature configurations +MICROPY_ROM_TEXT_COMPRESSION ?= 1 +MICROPY_FLOAT_IMPL ?= double + +# VFS support. +MICROPY_VFS_FAT ?= 1 +MICROPY_VFS_LFS2 ?= 1 + +# File containing description of content to be frozen into firmware. +FROZEN_MANIFEST ?= boards/manifest.py diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c new file mode 100644 index 0000000000000..b433aa5e155d2 --- /dev/null +++ b/ports/alif/mphalport.c @@ -0,0 +1,152 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Damien P. George + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "py/ringbuf.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "extmod/misc.h" +#include "shared/runtime/interrupt_char.h" +#include "shared/timeutils/timeutils.h" +#include "shared/tinyusb/mp_usbd.h" +#include "shared/tinyusb/mp_usbd_cdc.h" +#include "tusb.h" +#include "mpuart.h" + +#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) }; + +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { + uintptr_t ret = 0; + #if MICROPY_HW_USB_CDC + ret |= mp_usbd_cdc_poll_interfaces(poll_flags); + #endif + #if MICROPY_HW_ENABLE_UART_REPL + if (poll_flags & MP_STREAM_POLL_WR) { + ret |= MP_STREAM_POLL_WR; + } + #endif + #if MICROPY_PY_OS_DUPTERM + ret |= mp_os_dupterm_poll(poll_flags); + #endif + return ret; +} + +// Receive single character +int mp_hal_stdin_rx_chr(void) { + for (;;) { + #if 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_OS_DUPTERM + int dupterm_c = mp_os_dupterm_rx_chr(); + if (dupterm_c >= 0) { + return dupterm_c; + } + #endif + mp_event_wait_indefinite(); + } +} + +// Send string of given length +mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { + mp_uint_t ret = len; + bool did_write = false; + + #if MICROPY_HW_ENABLE_UART_REPL + mp_uart_write_strn(str, len); + did_write = true; + #endif + + #if 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); + } + #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; +} + +static uint32_t volatile ticks_ms; + +void SysTick_Handler(void) { + ++ticks_ms; +} + +mp_uint_t mp_hal_ticks_cpu(void) { + if (!(DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk)) { + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + #if defined(__CORTEX_M) && __CORTEX_M == 7 + // on Cortex-M7 we must unlock the DWT before writing to its registers + DWT->LAR = 0xc5acce55; + #endif + DWT->CYCCNT = 0; + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + } + return DWT->CYCCNT; +} + +mp_uint_t mp_hal_ticks_us(void) { + return ticks_ms / 1000; +} + +mp_uint_t mp_hal_ticks_ms(void) { + return ticks_ms; +} + +void mp_hal_delay_us(mp_uint_t us) { + mp_hal_delay_ms(us / 1000); +} + +void mp_hal_delay_ms(mp_uint_t ms) { + uint32_t t0 = mp_hal_ticks_ms(); + while ((mp_hal_ticks_ms() - t0) < ms) { + __WFI(); + } +} + +uint64_t mp_hal_time_ns(void) { + return 0; +} diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h new file mode 100644 index 0000000000000..1b8c854fd0b95 --- /dev/null +++ b/ports/alif/mphalport.h @@ -0,0 +1,152 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/ringbuf.h" +#include "shared/runtime/interrupt_char.h" +#include "irq.h" +#include ALIF_CMSIS_H + +#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq() +#define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state) + +// For regular code that wants to prevent "background tasks" from running. +// These background tasks (LWIP, Bluetooth) run in PENDSV context. +#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_PENDSV_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state); + +// Port level Wait-for-Event macro +// +// Do not use this macro directly, include py/runtime.h and +// call mp_event_wait_indefinite() or mp_event_wait_ms(timeout) +#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) \ + do { \ + if ((TIMEOUT_MS) < 0) { \ + __WFE(); \ + } else { \ + /* TODO */ \ + __WFE(); \ + } \ + } while (0) + +// TODO requires mods to py/emitglue.c for this to be picked up +#define MP_HAL_CLEAN_DCACHE(addr, size) \ + (SCB_CleanDCache_by_Addr((uint32_t *)((uint32_t)addr & ~0x1f), \ + ((uint32_t)((uint8_t *)addr + size + 0x1f) & ~0x1f) - ((uint32_t)addr & ~0x1f))) + +extern ringbuf_t stdin_ringbuf; + +// TODO +#define mp_hal_quiet_timing_enter() 0 +#define mp_hal_quiet_timing_exit(x) (void)x +#define mp_hal_delay_us_fast mp_hal_delay_us + +/******************************************************************************/ +// C-level pin HAL + +#include "py/obj.h" +#include "gpio.h" +#include "pinconf.h" + +#define MP_HAL_PIN_FMT "%q" +#define MP_HAL_PIN_MODE_INPUT (0) +#define MP_HAL_PIN_MODE_OUTPUT (1) +#define MP_HAL_PIN_MODE_OPEN_DRAIN (2) +#define MP_HAL_PIN_PULL_NONE (0) +#define MP_HAL_PIN_PULL_UP (1) +#define MP_HAL_PIN_PULL_DOWN (2) + +#define mp_hal_pin_obj_t const machine_pin_obj_t * + +typedef struct _machine_pin_obj_t { + mp_obj_base_t base; + GPIO_Type *gpio; + uint8_t port; + uint8_t pin; + qstr name; +} machine_pin_obj_t; + +mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in); + +static inline qstr mp_hal_pin_name(mp_hal_pin_obj_t pin) { + return pin->name; +} + +static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) { + uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0; + uint8_t pad_ctrl = PADCTRL_READ_ENABLE; + pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl); + gpio_set_direction_input(pin->gpio, pin->pin); +} + +static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) { + uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0; + uint8_t pad_ctrl = PADCTRL_READ_ENABLE; + pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl); + gpio_set_direction_output(pin->gpio, pin->pin); +} + +static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) { + uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0; + uint8_t pad_ctrl = PADCTRL_DRIVER_OPEN_DRAIN | PADCTRL_READ_ENABLE; + pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl); + gpio_set_direction_output(pin->gpio, pin->pin); +} + +static inline void mp_hal_pin_low(mp_hal_pin_obj_t pin) { + gpio_set_value_low(pin->gpio, pin->pin); +} + +static inline void mp_hal_pin_high(mp_hal_pin_obj_t pin) { + gpio_set_value_high(pin->gpio, pin->pin); +} + +static inline int mp_hal_pin_read(mp_hal_pin_obj_t pin) { + return gpio_get_value(pin->gpio, pin->pin); +} + +static inline void mp_hal_pin_write(mp_hal_pin_obj_t pin, int v) { + if (v) { + mp_hal_pin_high(pin); + } else { + mp_hal_pin_low(pin); + } +} + +static inline void mp_hal_pin_od_low(mp_hal_pin_obj_t pin) { + mp_hal_pin_low(pin); +} + +static inline void mp_hal_pin_od_high(mp_hal_pin_obj_t pin) { + mp_hal_pin_high(pin); +} + +static inline void mp_hal_wake_main_task_from_isr(void) { + // Defined for tinyusb support, nothing needs to be done here. +} + +// Include all the pin definitions. +#include "genhdr/pins_board.h" diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c new file mode 100644 index 0000000000000..e9cfcf34fed53 --- /dev/null +++ b/ports/alif/mpuart.c @@ -0,0 +1,113 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "py/runtime.h" +#include "py/mphal.h" +#include "mpuart.h" + +#if MICROPY_HW_ENABLE_UART_REPL + +#include "pinconf.h" +#include "sys_ctrl_uart.h" +#include "uart.h" + +#define TX_PORT PORT_12 +#define TX_PIN PIN_2 +#define RX_PORT PORT_12 +#define RX_PIN PIN_1 +#define UART_ID 4 +#define UART_IRQN UART4_IRQ_IRQn +#define UART_PTR ((UART_Type *)UART4_BASE) +#define BAUDRATE 115200 +#define SYST_PCLK 100000000 + +static UART_TRANSFER transfer; + +void mp_uart_init(void) { + pinconf_set(TX_PORT, TX_PIN, PINMUX_ALTERNATE_FUNCTION_2, 0); + pinconf_set(RX_PORT, RX_PIN, PINMUX_ALTERNATE_FUNCTION_2, PADCTRL_READ_ENABLE); + select_uart_clock_syst_pclk(UART_ID); + enable_uart_clock(UART_ID); + uart_software_reset(UART_PTR); + uart_enable_fifo(UART_PTR); + uart_disable_tx_irq(UART_PTR); + uart_disable_rx_irq(UART_PTR); + uart_set_baudrate(UART_PTR, SYST_PCLK, BAUDRATE); + uart_set_data_parity_stop_bits(UART_PTR, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1); + uart_set_flow_control(UART_PTR, UART_FLOW_CONTROL_NONE); + NVIC_ClearPendingIRQ(UART_IRQN); + NVIC_SetPriority(UART_IRQN, IRQ_PRI_UART_REPL); + NVIC_EnableIRQ(UART_IRQN); + uart_set_tx_trigger(UART_PTR, UART_TX_FIFO_EMPTY); + uart_set_rx_trigger(UART_PTR, UART_RX_ONE_CHAR_IN_FIFO); + uart_enable_rx_irq(UART_PTR); +} + +void mp_uart_write_strn(const char *str, size_t len) { + memset(&transfer, 0, sizeof(transfer)); + transfer.tx_buf = (uint8_t *)str; + transfer.tx_total_num = len; + transfer.tx_curr_cnt = 0U; + transfer.status = UART_TRANSFER_STATUS_NONE; + + uart_enable_tx_irq(UART_PTR); + + uint32_t start = mp_hal_ticks_ms(); + while (transfer.status == UART_TRANSFER_STATUS_NONE) { + if (mp_hal_ticks_ms() - start > 10 * len) { + break; + } + __WFE(); + } + uart_disable_tx_irq(UART_PTR); +} + +void UART4_IRQHandler(void) { + if (UART_PTR->UART_RFL) { + for (;;) { + uint32_t rx_fifo_available_cnt = UART_PTR->UART_RFL; + if (rx_fifo_available_cnt == 0) { + break; + } + for (uint32_t i = 0; i < rx_fifo_available_cnt; ++i) { + int c = UART_PTR->UART_RBR; + #if MICROPY_KBD_EXCEPTION + if (c == mp_interrupt_char) { + mp_sched_keyboard_interrupt(); + continue; + } + #endif + ringbuf_put(&stdin_ringbuf, c); + } + } + } else { + uart_irq_handler(UART_PTR, &transfer); + } + __SEV(); +} + +#endif diff --git a/ports/alif/mpuart.h b/ports/alif/mpuart.h new file mode 100644 index 0000000000000..0c2e510a94e7c --- /dev/null +++ b/ports/alif/mpuart.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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_ALIF2_UART_H +#define MICROPY_INCLUDED_ALIF2_UART_H + +void mp_uart_init(void); +void mp_uart_write_strn(const char *str, size_t len); + +#endif // MICROPY_INCLUDED_ALIF2_UART_H diff --git a/ports/alif/msc_disk.c b/ports/alif/msc_disk.c new file mode 100644 index 0000000000000..a01494d20f0e4 --- /dev/null +++ b/ports/alif/msc_disk.c @@ -0,0 +1,151 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mpconfig.h" +#include "py/misc.h" +#include "ospi_flash.h" +#include "tusb.h" + +#if CFG_TUD_MSC + +#if MICROPY_FATFS_MAX_SS != MICROPY_HW_FLASH_BLOCK_SIZE_BYTES +#error MICROPY_FATFS_MAX_SS must be the same size as MICROPY_HW_FLASH_BLOCK_SIZE_BYTES +#endif + +#define BLOCK_SIZE (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES) +#define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_BYTES / BLOCK_SIZE) +#define FLASH_BASE_ADDR (MICROPY_HW_FLASH_STORAGE_BASE_ADDR) + +static bool ejected = false; + +// Invoked on SCSI_CMD_INQUIRY. +// Fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively. +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { + memcpy(vendor_id, MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING), 8)); + memcpy(product_id, MICROPY_HW_USB_MSC_INQUIRY_PRODUCT_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_PRODUCT_STRING), 16)); + memcpy(product_rev, MICROPY_HW_USB_MSC_INQUIRY_REVISION_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_REVISION_STRING), 4)); +} + +// Invoked on Test-Unit-Ready command. +// Return true allowing host to read/write this LUN (e.g SD card inserted). +bool tud_msc_test_unit_ready_cb(uint8_t lun) { + if (ejected) { + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); + return false; + } + return true; +} + +// Invoked on SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size. +void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) { + *block_size = BLOCK_SIZE; + *block_count = BLOCK_COUNT; +} + +// Invoked on Start-Stop-Unit command: +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { + if (load_eject) { + if (start) { + // load disk storage + ejected = false; + } else { + // unload disk storage + ejected = true; + } + } + return true; +} + +// Callback invoked on READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of read bytes. +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) { + uint32_t addr = FLASH_BASE_ADDR + lba * BLOCK_SIZE; + uint32_t block_count = bufsize / BLOCK_SIZE; + int ret = ospi_flash_read(addr, block_count * BLOCK_SIZE, buffer); + if (ret < 0) { + return ret; + } + return block_count * BLOCK_SIZE; +} + +// Callback invoked on WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes. +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) { + if (bufsize < BLOCK_SIZE) { + // Workaround for issue with TinyUSB passing in a small buffer. + uint32_t addr = FLASH_BASE_ADDR + lba * BLOCK_SIZE + offset; + if (offset == 0) { + int ret = ospi_flash_erase_sector(addr); + if (ret < 0) { + return ret; + } + } + int ret = ospi_flash_write(addr, bufsize, buffer); + if (ret < 0) { + return ret; + } + return bufsize; + } + + uint32_t addr = FLASH_BASE_ADDR + lba * BLOCK_SIZE; + uint32_t block_count = bufsize / BLOCK_SIZE; + for (uint32_t block = 0; block < block_count; ++block) { + int ret = ospi_flash_erase_sector(addr); + if (ret < 0) { + return ret; + } + ret = ospi_flash_write(addr, BLOCK_SIZE, buffer); + if (ret < 0) { + return ret; + } + addr += BLOCK_SIZE; + buffer += BLOCK_SIZE; + } + return block_count * BLOCK_SIZE; +} + +// Callback invoked on a SCSI command that's not handled by TinyUSB. +int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) { + int32_t resplen = 0; + switch (scsi_cmd[0]) { + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + // Sync the logical unit if needed. + break; + + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + // negative means error -> tinyusb could stall and/or response with failed status + resplen = -1; + break; + } + return resplen; +} + +#endif diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c new file mode 100644 index 0000000000000..fa96053b36579 --- /dev/null +++ b/ports/alif/ospi_flash.c @@ -0,0 +1,265 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mperrno.h" +#include "py/mphal.h" +#include "ospi_flash.h" + +#include "ospi_drv.h" +#include "pinconf.h" + +#define CMD_RDSR (0x05) +#define CMD_WREN (0x06) +#define CMD_SEC_ERASE_32ADDR (0x21) // 4kiB sector erase with 32-bit address +#define CMD_WRVOL (0x81) +#define CMD_RD_DEVID (0x9f) + +#define WAIT_SR_TIMEOUT (1000000) + +// maximum bytes we can write in one SPI transfer +// limited by 256 byte FIFO buffer (can't go up to 256) +// need to use DMA to make this 256 +#define PAGE_SIZE (128) + +#define ISSI_MODE_OCTAL_DDR_DQS (0xe7) + +// All OSPI1 pins use the same alternate function. +#define OSPI1_PIN_FUNCTION PINMUX_ALTERNATE_FUNCTION_1 + +typedef struct _mp_spiflash_t { + ospi_flash_cfg_t cfg; +} mp_spiflash_t; + +static mp_spiflash_t global_flash; + +// Alif version of this function can overwrite the destination buffer. +static void ospi_recv_blocking2(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer) { + uint32_t val; + + ospi_writel(ospi_cfg, data_reg, command); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + ospi_cfg->rx_cnt = 0; + + while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return; + } + } + val = ospi_readl(ospi_cfg, data_reg); + *buffer++ = (uint8_t)val; + ospi_cfg->rx_cnt++; + } +} + +static int mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len, uint8_t *dest) { + ospi_setup_read(&self->cfg, 0, len, 8); + ospi_recv_blocking2(&self->cfg, cmd, dest); + return 0; +} + +static int mp_spiflash_write_cmd(mp_spiflash_t *self, uint8_t cmd) { + ospi_setup_write(&self->cfg, 0); + ospi_send_blocking(&self->cfg, cmd); + return 0; +} + +static int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) { + do { + uint8_t sr; + int ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr); + if (ret != 0) { + return ret; + } + if ((sr & mask) == val) { + return 0; // success + } + } while (timeout--); + + return -MP_ETIMEDOUT; +} + +static int mp_spiflash_wait_wel1(mp_spiflash_t *self) { + return mp_spiflash_wait_sr(self, 2, 2, WAIT_SR_TIMEOUT); +} + +static int mp_spiflash_wait_wip0(mp_spiflash_t *self) { + return mp_spiflash_wait_sr(self, 1, 0, WAIT_SR_TIMEOUT); +} + +static uint32_t ospi_flash_read_id(mp_spiflash_t *self) { + uint8_t buf[8]; + ospi_setup_read(&self->cfg, 0, 3, ospi_flash_settings.read_id_dummy_cycles); + ospi_recv_blocking2(&self->cfg, CMD_RD_DEVID, buf); + return buf[0] | buf[1] << 8 | buf[2] << 16; +} + +static void ospi_flash_write_reg_sdr(mp_spiflash_t *self, uint8_t cmd, uint8_t addr, uint8_t value) { + mp_spiflash_write_cmd(self, CMD_WREN); + ospi_setup_write_sdr(&self->cfg, 6); + ospi_push(&self->cfg, cmd); + ospi_push(&self->cfg, 0x00); + ospi_push(&self->cfg, 0x00); + ospi_push(&self->cfg, addr); + ospi_send_blocking(&self->cfg, value); +} + +int ospi_flash_init(void) { + mp_spiflash_t *self = &global_flash; + + uint32_t pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; + + pinconf_set(pin_OSPI1_CS->port, pin_OSPI1_CS->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); + pinconf_set(pin_OSPI1_SCLK->port, pin_OSPI1_SCLK->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST); + pinconf_set(pin_OSPI1_D0->port, pin_OSPI1_D0->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D1->port, pin_OSPI1_D1->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D2->port, pin_OSPI1_D2->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D3->port, pin_OSPI1_D3->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + #if defined(pin_OSPI1_D4) + pinconf_set(pin_OSPI1_D4->port, pin_OSPI1_D4->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D5->port, pin_OSPI1_D5->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D6->port, pin_OSPI1_D6->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin_OSPI1_D7->port, pin_OSPI1_D7->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + #endif + #if defined(pin_OSPI1_RXDS) + pinconf_set(pin_OSPI1_RXDS->port, pin_OSPI1_RXDS->pin, OSPI1_PIN_FUNCTION, pad_ctrl); + #endif + + #if defined(pin_OSPI1_RXDS) + if (pin_OSPI1_RXDS->port == PORT_10 && pin_OSPI1_RXDS->pin == PIN_7) { + // Alif: P5_6 is needed to support proper alt function selection of P10_7. + pinconf_set(PORT_5, PIN_6, OSPI1_PIN_FUNCTION, pad_ctrl); + } + #endif + + // Reset the SPI flash. + mp_hal_pin_output(pin_OSPI1_RESET); + mp_hal_pin_low(pin_OSPI1_RESET); + mp_hal_delay_us(30); + mp_hal_pin_high(pin_OSPI1_RESET); + + // Configure the OSPI peripheral. + self->cfg.regs = (ssi_regs_t *)OSPI1_BASE; + self->cfg.aes_regs = (aes_regs_t *)AES1_BASE; + self->cfg.xip_base = (volatile void *)OSPI1_XIP_BASE; + self->cfg.ser = 1; + self->cfg.addrlen = 8; // 32-bit address length + self->cfg.ospi_clock = ospi_flash_settings.freq_mhz; + self->cfg.ddr_en = 0; + self->cfg.wait_cycles = 0; // used only for ospi_xip_exit + ospi_init(&self->cfg); + + if (ospi_flash_settings.is_oct && ospi_flash_settings.is_ddr) { + // Switch SPI flash to Octal DDR mode. + ospi_flash_write_reg_sdr(self, CMD_WRVOL, 0x00, ISSI_MODE_OCTAL_DDR_DQS); + self->cfg.ddr_en = 1; + } + + // Check the device ID. + if (ospi_flash_read_id(self) != ospi_flash_settings.jedec_id) { + return -1; + } + + return 0; +} + +int ospi_flash_erase_sector(uint32_t addr) { + mp_spiflash_t *self = &global_flash; + + mp_spiflash_write_cmd(self, CMD_WREN); + int ret = mp_spiflash_wait_wel1(self); + if (ret < 0) { + return ret; + } + + ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/); + ospi_push(&self->cfg, CMD_SEC_ERASE_32ADDR); + ospi_send_blocking(&self->cfg, addr); + + return mp_spiflash_wait_wip0(self); +} + +int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { + // OSPI FIFO is limited to 256 bytes. Need DMA to get a longer read. + mp_spiflash_t *self = &global_flash; + + while (len) { + uint32_t l = len; + if (l > 256) { + l = 256; + } + ospi_setup_read(&self->cfg, 8 /* 32-bit addr len*/, l, ospi_flash_settings.read_dummy_cycles); + ospi_push(&self->cfg, ospi_flash_settings.read_command); + ospi_recv_blocking2(&self->cfg, addr, dest); + addr += l; + len -= l; + dest += l; + } + + return 0; +} + +static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src) { + mp_spiflash_t *self = &global_flash; + + mp_spiflash_write_cmd(self, CMD_WREN); + int ret = mp_spiflash_wait_wel1(self); + if (ret < 0) { + return ret; + } + + ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/); + ospi_push(&self->cfg, ospi_flash_settings.write_command); + ospi_push(&self->cfg, addr); + while (--len) { + ospi_push(&self->cfg, *src++); + } + ospi_send_blocking(&self->cfg, *src); + + return mp_spiflash_wait_wip0(self); +} + +int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { + int ret = 0; + uint32_t offset = addr & (PAGE_SIZE - 1); + while (len) { + size_t rest = PAGE_SIZE - offset; + if (rest > len) { + rest = len; + } + ret = ospi_flash_write_page(addr, rest, src); + if (ret != 0) { + break; + } + len -= rest; + addr += rest; + src += rest; + offset = 0; + } + return ret; +} diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h new file mode 100644 index 0000000000000..945dc4bb07ba4 --- /dev/null +++ b/ports/alif/ospi_flash.h @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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_ALIF_OSPI_FLASH_H +#define MICROPY_INCLUDED_ALIF_OSPI_FLASH_H + +#include +#include + +typedef struct _ospi_flash_settings_t { + uint32_t jedec_id; + uint32_t freq_mhz; + bool is_quad : 1; + bool is_oct : 1; + bool is_ddr : 1; + uint8_t read_id_dummy_cycles; + uint8_t read_dummy_cycles; + uint8_t read_command; + uint8_t write_command; +} ospi_flash_settings_t; + +// Provided by the board when it enables OSPI1. +extern const ospi_flash_settings_t ospi_flash_settings; + +int ospi_flash_init(void); +int ospi_flash_erase_sector(uint32_t addr); +int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest); +int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src); + +#endif // MICROPY_INCLUDED_ALIF_OSPI_FLASH_H diff --git a/ports/alif/ospi_xip_user.h b/ports/alif/ospi_xip_user.h new file mode 100644 index 0000000000000..a15baff2a8ba4 --- /dev/null +++ b/ports/alif/ospi_xip_user.h @@ -0,0 +1,5 @@ +// This file is needed by ospi_xip/source/ospi/ospi_drv.c. +#define OSPI_XIP_ENABLE_AES_DECRYPTION (0) +#define OSPI_XIP_RX_SAMPLE_DELAY (4) +#define OSPI_XIP_DDR_DRIVE_EDGE (1) +#define OSPI_XIP_RXDS_DELAY (12) diff --git a/ports/alif/pendsv.c b/ports/alif/pendsv.c new file mode 100644 index 0000000000000..965053e6c595a --- /dev/null +++ b/ports/alif/pendsv.c @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "pendsv.h" +#include "irq.h" + +static pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS]; + +void pendsv_init(void) { + NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_PENDSV); +} + +void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { + pendsv_dispatch_table[slot] = f; + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; +} + +void PendSV_Handler(void) { + for (size_t i = 0; i < PENDSV_DISPATCH_NUM_SLOTS; ++i) { + if (pendsv_dispatch_table[i] != NULL) { + pendsv_dispatch_t f = pendsv_dispatch_table[i]; + pendsv_dispatch_table[i] = NULL; + f(); + } + } +} diff --git a/ports/alif/pendsv.h b/ports/alif/pendsv.h new file mode 100644 index 0000000000000..43a4b7a853846 --- /dev/null +++ b/ports/alif/pendsv.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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_ALIF_PENDSV_H +#define MICROPY_INCLUDED_ALIF_PENDSV_H + +#include "py/mpconfig.h" + +#ifndef MICROPY_BOARD_PENDSV_ENTRIES +#define MICROPY_BOARD_PENDSV_ENTRIES +#endif + +enum { + PENDSV_DISPATCH_SOFT_TIMER, + MICROPY_BOARD_PENDSV_ENTRIES + PENDSV_DISPATCH_MAX +}; + +#define PENDSV_DISPATCH_NUM_SLOTS PENDSV_DISPATCH_MAX + +typedef void (*pendsv_dispatch_t)(void); + +void pendsv_init(void); +void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f); + +#endif // MICROPY_INCLUDED_ALIF_PENDSV_H diff --git a/ports/alif/qstrdefsport.h b/ports/alif/qstrdefsport.h new file mode 100644 index 0000000000000..00d3e2ae3c555 --- /dev/null +++ b/ports/alif/qstrdefsport.h @@ -0,0 +1,2 @@ +// qstrs specific to this port +// *FORMAT-OFF* diff --git a/ports/alif/tinyusb_port/tusb_config.h b/ports/alif/tinyusb_port/tusb_config.h new file mode 100644 index 0000000000000..d244b4c241e6b --- /dev/null +++ b/ports/alif/tinyusb_port/tusb_config.h @@ -0,0 +1,73 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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 _ALIF_TUSB_CONFIG_H_ +#define _ALIF_TUSB_CONFIG_H_ + +// --------------------------------------------------------------------+ +// Board Specific Configuration +// --------------------------------------------------------------------+ + +#define CFG_TUSB_MCU OPT_MCU_NONE +// #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) +#define TUP_DCD_ENDPOINT_MAX 8 +#define TUD_OPT_RHPORT 0 + +// -------------------------------------------------------------------- +// COMMON CONFIGURATION +// -------------------------------------------------------------------- + +#define CFG_TUSB_OS OPT_OS_NONE +#define CFG_TUSB_DEBUG 0 + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED OPT_MODE_HIGH_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + */ +#define CFG_TUSB_MEM_SECTION __attribute__((section(".bss.sram0"))) + +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(32))) + +// -------------------------------------------------------------------- +// DEVICE CONFIGURATION +// -------------------------------------------------------------------- + +#define CFG_TUD_ENDPOINT0_SIZE 64 + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE (4096) +#define CFG_TUD_CDC_RX_BUFSIZE (4096) +#define CFG_TUD_CDC_TX_BUFSIZE (4096) + +#include "shared/tinyusb/tusb_config.h" + +#endif /* _ALIF_TUSB_CONFIG_H_ */ diff --git a/ports/alif/usbd.c b/ports/alif/usbd.c new file mode 100644 index 0000000000000..9df6610683507 --- /dev/null +++ b/ports/alif/usbd.c @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mpconfig.h" + +#if MICROPY_HW_ENABLE_USBDEV + +#include "shared/tinyusb/mp_usbd.h" +#include "tusb.h" + +void mp_usbd_port_get_serial_number(char *serial_buf) { + // TODO + uint8_t id[8] = "ABCDEFGH"; + MP_STATIC_ASSERT(sizeof(id) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); + mp_usbd_hex_str(serial_buf, id, sizeof(id)); +} + +#endif From 40ff0c2f276883ea325ce8977453bd16a2f2029a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Jan 2024 14:37:52 +1100 Subject: [PATCH 462/546] alif/system_tick: Use a UTIMER for system ticks and timing. Includes an implementation of `system_tick_wfe_with_timeout_us()`. Signed-off-by: Damien George --- ports/alif/Makefile | 2 + ports/alif/main.c | 3 +- ports/alif/mphalport.c | 36 +++++------ ports/alif/system_tick.c | 128 +++++++++++++++++++++++++++++++++++++++ ports/alif/system_tick.h | 38 ++++++++++++ 5 files changed, 184 insertions(+), 23 deletions(-) create mode 100644 ports/alif/system_tick.c create mode 100644 ports/alif/system_tick.h diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 2863ebe2c4d61..cd02f4ac2d61c 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -119,6 +119,7 @@ SRC_C = \ msc_disk.c \ ospi_flash.c \ pendsv.c \ + system_tick.c \ usbd.c \ $(wildcard $(BOARD_DIR)/*.c) @@ -175,6 +176,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ drivers/source/pinconf.c \ drivers/source/uart.c \ + drivers/source/utimer.c \ ospi_xip/source/ospi/ospi_drv.c \ ) diff --git a/ports/alif/main.c b/ports/alif/main.c index 0eb43a38268a6..9293d74c0b80d 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -38,6 +38,7 @@ #include "mpuart.h" #include "ospi_flash.h" #include "pendsv.h" +#include "system_tick.h" extern uint8_t __StackTop, __StackLimit; extern uint8_t __GcHeapStart, __GcHeapEnd; @@ -53,7 +54,7 @@ NORETURN void panic(const char *msg) { } void _start(void) { - SysTick_Config(SystemCoreClock / 1000); + system_tick_init(); MICROPY_BOARD_STARTUP(); diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index b433aa5e155d2..abb71aae69624 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -36,6 +36,7 @@ #include "shared/tinyusb/mp_usbd_cdc.h" #include "tusb.h" #include "mpuart.h" +#include "system_tick.h" #ifndef MICROPY_HW_STDIN_BUFFER_LEN #define MICROPY_HW_STDIN_BUFFER_LEN 512 @@ -109,42 +110,33 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { return did_write ? ret : 0; } -static uint32_t volatile ticks_ms; - -void SysTick_Handler(void) { - ++ticks_ms; -} - mp_uint_t mp_hal_ticks_cpu(void) { - if (!(DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk)) { - CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; - #if defined(__CORTEX_M) && __CORTEX_M == 7 - // on Cortex-M7 we must unlock the DWT before writing to its registers - DWT->LAR = 0xc5acce55; - #endif - DWT->CYCCNT = 0; - DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; - } - return DWT->CYCCNT; + return system_tick_get_u32(); } mp_uint_t mp_hal_ticks_us(void) { - return ticks_ms / 1000; + // Convert system tick to microsecond counter. + return system_tick_get_u64() / system_core_clock_mhz; } mp_uint_t mp_hal_ticks_ms(void) { - return ticks_ms; + // Convert system tick to millisecond counter. + return system_tick_get_u64() / (SystemCoreClock / 1000); } void mp_hal_delay_us(mp_uint_t us) { - mp_hal_delay_ms(us / 1000); + uint64_t ticks_delay = (uint64_t)us * system_core_clock_mhz; + uint64_t start = system_tick_get_u64(); + while (system_tick_get_u64() - start < ticks_delay) { + } } void mp_hal_delay_ms(mp_uint_t ms) { uint32_t t0 = mp_hal_ticks_ms(); - while ((mp_hal_ticks_ms() - t0) < ms) { - __WFI(); - } + do { + // TODO: power saving wait + mp_event_handle_nowait(); + } while (mp_hal_ticks_ms() - t0 < ms); } uint64_t mp_hal_time_ns(void) { diff --git a/ports/alif/system_tick.c b/ports/alif/system_tick.c new file mode 100644 index 0000000000000..95d5391c03855 --- /dev/null +++ b/ports/alif/system_tick.c @@ -0,0 +1,128 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "irq.h" +#include "system_tick.h" + +#include "utimer.h" + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +#define UTIMER ((UTIMER_Type *)UTIMER_BASE) +#define UTIMER_CHANNEL (11) + +uint64_t system_core_clock_mhz; +static volatile uint32_t system_tick_hi; + +static void system_tick_nvic_config(unsigned int index) { + NVIC_ClearPendingIRQ(UTIMER_IRQ0_IRQn + UTIMER_CHANNEL * 8 + index); + NVIC_SetPriority(UTIMER_IRQ0_IRQn + UTIMER_CHANNEL * 8 + index, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(UTIMER_IRQ0_IRQn + UTIMER_CHANNEL * 8 + index); +} + +void system_tick_init(void) { + system_tick_hi = 0; + system_core_clock_mhz = SystemCoreClock / 1000000; + + // Configure NVIC OVER_FLOW interrupt. + system_tick_nvic_config(7); + + utimer_clock_enable(UTIMER, UTIMER_CHANNEL); + utimer_channel_config cfg = { 0 }; + cfg.fixed_buffer = false; + utimer_config_direction(UTIMER, UTIMER_CHANNEL, UTIMER_COUNTER_UP, &cfg); + utimer_set_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR_PTR, 0xffffffff); + utimer_unmask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_OVER_FLOW_MASK); + utimer_control_enable(UTIMER, UTIMER_CHANNEL); + utimer_counter_start(UTIMER, UTIMER_CHANNEL); + + // Set up the UTIMER compare interrupt, to be used later. + system_tick_nvic_config(2); + UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_COMPARE_CTRL_A |= COMPARE_CTRL_DRV_COMPARE_EN; +} + +// COMPARE_A_BUF1 +void UTIMER_IRQ90Handler(void) { + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + if (chan_interrupt & CHAN_INTERRUPT_COMPARE_A_BUF1_MASK) { + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); + __SEV(); + } +} + +// OVER_FLOW +void UTIMER_IRQ95Handler(void) { + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + if (chan_interrupt & CHAN_INTERRUPT_OVER_FLOW_MASK) { + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_OVER_FLOW_MASK); + ++system_tick_hi; + } +} + +uint32_t system_tick_get_u32(void) { + return utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); +} + +uint64_t system_tick_get_u64(void) { + uint32_t irq_state = disable_irq(); + uint32_t ticks_lo = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + uint32_t ticks_hi = system_tick_hi; + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + enable_irq(irq_state); + + if (chan_interrupt & CHAN_INTERRUPT_OVER_FLOW_MASK) { + // The timer had an overflow while interrupts were disabled. + if (ticks_lo < 0x80000000) { + // The timer had an overflow just before we sampled it. + ++ticks_hi; + } + } + + // This ticks runs at SystemCoreClock. + return (uint64_t)ticks_hi << 32 | ticks_lo; +} + +void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { + // Maximum 10 second timeout, to not overflow signed 32-bit ticks when + // system_core_clock_mhz==400. + uint32_t timeout_ticks = MIN(timeout_us, 10000000) * system_core_clock_mhz; + + // Set up the UTIMER compare interrupt to fire after the given timeout. + uint32_t cntr = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + utimer_set_count(UTIMER, UTIMER_CHANNEL, UTIMER_COMPARE_A_BUF1, cntr + timeout_ticks); + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); + utimer_unmask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); + + // Wait for an event (or compare timeout event) if the timeout hasn't expired yet + // (this check handles the case of short timeouts). + uint32_t cntr2 = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + if ((uint32_t)(cntr2 - cntr) < timeout_ticks) { + __WFE(); + } + + // Disable the UTIMER compare interrupt. + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); +} diff --git a/ports/alif/system_tick.h b/ports/alif/system_tick.h new file mode 100644 index 0000000000000..307c87a345fb8 --- /dev/null +++ b/ports/alif/system_tick.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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_ALIF_SYSTEM_TICK_H +#define MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H + +#include + +extern uint64_t system_core_clock_mhz; + +void system_tick_init(void); +uint32_t system_tick_get_u32(void); +uint64_t system_tick_get_u64(void); +void system_tick_wfe_with_timeout_us(uint32_t timeout_us); + +#endif // MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H From 975f84f2adc1f89b4c9349b6a6e83f6bc4ec2fd6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Jan 2024 16:08:45 +1100 Subject: [PATCH 463/546] alif/mphalport: Enable efficient events and implement quiet timing. Signed-off-by: Damien George --- ports/alif/mphalport.c | 12 ++++++++---- ports/alif/mphalport.h | 13 ++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index abb71aae69624..8d0fa1b92ba4a 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -133,10 +133,14 @@ void mp_hal_delay_us(mp_uint_t us) { void mp_hal_delay_ms(mp_uint_t ms) { uint32_t t0 = mp_hal_ticks_ms(); - do { - // TODO: power saving wait - mp_event_handle_nowait(); - } while (mp_hal_ticks_ms() - t0 < ms); + mp_event_handle_nowait(); + for (;;) { + uint32_t t1 = mp_hal_ticks_ms(); + if (t1 - t0 >= ms) { + break; + } + mp_event_wait_ms(ms - (t1 - t0)); + } } uint64_t mp_hal_time_ns(void) { diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 1b8c854fd0b95..8098c2650df01 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -27,6 +27,7 @@ #include "py/ringbuf.h" #include "shared/runtime/interrupt_char.h" #include "irq.h" +#include "system_tick.h" #include ALIF_CMSIS_H #define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq() @@ -47,8 +48,7 @@ if ((TIMEOUT_MS) < 0) { \ __WFE(); \ } else { \ - /* TODO */ \ - __WFE(); \ + system_tick_wfe_with_timeout_us(TIMEOUT_MS * 1000); \ } \ } while (0) @@ -57,13 +57,12 @@ (SCB_CleanDCache_by_Addr((uint32_t *)((uint32_t)addr & ~0x1f), \ ((uint32_t)((uint8_t *)addr + size + 0x1f) & ~0x1f) - ((uint32_t)addr & ~0x1f))) -extern ringbuf_t stdin_ringbuf; - -// TODO -#define mp_hal_quiet_timing_enter() 0 -#define mp_hal_quiet_timing_exit(x) (void)x +#define mp_hal_quiet_timing_enter() raise_irq_pri(IRQ_PRI_QUIET_TIMING) +#define mp_hal_quiet_timing_exit(irq_state) restore_irq_pri(irq_state) #define mp_hal_delay_us_fast mp_hal_delay_us +extern ringbuf_t stdin_ringbuf; + /******************************************************************************/ // C-level pin HAL From ada0939c5f474e7204b6d3468bd27603daea676b Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Jan 2024 23:41:00 +1100 Subject: [PATCH 464/546] alif/system_tick: Integrate soft timer. Signed-off-by: Damien George --- ports/alif/main.c | 2 ++ ports/alif/mphalport.c | 17 ++++++++++++++++ ports/alif/system_tick.c | 44 ++++++++++++++++++++++++++++++++++++++-- ports/alif/system_tick.h | 2 ++ 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/ports/alif/main.c b/ports/alif/main.c index 9293d74c0b80d..b4e2434399970 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -33,6 +33,7 @@ #include "shared/readline/readline.h" #include "shared/runtime/gchelper.h" #include "shared/runtime/pyexec.h" +#include "shared/runtime/softtimer.h" #include "shared/tinyusb/mp_usbd.h" #include "tusb.h" #include "mpuart.h" @@ -119,6 +120,7 @@ void _start(void) { soft_reset_exit: mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); + soft_timer_deinit(); gc_sweep_all(); mp_deinit(); } diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 8d0fa1b92ba4a..ba58923f2fe2a 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -31,11 +31,13 @@ #include "py/stream.h" #include "extmod/misc.h" #include "shared/runtime/interrupt_char.h" +#include "shared/runtime/softtimer.h" #include "shared/timeutils/timeutils.h" #include "shared/tinyusb/mp_usbd.h" #include "shared/tinyusb/mp_usbd_cdc.h" #include "tusb.h" #include "mpuart.h" +#include "pendsv.h" #include "system_tick.h" #ifndef MICROPY_HW_STDIN_BUFFER_LEN @@ -146,3 +148,18 @@ void mp_hal_delay_ms(mp_uint_t ms) { uint64_t mp_hal_time_ns(void) { return 0; } + +void system_tick_schedule_callback(void) { + pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); +} + +uint32_t soft_timer_get_ms(void) { + return mp_hal_ticks_ms(); +} + +void soft_timer_schedule_at_ms(uint32_t ticks_ms) { + int32_t ms = soft_timer_ticks_diff(ticks_ms, mp_hal_ticks_ms()); + ms = MAX(0, ms); + ms = MIN(ms, 4000000); // ensure ms * 1000 doesn't overflow + system_tick_schedule_after_us(ms * 1000); +} diff --git a/ports/alif/system_tick.c b/ports/alif/system_tick.c index 95d5391c03855..5cdf45cba0126 100644 --- a/ports/alif/system_tick.c +++ b/ports/alif/system_tick.c @@ -59,9 +59,13 @@ void system_tick_init(void) { utimer_control_enable(UTIMER, UTIMER_CHANNEL); utimer_counter_start(UTIMER, UTIMER_CHANNEL); - // Set up the UTIMER compare interrupt, to be used later. + // Set up the UTIMER compare A interrupt, to be used by system_tick_wfe_with_timeout_us. system_tick_nvic_config(2); UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_COMPARE_CTRL_A |= COMPARE_CTRL_DRV_COMPARE_EN; + + // Set up the UTIMER compare B interrupt, to be used by soft-timer. + system_tick_nvic_config(4); + UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_COMPARE_CTRL_B |= COMPARE_CTRL_DRV_COMPARE_EN; } // COMPARE_A_BUF1 @@ -73,6 +77,17 @@ void UTIMER_IRQ90Handler(void) { } } +// COMPARE_B_BUF1 +void UTIMER_IRQ92Handler(void) { + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + if (chan_interrupt & CHAN_INTERRUPT_COMPARE_B_BUF1_MASK) { + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + system_tick_schedule_callback(); + __SEV(); + } +} + // OVER_FLOW void UTIMER_IRQ95Handler(void) { uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; @@ -106,7 +121,7 @@ uint64_t system_tick_get_u64(void) { } void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { - // Maximum 10 second timeout, to not overflow signed 32-bit ticks when + // Maximum 10 second timeout, to not overflow 32-bit ticks when // system_core_clock_mhz==400. uint32_t timeout_ticks = MIN(timeout_us, 10000000) * system_core_clock_mhz; @@ -126,3 +141,28 @@ void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { // Disable the UTIMER compare interrupt. utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); } + +void system_tick_schedule_after_us(uint32_t ticks_us) { + // Disable the interrupt in case it's still active. + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + + // Maximum 10 second timeout, to not overflow 32-bit ticks when + // system_core_clock_mhz==400. + uint32_t timeout_ticks = MIN(ticks_us, 10000000) * system_core_clock_mhz; + + // Set up the UTIMER compare interrupt to fire after the given timeout. + uint32_t cntr = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + utimer_set_count(UTIMER, UTIMER_CHANNEL, UTIMER_COMPARE_B_BUF1, cntr + timeout_ticks); + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + utimer_unmask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + + // Handle the case of short timeouts. + uint32_t cntr2 = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + if ((uint32_t)(cntr2 - cntr) >= timeout_ticks) { + if (!(UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT_MASK & CHAN_INTERRUPT_COMPARE_B_BUF1_MASK)) { + // Interrupt is still enabled, so disable it and manually call the callback. + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + system_tick_schedule_callback(); + } + } +} diff --git a/ports/alif/system_tick.h b/ports/alif/system_tick.h index 307c87a345fb8..2373f9421de7b 100644 --- a/ports/alif/system_tick.h +++ b/ports/alif/system_tick.h @@ -34,5 +34,7 @@ void system_tick_init(void); uint32_t system_tick_get_u32(void); uint64_t system_tick_get_u64(void); void system_tick_wfe_with_timeout_us(uint32_t timeout_us); +void system_tick_schedule_after_us(uint32_t ticks_us); +void system_tick_schedule_callback(void); #endif // MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H From 53b7c14836b00a432855439513721dcf07392041 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Jan 2024 23:41:10 +1100 Subject: [PATCH 465/546] alif/modmachine: Enable machine.Timer. Signed-off-by: Damien George --- ports/alif/modmachine.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index c4102225dabfc..e19150e010d0b 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -29,6 +29,7 @@ #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ + { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, \ static void mp_machine_idle(void) { mp_event_wait_indefinite(); From 4e62ade4421337944a12a53c05ddd4498ba3be8f Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Jan 2024 12:00:18 +1100 Subject: [PATCH 466/546] alif/se_services: Add SE services interface. Includes services to get random numbers, reset SoC, get unique-id, dump SoC info, and CPU control services. Signed-off-by: Damien George Signed-off-by: iabdalkader --- ports/alif/Makefile | 12 ++ ports/alif/irq.h | 1 + ports/alif/main.c | 2 + ports/alif/se_services.c | 247 +++++++++++++++++++++++++++++++++++++++ ports/alif/se_services.h | 47 ++++++++ 5 files changed, 309 insertions(+) create mode 100644 ports/alif/se_services.c create mode 100644 ports/alif/se_services.h diff --git a/ports/alif/Makefile b/ports/alif/Makefile index cd02f4ac2d61c..656cdc0e4cd44 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -42,6 +42,7 @@ INC += -I$(BUILD) INC += -I$(BOARD_DIR) INC += -I$(CMSIS_DIR) INC += -I$(ALIF_DFP_REL_HERE)/drivers/include/ +INC += -I$(ALIF_DFP_REL_HERE)/se_services/include/ INC += -I$(ALIF_DFP_REL_HERE)/ospi_xip/source/ospi INC += -I$(ALIF_DFP_REL_HERE)/Device/common/config/ INC += -I$(ALIF_DFP_REL_HERE)/Device/common/include/ @@ -120,6 +121,7 @@ SRC_C = \ ospi_flash.c \ pendsv.c \ system_tick.c \ + se_services.c \ usbd.c \ $(wildcard $(BOARD_DIR)/*.c) @@ -174,13 +176,23 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/common/source/system_M55.c \ Device/common/source/system_utils.c \ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ + drivers/source/mhu_driver.c \ + drivers/source/mhu_receiver.c \ + drivers/source/mhu_sender.c \ drivers/source/pinconf.c \ drivers/source/uart.c \ drivers/source/utimer.c \ ospi_xip/source/ospi/ospi_drv.c \ + se_services/source/services_host_application.c \ + se_services/source/services_host_boot.c \ + se_services/source/services_host_clocks.c \ + se_services/source/services_host_cryptocell.c \ + se_services/source/services_host_handler.c \ + se_services/source/services_host_system.c \ ) $(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 +$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation # List of sources for qstr extraction SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) $(GEN_PINS_SRC) diff --git a/ports/alif/irq.h b/ports/alif/irq.h index 723f89f1d907e..e2e0685c679a9 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -40,6 +40,7 @@ #define NVIC_PRIORITYGROUP_7 ((uint32_t)0x00000000U) #define IRQ_PRI_SYSTEM_TICK NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 0, 0) +#define IRQ_PRI_MHU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 0, 0) #define IRQ_PRI_QUIET_TIMING NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) diff --git a/ports/alif/main.c b/ports/alif/main.c index b4e2434399970..2d3134176d32e 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -39,6 +39,7 @@ #include "mpuart.h" #include "ospi_flash.h" #include "pendsv.h" +#include "se_services.h" #include "system_tick.h" extern uint8_t __StackTop, __StackLimit; @@ -60,6 +61,7 @@ void _start(void) { MICROPY_BOARD_STARTUP(); pendsv_init(); + se_services_init(); MICROPY_BOARD_EARLY_INIT(); diff --git a/ports/alif/se_services.c b/ports/alif/se_services.c new file mode 100644 index 0000000000000..cbf66bfac6ccd --- /dev/null +++ b/ports/alif/se_services.c @@ -0,0 +1,247 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "irq.h" +#include "se_services.h" + +#include "mhu.h" +#include "services_lib_bare_metal.h" +#include "services_lib_protocol.h" + +#include "py/mphal.h" + +// MHU indices. +#define MHU_M55_SE_MHU0 0 +#define MAX_MHU 1 + +// The following timeout is implemented in se_services_handle.c as a +// simple loop busy polling on a variable set from an IRQ. +#define TIMEOUT 10000000 + +typedef struct { + volatile unsigned int RST_CTRL; // 0x1A010318 + volatile unsigned int RST_STAT; // 0x1A01031C +} CPU_Type; + +// HE CPU register flags +#define RST_CTRL_CPUWAIT_MASK (1 << 0) +#define RST_CTRL_RST_REQ_MASK (1 << 1) +#define RST_STAT_RST_ACK_MASK (3 << 1) +#define HE_CPU ((CPU_Type *)0x1A010318) + +static const uint32_t mhu_sender_base_address_list[MAX_MHU] = { + MHU_SESS_S_TX_BASE, +}; + +static const uint32_t mhu_receiver_base_address_list[MAX_MHU] = { + MHU_SESS_S_RX_BASE, +}; + +// Must be aligned as a uint32_t. +static uint32_t packet_buffer[SERVICES_MAX_PACKET_BUFFER_SIZE / sizeof(uint32_t)]; + +static mhu_driver_out_t mhu_driver_out; +static uint32_t se_services_handle; + +void MHU_SESS_S_TX_IRQHandler(void) { + mhu_driver_out.sender_irq_handler(MHU_M55_SE_MHU0); +} + +void MHU_SESS_S_RX_IRQHandler(void) { + mhu_driver_out.receiver_irq_handler(MHU_M55_SE_MHU0); +} + +int dummy_printf(const char *fmt, ...) { + (void)fmt; + return 0; +} + +void se_services_init(void) { + // Initialize MHU. + mhu_driver_in_t mhu_driver_in; + mhu_driver_in.sender_base_address_list = (uint32_t *)mhu_sender_base_address_list; + mhu_driver_in.receiver_base_address_list = (uint32_t *)mhu_receiver_base_address_list; + mhu_driver_in.mhu_count = MAX_MHU; + mhu_driver_in.send_msg_acked_callback = SERVICES_send_msg_acked_callback; + mhu_driver_in.rx_msg_callback = SERVICES_rx_msg_callback; + mhu_driver_in.debug_print = NULL; // not currently used by MHU_driver_initialize + MHU_driver_initialize(&mhu_driver_in, &mhu_driver_out); + + // Initialize SE services. + services_lib_t services_init_params = { + .packet_buffer_address = (uint32_t)packet_buffer, + .fn_send_mhu_message = mhu_driver_out.send_message, + .fn_wait_ms = NULL, // not currently used by services_host_handler.c + .wait_timeout = TIMEOUT, + .fn_print_msg = dummy_printf, + }; + SERVICES_initialize(&services_init_params); + + // Create SE services channel for sending requests. + se_services_handle = SERVICES_register_channel(MHU_M55_SE_MHU0, 0); + + // Enable MHU interrupts. + NVIC_ClearPendingIRQ(MHU_SESS_S_RX_IRQ_IRQn); + NVIC_SetPriority(MHU_SESS_S_RX_IRQ_IRQn, IRQ_PRI_MHU); + NVIC_EnableIRQ(MHU_SESS_S_RX_IRQ_IRQn); + NVIC_ClearPendingIRQ(MHU_SESS_S_TX_IRQ_IRQn); + NVIC_SetPriority(MHU_SESS_S_TX_IRQ_IRQn, IRQ_PRI_MHU); + NVIC_EnableIRQ(MHU_SESS_S_TX_IRQ_IRQn); + + // Send heartbeat services requests until one succeeds. + SERVICES_synchronize_with_se(se_services_handle); +} + +void se_services_dump_device_data(void) { + uint32_t error_code; + + uint8_t revision[80]; + SERVICES_get_se_revision(se_services_handle, revision, &error_code); + + SERVICES_version_data_t data; + SERVICES_system_get_device_data(se_services_handle, &data, &error_code); + + printf("SE revision: %s\n", revision); + printf("ALIF_PN: %s\n", data.ALIF_PN); + printf("Raw device data:\n"); + for (int i = 0; i < sizeof(data); ++i) { + printf(" %02x", ((uint8_t *)&data)[i]); + if (i % 16 == 15) { + printf("\n"); + } + } + printf("\n"); +} + +void se_services_get_unique_id(uint8_t id[5]) { + uint32_t error_code; + SERVICES_version_data_t data; + SERVICES_system_get_device_data(se_services_handle, &data, &error_code); + // The MfgData has 5 bytes of valid data, at least on REV_B2. + memcpy(id, data.MfgData, 5); +} + +__attribute__((noreturn)) void se_services_reset_soc(void) { + SERVICES_boot_reset_soc(se_services_handle); + NVIC_SystemReset(); +} + +uint64_t se_services_rand64(void) { + // If the SE core is not ready then the return value can be + // SERVICES_REQ_NOT_ACKNOWLEDGE. So retry a few times. + for (int retry = 0; retry < 100; ++retry) { + uint64_t value; + int32_t error_code; + uint32_t ret = SERVICES_cryptocell_get_rnd(se_services_handle, sizeof(uint64_t), &value, &error_code); + if (ret == SERVICES_REQ_SUCCESS) { + return value; + } + } + + // No random number available. + return 0; +} + +uint32_t se_services_enable_clock(clock_enable_t clock, bool enable) { + uint32_t error_code; + SERVICES_clocks_enable_clock(se_services_handle, clock, enable, &error_code); + return error_code; +} + +uint32_t se_services_select_pll_source(pll_source_t source, pll_target_t target) { + uint32_t error_code; + SERVICES_clocks_select_pll_source(se_services_handle, source, target, &error_code); + return error_code; +} + +uint32_t se_services_get_run_profile(run_profile_t *profile) { + uint32_t error_code; + SERVICES_get_run_cfg(se_services_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_set_run_profile(run_profile_t *profile) { + uint32_t error_code; + SERVICES_set_run_cfg(se_services_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_get_off_profile(off_profile_t *profile) { + uint32_t error_code; + SERVICES_get_off_cfg(se_services_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_set_off_profile(off_profile_t *profile) { + uint32_t error_code; + SERVICES_set_off_cfg(se_services_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_boot_process_toc_entry(const uint8_t *image_id) { + uint32_t error_code; + SERVICES_boot_process_toc_entry(se_services_handle, image_id, &error_code); + return error_code; +} + +uint32_t se_services_boot_cpu(uint32_t cpu_id, uint32_t address) { + uint32_t error_code; + SERVICES_boot_cpu(se_services_handle, cpu_id, address, &error_code); + return error_code; +} + +uint32_t se_services_boot_reset_cpu(uint32_t cpu_id) { + uint32_t error_code; + if (HE_CPU->RST_CTRL & RST_CTRL_CPUWAIT_MASK) { + // CPU held in reset + return SERVICES_REQ_SUCCESS; + } + + for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(1)) { + uint32_t ret = SERVICES_boot_reset_cpu(se_services_handle, cpu_id, &error_code); + if (ret != SERVICES_REQ_SUCCESS) { + return error_code; + } + + if ((HE_CPU->RST_STAT & RST_STAT_RST_ACK_MASK) == 0x4) { + return SERVICES_REQ_SUCCESS; + } + + if ((mp_hal_ticks_ms() - start) >= 100) { + return SERVICES_REQ_TIMEOUT; + } + } + return error_code; +} + +uint32_t se_services_boot_release_cpu(uint32_t cpu_id) { + uint32_t error_code; + SERVICES_boot_release_cpu(se_services_handle, cpu_id, &error_code); + return error_code; +} diff --git a/ports/alif/se_services.h b/ports/alif/se_services.h new file mode 100644 index 0000000000000..b1079130c1635 --- /dev/null +++ b/ports/alif/se_services.h @@ -0,0 +1,47 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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_ALIF_SE_SERVICES_H +#define MICROPY_INCLUDED_ALIF_SE_SERVICES_H + +#include "services_lib_api.h" + +void se_services_init(void); +void se_services_dump_device_data(void); +void se_services_get_unique_id(uint8_t id[5]); +__attribute__((noreturn)) void se_services_reset_soc(void); +uint64_t se_services_rand64(void); +uint32_t se_services_enable_clock(clock_enable_t clock, bool enable); +uint32_t se_services_select_pll_source(pll_source_t source, pll_target_t target); +uint32_t se_services_get_run_profile(run_profile_t *profile); +uint32_t se_services_set_run_profile(run_profile_t *profile); +uint32_t se_services_get_off_profile(off_profile_t *profile); +uint32_t se_services_set_off_profile(off_profile_t *profile); + +uint32_t se_services_boot_process_toc_entry(const uint8_t *image_id); +uint32_t se_services_boot_cpu(uint32_t cpu_id, uint32_t address); +uint32_t se_services_boot_reset_cpu(uint32_t cpu_id); +uint32_t se_services_boot_release_cpu(uint32_t cpu_id); +#endif // MICROPY_INCLUDED_ALIF_SE_SERVICES_H From c6cb082ed1716396f436ae4983131be2dcbf0447 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Jan 2024 12:00:37 +1100 Subject: [PATCH 467/546] alif/mpconfigport: Enable os.urandom(). Uses the SE services to provide random numbers. Signed-off-by: Damien George --- ports/alif/modos.c | 49 +++++++++++++++++++++++++++++++++++++++ ports/alif/mpconfigport.h | 2 ++ 2 files changed, 51 insertions(+) create mode 100644 ports/alif/modos.c diff --git a/ports/alif/modos.c b/ports/alif/modos.c new file mode 100644 index 0000000000000..9c8c84268ca16 --- /dev/null +++ b/ports/alif/modos.c @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "se_services.h" + +#if MICROPY_PY_OS_URANDOM +static mp_obj_t mp_os_urandom(mp_obj_t num) { + mp_int_t n = mp_obj_get_int(num); + vstr_t vstr; + vstr_init_len(&vstr, n); + uint64_t rnd = 0; + size_t rnd_bits = 0; + for (int i = 0; i < n; i++) { + if (rnd_bits == 0) { + rnd = se_services_rand64(); + rnd_bits = 64; + } + vstr.buf[i] = rnd; + rnd >>= 8; + rnd_bits -= 8; + } + return mp_obj_new_bytes_from_vstr(&vstr); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_os_urandom_obj, mp_os_urandom); +#endif diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 2d8e3b2a09dbe..74cfe8edaab74 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -87,10 +87,12 @@ // Extended modules #define MICROPY_EPOCH_IS_1970 (1) +#define MICROPY_PY_OS_INCLUDEFILE "ports/alif/modos.c" #define MICROPY_PY_OS_DUPTERM (1) #define MICROPY_PY_OS_SEP (1) #define MICROPY_PY_OS_SYNC (1) #define MICROPY_PY_OS_UNAME (1) +#define MICROPY_PY_OS_URANDOM (1) #define MICROPY_PY_TIME (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/alif/modmachine.c" From 64af93e74e9de36fd6106e6bea4923fc300afb49 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Jan 2024 12:03:46 +1100 Subject: [PATCH 468/546] alif/mpconfigport: Enable MICROPY_PY_RANDOM_SEED_INIT_FUNC. Uses the SE services to provide a random seed. Signed-off-by: Damien George --- ports/alif/mpconfigport.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 74cfe8edaab74..b01eb1908abb0 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -93,6 +93,7 @@ #define MICROPY_PY_OS_SYNC (1) #define MICROPY_PY_OS_UNAME (1) #define MICROPY_PY_OS_URANDOM (1) +#define MICROPY_PY_RANDOM_SEED_INIT_FUNC (se_services_rand64()) #define MICROPY_PY_TIME (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/alif/modmachine.c" @@ -155,3 +156,6 @@ extern void panic(const char *); #ifndef MICROPY_BOARD_ENTER_BOOTLOADER #define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) #endif + +// Needed for MICROPY_PY_RANDOM_SEED_INIT_FUNC. +uint64_t se_services_rand64(void); From 2a580b05ada2ce6c87d6e94196178c511ef7acdf Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Feb 2024 17:50:15 +1100 Subject: [PATCH 469/546] alif/modalif: Add alif.info() function. Calls the SE services to print information about the SoC. Signed-off-by: Damien George --- ports/alif/modalif.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/alif/modalif.c b/ports/alif/modalif.c index 69b7e6c5c8e87..5577b53eaca85 100644 --- a/ports/alif/modalif.c +++ b/ports/alif/modalif.c @@ -27,10 +27,18 @@ #include "py/mphal.h" #include "py/runtime.h" #include "modalif.h" +#include "se_services.h" + +static mp_obj_t alif_info(void) { + se_services_dump_device_data(); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(alif_info_obj, alif_info); static const mp_rom_map_elem_t alif_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_alif) }, { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&alif_flash_type) }, + { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&alif_info_obj) }, #if MICROPY_HW_USB_MSC // Attribute to indicate USB MSC is enabled. { MP_ROM_QSTR(MP_QSTR_usb_msc), MP_ROM_TRUE }, From 2f85a19d7d7c342aefa6e3d628c1d398f53d3adf Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Feb 2024 17:50:34 +1100 Subject: [PATCH 470/546] alif/modmachine: Implement machine.unique_id(), fix machine.reset(). They both use SE services. Signed-off-by: Damien George --- ports/alif/modmachine.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index e19150e010d0b..29a70305dd516 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -27,6 +27,8 @@ // This file is never compiled standalone, it's included directly from // extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE. +#include "se_services.h" + #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, \ @@ -36,11 +38,13 @@ static void mp_machine_idle(void) { } static mp_obj_t mp_machine_unique_id(void) { - return mp_obj_new_bytes((const uint8_t *)"ABCD", 4); + uint8_t id[5]; + se_services_get_unique_id(id); + return mp_obj_new_bytes(id, sizeof(id)); } NORETURN static void mp_machine_reset(void) { - NVIC_SystemReset(); + se_services_reset_soc(); } NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { From 31d18c5885fd53c6aacc80428bdfb8b0bbdcd7cb Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Feb 2024 17:51:03 +1100 Subject: [PATCH 471/546] alif/usbd: Implement proper USB serial number. Using SE services to get the SoC unique id. Signed-off-by: Damien George --- ports/alif/usbd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ports/alif/usbd.c b/ports/alif/usbd.c index 9df6610683507..da16905d43cd9 100644 --- a/ports/alif/usbd.c +++ b/ports/alif/usbd.c @@ -30,10 +30,11 @@ #include "shared/tinyusb/mp_usbd.h" #include "tusb.h" +#include "se_services.h" void mp_usbd_port_get_serial_number(char *serial_buf) { - // TODO - uint8_t id[8] = "ABCDEFGH"; + uint8_t id[5]; + se_services_get_unique_id(id); MP_STATIC_ASSERT(sizeof(id) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); mp_usbd_hex_str(serial_buf, id, sizeof(id)); } From 62beb541e76e7776cb402fabc10a6116da8f2dcf Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 13 Feb 2024 18:05:05 +1100 Subject: [PATCH 472/546] alif/machine_adc: Add basic ADC support. ADC12 information has been added to pin struct. Signed-off-by: Damien George --- ports/alif/Makefile | 1 + ports/alif/irq.h | 1 + ports/alif/machine_adc.c | 225 ++++++++++++++++++++++++++++++++++++ ports/alif/mcu/make-pins.py | 33 +++++- ports/alif/mpconfigport.h | 2 + ports/alif/mphalport.h | 2 + 6 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 ports/alif/machine_adc.c diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 656cdc0e4cd44..068e34575a143 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -176,6 +176,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/common/source/system_M55.c \ Device/common/source/system_utils.c \ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ + drivers/source/adc.c \ drivers/source/mhu_driver.c \ drivers/source/mhu_receiver.c \ drivers/source/mhu_sender.c \ diff --git a/ports/alif/irq.h b/ports/alif/irq.h index e2e0685c679a9..a20ff7aebdef1 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -43,6 +43,7 @@ #define IRQ_PRI_MHU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 0, 0) #define IRQ_PRI_QUIET_TIMING NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) +#define IRQ_PRI_ADC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 3, 0) #define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) diff --git a/ports/alif/machine_adc.c b/ports/alif/machine_adc.c new file mode 100644 index 0000000000000..91eb95b5ba002 --- /dev/null +++ b/ports/alif/machine_adc.c @@ -0,0 +1,225 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ + +// This file is never compiled standalone, it's included directly from +// extmod/machine_adc.c via MICROPY_PY_MACHINE_ADC_INCLUDEFILE. + +#include "py/mphal.h" + +#include "analog_config.h" +#include "adc.h" +#include "sys_ctrl_adc.h" + +#define ADC_CHANNEL_TEMP_SENSOR (6) +#define ADC_CHANNEL_INT_VREF (7) + +static const uint8_t adc_instance_table[4] = { + ADC_INSTANCE_ADC12_0, + ADC_INSTANCE_ADC12_1, + ADC_INSTANCE_ADC12_2, + ADC_INSTANCE_ADC24_0, +}; + +static ADC_Type *const adc_regs_table[4] = { + (ADC_Type *)ADC120_BASE, + (ADC_Type *)ADC121_BASE, + (ADC_Type *)ADC122_BASE, + (ADC_Type *)ADC24_BASE, +}; + +static bool adc_inited[4]; +static conv_info_t adc_conv_info_table[4]; + +static void adc_nvic_config(unsigned int irq) { + NVIC_ClearPendingIRQ(irq); + NVIC_SetPriority(irq, IRQ_PRI_ADC); + NVIC_EnableIRQ(irq); +} + +static void adc_init(uint8_t adc_periph) { + if (adc_inited[adc_periph]) { + return; + } + + ADC_Type *regs = adc_regs_table[adc_periph]; + uint8_t adc_instance = adc_instance_table[adc_periph]; + conv_info_t *conv_info = &adc_conv_info_table[adc_periph]; + + adc_set_clk_control(adc_instance, true); + enable_cmp_periph_clk(); + analog_config_vbat_reg2(); + analog_config_cmp_reg2(); + adc_set_differential_ctrl(adc_instance, false); + adc_set_comparator_ctrl(adc_instance, true, 2); + + if (adc_instance == ADC_INSTANCE_ADC24_0) { + enable_adc24(); + set_adc24_output_rate(0); + set_adc24_bias(0); + } else { + adc_set_sample_width(regs, 32); + } + + adc_set_clk_div(regs, 4); + adc_set_avg_sample(regs, 32); + adc_set_n_shift_bit(regs, 1, 1); + adc_set_single_ch_scan_mode(regs, conv_info); + adc_unmask_interrupt(regs); + + adc_nvic_config(ADC120_DONE0_IRQ_IRQn); + adc_nvic_config(ADC120_DONE1_IRQ_IRQn); + adc_nvic_config(ADC121_DONE0_IRQ_IRQn); + adc_nvic_config(ADC121_DONE1_IRQ_IRQn); + adc_nvic_config(ADC122_DONE0_IRQ_IRQn); + adc_nvic_config(ADC122_DONE1_IRQ_IRQn); +} + +static uint16_t adc_config_and_read_u16(unsigned int adc_periph, uint32_t channel) { + ADC_Type *adc_regs = adc_regs_table[adc_periph]; + conv_info_t *conv_info = &adc_conv_info_table[adc_periph]; + + conv_info->status = ADC_CONV_STAT_NONE; + conv_info->mode = ADC_CONV_MODE_SINGLE_SHOT; + + // Select channel and start conversion. + adc_init_channel_select(adc_regs, channel); + adc_set_single_ch_scan_mode(adc_regs, conv_info); + adc_enable_single_shot_conv(adc_regs); + + // Wait for conversion to complete. + while (!(conv_info->status & ADC_CONV_STAT_COMPLETE)) { + __WFE(); + } + + return conv_info->sampled_value; +} + +static void adc_done0_irq_handler_helper(unsigned int adc_periph) { + adc_done0_irq_handler(adc_regs_table[adc_periph], &adc_conv_info_table[adc_periph]); +} + +static void adc_done1_irq_handler_helper(unsigned int adc_periph) { + adc_done1_irq_handler(adc_regs_table[adc_periph], &adc_conv_info_table[adc_periph]); +} + +void ADC120_DONE0_IRQHandler(void) { + adc_done0_irq_handler_helper(0); + __SEV(); +} + +void ADC120_DONE1_IRQHandler(void) { + adc_done1_irq_handler_helper(0); + __SEV(); +} + +void ADC121_DONE0_IRQHandler(void) { + adc_done0_irq_handler_helper(1); + __SEV(); +} + +void ADC121_DONE1_IRQHandler(void) { + adc_done1_irq_handler_helper(1); + __SEV(); +} + +void ADC122_DONE0_IRQHandler(void) { + adc_done0_irq_handler_helper(2); + __SEV(); +} + +void ADC122_DONE1_IRQHandler(void) { + adc_done1_irq_handler_helper(2); + __SEV(); +} + +/******************************************************************************/ +// MicroPython bindings for machine.ADC + +#define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS \ + { MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMP_SENSOR) }, \ + { MP_ROM_QSTR(MP_QSTR_CORE_VREF), MP_ROM_INT(ADC_CHANNEL_INT_VREF) }, \ + +typedef struct _machine_adc_obj_t { + mp_obj_base_t base; + uint8_t adc_periph; + uint8_t adc_channel; +} machine_adc_obj_t; + +static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "", self->adc_periph, self->adc_channel); +} + +// ADC(id) +static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Check number of arguments + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_obj_t source = all_args[0]; + + uint8_t adc_periph; + uint8_t adc_channel; + const machine_pin_obj_t *pin = NULL; + + if (mp_obj_is_int(source)) { + // Get and validate channel number. + adc_channel = mp_obj_get_int(source); + if (adc_channel < ADC_CHANNEL_TEMP_SENSOR) { + adc_periph = 0; + } else if (adc_channel == ADC_CHANNEL_TEMP_SENSOR || adc_channel == ADC_CHANNEL_INT_VREF) { + adc_periph = 2; + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid channel")); + } + } else { + // Get GPIO and check it has ADC capabilities. + pin = mp_hal_get_pin_obj(source); + if (pin->adc12_periph <= 2 && pin->adc12_channel <= 5) { + // Select the ADC12 peripheral and channel. + adc_periph = pin->adc12_periph; + adc_channel = pin->adc12_channel; + // Configure the pad for analog input. + pinconf_set(pin->port, pin->pin, PINMUX_ALTERNATE_FUNCTION_7, PADCTRL_READ_ENABLE); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("Pin doesn't have ADC capabilities")); + } + } + + // Initialise the ADC peripheral. + adc_init(adc_periph); + + // Create ADC object. + machine_adc_obj_t *o = mp_obj_malloc(machine_adc_obj_t, &machine_adc_type); + o->adc_periph = adc_periph; + o->adc_channel = adc_channel; + + return MP_OBJ_FROM_PTR(o); +} + +// read_u16() +static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { + return adc_config_and_read_u16(self->adc_periph, self->adc_channel); +} diff --git a/ports/alif/mcu/make-pins.py b/ports/alif/mcu/make-pins.py index b49db4cfe49bb..d4fbf5d8b8d9a 100755 --- a/ports/alif/mcu/make-pins.py +++ b/ports/alif/mcu/make-pins.py @@ -10,11 +10,34 @@ NUM_PORTS = 16 NUM_PINS_PER_PORT = 8 +# Maps pad name to (adc12_periph, adc12_channel). +ADC12_ANA_MAP = { + "P0_0": (0, 0), + "P0_1": (0, 1), + "P0_2": (0, 2), + "P0_3": (0, 3), + "P0_4": (0, 4), + "P0_5": (0, 5), + "P0_6": (1, 0), + "P0_7": (1, 1), + "P1_0": (1, 2), + "P1_1": (1, 3), + "P1_2": (1, 4), + "P1_3": (1, 5), + "P1_4": (2, 0), + "P1_5": (2, 1), + "P1_6": (2, 2), + "P1_7": (2, 3), + "P2_0": (2, 4), + "P2_1": (2, 5), +} + class AlifPin(boardgen.Pin): # Emit the struct which contains the pin instance. def definition(self): port, pin = self.name()[1:].split("_") + adc12_periph, adc12_channel = ADC12_ANA_MAP.get(self.name(), (3, 7)) base = "LPGPIO_BASE" if port == "15" else "GPIO{}_BASE".format(port) return ( "{{ " @@ -22,8 +45,16 @@ def definition(self): ".gpio = (GPIO_Type *){base}, " ".port = PORT_{port}, " ".pin = PIN_{pin}, " + ".adc12_periph = {adc12_periph}, " + ".adc12_channel = {adc12_channel}, " ".name = MP_QSTR_P{port}_{pin} " - "}}".format(port=port, pin=pin, base=base) + "}}".format( + port=port, + pin=pin, + base=base, + adc12_periph=adc12_periph, + adc12_channel=adc12_channel, + ) ) # Alif cpu names must be "Pn_m". diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index b01eb1908abb0..95b5e3ca9fa24 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -101,6 +101,8 @@ #define MICROPY_PY_MACHINE_BOOTLOADER (1) #define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1) #define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1) +#define MICROPY_PY_MACHINE_ADC (1) +#define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/alif/machine_adc.c" #define MICROPY_PY_MACHINE_DHT_READINTO (1) #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 8098c2650df01..7b054b671bcd6 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -85,6 +85,8 @@ typedef struct _machine_pin_obj_t { GPIO_Type *gpio; uint8_t port; uint8_t pin; + uint8_t adc12_periph : 2; + uint8_t adc12_channel : 3; qstr name; } machine_pin_obj_t; From b7df5aa86a6f150ab3282f0525fe9c3538ceaef7 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 10 Jul 2024 20:30:02 +0300 Subject: [PATCH 473/546] alif/mcu: Add ToC config for dual images. Note that 128K at the beginning of MRAM is reserved for future bootloaders. Signed-off-by: iabdalkader --- ports/alif/mcu/M55_DUAL_cfg.json | 26 ++++++++++++++++++++++++++ ports/alif/mcu/M55_HE_cfg.json | 17 +++++++++++++++++ ports/alif/mcu/M55_HP_cfg.json | 17 ++++++++++------- 3 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 ports/alif/mcu/M55_DUAL_cfg.json create mode 100644 ports/alif/mcu/M55_HE_cfg.json diff --git a/ports/alif/mcu/M55_DUAL_cfg.json b/ports/alif/mcu/M55_DUAL_cfg.json new file mode 100644 index 0000000000000..81beef5f03614 --- /dev/null +++ b/ports/alif/mcu/M55_DUAL_cfg.json @@ -0,0 +1,26 @@ +{ + "DEVICE": { + "disabled" : false, + "binary": "app-device-config.json", + "version" : "0.5.00", + "signed": false + }, + "HP_APP": { + "disabled" : false, + "binary": "M55_HP/firmware.bin", + "mramAddress": "0x80020000", + "version": "1.0.0", + "cpu_id": "M55_HP", + "flags": ["boot"], + "signed": false + }, + "HE_APP": { + "disabled" : false, + "binary": "M55_HE/firmware.bin", + "mramAddress": "0x80320000", + "version": "1.0.0", + "cpu_id": "M55_HE", + "flags": ["deferred"], + "signed": false + } +} diff --git a/ports/alif/mcu/M55_HE_cfg.json b/ports/alif/mcu/M55_HE_cfg.json new file mode 100644 index 0000000000000..83898418043ec --- /dev/null +++ b/ports/alif/mcu/M55_HE_cfg.json @@ -0,0 +1,17 @@ +{ + "DEVICE": { + "disabled" : false, + "binary": "app-device-config.json", + "version" : "0.5.00", + "signed": false + }, + "HE_APP": { + "disabled" : false, + "binary": "M55_HE/firmware.bin", + "mramAddress": "0x80320000", + "version": "1.0.0", + "cpu_id": "M55_HE", + "flags": ["boot"], + "signed": false + } +} diff --git a/ports/alif/mcu/M55_HP_cfg.json b/ports/alif/mcu/M55_HP_cfg.json index 228ddbf6c9464..d6710f69c77b6 100644 --- a/ports/alif/mcu/M55_HP_cfg.json +++ b/ports/alif/mcu/M55_HP_cfg.json @@ -1,14 +1,17 @@ { - "USER_APP": { - "binary": "firmware.bin", - "mramAddress": "0x80000000", + "DEVICE": { + "disabled" : false, + "binary": "app-device-config.json", + "version" : "0.5.00", + "signed": false + }, + "HP_APP": { + "disabled" : false, + "binary": "M55_HP/firmware.bin", + "mramAddress": "0x80020000", "version": "1.0.0", "cpu_id": "M55_HP", "flags": ["boot"], "signed": false - }, - "DEVICE": { - "binary": "app-device-config.json", - "version" : "0.5.00" } } From 8f82089bd051eb154e7c55951dd29220bde94611 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 10 Jul 2024 20:27:18 +0300 Subject: [PATCH 474/546] alif: Support building the port for HE or HP or both cores. With this new Makefile you can build the following: make BOARD=MY_BOARD MCU_CORE=M55_HP # build HP firmware/ToC. make BOARD=MY_BOARD MCU_CORE=M55_HE # build HE firmware/ToC. make BOARD=MY_BOARD MCU_CORE=M55_DUAL # build HE+HP firmware + ToC. Signed-off-by: iabdalkader --- ports/alif/Makefile | 269 +++++++---------------------------- ports/alif/alif.mk | 257 +++++++++++++++++++++++++++++++++ ports/alif/mcu/ensemble.ld.S | 43 ++++-- 3 files changed, 343 insertions(+), 226 deletions(-) create mode 100644 ports/alif/alif.mk diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 068e34575a143..5cc334a7cf9f9 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -1,242 +1,96 @@ -################################################################################ -# Initial setup of Makefile environment - BOARD ?= ALIF_ENSEMBLE BOARD_DIR ?= boards/$(BOARD) BUILD ?= build-$(BOARD) - -ifeq ($(wildcard $(BOARD_DIR)/.),) -$(error Invalid BOARD specified: $(BOARD_DIR)) -endif - -include ../../py/mkenv.mk -include mpconfigport.mk -include $(BOARD_DIR)/mpconfigboard.mk - -# qstr definitions (must come before including py.mk) -QSTR_DEFS += qstrdefsport.h - -# include py core make definitions -include $(TOP)/py/py.mk -include $(TOP)/extmod/extmod.mk - -################################################################################ -# Project specific settings and compiler/linker flags - -CROSS_COMPILE ?= arm-none-eabi- +MCU_CORE ?= M55_HP GIT_SUBMODULES += lib/tinyusb lib/alif_ensemble-cmsis-dfp lib/alif-security-toolkit PORT ?= /dev/ttyACM0 ALIF_TOOLS ?= ../../lib/alif-security-toolkit/toolkit -ALIF_DFP_REL_TOP ?= lib/alif_ensemble-cmsis-dfp -ALIF_DFP_REL_HERE ?= $(TOP)/lib/alif_ensemble-cmsis-dfp -CMSIS_DIR ?= $(TOP)/lib/cmsis/inc - -MCU_CORE ?= M55_HP -ALIF_CONFIG ?= mcu/$(MCU_CORE)_cfg.json -LD_FILE ?= mcu/ensemble.ld.S - -INC += -I. -INC += -I$(TOP) -INC += -I$(BUILD) -INC += -I$(BOARD_DIR) -INC += -I$(CMSIS_DIR) -INC += -I$(ALIF_DFP_REL_HERE)/drivers/include/ -INC += -I$(ALIF_DFP_REL_HERE)/se_services/include/ -INC += -I$(ALIF_DFP_REL_HERE)/ospi_xip/source/ospi -INC += -I$(ALIF_DFP_REL_HERE)/Device/common/config/ -INC += -I$(ALIF_DFP_REL_HERE)/Device/common/include/ -INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/config/ -INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/include/ -INC += -I$(ALIF_DFP_REL_HERE)/Device/$(MCU_SERIES)/$(MCU_VARIANT)/ -INC += -I$(TOP)/lib/tinyusb/src -INC += -Itinyusb_port - -GEN_PIN_MKPINS = mcu/make-pins.py -GEN_PIN_PREFIX = mcu/pins_prefix.c -GEN_PINS_BOARD_CSV = $(BOARD_DIR)/pins.csv -GEN_PINS_SRC = $(BUILD)/pins_board.c -GEN_PINS_HDR = $(HEADER_BUILD)/pins_board.h - -CFLAGS_FPU += -mfloat-abi=hard -mfpu=fpv5-d16 -CFLAGS_CORTEX_M55 += -mthumb -mcpu=cortex-m55 -mtune=cortex-m55 $(CFLAGS_FPU) - -CFLAGS += $(INC) -Wall -Werror -std=c99 $(CFLAGS_CORTEX_M55) -nostdlib -CFLAGS += -Wdouble-promotion -Wfloat-conversion -CFLAGS += -fdata-sections -ffunction-sections -CFLAGS += -D$(MCU_CORE) -DCORE_$(MCU_CORE) -DALIF_CMSIS_H="\"$(MCU_CORE).h\"" - -ifeq ($(MICROPY_FLOAT_IMPL),float) -CFLAGS += -fsingle-precision-constant -CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT -else -CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_DOUBLE -endif - -AFLAGS = -mthumb -march=armv8.1-m.main $(CFLAGS_FPU) - -LDFLAGS += -nostdlib -LDFLAGS += -T$(BUILD)/ensemble.ld -Map=$@.map --cref --gc-sections -LDFLAGS += --wrap=dcd_event_handler -# Tune for Debugging or Optimization -ifeq ($(DEBUG), 1) -CFLAGS += -Og -ggdb3 -# Disable text compression in debug builds -MICROPY_ROM_TEXT_COMPRESSION = 0 -else -CFLAGS += -O2 -DNDEBUG -endif - -LIBS += "$(shell $(CC) $(CFLAGS) -print-libgcc-file-name)" - -JLINK_CMD = '\ +JLINK_CMD_PREFIX = \ ExitOnError 1\n\ Device $(JLINK_DEV)\n\ SelectInterface SWD\n\ Speed auto\n\ Connect\n\ Reset\n\ -ShowHWStatus\n\ -LoadFile "$(BUILD)/firmware_toc.bin",0x8057f1c0\n\ -LoadFile "$(BUILD)/firmware.bin",0x80000000\n\ -Reset\n\ -Exit' - -################################################################################ -# Source files and libraries +ShowHWStatus\n -SRC_O += \ - shared/runtime/gchelper_thumb2.o +JLINK_CMD_SUFFIX = \ +Reset\n\ +Exit -SRC_C = \ - alif_flash.c \ - fatfs_port.c \ - machine_pin.c \ - main.c \ - modalif.c \ - mphalport.c \ - mpuart.c \ - msc_disk.c \ - ospi_flash.c \ - pendsv.c \ - system_tick.c \ - se_services.c \ - usbd.c \ - $(wildcard $(BOARD_DIR)/*.c) +ifeq ($(MCU_CORE),M55_HP) -ifeq ($(MICROPY_FLOAT_IMPL),float) -LIBM_SRC_C += $(SRC_LIB_LIBM_C) -LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C) -$(BUILD)/lib/libm/%.o: CFLAGS += -Wno-maybe-uninitialized -else -LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_C) -LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_SQRT_HW_C) -$(BUILD)/lib/libm_dbl/%.o: CFLAGS += -Wno-maybe-uninitialized -endif +ALIF_TOC_CONFIG = mcu/M55_HP_cfg.json +ALIF_TOC_APPS = $(BUILD)/M55_HP/firmware.bin +JLINK_CMD = '\ +$(JLINK_CMD_PREFIX)\ +LoadFile "$(BUILD)/M55_HP/firmware.bin",0x80020000\n\ +$(JLINK_CMD_SUFFIX)' -SHARED_SRC_C += $(addprefix shared/,\ - libc/string0.c \ - netutils/dhcpserver.c \ - netutils/netutils.c \ - netutils/trace.c \ - readline/readline.c \ - runtime/gchelper_native.c \ - runtime/interrupt_char.c \ - runtime/mpirq.c \ - runtime/pyexec.c \ - runtime/softtimer.c \ - runtime/stdout_helpers.c \ - runtime/sys_stdio_mphal.c \ - timeutils/timeutils.c \ - tinyusb/mp_usbd.c \ - tinyusb/mp_usbd_cdc.c \ - tinyusb/mp_usbd_descriptor.c \ - ) +else ifeq ($(MCU_CORE),M55_HE) -DRIVERS_SRC_C += $(addprefix drivers/,\ - bus/softspi.c \ - bus/softqspi.c \ - memory/spiflash.c \ - dht/dht.c \ - ) +ALIF_TOC_CONFIG = mcu/M55_HE_cfg.json +ALIF_TOC_APPS = $(BUILD)/M55_HE/firmware.bin +JLINK_CMD = '\ +$(JLINK_CMD_PREFIX)\ +LoadFile "$(BUILD)/M55_HE/firmware.bin",0x80320000\n\ +$(JLINK_CMD_SUFFIX)' -TINYUSB_SRC_C += \ - lib/tinyusb/src/tusb.c \ - lib/tinyusb/src/class/cdc/cdc_device.c \ - lib/tinyusb/src/class/msc/msc_device.c \ - lib/tinyusb/src/common/tusb_fifo.c \ - lib/tinyusb/src/device/usbd.c \ - lib/tinyusb/src/device/usbd_control.c \ - tinyusb_port/tusb_alif_dcd.c \ +else ifeq ($(MCU_CORE),M55_DUAL) -ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ - Device/common/source/clk.c \ - Device/common/source/mpu_M55.c \ - Device/common/source/system_M55.c \ - Device/common/source/system_utils.c \ - Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ - drivers/source/adc.c \ - drivers/source/mhu_driver.c \ - drivers/source/mhu_receiver.c \ - drivers/source/mhu_sender.c \ - drivers/source/pinconf.c \ - drivers/source/uart.c \ - drivers/source/utimer.c \ - ospi_xip/source/ospi/ospi_drv.c \ - se_services/source/services_host_application.c \ - se_services/source/services_host_boot.c \ - se_services/source/services_host_clocks.c \ - se_services/source/services_host_cryptocell.c \ - se_services/source/services_host_handler.c \ - se_services/source/services_host_system.c \ - ) +ALIF_TOC_CONFIG = mcu/M55_DUAL_cfg.json +ALIF_TOC_APPS = $(BUILD)/M55_HP/firmware.bin $(BUILD)/M55_HE/firmware.bin +JLINK_CMD = '\ +$(JLINK_CMD_PREFIX)\ +LoadFile "$(BUILD)/M55_HP/firmware.bin",0x80020000\n\ +LoadFile "$(BUILD)/M55_HE/firmware.bin",0x80320000\n\ +$(JLINK_CMD_SUFFIX)' -$(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 -$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation +else +$(error Invalid MCU core specified)) +endif -# List of sources for qstr extraction -SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) $(GEN_PINS_SRC) +include ../../py/mkenv.mk +include mpconfigport.mk +include $(BOARD_DIR)/mpconfigboard.mk -OBJ += $(PY_O) -OBJ += $(addprefix $(BUILD)/, $(SRC_O)) -OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(LIBM_SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(TINYUSB_SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(ALIF_SRC_C:.c=.o)) -OBJ += $(GEN_PINS_SRC:.c=.o) +# include py core make definitions +include $(TOP)/py/py.mk +include $(TOP)/extmod/extmod.mk ################################################################################ # Main targets -.DELETE_ON_ERROR: - .PHONY: all -all: $(BUILD)/firmware_toc.bin +all: $(BUILD)/firmware.toc.bin + +# Force make commands to run the targets every time +# regardless of whether firmware.toc.bin already exists +# to detect changes in the source files and rebuild. +.PHONY: $(BUILD)/M55_HE/firmware.bin +.PHONY: $(BUILD)/M55_HP/firmware.bin -$(BUILD)/ensemble.ld: $(LD_FILE) - $(ECHO) "Preprocess linker script $@" - $(Q)$(CPP) -P -E $(CFLAGS) $^ > $@ +$(BUILD): + $(MKDIR) -p $@ -$(BUILD)/firmware.elf: $(OBJ) $(BUILD)/ensemble.ld - $(ECHO) "Link $@" - $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) - $(Q)$(SIZE) $@ +$(BUILD)/M55_HP/firmware.bin: + make -f alif.mk MCU_CORE=M55_HP -$(BUILD)/firmware.bin: $(BUILD)/firmware.elf - $(Q)$(OBJCOPY) -Obinary $^ $(BUILD)/firmware.bin +$(BUILD)/M55_HE/firmware.bin: + make -f alif.mk MCU_CORE=M55_HE -$(BUILD)/firmware_toc.bin: $(BUILD)/firmware.bin +$(BUILD)/firmware.toc.bin: $(ALIF_TOC_APPS) $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \ - --filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \ + --filename $(abspath $(ALIF_TOC_CONFIG)) \ + --config-dir $(BOARD_DIR) \ --output-dir $(BUILD) \ --firmware-dir $(BUILD) \ --output $@ .PHONY: deploy -deploy: $(BUILD)/firmware_toc.bin +deploy: $(BUILD)/firmware.toc.bin $(ECHO) "Writing $< to the board" $(Q)python $(ALIF_TOOLS)/app-write-mram.py \ --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ @@ -245,7 +99,7 @@ deploy: $(BUILD)/firmware_toc.bin --images file:$(BUILD)/application_package.ds .PHONY: deploy-jlink -deploy-jlink: $(BUILD)/firmware_toc.bin +deploy-jlink: $(ALIF_TOC_APPS) $(Q)echo -e $(JLINK_CMD) | $(JLINK_EXE) .PHONY: maintenance @@ -260,17 +114,4 @@ update-system-package: --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ --port $(PORT) -################################################################################ -# Remaining make rules - -# Use a pattern rule here so that make will only call make-pins.py once to make -# both pins_board.c and pins_board.h -$(BUILD)/%_board.c $(HEADER_BUILD)/%_board.h: $(BOARD_DIR)/%.csv $(GEN_PIN_MKPINS) $(GEN_PIN_PREFIX) | $(HEADER_BUILD) - $(ECHO) "GEN $@" - $(Q)$(PYTHON) $(GEN_PIN_MKPINS) \ - --board-csv $(GEN_PINS_BOARD_CSV) \ - --prefix $(GEN_PIN_PREFIX) \ - --output-source $(GEN_PINS_SRC) \ - --output-header $(GEN_PINS_HDR) - include $(TOP)/py/mkrules.mk diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk new file mode 100644 index 0000000000000..ad69499eccfa8 --- /dev/null +++ b/ports/alif/alif.mk @@ -0,0 +1,257 @@ +################################################################################ +# Initial setup of Makefile environment + +BOARD ?= ALIF_ENSEMBLE +BOARD_DIR ?= boards/$(BOARD) +BUILD ?= build-$(BOARD)/$(MCU_CORE) + +ifeq ($(wildcard $(BOARD_DIR)/.),) +$(error Invalid BOARD specified: $(BOARD_DIR)) +endif + +include ../../py/mkenv.mk +include mpconfigport.mk +include $(BOARD_DIR)/mpconfigboard.mk + +# qstr definitions (must come before including py.mk) +QSTR_DEFS += qstrdefsport.h + +# include py core make definitions +include $(TOP)/py/py.mk +include $(TOP)/extmod/extmod.mk + +################################################################################ +# Project specific settings and compiler/linker flags + +CROSS_COMPILE ?= arm-none-eabi- +ALIF_DFP_REL_TOP ?= lib/alif_ensemble-cmsis-dfp +ALIF_DFP_REL_HERE ?= $(TOP)/lib/alif_ensemble-cmsis-dfp +CMSIS_DIR ?= $(TOP)/lib/cmsis/inc + +MCU_CORE ?= M55_HP +ALIF_CONFIG ?= mcu/$(MCU_CORE)_cfg.json +LD_FILE ?= mcu/ensemble.ld.S + +INC += -I. +INC += -I$(TOP) +INC += -I$(BUILD) +INC += -I$(BOARD_DIR) +INC += -I$(CMSIS_DIR) +INC += -I$(ALIF_DFP_REL_HERE)/drivers/include/ +INC += -I$(ALIF_DFP_REL_HERE)/se_services/include +INC += -I$(ALIF_DFP_REL_HERE)/ospi_xip/source/ospi +INC += -I$(ALIF_DFP_REL_HERE)/Device/common/config/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/common/include/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/config/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/include/ +INC += -I$(ALIF_DFP_REL_HERE)/Device/$(MCU_SERIES)/$(MCU_VARIANT)/ +INC += -I$(TOP)/lib/tinyusb/src +INC += -Itinyusb_port + +GEN_PIN_MKPINS = mcu/make-pins.py +GEN_PIN_PREFIX = mcu/pins_prefix.c +GEN_PINS_BOARD_CSV = $(BOARD_DIR)/pins.csv +GEN_PINS_SRC = $(BUILD)/pins_board.c +GEN_PINS_HDR = $(HEADER_BUILD)/pins_board.h + +CFLAGS_FPU += -mfloat-abi=hard -mfpu=fpv5-d16 + +CFLAGS += $(INC) \ + -std=c99 \ + -Wall \ + -Werror \ + -Wdouble-promotion \ + -Wfloat-conversion \ + -mthumb \ + -mcpu=cortex-m55 \ + -mtune=cortex-m55 \ + $(CFLAGS_FPU) \ + -march=armv8.1-m.main+fp+mve.fp \ + -nostdlib \ + -fdata-sections \ + -ffunction-sections \ + -D$(MCU_CORE)=1 \ + -DCORE_$(MCU_CORE) \ + -DALIF_CMSIS_H="\"$(MCU_CORE).h\"" + +ifeq ($(MICROPY_FLOAT_IMPL),float) +CFLAGS += -fsingle-precision-constant +CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT +else +CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_DOUBLE +endif + +# Tune for Debugging or Optimization +ifeq ($(DEBUG), 1) +CFLAGS += -Og -ggdb3 +# Disable text compression in debug builds +MICROPY_ROM_TEXT_COMPRESSION = 0 +else +CFLAGS += -O2 -DNDEBUG +endif + +CFLAGS += $(CFLAGS_EXTRA) + +AFLAGS = -mthumb -march=armv8.1-m.main+fp+mve.fp $(CFLAGS_FPU) + +LDFLAGS += -nostdlib \ + -T$(BUILD)/ensemble.ld \ + -Map=$@.map \ + --cref \ + --gc-sections \ + --print-memory-usage +ifeq ($(MCU_CORE),M55_HP) +LDFLAGS += --wrap=dcd_event_handler +endif + +LIBS += "$(shell $(CC) $(CFLAGS) -print-libgcc-file-name)" + +################################################################################ +# Source files and libraries + +SRC_O += \ + shared/runtime/gchelper_thumb2.o + +SRC_C = \ + alif_flash.c \ + fatfs_port.c \ + machine_pin.c \ + main.c \ + modalif.c \ + mphalport.c \ + mpuart.c \ + msc_disk.c \ + ospi_flash.c \ + pendsv.c \ + system_tick.c \ + se_services.c \ + usbd.c \ + $(wildcard $(BOARD_DIR)/*.c) + +ifeq ($(MICROPY_FLOAT_IMPL),float) +LIBM_SRC_C += $(SRC_LIB_LIBM_C) +LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C) +$(BUILD)/lib/libm/%.o: CFLAGS += -Wno-maybe-uninitialized +else +LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_C) +LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_SQRT_HW_C) +$(BUILD)/lib/libm_dbl/%.o: CFLAGS += -Wno-maybe-uninitialized +endif + +SHARED_SRC_C += $(addprefix shared/,\ + libc/string0.c \ + netutils/dhcpserver.c \ + netutils/netutils.c \ + netutils/trace.c \ + readline/readline.c \ + runtime/gchelper_native.c \ + runtime/interrupt_char.c \ + runtime/mpirq.c \ + runtime/pyexec.c \ + runtime/softtimer.c \ + runtime/stdout_helpers.c \ + runtime/sys_stdio_mphal.c \ + timeutils/timeutils.c \ + tinyusb/mp_usbd.c \ + tinyusb/mp_usbd_cdc.c \ + tinyusb/mp_usbd_descriptor.c \ + ) + +DRIVERS_SRC_C += $(addprefix drivers/,\ + bus/softspi.c \ + bus/softqspi.c \ + memory/spiflash.c \ + dht/dht.c \ + ) + +TINYUSB_SRC_C += \ + lib/tinyusb/src/tusb.c \ + lib/tinyusb/src/class/cdc/cdc_device.c \ + lib/tinyusb/src/class/msc/msc_device.c \ + lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/usbd.c \ + lib/tinyusb/src/device/usbd_control.c \ + tinyusb_port/tusb_alif_dcd.c \ + +ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ + Device/common/source/clk.c \ + Device/common/source/mpu_M55.c \ + Device/common/source/system_M55.c \ + Device/common/source/system_utils.c \ + Device/common/source/tcm_partition.c \ + Device/common/source/tgu_M55.c \ + Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ + drivers/source/adc.c \ + drivers/source/mhu_driver.c \ + drivers/source/mhu_receiver.c \ + drivers/source/mhu_sender.c \ + drivers/source/pinconf.c \ + drivers/source/uart.c \ + drivers/source/utimer.c \ + ospi_xip/source/ospi/ospi_drv.c \ + se_services/source/services_host_application.c \ + se_services/source/services_host_boot.c \ + se_services/source/services_host_clocks.c \ + se_services/source/services_host_cryptocell.c \ + se_services/source/services_host_handler.c \ + se_services/source/services_host_system.c \ + se_services/source/services_host_power.c \ + se_services/source/services_host_maintenance.c \ + ) + +$(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 +$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation +$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_system.o: CFLAGS += -Wno-maybe-uninitialized + +# List of sources for qstr extraction +SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) $(GEN_PINS_SRC) + +OBJ += $(PY_O) +OBJ += $(addprefix $(BUILD)/, $(SRC_O)) +OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(LIBM_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(TINYUSB_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(ALIF_SRC_C:.c=.o)) +OBJ += $(GEN_PINS_SRC:.c=.o) + +################################################################################ +# Main targets + +.PHONY: all erase deploy gdb objdump release + +.DELETE_ON_ERROR: + +obj: $(OBJ) +all: $(BUILD)/firmware.bin + +$(BUILD): + $(MKDIR) -p $@ + +$(BUILD)/ensemble.ld: $(LD_FILE) + $(ECHO) "Preprocess linker script $@" + $(Q)$(CPP) -P -E $(CFLAGS) $^ > $@ + +$(BUILD)/firmware.elf: $(OBJ) $(BUILD)/ensemble.ld + $(ECHO) "Link $@" + $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + $(Q)$(SIZE) $@ + +$(BUILD)/firmware.bin: $(BUILD)/firmware.elf + $(Q)$(OBJCOPY) -Obinary $^ $(BUILD)/firmware.bin + +################################################################################ +# Remaining make rules + +# Use a pattern rule here so that make will only call make-pins.py once to make +# both pins_board.c and pins_board.h +$(BUILD)/%_board.c $(HEADER_BUILD)/%_board.h: $(BOARD_DIR)/%.csv $(GEN_PIN_MKPINS) $(GEN_PIN_PREFIX) | $(HEADER_BUILD) + $(ECHO) "GEN $@" + $(Q)$(PYTHON) $(GEN_PIN_MKPINS) \ + --board-csv $(GEN_PINS_BOARD_CSV) \ + --prefix $(GEN_PIN_PREFIX) \ + --output-source $(GEN_PINS_SRC) \ + --output-header $(GEN_PINS_HDR) + +include $(TOP)/py/mkrules.mk diff --git a/ports/alif/mcu/ensemble.ld.S b/ports/alif/mcu/ensemble.ld.S index 198641ba3dcba..8129a749eb22b 100644 --- a/ports/alif/mcu/ensemble.ld.S +++ b/ports/alif/mcu/ensemble.ld.S @@ -24,25 +24,44 @@ * THE SOFTWARE. */ +#ifdef CORE_M55_HP +__DTCM_SIZE = 1024K; +#else +__DTCM_SIZE = 256K; +#endif + // Entry Point ENTRY(Reset_Handler) MEMORY { - ROM (rx) : ORIGIN = 0x80000000, LENGTH = 0x0057F000 - ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 - #ifdef CORE_M55_HP - DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00100000 - #else - DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00040000 - #endif - SRAM0 (rwx) : ORIGIN = 0x02000000, LENGTH = 0x00400000 - SRAM1 (rwx) : ORIGIN = 0x08000000, LENGTH = 0x00280000 + MRAM_BL (rx) : ORIGIN = 0x80000000, LENGTH = 128K + MRAM_HP (rx) : ORIGIN = 0x80020000, LENGTH = 3072K + MRAM_HE (rx) : ORIGIN = 0x80320000, LENGTH = 1400K + MRAM_FS (rx) : ORIGIN = 0x8047e000, LENGTH = 1024K + MRAM_TOC (rx): ORIGIN = 0x8057e000, LENGTH = 8K + ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 256K + DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = __DTCM_SIZE + SRAM0 (rwx) : ORIGIN = 0x02000000, LENGTH = 4096K + SRAM1 (rwx) : ORIGIN = 0x08000000, LENGTH = 2560K + SRAM6_A (rw) : ORIGIN = 0x62000000, LENGTH = 1024K + SRAM6_B (rw) : ORIGIN = 0x62400000, LENGTH = 1024K + SRAM7 (rw) : ORIGIN = 0x63000000, LENGTH = 512K + SRAM8 (rw) : ORIGIN = 0x63200000, LENGTH = 2048K + SRAM9_A (rw) : ORIGIN = 0x60000000, LENGTH = 256K + SRAM9_B (rw) : ORIGIN = 0x60040000, LENGTH = 512K } -__STACK_SIZE = 0x00004000; -__HEAP_SIZE = 0x00004000; -__MP_HEAP_SIZE = 0x00040000; +#ifdef CORE_M55_HP +REGION_ALIAS("ROM", MRAM_HP); +__MP_HEAP_SIZE = 256K; +#else +REGION_ALIAS("ROM", MRAM_HE); +__MP_HEAP_SIZE = 128K; +#endif + +__STACK_SIZE = 16K; +__HEAP_SIZE = 16K; SECTIONS { From 6b4d46569bf87673521645b3340e25a3336c8d40 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 17 Jul 2024 17:43:38 +0300 Subject: [PATCH 475/546] alif: Support running the port on the HE core. The same MicroPython firmware is built for the HE but with slightly different options, for example no USB. Signed-off-by: iabdalkader --- ports/alif/alif_flash.c | 2 ++ ports/alif/boards/manifest.py | 2 +- ports/alif/main.c | 4 ++-- ports/alif/modalif.c | 2 ++ ports/alif/modules/he/_boot.py | 21 +++++++++++++++++++++ ports/alif/modules/{ => hp}/_boot.py | 0 ports/alif/mpconfigport.h | 20 +++++++++++++++----- ports/alif/mpconfigport.mk | 1 + ports/alif/mphalport.c | 2 ++ ports/alif/ospi_flash.c | 4 +++- ports/alif/tinyusb_port/tusb_config.h | 2 +- 11 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 ports/alif/modules/he/_boot.py rename ports/alif/modules/{ => hp}/_boot.py (100%) diff --git a/ports/alif/alif_flash.c b/ports/alif/alif_flash.c index e3b2125800ea2..f2ea5ac85a88e 100644 --- a/ports/alif/alif_flash.c +++ b/ports/alif/alif_flash.c @@ -30,6 +30,7 @@ #include "modalif.h" #include "ospi_flash.h" +#if MICROPY_HW_ENABLE_OSPI typedef struct _alif_flash_obj_t { mp_obj_base_t base; uint32_t flash_base_addr; @@ -158,3 +159,4 @@ MP_DEFINE_CONST_OBJ_TYPE( make_new, alif_flash_make_new, locals_dict, &alif_flash_locals_dict ); +#endif // MICROPY_HW_ENABLE_OSPI diff --git a/ports/alif/boards/manifest.py b/ports/alif/boards/manifest.py index 148f1d6a6eb5e..1dc9e179acc14 100644 --- a/ports/alif/boards/manifest.py +++ b/ports/alif/boards/manifest.py @@ -1,4 +1,4 @@ -freeze("$(PORT_DIR)/modules") +freeze("$(PORT_DIR)/modules/$(MCU_CORE)") include("$(MPY_DIR)/extmod/asyncio") require("dht") require("neopixel") diff --git a/ports/alif/main.c b/ports/alif/main.c index 2d3134176d32e..975ee7ed2d328 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -68,11 +68,11 @@ void _start(void) { #if MICROPY_HW_ENABLE_UART_REPL mp_uart_init(); #endif - + #if MICROPY_HW_ENABLE_OSPI if (ospi_flash_init() != 0) { MICROPY_BOARD_FATAL_ERROR("ospi_init failed"); } - + #endif #if MICROPY_HW_ENABLE_USBDEV NVIC_ClearPendingIRQ(USB_IRQ_IRQn); NVIC_SetPriority(USB_IRQ_IRQn, IRQ_PRI_USB); diff --git a/ports/alif/modalif.c b/ports/alif/modalif.c index 5577b53eaca85..1c3c34394cd11 100644 --- a/ports/alif/modalif.c +++ b/ports/alif/modalif.c @@ -37,7 +37,9 @@ static MP_DEFINE_CONST_FUN_OBJ_0(alif_info_obj, alif_info); static const mp_rom_map_elem_t alif_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_alif) }, + #if MICROPY_HW_ENABLE_OSPI { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&alif_flash_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&alif_info_obj) }, #if MICROPY_HW_USB_MSC // Attribute to indicate USB MSC is enabled. diff --git a/ports/alif/modules/he/_boot.py b/ports/alif/modules/he/_boot.py new file mode 100644 index 0000000000000..bc1320f190694 --- /dev/null +++ b/ports/alif/modules/he/_boot.py @@ -0,0 +1,21 @@ +import openamp +import time +from machine import Pin + + +def ept_recv_callback(src_addr, data): + print("Received message on endpoint", data) + + +# Create a new RPMsg endpoint to communicate with main core. +ept = openamp.Endpoint("vuart-channel", callback=ept_recv_callback) + +pin = Pin("LED_BLUE", Pin.OUT) + +count = 0 +while True: + if ept.is_ready(): + ept.send("Hello from HE %d" % count, timeout=1000) + count += 1 + time.sleep_ms(100) + pin(not pin()) diff --git a/ports/alif/modules/_boot.py b/ports/alif/modules/hp/_boot.py similarity index 100% rename from ports/alif/modules/_boot.py rename to ports/alif/modules/hp/_boot.py diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 95b5e3ca9fa24..df33b92202431 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -36,13 +36,18 @@ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) #endif -#define MICROPY_HW_ENABLE_UART_REPL (1) // useful if there is no USB -#define MICROPY_HW_ENABLE_USBDEV (1) - +#ifndef MICROPY_HW_ENABLE_OSPI +#define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) +#endif +#ifndef MICROPY_HW_ENABLE_USBDEV +#define MICROPY_HW_ENABLE_USBDEV (CORE_M55_HP) +#endif #ifndef MICROPY_HW_USB_PRODUCT_FS_STRING #define MICROPY_HW_USB_PRODUCT_FS_STRING "Board in HS mode" #endif -#define MICROPY_HW_USB_CDC (1) +#ifndef MICROPY_HW_USB_CDC +#define MICROPY_HW_USB_CDC (CORE_M55_HP) +#endif #define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) #ifndef MICROPY_HW_USB_MSC #define MICROPY_HW_USB_MSC (0) @@ -53,7 +58,9 @@ #ifndef MICROPY_HW_USB_PID #define MICROPY_HW_USB_PID (0x9802) // interface has CDC only #endif - +#ifndef MICROPY_HW_ENABLE_UART_REPL +#define MICROPY_HW_ENABLE_UART_REPL (CORE_M55_HP) // useful if there is no USB +#endif #define MICROPY_HW_FLASH_BLOCK_SIZE_BYTES (4096) // Memory allocation policies @@ -78,6 +85,9 @@ #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#ifndef MICROPY_FLOAT_IMPL +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#endif #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_USE_INTERNAL_ERRNO (1) diff --git a/ports/alif/mpconfigport.mk b/ports/alif/mpconfigport.mk index 3d04450a60fa7..92d9a5db8faf0 100644 --- a/ports/alif/mpconfigport.mk +++ b/ports/alif/mpconfigport.mk @@ -10,3 +10,4 @@ MICROPY_VFS_LFS2 ?= 1 # File containing description of content to be frozen into firmware. FROZEN_MANIFEST ?= boards/manifest.py +MICROPY_MANIFEST_MCU_CORE := $(shell echo $(MCU_CORE) | awk -F'_' '{print tolower($$2)}') diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index ba58923f2fe2a..9769ddeaeb61e 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -85,8 +85,10 @@ int mp_hal_stdin_rx_chr(void) { // Send string of given length mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { + #if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC || MICROPY_PY_OS_DUPTERM mp_uint_t ret = len; bool did_write = false; + #endif #if MICROPY_HW_ENABLE_UART_REPL mp_uart_write_strn(str, len); diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index fa96053b36579..f84761366b3f0 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -26,8 +26,9 @@ #include "py/mperrno.h" #include "py/mphal.h" -#include "ospi_flash.h" +#if MICROPY_HW_ENABLE_OSPI +#include "ospi_flash.h" #include "ospi_drv.h" #include "pinconf.h" @@ -263,3 +264,4 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { } return ret; } +#endif // MICROPY_HW_ENABLE_OSPI diff --git a/ports/alif/tinyusb_port/tusb_config.h b/ports/alif/tinyusb_port/tusb_config.h index d244b4c241e6b..6c4ead1128846 100644 --- a/ports/alif/tinyusb_port/tusb_config.h +++ b/ports/alif/tinyusb_port/tusb_config.h @@ -44,7 +44,7 @@ #define CFG_TUSB_DEBUG 0 // Enable Device stack -#define CFG_TUD_ENABLED 1 +#define CFG_TUD_ENABLED (CORE_M55_HP) // Default is max speed that hardware controller could support with on-chip PHY #define CFG_TUD_MAX_SPEED OPT_MODE_HIGH_SPEED From 4f6f283abbdcbdec5cb2144a74fdd2937540ac71 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 17 Jul 2024 17:45:08 +0300 Subject: [PATCH 476/546] alif: Implement Open-AMP port backend. Signed-off-by: iabdalkader --- ports/alif/Makefile | 4 +- ports/alif/alif.mk | 11 +++ ports/alif/irq.h | 1 + ports/alif/mcu/ensemble.ld.S | 8 ++ ports/alif/mpconfigport.h | 1 + ports/alif/mpmetalport.c | 119 +++++++++++++++++++++++ ports/alif/mpmetalport.h | 89 ++++++++++++++++++ ports/alif/mpremoteprocport.c | 172 ++++++++++++++++++++++++++++++++++ 8 files changed, 403 insertions(+), 2 deletions(-) create mode 100644 ports/alif/mpmetalport.c create mode 100644 ports/alif/mpmetalport.h create mode 100644 ports/alif/mpremoteprocport.c diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 5cc334a7cf9f9..7d6731dacf200 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -76,10 +76,10 @@ $(BUILD): $(MKDIR) -p $@ $(BUILD)/M55_HP/firmware.bin: - make -f alif.mk MCU_CORE=M55_HP + make -f alif.mk MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0 $(BUILD)/M55_HE/firmware.bin: - make -f alif.mk MCU_CORE=M55_HE + make -f alif.mk MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1 $(BUILD)/firmware.toc.bin: $(ALIF_TOC_APPS) $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \ diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index ad69499eccfa8..c68fc66d44cc6 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -203,6 +203,17 @@ $(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALI $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_system.o: CFLAGS += -Wno-maybe-uninitialized +# Add Alif-specific implementation of libmetal (and optionally OpenAMP's rproc). +# Note: libmetal code is generated via a pre-processor so ensure that runs first. +ifeq ($(MICROPY_PY_OPENAMP),1) +SRC_C += mpmetalport.c +$(BUILD)/mpmetalport.o: $(BUILD)/openamp/metal/config.h +ifeq ($(MICROPY_PY_OPENAMP_REMOTEPROC),1) +SRC_C += mpremoteprocport.c +$(BUILD)/mpremoteprocport.o: $(BUILD)/openamp/metal/config.h +endif +endif + # List of sources for qstr extraction SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) $(GEN_PINS_SRC) diff --git a/ports/alif/irq.h b/ports/alif/irq.h index a20ff7aebdef1..de6d07ab6d32c 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -45,6 +45,7 @@ #define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_ADC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 3, 0) #define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) +#define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) // these states correspond to values from query_irq, enable_irq and disable_irq diff --git a/ports/alif/mcu/ensemble.ld.S b/ports/alif/mcu/ensemble.ld.S index 8129a749eb22b..aeefdb7eacb78 100644 --- a/ports/alif/mcu/ensemble.ld.S +++ b/ports/alif/mcu/ensemble.ld.S @@ -114,6 +114,14 @@ SECTIONS * (.bss.sram0*) } > SRAM0 + /* Open-AMP Shared Memory Region */ + .openamp_memory (NOLOAD) : ALIGN(32) + { + _openamp_shm_region_start = .; + . = . + 64K; + _openamp_shm_region_end = .; + } >SRAM6_A + .bss : ALIGN(4) { __bss_start__ = .; diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index df33b92202431..dfe1ec2561380 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -91,6 +91,7 @@ #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_USE_INTERNAL_ERRNO (1) +#define MICROPY_TRACKED_ALLOC (MICROPY_PY_OPENAMP) // Fine control over Python builtins, classes, modules, etc #define MICROPY_PY_SYS_PLATFORM "alif" diff --git a/ports/alif/mpmetalport.c b/ports/alif/mpmetalport.c new file mode 100644 index 0000000000000..f80d039c26d23 --- /dev/null +++ b/ports/alif/mpmetalport.c @@ -0,0 +1,119 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + * + * libmetal Alif port. + */ + +#include ALIF_CMSIS_H +#include "hwsem.h" + +#include "py/mperrno.h" +#include "py/mphal.h" + +#include "metal/sys.h" +#include "metal/utilities.h" +#include "metal/device.h" + +struct metal_state _metal; +static mp_sched_node_t rproc_notify_node; + +int metal_sys_init(const struct metal_init_params *params) { + metal_unused(params); + + // Reset the hardware semaphore. + hwsem_reset(METAL_HSEM_DEVICE); + #if MICROPY_PY_OPENAMP_HOST + hwsem_reset(METAL_HSEM_REMOTE); + #endif + + // Enable the hardware semaphore IRQ. + NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); + NVIC_SetPriority(METAL_HSEM_IRQn, IRQ_PRI_HWSEM); + NVIC_EnableIRQ(METAL_HSEM_IRQn); + + // If cache management is not enabled, configure the MPU to disable + // caching for the entire Open-AMP shared memory region. + #ifndef VIRTIO_USE_DCACHE + ARM_MPU_Disable(); + // NOTE: The startup code uses the first 4 attributes. + #define MEMATTR_IDX_NORMAL_NON_CACHEABLE 4 + ARM_MPU_SetMemAttr(MEMATTR_IDX_NORMAL_NON_CACHEABLE, ARM_MPU_ATTR( + ARM_MPU_ATTR_NON_CACHEABLE, + ARM_MPU_ATTR_NON_CACHEABLE)); + MPU->RNR = METAL_MPU_REGION_ID; + MPU->RBAR = ARM_MPU_RBAR(METAL_MPU_REGION_BASE, ARM_MPU_SH_NON, 0, 1, 0); // RO-0, NP-1, XN-0 + MPU->RLAR = ARM_MPU_RLAR(METAL_MPU_REGION_BASE + METAL_MPU_REGION_SIZE - 1, MEMATTR_IDX_NORMAL_NON_CACHEABLE); + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); + #endif + + metal_bus_register(&metal_generic_bus); + + return 0; +} + +void metal_sys_finish(void) { + NVIC_DisableIRQ(METAL_HSEM_IRQn); + NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); + hwsem_reset(METAL_HSEM_DEVICE); + metal_bus_unregister(&metal_generic_bus); +} + +unsigned int sys_irq_save_disable(void) { + return disable_irq(); +} + +void sys_irq_restore_enable(unsigned int state) { + enable_irq(state); +} + +void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa, + size_t size, unsigned int flags) { + metal_unused(pa); + metal_unused(size); + metal_unused(flags); + return va; +} + +void metal_machine_cache_flush(void *addr, unsigned int len) { + SCB_CleanDCache_by_Addr(addr, len); +} + +void metal_machine_cache_invalidate(void *addr, unsigned int len) { + SCB_InvalidateDCache_by_Addr(addr, len); +} + +int metal_rproc_notify(void *priv, uint32_t id) { + // Release the HW semaphore to notify the other core. + hwsem_release(METAL_HSEM_REMOTE, HWSEM_MASTERID); + return 0; +} + +void METAL_HSEM_IRQ_HANDLER(void) { + // Schedule the node only if the other core released the Semaphore. + if (METAL_HSEM_DEVICE->HWSEM_REL_REG == 0) { + mp_sched_schedule_node(&rproc_notify_node, openamp_remoteproc_notified); + } + hwsem_request(METAL_HSEM_DEVICE, METAL_HSEM_REMOTE_ID); +} diff --git a/ports/alif/mpmetalport.h b/ports/alif/mpmetalport.h new file mode 100644 index 0000000000000..5cd697c0e9c7b --- /dev/null +++ b/ports/alif/mpmetalport.h @@ -0,0 +1,89 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + * + * libmetal alif port. + */ +#ifndef MICROPY_INCLUDED_ALIF_MPMETALPORT_H +#define MICROPY_INCLUDED_ALIF_MPMETALPORT_H + +#include +#include "py/mphal.h" +#include "py/runtime.h" + +#define METAL_HAVE_STDATOMIC_H 0 +#define METAL_HAVE_FUTEX_H 0 + +#define METAL_MAX_DEVICE_REGIONS 2 + +#if MICROPY_PY_OPENAMP_HOST +#define METAL_HSEM_DEVICE ((HWSEM_Type *)HWSEM14_BASE) +#define METAL_HSEM_REMOTE ((HWSEM_Type *)HWSEM15_BASE) +#define METAL_HSEM_REMOTE_ID (0x410FD222U) +#define METAL_HSEM_IRQn HWSEM_IRQ14_IRQn +#define METAL_HSEM_IRQ_HANDLER HWSEM_IRQ14Handler +#else +#define METAL_HSEM_DEVICE ((HWSEM_Type *)HWSEM15_BASE) +#define METAL_HSEM_REMOTE ((HWSEM_Type *)HWSEM14_BASE) +#define METAL_HSEM_REMOTE_ID (0x410FD221U) +#define METAL_HSEM_IRQn HWSEM_IRQ15_IRQn +#define METAL_HSEM_IRQ_HANDLER HWSEM_IRQ15Handler +#endif + +// Set to 1 to enable log output. +#define METAL_LOG_HANDLER_ENABLE 0 + +#define metal_cpu_yield() + +// Shared memory config +#define METAL_SHM_NAME "OPENAMP_SHM" +// Note 1K must be reserved at the start of the openamp +// shared memory region, for the shared resource table. +#define METAL_RSC_ADDR ((void *)_openamp_shm_region_start) +#define METAL_RSC_SIZE (1024) + +#define METAL_SHM_ADDR ((metal_phys_addr_t)(_openamp_shm_region_start + METAL_RSC_SIZE)) +#define METAL_SHM_SIZE ((size_t)(_openamp_shm_region_end - _openamp_shm_region_start - METAL_RSC_SIZE)) + +#define METAL_MPU_REGION_ID (9) // NOTE: The startup code uses the first 9 regions. +#define METAL_MPU_REGION_BASE ((uint32_t)_openamp_shm_region_start) +#define METAL_MPU_REGION_SIZE (0x00010000U) + +extern const char _openamp_shm_region_start[]; +extern const char _openamp_shm_region_end[]; + +int metal_rproc_notify(void *priv, uint32_t id); +extern void openamp_remoteproc_notified(mp_sched_node_t *node); + +static inline int __metal_sleep_usec(unsigned int usec) { + mp_hal_delay_us(usec); + return 0; +} + +static inline void metal_generic_default_poll(void) { + mp_event_handle_nowait(); + __WFI(); +} + +#endif // MICROPY_INCLUDED_ALIF_METAL_PORT_H diff --git a/ports/alif/mpremoteprocport.c b/ports/alif/mpremoteprocport.c new file mode 100644 index 0000000000000..27210a824057e --- /dev/null +++ b/ports/alif/mpremoteprocport.c @@ -0,0 +1,172 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + * + * modremoteproc alif port. + */ + +#include +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "metal/alloc.h" +#include "metal/errno.h" +#include "metal/io.h" +#include "metal/sys.h" +#include "metal/device.h" +#include "metal/utilities.h" +#include "extmod/modopenamp_remoteproc.h" +#include "se_services.h" + +typedef struct mmap { + uintptr_t base; + uintptr_t limit; +} mmap_t; + +static const mmap_t mmap_nocache[] = { + { .base = ITCM_BASE, .limit = ITCM_BASE + ITCM_SIZE }, + { .base = DTCM_BASE, .limit = DTCM_BASE + ITCM_SIZE }, + { .base = MRAM_BASE, .limit = MRAM_BASE + MRAM_SIZE } +}; + +static bool is_cacheable(const void *p, size_t bytes) { + uintptr_t base = (uintptr_t)p; + if (bytes == 0) { + return false; + } + uintptr_t limit = base + bytes; + for (unsigned int i = 0; i < sizeof(mmap_nocache) / sizeof(mmap_nocache[0]); i++) { + if (base >= mmap_nocache[i].base && limit < mmap_nocache[i].limit) { + return false; + } + } + return true; +} + +struct remoteproc *mp_openamp_remoteproc_init(struct remoteproc *rproc, + const struct remoteproc_ops *ops, void *arg) { + metal_log(METAL_LOG_DEBUG, "rproc_init()\n"); + + rproc->ops = ops; + rproc->state = RPROC_OFFLINE; + // Allocate the image store and save it in private data. + rproc->priv = mp_openamp_remoteproc_store_alloc(); + + // Reset the remote core. + se_services_boot_reset_cpu(EXTSYS_1); + return rproc; +} + +void *mp_openamp_remoteproc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, unsigned int attribute, + struct metal_io_region **io) { + metal_log(METAL_LOG_DEBUG, "rproc_mmap(): pa 0x%p da 0x%p io 0x%p size %u\n", *pa, *da, *io, size); + + struct remoteproc_mem *mem; + metal_phys_addr_t lpa = *pa; + metal_phys_addr_t lda = *da; + + if (lda == METAL_BAD_PHYS) { + return NULL; + } + + if (lpa == METAL_BAD_PHYS) { + lpa = lda; + } + + // Currently this port doesn't support loading firmware to flash, + // only SD/SRAM images are supported. Check of load address is in + // the flash region, and if so return NULL. + if (lda >= MRAM_BASE && lda < (MRAM_BASE + MRAM_SIZE)) { + return NULL; + } + + mem = metal_allocate_memory(sizeof(*mem)); + if (!mem) { + return NULL; + } + + *io = metal_allocate_memory(sizeof(struct metal_io_region)); + if (!*io) { + metal_free_memory(mem); + return NULL; + } + + remoteproc_init_mem(mem, NULL, lpa, lda, size, *io); + + metal_io_init(*io, (void *)mem->da, &mem->pa, size, + sizeof(metal_phys_addr_t) << 3, attribute, NULL); + + remoteproc_add_mem(rproc, mem); + *pa = lpa; + *da = lda; + return metal_io_phys_to_virt(*io, mem->pa); +} + +int mp_openamp_remoteproc_start(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_start()\n"); + + // Flush cached areas for the remote core. + struct metal_list *node; + metal_list_for_each(&rproc->mems, node) { + struct remoteproc_mem *mem; + mem = metal_container_of(node, struct remoteproc_mem, node); + if (is_cacheable((uint32_t *)mem->pa, mem->size)) { + SCB_CleanDCache_by_Addr((uint32_t *)mem->pa, mem->size); + } + } + + se_services_boot_reset_cpu(EXTSYS_1); + se_services_boot_cpu(EXTSYS_1, (uint32_t)rproc->bootaddr); // GlobalToLocal + return 0; +} + +int mp_openamp_remoteproc_stop(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_stop()\n"); + if (rproc->state == RPROC_RUNNING) { + se_services_boot_reset_cpu(EXTSYS_1); + } + return 0; +} + +int mp_openamp_remoteproc_config(struct remoteproc *rproc, void *data) { + metal_log(METAL_LOG_DEBUG, "rproc_config()\n"); + (void)rproc; + return 0; +} + +void mp_openamp_remoteproc_remove(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_remove()\n"); + (void)rproc; +} + +int mp_openamp_remoteproc_shutdown(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_shutdown()\n"); + if (rproc->state == RPROC_RUNNING) { + se_services_boot_reset_cpu(EXTSYS_1); + } + return 0; +} From cee8e111cbaedc877fcffeb7d8166b60317f7377 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 28 Jul 2024 21:51:29 +0300 Subject: [PATCH 477/546] alif/irq: Define more IRQ priorities. Signed-off-by: Damien George --- ports/alif/irq.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ports/alif/irq.h b/ports/alif/irq.h index de6d07ab6d32c..59dbce9705947 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -44,8 +44,10 @@ #define IRQ_PRI_QUIET_TIMING NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0) #define IRQ_PRI_ADC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 3, 0) -#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) +#define IRQ_PRI_CSI NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) +#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 7, 0) #define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0) +#define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) // these states correspond to values from query_irq, enable_irq and disable_irq From bbb8fd77fde3400cc1b9902e94f05889a7c1f49a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 14 Aug 2024 16:50:28 +1000 Subject: [PATCH 478/546] alif/system_tick: Implement optional LPTIMER support for systick. Signed-off-by: Damien George --- ports/alif/mpconfigport.h | 6 ++ ports/alif/mphalport.c | 12 ++++ ports/alif/system_tick.c | 137 +++++++++++++++++++++++++++++++++++++- ports/alif/system_tick.h | 6 +- 4 files changed, 158 insertions(+), 3 deletions(-) diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index dfe1ec2561380..b1a01910628bb 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -36,6 +36,12 @@ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) #endif +// Select the low-level system tick implementation. +#if !defined(MICROPY_HW_SYSTEM_TICK_USE_LPTIMER) \ + && !defined(MICROPY_HW_SYSTEM_TICK_USE_UTIMER) +#define MICROPY_HW_SYSTEM_TICK_USE_UTIMER (1) +#endif + #ifndef MICROPY_HW_ENABLE_OSPI #define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) #endif diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 9769ddeaeb61e..becac8f3b4a0a 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -120,16 +120,28 @@ mp_uint_t mp_hal_ticks_cpu(void) { mp_uint_t mp_hal_ticks_us(void) { // Convert system tick to microsecond counter. + #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + return system_tick_get_u64() * 1000000 / system_tick_source_hz; + #else return system_tick_get_u64() / system_core_clock_mhz; + #endif } mp_uint_t mp_hal_ticks_ms(void) { // Convert system tick to millisecond counter. + #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + return system_tick_get_u64() * 1000ULL / system_tick_source_hz; + #else return system_tick_get_u64() / (SystemCoreClock / 1000); + #endif } void mp_hal_delay_us(mp_uint_t us) { + #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + uint64_t ticks_delay = (uint64_t)us * system_tick_source_hz / 1000000; + #else uint64_t ticks_delay = (uint64_t)us * system_core_clock_mhz; + #endif uint64_t start = system_tick_get_u64(); while (system_tick_get_u64() - start < ticks_delay) { } diff --git a/ports/alif/system_tick.c b/ports/alif/system_tick.c index 5cdf45cba0126..72b8d021c39c0 100644 --- a/ports/alif/system_tick.c +++ b/ports/alif/system_tick.c @@ -27,10 +27,141 @@ #include "irq.h" #include "system_tick.h" -#include "utimer.h" - #define MIN(x, y) ((x) < (y) ? (x) : (y)) +#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + +#include "lptimer.h" +#include "sys_ctrl_lptimer.h" + +// Channel 0 and 1 are cascaded to make a 64-bit counter. +// Channel 2 is used for system_tick_wfe_with_timeout_us. +// Channel 3 is used for system_tick_schedule_after_us. +#define LPTIMER ((LPTIMER_Type *)LPTIMER_BASE) +#define LPTIMER_CH_A (0) +#define LPTIMER_CH_B (1) +#define LPTIMER_CH_C (2) +#define LPTIMER_CH_D (3) + +uint64_t system_tick_source_hz; + +void system_tick_init(void) { + lptimer_disable_counter(LPTIMER, LPTIMER_CH_A); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_B); + + ANA_REG->MISC_CTRL |= 1 << 0; // SEL_32K, select LXFO + + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_A); + select_lptimer_clk(LPTIMER_CLK_SOURCE_CASCADE, LPTIMER_CH_B); + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_C); + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_D); + + lptimer_load_max_count(LPTIMER, LPTIMER_CH_A); + lptimer_set_mode_freerunning(LPTIMER, LPTIMER_CH_A); + + lptimer_load_max_count(LPTIMER, LPTIMER_CH_B); + lptimer_set_mode_freerunning(LPTIMER, LPTIMER_CH_B); + + lptimer_enable_counter(LPTIMER, LPTIMER_CH_B); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_A); + + system_tick_source_hz = 32768; + + NVIC_ClearPendingIRQ(LPTIMER2_IRQ_IRQn); + NVIC_SetPriority(LPTIMER2_IRQ_IRQn, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(LPTIMER2_IRQ_IRQn); + + NVIC_ClearPendingIRQ(LPTIMER3_IRQ_IRQn); + NVIC_SetPriority(LPTIMER3_IRQ_IRQn, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(LPTIMER3_IRQ_IRQn); +} + +void LPTIMER2_IRQHandler(void) { + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_C); + __SEV(); +} + +void LPTIMER3_IRQHandler(void) { + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_D); + system_tick_schedule_callback(); + __SEV(); +} + +uint32_t system_tick_get_u32(void) { + return 0xffffffff - lptimer_get_count(LPTIMER, LPTIMER_CH_A); +} + +uint64_t system_tick_get_u64(void) { + // Get 64-bit counter value from the hardware timer. + // Sample it twice in case the low counter wraps around while sampling. + uint32_t irq_state = disable_irq(); + uint32_t lo0 = lptimer_get_count(LPTIMER, LPTIMER_CH_A); + uint32_t hi0 = lptimer_get_count(LPTIMER, LPTIMER_CH_B); + uint32_t lo1 = lptimer_get_count(LPTIMER, LPTIMER_CH_A); + uint32_t hi1 = lptimer_get_count(LPTIMER, LPTIMER_CH_B); + enable_irq(irq_state); + + if (hi0 == hi1) { + // Low counter may have wrapped around between sampling of lo0 and hi0, so prefer second sampling. + lo0 = lo1; + hi0 = hi1; + } else { + // Low counter wrapped around either between sampling of hi0 and lo1, or sampling of lo1 and hi1. + // In either case use the first sampling. + } + + // Convert from descending count to ascending. + lo0 = 0xffffffff - lo0; + hi0 = 0xffffffff - hi0; + + // Return a 64-bit value. + return ((uint64_t)hi0 << 32) | (uint64_t)lo0; +} + +void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { + // Maximum 131 second timeout, to not overflow 32-bit ticks when + // LPTIMER is clocked at 32768Hz. + uint32_t timeout_ticks = (uint64_t)MIN(timeout_us, 131000000) * system_tick_source_hz / 1000000; + + // Set up the LPTIMER interrupt to fire after the given timeout. + lptimer_disable_counter(LPTIMER, LPTIMER_CH_C); + lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_C); + lptimer_load_count(LPTIMER, LPTIMER_CH_C, &timeout_ticks); + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_C); + + // Wait for an event. + __WFE(); + + // Disable the LPTIMER interrupt (in case a different interrupt woke the WFE). + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_C); +} + +void system_tick_schedule_after_us(uint32_t ticks_us) { + // Disable the interrupt in case it's still active. + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_D); + + // Maximum 131 second timeout, to not overflow 32-bit ticks when + // LPTIMER is clocked at 32768Hz. + uint32_t timeout_ticks = (uint64_t)MIN(ticks_us, 131000000) * system_tick_source_hz / 1000000; + + // Set up the LPTIMER interrupt to fire after the given timeout. + lptimer_disable_counter(LPTIMER, LPTIMER_CH_D); + lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_D); + lptimer_load_count(LPTIMER, LPTIMER_CH_D, &timeout_ticks); + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_D); +} + +#elif MICROPY_HW_SYSTEM_TICK_USE_UTIMER + +#include "utimer.h" + #define UTIMER ((UTIMER_Type *)UTIMER_BASE) #define UTIMER_CHANNEL (11) @@ -166,3 +297,5 @@ void system_tick_schedule_after_us(uint32_t ticks_us) { } } } + +#endif diff --git a/ports/alif/system_tick.h b/ports/alif/system_tick.h index 2373f9421de7b..d808b76322e7d 100644 --- a/ports/alif/system_tick.h +++ b/ports/alif/system_tick.h @@ -26,9 +26,13 @@ #ifndef MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H #define MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H -#include +#include "py/mpconfig.h" +#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER +extern uint64_t system_tick_source_hz; +#else extern uint64_t system_core_clock_mhz; +#endif void system_tick_init(void); uint32_t system_tick_get_u32(void); From c6ebecc4c3dbeeecbb0eecabd4ecb6162b8f14bb Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Aug 2024 12:59:47 +1000 Subject: [PATCH 479/546] alif/system_tick: Implement optional ARM SysTick support for systick. Signed-off-by: Damien George --- ports/alif/mpconfigport.h | 6 +++- ports/alif/mphalport.c | 16 ++++++++-- ports/alif/system_tick.c | 64 ++++++++++++++++++++++++++++++++++++++- ports/alif/system_tick.h | 2 +- 4 files changed, 82 insertions(+), 6 deletions(-) diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index b1a01910628bb..0fee2c379a3bd 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -37,10 +37,14 @@ #endif // Select the low-level system tick implementation. -#if !defined(MICROPY_HW_SYSTEM_TICK_USE_LPTIMER) \ +#if !defined(MICROPY_HW_SYSTEM_TICK_USE_SYSTICK) \ + && !defined(MICROPY_HW_SYSTEM_TICK_USE_LPTIMER) \ && !defined(MICROPY_HW_SYSTEM_TICK_USE_UTIMER) #define MICROPY_HW_SYSTEM_TICK_USE_UTIMER (1) #endif +#if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK +#define MICROPY_SOFT_TIMER_TICKS_MS system_tick_ms_counter +#endif #ifndef MICROPY_HW_ENABLE_OSPI #define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index becac8f3b4a0a..5e3dad141908e 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -120,7 +120,9 @@ mp_uint_t mp_hal_ticks_cpu(void) { mp_uint_t mp_hal_ticks_us(void) { // Convert system tick to microsecond counter. - #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + #if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + return system_tick_get_u64(); + #elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER return system_tick_get_u64() * 1000000 / system_tick_source_hz; #else return system_tick_get_u64() / system_core_clock_mhz; @@ -129,7 +131,9 @@ mp_uint_t mp_hal_ticks_us(void) { mp_uint_t mp_hal_ticks_ms(void) { // Convert system tick to millisecond counter. - #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + #if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + return system_tick_get_u64() / 1000ULL; + #elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER return system_tick_get_u64() * 1000ULL / system_tick_source_hz; #else return system_tick_get_u64() / (SystemCoreClock / 1000); @@ -137,7 +141,9 @@ mp_uint_t mp_hal_ticks_ms(void) { } void mp_hal_delay_us(mp_uint_t us) { - #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + #if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + uint64_t ticks_delay = (uint64_t)us; + #elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER uint64_t ticks_delay = (uint64_t)us * system_tick_source_hz / 1000000; #else uint64_t ticks_delay = (uint64_t)us * system_core_clock_mhz; @@ -167,6 +173,8 @@ void system_tick_schedule_callback(void) { pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); } +#if !defined(MICROPY_SOFT_TIMER_TICKS_MS) + uint32_t soft_timer_get_ms(void) { return mp_hal_ticks_ms(); } @@ -177,3 +185,5 @@ void soft_timer_schedule_at_ms(uint32_t ticks_ms) { ms = MIN(ms, 4000000); // ensure ms * 1000 doesn't overflow system_tick_schedule_after_us(ms * 1000); } + +#endif diff --git a/ports/alif/system_tick.c b/ports/alif/system_tick.c index 72b8d021c39c0..4f161e6709e4b 100644 --- a/ports/alif/system_tick.c +++ b/ports/alif/system_tick.c @@ -29,7 +29,69 @@ #define MIN(x, y) ((x) < (y) ? (x) : (y)) -#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER +#if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + +#include "shared/runtime/softtimer.h" +#include "pendsv.h" + +volatile uint32_t system_tick_ms_counter; + +void system_tick_init(void) { + // Configure SysTick to run at 1kHz (1ms interval) + SysTick_Config(SystemCoreClock / 1000); + NVIC_SetPriority(SysTick_IRQn, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(SysTick_IRQn); +} + +void SysTick_Handler(void) { + uint32_t uw_tick = system_tick_ms_counter + 1; + system_tick_ms_counter = uw_tick; + + // Read the systick control register to clear the COUNTFLAG bit. + SysTick->CTRL; + + if (soft_timer_next == uw_tick) { + pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); + } +} + +uint32_t system_tick_get_u32(void) { + return system_tick_get_u64(); +} + +uint64_t system_tick_get_u64(void) { + mp_uint_t irq_state = disable_irq(); + uint32_t counter = SysTick->VAL; + uint32_t milliseconds = system_tick_ms_counter; + uint32_t status = SysTick->CTRL; + enable_irq(irq_state); + + // It's still possible for the COUNTFLAG bit to get set if the counter was + // reloaded between reading VAL and reading CTRL. With interrupts disabled + // it definitely takes less than 50 cycles between reading VAL and + // reading CTRL, so the test (counter > 50) is to cover the case where VAL + // is +ve and very close to zero, and the COUNTFLAG bit is also set. + if ((status & SysTick_CTRL_COUNTFLAG_Msk) && counter > 50) { + // This means that the HW reloaded VAL between the time we read VAL and the + // time we read CTRL, which implies that there is an interrupt pending + // to increment the tick counter. + milliseconds++; + } + uint32_t load = SysTick->LOAD; + counter = load - counter; // Convert from decrementing to incrementing + + // Calculate 64-bit microsecond counter. + return (uint64_t)milliseconds * 1000ULL + (uint64_t)((counter * 1000) / (load + 1)); +} + +void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { + if (timeout_us > 1000) { + // SysTick will wake us in at most 1ms. + __WFI(); + } +} + +#elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER #include "lptimer.h" #include "sys_ctrl_lptimer.h" diff --git a/ports/alif/system_tick.h b/ports/alif/system_tick.h index d808b76322e7d..a380808b2e043 100644 --- a/ports/alif/system_tick.h +++ b/ports/alif/system_tick.h @@ -30,7 +30,7 @@ #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER extern uint64_t system_tick_source_hz; -#else +#elif MICROPY_HW_SYSTEM_TICK_USE_UTIMER extern uint64_t system_core_clock_mhz; #endif From 58d6fe236b60daa92810abc81c8462e362a344dc Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 3 Sep 2024 17:04:56 +1000 Subject: [PATCH 480/546] alif/mpconfigport: Select SysTick on HE core. UTIMER is used by the HP. Signed-off-by: Damien George --- ports/alif/mpconfigport.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 0fee2c379a3bd..ae3ad7a30d352 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -40,7 +40,11 @@ #if !defined(MICROPY_HW_SYSTEM_TICK_USE_SYSTICK) \ && !defined(MICROPY_HW_SYSTEM_TICK_USE_LPTIMER) \ && !defined(MICROPY_HW_SYSTEM_TICK_USE_UTIMER) +#if CORE_M55_HP #define MICROPY_HW_SYSTEM_TICK_USE_UTIMER (1) +#else +#define MICROPY_HW_SYSTEM_TICK_USE_SYSTICK (1) +#endif #endif #if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK #define MICROPY_SOFT_TIMER_TICKS_MS system_tick_ms_counter From 4c4b4844df7746aa2ed4b124663e2472cb908b1a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Oct 2024 18:20:06 +1100 Subject: [PATCH 481/546] alif/mpu: Add custom MPU_Load_Regions function. Signed-off-by: Damien George --- ports/alif/alif.mk | 1 + ports/alif/mpu.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++ ports/alif/mpu.h | 31 ++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 ports/alif/mpu.c create mode 100644 ports/alif/mpu.h diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index c68fc66d44cc6..210b06c65f74e 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -119,6 +119,7 @@ SRC_C = \ main.c \ modalif.c \ mphalport.c \ + mpu.c \ mpuart.c \ msc_disk.c \ ospi_flash.c \ diff --git a/ports/alif/mpu.c b/ports/alif/mpu.c new file mode 100644 index 0000000000000..11c6273fcb345 --- /dev/null +++ b/ports/alif/mpu.c @@ -0,0 +1,78 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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/mpconfig.h" +#include "mpu.h" +#include ALIF_CMSIS_H + +static const ARM_MPU_Region_t mpu_table[] __STARTUP_RO_DATA_ATTRIBUTE = { + { /* SRAM0 - 4MB : RO-0, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0x02000000, ARM_MPU_SH_NON, 0, 1, 0), + .RLAR = ARM_MPU_RLAR(0x023FFFFF, MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT) + }, + { /* SRAM1 - 2.5MB : RO-0, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0x08000000, ARM_MPU_SH_NON, 0, 1, 0), + .RLAR = ARM_MPU_RLAR(0x0827FFFF, MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA) + }, + { /* Host Peripherals - 16MB : RO-0, NP-1, XN-1 */ + .RBAR = ARM_MPU_RBAR(0x1A000000, ARM_MPU_SH_NON, 0, 1, 1), + .RLAR = ARM_MPU_RLAR(0x1AFFFFFF, MP_MPU_ATTR_INDEX_DEVICE_nGnRE) + }, + { /* MRAM - 5.5MB : RO-1, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0x80000000, ARM_MPU_SH_NON, 1, 1, 0), + .RLAR = ARM_MPU_RLAR(0x8057FFFF, MP_MPU_ATTR_INDEX_NORMAL_WT_RA) + }, + { /* OSPI Regs - 16MB : RO-0, NP-1, XN-1 */ + .RBAR = ARM_MPU_RBAR(0x83000000, ARM_MPU_SH_NON, 0, 1, 1), + .RLAR = ARM_MPU_RLAR(0x83FFFFFF, MP_MPU_ATTR_INDEX_DEVICE_nGnRE) + }, + { /* OSPI0 XIP flash - 512MB : RO-1, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0xA0000000, ARM_MPU_SH_NON, 1, 1, 0), + .RLAR = ARM_MPU_RLAR(0xBFFFFFFF, MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE) + }, +}; + +void MPU_Load_Regions(void) { + // Configure memory attributes. + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT, + ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0), ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0))); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_DEVICE_nGnRE, + ARM_MPU_ATTR(ARM_MPU_ATTR_DEVICE, ARM_MPU_ATTR_DEVICE_nGnRE)); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA, + ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1), ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1))); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WT_RA, + ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0), ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0))); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE, + ARM_MPU_ATTR(ARM_MPU_ATTR_NON_CACHEABLE, ARM_MPU_ATTR_NON_CACHEABLE)); + + // Load the MPU regions from the table. + ARM_MPU_Load(0, mpu_table, sizeof(mpu_table) / sizeof(ARM_MPU_Region_t)); +} diff --git a/ports/alif/mpu.h b/ports/alif/mpu.h new file mode 100644 index 0000000000000..87a7518a126be --- /dev/null +++ b/ports/alif/mpu.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ + +#define MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT (0) +#define MP_MPU_ATTR_INDEX_DEVICE_nGnRE (1) +#define MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA (2) +#define MP_MPU_ATTR_INDEX_NORMAL_WT_RA (3) +#define MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE (4) From 84effb386ac23257e9a399bf52dc23f8f50387fe Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 7 Oct 2024 16:06:50 +1100 Subject: [PATCH 482/546] alif/ospi_flash: Generalise flash driver to support MX chips. Signed-off-by: Damien George --- ports/alif/alif.mk | 1 + ports/alif/ospi_ext.c | 292 ++++++++++++++++++++++++++++++ ports/alif/ospi_ext.h | 57 ++++++ ports/alif/ospi_flash.c | 362 ++++++++++++++++++++++++------------- ports/alif/ospi_flash.h | 61 ++++++- ports/alif/ospi_xip_user.h | 5 - 6 files changed, 643 insertions(+), 135 deletions(-) create mode 100644 ports/alif/ospi_ext.c create mode 100644 ports/alif/ospi_ext.h delete mode 100644 ports/alif/ospi_xip_user.h diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 210b06c65f74e..1581452195840 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -122,6 +122,7 @@ SRC_C = \ mpu.c \ mpuart.c \ msc_disk.c \ + ospi_ext.c \ ospi_flash.c \ pendsv.c \ system_tick.c \ diff --git a/ports/alif/ospi_ext.c b/ports/alif/ospi_ext.c new file mode 100644 index 0000000000000..48446d0622d48 --- /dev/null +++ b/ports/alif/ospi_ext.c @@ -0,0 +1,292 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 ALIF_CMSIS_H +#include "ospi_ext.h" +#include "ospi_xip_user.h" + +#define INST_L16bit (3) + +static void ospi_xip_disable(ospi_flash_cfg_t *ospi_cfg) { + ospi_cfg->aes_regs->aes_control &= ~AES_CONTROL_XIP_EN; +} + +static void ospi_xip_enable(ospi_flash_cfg_t *ospi_cfg) { + ospi_cfg->aes_regs->aes_control |= AES_CONTROL_XIP_EN; + #if OSPI_XIP_ENABLE_AES_DECRYPTION + ospi_cfg->aes_regs->aes_control |= (AES_CONTROL_LD_KEY | AES_CONTROL_DECRYPT_EN); + #endif +} + +// Standard SPI transfer. +void ospi_spi_transfer(ospi_flash_cfg_t *ospi_cfg, size_t len, const uint8_t *buf_out, uint8_t *buf_in) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t ctrlr0 = CTRLR0_IS_MST + | (SINGLE << CTRLR0_SPI_FRF_OFFSET) + | (SPI_TMOD_TR << CTRLR0_TMOD_OFFSET) + | (CTRLR0_DFS_8bit << CTRLR0_DFS_OFFSET) + ; + + uint32_t spi_ctrlr0 = TRANS_TYPE_STANDARD; + + ospi_writel(ospi_cfg, ctrlr0, ctrlr0); + ospi_writel(ospi_cfg, ctrlr1, len - 1); + ospi_writel(ospi_cfg, spi_ctrlr0, spi_ctrlr0); + spi_enable(ospi_cfg); + + // Buffer output data in SPI FIFO. + for (int i = 0; i < len; ++i) { + ospi_writel(ospi_cfg, data_reg, buf_out[i]); + } + + // Enable the SPI peripheral. + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + // Read in data. + for (int i = 0; i < len; ++i) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return; + } + } + buf_in[i] = ospi_readl(ospi_cfg, data_reg); + } +} + +void ospi_setup_read_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len, uint32_t read_len, uint32_t wait_cycles) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + ospi_writel(ospi_cfg, ctrlr1, read_len - 1); + + if (ospi_cfg->ddr_en) { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (1 << CTRLR0_SPI_DDR_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + if (inst_len == OSPI_INST_L_16bit) { + val |= 1 << CTRLR0_INST_DDR_EN_OFFSET; + } + } else { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + } + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + ospi_cfg->rx_req = read_len; + spi_enable(ospi_cfg); +} + +int ospi_recv_blocking_8bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer) { + ospi_writel(ospi_cfg, data_reg, command); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + ospi_cfg->rx_cnt = 0; + + while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return -OSPI_ETIMEDOUT; + } + } + *buffer++ = ospi_readl(ospi_cfg, data_reg); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +int ospi_recv_blocking_16bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint16_t *buffer) { + ospi_writel(ospi_cfg, data_reg, command); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + ospi_cfg->rx_cnt = 0; + + while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return -OSPI_ETIMEDOUT; + } + } + *buffer++ = ospi_readl(ospi_cfg, data_reg); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +int ospi_recv_blocking_32bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint32_t *buffer) { + ospi_writel(ospi_cfg, data_reg, command); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + ospi_cfg->rx_cnt = 0; + + while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return -OSPI_ETIMEDOUT; + } + } + *buffer++ = __ROR(ospi_readl(ospi_cfg, data_reg), 16); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (TMOD_TO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + ospi_writel(ospi_cfg, ctrlr1, 0); + + if (ospi_cfg->ddr_en) { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (1 << CTRLR0_SPI_DDR_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (0 << CTRLR0_WAIT_CYCLES_OFFSET); + if (inst_len == OSPI_INST_L_16bit) { + val |= 1 << CTRLR0_INST_DDR_EN_OFFSET; + } + } else { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (0 << CTRLR0_WAIT_CYCLES_OFFSET); + } + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + spi_enable(ospi_cfg); +} + +void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles) { + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (0 << CTRLR0_SCPOL_OFFSET) + | (0 << CTRLR0_SCPH_OFFSET) + | (0 << CTRLR0_SSTE_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + + val = (OCTAL << XIP_CTRL_FRF_OFFSET) + | (0x2 << XIP_CTRL_TRANS_TYPE_OFFSET) + | (ADDR_L32bit << XIP_CTRL_ADDR_L_OFFSET) + | (INST_L16bit << XIP_CTRL_INST_L_OFFSET) + | (0x0 << XIP_CTRL_MD_BITS_EN_OFFSET) + | (read_dummy_cycles << XIP_CTRL_WAIT_CYCLES_OFFSET) + | (0x1 << XIP_CTRL_DFC_HC_OFFSET) + | (ospi_cfg->ddr_en << XIP_CTRL_DDR_EN_OFFSET) + | (ospi_cfg->ddr_en << XIP_CTRL_INST_DDR_EN_OFFSET) + | (0x1 << XIP_CTRL_RXDS_EN_OFFSET) + | (0x1 << XIP_CTRL_INST_EN_OFFSET) + | (0x0 << XIP_CTRL_CONT_XFER_EN_OFFSET) + | (0x0 << XIP_CTRL_HYPERBUS_EN_OFFSET) + | (0x1 << XIP_CTRL_RXDS_SIG_EN) + | (0x0 << XIP_CTRL_XIP_MBL_OFFSET) + | (0x0 << XIP_PREFETCH_EN_OFFSET) + | (0x0 << XIP_CTRL_RXDS_VL_EN_OFFSET); + + ospi_writel(ospi_cfg, xip_ctrl, val); + + ospi_writel(ospi_cfg, rx_sample_dly, 4); + ospi_writel(ospi_cfg, txd_drive_edge, 1); + ospi_cfg->aes_regs->aes_rxds_delay = OSPI_XIP_RXDS_DELAY; + + ospi_writel(ospi_cfg, xip_mode_bits, 0x0); + ospi_writel(ospi_cfg, xip_incr_inst, incr_command); + ospi_writel(ospi_cfg, xip_wrap_inst, wrap_command); + ospi_writel(ospi_cfg, xip_ser, ospi_cfg->ser); + + spi_enable(ospi_cfg); + ospi_xip_enable(ospi_cfg); +} + +void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, uint16_t wrap_command) { + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (0 << CTRLR0_SCPOL_OFFSET) + | (0 << CTRLR0_SCPH_OFFSET) + | (0 << CTRLR0_SSTE_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (CTRLR0_DFS_32bit << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + + val = TRANS_TYPE_FRF_DEFINED + | ((ospi_cfg->ddr_en) << CTRLR0_SPI_DDR_EN_OFFSET) + | (2 << CTRLR0_XIP_MBL_OFFSET) + | (1 << CTRLR0_XIP_DFS_HC_OFFSET) + | (1 << CTRLR0_XIP_INST_EN_OFFSET) + | (CTRLR0_INST_L_16bit << CTRLR0_INST_L_OFFSET) + | (ospi_cfg->addrlen) << (CTRLR0_ADDR_L_OFFSET) + | (ospi_cfg->wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + + ospi_writel(ospi_cfg, xip_mode_bits, 0x1); + ospi_writel(ospi_cfg, xip_incr_inst, incr_command); + ospi_writel(ospi_cfg, xip_wrap_inst, wrap_command); + ospi_writel(ospi_cfg, xip_ser, ospi_cfg->ser); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + ospi_writel(ospi_cfg, xip_cnt_time_out, 100); + + spi_enable(ospi_cfg); + + ospi_xip_enable(ospi_cfg); + ospi_xip_disable(ospi_cfg); +} diff --git a/ports/alif/ospi_ext.h b/ports/alif/ospi_ext.h new file mode 100644 index 0000000000000..e467772d15670 --- /dev/null +++ b/ports/alif/ospi_ext.h @@ -0,0 +1,57 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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_ALIF_OSPI_EXT_H +#define MICROPY_INCLUDED_ALIF_OSPI_EXT_H + +#include +#include "ospi_drv.h" + +#define OSPI_ETIMEDOUT (110) + +#define OSPI_INST_L_8bit (CTRLR0_INST_L_8bit) +#define OSPI_INST_L_16bit (CTRLR0_INST_L_16bit) + +#define OSPI_ADDR_L_0bit UINT32_C(0x0) +#define OSPI_ADDR_L_24bit UINT32_C(0x6) +#define OSPI_ADDR_L_32bit UINT32_C(0x8) + +#define OSPI_DATA_L_8bit (CTRLR0_DFS_8bit) +#define OSPI_DATA_L_16bit (CTRLR0_DFS_16bit) +#define OSPI_DATA_L_32bit (CTRLR0_DFS_32bit) + +void ospi_spi_transfer(ospi_flash_cfg_t *ospi_cfg, size_t len, const uint8_t *buf_out, uint8_t *buf_in); + +void ospi_setup_read_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len, uint32_t read_len, uint32_t wait_cycles); +int ospi_recv_blocking_8bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer); +int ospi_recv_blocking_16bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint16_t *buffer); +int ospi_recv_blocking_32bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint32_t *buffer); + +void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len); + +void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles); +void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, uint16_t wrap_command); + +#endif // MICROPY_INCLUDED_ALIF_OSPI_EXT_H diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index f84761366b3f0..6f083de630aa9 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -28,72 +28,84 @@ #include "py/mphal.h" #if MICROPY_HW_ENABLE_OSPI + +#include "ospi_ext.h" #include "ospi_flash.h" #include "ospi_drv.h" +#include "ospi_xip_user.h" #include "pinconf.h" -#define CMD_RDSR (0x05) -#define CMD_WREN (0x06) -#define CMD_SEC_ERASE_32ADDR (0x21) // 4kiB sector erase with 32-bit address -#define CMD_WRVOL (0x81) -#define CMD_RD_DEVID (0x9f) - -#define WAIT_SR_TIMEOUT (1000000) +#define WAIT_SR_TIMEOUT (1000000) -// maximum bytes we can write in one SPI transfer -// limited by 256 byte FIFO buffer (can't go up to 256) -// need to use DMA to make this 256 -#define PAGE_SIZE (128) +// Generic SPI flash commands. +#define CMD_SPI_WREN (0x06) -#define ISSI_MODE_OCTAL_DDR_DQS (0xe7) +// This is the maximum number of bytes that can be written to SPI flash at once. +#define PAGE_SIZE (256) -// All OSPI1 pins use the same alternate function. -#define OSPI1_PIN_FUNCTION PINMUX_ALTERNATE_FUNCTION_1 +// All OSP0/OSPI1 pins use the same alternate function. +#define OSPI_PIN_FUNCTION PINMUX_ALTERNATE_FUNCTION_1 -typedef struct _mp_spiflash_t { +typedef struct _ospi_flash_t { + const ospi_pin_settings_t *pin; + const ospi_flash_settings_t *set; ospi_flash_cfg_t cfg; -} mp_spiflash_t; +} ospi_flash_t; -static mp_spiflash_t global_flash; +static ospi_flash_t global_flash; -// Alif version of this function can overwrite the destination buffer. -static void ospi_recv_blocking2(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer) { - uint32_t val; +/******************************************************************************/ +// Generic SPI-flash helper functions. - ospi_writel(ospi_cfg, data_reg, command); - ospi_writel(ospi_cfg, ser, ospi_cfg->ser); - - ospi_cfg->rx_cnt = 0; +static void ospi_flash_wren_spi(ospi_flash_t *self) { + uint8_t buf[1] = {CMD_SPI_WREN}; + ospi_spi_transfer(&self->cfg, 1, buf, buf); +} - while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { - unsigned int timeout = 100000; - while (ospi_readl(ospi_cfg, rxflr) == 0) { - if (--timeout == 0) { - return; - } - } - val = ospi_readl(ospi_cfg, data_reg); - *buffer++ = (uint8_t)val; - ospi_cfg->rx_cnt++; +static int ospi_flash_read_cmd(ospi_flash_t *self, uint32_t cmd, uint8_t cmd_dummy_cycles, size_t len, uint8_t *dest) { + int ret = 0; + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_0bit, OSPI_DATA_L_8bit, len, cmd_dummy_cycles); + ret = ospi_recv_blocking_8bit_data(&self->cfg, cmd, dest); + } else { + uint16_t dest16 = 0; + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, len, cmd_dummy_cycles); + ospi_push(&self->cfg, cmd); + ret = ospi_recv_blocking_16bit_data(&self->cfg, 0 /* addr */, &dest16); + *dest = dest16; } + return ret; } -static int mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len, uint8_t *dest) { - ospi_setup_read(&self->cfg, 0, len, 8); - ospi_recv_blocking2(&self->cfg, cmd, dest); +static int ospi_flash_write_cmd_addr(ospi_flash_t *self, uint32_t cmd, uint32_t addr_len, uint32_t addr) { + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_write_ext(&self->cfg, false, self->set->inst_len, addr_len, OSPI_DATA_L_8bit); + } else { + ospi_setup_write_ext(&self->cfg, self->set->rxds, self->set->inst_len, addr_len, OSPI_DATA_L_16bit); + } + if (addr_len == OSPI_ADDR_L_0bit) { + ospi_send_blocking(&self->cfg, cmd); + } else { + ospi_push(&self->cfg, cmd); + ospi_send_blocking(&self->cfg, addr); + } return 0; } -static int mp_spiflash_write_cmd(mp_spiflash_t *self, uint8_t cmd) { - ospi_setup_write(&self->cfg, 0); - ospi_send_blocking(&self->cfg, cmd); - return 0; +static int ospi_flash_write_cmd(ospi_flash_t *self, uint32_t cmd) { + return ospi_flash_write_cmd_addr(self, cmd, OSPI_ADDR_L_0bit, 0); } -static int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) { +static uint32_t ospi_flash_read_id_spi(ospi_flash_t *self) { + uint8_t buf[4] = {0x9f, 0, 0, 0}; + ospi_spi_transfer(&self->cfg, 4, buf, buf); + return buf[1] | buf[2] << 8 | buf[3] << 16; +} + +static int ospi_flash_wait_sr(ospi_flash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) { do { - uint8_t sr; - int ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr); + uint8_t sr = 0; + int ret = ospi_flash_read_cmd(self, self->set->read_sr, self->set->read_sr_dummy_cycles, 1, &sr); if (ret != 0) { return ret; } @@ -105,144 +117,251 @@ static int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, u return -MP_ETIMEDOUT; } -static int mp_spiflash_wait_wel1(mp_spiflash_t *self) { - return mp_spiflash_wait_sr(self, 2, 2, WAIT_SR_TIMEOUT); +static int ospi_flash_wait_wel1(ospi_flash_t *self) { + return ospi_flash_wait_sr(self, 2, 2, WAIT_SR_TIMEOUT); } -static int mp_spiflash_wait_wip0(mp_spiflash_t *self) { - return mp_spiflash_wait_sr(self, 1, 0, WAIT_SR_TIMEOUT); +static int ospi_flash_wait_wip0(ospi_flash_t *self) { + return ospi_flash_wait_sr(self, 1, 0, WAIT_SR_TIMEOUT); } -static uint32_t ospi_flash_read_id(mp_spiflash_t *self) { - uint8_t buf[8]; - ospi_setup_read(&self->cfg, 0, 3, ospi_flash_settings.read_id_dummy_cycles); - ospi_recv_blocking2(&self->cfg, CMD_RD_DEVID, buf); +static uint32_t ospi_flash_read_id(ospi_flash_t *self) { + uint8_t buf[4] = {0}; + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_0bit, OSPI_DATA_L_8bit, 3, self->set->read_id_dummy_cycles); + ospi_recv_blocking_8bit_data(&self->cfg, self->set->read_id, buf); + } else { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, 4, self->set->read_id_dummy_cycles); + ospi_push(&self->cfg, self->set->read_id); + // Read 8-bit values because data is in SDR mode for read id. + ospi_recv_blocking_8bit_data(&self->cfg, 0, buf); + } return buf[0] | buf[1] << 8 | buf[2] << 16; } -static void ospi_flash_write_reg_sdr(mp_spiflash_t *self, uint8_t cmd, uint8_t addr, uint8_t value) { - mp_spiflash_write_cmd(self, CMD_WREN); - ospi_setup_write_sdr(&self->cfg, 6); - ospi_push(&self->cfg, cmd); - ospi_push(&self->cfg, 0x00); - ospi_push(&self->cfg, 0x00); +/******************************************************************************/ +// Functions specific to ISSI flash chips. + +int ospi_flash_issi_octal_switch(ospi_flash_t *self) { + // Switch SPI flash to Octal DDR mode. + const uint8_t cmd_wrvol = 0x81; + const uint8_t issi_mode_octal_ddr_dqs = 0xe7; + ospi_flash_wren_spi(self); + uint8_t buf[5] = {cmd_wrvol, 0, 0, 0, issi_mode_octal_ddr_dqs}; + ospi_spi_transfer(&self->cfg, sizeof(buf), buf, buf); + self->cfg.ddr_en = 1; + return 0; +} + +/******************************************************************************/ +// Functions specific to MX flash chips. + +int ospi_flash_mx_octal_switch(ospi_flash_t *self) { + // Switch SPI flash to Octal SDR or DDR mode (SOPI or DOPI) by writing to CR2. + const uint8_t cmd_wrcr2 = 0x72; + const uint8_t mx_mode_enable_sopi = 0x01; + const uint8_t mx_mode_enable_dopi = 0x02; + uint8_t mx_mode; + if (self->set->octal_mode == OSPI_FLASH_OCTAL_MODE_DDD) { + mx_mode = mx_mode_enable_dopi; + } else { + mx_mode = mx_mode_enable_sopi; + } + ospi_flash_wren_spi(self); + uint8_t buf[6] = {cmd_wrcr2, 0, 0, 0, 0, mx_mode}; + ospi_spi_transfer(&self->cfg, sizeof(buf), buf, buf); + if (self->set->octal_mode == OSPI_FLASH_OCTAL_MODE_DDD) { + self->cfg.ddr_en = 1; + } else { + self->cfg.ddr_en = 0; + } + return 0; +} + +static uint8_t ospi_flash_mx_read_cr_helper(ospi_flash_t *self, uint16_t command, uint32_t addr) { + // TODO: currently only works in DDR mode + + uint16_t buf[1] = {0}; + ospi_setup_read_ext(&self->cfg, self->set->rxds, OSPI_INST_L_16bit, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, 1, 4); + ospi_push(&self->cfg, command); + ospi_recv_blocking_16bit_data(&self->cfg, addr, buf); + return buf[0] & 0xff; +} + +uint8_t ospi_flash_mx_read_cr(ospi_flash_t *self) { + return ospi_flash_mx_read_cr_helper(self, 0x15ea, 0); +} + +uint8_t ospi_flash_mx_read_cr2(ospi_flash_t *self, uint32_t addr) { + return ospi_flash_mx_read_cr_helper(self, 0x718e, addr); +} + +static int ospi_flash_mx_write_cr_helper(ospi_flash_t *self, uint16_t command, uint32_t addr, uint8_t value) { + // TODO: currently only works in DDR mode + + // Enable writes so that the register can be modified. + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); + if (ret < 0) { + return ret; + } + + // Do the write. + ospi_setup_write_ext(&self->cfg, self->set->rxds, OSPI_INST_L_16bit, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit); + ospi_push(&self->cfg, command); ospi_push(&self->cfg, addr); - ospi_send_blocking(&self->cfg, value); + ospi_push(&self->cfg, value << 8); // in DDR mode, MSByte contains the register value to write + ospi_writel((&self->cfg), ser, self->cfg.ser); + while ((ospi_readl((&self->cfg), sr) & (SR_TF_EMPTY | SR_BUSY)) != SR_TF_EMPTY) { + } + + // Wait for the write to finish. + return ospi_flash_wait_wip0(self); +} + +int ospi_flash_mx_write_cr(ospi_flash_t *self, uint8_t value) { + return ospi_flash_mx_write_cr_helper(self, 0x01fe, 1, value); } +int ospi_flash_mx_write_cr2(ospi_flash_t *self, uint32_t addr, uint8_t value) { + return ospi_flash_mx_write_cr_helper(self, 0x728d, addr, value); +} + +/******************************************************************************/ +// SPI flash initialisation. + int ospi_flash_init(void) { - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; + + const ospi_pin_settings_t *pin = &ospi_pin_settings; + const ospi_flash_settings_t *set = &ospi_flash_settings; + self->pin = pin; + self->set = set; uint32_t pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; - pinconf_set(pin_OSPI1_CS->port, pin_OSPI1_CS->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); - pinconf_set(pin_OSPI1_SCLK->port, pin_OSPI1_SCLK->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST); - pinconf_set(pin_OSPI1_D0->port, pin_OSPI1_D0->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D1->port, pin_OSPI1_D1->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D2->port, pin_OSPI1_D2->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D3->port, pin_OSPI1_D3->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - #if defined(pin_OSPI1_D4) - pinconf_set(pin_OSPI1_D4->port, pin_OSPI1_D4->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D5->port, pin_OSPI1_D5->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D6->port, pin_OSPI1_D6->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D7->port, pin_OSPI1_D7->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - #endif - #if defined(pin_OSPI1_RXDS) - pinconf_set(pin_OSPI1_RXDS->port, pin_OSPI1_RXDS->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - #endif - - #if defined(pin_OSPI1_RXDS) - if (pin_OSPI1_RXDS->port == PORT_10 && pin_OSPI1_RXDS->pin == PIN_7) { - // Alif: P5_6 is needed to support proper alt function selection of P10_7. - pinconf_set(PORT_5, PIN_6, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_cs->port, pin->pin_cs->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); + pinconf_set(pin->pin_clk->port, pin->pin_clk->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST); + if (pin->pin_rwds != NULL) { + pinconf_set(pin->pin_rwds->port, pin->pin_rwds->pin, OSPI_PIN_FUNCTION, pad_ctrl); + if (pin->pin_rwds->port == PORT_10 && pin->pin_rwds->pin == PIN_7) { + // Alif: P5_6 is needed to support proper alt function selection of P10_7. + pinconf_set(PORT_5, PIN_6, OSPI_PIN_FUNCTION, pad_ctrl); + } + } + pinconf_set(pin->pin_d0->port, pin->pin_d0->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d1->port, pin->pin_d1->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d3->port, pin->pin_d3->pin, OSPI_PIN_FUNCTION, pad_ctrl); + if (pin->pin_d4 != NULL) { + pinconf_set(pin->pin_d4->port, pin->pin_d4->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d5->port, pin->pin_d5->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d6->port, pin->pin_d6->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d7->port, pin->pin_d7->pin, OSPI_PIN_FUNCTION, pad_ctrl); } - #endif // Reset the SPI flash. - mp_hal_pin_output(pin_OSPI1_RESET); - mp_hal_pin_low(pin_OSPI1_RESET); - mp_hal_delay_us(30); - mp_hal_pin_high(pin_OSPI1_RESET); + mp_hal_pin_output(pin->pin_reset); + mp_hal_pin_low(pin->pin_reset); + mp_hal_delay_us(100); + mp_hal_pin_high(pin->pin_reset); + mp_hal_delay_us(1000); // Configure the OSPI peripheral. - self->cfg.regs = (ssi_regs_t *)OSPI1_BASE; - self->cfg.aes_regs = (aes_regs_t *)AES1_BASE; - self->cfg.xip_base = (volatile void *)OSPI1_XIP_BASE; - self->cfg.ser = 1; + if (pin->peripheral_number == 0) { + self->cfg.regs = (ssi_regs_t *)OSPI0_BASE; + self->cfg.aes_regs = (aes_regs_t *)AES0_BASE; + self->cfg.xip_base = (volatile void *)OSPI0_XIP_BASE; + } else { + self->cfg.regs = (ssi_regs_t *)OSPI1_BASE; + self->cfg.aes_regs = (aes_regs_t *)AES1_BASE; + self->cfg.xip_base = (volatile void *)OSPI1_XIP_BASE; + } + self->cfg.ser = 1; // enable slave select self->cfg.addrlen = 8; // 32-bit address length - self->cfg.ospi_clock = ospi_flash_settings.freq_mhz; + self->cfg.ospi_clock = set->freq_hz; self->cfg.ddr_en = 0; self->cfg.wait_cycles = 0; // used only for ospi_xip_exit ospi_init(&self->cfg); - if (ospi_flash_settings.is_oct && ospi_flash_settings.is_ddr) { - // Switch SPI flash to Octal DDR mode. - ospi_flash_write_reg_sdr(self, CMD_WRVOL, 0x00, ISSI_MODE_OCTAL_DDR_DQS); - self->cfg.ddr_en = 1; + // Check the device ID before attempting to switch to octal mode (if needed). + if (ospi_flash_read_id_spi(self) != set->jedec_id) { + return -1; } - // Check the device ID. - if (ospi_flash_read_id(self) != ospi_flash_settings.jedec_id) { - return -1; + // Switch to octal mode if needed. + if (set->octal_switch != NULL) { + set->octal_switch(self); + + // Check the device ID after switching mode. + if (ospi_flash_read_id(self) != set->jedec_id) { + return -1; + } } return 0; } +/******************************************************************************/ +// Top-level read/erase/write functions. + int ospi_flash_erase_sector(uint32_t addr) { - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; - mp_spiflash_write_cmd(self, CMD_WREN); - int ret = mp_spiflash_wait_wel1(self); + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); if (ret < 0) { return ret; } - ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/); - ospi_push(&self->cfg, CMD_SEC_ERASE_32ADDR); - ospi_send_blocking(&self->cfg, addr); + ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); - return mp_spiflash_wait_wip0(self); + return ospi_flash_wait_wip0(self); } int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { // OSPI FIFO is limited to 256 bytes. Need DMA to get a longer read. - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; while (len) { - uint32_t l = len; + uint32_t l = len / 4; if (l > 256) { l = 256; } - ospi_setup_read(&self->cfg, 8 /* 32-bit addr len*/, l, ospi_flash_settings.read_dummy_cycles); - ospi_push(&self->cfg, ospi_flash_settings.read_command); - ospi_recv_blocking2(&self->cfg, addr, dest); - addr += l; - len -= l; - dest += l; + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_32bit, l, self->set->read_dummy_cycles); + ospi_push(&self->cfg, self->set->read_command); + ospi_recv_blocking_32bit_data(&self->cfg, addr, (uint32_t *)dest); + addr += l * 4; + len -= l * 4; + dest += l * 4; } return 0; } static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src) { - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; - mp_spiflash_write_cmd(self, CMD_WREN); - int ret = mp_spiflash_wait_wel1(self); + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); if (ret < 0) { return ret; } - ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/); - ospi_push(&self->cfg, ospi_flash_settings.write_command); + ospi_setup_write_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_32bit); + ospi_push(&self->cfg, self->set->write_command); ospi_push(&self->cfg, addr); - while (--len) { - ospi_push(&self->cfg, *src++); + + const uint32_t *src32 = (const uint32_t *)src; + for (; len; len -= 4) { + ospi_push(&self->cfg, __ROR(*src32++, 16)); } - ospi_send_blocking(&self->cfg, *src); - return mp_spiflash_wait_wip0(self); + ospi_writel((&self->cfg), ser, self->cfg.ser); + while ((ospi_readl((&self->cfg), sr) & (SR_TF_EMPTY | SR_BUSY)) != SR_TF_EMPTY) { + } + + return ospi_flash_wait_wip0(self); } int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { @@ -264,4 +383,5 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { } return ret; } + #endif // MICROPY_HW_ENABLE_OSPI diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 945dc4bb07ba4..6f5d8327b416d 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -26,24 +26,67 @@ #ifndef MICROPY_INCLUDED_ALIF_OSPI_FLASH_H #define MICROPY_INCLUDED_ALIF_OSPI_FLASH_H -#include -#include +#include "py/mphal.h" + +// Format of command, address and data phases. +enum { + OSPI_FLASH_OCTAL_MODE_SSS, + OSPI_FLASH_OCTAL_MODE_SDD, + OSPI_FLASH_OCTAL_MODE_DDD, +}; + +struct _ospi_flash_t; + +typedef struct _ospi_pin_settings_t { + uint32_t peripheral_number; + const mp_hal_pin_obj_t pin_reset; + const mp_hal_pin_obj_t pin_cs; + const mp_hal_pin_obj_t pin_clk; + const mp_hal_pin_obj_t pin_rwds; + const mp_hal_pin_obj_t pin_d0; + const mp_hal_pin_obj_t pin_d1; + const mp_hal_pin_obj_t pin_d2; + const mp_hal_pin_obj_t pin_d3; + const mp_hal_pin_obj_t pin_d4; + const mp_hal_pin_obj_t pin_d5; + const mp_hal_pin_obj_t pin_d6; + const mp_hal_pin_obj_t pin_d7; +} ospi_pin_settings_t; typedef struct _ospi_flash_settings_t { uint32_t jedec_id; - uint32_t freq_mhz; - bool is_quad : 1; - bool is_oct : 1; - bool is_ddr : 1; + uint32_t freq_hz; + int (*octal_switch)(struct _ospi_flash_t *); + uint8_t octal_mode; + bool rxds; + uint8_t inst_len; + uint8_t xip_data_len; + uint16_t read_sr; + uint8_t read_sr_dummy_cycles; + uint16_t read_id; uint8_t read_id_dummy_cycles; + uint16_t write_en; + uint16_t read_command; uint8_t read_dummy_cycles; - uint8_t read_command; - uint8_t write_command; + uint16_t write_command; + uint16_t erase_command; } ospi_flash_settings_t; -// Provided by the board when it enables OSPI1. +// Provided by the board when it enables OSPI. +extern const ospi_pin_settings_t ospi_pin_settings; extern const ospi_flash_settings_t ospi_flash_settings; +// Functions specific to ISSI flash chips. +int ospi_flash_issi_octal_switch(struct _ospi_flash_t *self); + +// Functions specific to MX flash chips. +int ospi_flash_mx_octal_switch(struct _ospi_flash_t *self); +uint8_t ospi_flash_mx_read_cr(struct _ospi_flash_t *self); +uint8_t ospi_flash_mx_read_cr2(struct _ospi_flash_t *self, uint32_t addr); +int ospi_flash_mx_write_cr(struct _ospi_flash_t *self, uint8_t value); +int ospi_flash_mx_write_cr2(struct _ospi_flash_t *self, uint32_t addr, uint8_t value); + +// SPI flash interface. int ospi_flash_init(void); int ospi_flash_erase_sector(uint32_t addr); int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest); diff --git a/ports/alif/ospi_xip_user.h b/ports/alif/ospi_xip_user.h deleted file mode 100644 index a15baff2a8ba4..0000000000000 --- a/ports/alif/ospi_xip_user.h +++ /dev/null @@ -1,5 +0,0 @@ -// This file is needed by ospi_xip/source/ospi/ospi_drv.c. -#define OSPI_XIP_ENABLE_AES_DECRYPTION (0) -#define OSPI_XIP_RX_SAMPLE_DELAY (4) -#define OSPI_XIP_DDR_DRIVE_EDGE (1) -#define OSPI_XIP_RXDS_DELAY (12) From 41e16886b1bec870b601d8eb14cd4b05426d8015 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Oct 2024 23:07:35 +1100 Subject: [PATCH 483/546] alif/ospi_flash: Enter XIP mode when flash is idle. Signed-off-by: Damien George --- ports/alif/ospi_flash.c | 47 ++++++++++++++++++++++++++++++++++++++++- ports/alif/ospi_flash.h | 1 + 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 6f083de630aa9..be575d9ed772d 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -50,10 +50,14 @@ typedef struct _ospi_flash_t { const ospi_pin_settings_t *pin; const ospi_flash_settings_t *set; ospi_flash_cfg_t cfg; + bool xip_active; } ospi_flash_t; static ospi_flash_t global_flash; +static int ospi_flash_xip_enter(ospi_flash_t *self); +static int ospi_flash_xip_exit(ospi_flash_t *self); + /******************************************************************************/ // Generic SPI-flash helper functions. @@ -299,6 +303,34 @@ int ospi_flash_init(void) { } } + // Enter XIP mode. It will be disabled during flash read/erase/write. + ospi_flash_xip_enter(self); + + return 0; +} + +uintptr_t ospi_flash_get_xip_base(void) { + ospi_flash_t *self = &global_flash; + return (uintptr_t)self->cfg.xip_base; +} + +static int ospi_flash_xip_enter(ospi_flash_t *self) { + if (!self->xip_active) { + uint32_t irq_state = disable_irq(); + self->xip_active = true; + ospi_xip_enter_16bit_cmd(&self->cfg, self->set->xip_data_len, self->set->read_command, self->set->read_command, self->set->read_dummy_cycles); + enable_irq(irq_state); + } + return 0; +} + +static int ospi_flash_xip_exit(ospi_flash_t *self) { + if (self->xip_active) { + uint32_t irq_state = disable_irq(); + ospi_xip_exit_16bit_cmd(&self->cfg, self->set->read_command, self->set->read_command); + self->xip_active = false; + enable_irq(irq_state); + } return 0; } @@ -308,21 +340,29 @@ int ospi_flash_init(void) { int ospi_flash_erase_sector(uint32_t addr) { ospi_flash_t *self = &global_flash; + ospi_flash_xip_exit(self); + ospi_flash_write_cmd(self, self->set->write_en); int ret = ospi_flash_wait_wel1(self); if (ret < 0) { + ospi_flash_xip_enter(self); return ret; } ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); + ret = ospi_flash_wait_wip0(self); - return ospi_flash_wait_wip0(self); + ospi_flash_xip_enter(self); + return ret; } int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { // OSPI FIFO is limited to 256 bytes. Need DMA to get a longer read. + // Note that direct reading is much faster than using XIP memory-mapped read. ospi_flash_t *self = &global_flash; + ospi_flash_xip_exit(self); + while (len) { uint32_t l = len / 4; if (l > 256) { @@ -336,6 +376,7 @@ int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { dest += l * 4; } + ospi_flash_xip_enter(self); return 0; } @@ -365,6 +406,8 @@ static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src } int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { + ospi_flash_xip_exit(&global_flash); + int ret = 0; uint32_t offset = addr & (PAGE_SIZE - 1); while (len) { @@ -381,6 +424,8 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { src += rest; offset = 0; } + + ospi_flash_xip_enter(&global_flash); return ret; } diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 6f5d8327b416d..8fe69764a3777 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -88,6 +88,7 @@ int ospi_flash_mx_write_cr2(struct _ospi_flash_t *self, uint32_t addr, uint8_t v // SPI flash interface. int ospi_flash_init(void); +uintptr_t ospi_flash_get_xip_base(void); int ospi_flash_erase_sector(uint32_t addr); int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest); int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src); From 3d17f634785f4e151d883b1d1110db3a66673efd Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 21 Nov 2024 07:55:10 +0100 Subject: [PATCH 484/546] alif/mpu: Define constants for MPU regions. Signed-off-by: iabdalkader --- ports/alif/mpu.c | 34 +++++++++++++++++----------------- ports/alif/mpu.h | 17 ++++++++++++----- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/ports/alif/mpu.c b/ports/alif/mpu.c index 11c6273fcb345..471eff20a2c5b 100644 --- a/ports/alif/mpu.c +++ b/ports/alif/mpu.c @@ -29,48 +29,48 @@ #include ALIF_CMSIS_H static const ARM_MPU_Region_t mpu_table[] __STARTUP_RO_DATA_ATTRIBUTE = { - { /* SRAM0 - 4MB : RO-0, NP-1, XN-0 */ + [MP_MPU_REGION_SRAM0] = { /* SRAM0 - 4MB : RO-0, NP-1, XN-0 */ .RBAR = ARM_MPU_RBAR(0x02000000, ARM_MPU_SH_NON, 0, 1, 0), - .RLAR = ARM_MPU_RLAR(0x023FFFFF, MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT) + .RLAR = ARM_MPU_RLAR(0x023FFFFF, MP_MPU_ATTR_NORMAL_WT_RA_TRANSIENT) }, - { /* SRAM1 - 2.5MB : RO-0, NP-1, XN-0 */ + [MP_MPU_REGION_SRAM1] = { /* SRAM1 - 2.5MB : RO-0, NP-1, XN-0 */ .RBAR = ARM_MPU_RBAR(0x08000000, ARM_MPU_SH_NON, 0, 1, 0), - .RLAR = ARM_MPU_RLAR(0x0827FFFF, MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA) + .RLAR = ARM_MPU_RLAR(0x0827FFFF, MP_MPU_ATTR_NORMAL_WB_RA_WA) }, - { /* Host Peripherals - 16MB : RO-0, NP-1, XN-1 */ + [MP_MPU_REGION_HOST_PERIPHERALS] = { /* Host Peripherals - 16MB : RO-0, NP-1, XN-1 */ .RBAR = ARM_MPU_RBAR(0x1A000000, ARM_MPU_SH_NON, 0, 1, 1), - .RLAR = ARM_MPU_RLAR(0x1AFFFFFF, MP_MPU_ATTR_INDEX_DEVICE_nGnRE) + .RLAR = ARM_MPU_RLAR(0x1AFFFFFF, MP_MPU_ATTR_DEVICE_nGnRE) }, - { /* MRAM - 5.5MB : RO-1, NP-1, XN-0 */ + [MP_MPU_REGION_MRAM] = { /* MRAM - 5.5MB : RO-1, NP-1, XN-0 */ .RBAR = ARM_MPU_RBAR(0x80000000, ARM_MPU_SH_NON, 1, 1, 0), - .RLAR = ARM_MPU_RLAR(0x8057FFFF, MP_MPU_ATTR_INDEX_NORMAL_WT_RA) + .RLAR = ARM_MPU_RLAR(0x8057FFFF, MP_MPU_ATTR_NORMAL_WT_RA) }, - { /* OSPI Regs - 16MB : RO-0, NP-1, XN-1 */ + [MP_MPU_REGION_OSPI_REGISTERS] = { /* OSPI Regs - 16MB : RO-0, NP-1, XN-1 */ .RBAR = ARM_MPU_RBAR(0x83000000, ARM_MPU_SH_NON, 0, 1, 1), - .RLAR = ARM_MPU_RLAR(0x83FFFFFF, MP_MPU_ATTR_INDEX_DEVICE_nGnRE) + .RLAR = ARM_MPU_RLAR(0x83FFFFFF, MP_MPU_ATTR_DEVICE_nGnRE) }, - { /* OSPI0 XIP flash - 512MB : RO-1, NP-1, XN-0 */ + [MP_MPU_REGION_OSPI0_XIP] = { /* OSPI0 XIP flash - 512MB : RO-1, NP-1, XN-0 */ .RBAR = ARM_MPU_RBAR(0xA0000000, ARM_MPU_SH_NON, 1, 1, 0), - .RLAR = ARM_MPU_RLAR(0xBFFFFFFF, MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE) + .RLAR = ARM_MPU_RLAR(0xBFFFFFFF, MP_MPU_ATTR_NORMAL_NON_CACHEABLE) }, }; void MPU_Load_Regions(void) { // Configure memory attributes. - ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT, + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_WT_RA_TRANSIENT, ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0), ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0))); - ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_DEVICE_nGnRE, + ARM_MPU_SetMemAttr(MP_MPU_ATTR_DEVICE_nGnRE, ARM_MPU_ATTR(ARM_MPU_ATTR_DEVICE, ARM_MPU_ATTR_DEVICE_nGnRE)); - ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA, + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_WB_RA_WA, ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1), ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1))); - ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_WT_RA, + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_WT_RA, ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0), ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0))); - ARM_MPU_SetMemAttr(MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE, + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_NON_CACHEABLE, ARM_MPU_ATTR(ARM_MPU_ATTR_NON_CACHEABLE, ARM_MPU_ATTR_NON_CACHEABLE)); // Load the MPU regions from the table. diff --git a/ports/alif/mpu.h b/ports/alif/mpu.h index 87a7518a126be..c259c8585adfd 100644 --- a/ports/alif/mpu.h +++ b/ports/alif/mpu.h @@ -24,8 +24,15 @@ * THE SOFTWARE. */ -#define MP_MPU_ATTR_INDEX_NORMAL_WT_RA_TRANSIENT (0) -#define MP_MPU_ATTR_INDEX_DEVICE_nGnRE (1) -#define MP_MPU_ATTR_INDEX_NORMAL_WB_RA_WA (2) -#define MP_MPU_ATTR_INDEX_NORMAL_WT_RA (3) -#define MP_MPU_ATTR_INDEX_NORMAL_NON_CACHEABLE (4) +#define MP_MPU_ATTR_NORMAL_WT_RA_TRANSIENT (0) +#define MP_MPU_ATTR_DEVICE_nGnRE (1) +#define MP_MPU_ATTR_NORMAL_WB_RA_WA (2) +#define MP_MPU_ATTR_NORMAL_WT_RA (3) +#define MP_MPU_ATTR_NORMAL_NON_CACHEABLE (4) + +#define MP_MPU_REGION_SRAM0 (0) +#define MP_MPU_REGION_SRAM1 (1) +#define MP_MPU_REGION_HOST_PERIPHERALS (2) +#define MP_MPU_REGION_MRAM (3) +#define MP_MPU_REGION_OSPI_REGISTERS (4) +#define MP_MPU_REGION_OSPI0_XIP (5) From 5152a1f04da3699bc35419bc40bf204760eac66c Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 21 Nov 2024 07:56:02 +0100 Subject: [PATCH 485/546] alif/mpmetalport: Add Open-AMP MPU region. Define an MPU region for Open-AMP and remove hard-coded attribute. Signed-off-by: iabdalkader --- ports/alif/mpmetalport.c | 18 ++++++------------ ports/alif/mpmetalport.h | 3 ++- ports/alif/mpu.h | 1 + 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/ports/alif/mpmetalport.c b/ports/alif/mpmetalport.c index f80d039c26d23..a7d4025468a16 100644 --- a/ports/alif/mpmetalport.c +++ b/ports/alif/mpmetalport.c @@ -48,28 +48,22 @@ int metal_sys_init(const struct metal_init_params *params) { hwsem_reset(METAL_HSEM_REMOTE); #endif - // Enable the hardware semaphore IRQ. - NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); - NVIC_SetPriority(METAL_HSEM_IRQn, IRQ_PRI_HWSEM); - NVIC_EnableIRQ(METAL_HSEM_IRQn); - // If cache management is not enabled, configure the MPU to disable // caching for the entire Open-AMP shared memory region. #ifndef VIRTIO_USE_DCACHE ARM_MPU_Disable(); - // NOTE: The startup code uses the first 4 attributes. - #define MEMATTR_IDX_NORMAL_NON_CACHEABLE 4 - ARM_MPU_SetMemAttr(MEMATTR_IDX_NORMAL_NON_CACHEABLE, ARM_MPU_ATTR( - ARM_MPU_ATTR_NON_CACHEABLE, - ARM_MPU_ATTR_NON_CACHEABLE)); MPU->RNR = METAL_MPU_REGION_ID; - MPU->RBAR = ARM_MPU_RBAR(METAL_MPU_REGION_BASE, ARM_MPU_SH_NON, 0, 1, 0); // RO-0, NP-1, XN-0 - MPU->RLAR = ARM_MPU_RLAR(METAL_MPU_REGION_BASE + METAL_MPU_REGION_SIZE - 1, MEMATTR_IDX_NORMAL_NON_CACHEABLE); + MPU->RBAR = ARM_MPU_RBAR(METAL_MPU_REGION_BASE, ARM_MPU_SH_NON, 0, 1, 1); // RO-0, NP-1, XN-1 + MPU->RLAR = ARM_MPU_RLAR(METAL_MPU_REGION_BASE + METAL_MPU_REGION_SIZE - 1, MP_MPU_ATTR_NORMAL_NON_CACHEABLE); ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); #endif metal_bus_register(&metal_generic_bus); + // Enable the hardware semaphore IRQ. + NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); + NVIC_SetPriority(METAL_HSEM_IRQn, IRQ_PRI_HWSEM); + NVIC_EnableIRQ(METAL_HSEM_IRQn); return 0; } diff --git a/ports/alif/mpmetalport.h b/ports/alif/mpmetalport.h index 5cd697c0e9c7b..7a428dbc6c62a 100644 --- a/ports/alif/mpmetalport.h +++ b/ports/alif/mpmetalport.h @@ -31,6 +31,7 @@ #include #include "py/mphal.h" #include "py/runtime.h" +#include "mpu.h" #define METAL_HAVE_STDATOMIC_H 0 #define METAL_HAVE_FUTEX_H 0 @@ -66,7 +67,7 @@ #define METAL_SHM_ADDR ((metal_phys_addr_t)(_openamp_shm_region_start + METAL_RSC_SIZE)) #define METAL_SHM_SIZE ((size_t)(_openamp_shm_region_end - _openamp_shm_region_start - METAL_RSC_SIZE)) -#define METAL_MPU_REGION_ID (9) // NOTE: The startup code uses the first 9 regions. +#define METAL_MPU_REGION_ID (MP_MPU_REGION_OPENAMP) #define METAL_MPU_REGION_BASE ((uint32_t)_openamp_shm_region_start) #define METAL_MPU_REGION_SIZE (0x00010000U) diff --git a/ports/alif/mpu.h b/ports/alif/mpu.h index c259c8585adfd..88fbe011206ed 100644 --- a/ports/alif/mpu.h +++ b/ports/alif/mpu.h @@ -36,3 +36,4 @@ #define MP_MPU_REGION_MRAM (3) #define MP_MPU_REGION_OSPI_REGISTERS (4) #define MP_MPU_REGION_OSPI0_XIP (5) +#define MP_MPU_REGION_OPENAMP (6) From 1585080ff0ee3075e49fce01effddfc51644b5ed Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 14 Dec 2024 15:47:59 +0100 Subject: [PATCH 486/546] alif/ospi_flash: Fix XIP for 8-bit instructions (ISSI). Disable XIP instruction DDR for 8-bit instructions. Signed-off-by: iabdalkader --- ports/alif/ospi_ext.c | 15 ++++++++------- ports/alif/ospi_ext.h | 4 ++-- ports/alif/ospi_flash.c | 5 +++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/ports/alif/ospi_ext.c b/ports/alif/ospi_ext.c index 48446d0622d48..3226c244697b8 100644 --- a/ports/alif/ospi_ext.c +++ b/ports/alif/ospi_ext.c @@ -28,8 +28,6 @@ #include "ospi_ext.h" #include "ospi_xip_user.h" -#define INST_L16bit (3) - static void ospi_xip_disable(ospi_flash_cfg_t *ospi_cfg) { ospi_cfg->aes_regs->aes_control &= ~AES_CONTROL_XIP_EN; } @@ -208,7 +206,7 @@ void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_l spi_enable(ospi_cfg); } -void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles) { +void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles) { spi_disable(ospi_cfg); uint32_t val = CTRLR0_IS_MST @@ -224,12 +222,11 @@ void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uin val = (OCTAL << XIP_CTRL_FRF_OFFSET) | (0x2 << XIP_CTRL_TRANS_TYPE_OFFSET) | (ADDR_L32bit << XIP_CTRL_ADDR_L_OFFSET) - | (INST_L16bit << XIP_CTRL_INST_L_OFFSET) + | (inst_len << XIP_CTRL_INST_L_OFFSET) | (0x0 << XIP_CTRL_MD_BITS_EN_OFFSET) | (read_dummy_cycles << XIP_CTRL_WAIT_CYCLES_OFFSET) | (0x1 << XIP_CTRL_DFC_HC_OFFSET) | (ospi_cfg->ddr_en << XIP_CTRL_DDR_EN_OFFSET) - | (ospi_cfg->ddr_en << XIP_CTRL_INST_DDR_EN_OFFSET) | (0x1 << XIP_CTRL_RXDS_EN_OFFSET) | (0x1 << XIP_CTRL_INST_EN_OFFSET) | (0x0 << XIP_CTRL_CONT_XFER_EN_OFFSET) @@ -239,6 +236,10 @@ void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uin | (0x0 << XIP_PREFETCH_EN_OFFSET) | (0x0 << XIP_CTRL_RXDS_VL_EN_OFFSET); + if (inst_len == OSPI_INST_L_16bit) { + val |= 1 << XIP_CTRL_INST_DDR_EN_OFFSET; + } + ospi_writel(ospi_cfg, xip_ctrl, val); ospi_writel(ospi_cfg, rx_sample_dly, 4); @@ -254,7 +255,7 @@ void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uin ospi_xip_enable(ospi_cfg); } -void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, uint16_t wrap_command) { +void ospi_xip_exit_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint16_t incr_command, uint16_t wrap_command) { spi_disable(ospi_cfg); uint32_t val = CTRLR0_IS_MST @@ -272,7 +273,7 @@ void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, | (2 << CTRLR0_XIP_MBL_OFFSET) | (1 << CTRLR0_XIP_DFS_HC_OFFSET) | (1 << CTRLR0_XIP_INST_EN_OFFSET) - | (CTRLR0_INST_L_16bit << CTRLR0_INST_L_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) | (ospi_cfg->addrlen) << (CTRLR0_ADDR_L_OFFSET) | (ospi_cfg->wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); diff --git a/ports/alif/ospi_ext.h b/ports/alif/ospi_ext.h index e467772d15670..e5a2b50beb693 100644 --- a/ports/alif/ospi_ext.h +++ b/ports/alif/ospi_ext.h @@ -51,7 +51,7 @@ int ospi_recv_blocking_32bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len); -void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles); -void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, uint16_t wrap_command); +void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles); +void ospi_xip_exit_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint16_t incr_command, uint16_t wrap_command); #endif // MICROPY_INCLUDED_ALIF_OSPI_EXT_H diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index be575d9ed772d..c3ae50a9ee4ef 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -318,7 +318,8 @@ static int ospi_flash_xip_enter(ospi_flash_t *self) { if (!self->xip_active) { uint32_t irq_state = disable_irq(); self->xip_active = true; - ospi_xip_enter_16bit_cmd(&self->cfg, self->set->xip_data_len, self->set->read_command, self->set->read_command, self->set->read_dummy_cycles); + ospi_xip_enter_ext(&self->cfg, self->set->inst_len, self->set->xip_data_len, + self->set->read_command, self->set->read_command, self->set->read_dummy_cycles); enable_irq(irq_state); } return 0; @@ -327,7 +328,7 @@ static int ospi_flash_xip_enter(ospi_flash_t *self) { static int ospi_flash_xip_exit(ospi_flash_t *self) { if (self->xip_active) { uint32_t irq_state = disable_irq(); - ospi_xip_exit_16bit_cmd(&self->cfg, self->set->read_command, self->set->read_command); + ospi_xip_exit_ext(&self->cfg, self->set->inst_len, self->set->read_command, self->set->read_command); self->xip_active = false; enable_irq(irq_state); } From aec030004f019155334f56f61cf230fcf63ad759 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 16 Dec 2024 15:52:09 +0100 Subject: [PATCH 487/546] alif/ospi_flash: Support flash device auto-detection in runtime. This commit enables detecting the flash device in runtime, and uses the settings of the detected device instead of board-defined flash settings. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 26 +++++++++-- ports/alif/ospi_flash.h | 4 +- ports/alif/ospi_flash_settings.h | 79 ++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 ports/alif/ospi_flash_settings.h diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index c3ae50a9ee4ef..8bffd69805c9f 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -238,9 +238,9 @@ int ospi_flash_init(void) { ospi_flash_t *self = &global_flash; const ospi_pin_settings_t *pin = &ospi_pin_settings; - const ospi_flash_settings_t *set = &ospi_flash_settings; + const ospi_flash_settings_t *set = NULL; + self->pin = pin; - self->set = set; uint32_t pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; @@ -283,16 +283,32 @@ int ospi_flash_init(void) { } self->cfg.ser = 1; // enable slave select self->cfg.addrlen = 8; // 32-bit address length - self->cfg.ospi_clock = set->freq_hz; self->cfg.ddr_en = 0; self->cfg.wait_cycles = 0; // used only for ospi_xip_exit + self->cfg.ospi_clock = 100000; // use 100KHz for detection. ospi_init(&self->cfg); - // Check the device ID before attempting to switch to octal mode (if needed). - if (ospi_flash_read_id_spi(self) != set->jedec_id) { + // Read the device ID. + uint32_t jedec_id = ospi_flash_read_id_spi(self); + + // Auto-detect the flash. + for (size_t i = 0; i < ospi_flash_settings_len; i++) { + set = &ospi_flash_settings[i]; + if (jedec_id == set->jedec_id) { + self->set = set; + break; + } + } + + if (self->set == NULL) { + // Flash part is not supported. return -1; } + // Switch to the higher frequency. + self->cfg.ospi_clock = set->freq_hz; + ospi_init(&self->cfg); + // Switch to octal mode if needed. if (set->octal_switch != NULL) { set->octal_switch(self); diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 8fe69764a3777..20846d4471315 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_ALIF_OSPI_FLASH_H #include "py/mphal.h" +#include "ospi_flash_settings.h" // Format of command, address and data phases. enum { @@ -74,7 +75,8 @@ typedef struct _ospi_flash_settings_t { // Provided by the board when it enables OSPI. extern const ospi_pin_settings_t ospi_pin_settings; -extern const ospi_flash_settings_t ospi_flash_settings; +extern const ospi_flash_settings_t ospi_flash_settings[]; +extern const size_t ospi_flash_settings_len; // Functions specific to ISSI flash chips. int ospi_flash_issi_octal_switch(struct _ospi_flash_t *self); diff --git a/ports/alif/ospi_flash_settings.h b/ports/alif/ospi_flash_settings.h new file mode 100644 index 0000000000000..c409f856dfa1a --- /dev/null +++ b/ports/alif/ospi_flash_settings.h @@ -0,0 +1,79 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 __OSPI_FLASH_SETTINGS_H__ +#define __OSPI_FLASH_SETTINGS_H__ +#include +#include "ospi_flash.h" + +// Macronix MX25 +#define OSPI_FLASH_SETTINGS_MX25 \ + .octal_switch = ospi_flash_mx_octal_switch, \ + .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ + .rxds = true, \ + .inst_len = OSPI_INST_L_16bit, \ + .xip_data_len = OSPI_DATA_L_16bit, \ + .read_sr = 0x05fa, \ + .read_sr_dummy_cycles = 4, \ + .write_en = 0x06f9, \ + .read_id = 0x9f60, \ + .read_id_dummy_cycles = 4, \ + .read_command = 0xee11, \ + .write_command = 0x12ed, \ + .erase_command = 0x21de + +// Everspin EM. +#define OSPI_FLASH_SETTINGS_EM \ + .octal_switch = ospi_flash_issi_octal_switch, \ + .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ + .rxds = false, \ + .inst_len = OSPI_INST_L_8bit, \ + .xip_data_len = OSPI_DATA_L_16bit, \ + .read_sr = 0x05, \ + .read_sr_dummy_cycles = 8, \ + .write_en = 0x06, \ + .read_id = 0x9f, \ + .read_id_dummy_cycles = 8, \ + .read_command = 0xfd, \ + .write_command = 0xc2, \ + .erase_command = 0x21 + +// ISSI IS25. +#define OSPI_FLASH_SETTINGS_IS25 \ + .octal_switch = ospi_flash_issi_octal_switch, \ + .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ + .rxds = true, \ + .inst_len = OSPI_INST_L_8bit, \ + .xip_data_len = OSPI_DATA_L_16bit, \ + .read_sr = 0x05, \ + .read_sr_dummy_cycles = 8, \ + .write_en = 0x06, \ + .read_id = 0x9f, \ + .read_id_dummy_cycles = 8, \ + .read_command = 0xfd, \ + .write_command = 0xc2, \ + .erase_command = 0x21 +#endif // __OSPI_FLASH_SETTINGS_H__ From 8807f8d01bc29d7c09a71a329877a1e8ad617fa8 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 16 Dec 2024 15:52:51 +0100 Subject: [PATCH 488/546] alif/ospi_flash: Configure dummy cycles. The default dummy cycles may not match the actual flash frequency supported by a certain board. For example, the MX chip uses 20 dummy cycles by default which supports up to 200MHz DDR, but the maximum frequency supported by the AE3 board is 50MHz DDR. So the dummy cycles for this board can be as low as 6. It's important to set the correct dummy cycles, as it results in doubling the XIP read speed, in the case of the AE3 board. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 43 ++++++++++++++++++++++++-------- ports/alif/ospi_flash.h | 6 ++--- ports/alif/ospi_flash_settings.h | 6 ++--- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 8bffd69805c9f..279227aa36d06 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -146,13 +146,19 @@ static uint32_t ospi_flash_read_id(ospi_flash_t *self) { /******************************************************************************/ // Functions specific to ISSI flash chips. -int ospi_flash_issi_octal_switch(ospi_flash_t *self) { - // Switch SPI flash to Octal DDR mode. +int ospi_flash_issi_init(ospi_flash_t *self) { const uint8_t cmd_wrvol = 0x81; + + // Configure dummy cycles. + ospi_flash_wren_spi(self); + uint8_t buf0[5] = {cmd_wrvol, 0, 0, 1, self->set->read_dummy_cycles}; + ospi_spi_transfer(&self->cfg, sizeof(buf0), buf0, buf0); + + // Switch SPI flash to Octal DDR mode. const uint8_t issi_mode_octal_ddr_dqs = 0xe7; ospi_flash_wren_spi(self); - uint8_t buf[5] = {cmd_wrvol, 0, 0, 0, issi_mode_octal_ddr_dqs}; - ospi_spi_transfer(&self->cfg, sizeof(buf), buf, buf); + uint8_t buf1[5] = {cmd_wrvol, 0, 0, 0, issi_mode_octal_ddr_dqs}; + ospi_spi_transfer(&self->cfg, sizeof(buf1), buf1, buf1); self->cfg.ddr_en = 1; return 0; } @@ -160,8 +166,7 @@ int ospi_flash_issi_octal_switch(ospi_flash_t *self) { /******************************************************************************/ // Functions specific to MX flash chips. -int ospi_flash_mx_octal_switch(ospi_flash_t *self) { - // Switch SPI flash to Octal SDR or DDR mode (SOPI or DOPI) by writing to CR2. +int ospi_flash_mx_init(ospi_flash_t *self) { const uint8_t cmd_wrcr2 = 0x72; const uint8_t mx_mode_enable_sopi = 0x01; const uint8_t mx_mode_enable_dopi = 0x02; @@ -171,9 +176,26 @@ int ospi_flash_mx_octal_switch(ospi_flash_t *self) { } else { mx_mode = mx_mode_enable_sopi; } + + // Configure dummy cycles. + uint8_t ddc_value = 0; + const uint8_t ospi_flash_mx_ddc[][2] = { + {20, 0}, {18, 1}, {16, 2}, {14, 3}, {12, 4}, {10, 5}, {8, 6}, {6, 7} + }; + for (size_t i = 0; i < MP_ARRAY_SIZE(ospi_flash_mx_ddc); i++) { + if (self->set->read_dummy_cycles == ospi_flash_mx_ddc[i][0]) { + ddc_value = ospi_flash_mx_ddc[i][1]; + break; + } + } ospi_flash_wren_spi(self); - uint8_t buf[6] = {cmd_wrcr2, 0, 0, 0, 0, mx_mode}; - ospi_spi_transfer(&self->cfg, sizeof(buf), buf, buf); + uint8_t buf0[6] = {cmd_wrcr2, 0, 0, 3, 0, ddc_value}; + ospi_spi_transfer(&self->cfg, sizeof(buf0), buf0, buf0); + + // Switch SPI flash to Octal SDR or DDR mode. + ospi_flash_wren_spi(self); + uint8_t buf1[6] = {cmd_wrcr2, 0, 0, 0, 0, mx_mode}; + ospi_spi_transfer(&self->cfg, sizeof(buf1), buf1, buf1); if (self->set->octal_mode == OSPI_FLASH_OCTAL_MODE_DDD) { self->cfg.ddr_en = 1; } else { @@ -310,8 +332,8 @@ int ospi_flash_init(void) { ospi_init(&self->cfg); // Switch to octal mode if needed. - if (set->octal_switch != NULL) { - set->octal_switch(self); + if (set->flash_init != NULL) { + set->flash_init(self); // Check the device ID after switching mode. if (ospi_flash_read_id(self) != set->jedec_id) { @@ -321,7 +343,6 @@ int ospi_flash_init(void) { // Enter XIP mode. It will be disabled during flash read/erase/write. ospi_flash_xip_enter(self); - return 0; } diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 20846d4471315..39d270d7d5465 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -57,7 +57,7 @@ typedef struct _ospi_pin_settings_t { typedef struct _ospi_flash_settings_t { uint32_t jedec_id; uint32_t freq_hz; - int (*octal_switch)(struct _ospi_flash_t *); + int (*flash_init)(struct _ospi_flash_t *); uint8_t octal_mode; bool rxds; uint8_t inst_len; @@ -79,10 +79,10 @@ extern const ospi_flash_settings_t ospi_flash_settings[]; extern const size_t ospi_flash_settings_len; // Functions specific to ISSI flash chips. -int ospi_flash_issi_octal_switch(struct _ospi_flash_t *self); +int ospi_flash_issi_init(struct _ospi_flash_t *self); // Functions specific to MX flash chips. -int ospi_flash_mx_octal_switch(struct _ospi_flash_t *self); +int ospi_flash_mx_init(struct _ospi_flash_t *self); uint8_t ospi_flash_mx_read_cr(struct _ospi_flash_t *self); uint8_t ospi_flash_mx_read_cr2(struct _ospi_flash_t *self, uint32_t addr); int ospi_flash_mx_write_cr(struct _ospi_flash_t *self, uint8_t value); diff --git a/ports/alif/ospi_flash_settings.h b/ports/alif/ospi_flash_settings.h index c409f856dfa1a..c8605275cdcc0 100644 --- a/ports/alif/ospi_flash_settings.h +++ b/ports/alif/ospi_flash_settings.h @@ -31,7 +31,7 @@ // Macronix MX25 #define OSPI_FLASH_SETTINGS_MX25 \ - .octal_switch = ospi_flash_mx_octal_switch, \ + .flash_init = ospi_flash_mx_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ .rxds = true, \ .inst_len = OSPI_INST_L_16bit, \ @@ -47,7 +47,7 @@ // Everspin EM. #define OSPI_FLASH_SETTINGS_EM \ - .octal_switch = ospi_flash_issi_octal_switch, \ + .flash_init = ospi_flash_issi_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ .rxds = false, \ .inst_len = OSPI_INST_L_8bit, \ @@ -63,7 +63,7 @@ // ISSI IS25. #define OSPI_FLASH_SETTINGS_IS25 \ - .octal_switch = ospi_flash_issi_octal_switch, \ + .flash_init = ospi_flash_issi_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ .rxds = true, \ .inst_len = OSPI_INST_L_8bit, \ From 872f3d70d37d189525783137e53a50878314d37d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 14 Dec 2024 18:19:42 +0100 Subject: [PATCH 489/546] alif/ospi_flash: Add negative clock pin. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 28 ++++++++++++++++------------ ports/alif/ospi_flash.h | 3 ++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 279227aa36d06..8eaf923550141 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -264,26 +264,30 @@ int ospi_flash_init(void) { self->pin = pin; - uint32_t pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; + uint32_t ck_pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST; + uint32_t io_pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; pinconf_set(pin->pin_cs->port, pin->pin_cs->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); - pinconf_set(pin->pin_clk->port, pin->pin_clk->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST); + pinconf_set(pin->pin_clk_p->port, pin->pin_clk_p->pin, OSPI_PIN_FUNCTION, ck_pad_ctrl); + if (pin->pin_clk_n != NULL) { + pinconf_set(pin->pin_clk_n->port, pin->pin_clk_n->pin, OSPI_PIN_FUNCTION, ck_pad_ctrl); + } if (pin->pin_rwds != NULL) { - pinconf_set(pin->pin_rwds->port, pin->pin_rwds->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_rwds->port, pin->pin_rwds->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); if (pin->pin_rwds->port == PORT_10 && pin->pin_rwds->pin == PIN_7) { // Alif: P5_6 is needed to support proper alt function selection of P10_7. - pinconf_set(PORT_5, PIN_6, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(PORT_5, PIN_6, OSPI_PIN_FUNCTION, io_pad_ctrl); } } - pinconf_set(pin->pin_d0->port, pin->pin_d0->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d1->port, pin->pin_d1->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d3->port, pin->pin_d3->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d0->port, pin->pin_d0->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d1->port, pin->pin_d1->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d3->port, pin->pin_d3->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); if (pin->pin_d4 != NULL) { - pinconf_set(pin->pin_d4->port, pin->pin_d4->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d5->port, pin->pin_d5->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d6->port, pin->pin_d6->pin, OSPI_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin->pin_d7->port, pin->pin_d7->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d4->port, pin->pin_d4->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d5->port, pin->pin_d5->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d6->port, pin->pin_d6->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d7->port, pin->pin_d7->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); } // Reset the SPI flash. diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 39d270d7d5465..affa4e6102215 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -42,7 +42,8 @@ typedef struct _ospi_pin_settings_t { uint32_t peripheral_number; const mp_hal_pin_obj_t pin_reset; const mp_hal_pin_obj_t pin_cs; - const mp_hal_pin_obj_t pin_clk; + const mp_hal_pin_obj_t pin_clk_p; + const mp_hal_pin_obj_t pin_clk_n; const mp_hal_pin_obj_t pin_rwds; const mp_hal_pin_obj_t pin_d0; const mp_hal_pin_obj_t pin_d1; From 0709936653895e0de499e0a7019afb80eda7425c Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 19 Dec 2024 09:47:04 +0100 Subject: [PATCH 490/546] alif/ospi_flash: Enable pull-up IO2/WP. Leaving this pin low in combination with the default EM settings enables flash protection for the EM flash. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 8eaf923550141..d5aa99f2b493f 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -281,7 +281,7 @@ int ospi_flash_init(void) { } pinconf_set(pin->pin_d0->port, pin->pin_d0->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); pinconf_set(pin->pin_d1->port, pin->pin_d1->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, io_pad_ctrl | PADCTRL_DRIVER_DISABLED_PULL_UP); pinconf_set(pin->pin_d3->port, pin->pin_d3->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); if (pin->pin_d4 != NULL) { pinconf_set(pin->pin_d4->port, pin->pin_d4->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); From df06bf91a5a78c89eaa7b6b7d157df83c576553f Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 20 Dec 2024 08:57:08 +0100 Subject: [PATCH 491/546] alif/ospi_ext: Optimize XIP speed. This change increases XIP read speed to ~30Mbytes/s at 50MHz DDR: - Enable continuous mode. - Remove hard-coded settings. - Set XIP continuous mode timeout. The prefetch remains disabled. Although enabling the prefetch gives the best performance for the CPU in XIP mode, it must be disabled when the NPU accesses the OSPI flash. Signed-off-by: iabdalkader --- ports/alif/ospi_ext.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/alif/ospi_ext.c b/ports/alif/ospi_ext.c index 3226c244697b8..e3389d8bd54da 100644 --- a/ports/alif/ospi_ext.c +++ b/ports/alif/ospi_ext.c @@ -229,7 +229,7 @@ void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t | (ospi_cfg->ddr_en << XIP_CTRL_DDR_EN_OFFSET) | (0x1 << XIP_CTRL_RXDS_EN_OFFSET) | (0x1 << XIP_CTRL_INST_EN_OFFSET) - | (0x0 << XIP_CTRL_CONT_XFER_EN_OFFSET) + | (0x1 << XIP_CTRL_CONT_XFER_EN_OFFSET) | (0x0 << XIP_CTRL_HYPERBUS_EN_OFFSET) | (0x1 << XIP_CTRL_RXDS_SIG_EN) | (0x0 << XIP_CTRL_XIP_MBL_OFFSET) @@ -242,14 +242,15 @@ void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t ospi_writel(ospi_cfg, xip_ctrl, val); - ospi_writel(ospi_cfg, rx_sample_dly, 4); - ospi_writel(ospi_cfg, txd_drive_edge, 1); + ospi_writel(ospi_cfg, rx_sample_dly, OSPI_XIP_RX_SAMPLE_DELAY); + ospi_writel(ospi_cfg, txd_drive_edge, OSPI_XIP_DDR_DRIVE_EDGE); ospi_cfg->aes_regs->aes_rxds_delay = OSPI_XIP_RXDS_DELAY; ospi_writel(ospi_cfg, xip_mode_bits, 0x0); ospi_writel(ospi_cfg, xip_incr_inst, incr_command); ospi_writel(ospi_cfg, xip_wrap_inst, wrap_command); ospi_writel(ospi_cfg, xip_ser, ospi_cfg->ser); + ospi_writel(ospi_cfg, xip_cnt_time_out, 100); spi_enable(ospi_cfg); ospi_xip_enable(ospi_cfg); From 602bc86b6d8ebd200029568c060b878146050da1 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 8 Jan 2025 15:12:18 +0100 Subject: [PATCH 492/546] alif/ospi_flash: Use OSPI in XIP mode only. The OSPI controller supports concurrent direct/XIP accesses, there's no need to disable XIP on direct access. In addition to improving the performance, this change lays the groundwork for supporting access by the HP and HE cores simultaneously. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 39 ++++++++------------------------------- ports/alif/ospi_flash.h | 4 ++++ 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index d5aa99f2b493f..7400a678b226a 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -55,9 +55,6 @@ typedef struct _ospi_flash_t { static ospi_flash_t global_flash; -static int ospi_flash_xip_enter(ospi_flash_t *self); -static int ospi_flash_xip_exit(ospi_flash_t *self); - /******************************************************************************/ // Generic SPI-flash helper functions. @@ -188,6 +185,7 @@ int ospi_flash_mx_init(ospi_flash_t *self) { break; } } + ospi_flash_wren_spi(self); uint8_t buf0[6] = {cmd_wrcr2, 0, 0, 3, 0, ddc_value}; ospi_spi_transfer(&self->cfg, sizeof(buf0), buf0, buf0); @@ -355,7 +353,7 @@ uintptr_t ospi_flash_get_xip_base(void) { return (uintptr_t)self->cfg.xip_base; } -static int ospi_flash_xip_enter(ospi_flash_t *self) { +int ospi_flash_xip_enter(ospi_flash_t *self) { if (!self->xip_active) { uint32_t irq_state = disable_irq(); self->xip_active = true; @@ -366,7 +364,7 @@ static int ospi_flash_xip_enter(ospi_flash_t *self) { return 0; } -static int ospi_flash_xip_exit(ospi_flash_t *self) { +int ospi_flash_xip_exit(ospi_flash_t *self) { if (self->xip_active) { uint32_t irq_state = disable_irq(); ospi_xip_exit_ext(&self->cfg, self->set->inst_len, self->set->read_command, self->set->read_command); @@ -382,43 +380,23 @@ static int ospi_flash_xip_exit(ospi_flash_t *self) { int ospi_flash_erase_sector(uint32_t addr) { ospi_flash_t *self = &global_flash; - ospi_flash_xip_exit(self); - ospi_flash_write_cmd(self, self->set->write_en); int ret = ospi_flash_wait_wel1(self); if (ret < 0) { - ospi_flash_xip_enter(self); return ret; } ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); ret = ospi_flash_wait_wip0(self); - ospi_flash_xip_enter(self); + SCB_InvalidateDCache_by_Addr(global_flash.cfg.xip_base + addr, MICROPY_HW_FLASH_BLOCK_SIZE_BYTES); return ret; } int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { - // OSPI FIFO is limited to 256 bytes. Need DMA to get a longer read. - // Note that direct reading is much faster than using XIP memory-mapped read. ospi_flash_t *self = &global_flash; - - ospi_flash_xip_exit(self); - - while (len) { - uint32_t l = len / 4; - if (l > 256) { - l = 256; - } - ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_32bit, l, self->set->read_dummy_cycles); - ospi_push(&self->cfg, self->set->read_command); - ospi_recv_blocking_32bit_data(&self->cfg, addr, (uint32_t *)dest); - addr += l * 4; - len -= l * 4; - dest += l * 4; - } - - ospi_flash_xip_enter(self); + // Perform an XIP read (memcpy) + memcpy(dest, (void *)self->cfg.xip_base + addr, len); return 0; } @@ -448,10 +426,9 @@ static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src } int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { - ospi_flash_xip_exit(&global_flash); - int ret = 0; uint32_t offset = addr & (PAGE_SIZE - 1); + while (len) { size_t rest = PAGE_SIZE - offset; if (rest > len) { @@ -467,7 +444,7 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { offset = 0; } - ospi_flash_xip_enter(&global_flash); + SCB_InvalidateDCache_by_Addr(global_flash.cfg.xip_base + addr, len); return ret; } diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index affa4e6102215..0095ee2d4280b 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -89,6 +89,10 @@ uint8_t ospi_flash_mx_read_cr2(struct _ospi_flash_t *self, uint32_t addr); int ospi_flash_mx_write_cr(struct _ospi_flash_t *self, uint8_t value); int ospi_flash_mx_write_cr2(struct _ospi_flash_t *self, uint32_t addr, uint8_t value); +// XIP control +int ospi_flash_xip_enter(struct _ospi_flash_t *self); +int ospi_flash_xip_exit(struct _ospi_flash_t *self); + // SPI flash interface. int ospi_flash_init(void); uintptr_t ospi_flash_get_xip_base(void); From 92f056d58f74cfd3986aaef1eea08f4f948fe03a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 6 Feb 2025 10:14:34 +0100 Subject: [PATCH 493/546] alif/ospi_flash: Add 16-bit words swap flash setting. The byte order (endianness) seems to be swapped when read in 8D-8D-8D in XIP mode, for most flashes, with the exception of MX which seems to swap half-words. This commit adds a flash setting to allow parts to enable half-word swap when data is written, to fix this issue. By default, only endianness is fixed. Tested with both MX and ISSI parts on AE3, flash test and simple file write/read. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 12 ++++++++++-- ports/alif/ospi_flash.h | 1 + ports/alif/ospi_flash_settings.h | 5 ++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 7400a678b226a..f2f95a04cf241 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -414,8 +414,16 @@ static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src ospi_push(&self->cfg, addr); const uint32_t *src32 = (const uint32_t *)src; - for (; len; len -= 4) { - ospi_push(&self->cfg, __ROR(*src32++, 16)); + if (self->set->bswap16) { + // MX flashes swap 16-bit words when read in 8D-8D-8D. + for (; len; len -= 4) { + ospi_push(&self->cfg, __ROR(*src32++, 16)); + } + } else { + // For the rest of the flashes, we just correct the endianness. + for (; len; len -= 4) { + ospi_push(&self->cfg, __REV(*src32++)); + } } ospi_writel((&self->cfg), ser, self->cfg.ser); diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 0095ee2d4280b..f909f4a43c889 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -61,6 +61,7 @@ typedef struct _ospi_flash_settings_t { int (*flash_init)(struct _ospi_flash_t *); uint8_t octal_mode; bool rxds; + bool bswap16; uint8_t inst_len; uint8_t xip_data_len; uint16_t read_sr; diff --git a/ports/alif/ospi_flash_settings.h b/ports/alif/ospi_flash_settings.h index c8605275cdcc0..72403c26b67b5 100644 --- a/ports/alif/ospi_flash_settings.h +++ b/ports/alif/ospi_flash_settings.h @@ -34,6 +34,7 @@ .flash_init = ospi_flash_mx_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ .rxds = true, \ + .bswap16 = true, \ .inst_len = OSPI_INST_L_16bit, \ .xip_data_len = OSPI_DATA_L_16bit, \ .read_sr = 0x05fa, \ @@ -50,6 +51,7 @@ .flash_init = ospi_flash_issi_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ .rxds = false, \ + .bswap16 = false, \ .inst_len = OSPI_INST_L_8bit, \ .xip_data_len = OSPI_DATA_L_16bit, \ .read_sr = 0x05, \ @@ -65,7 +67,8 @@ #define OSPI_FLASH_SETTINGS_IS25 \ .flash_init = ospi_flash_issi_init, \ .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ - .rxds = true, \ + .rxds = false, \ + .bswap16 = false, \ .inst_len = OSPI_INST_L_8bit, \ .xip_data_len = OSPI_DATA_L_16bit, \ .read_sr = 0x05, \ From ff6ed730c534a809ea32648b0b30832b2993aaae Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 5 Feb 2025 19:57:40 +0100 Subject: [PATCH 494/546] alif/se_services: Use EUI extension for unique id. The right service call to get UID is SERVICES_system_get_eui_extension which returns an 8 bytes UID. Signed-off-by: iabdalkader --- ports/alif/modmachine.c | 2 +- ports/alif/se_services.c | 7 ++----- ports/alif/se_services.h | 2 +- ports/alif/usbd.c | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index 29a70305dd516..b77a207fe2cf7 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -38,7 +38,7 @@ static void mp_machine_idle(void) { } static mp_obj_t mp_machine_unique_id(void) { - uint8_t id[5]; + uint8_t id[8] = {0}; se_services_get_unique_id(id); return mp_obj_new_bytes(id, sizeof(id)); } diff --git a/ports/alif/se_services.c b/ports/alif/se_services.c index cbf66bfac6ccd..e79a5b97c8944 100644 --- a/ports/alif/se_services.c +++ b/ports/alif/se_services.c @@ -139,12 +139,9 @@ void se_services_dump_device_data(void) { printf("\n"); } -void se_services_get_unique_id(uint8_t id[5]) { +void se_services_get_unique_id(uint8_t id[8]) { uint32_t error_code; - SERVICES_version_data_t data; - SERVICES_system_get_device_data(se_services_handle, &data, &error_code); - // The MfgData has 5 bytes of valid data, at least on REV_B2. - memcpy(id, data.MfgData, 5); + SERVICES_system_get_eui_extension(se_services_handle, false, id, &error_code); } __attribute__((noreturn)) void se_services_reset_soc(void) { diff --git a/ports/alif/se_services.h b/ports/alif/se_services.h index b1079130c1635..87deb05592990 100644 --- a/ports/alif/se_services.h +++ b/ports/alif/se_services.h @@ -30,7 +30,7 @@ void se_services_init(void); void se_services_dump_device_data(void); -void se_services_get_unique_id(uint8_t id[5]); +void se_services_get_unique_id(uint8_t id[8]); __attribute__((noreturn)) void se_services_reset_soc(void); uint64_t se_services_rand64(void); uint32_t se_services_enable_clock(clock_enable_t clock, bool enable); diff --git a/ports/alif/usbd.c b/ports/alif/usbd.c index da16905d43cd9..8d841d188c333 100644 --- a/ports/alif/usbd.c +++ b/ports/alif/usbd.c @@ -33,7 +33,7 @@ #include "se_services.h" void mp_usbd_port_get_serial_number(char *serial_buf) { - uint8_t id[5]; + uint8_t id[8] = {0}; se_services_get_unique_id(id); MP_STATIC_ASSERT(sizeof(id) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); mp_usbd_hex_str(serial_buf, id, sizeof(id)); From 039df0c884ef0169257b3ab31255be8cd45caacf Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 3 Feb 2025 06:54:37 +0100 Subject: [PATCH 495/546] alif/modmachine: Implement proper low-power modes. Lightsleep current is around 23mA. Deepsleep current is sub 50uA. Signed-off-by: iabdalkader --- ports/alif/alif.mk | 1 + ports/alif/modmachine.c | 56 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 1581452195840..35f316cae0edd 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -182,6 +182,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/common/source/system_utils.c \ Device/common/source/tcm_partition.c \ Device/common/source/tgu_M55.c \ + Device/common/source/pm.c \ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ drivers/source/adc.c \ drivers/source/mhu_driver.c \ diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index b77a207fe2cf7..98d7bffbad932 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -28,6 +28,9 @@ // extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE. #include "se_services.h" +#include "tusb.h" + +extern void dcd_uninit(void); #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ @@ -70,15 +73,56 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { mp_raise_NotImplementedError(NULL); } -static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { - mp_int_t delay = -1; - if (n_args == 1) { - delay = mp_obj_get_int(args[0]); +#if MICROPY_HW_ENABLE_USBDEV +static void mp_machine_enable_usb(bool enable) { + if (enable) { + // Initialize TinyUSB and DCD. + tusb_init(); + } else { + // Disconnect USB device. + tud_disconnect(); + // Deinitialize TinyUSB. + tud_deinit(TUD_OPT_RHPORT); + // Deinitialize DCD (disables IRQs). + dcd_uninit(); } - mp_hal_delay_ms(delay); +} +#endif + +static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { + #if MICROPY_HW_ENABLE_USBDEV + mp_machine_enable_usb(false); + #endif + + #ifdef MICROPY_BOARD_ENTER_STANDBY + MICROPY_BOARD_ENTER_STANDBY(); + #endif + + // This enters the deepest possible CPU sleep state, without + // losing CPU state. CPU and subsystem power will remain on. + pm_core_enter_deep_sleep(); + + #ifdef MICROPY_BOARD_EXIT_STANDBY + MICROPY_BOARD_EXIT_STANDBY(); + #endif + + #if MICROPY_HW_ENABLE_USBDEV + mp_machine_enable_usb(true); + #endif } NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { - mp_machine_lightsleep(n_args, args); + #if MICROPY_HW_ENABLE_USBDEV + mp_machine_enable_usb(false); + #endif + + #ifdef MICROPY_BOARD_ENTER_STOP + MICROPY_BOARD_ENTER_STOP(); + #endif + + // If power is removed from the subsystem, the function does + // not return, and the CPU will reboot when/if the subsystem + // is next powered up. + pm_core_enter_deep_sleep_request_subsys_off(); mp_machine_reset(); } From 82bae652eb43ac3ce8226d08c1ce0047ffa76191 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 9 Jan 2025 19:58:25 +0100 Subject: [PATCH 496/546] alif: Add support for pin alternate function selection. Signed-off-by: iabdalkader --- ports/alif/alif.mk | 2 + ports/alif/mcu/ensemble_pin_alt.csv | 130 ++++++++++++++++++++++++++++ ports/alif/mcu/make-pins.py | 28 ++++-- ports/alif/mphalport.c | 50 +++++++++++ ports/alif/mphalport.h | 41 +++++++++ 5 files changed, 242 insertions(+), 9 deletions(-) create mode 100644 ports/alif/mcu/ensemble_pin_alt.csv diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 35f316cae0edd..b338e67c66c9a 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -51,6 +51,7 @@ INC += -Itinyusb_port GEN_PIN_MKPINS = mcu/make-pins.py GEN_PIN_PREFIX = mcu/pins_prefix.c GEN_PINS_BOARD_CSV = $(BOARD_DIR)/pins.csv +GEN_PINS_MCU_CSV = mcu/ensemble_pin_alt.csv GEN_PINS_SRC = $(BUILD)/pins_board.c GEN_PINS_HDR = $(HEADER_BUILD)/pins_board.h @@ -263,6 +264,7 @@ $(BUILD)/firmware.bin: $(BUILD)/firmware.elf $(BUILD)/%_board.c $(HEADER_BUILD)/%_board.h: $(BOARD_DIR)/%.csv $(GEN_PIN_MKPINS) $(GEN_PIN_PREFIX) | $(HEADER_BUILD) $(ECHO) "GEN $@" $(Q)$(PYTHON) $(GEN_PIN_MKPINS) \ + --af-csv $(GEN_PINS_MCU_CSV) \ --board-csv $(GEN_PINS_BOARD_CSV) \ --prefix $(GEN_PIN_PREFIX) \ --output-source $(GEN_PINS_SRC) \ diff --git a/ports/alif/mcu/ensemble_pin_alt.csv b/ports/alif/mcu/ensemble_pin_alt.csv new file mode 100644 index 0000000000000..10bfedc9a1d68 --- /dev/null +++ b/ports/alif/mcu/ensemble_pin_alt.csv @@ -0,0 +1,130 @@ +Pin,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7 +P0_0,,OSPI,UART,I3C,UT,LPCAM,CAM,ANA +P0_1,,OSPI,UART,I3C,UT,LPCAM,CAM,ANA +P0_2,,OSPI,UART,I2C,UT,LPCAM,CAM,ANA +P0_3,,OSPI,UART,I2C,UT,LPCAM,CAM,ANA +P0_4,,OSPI,UART,PDM,I2C,UT,,ANA +P0_5,,OSPI,UART,PDM,I2C,UT,,ANA +P0_6,,OSPI,UART,PDM,I2C,UT,,ANA +P0_7,,OSPI,UART,PDM,I2C,UT,CDC,ANA +P1_0,,UART,SPI,I2C,UT,LPCAM,ETH,ANA +P1_1,,UART,SPI,I2C,UT,LPCAM,ETH,ANA +P1_2,,UART,SPI,I3C,UT,LPCAM,ETH,ANA +P1_3,,UART,SPI,I3C,UT,LPCAM,ETH,ANA +P1_4,,OSPI,UART,SPI,UT,LPCAM,ETH,ANA +P1_5,,OSPI,UART,SPI,UT,LPCAM,ETH,ANA +P1_6,,OSPI,UART,I2S,UT,LPCAM,ETH,ANA +P1_7,,OSPI,UART,I2S,UT,LPCAM,ETH,ANA +P2_0,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA +P2_1,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA +P2_2,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA +P2_3,,OSPI,UART,LPPDM,UT,LPCAM,CDC,ANA +P2_4,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA +P2_5,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA +P2_6,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA +P2_7,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA +P3_0,,OSPI,UART,PDM,I2S,QEC,LPCAM,CAM +P3_1,,OSPI,UART,PDM,I2S,QEC,LPCAM,CAM +P3_2,,OSPI,PDM,I2S,I3C,QEC,LPCAM,CAM +P3_3,,OSPI,PDM,I2S,I3C,QEC,LPCAM,CAM +P3_4,,OSPI,UART,LPPDM,I2S,I2C,QEC,CAM +P3_5,,OSPI,UART,LPPDM,SPI,I2C,QEC,CAM +P3_6,,HFXO,LPUART,LPPDM,SPI,I2C,QEC,CAM +P3_7,,JTAG,LPUART,LPPDM,SPI,I2C,QEC,CAM +P4_0,,JTAG,,I2S,SPI,QEC,CDC,CAM +P4_1,,JTAG,I2S,SPI,QEC,SD,CDC,CAM +P4_2,,JTAG,,I2S,SPI,QEC,SD,CAM +P4_3,,JTAG,,I2S,SPI,QEC,SD,CAM +P4_4,,JTAG,I2S,SPI,FAULT,,, +P4_5,,JTAG,SPI,FAULT,,,, +P4_6,,JTAG,SPI,FAULT,,,, +P4_7,,JTAG,SPI,FAULT,,,, +P5_0,,OSPI,UART,PDM,SPI,I2C,UT,SD +P5_1,,OSPI,UART,PDM,SPI,I2C,UT,SD +P5_2,,OSPI,UART,PDM,SPI,LPI2C,UT,SD +P5_3,,OSPI,UART,SPI,LPI2C,UT,SD,CDC +P5_4,,OSPI,UART,PDM,SPI,UT,SD,CDC +P5_5,,OSPI,UART,PDM,UT,SD,ETH,CDC +# P5_6 doesn't really have OSPI on AF1 but it's needed for P10_7 to be in OSPI mode +P5_6,,OSPI,UART,I2C,UT,SD,ETH,CDC +P5_7,,OSPI,UART,I2C,UT,SD,ETH, +P6_0,,OSPI,UART,PDM,UT,SD,ETH, +P6_1,,OSPI,UART,PDM,UT,SD,ETH, +P6_2,,OSPI,UART,,PDM,UT,SD,ETH +P6_3,,OSPI,UART,,PDM,UT,SD,ETH +P6_4,,OSPI,UART,,SPI,UT,SD,ETH +P6_5,,OSPI,UART,,SPI,UT,SD,ETH +P6_6,,OSPI,UART,,SPI,UT,SD,ETH +P6_7,,OSPI,UART,PDM,SPI,UT,SD,ETH +P7_0,,,CMP,SPI,I2C,UT,SD, +P7_1,,,CMP,SPI,I2C,UT,SD, +P7_2,,,UART,CMP,SPI,I2C,UT,SD +P7_3,,,UART,CMP,SPI,I2C,UT, +P7_4,,,LPUART,LPPDM,LPSPI,LPI2C,UT, +P7_5,,,LPUART,,LPPDM,LPSPI,LPI2C,UT +P7_6,,,LPUART,,LPPDM,LPSPI,I3C,UT +P7_7,,,LPUART,,LPPDM,LPSPI,I3C,UT +P8_0,,OSPI,AUDIO,FAULT,LPCAM,SD,CDC,CAM +P8_1,,I2S,FAULT,LPCAM,SD,CDC,CAM, +P8_2,,I2S,SPI,FAULT,LPCAM,SD,CDC,CAM +P8_3,,I2S,SPI,FAULT,LPCAM,SD,CDC,CAM +P8_4,,I2S,SPI,QEC,LPCAM,SD,CDC,CAM +P8_5,,,SPI,QEC,LPCAM,SD,CDC,CAM +P8_6,,,I2S,QEC,LPCAM,SD,CDC,CAM +P8_7,,,I2S,QEC,LPCAM,SD,CDC,CAM +P9_0,,,I2S,QEC,SD,CDC,CAM, +P9_1,,LPUART,I2S,QEC,SD,CDC,CAM, +P9_2,,LPUART,I2S,SPI,QEC,SD,CDC,CAM +P9_3,,HFXO,UART,I2S,SPI,QEC,CDC,CAM +P9_4,,UART,I2S,SPI,I2C,QEC,CDC,CAM +P9_5,,OSPI,I2S,SPI,I2C,QEC,CDC,CAM +P9_6,,OSPI,AUDIO,SPI,I2C,QEC,CDC,CAM +P9_7,,OSPI,UART,SPI,I2C,QEC,CDC,CAM +P10_0,,OSPI,UART,SPI,UT,LPCAM,CDC,CAM +P10_1,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM +P10_2,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM +P10_3,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM +P10_4,,OSPI,,LPI2S,I2C,UT,ETH,CDC +P10_5,,UART,I2S,SPI,I2C,UT,ETH,CDC +P10_6,,UART,I2S,SPI,I2C,UT,ETH,CDC +P10_7,,UART,I2S,SPI,I2C,UT,CDC,OSPI +P11_0,,OSPI,UART,I2S,SPI,UT,ETH,CDC +P11_1,,OSPI,UART,SPI,UT,ETH,CDC, +P11_2,,OSPI,UART,LPPDM,SPI,UT,ETH,CDC +P11_3,,OSPI,UART,LPPDM,SPI,UT,ETH,CDC +P11_4,,OSPI,UART,PDM,LPSPI,UT,ETH,CDC +P11_5,,OSPI,UART,PDM,LPSPI,UT,ETH,CDC +P11_6,,OSPI,UART,LPPDM,LPSPI,UT,ETH,CDC +P11_7,,OSPI,UART,LPPDM,LPSPI,UT,ETH,CDC +P12_0,,OSPI,AUDIO,I2S,UT,CDC,, +P12_1,,OSPI,UART,I2S,UT,CDC,, +P12_2,,OSPI,UART,I2S,UT,CDC,, +P12_3,,OSPI,UART,I2S,UT,CDC,, +P12_4,,OSPI,SPI,UT,,CDC,, +P12_5,,,SPI,UT,,CDC,, +P12_6,,,SPI,UT,,CDC,, +P12_7,,OSPI,,SPI,UT,CDC,, +P13_0,,OSPI,,SPI,QEC,SD,CDC, +P13_1,,OSPI,SPI,QEC,SD,CDC,, +P13_2,,OSPI,SPI,QEC,SD,CDC,, +P13_3,,OSPI,SPI,QEC,SD,CDC,, +P13_4,,OSPI,LPI2S,QEC,SD,CDC,, +P13_5,,OSPI,LPI2S,QEC,SD,CDC,, +P13_6,,OSPI,LPI2S,QEC,SD,CDC,, +P13_7,,OSPI,LPI2S,QEC,SD,CDC,, +P14_0,,OSPI,UART,QEC,SD,,, +P14_1,,OSPI,UART,,QEC,SD,, +P14_2,,OSPI,UART,,QEC,SD,, +P14_3,,OSPI,UART,,QEC,,, +P14_4,,CMP,SPI,FAULT,,,, +P14_5,,CMP,SPI,FAULT,,,, +P14_6,,CMP,SPI,FAULT,,,, +P14_7,,CMP,SPI,FAULT,,,, +P15_0,,LPTMR,,,,,, +P15_1,,LPTMR,,,,,, +P15_2,,LPTMR,,,,,, +P15_3,,LPTMR,,,,,, +P15_4,,,,,,,, +P15_5,,,,,,,, +P15_6,,,,,,,, +P15_7,,,,,,,, diff --git a/ports/alif/mcu/make-pins.py b/ports/alif/mcu/make-pins.py index d4fbf5d8b8d9a..fb000082642bc 100755 --- a/ports/alif/mcu/make-pins.py +++ b/ports/alif/mcu/make-pins.py @@ -34,26 +34,36 @@ class AlifPin(boardgen.Pin): + def __init__(self, cpu_pin_name): + super().__init__(cpu_pin_name) + self._afs = ["MP_HAL_PIN_ALT_NONE"] * 8 + + # Called for each AF defined in the csv file for this pin. + def add_af(self, af_idx, af_name, af): + self._afs[af_idx] = f"MP_HAL_PIN_ALT_{af}" + # Emit the struct which contains the pin instance. def definition(self): port, pin = self.name()[1:].split("_") adc12_periph, adc12_channel = ADC12_ANA_MAP.get(self.name(), (3, 7)) base = "LPGPIO_BASE" if port == "15" else "GPIO{}_BASE".format(port) return ( - "{{ " - ".base = {{ .type = &machine_pin_type }}, " - ".gpio = (GPIO_Type *){base}, " - ".port = PORT_{port}, " - ".pin = PIN_{pin}, " - ".adc12_periph = {adc12_periph}, " - ".adc12_channel = {adc12_channel}, " - ".name = MP_QSTR_P{port}_{pin} " + "{{\n" + " .name = MP_QSTR_P{port}_{pin},\n" + " .base = {{ .type = &machine_pin_type }},\n" + " .gpio = (GPIO_Type *){base},\n" + " .port = PORT_{port},\n" + " .pin = PIN_{pin},\n" + " .adc12_periph = {adc12_periph},\n" + " .adc12_channel = {adc12_channel},\n" + " .alt = {{{alt}}},\n" "}}".format( port=port, pin=pin, base=base, adc12_periph=adc12_periph, adc12_channel=adc12_channel, + alt=", ".join([f"{af}" for af in self._afs]), ) ) @@ -76,7 +86,7 @@ def validate_cpu_pin_name(cpu_pin_name): class AlifPinGenerator(boardgen.PinGenerator): def __init__(self): # Use custom pin type above. - super().__init__(pin_type=AlifPin) + super().__init__(pin_type=AlifPin, enable_af=True) # Pre-define the pins (i.e. don't require them to be listed in pins.csv). for i in range(NUM_PORTS): diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 5e3dad141908e..1a6136c31abeb 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -169,6 +169,56 @@ uint64_t mp_hal_time_ns(void) { return 0; } +void mp_hal_pin_config(const machine_pin_obj_t *pin, uint32_t mode, + uint32_t pull, uint32_t speed, uint32_t drive, uint32_t alt, bool ren) { + uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0; + uint8_t pad_ctrl = drive | speed | (ren ? PADCTRL_READ_ENABLE : 0); + + // Configure pull-up or pull-down. + if (pull & MP_HAL_PIN_PULL_UP) { + pad_ctrl |= PADCTRL_DRIVER_DISABLED_PULL_UP; + } + + if (pull & MP_HAL_PIN_PULL_DOWN) { + pad_ctrl |= PADCTRL_DRIVER_DISABLED_PULL_DOWN; + } + + // Configure open-drain mode. + if (mode == MP_HAL_PIN_MODE_OPEN_DRAIN) { + pad_ctrl |= PADCTRL_DRIVER_OPEN_DRAIN; + } + + // For ALT mode, find alternate function. + if (mode == MP_HAL_PIN_MODE_ALT) { + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(pin->alt); i++) { + if (alt == pin->alt[i]) { + alt_func = i; + break; + } + } + if (alt_func == PINMUX_ALTERNATE_FUNCTION_0) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin af: %d"), alt); + } + } + + // Set pad config. + pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl); + + // For INPUT/OUTPUT/OD modes, set the GPIO direction. + switch (mode) { + case MP_HAL_PIN_MODE_INPUT: + gpio_set_direction_input(pin->gpio, pin->pin); + break; + case MP_HAL_PIN_MODE_OUTPUT: + case MP_HAL_PIN_MODE_OPEN_DRAIN: + gpio_set_direction_output(pin->gpio, pin->pin); + break; + default: + break; + } +} + + void system_tick_schedule_callback(void) { pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); } diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 7b054b671bcd6..3b23cb6a2c514 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -74,12 +74,49 @@ extern ringbuf_t stdin_ringbuf; #define MP_HAL_PIN_MODE_INPUT (0) #define MP_HAL_PIN_MODE_OUTPUT (1) #define MP_HAL_PIN_MODE_OPEN_DRAIN (2) +#define MP_HAL_PIN_MODE_ALT (3) #define MP_HAL_PIN_PULL_NONE (0) #define MP_HAL_PIN_PULL_UP (1) #define MP_HAL_PIN_PULL_DOWN (2) +#define MP_HAL_PIN_DRIVE_2MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_2MA) +#define MP_HAL_PIN_DRIVE_4MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_4MA) +#define MP_HAL_PIN_DRIVE_8MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_8MA) +#define MP_HAL_PIN_DRIVE_12MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA) +#define MP_HAL_PIN_SPEED_LOW (0) +#define MP_HAL_PIN_SPEED_HIGH (PADCTRL_SLEW_RATE_FAST) #define mp_hal_pin_obj_t const machine_pin_obj_t * +enum { + MP_HAL_PIN_ALT_NONE = 0, + MP_HAL_PIN_ALT_ANA, + MP_HAL_PIN_ALT_AUDIO, + MP_HAL_PIN_ALT_CAM, + MP_HAL_PIN_ALT_CDC, + MP_HAL_PIN_ALT_CMP, + MP_HAL_PIN_ALT_ETH, + MP_HAL_PIN_ALT_FAULT, + MP_HAL_PIN_ALT_HFXO, + MP_HAL_PIN_ALT_I2C, + MP_HAL_PIN_ALT_I2S, + MP_HAL_PIN_ALT_I3C, + MP_HAL_PIN_ALT_JTAG, + MP_HAL_PIN_ALT_LPCAM, + MP_HAL_PIN_ALT_LPI2C, + MP_HAL_PIN_ALT_LPI2S, + MP_HAL_PIN_ALT_LPPDM, + MP_HAL_PIN_ALT_LPSPI, + MP_HAL_PIN_ALT_LPTMR, + MP_HAL_PIN_ALT_LPUART, + MP_HAL_PIN_ALT_OSPI, + MP_HAL_PIN_ALT_PDM, + MP_HAL_PIN_ALT_QEC, + MP_HAL_PIN_ALT_SD, + MP_HAL_PIN_ALT_SPI, + MP_HAL_PIN_ALT_UART, + MP_HAL_PIN_ALT_UT, +}; + typedef struct _machine_pin_obj_t { mp_obj_base_t base; GPIO_Type *gpio; @@ -88,6 +125,7 @@ typedef struct _machine_pin_obj_t { uint8_t adc12_periph : 2; uint8_t adc12_channel : 3; qstr name; + const uint8_t alt[8]; } machine_pin_obj_t; mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in); @@ -149,5 +187,8 @@ static inline void mp_hal_wake_main_task_from_isr(void) { // Defined for tinyusb support, nothing needs to be done here. } +void mp_hal_pin_config(const machine_pin_obj_t *pin, uint32_t mode, + uint32_t pull, uint32_t speed, uint32_t drive, uint32_t alt, bool ren); + // Include all the pin definitions. #include "genhdr/pins_board.h" From 9073270c2e0daa1a3539aa14c0feadadd6c6b08d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 2 Jan 2025 06:29:05 +0100 Subject: [PATCH 497/546] alif/machine_i2c: Add machine.I2C peripheral support. Signed-off-by: iabdalkader --- ports/alif/alif.mk | 2 + ports/alif/machine_i2c.c | 301 ++++++++++++++++++++++++++++++++++++++ ports/alif/mpconfigport.h | 2 + 3 files changed, 305 insertions(+) create mode 100644 ports/alif/machine_i2c.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index b338e67c66c9a..5cfd37fc4b6e9 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -117,6 +117,7 @@ SRC_C = \ alif_flash.c \ fatfs_port.c \ machine_pin.c \ + machine_i2c.c \ main.c \ modalif.c \ mphalport.c \ @@ -186,6 +187,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/common/source/pm.c \ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ drivers/source/adc.c \ + drivers/source/i2c.c \ drivers/source/mhu_driver.c \ drivers/source/mhu_receiver.c \ drivers/source/mhu_sender.c \ diff --git a/ports/alif/machine_i2c.c b/ports/alif/machine_i2c.c new file mode 100644 index 0000000000000..f117b93c2b1e8 --- /dev/null +++ b/ports/alif/machine_i2c.c @@ -0,0 +1,301 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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/mperrno.h" +#include "extmod/modmachine.h" + +#if MICROPY_HW_ENABLE_HW_I2C +#include "i2c.h" + +#define I2C_DEFAULT_FREQ (400000) +#define I2C_DEFAULT_TIMEOUT (50000) + +#define I2C_DAT_INDEX (0) +#define I2C_TX_FIFO_LEN (I2C_FIFO_DEPTH / 2) +#define I2C_RX_FIFO_LEN (I2C_FIFO_DEPTH / 2) + +#define I2C_SPEED(freq) \ + ((freq) <= 100000 ? I2C_SPEED_STANDARD : \ + ((freq) <= 400000 ? I2C_SPEED_FAST : \ + I2C_SPEED_FASTPLUS)) + +#define I2C_IC_CON_SPEED(freq) \ + ((freq) <= 100000 ? I2C_IC_CON_SPEED_STANDARD : \ + ((freq) <= 400000 ? I2C_IC_CON_SPEED_FAST : \ + I2C_IC_CON_SPEED_HIGH)) +#define I2C_IC_CON_MASTER_TX_EMPTY_CTRL (1 << 8) + +#define I2C_IC_STATUS_RFNE I2C_IC_STATUS_RECEIVE_FIFO_NOT_EMPTY +#define I2C_IC_STATUS_TFNF I2C_IC_STATUS_TRANSMIT_FIFO_NOT_FULL +#define I2C_STAT_ERRORS (I2C_IC_INTR_STAT_TX_ABRT | I2C_IC_INTR_STAT_TX_OVER | \ + I2C_IC_INTR_STAT_RX_OVER | I2C_IC_INTR_STAT_RX_UNDER) + +#define debug_printf(...) // mp_printf(&mp_plat_print, "i2c.c: " __VA_ARGS__) +#define I2C_CHECK_ERRORS(base) \ + if (base->I2C_RAW_INTR_STAT & I2C_STAT_ERRORS) { \ + uint32_t status = base->I2C_RAW_INTR_STAT; \ + debug_printf("status: 0x%lx raw_int: 0x%lx abort: 0x%lx line: %d\n", \ + base->I2C_STATUS, status, base->I2C_TX_ABRT_SOURCE, __LINE__); \ + (void)status; \ + (void)base->I2C_CLR_TX_ABRT; \ + (void)base->I2C_CLR_ACTIVITY; \ + return -MP_EIO; \ + } + +typedef struct _machine_i2c_obj_t { + mp_obj_base_t base; + uint32_t i2c_id; + I2C_Type *i2c; + mp_hal_pin_obj_t scl; + mp_hal_pin_obj_t sda; + uint32_t freq; + uint32_t timeout; +} machine_i2c_obj_t; + +static machine_i2c_obj_t machine_i2c_obj[] = { + #if defined(MICROPY_HW_I2C0_SCL) + [0] = {{&machine_i2c_type}, 0, (I2C_Type *)I2C0_BASE, MICROPY_HW_I2C0_SCL, MICROPY_HW_I2C0_SDA}, + #endif + #if defined(MICROPY_HW_I2C1_SCL) + [1] = {{&machine_i2c_type}, 1, (I2C_Type *)I2C1_BASE, MICROPY_HW_I2C1_SCL, MICROPY_HW_I2C1_SDA}, + #endif + #if defined(MICROPY_HW_I2C2_SCL) + [2] = {{&machine_i2c_type}, 2, (I2C_Type *)I2C2_BASE, MICROPY_HW_I2C2_SCL, MICROPY_HW_I2C2_SDA}, + #endif + #if defined(MICROPY_HW_I2C3_SCL) + [3] = {{&machine_i2c_type}, 3, (I2C_Type *)I2C3_BASE, MICROPY_HW_I2C3_SCL, MICROPY_HW_I2C3_SDA}, + #endif +}; + +static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "I2C(%u, freq=%u, scl=%q, sda=%q, timeout=%u)", + self->i2c_id, self->freq, self->scl->name, self->sda->name, self->timeout); +} + +mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_freq, ARG_scl, ARG_sda, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_freq, MP_ARG_INT, {.u_int = I2C_DEFAULT_FREQ} }, + { MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2C_DEFAULT_TIMEOUT} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get I2C bus. + int i2c_id = mp_obj_get_int(args[ARG_id].u_obj); + if (i2c_id < 0 || i2c_id >= MP_ARRAY_SIZE(machine_i2c_obj) || !machine_i2c_obj[i2c_id].i2c) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id); + } + + // Get static peripheral object. + machine_i2c_obj_t *self = &machine_i2c_obj[i2c_id]; + + // Set args + self->freq = args[ARG_freq].u_int; + self->timeout = args[ARG_timeout].u_int; + + // here we would check the scl/sda pins and configure them, but it's not implemented + if (args[ARG_scl].u_obj != mp_const_none || args[ARG_sda].u_obj != mp_const_none) { + mp_raise_ValueError(MP_ERROR_TEXT("explicit choice of scl/sda is not implemented")); + } + + // Disable I2C controller. + i2c_disable(self->i2c); + + // Configure I2C pins. + mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + + // Initialize I2C controller. + self->i2c->I2C_CON = I2C_IC_CON_ENABLE_MASTER_MODE | + I2C_IC_CON_MASTER_RESTART_EN | + I2C_IC_CON_MASTER_TX_EMPTY_CTRL | + I2C_IC_CON_SPEED(self->freq); + + // Configure FIFO threshold. + self->i2c->I2C_TX_TL = I2C_TX_FIFO_LEN; + self->i2c->I2C_RX_TL = I2C_RX_FIFO_LEN; + + // Configure clock. + i2c_master_set_clock(self->i2c, GetSystemAPBClock() / 1000, I2C_SPEED(self->freq)); + + // Enable I2C controller. + i2c_clear_all_interrupt(self->i2c); + i2c_enable(self->i2c); + + return MP_OBJ_FROM_PTR(self); +} + +static int machine_i2c_poll_flags(I2C_Type *base, uint32_t flags, uint32_t timeout_us) { + mp_uint_t tick_start = mp_hal_ticks_us(); + while (!(base->I2C_STATUS & flags)) { + I2C_CHECK_ERRORS(base); + if ((mp_hal_ticks_us() - tick_start) >= timeout_us) { + return -MP_ETIMEDOUT; + } + // Can't delay or handle pending events here otherwise we risk + // the FIFO getting empty, which will generate a STOP condition. + } + return 0; +} + +static int machine_i2c_write(machine_i2c_obj_t *self, uint8_t *buf, size_t tx_size) { + mp_uint_t tick_start = mp_hal_ticks_us(); + for (size_t tx_idx = 0; tx_idx < tx_size;) { + // Write data to FIFO + if (self->i2c->I2C_STATUS & I2C_IC_STATUS_TFNF) { + self->i2c->I2C_DATA_CMD = (uint16_t)buf[tx_idx++]; + I2C_CHECK_ERRORS(self->i2c); + tick_start = mp_hal_ticks_us(); + } + + // Check for timeout + if ((mp_hal_ticks_us() - tick_start) >= self->timeout) { + return -MP_ETIMEDOUT; + } + } + return 0; +} + +static int machine_i2c_read(machine_i2c_obj_t *self, uint8_t *buf, size_t rx_size) { + mp_uint_t tick_start = mp_hal_ticks_us(); + for (size_t tx_idx = 0, rx_idx = 0; rx_idx < rx_size;) { + // Write command to FIFO + if (tx_idx < rx_size && (self->i2c->I2C_STATUS & I2C_IC_STATUS_TFNF)) { + self->i2c->I2C_DATA_CMD = I2C_IC_DATA_CMD_READ_REQ; + I2C_CHECK_ERRORS(self->i2c); + ++tx_idx; + } + + // Read data from FIFO + while (rx_idx < rx_size && (self->i2c->I2C_STATUS & I2C_IC_STATUS_RFNE)) { + buf[rx_idx++] = self->i2c->I2C_DATA_CMD & 0xFF; + tick_start = mp_hal_ticks_us(); + } + + // Check for timeout + if ((mp_hal_ticks_us() - tick_start) >= self->timeout) { + return -MP_ETIMEDOUT; + } + } + return 0; +} + +int machine_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) { + int ret = 0; + uint32_t bytes = 0; + machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in; + + // The DesignWare I2C IP on AE3 is configured such that it auto-generates a STOP + // condition when the TX FIFO gets empty. In other words, the code can't have any + // control over STOP condition generation. The only fix for this would be to buffer + // complete read/write sequences and send them out when the STOP flag is set. + if (!(flags & MP_MACHINE_I2C_FLAG_STOP)) { + mp_raise_ValueError(MP_ERROR_TEXT("nostop flag is not supported")); + } + + i2c_clear_all_interrupt(self->i2c); + i2c_set_target_addr(self->i2c, addr, I2C_7BIT_ADDRESS, 0); + + // Workaround issue with hardware I2C not accepting zero-length writes. + if (!bufs->len) { + mp_machine_i2c_buf_t bufs = { 0 }; + + mp_machine_soft_i2c_obj_t soft_i2c = { + .base = { &mp_machine_soft_i2c_type }, + .scl = self->scl, + .sda = self->sda, + .us_timeout = self->timeout, + .us_delay = 500000 / self->freq + 1, + }; + + // Switch pins to GPIO/OD. + mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_NONE, true); + mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_NONE, true); + + // Perform the transfer. + ret = mp_machine_soft_i2c_transfer(&soft_i2c.base, addr, 1, &bufs, flags); + + // Re-configure I2C pins. + mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + + return ret; + } + + for (size_t i = 0; i < n; i++) { + mp_machine_i2c_buf_t *buf = &bufs[i]; + if (i == 0 && (flags & MP_MACHINE_I2C_FLAG_WRITE1)) { + ret = machine_i2c_write(self, buf->buf, buf->len); + } else if (flags & MP_MACHINE_I2C_FLAG_READ) { + ret = machine_i2c_read(self, buf->buf, buf->len); + } else if (bufs->len != 0) { + ret = machine_i2c_write(self, buf->buf, buf->len); + } + if (ret < 0) { + return ret; + } + bytes += bufs->len; + } + + // Wait for TX FIFO empty + ret = machine_i2c_poll_flags(self->i2c, I2C_IC_STATUS_TFE, self->timeout); + if (ret < 0) { + return ret; + } + + return bytes; +} + +static const mp_machine_i2c_p_t machine_i2c_p = { + .transfer_supports_write1 = true, + .transfer = machine_i2c_transfer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_i2c_type, + MP_QSTR_I2C, + MP_TYPE_FLAG_NONE, + make_new, machine_i2c_make_new, + print, machine_i2c_print, + protocol, &machine_i2c_p, + locals_dict, &mp_machine_i2c_locals_dict + ); +#endif // MICROPY_HW_ENABLE_HW_I2C diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index ae3ad7a30d352..b23886637e308 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -130,6 +130,8 @@ #define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/alif/machine_adc.c" #define MICROPY_PY_MACHINE_DHT_READINTO (1) #define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_I2C (MICROPY_HW_ENABLE_HW_I2C) +#define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_TIMER (1) From 280e6e2a40dbddb3aec5424c1ca1bf9dea876dd0 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 19 Jan 2025 15:59:25 +0100 Subject: [PATCH 498/546] alif/machine_spi: Add machine.SPI peripheral support. Signed-off-by: iabdalkader Signed-off-by: Damien George --- ports/alif/alif.mk | 3 + ports/alif/machine_spi.c | 312 ++++++++++++++++++++++++++++++++++++++ ports/alif/mpconfigport.h | 1 + 3 files changed, 316 insertions(+) create mode 100644 ports/alif/machine_spi.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 5cfd37fc4b6e9..63003b58a0937 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -118,6 +118,7 @@ SRC_C = \ fatfs_port.c \ machine_pin.c \ machine_i2c.c \ + machine_spi.c \ main.c \ modalif.c \ mphalport.c \ @@ -188,6 +189,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ drivers/source/adc.c \ drivers/source/i2c.c \ + drivers/source/spi.c \ drivers/source/mhu_driver.c \ drivers/source/mhu_receiver.c \ drivers/source/mhu_sender.c \ @@ -206,6 +208,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ ) $(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 +$(BUILD)/$(ALIF_DFP_REL_TOP)/drivers/source/spi.o: CFLAGS += -Wno-maybe-uninitialized $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_system.o: CFLAGS += -Wno-maybe-uninitialized diff --git a/ports/alif/machine_spi.c b/ports/alif/machine_spi.c new file mode 100644 index 0000000000000..d13c6368f2515 --- /dev/null +++ b/ports/alif/machine_spi.c @@ -0,0 +1,312 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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/mperrno.h" +#include "extmod/modmachine.h" + +#if MICROPY_PY_MACHINE_SPI +#include "clk.h" +#include "spi.h" +#include "sys_ctrl_spi.h" + +typedef struct _machine_spi_obj_t { + mp_obj_base_t base; + uint8_t id; + SPI_Type *inst; + bool is_lp; +} machine_spi_obj_t; + +static machine_spi_obj_t machine_spi_obj[] = { + #if defined(MICROPY_HW_SPI0_SCK) + [0] = {{&machine_spi_type}, 0, (SPI_Type *)SPI0_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI1_SCK) + [1] = {{&machine_spi_type}, 1, (SPI_Type *)SPI1_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI2_SCK) + [2] = {{&machine_spi_type}, 2, (SPI_Type *)SPI2_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI3_SCK) + [3] = {{&machine_spi_type}, 3, (SPI_Type *)SPI3_BASE, false}, + #endif + #if defined(MICROPY_HW_LPSPI0_SCK) + [4] = {{&machine_spi_type}, 4, (SPI_Type *)LPSPI0_BASE, true}, + #endif + +}; + +static inline uint32_t spi_get_clk(machine_spi_obj_t *spi) { + return spi->is_lp ? GetSystemCoreClock() : GetSystemAHBClock(); +} + +static void spi_init(machine_spi_obj_t *spi, uint32_t baudrate, + uint32_t polarity, uint32_t phase, uint32_t bits, uint32_t firstbit) { + const machine_pin_obj_t *pins[4] = { NULL, NULL, NULL, NULL }; + switch (spi->id) { + #if defined(MICROPY_HW_SPI0_SCK) + case 0: + pins[0] = MICROPY_HW_SPI0_SCK; + pins[1] = MICROPY_HW_SPI0_MISO; + pins[2] = MICROPY_HW_SPI0_MOSI; + #if defined(MICROPY_HW_SPI0_NSS) + pins[3] = MICROPY_HW_SPI0_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI1_SCK) + case 1: + pins[0] = MICROPY_HW_SPI1_SCK; + pins[1] = MICROPY_HW_SPI1_MISO; + pins[2] = MICROPY_HW_SPI1_MOSI; + #if defined(MICROPY_HW_SPI1_NSS) + pins[3] = MICROPY_HW_SPI1_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI2_SCK) + case 2: + pins[0] = MICROPY_HW_SPI2_SCK; + pins[1] = MICROPY_HW_SPI2_MISO; + pins[2] = MICROPY_HW_SPI2_MOSI; + #if defined(MICROPY_HW_SPI2_NSS) + pins[3] = MICROPY_HW_SPI2_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI3_SCK) + case 3: + pins[0] = MICROPY_HW_SPI3_SCK; + pins[1] = MICROPY_HW_SPI3_MISO; + pins[2] = MICROPY_HW_SPI3_MOSI; + #if defined(MICROPY_HW_SPI3_NSS) + pins[3] = MICROPY_HW_SPI3_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_LPSPI0_SCK) + case 4: // LPSPI0 + pins[0] = MICROPY_HW_LPSPI0_SCK; + pins[1] = MICROPY_HW_LPSPI0_MISO; + pins[2] = MICROPY_HW_LPSPI0_MOSI; + #if defined(MICROPY_HW_LPSPI0_NSS) + pins[3] = MICROPY_HW_LPSPI0_NSS; + #endif + break; + #endif + default: + return; + } + + // Disable SPI. + spi_disable(spi->inst); + + // Enable clocks. + if (spi->is_lp) { + enable_lpspi_clk(); + } + + // Configure SPI pins. + for (size_t i = 0; i < MP_ARRAY_SIZE(pins) && pins[i]; i++) { + mp_hal_pin_config(pins[i], MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + } + + // Disable all interrupts. + spi_mask_interrupts(spi->inst); + + // Configure baudrate clock + spi_set_bus_speed(spi->inst, baudrate, spi_get_clk(spi)); + + // Configure FIFOs + spi_set_tx_threshold(spi->inst, 0); + spi_set_rx_threshold(spi->inst, 0); + if (!spi->is_lp) { + spi_set_rx_sample_delay(spi->inst, 0); + spi_set_tx_fifo_start_level(spi->inst, 0); + } + + // Configure SPI bus mode. + uint32_t spi_mode = (polarity << 1) | phase; + if (!spi->is_lp) { + spi_set_mode(spi->inst, spi_mode); + } else { + lpspi_set_mode(spi->inst, spi_mode); + } + + // Configure SPI bus protocol. + uint32_t spi_proto = SPI_PROTO_SPI; + if (!spi->is_lp) { + spi_set_protocol(spi->inst, spi_proto); + } else { + lpspi_set_protocol(spi->inst, spi_proto); + } + + // Configure SPI transfer mode. + if (!spi->is_lp) { + spi_mode_master(spi->inst); + } + + // Configure frame size. + if (!spi->is_lp) { + spi_set_dfs(spi->inst, bits); + } else { + lpspi_set_dfs(spi->inst, bits); + } + + // Configure slave select pin + spi_control_ss(spi->inst, 0, true); + if (!spi->is_lp) { + spi_set_sste(spi->inst, false); + } else { + lpspi_set_sste(spi->inst, false); + } + + // Clear IRQs. + (void)spi->inst->SPI_ICR; + + // Enable SPI. + spi_enable(spi->inst); +} + +static void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t baudrate = spi_get_bus_speed(self->inst, spi_get_clk(self)); + mp_printf(print, "SPI(%u, baudrate=%u, lp=%u)", self->id, baudrate, self->is_lp); +} + +mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { 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} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get spi bus. + int spi_id = mp_obj_get_int(args[ARG_id].u_obj); + if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(machine_spi_obj) || !machine_spi_obj[spi_id].inst) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); + } + + // Get static peripheral object. + machine_spi_obj_t *self = &machine_spi_obj[spi_id]; + + // here we would check the sck/mosi/miso pins and configure them, but it's not implemented + 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) { + mp_raise_ValueError(MP_ERROR_TEXT("explicit choice of sck/mosi/miso is not implemented")); + } + + // Initialize and configure SPI. + spi_init(self, args[ARG_baudrate].u_int, args[ARG_polarity].u_int, + args[ARG_phase].u_int, args[ARG_bits].u_int, args[ARG_firstbit].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +static void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + // Parse the arguments. + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Initialize and configure SPI. + spi_init(self, args[ARG_baudrate].u_int, args[ARG_polarity].u_int, + args[ARG_phase].u_int, args[ARG_bits].u_int, args[ARG_firstbit].u_int); +} + +static void machine_spi_deinit(mp_obj_base_t *self_in) { + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + // Disable all interrupts. + spi_mask_interrupts(self->inst); + // Disable SS pin. + spi_control_ss(self->inst, 0, 0); + // Disable SPI. + spi_disable(self->inst); + // Deinitialize GPIOs and clocks. + if (self->is_lp) { + disable_lpspi_clk(); + } +} + +static void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + spi_transfer_t spi_xfer = { + .tx_buff = src, + .tx_total_cnt = len, + .rx_buff = dest, + .rx_total_cnt = len, + .tx_default_val = 0xFF, + .tx_default_enable = true, + .mode = SPI_TMOD_TX_AND_RX, + }; + // TODO redo transfer_blocking to timeout and poll events. + if (!self->is_lp) { + spi_transfer_blocking(self->inst, &spi_xfer); + } else { + lpspi_transfer_blocking(self->inst, &spi_xfer); + } +} + +static const mp_machine_spi_p_t machine_spi_p = { + .init = machine_spi_init, + .deinit = machine_spi_deinit, + .transfer = machine_spi_transfer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_spi_type, + MP_QSTR_SPI, + MP_TYPE_FLAG_NONE, + make_new, machine_spi_make_new, + print, machine_spi_print, + protocol, &machine_spi_p, + locals_dict, &mp_machine_spi_locals_dict + ); + +#endif // MICROPY_PY_MACHINE_SPI diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index b23886637e308..83f808b131f48 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -133,6 +133,7 @@ #define MICROPY_PY_MACHINE_I2C (MICROPY_HW_ENABLE_HW_I2C) #define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) +#define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_TIMER (1) #define MICROPY_VFS (1) From ec92bcfeff787a4ceb25c3b9c1703043a7e3052d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 31 Jan 2025 14:15:06 +0100 Subject: [PATCH 499/546] alif/machine_rtc: Add basic machine.RTC support. Signed-off-by: iabdalkader --- ports/alif/alif.mk | 1 + ports/alif/irq.h | 1 + ports/alif/machine_rtc.c | 109 +++++++++++++++++++++++++++++++++++++++ ports/alif/modmachine.c | 1 + 4 files changed, 112 insertions(+) create mode 100644 ports/alif/machine_rtc.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 63003b58a0937..aa2451d7a6291 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -119,6 +119,7 @@ SRC_C = \ machine_pin.c \ machine_i2c.c \ machine_spi.c \ + machine_rtc.c \ main.c \ modalif.c \ mphalport.c \ diff --git a/ports/alif/irq.h b/ports/alif/irq.h index 59dbce9705947..ed454adcb4657 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -48,6 +48,7 @@ #define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 7, 0) #define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0) #define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0) +#define IRQ_PRI_RTC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 100, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) // these states correspond to values from query_irq, enable_irq and disable_irq diff --git a/ports/alif/machine_rtc.c b/ports/alif/machine_rtc.c new file mode 100644 index 0000000000000..6473d1d80fbff --- /dev/null +++ b/ports/alif/machine_rtc.c @@ -0,0 +1,109 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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/mperrno.h" +#include "extmod/modmachine.h" +#include "rtc.h" +#include "sys_ctrl_rtc.h" + +typedef struct _machine_rtc_obj_t { + mp_obj_base_t base; + LPRTC_Type *rtc; +} machine_rtc_obj_t; + +// Singleton RTC object. +static const machine_rtc_obj_t machine_rtc = {{&machine_rtc_type}, (LPRTC_Type *)LPRTC_BASE}; + +void LPRTC_IRQHandler(void) { + lprtc_interrupt_ack(machine_rtc.rtc); + lprtc_interrupt_disable(machine_rtc.rtc); +} + +static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + const machine_rtc_obj_t *self = &machine_rtc; + + // Check arguments. + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + enable_lprtc_clk(); + lprtc_prescaler_disable(self->rtc); + lprtc_counter_wrap_disable(self->rtc); + lprtc_interrupt_disable(self->rtc); + lprtc_interrupt_unmask(self->rtc); + + NVIC_SetPriority(LPRTC_IRQ_IRQn, IRQ_PRI_RTC); + NVIC_ClearPendingIRQ(LPRTC_IRQ_IRQn); + NVIC_EnableIRQ(LPRTC_IRQ_IRQn); + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t machine_rtc_alarm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_id, ARG_time, ARG_repeat }; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_time, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_repeat, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + machine_rtc_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(args), allowed_args, args); + + if (mp_obj_is_int(args[ARG_time].u_obj)) { + uint32_t seconds = mp_obj_get_int(args[1].u_obj) / 1000; + + lprtc_counter_disable(self->rtc); + lprtc_load_count(self->rtc, 1); + lprtc_load_counter_match_register(self->rtc, seconds * 32768); + + lprtc_interrupt_ack(self->rtc); + lprtc_interrupt_enable(self->rtc); + lprtc_counter_enable(self->rtc); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid argument(s)")); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_rtc_alarm_obj, 1, machine_rtc_alarm); + +static const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_alarm), MP_ROM_PTR(&machine_rtc_alarm_obj) }, +}; +static MP_DEFINE_CONST_DICT(machine_rtc_locals_dict, machine_rtc_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_rtc_type, + MP_QSTR_RTC, + MP_TYPE_FLAG_NONE, + make_new, machine_rtc_make_new, + locals_dict, &machine_rtc_locals_dict + ); diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index 98d7bffbad932..9868abbeeaf35 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -35,6 +35,7 @@ extern void dcd_uninit(void); #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, \ + { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, \ static void mp_machine_idle(void) { mp_event_wait_indefinite(); From facd0b7190d48d7d6f4af1d1eaba0c3a18734356 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 7 Feb 2025 15:41:42 +0100 Subject: [PATCH 500/546] alif/ospi_flash: Use mp_hal_pin_config to configure OSPI pins. Signed-off-by: iabdalkader --- ports/alif/ospi_flash.c | 42 +++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index f2f95a04cf241..17c8be8128da3 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -262,30 +262,40 @@ int ospi_flash_init(void) { self->pin = pin; - uint32_t ck_pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST; - uint32_t io_pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; - - pinconf_set(pin->pin_cs->port, pin->pin_cs->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); - pinconf_set(pin->pin_clk_p->port, pin->pin_clk_p->pin, OSPI_PIN_FUNCTION, ck_pad_ctrl); + mp_hal_pin_config(pin->pin_cs, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); + mp_hal_pin_config(pin->pin_clk_p, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); if (pin->pin_clk_n != NULL) { - pinconf_set(pin->pin_clk_n->port, pin->pin_clk_n->pin, OSPI_PIN_FUNCTION, ck_pad_ctrl); + mp_hal_pin_config(pin->pin_clk_n, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); } if (pin->pin_rwds != NULL) { - pinconf_set(pin->pin_rwds->port, pin->pin_rwds->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + mp_hal_pin_config(pin->pin_rwds, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); if (pin->pin_rwds->port == PORT_10 && pin->pin_rwds->pin == PIN_7) { // Alif: P5_6 is needed to support proper alt function selection of P10_7. - pinconf_set(PORT_5, PIN_6, OSPI_PIN_FUNCTION, io_pad_ctrl); + mp_hal_pin_config(pin_P5_6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); } } - pinconf_set(pin->pin_d0->port, pin->pin_d0->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d1->port, pin->pin_d1->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, io_pad_ctrl | PADCTRL_DRIVER_DISABLED_PULL_UP); - pinconf_set(pin->pin_d3->port, pin->pin_d3->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + mp_hal_pin_config(pin->pin_d0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); if (pin->pin_d4 != NULL) { - pinconf_set(pin->pin_d4->port, pin->pin_d4->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d5->port, pin->pin_d5->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d6->port, pin->pin_d6->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); - pinconf_set(pin->pin_d7->port, pin->pin_d7->pin, OSPI_PIN_FUNCTION, io_pad_ctrl); + mp_hal_pin_config(pin->pin_d4, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d5, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + mp_hal_pin_config(pin->pin_d7, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); } // Reset the SPI flash. From b9e5f1ffba8551164374bf732aa165c87519a569 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 15 Feb 2025 13:53:44 +0100 Subject: [PATCH 501/546] alif/se_services: Add a secondary MHU channel. This channel can be used to communicate (pass messages) between the M55 cores in the RTSS. Currently it's only used to notify the cores. Signed-off-by: iabdalkader --- ports/alif/se_services.c | 120 +++++++++++++++++++++++++++++---------- ports/alif/se_services.h | 4 ++ 2 files changed, 93 insertions(+), 31 deletions(-) diff --git a/ports/alif/se_services.c b/ports/alif/se_services.c index e79a5b97c8944..1ed6e3fbfdad0 100644 --- a/ports/alif/se_services.c +++ b/ports/alif/se_services.c @@ -37,8 +37,9 @@ #include "py/mphal.h" // MHU indices. -#define MHU_M55_SE_MHU0 0 -#define MAX_MHU 1 +#define MHU_SESS_MHU0 0 +#define MHU_RTSS_MHU0 1 +#define MAX_MHU 2 // The following timeout is implemented in se_services_handle.c as a // simple loop busy polling on a variable set from an IRQ. @@ -57,24 +58,36 @@ typedef struct { static const uint32_t mhu_sender_base_address_list[MAX_MHU] = { MHU_SESS_S_TX_BASE, + MHU_RTSS_S_TX_BASE }; static const uint32_t mhu_receiver_base_address_list[MAX_MHU] = { MHU_SESS_S_RX_BASE, + MHU_RTSS_S_RX_BASE }; // Must be aligned as a uint32_t. static uint32_t packet_buffer[SERVICES_MAX_PACKET_BUFFER_SIZE / sizeof(uint32_t)]; +static uint32_t se_sess_handle; +static uint32_t se_rtss_handle; + static mhu_driver_out_t mhu_driver_out; -static uint32_t se_services_handle; void MHU_SESS_S_TX_IRQHandler(void) { - mhu_driver_out.sender_irq_handler(MHU_M55_SE_MHU0); + mhu_driver_out.sender_irq_handler(MHU_SESS_MHU0); } void MHU_SESS_S_RX_IRQHandler(void) { - mhu_driver_out.receiver_irq_handler(MHU_M55_SE_MHU0); + mhu_driver_out.receiver_irq_handler(MHU_SESS_MHU0); +} + +void MHU_RTSS_S_TX_IRQHandler(void) { + mhu_driver_out.sender_irq_handler(MHU_RTSS_MHU0); +} + +void MHU_RTSS_S_RX_IRQHandler(void) { + mhu_driver_out.receiver_irq_handler(MHU_RTSS_MHU0); } int dummy_printf(const char *fmt, ...) { @@ -82,6 +95,33 @@ int dummy_printf(const char *fmt, ...) { return 0; } +static void se_services_irq_config(IRQn_Type irqn, bool enable) { + if (enable) { + NVIC_ClearPendingIRQ(irqn); + NVIC_SetPriority(irqn, IRQ_PRI_MHU); + NVIC_EnableIRQ(irqn); + } else { + NVIC_DisableIRQ(irqn); + NVIC_ClearPendingIRQ(irqn); + } +} + +void se_services_rx_callback(uint32_t id, uint32_t channel, uint32_t data) { + switch (id) { + case MHU_SESS_MHU0: + SERVICES_rx_msg_callback(id, channel, data); + break; + case MHU_RTSS_MHU0: + #if MICROPY_PY_OPENAMP + extern void metal_rproc_notified(void); + metal_rproc_notified(); + #endif + break; + default: + break; + } +} + void se_services_init(void) { // Initialize MHU. mhu_driver_in_t mhu_driver_in; @@ -89,7 +129,7 @@ void se_services_init(void) { mhu_driver_in.receiver_base_address_list = (uint32_t *)mhu_receiver_base_address_list; mhu_driver_in.mhu_count = MAX_MHU; mhu_driver_in.send_msg_acked_callback = SERVICES_send_msg_acked_callback; - mhu_driver_in.rx_msg_callback = SERVICES_rx_msg_callback; + mhu_driver_in.rx_msg_callback = se_services_rx_callback; mhu_driver_in.debug_print = NULL; // not currently used by MHU_driver_initialize MHU_driver_initialize(&mhu_driver_in, &mhu_driver_out); @@ -103,29 +143,38 @@ void se_services_init(void) { }; SERVICES_initialize(&services_init_params); - // Create SE services channel for sending requests. - se_services_handle = SERVICES_register_channel(MHU_M55_SE_MHU0, 0); + // Register SESS MHU channel. + se_sess_handle = SERVICES_register_channel(MHU_SESS_MHU0, 0); + se_services_irq_config(MHU_SESS_S_RX_IRQ_IRQn, true); + se_services_irq_config(MHU_SESS_S_TX_IRQ_IRQn, true); - // Enable MHU interrupts. - NVIC_ClearPendingIRQ(MHU_SESS_S_RX_IRQ_IRQn); - NVIC_SetPriority(MHU_SESS_S_RX_IRQ_IRQn, IRQ_PRI_MHU); - NVIC_EnableIRQ(MHU_SESS_S_RX_IRQ_IRQn); - NVIC_ClearPendingIRQ(MHU_SESS_S_TX_IRQ_IRQn); - NVIC_SetPriority(MHU_SESS_S_TX_IRQ_IRQn, IRQ_PRI_MHU); - NVIC_EnableIRQ(MHU_SESS_S_TX_IRQ_IRQn); + // Register RTSS MHU channel. + se_rtss_handle = SERVICES_register_channel(MHU_RTSS_MHU0, 0); + se_services_irq_config(MHU_RTSS_S_RX_IRQ_IRQn, true); + se_services_irq_config(MHU_RTSS_S_TX_IRQ_IRQn, true); // Send heartbeat services requests until one succeeds. - SERVICES_synchronize_with_se(se_services_handle); + SERVICES_synchronize_with_se(se_sess_handle); +} + +void se_services_deinit(void) { + // Disable SESS MHU channel IRQs. + se_services_irq_config(MHU_SESS_S_RX_IRQ_IRQn, false); + se_services_irq_config(MHU_SESS_S_TX_IRQ_IRQn, false); + + // Disable RTSS MHU channel IRQs. + se_services_irq_config(MHU_RTSS_S_RX_IRQ_IRQn, false); + se_services_irq_config(MHU_RTSS_S_TX_IRQ_IRQn, false); } void se_services_dump_device_data(void) { uint32_t error_code; uint8_t revision[80]; - SERVICES_get_se_revision(se_services_handle, revision, &error_code); + SERVICES_get_se_revision(se_sess_handle, revision, &error_code); SERVICES_version_data_t data; - SERVICES_system_get_device_data(se_services_handle, &data, &error_code); + SERVICES_system_get_device_data(se_sess_handle, &data, &error_code); printf("SE revision: %s\n", revision); printf("ALIF_PN: %s\n", data.ALIF_PN); @@ -141,11 +190,11 @@ void se_services_dump_device_data(void) { void se_services_get_unique_id(uint8_t id[8]) { uint32_t error_code; - SERVICES_system_get_eui_extension(se_services_handle, false, id, &error_code); + SERVICES_system_get_eui_extension(se_sess_handle, false, id, &error_code); } __attribute__((noreturn)) void se_services_reset_soc(void) { - SERVICES_boot_reset_soc(se_services_handle); + SERVICES_boot_reset_soc(se_sess_handle); NVIC_SystemReset(); } @@ -155,7 +204,7 @@ uint64_t se_services_rand64(void) { for (int retry = 0; retry < 100; ++retry) { uint64_t value; int32_t error_code; - uint32_t ret = SERVICES_cryptocell_get_rnd(se_services_handle, sizeof(uint64_t), &value, &error_code); + uint32_t ret = SERVICES_cryptocell_get_rnd(se_sess_handle, sizeof(uint64_t), &value, &error_code); if (ret == SERVICES_REQ_SUCCESS) { return value; } @@ -165,51 +214,59 @@ uint64_t se_services_rand64(void) { return 0; } +uint32_t se_services_notify(void) { + uint32_t ret = SERVICES_send_msg(se_rtss_handle, LocalToGlobal(0)); + if (ret != SERVICES_REQ_SUCCESS) { + return -1; + } + return 0; +} + uint32_t se_services_enable_clock(clock_enable_t clock, bool enable) { uint32_t error_code; - SERVICES_clocks_enable_clock(se_services_handle, clock, enable, &error_code); + SERVICES_clocks_enable_clock(se_sess_handle, clock, enable, &error_code); return error_code; } uint32_t se_services_select_pll_source(pll_source_t source, pll_target_t target) { uint32_t error_code; - SERVICES_clocks_select_pll_source(se_services_handle, source, target, &error_code); + SERVICES_clocks_select_pll_source(se_sess_handle, source, target, &error_code); return error_code; } uint32_t se_services_get_run_profile(run_profile_t *profile) { uint32_t error_code; - SERVICES_get_run_cfg(se_services_handle, profile, &error_code); + SERVICES_get_run_cfg(se_sess_handle, profile, &error_code); return error_code; } uint32_t se_services_set_run_profile(run_profile_t *profile) { uint32_t error_code; - SERVICES_set_run_cfg(se_services_handle, profile, &error_code); + SERVICES_set_run_cfg(se_sess_handle, profile, &error_code); return error_code; } uint32_t se_services_get_off_profile(off_profile_t *profile) { uint32_t error_code; - SERVICES_get_off_cfg(se_services_handle, profile, &error_code); + SERVICES_get_off_cfg(se_sess_handle, profile, &error_code); return error_code; } uint32_t se_services_set_off_profile(off_profile_t *profile) { uint32_t error_code; - SERVICES_set_off_cfg(se_services_handle, profile, &error_code); + SERVICES_set_off_cfg(se_sess_handle, profile, &error_code); return error_code; } uint32_t se_services_boot_process_toc_entry(const uint8_t *image_id) { uint32_t error_code; - SERVICES_boot_process_toc_entry(se_services_handle, image_id, &error_code); + SERVICES_boot_process_toc_entry(se_sess_handle, image_id, &error_code); return error_code; } uint32_t se_services_boot_cpu(uint32_t cpu_id, uint32_t address) { uint32_t error_code; - SERVICES_boot_cpu(se_services_handle, cpu_id, address, &error_code); + SERVICES_boot_cpu(se_sess_handle, cpu_id, address, &error_code); return error_code; } @@ -221,7 +278,7 @@ uint32_t se_services_boot_reset_cpu(uint32_t cpu_id) { } for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(1)) { - uint32_t ret = SERVICES_boot_reset_cpu(se_services_handle, cpu_id, &error_code); + uint32_t ret = SERVICES_boot_reset_cpu(se_sess_handle, cpu_id, &error_code); if (ret != SERVICES_REQ_SUCCESS) { return error_code; } @@ -234,11 +291,12 @@ uint32_t se_services_boot_reset_cpu(uint32_t cpu_id) { return SERVICES_REQ_TIMEOUT; } } + return error_code; } uint32_t se_services_boot_release_cpu(uint32_t cpu_id) { uint32_t error_code; - SERVICES_boot_release_cpu(se_services_handle, cpu_id, &error_code); + SERVICES_boot_release_cpu(se_sess_handle, cpu_id, &error_code); return error_code; } diff --git a/ports/alif/se_services.h b/ports/alif/se_services.h index 87deb05592990..44b6584a20000 100644 --- a/ports/alif/se_services.h +++ b/ports/alif/se_services.h @@ -29,12 +29,16 @@ #include "services_lib_api.h" void se_services_init(void); +void se_services_deinit(void); void se_services_dump_device_data(void); void se_services_get_unique_id(uint8_t id[8]); __attribute__((noreturn)) void se_services_reset_soc(void); uint64_t se_services_rand64(void); +uint32_t se_services_notify(void); + uint32_t se_services_enable_clock(clock_enable_t clock, bool enable); uint32_t se_services_select_pll_source(pll_source_t source, pll_target_t target); + uint32_t se_services_get_run_profile(run_profile_t *profile); uint32_t se_services_set_run_profile(run_profile_t *profile); uint32_t se_services_get_off_profile(off_profile_t *profile); From 182b5f3a12427d3dc21744b0164d546663ea696b Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 15 Feb 2025 14:00:42 +0100 Subject: [PATCH 502/546] alif/mpmetalport: Use MHU to notify remote cores. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unlike HWSEM, the MHU IRQ can wake up cores from low-power modes, making it better suited for notifying remote cores. Note that no special function is required to wake up a remote core—the act of sending a message alone will notify it. Signed-off-by: iabdalkader --- ports/alif/mpmetalport.c | 31 +++++++------------------------ ports/alif/mpmetalport.h | 14 -------------- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/ports/alif/mpmetalport.c b/ports/alif/mpmetalport.c index a7d4025468a16..b0a017fdd797a 100644 --- a/ports/alif/mpmetalport.c +++ b/ports/alif/mpmetalport.c @@ -27,8 +27,6 @@ */ #include ALIF_CMSIS_H -#include "hwsem.h" - #include "py/mperrno.h" #include "py/mphal.h" @@ -36,18 +34,14 @@ #include "metal/utilities.h" #include "metal/device.h" +#include "se_services.h" + struct metal_state _metal; static mp_sched_node_t rproc_notify_node; int metal_sys_init(const struct metal_init_params *params) { metal_unused(params); - // Reset the hardware semaphore. - hwsem_reset(METAL_HSEM_DEVICE); - #if MICROPY_PY_OPENAMP_HOST - hwsem_reset(METAL_HSEM_REMOTE); - #endif - // If cache management is not enabled, configure the MPU to disable // caching for the entire Open-AMP shared memory region. #ifndef VIRTIO_USE_DCACHE @@ -59,18 +53,10 @@ int metal_sys_init(const struct metal_init_params *params) { #endif metal_bus_register(&metal_generic_bus); - - // Enable the hardware semaphore IRQ. - NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); - NVIC_SetPriority(METAL_HSEM_IRQn, IRQ_PRI_HWSEM); - NVIC_EnableIRQ(METAL_HSEM_IRQn); return 0; } void metal_sys_finish(void) { - NVIC_DisableIRQ(METAL_HSEM_IRQn); - NVIC_ClearPendingIRQ(METAL_HSEM_IRQn); - hwsem_reset(METAL_HSEM_DEVICE); metal_bus_unregister(&metal_generic_bus); } @@ -99,15 +85,12 @@ void metal_machine_cache_invalidate(void *addr, unsigned int len) { } int metal_rproc_notify(void *priv, uint32_t id) { - // Release the HW semaphore to notify the other core. - hwsem_release(METAL_HSEM_REMOTE, HWSEM_MASTERID); + // Notify the remote core. + se_services_notify(); return 0; } -void METAL_HSEM_IRQ_HANDLER(void) { - // Schedule the node only if the other core released the Semaphore. - if (METAL_HSEM_DEVICE->HWSEM_REL_REG == 0) { - mp_sched_schedule_node(&rproc_notify_node, openamp_remoteproc_notified); - } - hwsem_request(METAL_HSEM_DEVICE, METAL_HSEM_REMOTE_ID); +void metal_rproc_notified(void) { + // The remote core notified this core. + mp_sched_schedule_node(&rproc_notify_node, openamp_remoteproc_notified); } diff --git a/ports/alif/mpmetalport.h b/ports/alif/mpmetalport.h index 7a428dbc6c62a..c11725a1f1e45 100644 --- a/ports/alif/mpmetalport.h +++ b/ports/alif/mpmetalport.h @@ -38,20 +38,6 @@ #define METAL_MAX_DEVICE_REGIONS 2 -#if MICROPY_PY_OPENAMP_HOST -#define METAL_HSEM_DEVICE ((HWSEM_Type *)HWSEM14_BASE) -#define METAL_HSEM_REMOTE ((HWSEM_Type *)HWSEM15_BASE) -#define METAL_HSEM_REMOTE_ID (0x410FD222U) -#define METAL_HSEM_IRQn HWSEM_IRQ14_IRQn -#define METAL_HSEM_IRQ_HANDLER HWSEM_IRQ14Handler -#else -#define METAL_HSEM_DEVICE ((HWSEM_Type *)HWSEM15_BASE) -#define METAL_HSEM_REMOTE ((HWSEM_Type *)HWSEM14_BASE) -#define METAL_HSEM_REMOTE_ID (0x410FD221U) -#define METAL_HSEM_IRQn HWSEM_IRQ15_IRQn -#define METAL_HSEM_IRQ_HANDLER HWSEM_IRQ15Handler -#endif - // Set to 1 to enable log output. #define METAL_LOG_HANDLER_ENABLE 0 From 68b1dae011dd9f2696e9f6f2f914317d827c879d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 19 Feb 2025 14:42:05 +0100 Subject: [PATCH 503/546] alif: Link with libnosys. This allows the correct start up functions to be called by the stdlib. Signed-off-by: iabdalkader --- ports/alif/alif.mk | 22 +++++++++---------- ports/alif/main.c | 2 +- ports/alif/nosys_stubs.c | 46 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 ports/alif/nosys_stubs.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index aa2451d7a6291..7e58aa5729f39 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -68,9 +68,9 @@ CFLAGS += $(INC) \ -mtune=cortex-m55 \ $(CFLAGS_FPU) \ -march=armv8.1-m.main+fp+mve.fp \ - -nostdlib \ -fdata-sections \ -ffunction-sections \ + --specs=nosys.specs \ -D$(MCU_CORE)=1 \ -DCORE_$(MCU_CORE) \ -DALIF_CMSIS_H="\"$(MCU_CORE).h\"" @@ -95,18 +95,17 @@ CFLAGS += $(CFLAGS_EXTRA) AFLAGS = -mthumb -march=armv8.1-m.main+fp+mve.fp $(CFLAGS_FPU) -LDFLAGS += -nostdlib \ - -T$(BUILD)/ensemble.ld \ - -Map=$@.map \ - --cref \ - --gc-sections \ - --print-memory-usage +CFLAGS += -Wl,-T$(BUILD)/ensemble.ld \ + -Wl,-Map=$@.map \ + -Wl,--cref \ + -Wl,--gc-sections \ + -Wl,--print-memory-usage \ + -Wl,--no-warn-rwx-segment + ifeq ($(MCU_CORE),M55_HP) -LDFLAGS += --wrap=dcd_event_handler +CFLAGS += -Wl,--wrap=dcd_event_handler endif -LIBS += "$(shell $(CC) $(CFLAGS) -print-libgcc-file-name)" - ################################################################################ # Source files and libraries @@ -126,6 +125,7 @@ SRC_C = \ mpu.c \ mpuart.c \ msc_disk.c \ + nosys_stubs.c \ ospi_ext.c \ ospi_flash.c \ pendsv.c \ @@ -256,7 +256,7 @@ $(BUILD)/ensemble.ld: $(LD_FILE) $(BUILD)/firmware.elf: $(OBJ) $(BUILD)/ensemble.ld $(ECHO) "Link $@" - $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + $(Q)$(CC) $(CFLAGS) -o $@ $(OBJ) $(LIBS) $(Q)$(SIZE) $@ $(BUILD)/firmware.bin: $(BUILD)/firmware.elf diff --git a/ports/alif/main.c b/ports/alif/main.c index 975ee7ed2d328..793425fff2e74 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -55,7 +55,7 @@ NORETURN void panic(const char *msg) { } } -void _start(void) { +int main(void) { system_tick_init(); MICROPY_BOARD_STARTUP(); diff --git a/ports/alif/nosys_stubs.c b/ports/alif/nosys_stubs.c new file mode 100644 index 0000000000000..a394ec8f1cf66 --- /dev/null +++ b/ports/alif/nosys_stubs.c @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 + +int _write(int handle, char *buffer, int size) { + errno = ENOSYS; + return -1; +} + +int _read(int handle, char *buffer, int size) { + errno = ENOSYS; + return -1; +} + +int _close(int f) { + errno = ENOSYS; + return -1; +} + +int _lseek(int f, int ptr, int dir) { + errno = ENOSYS; + return -1; +} From 7e32c232183441e1022f59e58c31ba431c25fe2f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Mar 2025 23:29:58 +1100 Subject: [PATCH 504/546] alif/mpmetalport: Only notify after metal subsystem is init'd. Signed-off-by: Damien George --- ports/alif/mpmetalport.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/alif/mpmetalport.c b/ports/alif/mpmetalport.c index b0a017fdd797a..9d774cb906766 100644 --- a/ports/alif/mpmetalport.c +++ b/ports/alif/mpmetalport.c @@ -37,6 +37,8 @@ #include "se_services.h" struct metal_state _metal; + +static bool metal_active; static mp_sched_node_t rproc_notify_node; int metal_sys_init(const struct metal_init_params *params) { @@ -53,10 +55,13 @@ int metal_sys_init(const struct metal_init_params *params) { #endif metal_bus_register(&metal_generic_bus); + metal_active = true; + return 0; } void metal_sys_finish(void) { + metal_active = false; metal_bus_unregister(&metal_generic_bus); } @@ -91,6 +96,9 @@ int metal_rproc_notify(void *priv, uint32_t id) { } void metal_rproc_notified(void) { + if (!metal_active) { + return; + } // The remote core notified this core. mp_sched_schedule_node(&rproc_notify_node, openamp_remoteproc_notified); } From ca3d50a096ffa999e47b98335ceb0b7f459196c2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Mar 2025 23:31:58 +1100 Subject: [PATCH 505/546] alif/mpuart: Use mp_hal_pin_config for TX/RX configuration. Signed-off-by: Damien George --- ports/alif/mpuart.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c index e9cfcf34fed53..69734659a4101 100644 --- a/ports/alif/mpuart.c +++ b/ports/alif/mpuart.c @@ -35,10 +35,8 @@ #include "sys_ctrl_uart.h" #include "uart.h" -#define TX_PORT PORT_12 -#define TX_PIN PIN_2 -#define RX_PORT PORT_12 -#define RX_PIN PIN_1 +#define TX_PIN pin_P12_2 +#define RX_PIN pin_P12_1 #define UART_ID 4 #define UART_IRQN UART4_IRQ_IRQn #define UART_PTR ((UART_Type *)UART4_BASE) @@ -48,8 +46,8 @@ static UART_TRANSFER transfer; void mp_uart_init(void) { - pinconf_set(TX_PORT, TX_PIN, PINMUX_ALTERNATE_FUNCTION_2, 0); - pinconf_set(RX_PORT, RX_PIN, PINMUX_ALTERNATE_FUNCTION_2, PADCTRL_READ_ENABLE); + mp_hal_pin_config(TX_PIN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_UART, false); + mp_hal_pin_config(RX_PIN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_UART, true); select_uart_clock_syst_pclk(UART_ID); enable_uart_clock(UART_ID); uart_software_reset(UART_PTR); From af574a86c2ec3af02b167d449b8a8718f39f1ca8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 24 Dec 2024 00:18:56 +1100 Subject: [PATCH 506/546] alif/alif_flash: Distinguish between total flash size and FS size. Signed-off-by: Damien George --- ports/alif/alif_flash.c | 8 ++++---- ports/alif/msc_disk.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/alif/alif_flash.c b/ports/alif/alif_flash.c index f2ea5ac85a88e..de7b37804f955 100644 --- a/ports/alif/alif_flash.c +++ b/ports/alif/alif_flash.c @@ -37,10 +37,10 @@ typedef struct _alif_flash_obj_t { uint32_t flash_size; } alif_flash_obj_t; -static alif_flash_obj_t alif_flash_obj = { +static const alif_flash_obj_t alif_flash_fs_obj = { .base = { &alif_flash_type }, .flash_base_addr = MICROPY_HW_FLASH_STORAGE_BASE_ADDR, - .flash_size = MICROPY_HW_FLASH_STORAGE_BYTES, + .flash_size = MICROPY_HW_FLASH_STORAGE_FS_BYTES, }; static mp_obj_t alif_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { @@ -54,8 +54,8 @@ static mp_obj_t alif_flash_make_new(const mp_obj_type_t *type, size_t n_args, si mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if (args[ARG_start].u_int == -1 && args[ARG_len].u_int == -1) { - // Default singleton object that accesses entire flash - return MP_OBJ_FROM_PTR(&alif_flash_obj); + // Default singleton object that accesses writable-filesystem flash + return MP_OBJ_FROM_PTR(&alif_flash_fs_obj); } alif_flash_obj_t *self = mp_obj_malloc(alif_flash_obj_t, &alif_flash_type); diff --git a/ports/alif/msc_disk.c b/ports/alif/msc_disk.c index a01494d20f0e4..0b8ddddeda248 100644 --- a/ports/alif/msc_disk.c +++ b/ports/alif/msc_disk.c @@ -37,7 +37,7 @@ #endif #define BLOCK_SIZE (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES) -#define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_BYTES / BLOCK_SIZE) +#define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_FS_BYTES / BLOCK_SIZE) #define FLASH_BASE_ADDR (MICROPY_HW_FLASH_STORAGE_BASE_ADDR) static bool ejected = false; From d895a62b0703d28fb2b15427760aae8a67ad666f Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 24 Dec 2024 00:19:20 +1100 Subject: [PATCH 507/546] alif/alif_flash: Make flash respond to the buffer protocol. Signed-off-by: Damien George --- ports/alif/alif_flash.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ports/alif/alif_flash.c b/ports/alif/alif_flash.c index de7b37804f955..722afadae1f7b 100644 --- a/ports/alif/alif_flash.c +++ b/ports/alif/alif_flash.c @@ -80,6 +80,19 @@ static mp_obj_t alif_flash_make_new(const mp_obj_type_t *type, size_t n_args, si return MP_OBJ_FROM_PTR(self); } +static mp_int_t alif_flash_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + alif_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)(ospi_flash_get_xip_base() + self->flash_base_addr); + bufinfo->len = self->flash_size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Can't return a writable buffer. + return 1; + } +} + static mp_obj_t alif_flash_readblocks(size_t n_args, const mp_obj_t *args) { alif_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); uint32_t offset = mp_obj_get_int(args[1]) * MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; @@ -157,6 +170,7 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_QSTR_Flash, MP_TYPE_FLAG_NONE, make_new, alif_flash_make_new, + buffer, alif_flash_get_buffer, locals_dict, &alif_flash_locals_dict ); #endif // MICROPY_HW_ENABLE_OSPI From f83f6e7eed39f961ff9788814d3ee9104d3577a6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Mar 2025 16:30:15 +1100 Subject: [PATCH 508/546] alif/mpu: Add function to set read-only bit on MRAM MPU region. To allow writing to MRAM region. Signed-off-by: Damien George --- ports/alif/mpu.c | 11 +++++++++++ ports/alif/mpu.h | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/ports/alif/mpu.c b/ports/alif/mpu.c index 471eff20a2c5b..60753674ae1e5 100644 --- a/ports/alif/mpu.c +++ b/ports/alif/mpu.c @@ -25,6 +25,7 @@ */ #include "py/mpconfig.h" +#include "irq.h" #include "mpu.h" #include ALIF_CMSIS_H @@ -76,3 +77,13 @@ void MPU_Load_Regions(void) { // Load the MPU regions from the table. ARM_MPU_Load(0, mpu_table, sizeof(mpu_table) / sizeof(ARM_MPU_Region_t)); } + +void mpu_config_mram(bool read_only) { + uintptr_t atomic = disable_irq(); + ARM_MPU_Disable(); + MPU->RNR = MP_MPU_REGION_MRAM; + MPU->RBAR = ARM_MPU_RBAR(MRAM_BASE, ARM_MPU_SH_NON, read_only, 1, 0); + MPU->RLAR = ARM_MPU_RLAR(MRAM_BASE + MRAM_SIZE - 1, MP_MPU_ATTR_NORMAL_WT_RA); + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); + enable_irq(atomic); +} diff --git a/ports/alif/mpu.h b/ports/alif/mpu.h index 88fbe011206ed..1d3602941efb7 100644 --- a/ports/alif/mpu.h +++ b/ports/alif/mpu.h @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#include + #define MP_MPU_ATTR_NORMAL_WT_RA_TRANSIENT (0) #define MP_MPU_ATTR_DEVICE_nGnRE (1) #define MP_MPU_ATTR_NORMAL_WB_RA_WA (2) @@ -37,3 +39,5 @@ #define MP_MPU_REGION_OSPI_REGISTERS (4) #define MP_MPU_REGION_OSPI0_XIP (5) #define MP_MPU_REGION_OPENAMP (6) + +void mpu_config_mram(bool read_only); From 8297c95c22fd15aa706243bfebb0688a61827855 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Mar 2025 14:51:45 +1100 Subject: [PATCH 509/546] alif/vfs_rom_ioctl: Add vfs_rom_ioctl with support for OSPI and MRAM. Signed-off-by: Damien George --- ports/alif/alif.mk | 3 + ports/alif/mpconfigport.h | 1 + ports/alif/vfs_rom_ioctl.c | 175 +++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 ports/alif/vfs_rom_ioctl.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 7e58aa5729f39..f0ff8d91a53d9 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -132,6 +132,7 @@ SRC_C = \ system_tick.c \ se_services.c \ usbd.c \ + vfs_rom_ioctl.c \ $(wildcard $(BOARD_DIR)/*.c) ifeq ($(MICROPY_FLOAT_IMPL),float) @@ -194,6 +195,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ drivers/source/mhu_driver.c \ drivers/source/mhu_receiver.c \ drivers/source/mhu_sender.c \ + drivers/source/mram.c \ drivers/source/pinconf.c \ drivers/source/uart.c \ drivers/source/utimer.c \ @@ -209,6 +211,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ ) $(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 +$(BUILD)/$(ALIF_DFP_REL_TOP)/drivers/source/mram.o: CFLAGS += -Wno-strict-aliasing $(BUILD)/$(ALIF_DFP_REL_TOP)/drivers/source/spi.o: CFLAGS += -Wno-maybe-uninitialized $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_system.o: CFLAGS += -Wno-maybe-uninitialized diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 83f808b131f48..33f300641fbba 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -137,6 +137,7 @@ #define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_TIMER (1) #define MICROPY_VFS (1) +#define MICROPY_VFS_ROM (1) // fatfs configuration #define MICROPY_FATFS_ENABLE_LFN (1) diff --git a/ports/alif/vfs_rom_ioctl.c b/ports/alif/vfs_rom_ioctl.c new file mode 100644 index 0000000000000..d82ac6f8417e2 --- /dev/null +++ b/ports/alif/vfs_rom_ioctl.c @@ -0,0 +1,175 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 OpenMV LLC. + * + * 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/mperrno.h" +#include "py/objarray.h" +#include "py/runtime.h" +#include "extmod/vfs.h" +#include "modalif.h" +#include "mpu.h" +#include "mram.h" +#include "ospi_flash.h" + +#if MICROPY_VFS_ROM_IOCTL + +#if MICROPY_HW_ROMFS_ENABLE_PART0 && !defined(MICROPY_HW_ROMFS_PART0_START) +#define MICROPY_HW_ROMFS_PART0_START (uintptr_t)(&_micropy_hw_romfs_part0_start) +#define MICROPY_HW_ROMFS_PART0_SIZE (uintptr_t)(&_micropy_hw_romfs_part0_size) +extern uint8_t _micropy_hw_romfs_part0_start; +extern uint8_t _micropy_hw_romfs_part0_size; +#endif + +#if MICROPY_HW_ROMFS_ENABLE_PART1 && !defined(MICROPY_HW_ROMFS_PART1_START) +#define MICROPY_HW_ROMFS_PART1_START (uintptr_t)(&_micropy_hw_romfs_part1_start) +#define MICROPY_HW_ROMFS_PART1_SIZE (uintptr_t)(&_micropy_hw_romfs_part1_size) +extern uint8_t _micropy_hw_romfs_part1_start; +extern uint8_t _micropy_hw_romfs_part1_size; +#endif + +#define ROMFS_MEMORYVIEW(base, size) {{&mp_type_memoryview}, 'B', 0, (size), (void *)(base)} + +static const mp_obj_array_t romfs_obj_table[] = { + #if MICROPY_HW_ROMFS_ENABLE_PART0 + ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART0_START, MICROPY_HW_ROMFS_PART0_SIZE), + #endif + #if MICROPY_HW_ROMFS_ENABLE_PART1 + ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART1_START, MICROPY_HW_ROMFS_PART1_SIZE), + #endif +}; + +static inline bool mram_is_valid_addr(uintptr_t addr) { + return MRAM_BASE <= addr && addr < MRAM_BASE + MRAM_SIZE; +} + +static inline bool ospi_is_valid_addr(uintptr_t xip_base, uintptr_t addr) { + MP_STATIC_ASSERT(OSPI0_XIP_SIZE == OSPI1_XIP_SIZE); + return xip_base <= addr && addr < xip_base + OSPI0_XIP_SIZE; +} + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + mp_int_t cmd = mp_obj_get_int(args[0]); + if (cmd == MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS) { + return MP_OBJ_NEW_SMALL_INT(MP_ARRAY_SIZE(romfs_obj_table)); + } + + if (n_args < 2) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + mp_int_t romfs_id = mp_obj_get_int(args[1]); + if (!(0 <= romfs_id && romfs_id < MP_ARRAY_SIZE(romfs_obj_table))) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + const mp_obj_array_t *romfs_obj = &romfs_obj_table[romfs_id]; + + if (cmd == MP_VFS_ROM_IOCTL_GET_SEGMENT) { + // Return the ROMFS memoryview object. + return MP_OBJ_FROM_PTR(romfs_obj); + } + + uintptr_t romfs_base = (uintptr_t)romfs_obj->items; + uintptr_t romfs_len = romfs_obj->len; + + #if MICROPY_HW_ENABLE_OSPI + const uintptr_t ospi_base = ospi_flash_get_xip_base(); + #endif + + if (cmd == MP_VFS_ROM_IOCTL_WRITE_PREPARE) { + if (n_args < 3) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = romfs_base; + uint32_t dest_max = dest + mp_obj_get_int(args[2]); + if (dest_max > romfs_base + romfs_len) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + if (mram_is_valid_addr(dest)) { + // No preparation needed for MRAM. + // Return the minimum write size. + return MP_OBJ_NEW_SMALL_INT(MRAM_SECTOR_SIZE); + } + + #if MICROPY_HW_ENABLE_OSPI + if (ospi_is_valid_addr(ospi_base, dest)) { + // Erase OSPI flash. + dest -= ospi_base; + dest_max -= ospi_base; + while (dest < dest_max) { + int ret = ospi_flash_erase_sector(dest); + mp_event_handle_nowait(); + if (ret < 0) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + dest += MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + } + // Return the minimum write size. + return MP_OBJ_NEW_SMALL_INT(4); + } + #endif + } + + if (cmd == MP_VFS_ROM_IOCTL_WRITE) { + if (n_args < 4) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = romfs_base + mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + if (dest + bufinfo.len > romfs_base + romfs_len) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + if (mram_is_valid_addr(dest)) { + // Write data to MRAM. + mpu_config_mram(false); + const uint8_t *src = bufinfo.buf; + const uint8_t *max = src + bufinfo.len; + while (src < max) { + mram_write_128bit((uint8_t *)dest, src); + dest += MRAM_SECTOR_SIZE; + src += MRAM_SECTOR_SIZE; + } + mpu_config_mram(true); + return MP_OBJ_NEW_SMALL_INT(0); // success + } + + #if MICROPY_HW_ENABLE_OSPI + if (ospi_is_valid_addr(ospi_base, dest)) { + // Write data to OSPI flash. + dest -= ospi_base; + int ret = ospi_flash_write(dest, bufinfo.len, bufinfo.buf); + mp_event_handle_nowait(); + return MP_OBJ_NEW_SMALL_INT(ret); + } + #endif + } + + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); +} + +#endif // MICROPY_VFS_ROM_IOCTL From d1b12cb6766d8b48d0e8cdf551808c324d0599c1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 8 Mar 2025 23:19:47 +1100 Subject: [PATCH 510/546] alif/modules: Make HE core set /rom as current dir. This allows HE to execute code from the ROMFS in MRAM. Signed-off-by: Damien George --- ports/alif/modules/he/_boot.py | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/ports/alif/modules/he/_boot.py b/ports/alif/modules/he/_boot.py index bc1320f190694..3df1804709073 100644 --- a/ports/alif/modules/he/_boot.py +++ b/ports/alif/modules/he/_boot.py @@ -1,21 +1,7 @@ -import openamp -import time -from machine import Pin +# Change working directory to ROMFS, so boot.py and main.py can run from there. +try: + import os - -def ept_recv_callback(src_addr, data): - print("Received message on endpoint", data) - - -# Create a new RPMsg endpoint to communicate with main core. -ept = openamp.Endpoint("vuart-channel", callback=ept_recv_callback) - -pin = Pin("LED_BLUE", Pin.OUT) - -count = 0 -while True: - if ept.is_ready(): - ept.send("Hello from HE %d" % count, timeout=1000) - count += 1 - time.sleep_ms(100) - pin(not pin()) + os.chdir("/rom") +except: + pass From 4f2a8bd99f81f88fd173aa717523345ba32083e0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 23 Oct 2024 16:25:22 +1100 Subject: [PATCH 511/546] alif/mphalport: Add mp_hal_pin_config_irq_falling helper. Signed-off-by: Damien George --- ports/alif/mphalport.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 3b23cb6a2c514..5f1e3fff8296e 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -155,6 +155,16 @@ static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) { gpio_set_direction_output(pin->gpio, pin->pin); } +static inline void mp_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool enable) { + if (enable) { + gpio_enable_interrupt(pin->gpio, pin->pin); + gpio_interrupt_set_edge_trigger(pin->gpio, pin->pin); + gpio_interrupt_set_polarity_low(pin->gpio, pin->pin); + } else { + gpio_disable_interrupt(pin->gpio, pin->pin); + } +} + static inline void mp_hal_pin_low(mp_hal_pin_obj_t pin) { gpio_set_value_low(pin->gpio, pin->pin); } From 411146b0ed0fa2d70fc13264b1bd2cda9045c99e Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 14 Oct 2024 21:48:51 +1100 Subject: [PATCH 512/546] alif/mpuart: Generalise UART driver to suppot all UART instances. Signed-off-by: Damien George --- ports/alif/main.c | 2 +- ports/alif/mpconfigport.h | 2 +- ports/alif/mphalport.c | 2 +- ports/alif/mpuart.c | 294 ++++++++++++++++++++++++++++++-------- ports/alif/mpuart.h | 17 ++- 5 files changed, 251 insertions(+), 66 deletions(-) diff --git a/ports/alif/main.c b/ports/alif/main.c index 793425fff2e74..77e413e1eb492 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -66,7 +66,7 @@ int main(void) { MICROPY_BOARD_EARLY_INIT(); #if MICROPY_HW_ENABLE_UART_REPL - mp_uart_init(); + mp_uart_init_repl(); #endif #if MICROPY_HW_ENABLE_OSPI if (ospi_flash_init() != 0) { diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 33f300641fbba..a5fd4f6dbfa87 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -73,7 +73,7 @@ #define MICROPY_HW_USB_PID (0x9802) // interface has CDC only #endif #ifndef MICROPY_HW_ENABLE_UART_REPL -#define MICROPY_HW_ENABLE_UART_REPL (CORE_M55_HP) // useful if there is no USB +#define MICROPY_HW_ENABLE_UART_REPL (0) #endif #define MICROPY_HW_FLASH_BLOCK_SIZE_BYTES (4096) diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 1a6136c31abeb..8f7780bb8acdf 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -91,7 +91,7 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { #endif #if MICROPY_HW_ENABLE_UART_REPL - mp_uart_write_strn(str, len); + mp_uart_write_strn_repl(str, len); did_write = true; #endif diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c index 69734659a4101..7f14ff5444702 100644 --- a/ports/alif/mpuart.c +++ b/ports/alif/mpuart.c @@ -24,88 +24,260 @@ * THE SOFTWARE. */ -#include -#include "py/runtime.h" #include "py/mphal.h" +#include "py/runtime.h" #include "mpuart.h" -#if MICROPY_HW_ENABLE_UART_REPL - -#include "pinconf.h" #include "sys_ctrl_uart.h" #include "uart.h" -#define TX_PIN pin_P12_2 -#define RX_PIN pin_P12_1 -#define UART_ID 4 -#define UART_IRQN UART4_IRQ_IRQn -#define UART_PTR ((UART_Type *)UART4_BASE) -#define BAUDRATE 115200 -#define SYST_PCLK 100000000 - -static UART_TRANSFER transfer; - -void mp_uart_init(void) { - mp_hal_pin_config(TX_PIN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_UART, false); - mp_hal_pin_config(RX_PIN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_UART, true); - select_uart_clock_syst_pclk(UART_ID); - enable_uart_clock(UART_ID); - uart_software_reset(UART_PTR); - uart_enable_fifo(UART_PTR); - uart_disable_tx_irq(UART_PTR); - uart_disable_rx_irq(UART_PTR); - uart_set_baudrate(UART_PTR, SYST_PCLK, BAUDRATE); - uart_set_data_parity_stop_bits(UART_PTR, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1); - uart_set_flow_control(UART_PTR, UART_FLOW_CONTROL_NONE); - NVIC_ClearPendingIRQ(UART_IRQN); - NVIC_SetPriority(UART_IRQN, IRQ_PRI_UART_REPL); - NVIC_EnableIRQ(UART_IRQN); - uart_set_tx_trigger(UART_PTR, UART_TX_FIFO_EMPTY); - uart_set_rx_trigger(UART_PTR, UART_RX_ONE_CHAR_IN_FIFO); - uart_enable_rx_irq(UART_PTR); +#define UART_MAX (8) +#define SYST_PCLK (100000000) + +typedef struct _uart_state_t { + UART_TRANSFER_STATUS status; + ringbuf_t *rx_ringbuf; + const uint8_t *tx_src; + const uint8_t *tx_src_max; + void (*irq_callback)(void); +} uart_state_t; + +static const uint8_t uart_irqn[UART_MAX] = { + UART0_IRQ_IRQn, + UART1_IRQ_IRQn, + UART2_IRQ_IRQn, + UART3_IRQ_IRQn, + UART4_IRQ_IRQn, + UART5_IRQ_IRQn, + UART6_IRQ_IRQn, + UART7_IRQ_IRQn, +}; + +static UART_Type *const uart_periph[UART_MAX] = { + (UART_Type *)UART0_BASE, + (UART_Type *)UART1_BASE, + (UART_Type *)UART2_BASE, + (UART_Type *)UART3_BASE, + (UART_Type *)UART4_BASE, + (UART_Type *)UART5_BASE, + (UART_Type *)UART6_BASE, + (UART_Type *)UART7_BASE, +}; + +static uart_state_t uart_state[UART_MAX]; + +void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf) { + UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + + // Configure TX/RX pins. + mp_hal_pin_config(tx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, false); + mp_hal_pin_config(rx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, true); + + // Configure the UART peripheral. + select_uart_clock_syst_pclk(uart_id); + enable_uart_clock(uart_id); + uart_software_reset(uart); + uart_enable_fifo(uart); + uart_disable_tx_irq(uart); + uart_disable_rx_irq(uart); + uart_set_baudrate(uart, SYST_PCLK, baudrate); + uart_set_data_parity_stop_bits(uart, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1); + uart_set_flow_control(uart, UART_FLOW_CONTROL_NONE); + uart->UART_FCR |= UART_FCR_RCVR_FIFO_RESET; + uart_set_tx_trigger(uart, UART_TX_FIFO_EMPTY); + uart_set_rx_trigger(uart, UART_RX_ONE_CHAR_IN_FIFO); + + // Initialise the state. + state->status = UART_TRANSFER_STATUS_NONE; + state->rx_ringbuf = rx_ringbuf; + state->tx_src = NULL; + state->tx_src_max = NULL; + state->irq_callback = NULL; + + // Enable interrupts. + NVIC_ClearPendingIRQ(uart_irqn[uart_id]); + NVIC_SetPriority(uart_irqn[uart_id], IRQ_PRI_UART_REPL); + NVIC_EnableIRQ(uart_irqn[uart_id]); + uart_enable_rx_irq(uart); +} + +void mp_uart_deinit(unsigned int uart_id) { + UART_Type *uart = uart_periph[uart_id]; + + uart_disable_rx_irq(uart); + NVIC_DisableIRQ(uart_irqn[uart_id]); +} + +void mp_uart_set_irq_callback(unsigned int uart_id, void (*callback)(void)) { + uart_state_t *state = &uart_state[uart_id]; + state->irq_callback = callback; } -void mp_uart_write_strn(const char *str, size_t len) { - memset(&transfer, 0, sizeof(transfer)); - transfer.tx_buf = (uint8_t *)str; - transfer.tx_total_num = len; - transfer.tx_curr_cnt = 0U; - transfer.status = UART_TRANSFER_STATUS_NONE; +void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts) { + UART_Type *uart = uart_periph[uart_id]; - uart_enable_tx_irq(UART_PTR); + unsigned int flow = UART_FLOW_CONTROL_NONE; + if (rts != NULL) { + flow |= UART_FLOW_CONTROL_RTS; + mp_hal_pin_config(rts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, false); + } + if (cts != NULL) { + flow |= UART_FLOW_CONTROL_CTS; + mp_hal_pin_config(cts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, true); + } + uart_set_flow_control(uart, flow); +} + +void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate) { + UART_Type *uart = uart_periph[uart_id]; + + uart_set_baudrate(uart, SYST_PCLK, baudrate); +} + +size_t mp_uart_rx_any(unsigned int uart_id) { + uart_state_t *state = &uart_state[uart_id]; + if (state->rx_ringbuf != NULL) { + return ringbuf_avail(state->rx_ringbuf); + } + return 0; +} +int mp_uart_rx_char(unsigned int uart_id) { + uart_state_t *state = &uart_state[uart_id]; + if (state->rx_ringbuf != NULL && ringbuf_avail(state->rx_ringbuf)) { + return ringbuf_get(state->rx_ringbuf); + } + return -1; +} + +void mp_uart_tx_data_blocking(unsigned int uart_id, const uint8_t *src, size_t len) { + UART_Type *uart = uart_periph[uart_id]; + + for (size_t i = 0; i < len; ++i) { + for (;;) { + size_t tx_avail = UART_FIFO_DEPTH - uart->UART_TFL; + if (tx_avail > 0) { + break; + } + mp_event_handle_nowait(); + } + uart->UART_THR = src[i]; + } +} + +void mp_uart_tx_data(unsigned int uart_id, const uint8_t *src, size_t len) { + UART_Type *uart = uart_periph[uart_id]; + + // Configure transmission, to be handled by IRQ. + uart_state_t *state = &uart_state[uart_id]; + state->status = UART_TRANSFER_STATUS_NONE; + state->tx_src = src; + state->tx_src_max = src + len; + + // Enable TX IRQ to start transmission. + uart_enable_tx_irq(uart); + + // Wait for transfer to complete. + const uint32_t total_timeout_ms = 100 * len; uint32_t start = mp_hal_ticks_ms(); - while (transfer.status == UART_TRANSFER_STATUS_NONE) { - if (mp_hal_ticks_ms() - start > 10 * len) { + while (state->status == UART_TRANSFER_STATUS_NONE) { + if (mp_hal_ticks_ms() - start > total_timeout_ms) { break; } - __WFE(); + mp_event_wait_indefinite(); } - uart_disable_tx_irq(UART_PTR); + + // Disable TX IRQ. + uart_disable_tx_irq(uart); } -void UART4_IRQHandler(void) { - if (UART_PTR->UART_RFL) { - for (;;) { - uint32_t rx_fifo_available_cnt = UART_PTR->UART_RFL; - if (rx_fifo_available_cnt == 0) { - break; +static void mp_uart_irq_handler(unsigned int uart_id) { + UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + + // Process pending interrupt (below is order of highest to lowest priority). + uint32_t iir = uart->UART_IIR & UART_IIR_INTERRUPT_ID_MASK; + switch (iir) { + case UART_IIR_RECEIVER_LINE_STATUS: { + uint32_t lsr = uart->UART_LSR; + if (lsr & (UART_LSR_RECEIVER_FIFO_ERR | UART_LSR_OVERRUN_ERR)) { + state->status = UART_TRANSFER_STATUS_ERROR; } - for (uint32_t i = 0; i < rx_fifo_available_cnt; ++i) { - int c = UART_PTR->UART_RBR; - #if MICROPY_KBD_EXCEPTION - if (c == mp_interrupt_char) { - mp_sched_keyboard_interrupt(); - continue; + break; + } + + case UART_IIR_RECEIVED_DATA_AVAILABLE: + case UART_IIR_CHARACTER_TIMEOUT: + while (uart->UART_USR & UART_USR_RECEIVE_FIFO_NOT_EMPTY) { + for (uint32_t rfl = uart->UART_RFL; rfl; --rfl) { + int c = uart->UART_RBR; + #if MICROPY_HW_ENABLE_UART_REPL && MICROPY_KBD_EXCEPTION + if (uart_id == MICROPY_HW_UART_REPL) { + if (c == mp_interrupt_char) { + mp_sched_keyboard_interrupt(); + continue; + } + } + #endif + if (state->rx_ringbuf != NULL) { + ringbuf_put(state->rx_ringbuf, c); + } } - #endif - ringbuf_put(&stdin_ringbuf, c); } - } - } else { - uart_irq_handler(UART_PTR, &transfer); + + if (iir == UART_IIR_CHARACTER_TIMEOUT) { + if (state->irq_callback != NULL) { + state->irq_callback(); + } + } + + break; + + case UART_IIR_TRANSMIT_HOLDING_REG_EMPTY: + while (uart->UART_USR & UART_USR_TRANSMIT_FIFO_NOT_FULL) { + if (state->tx_src < state->tx_src_max) { + uart->UART_THR = *state->tx_src++; + } else { + uart_disable_tx_irq(uart); + state->status = UART_TRANSFER_STATUS_SEND_COMPLETE; + break; + } + } + break; + + case UART_IIR_MODEM_STATUS: + (void)uart->UART_MSR; + break; } + __SEV(); } +#define DEFINE_IRQ_HANDLER(id) \ + void UART##id##_IRQHandler(void) { \ + mp_uart_irq_handler(id); \ + } + +DEFINE_IRQ_HANDLER(0) +DEFINE_IRQ_HANDLER(1) +DEFINE_IRQ_HANDLER(2) +DEFINE_IRQ_HANDLER(3) +DEFINE_IRQ_HANDLER(4) +DEFINE_IRQ_HANDLER(5) +DEFINE_IRQ_HANDLER(6) +DEFINE_IRQ_HANDLER(7) + +#if MICROPY_HW_ENABLE_UART_REPL + +#define REPL_BAUDRATE (115200) + +void mp_uart_init_repl(void) { + mp_uart_init(MICROPY_HW_UART_REPL, REPL_BAUDRATE, pin_REPL_UART_TX, pin_REPL_UART_RX, &stdin_ringbuf); +} + +void mp_uart_write_strn_repl(const char *str, size_t len) { + mp_uart_tx_data(MICROPY_HW_UART_REPL, (const uint8_t *)str, len); +} + #endif diff --git a/ports/alif/mpuart.h b/ports/alif/mpuart.h index 0c2e510a94e7c..1c28da4627859 100644 --- a/ports/alif/mpuart.h +++ b/ports/alif/mpuart.h @@ -26,7 +26,20 @@ #ifndef MICROPY_INCLUDED_ALIF2_UART_H #define MICROPY_INCLUDED_ALIF2_UART_H -void mp_uart_init(void); -void mp_uart_write_strn(const char *str, size_t len); +#include "py/ringbuf.h" + +void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf); +void mp_uart_deinit(unsigned int uart_id); + +void mp_uart_set_irq_callback(unsigned int uart_id, void (*callback)(void)); +void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts); +void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate); + +size_t mp_uart_rx_any(unsigned int uart_id); +int mp_uart_rx_char(unsigned int uart_id); +void mp_uart_tx_data(unsigned int uart_id, const uint8_t *src, size_t len); + +void mp_uart_init_repl(void); +void mp_uart_write_strn_repl(const char *str, size_t len); #endif // MICROPY_INCLUDED_ALIF2_UART_H From 526c7eabcec97c5705ce43fe2e9588506c4a936a Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 14 Oct 2024 21:48:51 +1100 Subject: [PATCH 513/546] alif: Integrate lwIP and mbedTLS. Signed-off-by: Damien George --- ports/alif/alif.mk | 7 ++- ports/alif/boards/manifest.py | 1 + ports/alif/lwip_inc/arch/cc.h | 10 ++++ ports/alif/lwip_inc/arch/sys_arch.h | 1 + ports/alif/lwip_inc/lwipopts.h | 60 +++++++++++++++++++++ ports/alif/main.c | 17 ++++++ ports/alif/mbedtls/mbedtls_config_port.h | 41 +++++++++++++++ ports/alif/mbedtls/mbedtls_port.c | 65 +++++++++++++++++++++++ ports/alif/mpconfigport.h | 4 ++ ports/alif/mphalport.c | 30 +++++++++++ ports/alif/mphalport.h | 19 +++++++ ports/alif/mpnetworkport.c | 66 ++++++++++++++++++++++++ 12 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 ports/alif/lwip_inc/arch/cc.h create mode 100644 ports/alif/lwip_inc/arch/sys_arch.h create mode 100644 ports/alif/lwip_inc/lwipopts.h create mode 100644 ports/alif/mbedtls/mbedtls_config_port.h create mode 100644 ports/alif/mbedtls/mbedtls_port.c create mode 100644 ports/alif/mpnetworkport.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index f0ff8d91a53d9..e21fe74c46ec6 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -47,6 +47,7 @@ INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/include/ INC += -I$(ALIF_DFP_REL_HERE)/Device/$(MCU_SERIES)/$(MCU_VARIANT)/ INC += -I$(TOP)/lib/tinyusb/src INC += -Itinyusb_port +INC += -Ilwip_inc GEN_PIN_MKPINS = mcu/make-pins.py GEN_PIN_PREFIX = mcu/pins_prefix.c @@ -122,6 +123,7 @@ SRC_C = \ main.c \ modalif.c \ mphalport.c \ + mpnetworkport.c \ mpu.c \ mpuart.c \ msc_disk.c \ @@ -135,6 +137,10 @@ SRC_C = \ vfs_rom_ioctl.c \ $(wildcard $(BOARD_DIR)/*.c) +ifeq ($(MICROPY_SSL_MBEDTLS),1) +SRC_C += mbedtls/mbedtls_port.c +endif + ifeq ($(MICROPY_FLOAT_IMPL),float) LIBM_SRC_C += $(SRC_LIB_LIBM_C) LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C) @@ -148,7 +154,6 @@ endif SHARED_SRC_C += $(addprefix shared/,\ libc/string0.c \ netutils/dhcpserver.c \ - netutils/netutils.c \ netutils/trace.c \ readline/readline.c \ runtime/gchelper_native.c \ diff --git a/ports/alif/boards/manifest.py b/ports/alif/boards/manifest.py index 1dc9e179acc14..834aa46702731 100644 --- a/ports/alif/boards/manifest.py +++ b/ports/alif/boards/manifest.py @@ -3,3 +3,4 @@ require("dht") require("neopixel") require("onewire") +require("bundle-networking") diff --git a/ports/alif/lwip_inc/arch/cc.h b/ports/alif/lwip_inc/arch/cc.h new file mode 100644 index 0000000000000..35d45afa71c8c --- /dev/null +++ b/ports/alif/lwip_inc/arch/cc.h @@ -0,0 +1,10 @@ +#ifndef MICROPY_INCLUDED_ALIF_LWIP_ARCH_CC_H +#define MICROPY_INCLUDED_ALIF_LWIP_ARCH_CC_H + +#include +#define LWIP_PLATFORM_DIAG(x) +#define LWIP_PLATFORM_ASSERT(x) { assert(1); } + +#define LWIP_NO_CTYPE_H 1 + +#endif // MICROPY_INCLUDED_ALIF_LWIP_ARCH_CC_H diff --git a/ports/alif/lwip_inc/arch/sys_arch.h b/ports/alif/lwip_inc/arch/sys_arch.h new file mode 100644 index 0000000000000..8b1a393741c96 --- /dev/null +++ b/ports/alif/lwip_inc/arch/sys_arch.h @@ -0,0 +1 @@ +// empty diff --git a/ports/alif/lwip_inc/lwipopts.h b/ports/alif/lwip_inc/lwipopts.h new file mode 100644 index 0000000000000..c0622225e10de --- /dev/null +++ b/ports/alif/lwip_inc/lwipopts.h @@ -0,0 +1,60 @@ +#ifndef MICROPY_INCLUDED_ALIF_LWIP_LWIPOPTS_H +#define MICROPY_INCLUDED_ALIF_LWIP_LWIPOPTS_H + +#include + +// This protection is not needed, instead we execute all lwIP code at PendSV priority +#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) +#define SYS_ARCH_PROTECT(lev) do { } while (0) +#define SYS_ARCH_UNPROTECT(lev) do { } while (0) + +#define NO_SYS 1 +#define SYS_LIGHTWEIGHT_PROT 1 +#define MEM_ALIGNMENT 4 + +#define LWIP_CHKSUM_ALGORITHM 3 +#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 + +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_RAW 1 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define LWIP_STATS 0 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETIF_EXT_STATUS_CALLBACK 1 + +#define LWIP_LOOPIF_MULTICAST 1 +#define LWIP_LOOPBACK_MAX_PBUFS 8 + +#define LWIP_IPV6 0 +#define LWIP_DHCP 1 +#define LWIP_DHCP_CHECK_LINK_UP 1 +#define LWIP_DHCP_DOES_ACD_CHECK 0 // to speed DHCP up +#define LWIP_DNS 1 +#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 +#define LWIP_MDNS_RESPONDER 1 +#define LWIP_IGMP 1 + +#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) + +#define SO_REUSE 1 +#define TCP_LISTEN_BACKLOG 1 + +extern uint64_t se_services_rand64(void); +#define LWIP_RAND() se_services_rand64() + +#define MEM_SIZE (16 * 1024) +#define TCP_MSS (1460) +#define TCP_OVERSIZE (TCP_MSS) +#define TCP_WND (8 * TCP_MSS) +#define TCP_SND_BUF (8 * TCP_MSS) +#define TCP_SND_QUEUELEN (2 * (TCP_SND_BUF / TCP_MSS)) +#define TCP_QUEUE_OOSEQ (1) +#define MEMP_NUM_TCP_SEG (2 * TCP_SND_QUEUELEN) + +typedef uint32_t sys_prot_t; + +#endif // MICROPY_INCLUDED_ALIF_LWIP_LWIPOPTS_H diff --git a/ports/alif/main.c b/ports/alif/main.c index 77e413e1eb492..15a77348cf7e3 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -30,6 +30,7 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "py/stackctrl.h" +#include "extmod/modnetwork.h" #include "shared/readline/readline.h" #include "shared/runtime/gchelper.h" #include "shared/runtime/pyexec.h" @@ -42,6 +43,11 @@ #include "se_services.h" #include "system_tick.h" +#if MICROPY_PY_LWIP +#include "lwip/init.h" +#include "lwip/apps/mdns.h" +#endif + extern uint8_t __StackTop, __StackLimit; extern uint8_t __GcHeapStart, __GcHeapEnd; @@ -83,6 +89,17 @@ int main(void) { mp_stack_set_limit(&__StackTop - &__StackLimit - 1024); gc_init(&__GcHeapStart, &__GcHeapEnd); + #if MICROPY_PY_LWIP + // lwIP doesn't allow to reinitialise itself by subsequent calls to this function + // because the system timeout list (next_timeout) is only ever reset by BSS clearing. + // So for now we only init the lwIP stack once on power-up. + lwip_init(); + #if LWIP_MDNS_RESPONDER + mdns_resp_init(); + #endif + mod_network_lwip_init(); + #endif + for (;;) { // Initialise MicroPython runtime. mp_init(); diff --git a/ports/alif/mbedtls/mbedtls_config_port.h b/ports/alif/mbedtls/mbedtls_config_port.h new file mode 100644 index 0000000000000..d9566304d28b9 --- /dev/null +++ b/ports/alif/mbedtls/mbedtls_config_port.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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_MBEDTLS_CONFIG_H +#define MICROPY_INCLUDED_MBEDTLS_CONFIG_H + +// Time hook. +#include +extern time_t alif_mbedtls_time(time_t *timer); +#define MBEDTLS_PLATFORM_TIME_MACRO alif_mbedtls_time +#define MBEDTLS_PLATFORM_MS_TIME_ALT undefined_and_unused + +// Set MicroPython-specific options. +#define MICROPY_MBEDTLS_CONFIG_BARE_METAL (1) + +// Include common mbedtls configuration. +#include "extmod/mbedtls/mbedtls_config_common.h" + +#endif /* MICROPY_INCLUDED_MBEDTLS_CONFIG_H */ diff --git a/ports/alif/mbedtls/mbedtls_port.c b/ports/alif/mbedtls/mbedtls_port.c new file mode 100644 index 0000000000000..a8c155e31ae65 --- /dev/null +++ b/ports/alif/mbedtls/mbedtls_port.c @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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/obj.h" +#include "se_services.h" +#include "mbedtls_config_port.h" + +#if defined(MBEDTLS_HAVE_TIME) +#include "shared/timeutils/timeutils.h" +#include "mbedtls/platform_time.h" +#endif + +int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen) { + uint32_t val = 0; + int n = 0; + *olen = len; + while (len--) { + if (!n) { + val = se_services_rand64(); + n = 4; + } + *output++ = val; + val >>= 8; + --n; + } + return 0; +} + +#if defined(MBEDTLS_HAVE_TIME) + +time_t alif_mbedtls_time(time_t *timer) { + // TODO implement proper RTC time + unsigned int year = 2025; + unsigned int month = 1; + unsigned int date = 1; + unsigned int hours = 12; + unsigned int minutes = 0; + unsigned int seconds = 0; + return timeutils_seconds_since_epoch(year, month, date, hours, minutes, seconds); +} + +#endif diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index a5fd4f6dbfa87..a437c1385cb0c 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -136,6 +136,10 @@ #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_TIMER (1) +#define MICROPY_PY_NETWORK (CORE_M55_HP) +#ifndef MICROPY_PY_NETWORK_HOSTNAME_DEFAULT +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-alif" +#endif #define MICROPY_VFS (1) #define MICROPY_VFS_ROM (1) diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 8f7780bb8acdf..39528a4b9f272 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -38,6 +38,7 @@ #include "tusb.h" #include "mpuart.h" #include "pendsv.h" +#include "se_services.h" #include "system_tick.h" #ifndef MICROPY_HW_STDIN_BUFFER_LEN @@ -237,3 +238,32 @@ void soft_timer_schedule_at_ms(uint32_t ticks_ms) { } #endif + +/*******************************************************************************/ +// MAC address + +// Generate a random locally administered MAC address (LAA) +void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]) { + uint8_t id[8]; + se_services_get_unique_id(id); + buf[0] = 0x02; // LAA range + buf[1] = id[4]; + buf[2] = id[3]; + buf[3] = id[2]; + buf[4] = id[1]; + buf[5] = (id[0] << 2) | idx; +} + +// A board can override this if needed +MP_WEAK void mp_hal_get_mac(int idx, uint8_t buf[6]) { + mp_hal_generate_laa_mac(idx, buf); +} + +void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest) { + static const char hexchr[16] = "0123456789ABCDEF"; + uint8_t mac[6]; + mp_hal_get_mac(idx, mac); + for (; chr_len; ++chr_off, --chr_len) { + *dest++ = hexchr[mac[chr_off >> 1] >> (4 * (1 - (chr_off & 1))) & 0xf]; + } +} diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 5f1e3fff8296e..2ba62db2d2e5a 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -39,6 +39,11 @@ #define MICROPY_PY_PENDSV_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); #define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state); +// Prevent the "lwIP task" from running. +#define MICROPY_PY_LWIP_ENTER MICROPY_PY_PENDSV_ENTER +#define MICROPY_PY_LWIP_REENTER MICROPY_PY_PENDSV_REENTER +#define MICROPY_PY_LWIP_EXIT MICROPY_PY_PENDSV_EXIT + // Port level Wait-for-Event macro // // Do not use this macro directly, include py/runtime.h and @@ -202,3 +207,17 @@ void mp_hal_pin_config(const machine_pin_obj_t *pin, uint32_t mode, // Include all the pin definitions. #include "genhdr/pins_board.h" + +/******************************************************************************/ +// Other HAL functions. + +enum { + MP_HAL_MAC_WLAN0 = 0, + MP_HAL_MAC_WLAN1, + MP_HAL_MAC_BDADDR, + MP_HAL_MAC_ETH0, +}; + +void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]); +void mp_hal_get_mac(int idx, uint8_t buf[6]); +void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest); diff --git a/ports/alif/mpnetworkport.c b/ports/alif/mpnetworkport.c new file mode 100644 index 0000000000000..312fadf08a9c3 --- /dev/null +++ b/ports/alif/mpnetworkport.c @@ -0,0 +1,66 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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 "py/obj.h" + +#if MICROPY_PY_LWIP + +#include "shared/runtime/softtimer.h" +#include "lwip/timeouts.h" + +// Poll lwIP every 64ms by default +#define LWIP_TICK_RATE_MS 64 + +// Soft timer for running lwIP in the background. +static soft_timer_entry_t mp_network_soft_timer; + +u32_t sys_now(void) { + return mp_hal_ticks_ms(); +} + +// This is called by soft_timer and executes at PendSV level. +static void mp_network_soft_timer_callback(soft_timer_entry_t *self) { + // Run the lwIP internal updates. + sys_check_timeouts(); + + #if LWIP_NETIF_LOOPBACK + netif_poll_all(); + #endif +} + +void mod_network_lwip_init(void) { + soft_timer_static_init( + &mp_network_soft_timer, + SOFT_TIMER_MODE_PERIODIC, + LWIP_TICK_RATE_MS, + mp_network_soft_timer_callback + ); + + soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS); +} + +#endif // MICROPY_PY_LWIP From d6e33423da471ddcba91632f032b18c5a79e9880 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 10 Mar 2025 14:09:04 +1100 Subject: [PATCH 514/546] alif: Integrate cyw43 WLAN driver. Signed-off-by: Damien George --- ports/alif/alif.mk | 3 +- ports/alif/cyw43_configport.h | 123 ++++++++++++++++++++++++++++ ports/alif/cyw43_port_spi.c | 150 ++++++++++++++++++++++++++++++++++ ports/alif/irq.h | 1 + ports/alif/main.c | 15 ++++ ports/alif/mpconfigport.h | 15 ++++ ports/alif/mpnetworkport.c | 14 ++++ ports/alif/pendsv.h | 3 + 8 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 ports/alif/cyw43_configport.h create mode 100644 ports/alif/cyw43_port_spi.c diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index e21fe74c46ec6..179df67a58c30 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -115,6 +115,7 @@ SRC_O += \ SRC_C = \ alif_flash.c \ + cyw43_port_spi.c \ fatfs_port.c \ machine_pin.c \ machine_i2c.c \ @@ -196,12 +197,12 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ drivers/source/adc.c \ drivers/source/i2c.c \ - drivers/source/spi.c \ drivers/source/mhu_driver.c \ drivers/source/mhu_receiver.c \ drivers/source/mhu_sender.c \ drivers/source/mram.c \ drivers/source/pinconf.c \ + drivers/source/spi.c \ drivers/source/uart.c \ drivers/source/utimer.c \ ospi_xip/source/ospi/ospi_drv.c \ diff --git a/ports/alif/cyw43_configport.h b/ports/alif/cyw43_configport.h new file mode 100644 index 0000000000000..b7e4ad702a599 --- /dev/null +++ b/ports/alif/cyw43_configport.h @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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_ALIF_CYW43_CONFIGPORT_H +#define MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H + +// The board-level config will be included here, so it can set some CYW43 values. +#include "py/mpconfig.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "extmod/modnetwork.h" +#include "pendsv.h" + +#ifndef static_assert +#define static_assert(expr, msg) typedef int static_assert_##__LINE__[(expr) ? 1 : -1] +#endif + +#define CYW43_USE_SPI (1) +#define CYW43_LWIP (1) +#define CYW43_USE_STATS (0) +#define CYW43_PRINTF(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) + +#if 0 +#define CYW43_VERBOSE_DEBUG (1) +#define CYW43_VDEBUG(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#define CYW43_DEBUG(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#define CYW43_INFO(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#define CYW43_WARN(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#endif + +#define CYW43_IOCTL_TIMEOUT_US (1000000) +#define CYW43_SLEEP_MAX (50) +#define CYW43_NETUTILS (1) +#define CYW43_CLEAR_SDIO_INT (1) + +#define CYW43_EPERM MP_EPERM // Operation not permitted +#define CYW43_EIO MP_EIO // I/O error +#define CYW43_EINVAL MP_EINVAL // Invalid argument +#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out + +#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER +#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT +#define CYW43_THREAD_LOCK_CHECK + +#define CYW43_HOST_NAME mod_network_hostname_data + +#define CYW43_SDPCM_SEND_COMMON_WAIT __WFE() +#define CYW43_DO_IOCTL_WAIT // __WFE() +#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait() + +#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) + +#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT +#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT +#define CYW43_HAL_PIN_PULL_NONE 0 + +#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 + +#define cyw43_hal_ticks_us mp_hal_ticks_us +#define cyw43_hal_ticks_ms mp_hal_ticks_ms + +#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t +#define cyw43_hal_pin_read mp_hal_pin_read +#define cyw43_hal_pin_low mp_hal_pin_low +#define cyw43_hal_pin_high mp_hal_pin_high + +#define cyw43_hal_get_mac mp_hal_get_mac +#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii +#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac + +#define CYW43_PIN_WL_REG_ON pin_WL_REG_ON +#define CYW43_PIN_WL_IRQ pin_WL_IRQ + +#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) + +void cyw43_post_poll_hook(void); + +static inline void cyw43_delay_us(uint32_t us) { + uint32_t start = mp_hal_ticks_us(); + while (mp_hal_ticks_us() - start < us) { + } +} + +static inline void cyw43_delay_ms(uint32_t ms) { + mp_hal_delay_ms(ms); +} + +static inline void cyw43_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, uint32_t alt) { + if (mode == MP_HAL_PIN_MODE_INPUT) { + mp_hal_pin_input(pin); + } else { + mp_hal_pin_output(pin); + } +} + +static inline void cyw43_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool enable) { + mp_hal_pin_config_irq_falling(pin, enable); +} + +#endif // MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H diff --git a/ports/alif/cyw43_port_spi.c b/ports/alif/cyw43_port_spi.c new file mode 100644 index 0000000000000..84d84b0e1de48 --- /dev/null +++ b/ports/alif/cyw43_port_spi.c @@ -0,0 +1,150 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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. + */ + +#if MICROPY_PY_NETWORK_CYW43 + +#include "lib/cyw43-driver/src/cyw43.h" +#include "lib/cyw43-driver/src/cyw43_internal.h" +#include "lib/cyw43-driver/src/cyw43_spi.h" + +#include "spi.h" +#include "sys_ctrl_spi.h" + +// CYW43 is connected to SPI3. +#define HW_SPI ((SPI_Type *)SPI3_BASE) +#define SPI_BAUDRATE (16000000) +#define SPI_RX_FIFO_SIZE (16) + +// WL_IRQ is on P9_6. +#define WL_IRQ_IRQN (GPIO9_IRQ6_IRQn) +#define WL_IRQ_HANDLER GPIO9_IRQ6Handler + +// Must run at IRQ priority above PendSV so it can wake cyw43-driver when PendSV is disabled. +void WL_IRQ_HANDLER(void) { + if (gpio_read_int_rawstatus(pin_WL_IRQ->gpio, pin_WL_IRQ->pin)) { + gpio_interrupt_eoi(pin_WL_IRQ->gpio, pin_WL_IRQ->pin); + pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, cyw43_poll); + __SEV(); + } +} + +static void spi_bus_init(void) { + // Configure pins. + mp_hal_pin_output(pin_WL_CS); + mp_hal_pin_high(pin_WL_CS); + // NOTE: Alif recommends enabled input read for all SPI pins. + mp_hal_pin_config(pin_WL_SCLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + mp_hal_pin_config(pin_WL_MOSI, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + mp_hal_pin_config(pin_WL_MISO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + + // Starts out clock_polarity=1, clock_phase=0. + spi_mode_master(HW_SPI); + spi_set_bus_speed(HW_SPI, SPI_BAUDRATE, GetSystemAHBClock()); + spi_set_mode(HW_SPI, SPI_MODE_2); + spi_set_protocol(HW_SPI, SPI_PROTO_SPI); + spi_set_dfs(HW_SPI, 8); + spi_set_tmod(HW_SPI, SPI_TMOD_TX_AND_RX); + spi_control_ss(HW_SPI, 0, SPI_SS_STATE_ENABLE); +} + +int cyw43_spi_init(cyw43_int_t *self) { + spi_bus_init(); + + // Configure IRQ for WL_IRQ (active low input). + NVIC_SetPriority(WL_IRQ_IRQN, IRQ_PRI_CYW43); + NVIC_ClearPendingIRQ(WL_IRQ_IRQN); + NVIC_EnableIRQ(WL_IRQ_IRQN); + + return 0; +} + +void cyw43_spi_deinit(cyw43_int_t *self) { + // Disable clock, SS and SPI. + spi_mask_interrupts(HW_SPI); + spi_control_ss(HW_SPI, 0, 0); + spi_disable(HW_SPI); + + // Disable SPI IRQ. + NVIC_DisableIRQ(WL_IRQ_IRQN); + NVIC_ClearPendingIRQ(WL_IRQ_IRQN); +} + +void cyw43_spi_gpio_setup(void) { +} + +void cyw43_spi_reset(void) { +} + +void cyw43_spi_set_polarity(cyw43_int_t *self, int pol) { + (void)self; + + if (pol == 0) { + spi_set_mode(HW_SPI, SPI_MODE_0); + } else { + spi_set_mode(HW_SPI, SPI_MODE_2); + } +} + +// tx must not be NULL. +// rx_len must be 0, or the same as tx_len. +int cyw43_spi_transfer(cyw43_int_t *self, const uint8_t *tx, size_t tx_len, uint8_t *rx, size_t rx_len) { + (void)self; + + if (tx_len == 0 && rx_len == 0) { + return 0; + } + + mp_hal_pin_low(pin_WL_CS); + + // Must read the same amount of data that is written out to SPI. + rx_len = tx_len; + + while (tx_len || rx_len) { + // Only add data to the TX FIFO if: + // - there's data to add + // - and TX is not ahead of RX by more than the RX FIFO size + // - and there's space in the TX FIFO + if (tx_len && tx_len + SPI_RX_FIFO_SIZE > rx_len && (HW_SPI->SPI_SR & SPI_SR_TFNF) != 0) { + HW_SPI->SPI_DR[0] = *tx++; + --tx_len; + } + + // Take data from the RX FIFO and store it into the output buffer (if given). + if (rx_len && (HW_SPI->SPI_SR & SPI_SR_RFNE) != 0) { + uint8_t data = HW_SPI->SPI_DR[0]; + if (rx != NULL) { + *rx++ = data; + } + --rx_len; + } + } + + mp_hal_pin_high(pin_WL_CS); + + return 0; +} + +#endif diff --git a/ports/alif/irq.h b/ports/alif/irq.h index ed454adcb4657..08b8ef8086bc3 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -49,6 +49,7 @@ #define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0) #define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0) #define IRQ_PRI_RTC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 100, 0) +#define IRQ_PRI_CYW43 NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 126, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) // these states correspond to values from query_irq, enable_irq and disable_irq diff --git a/ports/alif/main.c b/ports/alif/main.c index 15a77348cf7e3..3bff8cf4ae9f8 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -47,6 +47,9 @@ #include "lwip/init.h" #include "lwip/apps/mdns.h" #endif +#if MICROPY_PY_NETWORK_CYW43 +#include "lib/cyw43-driver/src/cyw43.h" +#endif extern uint8_t __StackTop, __StackLimit; extern uint8_t __GcHeapStart, __GcHeapEnd; @@ -100,6 +103,18 @@ int main(void) { mod_network_lwip_init(); #endif + #if MICROPY_PY_NETWORK_CYW43 + { + cyw43_init(&cyw43_state); + uint8_t buf[8]; + memcpy(&buf[0], "ALIF", 4); + mp_hal_get_mac_ascii(MP_HAL_MAC_WLAN0, 8, 4, (char *)&buf[4]); + cyw43_wifi_ap_set_ssid(&cyw43_state, 8, buf); + cyw43_wifi_ap_set_auth(&cyw43_state, CYW43_AUTH_WPA2_AES_PSK); + cyw43_wifi_ap_set_password(&cyw43_state, 8, (const uint8_t *)"alif0123"); + } + #endif + for (;;) { // Initialise MicroPython runtime. mp_init(); diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index a437c1385cb0c..76b603027fcf1 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -155,6 +155,21 @@ #define MICROPY_FATFS_MAX_SS (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES) #endif +#if MICROPY_PY_NETWORK_CYW43 +extern const struct _mp_obj_type_t mp_network_cyw43_type; +#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, +#else +#define MICROPY_HW_NIC_CYW43 +#endif + +#ifndef MICROPY_BOARD_NETWORK_INTERFACES +#define MICROPY_BOARD_NETWORK_INTERFACES +#endif + +#define MICROPY_PORT_NETWORK_INTERFACES \ + MICROPY_HW_NIC_CYW43 \ + MICROPY_BOARD_NETWORK_INTERFACES \ + #define MP_STATE_PORT MP_STATE_VM // Miscellaneous settings diff --git a/ports/alif/mpnetworkport.c b/ports/alif/mpnetworkport.c index 312fadf08a9c3..40def7f7fd7e9 100644 --- a/ports/alif/mpnetworkport.c +++ b/ports/alif/mpnetworkport.c @@ -32,6 +32,10 @@ #include "shared/runtime/softtimer.h" #include "lwip/timeouts.h" +#if MICROPY_PY_NETWORK_CYW43 +#include "lib/cyw43-driver/src/cyw43.h" +#endif + // Poll lwIP every 64ms by default #define LWIP_TICK_RATE_MS 64 @@ -50,6 +54,16 @@ static void mp_network_soft_timer_callback(soft_timer_entry_t *self) { #if LWIP_NETIF_LOOPBACK netif_poll_all(); #endif + + #if MICROPY_PY_NETWORK_CYW43 + if (cyw43_poll) { + if (cyw43_sleep != 0) { + if (--cyw43_sleep == 0) { + cyw43_poll(); + } + } + } + #endif } void mod_network_lwip_init(void) { diff --git a/ports/alif/pendsv.h b/ports/alif/pendsv.h index 43a4b7a853846..17d7e82dfc7e7 100644 --- a/ports/alif/pendsv.h +++ b/ports/alif/pendsv.h @@ -34,6 +34,9 @@ enum { PENDSV_DISPATCH_SOFT_TIMER, + #if MICROPY_PY_NETWORK_CYW43 + PENDSV_DISPATCH_CYW43, + #endif MICROPY_BOARD_PENDSV_ENTRIES PENDSV_DISPATCH_MAX }; From 30dfbe5dc0d8a8ce4af3724b9b2a49b60a323a31 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 10 Mar 2025 13:57:48 +1100 Subject: [PATCH 515/546] alif: Integrate cyw43 Bluetooth with NimBLE. Signed-off-by: Damien George --- ports/alif/alif.mk | 4 + ports/alif/cyw43_configport.h | 18 +++++ ports/alif/main.c | 9 +++ ports/alif/mpbthciport.c | 140 ++++++++++++++++++++++++++++++++++ ports/alif/mpbthciport.h | 46 +++++++++++ ports/alif/mpconfigport.h | 6 ++ ports/alif/mpnimbleport.c | 78 +++++++++++++++++++ ports/alif/mpnimbleport.h | 32 ++++++++ 8 files changed, 333 insertions(+) create mode 100644 ports/alif/mpbthciport.c create mode 100644 ports/alif/mpbthciport.h create mode 100644 ports/alif/mpnimbleport.c create mode 100644 ports/alif/mpnimbleport.h diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 179df67a58c30..eee27b3b7d698 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -142,6 +142,10 @@ ifeq ($(MICROPY_SSL_MBEDTLS),1) SRC_C += mbedtls/mbedtls_port.c endif +ifeq ($(MICROPY_PY_BLUETOOTH),1) +SRC_C += mpbthciport.c mpnimbleport.c +endif + ifeq ($(MICROPY_FLOAT_IMPL),float) LIBM_SRC_C += $(SRC_LIB_LIBM_C) LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C) diff --git a/ports/alif/cyw43_configport.h b/ports/alif/cyw43_configport.h index b7e4ad702a599..4b5cce67132e9 100644 --- a/ports/alif/cyw43_configport.h +++ b/ports/alif/cyw43_configport.h @@ -32,6 +32,7 @@ #include "py/mphal.h" #include "py/runtime.h" #include "extmod/modnetwork.h" +#include "extmod/mpbthci.h" #include "pendsv.h" #ifndef static_assert @@ -39,6 +40,7 @@ #endif #define CYW43_USE_SPI (1) +#define CYW43_ENABLE_BLUETOOTH_OVER_UART (1) #define CYW43_LWIP (1) #define CYW43_USE_STATS (0) #define CYW43_PRINTF(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) @@ -78,6 +80,7 @@ #define CYW43_HAL_PIN_PULL_NONE 0 #define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 +#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR #define cyw43_hal_ticks_us mp_hal_ticks_us #define cyw43_hal_ticks_ms mp_hal_ticks_ms @@ -87,6 +90,10 @@ #define cyw43_hal_pin_low mp_hal_pin_low #define cyw43_hal_pin_high mp_hal_pin_high +#define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate +#define cyw43_hal_uart_write mp_bluetooth_hci_uart_write +#define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar + #define cyw43_hal_get_mac mp_hal_get_mac #define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii #define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac @@ -94,6 +101,11 @@ #define CYW43_PIN_WL_REG_ON pin_WL_REG_ON #define CYW43_PIN_WL_IRQ pin_WL_IRQ +#define CYW43_PIN_BT_REG_ON pin_BT_REG_ON +#define CYW43_PIN_BT_HOST_WAKE pin_BT_HOST_WAKE +#define CYW43_PIN_BT_DEV_WAKE pin_BT_DEV_WAKE +#define CYW43_PIN_BT_CTS pin_BT_UART_CTS + #define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) void cyw43_post_poll_hook(void); @@ -120,4 +132,10 @@ static inline void cyw43_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool e mp_hal_pin_config_irq_falling(pin, enable); } +#define cyw43_bluetooth_controller_init mp_bluetooth_hci_controller_init +#define cyw43_bluetooth_controller_deinit mp_bluetooth_hci_controller_deinit +#define cyw43_bluetooth_controller_sleep_maybe mp_bluetooth_hci_controller_sleep_maybe +#define cyw43_bluetooth_controller_woken mp_bluetooth_hci_controller_woken +#define cyw43_bluetooth_controller_wakeup mp_bluetooth_hci_controller_wakeup + #endif // MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H diff --git a/ports/alif/main.c b/ports/alif/main.c index 3bff8cf4ae9f8..fd35604cbc234 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -30,6 +30,7 @@ #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" #include "shared/runtime/gchelper.h" @@ -37,6 +38,7 @@ #include "shared/runtime/softtimer.h" #include "shared/tinyusb/mp_usbd.h" #include "tusb.h" +#include "mpbthciport.h" #include "mpuart.h" #include "ospi_flash.h" #include "pendsv.h" @@ -103,6 +105,10 @@ int main(void) { mod_network_lwip_init(); #endif + #if MICROPY_PY_BLUETOOTH + mp_bluetooth_hci_init(); + #endif + #if MICROPY_PY_NETWORK_CYW43 { cyw43_init(&cyw43_state); @@ -154,6 +160,9 @@ int main(void) { soft_reset_exit: mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); + #if MICROPY_PY_BLUETOOTH + mp_bluetooth_deinit(); + #endif soft_timer_deinit(); gc_sweep_all(); mp_deinit(); diff --git a/ports/alif/mpbthciport.c b/ports/alif/mpbthciport.c new file mode 100644 index 0000000000000..a37c9621564a1 --- /dev/null +++ b/ports/alif/mpbthciport.c @@ -0,0 +1,140 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 OpenMV LLC. + * + * 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 "py/runtime.h" +#include "extmod/mpbthci.h" +#include "shared/runtime/softtimer.h" +#include "mpbthciport.h" + +#if MICROPY_PY_BLUETOOTH + +uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + +// Soft timer and scheduling node for scheduling a HCI poll. +static soft_timer_entry_t mp_bluetooth_hci_soft_timer; +static mp_sched_node_t mp_bluetooth_hci_sched_node; + +// This is called by soft_timer and executes at IRQ_PRI_PENDSV. +static void mp_bluetooth_hci_soft_timer_callback(soft_timer_entry_t *self) { + mp_bluetooth_hci_poll_now(); +} + +void mp_bluetooth_hci_init(void) { + soft_timer_static_init( + &mp_bluetooth_hci_soft_timer, + SOFT_TIMER_MODE_ONE_SHOT, + 0, + mp_bluetooth_hci_soft_timer_callback + ); +} + +static void mp_bluetooth_hci_start_polling(void) { + mp_bluetooth_hci_poll_now(); +} + +void mp_bluetooth_hci_poll_in_ms(uint32_t ms) { + soft_timer_reinsert(&mp_bluetooth_hci_soft_timer, ms); +} + +// For synchronous mode, we run all BLE stack code inside a scheduled task. +// This task is scheduled periodically via a timer, or immediately after UART RX IRQ. +static void run_events_scheduled_task(mp_sched_node_t *node) { + (void)node; + // This will process all buffered HCI UART data, and run any callouts or events. + mp_bluetooth_hci_poll(); +} + +// Called periodically (systick) or directly (e.g. UART RX IRQ) in order to +// request that processing happens ASAP in the scheduler. +void mp_bluetooth_hci_poll_now(void) { + mp_sched_schedule_node(&mp_bluetooth_hci_sched_node, run_events_scheduled_task); +} + +/******************************************************************************/ +// HCI over UART + +#include "mpuart.h" + +static uint32_t hci_uart_id; +static bool hci_uart_first_char; +static uint8_t hci_rx_ringbuf_array[768]; +static ringbuf_t hci_rx_ringbuf = { + .buf = hci_rx_ringbuf_array, + .size = sizeof(hci_rx_ringbuf_array), + .iget = 0, + .iput = 0, +}; + +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { + hci_uart_id = port; + hci_uart_first_char = true; + + // Initialise the UART. + mp_uart_init(hci_uart_id, baudrate, pin_BT_UART_TX, pin_BT_UART_RX, &hci_rx_ringbuf); + mp_uart_set_flow(hci_uart_id, pin_BT_UART_RTS, pin_BT_UART_CTS); + mp_uart_set_irq_callback(hci_uart_id, mp_bluetooth_hci_poll_now); + + // Start the HCI polling to process any initial events/packets. + mp_bluetooth_hci_start_polling(); + + return 0; +} + +int mp_bluetooth_hci_uart_deinit(void) { + mp_uart_deinit(hci_uart_id); + return 0; +} + +int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { + mp_uart_set_baudrate(hci_uart_id, baudrate); + return 0; +} + +int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { + mp_bluetooth_hci_controller_wakeup(); + mp_uart_tx_data(hci_uart_id, (void *)buf, len); + return 0; +} + +// This function expects the controller to be in the wake state via a previous call +// to mp_bluetooth_hci_controller_woken. +int mp_bluetooth_hci_uart_readchar(void) { + if (mp_uart_rx_any(hci_uart_id)) { + int c = mp_uart_rx_char(hci_uart_id); + if (hci_uart_first_char) { + if (c == 0) { + return -1; + } + hci_uart_first_char = false; + } + return c; + } else { + return -1; + } +} + +#endif // MICROPY_PY_BLUETOOTH diff --git a/ports/alif/mpbthciport.h b/ports/alif/mpbthciport.h new file mode 100644 index 0000000000000..65e9d1752a798 --- /dev/null +++ b/ports/alif/mpbthciport.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 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. + */ +#ifndef MICROPY_INCLUDED_ALIF_MPBTHCIPORT_H +#define MICROPY_INCLUDED_ALIF_MPBTHCIPORT_H + +#include "py/mpconfig.h" + +// Initialise the HCI subsystem (should be called once, early on). +void mp_bluetooth_hci_init(void); + +// Call this to poll the HCI now. +void mp_bluetooth_hci_poll_now(void); + +// Call this to poll the HCI after a certain timeout. +void mp_bluetooth_hci_poll_in_ms(uint32_t ms); + +// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c). +// Request new data from the uart and pass to the stack, and run pending events/callouts. +// This is a low-level function and should not be called directly, use +// mp_bluetooth_hci_poll_now/mp_bluetooth_hci_poll_in_ms instead. +void mp_bluetooth_hci_poll(void); + +#endif // MICROPY_INCLUDED_ALIF_MPBTHCIPORT_H diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 76b603027fcf1..73abb8924c393 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -170,6 +170,12 @@ extern const struct _mp_obj_type_t mp_network_cyw43_type; MICROPY_HW_NIC_CYW43 \ MICROPY_BOARD_NETWORK_INTERFACES \ +// Bluetooth code only runs in the scheduler, no locking/mutex required. +#define MICROPY_PY_BLUETOOTH_ENTER uint32_t atomic_state = 0; +#define MICROPY_PY_BLUETOOTH_EXIT (void)atomic_state; +#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1) +#define MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS (1) + #define MP_STATE_PORT MP_STATE_VM // Miscellaneous settings diff --git a/ports/alif/mpnimbleport.c b/ports/alif/mpnimbleport.c new file mode 100644 index 0000000000000..8da5ce293c9bd --- /dev/null +++ b/ports/alif/mpnimbleport.c @@ -0,0 +1,78 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jim Mussared + * Copyright (c) 2020 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/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE + +#define DEBUG_printf(...) // printf("mpnimbleport.c: " __VA_ARGS__) + +#include "host/ble_hs.h" +#include "nimble/nimble_npl.h" + +#include "extmod/mpbthci.h" +#include "extmod/modbluetooth.h" +#include "extmod/nimble/modbluetooth_nimble.h" +#include "extmod/nimble/hal/hal_uart.h" +#include "mpbthciport.h" + +// Get any pending data from the UART and send it to NimBLE's HCI buffers. +// Any further processing by NimBLE will be run via its event queue. +void mp_bluetooth_hci_poll(void) { + if (mp_bluetooth_nimble_ble_state >= MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC) { + // DEBUG_printf("mp_bluetooth_hci_poll_uart %d\n", mp_bluetooth_nimble_ble_state); + + // Run any timers. + mp_bluetooth_nimble_os_callout_process(); + + // Process incoming UART data, and run events as they are generated. + mp_bluetooth_nimble_hci_uart_process(true); + + // Run any remaining events (e.g. if there was no UART data). + mp_bluetooth_nimble_os_eventq_run_all(); + } + + if (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { + // Call this function again in 128ms to check for new events. + // TODO: improve this by only calling back when needed. + mp_bluetooth_hci_poll_in_ms(128); + } +} + +// --- Port-specific helpers for the generic NimBLE bindings. ----------------- + +void mp_bluetooth_nimble_hci_uart_wfi(void) { + __WFE(); + + // This is called while NimBLE is waiting in ble_npl_sem_pend, i.e. waiting for an HCI ACK. + // Do not need to run events here (it must not invoke Python code), only processing incoming HCI data. + mp_bluetooth_nimble_hci_uart_process(false); +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/ports/alif/mpnimbleport.h b/ports/alif/mpnimbleport.h new file mode 100644 index 0000000000000..150ec42da76d7 --- /dev/null +++ b/ports/alif/mpnimbleport.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_ALIF_NIMBLE_PORT_H +#define MICROPY_INCLUDED_ALIF_NIMBLE_PORT_H + +// To allow MICROPY_HW_BLE_UART_ID to be resolved. + +#endif // MICROPY_INCLUDED_ALIF_NIMBLE_PORT_H From da46b4d7089e6b0be553798568bbdf9e7ea9368b Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 13 Mar 2025 14:54:49 +0100 Subject: [PATCH 516/546] alif/mcu: Remove json config files. They will be generated as part of the build. Signed-off-by: iabdalkader --- ports/alif/mcu/M55_DUAL_cfg.json | 26 -------------------------- ports/alif/mcu/M55_HE_cfg.json | 17 ----------------- ports/alif/mcu/M55_HP_cfg.json | 17 ----------------- 3 files changed, 60 deletions(-) delete mode 100644 ports/alif/mcu/M55_DUAL_cfg.json delete mode 100644 ports/alif/mcu/M55_HE_cfg.json delete mode 100644 ports/alif/mcu/M55_HP_cfg.json diff --git a/ports/alif/mcu/M55_DUAL_cfg.json b/ports/alif/mcu/M55_DUAL_cfg.json deleted file mode 100644 index 81beef5f03614..0000000000000 --- a/ports/alif/mcu/M55_DUAL_cfg.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "DEVICE": { - "disabled" : false, - "binary": "app-device-config.json", - "version" : "0.5.00", - "signed": false - }, - "HP_APP": { - "disabled" : false, - "binary": "M55_HP/firmware.bin", - "mramAddress": "0x80020000", - "version": "1.0.0", - "cpu_id": "M55_HP", - "flags": ["boot"], - "signed": false - }, - "HE_APP": { - "disabled" : false, - "binary": "M55_HE/firmware.bin", - "mramAddress": "0x80320000", - "version": "1.0.0", - "cpu_id": "M55_HE", - "flags": ["deferred"], - "signed": false - } -} diff --git a/ports/alif/mcu/M55_HE_cfg.json b/ports/alif/mcu/M55_HE_cfg.json deleted file mode 100644 index 83898418043ec..0000000000000 --- a/ports/alif/mcu/M55_HE_cfg.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "DEVICE": { - "disabled" : false, - "binary": "app-device-config.json", - "version" : "0.5.00", - "signed": false - }, - "HE_APP": { - "disabled" : false, - "binary": "M55_HE/firmware.bin", - "mramAddress": "0x80320000", - "version": "1.0.0", - "cpu_id": "M55_HE", - "flags": ["boot"], - "signed": false - } -} diff --git a/ports/alif/mcu/M55_HP_cfg.json b/ports/alif/mcu/M55_HP_cfg.json deleted file mode 100644 index d6710f69c77b6..0000000000000 --- a/ports/alif/mcu/M55_HP_cfg.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "DEVICE": { - "disabled" : false, - "binary": "app-device-config.json", - "version" : "0.5.00", - "signed": false - }, - "HP_APP": { - "disabled" : false, - "binary": "M55_HP/firmware.bin", - "mramAddress": "0x80020000", - "version": "1.0.0", - "cpu_id": "M55_HP", - "flags": ["boot"], - "signed": false - } -} From 19a4689c6b0b652f8d50be6b658adc649649d5d1 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 13 Mar 2025 14:55:19 +0100 Subject: [PATCH 517/546] alif/mcu: Pre-process Alif ToC config file. Signed-off-by: iabdalkader --- ports/alif/Makefile | 24 +++++++++++------ ports/alif/mcu/alif_cfg.json.in | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 ports/alif/mcu/alif_cfg.json.in diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 7d6731dacf200..68706674365c1 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -20,10 +20,14 @@ JLINK_CMD_SUFFIX = \ Reset\n\ Exit +ALIF_TOC_CONFIG = alif_cfg.json +ALIF_TOC_APPS = $(BUILD)/$(ALIF_TOC_CONFIG) +ALIF_TOC_CFLAGS += -DTOC_CFG_FILE=$(ALIF_TOOLKIT_CFG_FILE) + ifeq ($(MCU_CORE),M55_HP) -ALIF_TOC_CONFIG = mcu/M55_HP_cfg.json -ALIF_TOC_APPS = $(BUILD)/M55_HP/firmware.bin +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HP_APP=1 +ALIF_TOC_APPS += $(BUILD)/M55_HP/firmware.bin JLINK_CMD = '\ $(JLINK_CMD_PREFIX)\ LoadFile "$(BUILD)/M55_HP/firmware.bin",0x80020000\n\ @@ -31,8 +35,8 @@ $(JLINK_CMD_SUFFIX)' else ifeq ($(MCU_CORE),M55_HE) -ALIF_TOC_CONFIG = mcu/M55_HE_cfg.json -ALIF_TOC_APPS = $(BUILD)/M55_HE/firmware.bin +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HE_APP=1 +ALIF_TOC_APPS += $(BUILD)/M55_HE/firmware.bin JLINK_CMD = '\ $(JLINK_CMD_PREFIX)\ LoadFile "$(BUILD)/M55_HE/firmware.bin",0x80320000\n\ @@ -40,8 +44,9 @@ $(JLINK_CMD_SUFFIX)' else ifeq ($(MCU_CORE),M55_DUAL) -ALIF_TOC_CONFIG = mcu/M55_DUAL_cfg.json -ALIF_TOC_APPS = $(BUILD)/M55_HP/firmware.bin $(BUILD)/M55_HE/firmware.bin +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HP_APP=1 +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HE_APP=1 +ALIF_TOC_APPS += $(BUILD)/M55_HP/firmware.bin $(BUILD)/M55_HE/firmware.bin JLINK_CMD = '\ $(JLINK_CMD_PREFIX)\ LoadFile "$(BUILD)/M55_HP/firmware.bin",0x80020000\n\ @@ -81,10 +86,13 @@ $(BUILD)/M55_HP/firmware.bin: $(BUILD)/M55_HE/firmware.bin: make -f alif.mk MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1 +$(BUILD)/$(ALIF_TOC_CONFIG): mcu/$(ALIF_TOC_CONFIG).in | $(BUILD) + $(ECHO) "Preprocess toc config $@" + $(Q)$(CPP) -P -E $(ALIF_TOC_CFLAGS) - < mcu/$(ALIF_TOC_CONFIG).in > $@ + $(BUILD)/firmware.toc.bin: $(ALIF_TOC_APPS) $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \ - --filename $(abspath $(ALIF_TOC_CONFIG)) \ - --config-dir $(BOARD_DIR) \ + --filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \ --output-dir $(BUILD) \ --firmware-dir $(BUILD) \ --output $@ diff --git a/ports/alif/mcu/alif_cfg.json.in b/ports/alif/mcu/alif_cfg.json.in new file mode 100644 index 0000000000000..7760bd91d2e43 --- /dev/null +++ b/ports/alif/mcu/alif_cfg.json.in @@ -0,0 +1,47 @@ +{ + "DEVICE": { + "disabled" : false, + "binary": TOC_CFG_FILE, + "version" : "0.5.00", + "signed": false + }, + "HP_BOOT": { + #if TOC_CORE_M55_HP_BOOT + "disabled" : false, + #else + "disabled" : true, + #endif + "binary": "bootloader.bin", + "mramAddress": "0x80000000", + "version": "1.0.0", + "cpu_id": "M55_HP", + "flags": ["boot"], + "signed": false + }, + "HP_APP": { + #if TOC_CORE_M55_HP_APP + "disabled" : false, + #else + "disabled" : true, + #endif + "binary": "M55_HP/firmware.bin", + "mramAddress": "0x80020000", + "version": "1.0.0", + "cpu_id": "M55_HP", + "flags": ["boot"], + "signed": false + }, + "HE_APP": { + #if TOC_CORE_M55_HE_APP + "disabled" : false, + #else + "disabled" : true, + #endif + "binary": "M55_HE/firmware.bin", + "mramAddress": "0x80320000", + "version": "1.0.0", + "cpu_id": "M55_HE", + "flags": ["deferred"], + "signed": false + } +} From 293e8db9d739c4e32420a48304ac9d2251e63570 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 17 Mar 2025 13:19:07 +1100 Subject: [PATCH 518/546] alif/mpuart: Enhance UART to support bits/parity/stop and more IRQs. Signed-off-by: Damien George --- ports/alif/mpbthciport.c | 10 ++++- ports/alif/mpuart.c | 85 ++++++++++++++++++++++++++++++++++------ ports/alif/mpuart.h | 14 ++++++- 3 files changed, 92 insertions(+), 17 deletions(-) diff --git a/ports/alif/mpbthciport.c b/ports/alif/mpbthciport.c index a37c9621564a1..10a0505fe02d8 100644 --- a/ports/alif/mpbthciport.c +++ b/ports/alif/mpbthciport.c @@ -89,14 +89,20 @@ static ringbuf_t hci_rx_ringbuf = { .iput = 0, }; +static void mp_bluetooth_hci_uart_irq_callback(unsigned int uart_id, unsigned int trigger) { + if (trigger == MP_UART_IRQ_RXIDLE) { + mp_bluetooth_hci_poll_now(); + } +} + int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { hci_uart_id = port; hci_uart_first_char = true; // Initialise the UART. - mp_uart_init(hci_uart_id, baudrate, pin_BT_UART_TX, pin_BT_UART_RX, &hci_rx_ringbuf); + mp_uart_init(hci_uart_id, baudrate, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1, pin_BT_UART_TX, pin_BT_UART_RX, &hci_rx_ringbuf); mp_uart_set_flow(hci_uart_id, pin_BT_UART_RTS, pin_BT_UART_CTS); - mp_uart_set_irq_callback(hci_uart_id, mp_bluetooth_hci_poll_now); + mp_uart_set_irq_callback(hci_uart_id, MP_UART_IRQ_RXIDLE, mp_bluetooth_hci_uart_irq_callback); // Start the HCI polling to process any initial events/packets. mp_bluetooth_hci_start_polling(); diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c index 7f14ff5444702..4fe82ce123176 100644 --- a/ports/alif/mpuart.c +++ b/ports/alif/mpuart.c @@ -26,20 +26,28 @@ #include "py/mphal.h" #include "py/runtime.h" +#include "shared/runtime/softtimer.h" #include "mpuart.h" - #include "sys_ctrl_uart.h" -#include "uart.h" -#define UART_MAX (8) +#define mp_container_of(ptr, structure, member) (void *)((uintptr_t)(ptr) - offsetof(structure, member)) + +#define UART_LSR_TEMT_Pos (6) #define SYST_PCLK (100000000) +#define CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits) \ + (1 + ((data_bits) + 5) + ((parity) != UART_PARITY_NONE) + ((stop_bits) + 1)) + typedef struct _uart_state_t { UART_TRANSFER_STATUS status; + uint32_t baudrate; + uint32_t bits_per_char; ringbuf_t *rx_ringbuf; const uint8_t *tx_src; const uint8_t *tx_src_max; - void (*irq_callback)(void); + soft_timer_entry_t rx_idle_timer; + unsigned int irq_trigger; + void (*irq_callback)(unsigned int uart_id, unsigned int trigger); } uart_state_t; static const uint8_t uart_irqn[UART_MAX] = { @@ -66,7 +74,19 @@ static UART_Type *const uart_periph[UART_MAX] = { static uart_state_t uart_state[UART_MAX]; -void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf) { +// This is called by soft_timer and executes at IRQ_PRI_PENDSV. +static void rx_idle_timer_callback(soft_timer_entry_t *self) { + uart_state_t *state = mp_container_of(self, uart_state_t, rx_idle_timer); + if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_RXIDLE) { + unsigned int uart_id = state - &uart_state[0]; + state->irq_callback(uart_id, MP_UART_IRQ_RXIDLE); + } +} + +void mp_uart_init(unsigned int uart_id, uint32_t baudrate, + UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits, + mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf) { + UART_Type *uart = uart_periph[uart_id]; uart_state_t *state = &uart_state[uart_id]; @@ -82,17 +102,20 @@ void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, uart_disable_tx_irq(uart); uart_disable_rx_irq(uart); uart_set_baudrate(uart, SYST_PCLK, baudrate); - uart_set_data_parity_stop_bits(uart, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1); + uart_set_data_parity_stop_bits(uart, data_bits, parity, stop_bits); uart_set_flow_control(uart, UART_FLOW_CONTROL_NONE); uart->UART_FCR |= UART_FCR_RCVR_FIFO_RESET; uart_set_tx_trigger(uart, UART_TX_FIFO_EMPTY); - uart_set_rx_trigger(uart, UART_RX_ONE_CHAR_IN_FIFO); + uart_set_rx_trigger(uart, UART_RX_FIFO_QUARTER_FULL); // Initialise the state. state->status = UART_TRANSFER_STATUS_NONE; + state->baudrate = baudrate; + state->bits_per_char = CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits); state->rx_ringbuf = rx_ringbuf; state->tx_src = NULL; state->tx_src_max = NULL; + state->irq_trigger = 0; state->irq_callback = NULL; // Enable interrupts. @@ -100,6 +123,8 @@ void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, NVIC_SetPriority(uart_irqn[uart_id], IRQ_PRI_UART_REPL); NVIC_EnableIRQ(uart_irqn[uart_id]); uart_enable_rx_irq(uart); + + soft_timer_static_init(&state->rx_idle_timer, SOFT_TIMER_MODE_ONE_SHOT, 0, rx_idle_timer_callback); } void mp_uart_deinit(unsigned int uart_id) { @@ -109,9 +134,16 @@ void mp_uart_deinit(unsigned int uart_id) { NVIC_DisableIRQ(uart_irqn[uart_id]); } -void mp_uart_set_irq_callback(unsigned int uart_id, void (*callback)(void)) { +void mp_uart_set_irq_callback(unsigned int uart_id, unsigned int trigger, void (*callback)(unsigned int uart_id, unsigned int trigger)) { + UART_Type *uart = uart_periph[uart_id]; uart_state_t *state = &uart_state[uart_id]; + state->irq_trigger = trigger; state->irq_callback = callback; + if (trigger & MP_UART_IRQ_RX) { + uart_set_rx_trigger(uart, UART_RX_ONE_CHAR_IN_FIFO); + } else { + uart_set_rx_trigger(uart, UART_RX_FIFO_QUARTER_FULL); + } } void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts) { @@ -131,10 +163,20 @@ void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate) { UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + state->baudrate = baudrate; uart_set_baudrate(uart, SYST_PCLK, baudrate); } +void mp_uart_set_bits_parity_stop(unsigned int uart_id, UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits) { + UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + + state->bits_per_char = CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits); + uart_set_data_parity_stop_bits(uart, data_bits, parity, stop_bits); +} + size_t mp_uart_rx_any(unsigned int uart_id) { uart_state_t *state = &uart_state[uart_id]; if (state->rx_ringbuf != NULL) { @@ -143,6 +185,11 @@ size_t mp_uart_rx_any(unsigned int uart_id) { return 0; } +size_t mp_uart_tx_any(unsigned int uart_id) { + UART_Type *uart = uart_periph[uart_id]; + return uart->UART_TFL + !((uart->UART_LSR >> UART_LSR_TEMT_Pos) & 1); +} + int mp_uart_rx_char(unsigned int uart_id) { uart_state_t *state = &uart_state[uart_id]; if (state->rx_ringbuf != NULL && ringbuf_avail(state->rx_ringbuf)) { @@ -208,9 +255,11 @@ static void mp_uart_irq_handler(unsigned int uart_id) { } case UART_IIR_RECEIVED_DATA_AVAILABLE: - case UART_IIR_CHARACTER_TIMEOUT: + case UART_IIR_CHARACTER_TIMEOUT: { + bool had_char = false; while (uart->UART_USR & UART_USR_RECEIVE_FIFO_NOT_EMPTY) { for (uint32_t rfl = uart->UART_RFL; rfl; --rfl) { + had_char = true; int c = uart->UART_RBR; #if MICROPY_HW_ENABLE_UART_REPL && MICROPY_KBD_EXCEPTION if (uart_id == MICROPY_HW_UART_REPL) { @@ -226,13 +275,18 @@ static void mp_uart_irq_handler(unsigned int uart_id) { } } - if (iir == UART_IIR_CHARACTER_TIMEOUT) { - if (state->irq_callback != NULL) { - state->irq_callback(); + if (had_char) { + if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_RX) { + state->irq_callback(uart_id, MP_UART_IRQ_RX); } } + if (state->irq_trigger & MP_UART_IRQ_RXIDLE) { + // Wait for 2 characters worth of time before triggering the RXIDLE event. + soft_timer_reinsert(&state->rx_idle_timer, 2000 * state->bits_per_char / state->baudrate + 1); + } break; + } case UART_IIR_TRANSMIT_HOLDING_REG_EMPTY: while (uart->UART_USR & UART_USR_TRANSMIT_FIFO_NOT_FULL) { @@ -241,6 +295,9 @@ static void mp_uart_irq_handler(unsigned int uart_id) { } else { uart_disable_tx_irq(uart); state->status = UART_TRANSFER_STATUS_SEND_COMPLETE; + if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_TXIDLE) { + state->irq_callback(uart_id, MP_UART_IRQ_TXIDLE); + } break; } } @@ -273,7 +330,9 @@ DEFINE_IRQ_HANDLER(7) #define REPL_BAUDRATE (115200) void mp_uart_init_repl(void) { - mp_uart_init(MICROPY_HW_UART_REPL, REPL_BAUDRATE, pin_REPL_UART_TX, pin_REPL_UART_RX, &stdin_ringbuf); + mp_uart_init(MICROPY_HW_UART_REPL, + REPL_BAUDRATE, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1, + pin_REPL_UART_TX, pin_REPL_UART_RX, &stdin_ringbuf); } void mp_uart_write_strn_repl(const char *str, size_t len) { diff --git a/ports/alif/mpuart.h b/ports/alif/mpuart.h index 1c28da4627859..76dadaab38015 100644 --- a/ports/alif/mpuart.h +++ b/ports/alif/mpuart.h @@ -27,15 +27,25 @@ #define MICROPY_INCLUDED_ALIF2_UART_H #include "py/ringbuf.h" +#include "uart.h" -void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf); +#define UART_MAX (8) +#define MP_UART_IRQ_RX (1) +#define MP_UART_IRQ_RXIDLE (2) +#define MP_UART_IRQ_TXIDLE (4) + +void mp_uart_init(unsigned int uart_id, uint32_t baudrate, + UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits, + mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf); void mp_uart_deinit(unsigned int uart_id); -void mp_uart_set_irq_callback(unsigned int uart_id, void (*callback)(void)); +void mp_uart_set_irq_callback(unsigned int uart_id, unsigned int trigger, void (*callback)(unsigned int uart_id, unsigned int trigger)); void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts); void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate); +void mp_uart_set_bits_parity_stop(unsigned int uart_id, UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits); size_t mp_uart_rx_any(unsigned int uart_id); +size_t mp_uart_tx_any(unsigned int uart_id); int mp_uart_rx_char(unsigned int uart_id); void mp_uart_tx_data(unsigned int uart_id, const uint8_t *src, size_t len); From 29a873ec0778107bcc9b25b0ab8a32a7f9e5bba9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 14 Mar 2025 23:38:56 +1100 Subject: [PATCH 519/546] alif/machine_uart: Add machine.UART peripheral support. Signed-off-by: Damien George --- ports/alif/machine_uart.c | 560 ++++++++++++++++++++++++++++++++++++++ ports/alif/mpconfigport.h | 3 + 2 files changed, 563 insertions(+) create mode 100644 ports/alif/machine_uart.c diff --git a/ports/alif/machine_uart.c b/ports/alif/machine_uart.c new file mode 100644 index 0000000000000..746c86677f956 --- /dev/null +++ b/ports/alif/machine_uart.c @@ -0,0 +1,560 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 OpenMV LLC. + * + * 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. + */ + +// This file is never compiled standalone, it's included directly from +// extmod/machine_uart.c via MICROPY_PY_MACHINE_UART_INCLUDEFILE. + +#include "py/mphal.h" +#include "py/mperrno.h" +#include "mpuart.h" + +#define DEFAULT_UART_BAUDRATE (115200) +#define DEFAULT_UART_BITS (8) +#define DEFAULT_UART_PARITY (MP_ROM_NONE) +#define DEFAULT_UART_STOP (1) +#define DEFAULT_UART_RX_BUFFER_SIZE (256) + +typedef struct _machine_uart_obj_t { + mp_obj_base_t base; + unsigned int uart_id; + uint32_t baudrate; + UART_DATA_BITS bits; + UART_PARITY parity; + UART_STOP_BITS stop; + mp_hal_pin_obj_t tx; + mp_hal_pin_obj_t rx; + mp_hal_pin_obj_t rts; + mp_hal_pin_obj_t cts; + uint32_t flow; + uint16_t timeout; // timeout waiting for first char (in ms) + uint16_t timeout_char; // timeout waiting between chars (in ms) + uint8_t *rx_ringbuf_array; + ringbuf_t rx_ringbuf; + uint32_t irq_flags; + uint32_t irq_trigger; + mp_irq_obj_t irq_obj; +} machine_uart_obj_t; + +typedef struct _machine_uart_default_pins_t { + mp_hal_pin_obj_t tx; + mp_hal_pin_obj_t rx; + mp_hal_pin_obj_t rts; + mp_hal_pin_obj_t cts; +} machine_uart_default_pins_t; + +static const machine_uart_default_pins_t machine_uart_default_pins[UART_MAX] = { + [0] = { + #if defined(MICROPY_HW_UART0_TX) && defined(MICROPY_HW_UART0_RX) + MICROPY_HW_UART0_TX, MICROPY_HW_UART0_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART0_RTS) && defined(MICROPY_HW_UART0_CTS) + MICROPY_HW_UART0_RTS, MICROPY_HW_UART0_CTS, + #else + NULL, NULL, + #endif + }, + [1] = { + #if defined(MICROPY_HW_UART1_TX) && defined(MICROPY_HW_UART1_RX) + MICROPY_HW_UART1_TX, MICROPY_HW_UART1_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART1_RTS) && defined(MICROPY_HW_UART1_CTS) + MICROPY_HW_UART1_RTS, MICROPY_HW_UART1_CTS, + #else + NULL, NULL, + #endif + }, + [2] = { + #if defined(MICROPY_HW_UART2_TX) && defined(MICROPY_HW_UART2_RX) + MICROPY_HW_UART2_TX, MICROPY_HW_UART2_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART2_RTS) && defined(MICROPY_HW_UART2_CTS) + MICROPY_HW_UART2_RTS, MICROPY_HW_UART2_CTS, + #else + NULL, NULL, + #endif + }, + [3] = { + #if defined(MICROPY_HW_UART3_TX) && defined(MICROPY_HW_UART3_RX) + MICROPY_HW_UART3_TX, MICROPY_HW_UART3_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART3_RTS) && defined(MICROPY_HW_UART3_CTS) + MICROPY_HW_UART3_RTS, MICROPY_HW_UART3_CTS, + #else + NULL, NULL, + #endif + }, + [4] = { + #if defined(MICROPY_HW_UART4_TX) && defined(MICROPY_HW_UART4_RX) + MICROPY_HW_UART4_TX, MICROPY_HW_UART4_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART4_RTS) && defined(MICROPY_HW_UART4_CTS) + MICROPY_HW_UART4_RTS, MICROPY_HW_UART4_CTS, + #else + NULL, NULL, + #endif + }, + [5] = { + #if defined(MICROPY_HW_UART5_TX) && defined(MICROPY_HW_UART5_RX) + MICROPY_HW_UART5_TX, MICROPY_HW_UART5_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART5_RTS) && defined(MICROPY_HW_UART5_CTS) + MICROPY_HW_UART5_RTS, MICROPY_HW_UART5_CTS, + #else + NULL, NULL, + #endif + }, + [6] = { + #if defined(MICROPY_HW_UART6_TX) && defined(MICROPY_HW_UART6_RX) + MICROPY_HW_UART6_TX, MICROPY_HW_UART6_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART6_RTS) && defined(MICROPY_HW_UART6_CTS) + MICROPY_HW_UART6_RTS, MICROPY_HW_UART6_CTS, + #else + NULL, NULL, + #endif + }, + [7] = { + #if defined(MICROPY_HW_UART7_TX) && defined(MICROPY_HW_UART7_RX) + MICROPY_HW_UART7_TX, MICROPY_HW_UART7_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART7_RTS) && defined(MICROPY_HW_UART7_CTS) + MICROPY_HW_UART7_RTS, MICROPY_HW_UART7_CTS, + #else + NULL, NULL, + #endif + }, +}; + +static void machine_uart_irq_callback(unsigned int uart_id, unsigned int trigger); + +MP_REGISTER_ROOT_POINTER(struct _machine_uart_obj_t *machine_uart_obj_all[UART_MAX]); + +/******************************************************************************/ +// MicroPython bindings for UART + +#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \ + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_uart___del___obj) }, \ + { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_FLOW_CONTROL_RTS) }, \ + { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_FLOW_CONTROL_CTS) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(MP_UART_IRQ_RX) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(MP_UART_IRQ_RXIDLE) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_TXIDLE), MP_ROM_INT(MP_UART_IRQ_TXIDLE) }, \ + +#define GET_PIN_WITH_DEFAULT(uart_id_, pin_name, pin_selection) \ + (pin_selection == mp_const_none ? machine_uart_default_pins[uart_id_].pin_name : mp_hal_get_pin_obj(pin_selection)) + +static void machine_uart_set_bits(machine_uart_obj_t *self, mp_int_t bits) { + if (!(5 <= bits && bits <= 8)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); + } + self->bits = UART_DATA_BITS_5 + (bits - 5); +} + +static void machine_uart_set_parity(machine_uart_obj_t *self, mp_obj_t parity) { + if (parity == mp_const_none) { + self->parity = UART_PARITY_NONE; + } else if (mp_obj_get_int(parity) & 1) { + self->parity = UART_PARITY_ODD; + } else { + self->parity = UART_PARITY_EVEN; + } +} + +static void machine_uart_set_stop(machine_uart_obj_t *self, mp_int_t stop) { + if (!(1 <= stop && stop <= 2)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid stop")); + } + self->stop = UART_STOP_BITS_1 + (stop - 1); +} + +static void machine_uart_set_timeout_char(machine_uart_obj_t *self, mp_int_t timeout_char) { + // Make sure timeout_char is at least as long as a whole character (13 bits to be safe). + uint32_t min_timeout_char = 13000 / self->baudrate + 1; + self->timeout_char = MAX(min_timeout_char, timeout_char); +} + +static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + static const char *parity_name[] = {[UART_PARITY_NONE] = "None", [UART_PARITY_EVEN] = "0", [UART_PARITY_ODD] = "1"}; + + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + unsigned int bits = 5 + (self->bits - UART_DATA_BITS_5); + const char *parity = parity_name[self->parity]; + unsigned int stop = 1 + (self->stop - UART_STOP_BITS_1); + + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, " + "tx=%q, rx=%q, ", + self->uart_id, self->baudrate, bits, parity, stop, + mp_hal_pin_name(self->tx), mp_hal_pin_name(self->rx)); + if (self->rts != NULL) { + mp_printf(print, "rts=%q, ", mp_hal_pin_name(self->rts)); + } + if (self->cts != NULL) { + mp_printf(print, "cts=%q, ", mp_hal_pin_name(self->cts)); + } + mp_printf(print, "flow=", self->rts); + if (self->flow == 0) { + mp_printf(print, "0"); + } else { + if (self->flow & UART_FLOW_CONTROL_RTS) { + mp_printf(print, "RTS"); + if (self->flow & UART_FLOW_CONTROL_CTS) { + mp_printf(print, "|"); + } + } + if (self->flow & UART_FLOW_CONTROL_CTS) { + mp_printf(print, "CTS"); + } + } + mp_printf(print, ", timeout=%u, timeout_char=%u, rxbuf=%u)", + self->timeout, self->timeout_char, self->rx_ringbuf.size - 1); +} + +static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { + ARG_id, + ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, + ARG_tx, ARG_rx, ARG_rts, ARG_cts, + ARG_flow, ARG_timeout, ARG_timeout_char, ARG_rxbuf, + }; + static const mp_arg_t allowed_args[] = { + #if !defined(MICROPY_HW_DEFAULT_UART_ID) + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + #else + { MP_QSTR_id, MP_ARG_INT, {.u_int = MICROPY_HW_DEFAULT_UART_ID} }, + #endif + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = DEFAULT_UART_BAUDRATE} }, + { MP_QSTR_bits, MP_ARG_INT, {.u_int = DEFAULT_UART_BITS} }, + { MP_QSTR_parity, MP_ARG_OBJ, {.u_rom_obj = DEFAULT_UART_PARITY} }, + { MP_QSTR_stop, MP_ARG_INT, {.u_int = DEFAULT_UART_STOP} }, + { MP_QSTR_tx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { 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} }, + { MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_UART_RX_BUFFER_SIZE} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get UART bus. + int uart_id = args[ARG_id].u_int; + if (uart_id < 0 || uart_id >= UART_MAX) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) doesn't exist"), uart_id); + } + + machine_uart_obj_t *self; + bool need_init; + if (MP_STATE_PORT(machine_uart_obj_all)[uart_id] == NULL) { + // Create new UART object. + self = mp_obj_malloc_with_finaliser(machine_uart_obj_t, &machine_uart_type); + self->uart_id = uart_id; + need_init = true; + } else { + // Reference existing UART object. + self = MP_STATE_PORT(machine_uart_obj_all)[uart_id]; + need_init = n_args > 1 || n_kw > 0; + } + + if (need_init) { + // Set the UART parameters. + self->baudrate = args[ARG_baudrate].u_int; + machine_uart_set_bits(self, args[ARG_bits].u_int); + machine_uart_set_parity(self, args[ARG_parity].u_obj); + machine_uart_set_stop(self, args[ARG_stop].u_int); + self->tx = GET_PIN_WITH_DEFAULT(self->uart_id, tx, args[ARG_tx].u_obj); + self->rx = GET_PIN_WITH_DEFAULT(self->uart_id, rx, args[ARG_rx].u_obj); + self->rts = GET_PIN_WITH_DEFAULT(self->uart_id, rts, args[ARG_rts].u_obj); + self->cts = GET_PIN_WITH_DEFAULT(self->uart_id, cts, args[ARG_cts].u_obj); + self->flow = args[ARG_flow].u_int; + self->timeout = args[ARG_timeout].u_int; + uint32_t min_timeout_char = 13000 / self->baudrate + 1; // 13 bits to be safe + self->timeout = MAX(min_timeout_char, args[ARG_timeout_char].u_int); + + // Check TX/RX pins are given. + if (self->tx == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("tx not given")); + } + if (self->rx == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("rx not given")); + } + + // Configure flow control. + mp_hal_pin_obj_t rts = NULL; + mp_hal_pin_obj_t cts = NULL; + if (self->flow & UART_FLOW_CONTROL_RTS) { + if (self->rts == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("rts not given")); + } + rts = self->rts; + } + if (self->flow & UART_FLOW_CONTROL_CTS) { + if (self->cts == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("cts not given")); + } + cts = self->cts; + } + + // Allocate the RX buffer. + size_t rxbuf_len = args[ARG_rxbuf].u_int; + self->rx_ringbuf_array = m_new(uint8_t, rxbuf_len + 1); + self->rx_ringbuf.buf = self->rx_ringbuf_array; + self->rx_ringbuf.size = rxbuf_len + 1; + self->rx_ringbuf.iput = 0; + self->rx_ringbuf.iget = 0; + + // Reset the IRQ state + self->irq_flags = 0; + self->irq_trigger = 0; + self->irq_obj.base.type = NULL; + + // Initialize the UART hardware. + mp_uart_init(self->uart_id, self->baudrate, self->bits, self->parity, self->stop, self->tx, self->rx, &self->rx_ringbuf); + + mp_uart_set_flow(self->uart_id, rts, cts); + } + + MP_STATE_PORT(machine_uart_obj_all)[uart_id] = self; + + return MP_OBJ_FROM_PTR(self); +} + +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) { + enum { + ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, + ARG_timeout, ARG_timeout_char, + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_parity, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, + { MP_QSTR_stop, MP_ARG_INT, {.u_int = -1} }, + { 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} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + bool change_baudrate = false; + bool change_bits_parity_stop = false; + + // Change baudrate if requested. + if (args[ARG_baudrate].u_int > 0) { + self->baudrate = args[ARG_baudrate].u_int; + change_baudrate = true; + } + + // Change data bits if requested. + if (args[ARG_bits].u_int > 0) { + machine_uart_set_bits(self, args[ARG_bits].u_int); + change_bits_parity_stop = true; + } + + // Change parity if requested. + if (args[ARG_parity].u_obj != MP_OBJ_NEW_SMALL_INT(-1)) { + machine_uart_set_parity(self, args[ARG_parity].u_obj); + change_bits_parity_stop = true; + } + + // Change stop bits if requested. + if (args[ARG_stop].u_int > 0) { + machine_uart_set_stop(self, args[ARG_stop].u_int); + change_bits_parity_stop = true; + } + + // Change timeout if requested. + if (args[ARG_timeout].u_int >= 0) { + self->timeout = args[ARG_timeout].u_int; + } + + // Change timeout_char if requested. + if (args[ARG_timeout_char].u_int >= 0) { + machine_uart_set_timeout_char(self, args[ARG_timeout_char].u_int); + } + + // Reconfigure the hardware parameters that have changed. + if (change_baudrate) { + mp_uart_set_baudrate(self->uart_id, self->baudrate); + } + if (change_bits_parity_stop) { + mp_uart_set_bits_parity_stop(self->uart_id, self->bits, self->parity, self->stop); + } +} + +static mp_obj_t machine_uart___del__(mp_obj_t self_in) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uart_deinit(self->uart_id); + MP_STATE_PORT(machine_uart_obj_all)[self->uart_id] = NULL; + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_uart___del___obj, machine_uart___del__); + +// Turn off the UART bus. +static void mp_machine_uart_deinit(machine_uart_obj_t *self) { + mp_uart_deinit(self->uart_id); +} + +// Return number of characters waiting. +static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) { + return mp_uart_rx_any(self->uart_id); +} + +// Since uart.write() waits up to the last byte, uart.txdone() always returns True. +static bool mp_machine_uart_txdone(machine_uart_obj_t *self) { + return !!mp_uart_tx_any(self->uart_id); +} + +// Change the trigger for the UART IRQ. +static mp_uint_t machine_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->irq_trigger = new_trigger; + mp_uart_set_irq_callback(self->uart_id, self->irq_trigger, machine_uart_irq_callback); + return 0; +} + +// Get UART IRQ state. +static mp_uint_t machine_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->irq_flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return self->irq_trigger; + } + return 0; +} + +static const mp_irq_methods_t machine_uart_irq_methods = { + .trigger = machine_uart_irq_trigger, + .info = machine_uart_irq_info, +}; + +// Retrieve and configure the UART IRQ object. +static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { + if (self->irq_obj.base.type == NULL) { + mp_irq_init(&self->irq_obj, &machine_uart_irq_methods, MP_OBJ_FROM_PTR(self)); + } + + if (any_args) { + self->irq_obj.handler = args[MP_IRQ_ARG_INIT_handler].u_obj; + self->irq_obj.ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; + machine_uart_irq_trigger(MP_OBJ_FROM_PTR(self), args[MP_IRQ_ARG_INIT_trigger].u_int); + } + + return &self->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(); + mp_uint_t timeout = self->timeout; + uint8_t *dest = buf_in; + + for (size_t i = 0; i < size; i++) { + // Wait for the first/next character + while (!mp_uart_rx_any(self->uart_id)) { + mp_uint_t elapsed = mp_hal_ticks_ms() - start; + if (elapsed > timeout) { // timed out + if (i <= 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } else { + return i; + } + } + mp_event_handle_nowait(); + } + *dest++ = mp_uart_rx_char(self->uart_id); + start = mp_hal_ticks_ms(); // Inter-character timeout + timeout = self->timeout_char; + } + 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 = MP_OBJ_TO_PTR(self_in); + mp_uart_tx_data(self->uart_id, buf_in, size); + return size; +} + +static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + machine_uart_obj_t *self = self_in; + mp_uint_t ret; + if (request == MP_STREAM_POLL) { + uintptr_t flags = arg; + ret = 0; + if ((flags & MP_STREAM_POLL_RD) && mp_uart_rx_any(self->uart_id)) { + ret |= MP_STREAM_POLL_RD; + } + if ((flags & MP_STREAM_POLL_WR)) { + ret |= MP_STREAM_POLL_WR; + } + } else if (request == MP_STREAM_FLUSH) { + uint32_t t0 = mp_hal_ticks_ms(); + const uint32_t timeout_ms = 100 + 10 * mp_uart_tx_any(self->uart_id); + while (mp_uart_tx_any(self->uart_id)) { + if (mp_hal_ticks_ms() - t0 > timeout_ms) { + *errcode = MP_ETIMEDOUT; + ret = MP_STREAM_ERROR; + } + mp_event_handle_nowait(); + } + return 0; + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +static void machine_uart_irq_callback(unsigned int uart_id, unsigned int trigger) { + machine_uart_obj_t *self = MP_STATE_PORT(machine_uart_obj_all)[uart_id]; + self->irq_flags = trigger; + if (self->irq_obj.base.type != NULL && (self->irq_flags & self->irq_trigger)) { + mp_irq_handler(&self->irq_obj); + } +} diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 73abb8924c393..6c9e6af635f57 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -136,6 +136,9 @@ #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_TIMER (1) +#define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/alif/machine_uart.c" +#define MICROPY_PY_MACHINE_UART_IRQ (1) #define MICROPY_PY_NETWORK (CORE_M55_HP) #ifndef MICROPY_PY_NETWORK_HOSTNAME_DEFAULT #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-alif" From 737acef5cbd8ffb5a7c0ef605741debd70e72606 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Mar 2025 14:28:57 +1100 Subject: [PATCH 520/546] alif: Support more fine-grained pin alternate function selection. Now raises an exception if the pin doesn't support the alternate function unit number and line type, eg UART0_TX (previously it only checked the peripheral). Signed-off-by: Damien George --- ports/alif/cyw43_port_spi.c | 10 +- ports/alif/machine_i2c.c | 12 +- ports/alif/machine_spi.c | 22 ++- ports/alif/mcu/ensemble_pin_alt.csv | 258 ++++++++++++++-------------- ports/alif/mcu/make-pins.py | 14 +- ports/alif/mphalport.h | 201 +++++++++++++++++++--- ports/alif/mpuart.c | 12 +- ports/alif/ospi_flash.c | 27 +-- 8 files changed, 371 insertions(+), 185 deletions(-) diff --git a/ports/alif/cyw43_port_spi.c b/ports/alif/cyw43_port_spi.c index 84d84b0e1de48..6ebcaa057dc97 100644 --- a/ports/alif/cyw43_port_spi.c +++ b/ports/alif/cyw43_port_spi.c @@ -34,6 +34,7 @@ #include "sys_ctrl_spi.h" // CYW43 is connected to SPI3. +#define HW_SPI_UNIT (3) #define HW_SPI ((SPI_Type *)SPI3_BASE) #define SPI_BAUDRATE (16000000) #define SPI_RX_FIFO_SIZE (16) @@ -56,9 +57,12 @@ static void spi_bus_init(void) { mp_hal_pin_output(pin_WL_CS); mp_hal_pin_high(pin_WL_CS); // NOTE: Alif recommends enabled input read for all SPI pins. - mp_hal_pin_config(pin_WL_SCLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); - mp_hal_pin_config(pin_WL_MOSI, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); - mp_hal_pin_config(pin_WL_MISO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + mp_hal_pin_config(pin_WL_SCLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(SPI_SCLK, HW_SPI_UNIT), true); + mp_hal_pin_config(pin_WL_MOSI, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(SPI_MOSI, HW_SPI_UNIT), true); + mp_hal_pin_config(pin_WL_MISO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(SPI_MISO, HW_SPI_UNIT), true); // Starts out clock_polarity=1, clock_phase=0. spi_mode_master(HW_SPI); diff --git a/ports/alif/machine_i2c.c b/ports/alif/machine_i2c.c index f117b93c2b1e8..a710aeeb01132 100644 --- a/ports/alif/machine_i2c.c +++ b/ports/alif/machine_i2c.c @@ -135,9 +135,9 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n // Configure I2C pins. mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SCL, self->i2c_id), true); mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SDA, self->i2c_id), true); // Initialize I2C controller. self->i2c->I2C_CON = I2C_IC_CON_ENABLE_MASTER_MODE | @@ -244,18 +244,18 @@ int machine_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_mac // Switch pins to GPIO/OD. mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_NONE, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, 0, true); mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_NONE, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, 0, true); // Perform the transfer. ret = mp_machine_soft_i2c_transfer(&soft_i2c.base, addr, 1, &bufs, flags); // Re-configure I2C pins. mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SCL, self->i2c_id), true); mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SDA, self->i2c_id), true); return ret; } diff --git a/ports/alif/machine_spi.c b/ports/alif/machine_spi.c index d13c6368f2515..abb60bc4e8325 100644 --- a/ports/alif/machine_spi.c +++ b/ports/alif/machine_spi.c @@ -60,6 +60,20 @@ static machine_spi_obj_t machine_spi_obj[] = { }; +static const uint8_t spi_pin_alt[] = { + MP_HAL_PIN_ALT_SPI_SCLK, + MP_HAL_PIN_ALT_SPI_MISO, + MP_HAL_PIN_ALT_SPI_MOSI, + MP_HAL_PIN_ALT_SPI_SS0, +}; + +static const uint8_t lpspi_pin_alt[] = { + MP_HAL_PIN_ALT_LPSPI_SCLK, + MP_HAL_PIN_ALT_LPSPI_MISO, + MP_HAL_PIN_ALT_LPSPI_MOSI, + MP_HAL_PIN_ALT_LPSPI_SS, +}; + static inline uint32_t spi_get_clk(machine_spi_obj_t *spi) { return spi->is_lp ? GetSystemCoreClock() : GetSystemAHBClock(); } @@ -131,9 +145,15 @@ static void spi_init(machine_spi_obj_t *spi, uint32_t baudrate, } // Configure SPI pins. + const uint8_t *alt; + if (spi->id <= 3) { + alt = spi_pin_alt; + } else { + alt = lpspi_pin_alt; + } for (size_t i = 0; i < MP_ARRAY_SIZE(pins) && pins[i]; i++) { mp_hal_pin_config(pins[i], MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_MAKE(alt[i], spi->id), true); } // Disable all interrupts. diff --git a/ports/alif/mcu/ensemble_pin_alt.csv b/ports/alif/mcu/ensemble_pin_alt.csv index 10bfedc9a1d68..34f13e86f6d3c 100644 --- a/ports/alif/mcu/ensemble_pin_alt.csv +++ b/ports/alif/mcu/ensemble_pin_alt.csv @@ -1,130 +1,130 @@ Pin,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7 -P0_0,,OSPI,UART,I3C,UT,LPCAM,CAM,ANA -P0_1,,OSPI,UART,I3C,UT,LPCAM,CAM,ANA -P0_2,,OSPI,UART,I2C,UT,LPCAM,CAM,ANA -P0_3,,OSPI,UART,I2C,UT,LPCAM,CAM,ANA -P0_4,,OSPI,UART,PDM,I2C,UT,,ANA -P0_5,,OSPI,UART,PDM,I2C,UT,,ANA -P0_6,,OSPI,UART,PDM,I2C,UT,,ANA -P0_7,,OSPI,UART,PDM,I2C,UT,CDC,ANA -P1_0,,UART,SPI,I2C,UT,LPCAM,ETH,ANA -P1_1,,UART,SPI,I2C,UT,LPCAM,ETH,ANA -P1_2,,UART,SPI,I3C,UT,LPCAM,ETH,ANA -P1_3,,UART,SPI,I3C,UT,LPCAM,ETH,ANA -P1_4,,OSPI,UART,SPI,UT,LPCAM,ETH,ANA -P1_5,,OSPI,UART,SPI,UT,LPCAM,ETH,ANA -P1_6,,OSPI,UART,I2S,UT,LPCAM,ETH,ANA -P1_7,,OSPI,UART,I2S,UT,LPCAM,ETH,ANA -P2_0,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA -P2_1,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA -P2_2,,OSPI,UART,LPPDM,UT,LPCAM,ETH,ANA -P2_3,,OSPI,UART,LPPDM,UT,LPCAM,CDC,ANA -P2_4,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA -P2_5,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA -P2_6,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA -P2_7,,OSPI,LPI2S,SPI,UT,LPCAM,CAM,ANA -P3_0,,OSPI,UART,PDM,I2S,QEC,LPCAM,CAM -P3_1,,OSPI,UART,PDM,I2S,QEC,LPCAM,CAM -P3_2,,OSPI,PDM,I2S,I3C,QEC,LPCAM,CAM -P3_3,,OSPI,PDM,I2S,I3C,QEC,LPCAM,CAM -P3_4,,OSPI,UART,LPPDM,I2S,I2C,QEC,CAM -P3_5,,OSPI,UART,LPPDM,SPI,I2C,QEC,CAM -P3_6,,HFXO,LPUART,LPPDM,SPI,I2C,QEC,CAM -P3_7,,JTAG,LPUART,LPPDM,SPI,I2C,QEC,CAM -P4_0,,JTAG,,I2S,SPI,QEC,CDC,CAM -P4_1,,JTAG,I2S,SPI,QEC,SD,CDC,CAM -P4_2,,JTAG,,I2S,SPI,QEC,SD,CAM -P4_3,,JTAG,,I2S,SPI,QEC,SD,CAM -P4_4,,JTAG,I2S,SPI,FAULT,,, -P4_5,,JTAG,SPI,FAULT,,,, -P4_6,,JTAG,SPI,FAULT,,,, -P4_7,,JTAG,SPI,FAULT,,,, -P5_0,,OSPI,UART,PDM,SPI,I2C,UT,SD -P5_1,,OSPI,UART,PDM,SPI,I2C,UT,SD -P5_2,,OSPI,UART,PDM,SPI,LPI2C,UT,SD -P5_3,,OSPI,UART,SPI,LPI2C,UT,SD,CDC -P5_4,,OSPI,UART,PDM,SPI,UT,SD,CDC -P5_5,,OSPI,UART,PDM,UT,SD,ETH,CDC -# P5_6 doesn't really have OSPI on AF1 but it's needed for P10_7 to be in OSPI mode -P5_6,,OSPI,UART,I2C,UT,SD,ETH,CDC -P5_7,,OSPI,UART,I2C,UT,SD,ETH, -P6_0,,OSPI,UART,PDM,UT,SD,ETH, -P6_1,,OSPI,UART,PDM,UT,SD,ETH, -P6_2,,OSPI,UART,,PDM,UT,SD,ETH -P6_3,,OSPI,UART,,PDM,UT,SD,ETH -P6_4,,OSPI,UART,,SPI,UT,SD,ETH -P6_5,,OSPI,UART,,SPI,UT,SD,ETH -P6_6,,OSPI,UART,,SPI,UT,SD,ETH -P6_7,,OSPI,UART,PDM,SPI,UT,SD,ETH -P7_0,,,CMP,SPI,I2C,UT,SD, -P7_1,,,CMP,SPI,I2C,UT,SD, -P7_2,,,UART,CMP,SPI,I2C,UT,SD -P7_3,,,UART,CMP,SPI,I2C,UT, -P7_4,,,LPUART,LPPDM,LPSPI,LPI2C,UT, -P7_5,,,LPUART,,LPPDM,LPSPI,LPI2C,UT -P7_6,,,LPUART,,LPPDM,LPSPI,I3C,UT -P7_7,,,LPUART,,LPPDM,LPSPI,I3C,UT -P8_0,,OSPI,AUDIO,FAULT,LPCAM,SD,CDC,CAM -P8_1,,I2S,FAULT,LPCAM,SD,CDC,CAM, -P8_2,,I2S,SPI,FAULT,LPCAM,SD,CDC,CAM -P8_3,,I2S,SPI,FAULT,LPCAM,SD,CDC,CAM -P8_4,,I2S,SPI,QEC,LPCAM,SD,CDC,CAM -P8_5,,,SPI,QEC,LPCAM,SD,CDC,CAM -P8_6,,,I2S,QEC,LPCAM,SD,CDC,CAM -P8_7,,,I2S,QEC,LPCAM,SD,CDC,CAM -P9_0,,,I2S,QEC,SD,CDC,CAM, -P9_1,,LPUART,I2S,QEC,SD,CDC,CAM, -P9_2,,LPUART,I2S,SPI,QEC,SD,CDC,CAM -P9_3,,HFXO,UART,I2S,SPI,QEC,CDC,CAM -P9_4,,UART,I2S,SPI,I2C,QEC,CDC,CAM -P9_5,,OSPI,I2S,SPI,I2C,QEC,CDC,CAM -P9_6,,OSPI,AUDIO,SPI,I2C,QEC,CDC,CAM -P9_7,,OSPI,UART,SPI,I2C,QEC,CDC,CAM -P10_0,,OSPI,UART,SPI,UT,LPCAM,CDC,CAM -P10_1,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM -P10_2,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM -P10_3,,OSPI,,LPI2S,UT,LPCAM,CDC,CAM -P10_4,,OSPI,,LPI2S,I2C,UT,ETH,CDC -P10_5,,UART,I2S,SPI,I2C,UT,ETH,CDC -P10_6,,UART,I2S,SPI,I2C,UT,ETH,CDC -P10_7,,UART,I2S,SPI,I2C,UT,CDC,OSPI -P11_0,,OSPI,UART,I2S,SPI,UT,ETH,CDC -P11_1,,OSPI,UART,SPI,UT,ETH,CDC, -P11_2,,OSPI,UART,LPPDM,SPI,UT,ETH,CDC -P11_3,,OSPI,UART,LPPDM,SPI,UT,ETH,CDC -P11_4,,OSPI,UART,PDM,LPSPI,UT,ETH,CDC -P11_5,,OSPI,UART,PDM,LPSPI,UT,ETH,CDC -P11_6,,OSPI,UART,LPPDM,LPSPI,UT,ETH,CDC -P11_7,,OSPI,UART,LPPDM,LPSPI,UT,ETH,CDC -P12_0,,OSPI,AUDIO,I2S,UT,CDC,, -P12_1,,OSPI,UART,I2S,UT,CDC,, -P12_2,,OSPI,UART,I2S,UT,CDC,, -P12_3,,OSPI,UART,I2S,UT,CDC,, -P12_4,,OSPI,SPI,UT,,CDC,, -P12_5,,,SPI,UT,,CDC,, -P12_6,,,SPI,UT,,CDC,, -P12_7,,OSPI,,SPI,UT,CDC,, -P13_0,,OSPI,,SPI,QEC,SD,CDC, -P13_1,,OSPI,SPI,QEC,SD,CDC,, -P13_2,,OSPI,SPI,QEC,SD,CDC,, -P13_3,,OSPI,SPI,QEC,SD,CDC,, -P13_4,,OSPI,LPI2S,QEC,SD,CDC,, -P13_5,,OSPI,LPI2S,QEC,SD,CDC,, -P13_6,,OSPI,LPI2S,QEC,SD,CDC,, -P13_7,,OSPI,LPI2S,QEC,SD,CDC,, -P14_0,,OSPI,UART,QEC,SD,,, -P14_1,,OSPI,UART,,QEC,SD,, -P14_2,,OSPI,UART,,QEC,SD,, -P14_3,,OSPI,UART,,QEC,,, -P14_4,,CMP,SPI,FAULT,,,, -P14_5,,CMP,SPI,FAULT,,,, -P14_6,,CMP,SPI,FAULT,,,, -P14_7,,CMP,SPI,FAULT,,,, -P15_0,,LPTMR,,,,,, -P15_1,,LPTMR,,,,,, -P15_2,,LPTMR,,,,,, -P15_3,,LPTMR,,,,,, -P15_4,,,,,,,, -P15_5,,,,,,,, -P15_6,,,,,,,, -P15_7,,,,,,,, +P0_0,GPIO,OSPI0_D0,UART0_RX,I3C_SDA,UT0_T0,LPCAM_HSYNC,CAM_HSYNC,ANA_S0 +P0_1,GPIO,OSPI0_D1,UART0_TX,I3C_SCL,UT0_T1,LPCAM_VSYNC,CAM_VSYNC,ANA_S1 +P0_2,GPIO,OSPI0_D2,UART0_CTS,I2C0_SDA,UT1_T0,LPCAM_PCLK,CAM_PCLK,ANA_S2 +P0_3,GPIO,OSPI0_D3,UART0_RTS,I2C0_SCL,UT1_T1,LPCAM_XVCLK,CAM_XVCLK,ANA_S3 +P0_4,GPIO,OSPI0_D4,UART1_RX,PDM_D0,I2C1_SDA,UT2_T0,,ANA_S4 +P0_5,GPIO,OSPI0_D5,UART1_TX,PDM_C0,I2C1_SCL,UT2_T1,,ANA_S5 +P0_6,GPIO,OSPI0_D6,UART1_CTS,PDM_D1,I2C2_SCL,UT3_T0,,ANA_S6 +P0_7,GPIO,OSPI0_D7,UART1_RTS,PDM_C1,I2C2_SDA,UT3_T1,CDC_DE,ANA_S7 +P1_0,GPIO,UART2_RX,SPI0_MISO,I2C3_SDA,UT4_T0,LPCAM_HSYNC,ETH_RXD0,ANA_S8 +P1_1,GPIO,UART2_TX,SPI0_MOSI,I2C3_SCL,UT4_T1,LPCAM_VSYNC,ETH_RXD1,ANA_S9 +P1_2,GPIO,UART3_RX,SPI0_SCLK,I3C_SDA,UT5_T0,LPCAM_PCLK,ETH_RST,ANA_S10 +P1_3,GPIO,UART3_TX,SPI0_SS0,I3C_SCL,UT5_T1,LPCAM_XVCLK,ETH_TXD0,ANA_S11 +P1_4,GPIO,OSPI0_SS0,UART0_RX,SPI0_SS1,UT6_T0,LPCAM_D0,ETH_TXD1,ANA_S12 +P1_5,GPIO,OSPI0_SS1,UART0_TX,SPI0_SS2,UT6_T1,LPCAM_D1,ETH_TXEN,ANA_S13 +P1_6,GPIO,OSPI0_RXDS,UART1_RX,I2S0_SDI,UT7_T0,LPCAM_D2,ETH_IRQ,ANA_S14 +P1_7,GPIO,OSPI0_SCLK,UART1_TX,I2S0_SDO,UT7_T1,LPCAM_D3,ETH_REFCLK,ANA_S15 +P2_0,GPIO,OSPI0_D0,UART2_RX,LPPDM_D0,UT8_T0,LPCAM_D4,ETH_MDIO,ANA_S16 +P2_1,GPIO,OSPI0_D1,UART2_TX,LPPDM_C0,UT8_T1,LPCAM_D5,ETH_MDC,ANA_S17 +P2_2,GPIO,OSPI0_D2,UART3_RX,LPPDM_D1,UT9_T0,LPCAM_D6,ETH_CRS_DV_C,ANA_S18 +P2_3,GPIO,OSPI0_D3,UART3_TX,LPPDM_C1,UT9_T1,LPCAM_D7,CDC_PCLK,ANA_S19 +P2_4,GPIO,OSPI0_D4,LPI2S_SDI,SPI1_MISO,UT10_T0,LPCAM_D0,CAM_D0,ANA_S20 +P2_5,GPIO,OSPI0_D5,LPI2S_SDO,SPI1_MOSI,UT10_T1,LPCAM_D1,CAM_D1,ANA_S21 +P2_6,GPIO,OSPI0_D6,LPI2S_SCLK,SPI1_SCLK,UT11_T0,LPCAM_D2,CAM_D2,ANA_S22 +P2_7,GPIO,OSPI0_D7,LPI2S_WS,SPI1_SS0,UT11_T1,LPCAM_D3,CAM_D3,ANA_S23 +P3_0,GPIO,OSPI0_SCLK,UART4_RX,PDM_D0,I2S0_SCLK,QEC0_X,LPCAM_D4,CAM_D4 +P3_1,GPIO,OSPI0_SCLKN,UART4_TX,PDM_C0,I2S0_WS,QEC0_Y,LPCAM_D5,CAM_D5 +P3_2,GPIO,OSPI0_SS0,PDM_D1,I2S1_SDI,I3C_SDA,QEC0_Z,LPCAM_D6,CAM_D6 +P3_3,GPIO,OSPI0_SS1,PDM_C1,I2S1_SDO,I3C_SCL,QEC1_X,LPCAM_D7,CAM_D7 +P3_4,GPIO,OSPI0_RXDS,UART5_RX,LPPDM_C0,I2S1_SCLK,I2C0_SCL,QEC1_Y,CAM_D8 +P3_5,GPIO,OSPI0_SCLKN,UART5_TX,LPPDM_D0,SPI0_SS1,I2C0_SDA,QEC1_Z,CAM_D9 +P3_6,GPIO,HFXO_OUT,LPUART_CTS,LPPDM_C1,SPI0_SS2,I2C1_SDA,QEC2_X,CAM_D10 +P3_7,GPIO,JTAG_TRACECLK,LPUART_RTS,LPPDM_D1,SPI1_SS1,I2C1_SCL,QEC2_Y,CAM_D11 +P4_0,GPIO,JTAG_TDATA0,,I2S1_WS,SPI1_SS2,QEC2_Z,CDC_VSYNC,CAM_D12 +P4_1,GPIO,JTAG_TDATA1,I2S0_SDI,SPI1_SS3,QEC3_X,SD_CLK,CDC_HSYNC,CAM_D13 +P4_2,GPIO,JTAG_TDATA2,,I2S0_SDO,SPI2_MISO,QEC3_Y,SD_CMD,CAM_D14 +P4_3,GPIO,JTAG_TDATA3,,I2S0_SCLK,SPI2_MOSI,QEC3_Z,SD_RST,CAM_D15 +P4_4,GPIO,JTAG_TCK,I2S0_WS,SPI2_SCLK,FAULT0_A,,, +P4_5,GPIO,JTAG_TMS,SPI2_SS0,FAULT1_A,,,, +P4_6,GPIO,JTAG_TDI,SPI2_SS1,FAULT2_A,,,, +P4_7,GPIO,JTAG_TDO,SPI2_SS2,FAULT3_A,,,, +P5_0,GPIO,OSPI1_RXDS,UART4_RX,PDM_D2,SPI0_MISO,I2C2_SDA,UT0_T0,SD_D0 +P5_1,GPIO,OSPI1_SS0,UART4_TX,PDM_D3,SPI0_MOSI,I2C2_SCL,UT0_T1,SD_D1 +P5_2,GPIO,OSPI1_SCLKN,UART5_RX,PDM_C3,SPI0_SS0,LPI2C_SCL,UT1_T0,SD_D2 +P5_3,GPIO,OSPI1_SCLK,UART5_TX,SPI0_SCLK,LPI2C_SDA,UT1_T1,SD_D3,CDC_PCLK +P5_4,GPIO,OSPI1_SS1,UART3_CTS,PDM_D2,SPI0_SS3,UT2_T0,SD_D4,CDC_DE +P5_5,GPIO,OSPI1_SCLK,UART3_RTS,PDM_D3,UT2_T1,SD_D5,ETH_RXD0,CDC_HSYNC +# P5_6 doesn't really have OSPI on AF1 but it's needed for P10_7 to be in OSPI1_RXDS mode +P5_6,GPIO,OSPI1_RXDS,UART1_CTS,I2C2_SCL,UT3_T0,SD_D6,ETH_RXD1,CDC_VSYNC +P5_7,GPIO,OSPI1_SS0,UART1_RTS,I2C2_SDA,UT3_T1,SD_D7,ETH_RST, +P6_0,GPIO,OSPI0_D0,UART4_DE,PDM_D0,UT4_T0,SD_D0,ETH_TXD0, +P6_1,GPIO,OSPI0_D1,UART5_DE,PDM_C0,UT4_T1,SD_D1,ETH_TXD1, +P6_2,GPIO,OSPI0_D2,UART2_CTS,,PDM_D1,UT5_T0,SD_D2,ETH_TXEN +P6_3,GPIO,OSPI0_D3,UART2_RTS,,PDM_C1,UT5_T1,SD_D3,ETH_IRQ +P6_4,GPIO,OSPI0_D4,UART2_CTS,,SPI1_SS0,UT6_T0,SD_D4,ETH_REFCLK +P6_5,GPIO,OSPI0_D5,UART2_RTS,,SPI1_SS1,UT6_T1,SD_D5,ETH_MDIO +P6_6,GPIO,OSPI0_D6,UART0_CTS,,SPI1_SS2,UT7_T0,SD_D6,ETH_MDC +P6_7,GPIO,OSPI0_D7,UART0_RTS,PDM_C2,SPI1_SS3,UT7_T1,SD_D7,ETH_CRS_DV_A +P7_0,GPIO,,CMP3_OUT,SPI0_MISO,I2C0_SDA,UT8_T0,SD_CMD, +P7_1,GPIO,,CMP2_OUT,SPI0_MOSI,I2C0_SCL,UT8_T1,SD_CLK, +P7_2,GPIO,,UART3_CTS,CMP1_OUT,SPI0_SCLK,I2C1_SDA,UT9_T0,SD_RST +P7_3,GPIO,,UART3_RTS,CMP0_OUT,SPI0_SS0,I2C1_SCL,UT9_T1, +P7_4,GPIO,,LPUART_CTS,LPPDM_C2,LPSPI_MISO,LPI2C_SCL,UT10_T0, +P7_5,GPIO,,LPUART_RTS,,LPPDM_D2,LPSPI_MOSI,LPI2C_SDA,UT10_T1 +P7_6,GPIO,,LPUART_RX,,LPPDM_C3,LPSPI_SCLK,I3C_SDA,UT11_T0 +P7_7,GPIO,,LPUART_TX,,LPPDM_D3,LPSPI_SS,I3C_SCL,UT11_T1 +P8_0,GPIO,OSPI1_SCLKN,AUDIO_CLK,FAULT0_B,LPCAM_D0,SD_D0,CDC_D0,CAM_D0 +P8_1,GPIO,I2S2_SDI,FAULT1_B,LPCAM_D1,SD_D1,CDC_D1,CAM_D1, +P8_2,GPIO,I2S2_SDO,SPI0_SS3,FAULT2_B,LPCAM_D2,SD_D2,CDC_D2,CAM_D2 +P8_3,GPIO,I2S2_SCLK,SPI1_MISO,FAULT3_B,LPCAM_D3,SD_D3,CDC_D3,CAM_D3 +P8_4,GPIO,I2S2_WS,SPI1_MOSI,QEC0_X,LPCAM_D4,SD_D4,CDC_D4,CAM_D4 +P8_5,GPIO,,SPI1_SCLK,QEC0_Y,LPCAM_D5,SD_D5,CDC_D5,CAM_D5 +P8_6,GPIO,,I2S3_SCLK,QEC0_Z,LPCAM_D6,SD_D6,CDC_D6,CAM_D6 +P8_7,GPIO,,I2S3_WS,QEC1_X,LPCAM_D7,SD_D7,CDC_D7,CAM_D7 +P9_0,GPIO,,I2S3_SDI,QEC1_Y,SD_CMD,CDC_D8,CAM_D8, +P9_1,GPIO,LPUART_RX,I2S3_SDO,QEC1_Z,SD_CLK,CDC_D9,CAM_D9, +P9_2,GPIO,LPUART_TX,I2S3_SDI,SPI2_MISO,QEC2_X,SD_RST,CDC_D10,CAM_D10 +P9_3,GPIO,HFXO_OUT,UART7_RX,I2S3_SDO,SPI2_MOSI,QEC2_Y,CDC_D11,CAM_D11 +P9_4,GPIO,UART7_TX,I2S3_SCLK,SPI2_SCLK,I2C3_SDA,QEC2_Z,CDC_D12,CAM_D12 +P9_5,GPIO,OSPI1_D0,I2S3_WS,SPI2_SS0,I2C3_SCL,QEC3_X,CDC_D13,CAM_D13 +P9_6,GPIO,OSPI1_D1,AUDIO_CLK,SPI2_SS1,I2C3_SDA,QEC3_Y,CDC_D14,CAM_D14 +P9_7,GPIO,OSPI1_D2,UART7_DE,SPI2_SS2,I2C3_SCL,QEC3_Z,CDC_D15,CAM_D15 +P10_0,GPIO,OSPI1_D3,UART6_DE,SPI2_SS3,UT0_T0,LPCAM_HSYNC,CDC_D16,CAM_HSYNC +P10_1,GPIO,OSPI1_D4,,LPI2S_SDI,UT0_T1,LPCAM_VSYNC,CDC_D17,CAM_VSYNC +P10_2,GPIO,OSPI1_D5,,LPI2S_SDO,UT1_T0,LPCAM_PCLK,CDC_D18,CAM_PCLK +P10_3,GPIO,OSPI1_D6,,LPI2S_SCLK,UT1_T1,LPCAM_XVCLK,CDC_D19,CAM_XVCLK +P10_4,GPIO,OSPI1_D7,,LPI2S_WS,I2C0_SDA,UT2_T0,ETH_TXD0,CDC_D20 +P10_5,GPIO,UART6_RX,I2S2_SDI,SPI3_MISO,I2C0_SCL,UT2_T1,ETH_TXD1,CDC_D21 +P10_6,GPIO,UART6_TX,I2S2_SDO,SPI3_MOSI,I2C1_SDA,UT3_T0,ETH_TXEN,CDC_D22 +P10_7,GPIO,UART7_RX,I2S2_SCLK,SPI3_SCLK,I2C1_SCL,UT3_T1,CDC_D23,OSPI1_RXDS +P11_0,GPIO,OSPI1_D0,UART7_TX,I2S2_WS,SPI3_SS0,UT4_T0,ETH_REFCLK,CDC_D0 +P11_1,GPIO,OSPI1_D1,UART7_DE,SPI3_SS1,UT4_T1,ETH_MDIO,CDC_D1, +P11_2,GPIO,OSPI1_D2,UART6_DE,LPPDM_C2,SPI3_SS2,UT5_T0,ETH_MDC,CDC_D2 +P11_3,GPIO,OSPI1_D3,UART5_RX,LPPDM_C3,SPI3_SS3,UT5_T1,ETH_RXD0,CDC_D3 +P11_4,GPIO,OSPI1_D4,UART5_TX,PDM_C2,LPSPI_MISO,UT6_T0,ETH_RXD1,CDC_D4 +P11_5,GPIO,OSPI1_D5,UART6_RX,PDM_C3,LPSPI_MOSI,UT6_T1,ETH_CRS_DV_B,CDC_D5 +P11_6,GPIO,OSPI1_D6,UART6_TX,LPPDM_D2,LPSPI_SCLK,UT7_T0,ETH_RST,CDC_D6 +P11_7,GPIO,OSPI1_D7,UART5_DE,LPPDM_D3,LPSPI_SS,UT7_T1,ETH_IRQ,CDC_D7 +P12_0,GPIO,OSPI0_SCLK,AUDIO_CLK,I2S1_SDI,UT8_T0,CDC_D8,, +P12_1,GPIO,OSPI0_SCLKN,UART4_RX,I2S1_SDO,UT8_T1,CDC_D9,, +P12_2,GPIO,OSPI0_RXDS,UART4_TX,I2S1_SCLK,UT9_T0,CDC_D10,, +P12_3,GPIO,OSPI0_SS0,UART4_DE,I2S1_WS,UT9_T1,CDC_D11,, +P12_4,GPIO,OSPI0_SS1,SPI3_MISO,UT10_T0,,CDC_D12,, +P12_5,GPIO,,SPI3_MOSI,UT10_T1,,CDC_D13,, +P12_6,GPIO,,SPI3_SCLK,UT11_T0,,CDC_D14,, +P12_7,GPIO,OSPI1_RXDS,,SPI3_SS0,UT11_T1,CDC_D15,, +P13_0,GPIO,OSPI1_D0,,SPI3_SS1,QEC0_X,SD_D0,CDC_D16, +P13_1,GPIO,OSPI1_D1,SPI3_SS2,QEC0_Y,SD_D1,CDC_D17,, +P13_2,GPIO,OSPI1_D2,SPI3_SS3,QEC0_Z,SD_D2,CDC_D18,, +P13_3,GPIO,OSPI1_D3,SPI2_SS3,QEC1_X,SD_D3,CDC_D19,, +P13_4,GPIO,OSPI1_D4,LPI2S_SDI,QEC1_Y,SD_D4,CDC_D20,, +P13_5,GPIO,OSPI1_D5,LPI2S_SDO,QEC1_Z,SD_D5,CDC_D21,, +P13_6,GPIO,OSPI1_D6,LPI2S_SCLK,QEC2_X,SD_D6,CDC_D22,, +P13_7,GPIO,OSPI1_D7,LPI2S_WS,QEC2_Y,SD_D7,CDC_D23,, +P14_0,GPIO,OSPI1_SCLK,UART6_RX,QEC2_Z,SD_CMD,,, +P14_1,GPIO,OSPI1_SCLKN,UART6_TX,,QEC3_X,SD_CLK,, +P14_2,GPIO,OSPI1_SS0,UART7_RX,,QEC3_Y,SD_RST,, +P14_3,GPIO,OSPI1_SS1,UART7_TX,,QEC3_Z,,, +P14_4,GPIO,CMP3_OUT,SPI1_MISO,FAULT0_C,,,, +P14_5,GPIO,CMP2_OUT,SPI1_MOSI,FAULT1_C,,,, +P14_6,GPIO,CMP1_OUT,SPI1_SCLK,FAULT2_C,,,, +P14_7,GPIO,CMP0_OUT,SPI1_SS0,FAULT3_C,,,, +P15_0,GPIO,LPTMR0_CLK,,,,,, +P15_1,GPIO,LPTMR1_CLK,,,,,, +P15_2,GPIO,LPTMR2_CLK,,,,,, +P15_3,GPIO,LPTMR3_CLK,,,,,, +P15_4,GPIO,,,,,,, +P15_5,GPIO,,,,,,, +P15_6,GPIO,,,,,,, +P15_7,GPIO,,,,,,, diff --git a/ports/alif/mcu/make-pins.py b/ports/alif/mcu/make-pins.py index fb000082642bc..3666953e613f5 100755 --- a/ports/alif/mcu/make-pins.py +++ b/ports/alif/mcu/make-pins.py @@ -36,11 +36,19 @@ class AlifPin(boardgen.Pin): def __init__(self, cpu_pin_name): super().__init__(cpu_pin_name) - self._afs = ["MP_HAL_PIN_ALT_NONE"] * 8 + self._afs = [("NONE", -1)] * 8 # Called for each AF defined in the csv file for this pin. def add_af(self, af_idx, af_name, af): - self._afs[af_idx] = f"MP_HAL_PIN_ALT_{af}" + if af == "GPIO": + self._afs[af_idx] = "GPIO", -1 + elif af.startswith("ANA_S"): + self._afs[af_idx] = "ANA", int(af[5:]) + else: + m = re.match(r"([A-Z0-9]+[A-Z])(\d*)_([A-Z0-9_]+)$", af) + periph, unit, line = m.groups() + unit = -1 if unit == "" else int(unit) + self._afs[af_idx] = f"{periph}_{line}", unit # Emit the struct which contains the pin instance. def definition(self): @@ -63,7 +71,7 @@ def definition(self): base=base, adc12_periph=adc12_periph, adc12_channel=adc12_channel, - alt=", ".join([f"{af}" for af in self._afs]), + alt=", ".join([f"MP_HAL_PIN_ALT({func}, {unit})" for func, unit in self._afs]), ) ) diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 2ba62db2d2e5a..4f81439b5492e 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -92,34 +92,183 @@ extern ringbuf_t stdin_ringbuf; #define mp_hal_pin_obj_t const machine_pin_obj_t * +#define MP_HAL_PIN_ALT(function, unit) (MP_HAL_PIN_ALT_MAKE((MP_HAL_PIN_ALT_##function), (unit))) +#define MP_HAL_PIN_ALT_MAKE(function, unit) ((function) | ((unit) << 8)) +#define MP_HAL_PIN_ALT_GET_FUNC(alt) ((alt) & 0xff) +#define MP_HAL_PIN_ALT_GET_UNIT(alt) ((alt) >> 8) + enum { MP_HAL_PIN_ALT_NONE = 0, + MP_HAL_PIN_ALT_GPIO, MP_HAL_PIN_ALT_ANA, - MP_HAL_PIN_ALT_AUDIO, - MP_HAL_PIN_ALT_CAM, - MP_HAL_PIN_ALT_CDC, - MP_HAL_PIN_ALT_CMP, - MP_HAL_PIN_ALT_ETH, - MP_HAL_PIN_ALT_FAULT, - MP_HAL_PIN_ALT_HFXO, - MP_HAL_PIN_ALT_I2C, - MP_HAL_PIN_ALT_I2S, - MP_HAL_PIN_ALT_I3C, - MP_HAL_PIN_ALT_JTAG, - MP_HAL_PIN_ALT_LPCAM, - MP_HAL_PIN_ALT_LPI2C, - MP_HAL_PIN_ALT_LPI2S, - MP_HAL_PIN_ALT_LPPDM, - MP_HAL_PIN_ALT_LPSPI, - MP_HAL_PIN_ALT_LPTMR, - MP_HAL_PIN_ALT_LPUART, - MP_HAL_PIN_ALT_OSPI, - MP_HAL_PIN_ALT_PDM, - MP_HAL_PIN_ALT_QEC, - MP_HAL_PIN_ALT_SD, - MP_HAL_PIN_ALT_SPI, - MP_HAL_PIN_ALT_UART, - MP_HAL_PIN_ALT_UT, + MP_HAL_PIN_ALT_AUDIO_CLK, + MP_HAL_PIN_ALT_CAM_D0, + MP_HAL_PIN_ALT_CAM_D1, + MP_HAL_PIN_ALT_CAM_D2, + MP_HAL_PIN_ALT_CAM_D3, + MP_HAL_PIN_ALT_CAM_D4, + MP_HAL_PIN_ALT_CAM_D5, + MP_HAL_PIN_ALT_CAM_D6, + MP_HAL_PIN_ALT_CAM_D7, + MP_HAL_PIN_ALT_CAM_D8, + MP_HAL_PIN_ALT_CAM_D9, + MP_HAL_PIN_ALT_CAM_D10, + MP_HAL_PIN_ALT_CAM_D11, + MP_HAL_PIN_ALT_CAM_D12, + MP_HAL_PIN_ALT_CAM_D13, + MP_HAL_PIN_ALT_CAM_D14, + MP_HAL_PIN_ALT_CAM_D15, + MP_HAL_PIN_ALT_CAM_HSYNC, + MP_HAL_PIN_ALT_CAM_PCLK, + MP_HAL_PIN_ALT_CAM_VSYNC, + MP_HAL_PIN_ALT_CAM_XVCLK, + MP_HAL_PIN_ALT_CDC_D0, + MP_HAL_PIN_ALT_CDC_D1, + MP_HAL_PIN_ALT_CDC_D2, + MP_HAL_PIN_ALT_CDC_D3, + MP_HAL_PIN_ALT_CDC_D4, + MP_HAL_PIN_ALT_CDC_D5, + MP_HAL_PIN_ALT_CDC_D6, + MP_HAL_PIN_ALT_CDC_D7, + MP_HAL_PIN_ALT_CDC_D8, + MP_HAL_PIN_ALT_CDC_D9, + MP_HAL_PIN_ALT_CDC_D10, + MP_HAL_PIN_ALT_CDC_D11, + MP_HAL_PIN_ALT_CDC_D12, + MP_HAL_PIN_ALT_CDC_D13, + MP_HAL_PIN_ALT_CDC_D14, + MP_HAL_PIN_ALT_CDC_D15, + MP_HAL_PIN_ALT_CDC_D16, + MP_HAL_PIN_ALT_CDC_D17, + MP_HAL_PIN_ALT_CDC_D18, + MP_HAL_PIN_ALT_CDC_D19, + MP_HAL_PIN_ALT_CDC_D20, + MP_HAL_PIN_ALT_CDC_D21, + MP_HAL_PIN_ALT_CDC_D22, + MP_HAL_PIN_ALT_CDC_D23, + MP_HAL_PIN_ALT_CDC_DE, + MP_HAL_PIN_ALT_CDC_HSYNC, + MP_HAL_PIN_ALT_CDC_PCLK, + MP_HAL_PIN_ALT_CDC_VSYNC, + MP_HAL_PIN_ALT_CMP_OUT, + MP_HAL_PIN_ALT_ETH_CRS_DV_A, + MP_HAL_PIN_ALT_ETH_CRS_DV_B, + MP_HAL_PIN_ALT_ETH_CRS_DV_C, + MP_HAL_PIN_ALT_ETH_IRQ, + MP_HAL_PIN_ALT_ETH_MDC, + MP_HAL_PIN_ALT_ETH_MDIO, + MP_HAL_PIN_ALT_ETH_REFCLK, + MP_HAL_PIN_ALT_ETH_RST, + MP_HAL_PIN_ALT_ETH_RXD0, + MP_HAL_PIN_ALT_ETH_RXD1, + MP_HAL_PIN_ALT_ETH_TXD0, + MP_HAL_PIN_ALT_ETH_TXD1, + MP_HAL_PIN_ALT_ETH_TXEN, + MP_HAL_PIN_ALT_FAULT_A, + MP_HAL_PIN_ALT_FAULT_B, + MP_HAL_PIN_ALT_FAULT_C, + MP_HAL_PIN_ALT_HFXO_OUT, + MP_HAL_PIN_ALT_I2C_SCL, + MP_HAL_PIN_ALT_I2C_SDA, + MP_HAL_PIN_ALT_I2S_SCLK, + MP_HAL_PIN_ALT_I2S_SDI, + MP_HAL_PIN_ALT_I2S_SDO, + MP_HAL_PIN_ALT_I2S_WS, + MP_HAL_PIN_ALT_I3C_SCL, + MP_HAL_PIN_ALT_I3C_SDA, + MP_HAL_PIN_ALT_JTAG_TCK, + MP_HAL_PIN_ALT_JTAG_TDATA0, + MP_HAL_PIN_ALT_JTAG_TDATA1, + MP_HAL_PIN_ALT_JTAG_TDATA2, + MP_HAL_PIN_ALT_JTAG_TDATA3, + MP_HAL_PIN_ALT_JTAG_TDI, + MP_HAL_PIN_ALT_JTAG_TDO, + MP_HAL_PIN_ALT_JTAG_TMS, + MP_HAL_PIN_ALT_JTAG_TRACECLK, + MP_HAL_PIN_ALT_LPCAM_D0, + MP_HAL_PIN_ALT_LPCAM_D1, + MP_HAL_PIN_ALT_LPCAM_D2, + MP_HAL_PIN_ALT_LPCAM_D3, + MP_HAL_PIN_ALT_LPCAM_D4, + MP_HAL_PIN_ALT_LPCAM_D5, + MP_HAL_PIN_ALT_LPCAM_D6, + MP_HAL_PIN_ALT_LPCAM_D7, + MP_HAL_PIN_ALT_LPCAM_HSYNC, + MP_HAL_PIN_ALT_LPCAM_PCLK, + MP_HAL_PIN_ALT_LPCAM_VSYNC, + MP_HAL_PIN_ALT_LPCAM_XVCLK, + MP_HAL_PIN_ALT_LPI2C_SCL, + MP_HAL_PIN_ALT_LPI2C_SDA, + MP_HAL_PIN_ALT_LPI2S_SCLK, + MP_HAL_PIN_ALT_LPI2S_SDI, + MP_HAL_PIN_ALT_LPI2S_SDO, + MP_HAL_PIN_ALT_LPI2S_WS, + MP_HAL_PIN_ALT_LPPDM_C0, + MP_HAL_PIN_ALT_LPPDM_C1, + MP_HAL_PIN_ALT_LPPDM_C2, + MP_HAL_PIN_ALT_LPPDM_C3, + MP_HAL_PIN_ALT_LPPDM_D0, + MP_HAL_PIN_ALT_LPPDM_D1, + MP_HAL_PIN_ALT_LPPDM_D2, + MP_HAL_PIN_ALT_LPPDM_D3, + MP_HAL_PIN_ALT_LPSPI_MISO, + MP_HAL_PIN_ALT_LPSPI_MOSI, + MP_HAL_PIN_ALT_LPSPI_SCLK, + MP_HAL_PIN_ALT_LPSPI_SS, + MP_HAL_PIN_ALT_LPTMR_CLK, + MP_HAL_PIN_ALT_LPUART_CTS, + MP_HAL_PIN_ALT_LPUART_RTS, + MP_HAL_PIN_ALT_LPUART_RX, + MP_HAL_PIN_ALT_LPUART_TX, + MP_HAL_PIN_ALT_OSPI_D0, + MP_HAL_PIN_ALT_OSPI_D1, + MP_HAL_PIN_ALT_OSPI_D2, + MP_HAL_PIN_ALT_OSPI_D3, + MP_HAL_PIN_ALT_OSPI_D4, + MP_HAL_PIN_ALT_OSPI_D5, + MP_HAL_PIN_ALT_OSPI_D6, + MP_HAL_PIN_ALT_OSPI_D7, + MP_HAL_PIN_ALT_OSPI_RXDS, + MP_HAL_PIN_ALT_OSPI_SCLK, + MP_HAL_PIN_ALT_OSPI_SCLKN, + MP_HAL_PIN_ALT_OSPI_SS0, + MP_HAL_PIN_ALT_OSPI_SS1, + MP_HAL_PIN_ALT_PDM_C0, + MP_HAL_PIN_ALT_PDM_C1, + MP_HAL_PIN_ALT_PDM_C2, + MP_HAL_PIN_ALT_PDM_C3, + MP_HAL_PIN_ALT_PDM_D0, + MP_HAL_PIN_ALT_PDM_D1, + MP_HAL_PIN_ALT_PDM_D2, + MP_HAL_PIN_ALT_PDM_D3, + MP_HAL_PIN_ALT_QEC_X, + MP_HAL_PIN_ALT_QEC_Y, + MP_HAL_PIN_ALT_QEC_Z, + MP_HAL_PIN_ALT_SD_CLK, + MP_HAL_PIN_ALT_SD_CMD, + MP_HAL_PIN_ALT_SD_D0, + MP_HAL_PIN_ALT_SD_D1, + MP_HAL_PIN_ALT_SD_D2, + MP_HAL_PIN_ALT_SD_D3, + MP_HAL_PIN_ALT_SD_D4, + MP_HAL_PIN_ALT_SD_D5, + MP_HAL_PIN_ALT_SD_D6, + MP_HAL_PIN_ALT_SD_D7, + MP_HAL_PIN_ALT_SD_RST, + MP_HAL_PIN_ALT_SPI_MISO, + MP_HAL_PIN_ALT_SPI_MOSI, + MP_HAL_PIN_ALT_SPI_SCLK, + MP_HAL_PIN_ALT_SPI_SS0, + MP_HAL_PIN_ALT_SPI_SS1, + MP_HAL_PIN_ALT_SPI_SS2, + MP_HAL_PIN_ALT_SPI_SS3, + MP_HAL_PIN_ALT_UART_CTS, + MP_HAL_PIN_ALT_UART_DE, + MP_HAL_PIN_ALT_UART_RTS, + MP_HAL_PIN_ALT_UART_RX, + MP_HAL_PIN_ALT_UART_TX, + MP_HAL_PIN_ALT_UT_T0, + MP_HAL_PIN_ALT_UT_T1, }; typedef struct _machine_pin_obj_t { @@ -130,7 +279,7 @@ typedef struct _machine_pin_obj_t { uint8_t adc12_periph : 2; uint8_t adc12_channel : 3; qstr name; - const uint8_t alt[8]; + const uint16_t alt[8]; // holds result of MP_HAL_PIN_ALT_MAKE(function, unit) } machine_pin_obj_t; mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in); diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c index 4fe82ce123176..b646e6f2be471 100644 --- a/ports/alif/mpuart.c +++ b/ports/alif/mpuart.c @@ -91,8 +91,10 @@ void mp_uart_init(unsigned int uart_id, uint32_t baudrate, uart_state_t *state = &uart_state[uart_id]; // Configure TX/RX pins. - mp_hal_pin_config(tx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, false); - mp_hal_pin_config(rx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, true); + mp_hal_pin_config(tx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_TX, uart_id), false); + mp_hal_pin_config(rx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_RX, uart_id), true); // Configure the UART peripheral. select_uart_clock_syst_pclk(uart_id); @@ -152,11 +154,13 @@ void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj unsigned int flow = UART_FLOW_CONTROL_NONE; if (rts != NULL) { flow |= UART_FLOW_CONTROL_RTS; - mp_hal_pin_config(rts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, false); + mp_hal_pin_config(rts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_RTS, uart_id), false); } if (cts != NULL) { flow |= UART_FLOW_CONTROL_CTS; - mp_hal_pin_config(cts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_UART, true); + mp_hal_pin_config(cts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_CTS, uart_id), true); } uart_set_flow_control(uart, flow); } diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 17c8be8128da3..9ba7ebe6eca63 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -262,40 +262,41 @@ int ospi_flash_init(void) { self->pin = pin; + unsigned int unit = pin->peripheral_number; mp_hal_pin_config(pin->pin_cs, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_SS0, unit), false); mp_hal_pin_config(pin->pin_clk_p, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_SCLK, unit), false); if (pin->pin_clk_n != NULL) { mp_hal_pin_config(pin->pin_clk_n, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, false); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_SCLKN, unit), false); } if (pin->pin_rwds != NULL) { mp_hal_pin_config(pin->pin_rwds, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_RXDS, unit), true); if (pin->pin_rwds->port == PORT_10 && pin->pin_rwds->pin == PIN_7) { // Alif: P5_6 is needed to support proper alt function selection of P10_7. mp_hal_pin_config(pin_P5_6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_RXDS, unit), true); } } mp_hal_pin_config(pin->pin_d0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D0, unit), true); mp_hal_pin_config(pin->pin_d1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D1, unit), true); mp_hal_pin_config(pin->pin_d2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D2, unit), true); mp_hal_pin_config(pin->pin_d3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D3, unit), true); if (pin->pin_d4 != NULL) { mp_hal_pin_config(pin->pin_d4, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D4, unit), true); mp_hal_pin_config(pin->pin_d5, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D5, unit), true); mp_hal_pin_config(pin->pin_d6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D6, unit), true); mp_hal_pin_config(pin->pin_d7, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, - MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT_OSPI, true); + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D7, unit), true); } // Reset the SPI flash. From 3564ce5bd8c22254c39a556232891f9d43201eac Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Mar 2025 12:35:26 +1100 Subject: [PATCH 521/546] alif/ospi_flash: Don't invalidate cache after erasing/writing. It's not needed, the MPU configures the XIP as non-cacheable. Signed-off-by: Damien George --- ports/alif/ospi_flash.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 9ba7ebe6eca63..8bcd1fb5b13af 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -393,14 +393,11 @@ int ospi_flash_erase_sector(uint32_t addr) { ospi_flash_write_cmd(self, self->set->write_en); int ret = ospi_flash_wait_wel1(self); - if (ret < 0) { - return ret; + if (ret == 0) { + ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); + ret = ospi_flash_wait_wip0(self); } - ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); - ret = ospi_flash_wait_wip0(self); - - SCB_InvalidateDCache_by_Addr(global_flash.cfg.xip_base + addr, MICROPY_HW_FLASH_BLOCK_SIZE_BYTES); return ret; } @@ -463,7 +460,6 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { offset = 0; } - SCB_InvalidateDCache_by_Addr(global_flash.cfg.xip_base + addr, len); return ret; } From df5e4ced762aac83200906c34e6dfcda7b7ccd6e Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Mar 2025 12:35:50 +1100 Subject: [PATCH 522/546] alif/ospi_flash_settings: Use 8-bit DFS for XIP. To match the instruction length, so the DFS is restored to the XIP value after an erase or write (due to the final wait WIP). Signed-off-by: Damien George --- ports/alif/ospi_flash_settings.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/alif/ospi_flash_settings.h b/ports/alif/ospi_flash_settings.h index 72403c26b67b5..89686c4946c4b 100644 --- a/ports/alif/ospi_flash_settings.h +++ b/ports/alif/ospi_flash_settings.h @@ -53,7 +53,7 @@ .rxds = false, \ .bswap16 = false, \ .inst_len = OSPI_INST_L_8bit, \ - .xip_data_len = OSPI_DATA_L_16bit, \ + .xip_data_len = OSPI_DATA_L_8bit, \ .read_sr = 0x05, \ .read_sr_dummy_cycles = 8, \ .write_en = 0x06, \ @@ -70,7 +70,7 @@ .rxds = false, \ .bswap16 = false, \ .inst_len = OSPI_INST_L_8bit, \ - .xip_data_len = OSPI_DATA_L_16bit, \ + .xip_data_len = OSPI_DATA_L_8bit, \ .read_sr = 0x05, \ .read_sr_dummy_cycles = 8, \ .write_en = 0x06, \ From c395f5ebb07a2ebcafba32178f5d7680771969a3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 1 Apr 2025 23:07:27 +1100 Subject: [PATCH 523/546] alif/ospi_flash: Restore XIP settings after erase and write. Signed-off-by: Damien George --- ports/alif/ospi_ext.c | 28 ++++++++++++++++++---------- ports/alif/ospi_ext.h | 1 + ports/alif/ospi_flash.c | 16 +++++++++++++++- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/ports/alif/ospi_ext.c b/ports/alif/ospi_ext.c index e3389d8bd54da..6ddaaf4e5a325 100644 --- a/ports/alif/ospi_ext.c +++ b/ports/alif/ospi_ext.c @@ -206,20 +206,22 @@ void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_l spi_enable(ospi_cfg); } +static inline uint32_t ospi_xip_ctrlr0(uint32_t data_len) { + return CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (0 << CTRLR0_SCPOL_OFFSET) + | (0 << CTRLR0_SCPH_OFFSET) + | (0 << CTRLR0_SSTE_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); +} + void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles) { spi_disable(ospi_cfg); - uint32_t val = CTRLR0_IS_MST - | (OCTAL << CTRLR0_SPI_FRF_OFFSET) - | (0 << CTRLR0_SCPOL_OFFSET) - | (0 << CTRLR0_SCPH_OFFSET) - | (0 << CTRLR0_SSTE_OFFSET) - | (TMOD_RO << CTRLR0_TMOD_OFFSET) - | (data_len << CTRLR0_DFS_OFFSET); - - ospi_writel(ospi_cfg, ctrlr0, val); + ospi_writel(ospi_cfg, ctrlr0, ospi_xip_ctrlr0(data_len)); - val = (OCTAL << XIP_CTRL_FRF_OFFSET) + uint32_t val = (OCTAL << XIP_CTRL_FRF_OFFSET) | (0x2 << XIP_CTRL_TRANS_TYPE_OFFSET) | (ADDR_L32bit << XIP_CTRL_ADDR_L_OFFSET) | (inst_len << XIP_CTRL_INST_L_OFFSET) @@ -292,3 +294,9 @@ void ospi_xip_exit_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint16_t i ospi_xip_enable(ospi_cfg); ospi_xip_disable(ospi_cfg); } + +void ospi_xip_restore_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len) { + spi_disable(ospi_cfg); + ospi_writel(ospi_cfg, ctrlr0, ospi_xip_ctrlr0(data_len)); + spi_enable(ospi_cfg); +} diff --git a/ports/alif/ospi_ext.h b/ports/alif/ospi_ext.h index e5a2b50beb693..65e2ced1ff7bc 100644 --- a/ports/alif/ospi_ext.h +++ b/ports/alif/ospi_ext.h @@ -53,5 +53,6 @@ void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_l void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles); void ospi_xip_exit_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint16_t incr_command, uint16_t wrap_command); +void ospi_xip_restore_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len); #endif // MICROPY_INCLUDED_ALIF_OSPI_EXT_H diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index 8bcd1fb5b13af..f78002eb0ef97 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -354,8 +354,9 @@ int ospi_flash_init(void) { } } - // Enter XIP mode. It will be disabled during flash read/erase/write. + // Enter XIP mode. ospi_flash_xip_enter(self); + return 0; } @@ -385,6 +386,13 @@ int ospi_flash_xip_exit(ospi_flash_t *self) { return 0; } +int ospi_flash_xip_restore(ospi_flash_t *self) { + if (self->xip_active) { + ospi_xip_restore_ext(&self->cfg, self->set->xip_data_len); + } + return 0; +} + /******************************************************************************/ // Top-level read/erase/write functions. @@ -398,6 +406,8 @@ int ospi_flash_erase_sector(uint32_t addr) { ret = ospi_flash_wait_wip0(self); } + ospi_flash_xip_restore(self); + return ret; } @@ -442,6 +452,8 @@ static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src } int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { + ospi_flash_t *self = &global_flash; + int ret = 0; uint32_t offset = addr & (PAGE_SIZE - 1); @@ -460,6 +472,8 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { offset = 0; } + ospi_flash_xip_restore(self); + return ret; } From b79b64a726d9f1796605f75980d2ae52595863f8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Mar 2025 22:40:58 +1100 Subject: [PATCH 524/546] alif/mpu: Add MPU region for OSPI1 XIP memory range. Signed-off-by: Damien George --- ports/alif/mpu.c | 4 ++++ ports/alif/mpu.h | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ports/alif/mpu.c b/ports/alif/mpu.c index 60753674ae1e5..9a051794de845 100644 --- a/ports/alif/mpu.c +++ b/ports/alif/mpu.c @@ -54,6 +54,10 @@ static const ARM_MPU_Region_t mpu_table[] __STARTUP_RO_DATA_ATTRIBUTE = { .RBAR = ARM_MPU_RBAR(0xA0000000, ARM_MPU_SH_NON, 1, 1, 0), .RLAR = ARM_MPU_RLAR(0xBFFFFFFF, MP_MPU_ATTR_NORMAL_NON_CACHEABLE) }, + [MP_MPU_REGION_OSPI1_XIP] = { /* OSPI1 XIP flash - 512MB : RO-1, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0xC0000000, ARM_MPU_SH_NON, 1, 1, 0), + .RLAR = ARM_MPU_RLAR(0xDFFFFFFF, MP_MPU_ATTR_NORMAL_NON_CACHEABLE) + }, }; void MPU_Load_Regions(void) { diff --git a/ports/alif/mpu.h b/ports/alif/mpu.h index 1d3602941efb7..f4df496683e55 100644 --- a/ports/alif/mpu.h +++ b/ports/alif/mpu.h @@ -38,6 +38,7 @@ #define MP_MPU_REGION_MRAM (3) #define MP_MPU_REGION_OSPI_REGISTERS (4) #define MP_MPU_REGION_OSPI0_XIP (5) -#define MP_MPU_REGION_OPENAMP (6) +#define MP_MPU_REGION_OSPI1_XIP (6) +#define MP_MPU_REGION_OPENAMP (7) void mpu_config_mram(bool read_only); From 7c216d17b6e1d8394d05fd5eaae1c812c3846963 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 17 Dec 2023 12:12:06 +0100 Subject: [PATCH 525/546] alif/boards/ALIF_ENSEMBLE: Add Alif Ensemble board config. Signed-off-by: iabdalkader Signed-off-by: Damien George --- ports/alif/boards/ALIF_ENSEMBLE/board.c | 56 +++++++++++++++++++ ports/alif/boards/ALIF_ENSEMBLE/board.ld.S | 14 +++++ .../alif/boards/ALIF_ENSEMBLE/mpconfigboard.h | 56 +++++++++++++++++++ .../boards/ALIF_ENSEMBLE/mpconfigboard.mk | 11 ++++ .../alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h | 5 ++ ports/alif/boards/ALIF_ENSEMBLE/pins.csv | 30 ++++++++++ 6 files changed, 172 insertions(+) create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/board.c create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/board.ld.S create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.h create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.mk create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h create mode 100644 ports/alif/boards/ALIF_ENSEMBLE/pins.csv diff --git a/ports/alif/boards/ALIF_ENSEMBLE/board.c b/ports/alif/boards/ALIF_ENSEMBLE/board.c new file mode 100644 index 0000000000000..72b93e31ffbea --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/board.c @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "ospi_ext.h" +#include "ospi_flash.h" + +const ospi_pin_settings_t ospi_pin_settings = { + .peripheral_number = 1, + .pin_reset = pin_OSPI1_RESET, + .pin_cs = pin_OSPI1_CS, + .pin_clk_p = pin_OSPI1_SCLK, + .pin_clk_n = NULL, + .pin_rwds = pin_OSPI1_RXDS, + .pin_d0 = pin_OSPI1_D0, + .pin_d1 = pin_OSPI1_D1, + .pin_d2 = pin_OSPI1_D2, + .pin_d3 = pin_OSPI1_D3, + .pin_d4 = pin_OSPI1_D4, + .pin_d5 = pin_OSPI1_D5, + .pin_d6 = pin_OSPI1_D6, + .pin_d7 = pin_OSPI1_D7, +}; + +const ospi_flash_settings_t ospi_flash_settings[] = { + { + .jedec_id = 0x1a5b9d, + .freq_hz = 100000000, + .read_dummy_cycles = 9, + OSPI_FLASH_SETTINGS_IS25, + }, +}; +const size_t ospi_flash_settings_len = 1; diff --git a/ports/alif/boards/ALIF_ENSEMBLE/board.ld.S b/ports/alif/boards/ALIF_ENSEMBLE/board.ld.S new file mode 100644 index 0000000000000..7adcdbbcc3761 --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/board.ld.S @@ -0,0 +1,14 @@ +#include "mcu/ensemble.ld.S" + +/* Define ROMFS partition locations. */ +#if CORE_M55_HP +/* The HP core has access to the external OSPI1 flash and MRAM ROMFS partitions. */ +_micropy_hw_romfs_part0_start = 0xc1000000; +_micropy_hw_romfs_part0_size = 16M; +_micropy_hw_romfs_part1_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part1_size = LENGTH(MRAM_FS); +#else +/* The HP core has access to the MRAM ROMFS partition. */ +_micropy_hw_romfs_part0_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part0_size = LENGTH(MRAM_FS); +#endif diff --git a/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.h b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.h new file mode 100644 index 0000000000000..8a6003ebbfb53 --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.h @@ -0,0 +1,56 @@ +#define MICROPY_HW_BOARD_NAME "Alif Ensemble DevKit" +#define MICROPY_HW_MCU_NAME "AE722F80F55D5XX" + +#define MICROPY_HW_ENABLE_UART_REPL (CORE_M55_HP) +#define MICROPY_HW_UART_REPL (4) +#define MICROPY_HW_USB_MSC (1) +#define MICROPY_HW_ENABLE_HW_I2C (1) + +// ROMFS partitions +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (CORE_M55_HP) + +// I2C buses +#define MICROPY_HW_I2C0_SCL (pin_P0_3) +#define MICROPY_HW_I2C0_SDA (pin_P0_2) +#define MICROPY_HW_I2C1_SCL (pin_P3_7) +#define MICROPY_HW_I2C1_SDA (pin_P3_6) +#define MICROPY_HW_I2C2_SCL (pin_P5_1) +#define MICROPY_HW_I2C2_SDA (pin_P5_0) +#define MICROPY_HW_I2C3_SCL (pin_P1_1) +#define MICROPY_HW_I2C3_SDA (pin_P1_0) + +// SPI buses +#define MICROPY_HW_SPI0_MISO (pin_P1_0) +#define MICROPY_HW_SPI0_MOSI (pin_P1_1) +#define MICROPY_HW_SPI0_SCK (pin_P1_2) +#define MICROPY_HW_SPI1_MISO (pin_P2_4) +#define MICROPY_HW_SPI1_MOSI (pin_P2_5) +#define MICROPY_HW_SPI1_SCK (pin_P2_6) +#define MICROPY_HW_SPI2_MISO (pin_P4_2) +#define MICROPY_HW_SPI2_MOSI (pin_P4_3) +#define MICROPY_HW_SPI2_SCK (pin_P4_4) +#define MICROPY_HW_SPI3_MISO (pin_P12_4) +#define MICROPY_HW_SPI3_MOSI (pin_P12_5) +#define MICROPY_HW_SPI3_SCK (pin_P12_6) +#define MICROPY_HW_LPSPI0_MISO (pin_P7_4) +#define MICROPY_HW_LPSPI0_MOSI (pin_P7_5) +#define MICROPY_HW_LPSPI0_SCK (pin_P7_6) + +// UART buses +#define MICROPY_HW_UART0_TX (pin_P0_1) +#define MICROPY_HW_UART0_RX (pin_P0_0) +#define MICROPY_HW_UART0_RTS (pin_P0_3) +#define MICROPY_HW_UART0_CTS (pin_P0_2) +#define MICROPY_HW_UART1_TX (pin_P0_5) +#define MICROPY_HW_UART1_RX (pin_P0_4) +#define MICROPY_HW_UART1_RTS (pin_P0_7) +#define MICROPY_HW_UART1_CTS (pin_P0_6) +#define MICROPY_HW_REPL_UART_TX (pin_P12_2) +#define MICROPY_HW_REPL_UART_RX (pin_P12_1) + +// This is used for alif.Flash() and USB MSC. +#define MICROPY_HW_FLASH_STORAGE_BASE_ADDR (0) +#define MICROPY_HW_FLASH_STORAGE_BYTES (32 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_FS_BYTES (16 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_ROMFS_BYTES (16 * 1024 * 1024) diff --git a/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.mk b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.mk new file mode 100644 index 0000000000000..d35a7aad84a07 --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.mk @@ -0,0 +1,11 @@ +MCU_SERIES = E7 +MCU_VARIANT = AE722F80F55D5XX +JLINK_DEV = AE722F80F55D5_HP +LD_FILE = boards/ALIF_ENSEMBLE/board.ld.S + +ALIF_TOOLKIT_CFG_PART = AE722F80F55D5LS +ALIF_TOOLKIT_CFG_FILE = \"app-device-config-ae7.json\" + +MICROPY_FLOAT_IMPL = float +MICROPY_PY_OPENAMP = 1 +MICROPY_PY_OPENAMP_REMOTEPROC = 1 diff --git a/ports/alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h b/ports/alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h new file mode 100644 index 0000000000000..49921bdd22ddc --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h @@ -0,0 +1,5 @@ +// This file is needed by ospi_xip/source/ospi/ospi_drv.c. +#define OSPI_XIP_ENABLE_AES_DECRYPTION (0) +#define OSPI_XIP_RX_SAMPLE_DELAY (3) +#define OSPI_XIP_DDR_DRIVE_EDGE (1) +#define OSPI_XIP_RXDS_DELAY (12) diff --git a/ports/alif/boards/ALIF_ENSEMBLE/pins.csv b/ports/alif/boards/ALIF_ENSEMBLE/pins.csv new file mode 100644 index 0000000000000..ec397d9cf604c --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/pins.csv @@ -0,0 +1,30 @@ +# OSP1 flash +OSPI1_RESET,P15_7 +OSPI1_CS,P5_7 +OSPI1_SCLK,P5_5 +OSPI1_D0,P9_5 +OSPI1_D1,P9_6 +OSPI1_D2,P9_7 +OSPI1_D3,P10_0 +OSPI1_D4,P10_1 +OSPI1_D5,P10_2 +OSPI1_D6,P10_3 +OSPI1_D7,P10_4 +OSPI1_RXDS,P10_7 + +LED_BLUE,P12_0 +LED_RED,P12_3 +JOY_LEFT,P15_0 +JOY_RIGHT,P15_1 + +# UART buses +UART0_TX,P0_1 +UART0_RX,P0_0 +UART0_RTS,P0_3 +UART0_CTS,P0_2 +UART1_TX,P0_5 +UART1_RX,P0_4 +UART1_RTS,P0_7 +UART1_CTS,P0_6 +REPL_UART_TX,P12_2 +REPL_UART_RX,P12_1 From 704d2f2d57b3e30b3b0d0cf02849e9784b60ccb6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 2 Sep 2024 21:24:47 +1000 Subject: [PATCH 526/546] alif/boards/OPENMV_AE3: Add OpenMV AE3 board definition. Supports Murata 1YN for WiFi and BLE. Signed-off-by: iabdalkader Signed-off-by: Damien George --- ports/alif/boards/OPENMV_AE3/board.c | 192 ++++++++++++++++++ ports/alif/boards/OPENMV_AE3/board.ld.S | 14 ++ ports/alif/boards/OPENMV_AE3/mpconfigboard.h | 85 ++++++++ ports/alif/boards/OPENMV_AE3/mpconfigboard.mk | 22 ++ ports/alif/boards/OPENMV_AE3/ospi_xip_user.h | 6 + ports/alif/boards/OPENMV_AE3/pins.csv | 69 +++++++ 6 files changed, 388 insertions(+) create mode 100644 ports/alif/boards/OPENMV_AE3/board.c create mode 100644 ports/alif/boards/OPENMV_AE3/board.ld.S create mode 100644 ports/alif/boards/OPENMV_AE3/mpconfigboard.h create mode 100644 ports/alif/boards/OPENMV_AE3/mpconfigboard.mk create mode 100644 ports/alif/boards/OPENMV_AE3/ospi_xip_user.h create mode 100644 ports/alif/boards/OPENMV_AE3/pins.csv diff --git a/ports/alif/boards/OPENMV_AE3/board.c b/ports/alif/boards/OPENMV_AE3/board.c new file mode 100644 index 0000000000000..65da152d8536f --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/board.c @@ -0,0 +1,192 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 "ospi_ext.h" +#include "ospi_flash.h" +#include "se_services.h" + +#define OMV_BOOT_MAGIC_ADDR (0x200FFFFCU) +#define OMV_BOOT_MAGIC_VALUE (0xB00710ADU) + +#if CORE_M55_HP +#define NPU_IRQ_NUMBER NPU_HP_IRQ_IRQn +#define NPU_BASE_ADDRESS (void *)NPU_HP_BASE +#else +#define NPU_IRQ_NUMBER NPU_HE_IRQ_IRQn +#define NPU_BASE_ADDRESS (void *)NPU_HE_BASE +#endif + +typedef struct { + volatile uint32_t ID; // 0x0 + volatile uint32_t STATUS; // 0x4 + volatile uint32_t CMD; // 0x8 + volatile uint32_t RESET; // 0xC +} npu_regs_t; + +#define NPU ((npu_regs_t *)NPU_BASE_ADDRESS) + +const ospi_pin_settings_t ospi_pin_settings = { + .peripheral_number = 0, + .pin_reset = pin_FLASH_RESET, + .pin_cs = pin_FLASH_CS, + .pin_clk_p = pin_FLASH_SCLK_P, + .pin_clk_n = pin_FLASH_SCLK_N, + .pin_rwds = pin_FLASH_DQSM, + .pin_d0 = pin_FLASH_D0, + .pin_d1 = pin_FLASH_D1, + .pin_d2 = pin_FLASH_D2, + .pin_d3 = pin_FLASH_D3, + .pin_d4 = pin_FLASH_D4, + .pin_d5 = pin_FLASH_D5, + .pin_d6 = pin_FLASH_D6, + .pin_d7 = pin_FLASH_D7, +}; + +const ospi_flash_settings_t ospi_flash_settings[] = { + { + .jedec_id = 0x3980c2, + .freq_hz = 100000000, + .read_dummy_cycles = 10, + OSPI_FLASH_SETTINGS_MX25, + }, + { + .jedec_id = 0x195b9d, + .freq_hz = 100000000, + .read_dummy_cycles = 9, + OSPI_FLASH_SETTINGS_IS25, + }, + { + .jedec_id = 0x17bb6b, + .freq_hz = 100000000, + .read_dummy_cycles = 7, + OSPI_FLASH_SETTINGS_EM, + }, +}; +const size_t ospi_flash_settings_len = 3; + +void board_startup(void) { + // Switch the USB multiplexer to use the Alif USB port. + mp_hal_pin_output(pin_USB_D_SEL); + mp_hal_pin_high(pin_USB_D_SEL); +} + +void board_enter_bootloader(void) { + *((uint32_t *)OMV_BOOT_MAGIC_ADDR) = OMV_BOOT_MAGIC_VALUE; + NVIC_SystemReset(); +} + +void board_early_init(void) { + // Set default run profile + run_profile_t run_profile = { + .dcdc_mode = DCDC_MODE_PWM, + .dcdc_voltage = DCDC_VOUT_0825, + // CLK_SRC_LFRC or CLK_SRC_LFXO + .aon_clk_src = CLK_SRC_LFXO, + // CLK_SRC_HFRC, CLK_SRC_HFXO or CLK_SRC_PLL + .run_clk_src = CLK_SRC_PLL, + #if CORE_M55_HP + .cpu_clk_freq = CLOCK_FREQUENCY_400MHZ, + #else + .cpu_clk_freq = CLOCK_FREQUENCY_160MHZ, + #endif + .scaled_clk_freq = SCALED_FREQ_XO_HIGH_DIV_38_4_MHZ, + // AON, modem aon, SSE-700 AON, modem, SYSTOP, DEBUG, SE + .power_domains = PD_VBAT_AON_MASK | PD_SSE700_AON_MASK | PD_SYST_MASK | + PD_DBSS_MASK | PD_SESS_MASK | PD_SRAMS_MASK | PD_SRAM_CTRL_AON_MASK, + // Add all memories + .memory_blocks = SERAM_MASK | SRAM0_MASK | SRAM1_MASK | MRAM_MASK | BACKUP4K_MASK | + SRAM6A_MASK | SRAM6B_MASK | SRAM7_1_MASK | SRAM7_2_MASK | SRAM7_3_MASK | + SRAM8_MASK | SRAM9_MASK | FWRAM_MASK, + .phy_pwr_gating = LDO_PHY_MASK | USB_PHY_MASK | MIPI_TX_DPHY_MASK | MIPI_RX_DPHY_MASK | + MIPI_PLL_DPHY_MASK, + .vdd_ioflex_3V3 = IOFLEX_LEVEL_3V3, + }; + + if (se_services_set_run_profile(&run_profile)) { + MICROPY_BOARD_FATAL_ERROR("se_services_set_run_profile"); + } + + // Set default off profile + off_profile_t off_profile = { + .dcdc_mode = DCDC_MODE_PWM, + .dcdc_voltage = DCDC_VOUT_0825, + // CLK_SRC_LFRC or CLK_SRC_LFXO + .aon_clk_src = CLK_SRC_LFXO, + // CLK_SRC_HFRC, CLK_SRC_HFXO or CLK_SRC_PLL + .stby_clk_src = CLK_SRC_HFRC, + .stby_clk_freq = SCALED_FREQ_RC_STDBY_76_8_MHZ, + // Disable all power domains. + .power_domains = 0, + // Add all memories + .memory_blocks = SERAM_MASK | SRAM0_MASK | SRAM1_MASK | MRAM_MASK | BACKUP4K_MASK | + SRAM6A_MASK | SRAM6B_MASK | SRAM7_1_MASK | SRAM7_2_MASK | SRAM7_3_MASK | + SRAM8_MASK | SRAM9_MASK | FWRAM_MASK, + .phy_pwr_gating = LDO_PHY_MASK | USB_PHY_MASK | MIPI_TX_DPHY_MASK | MIPI_RX_DPHY_MASK | + MIPI_PLL_DPHY_MASK, + .vdd_ioflex_3V3 = IOFLEX_LEVEL_3V3, + .vtor_address = SCB->VTOR, + .vtor_address_ns = SCB->VTOR, + .ewic_cfg = EWIC_RTC_A, + .wakeup_events = WE_LPRTC, + }; + + if (se_services_set_off_profile(&off_profile)) { + MICROPY_BOARD_FATAL_ERROR("se_services_set_off_profile"); + } + + // Select PLL for PD4 memory. + if (se_services_select_pll_source(PLL_SOURCE_PLL, PLL_TARGET_PD4_SRAM)) { + MICROPY_BOARD_FATAL_ERROR("se_services_select_pll_source"); + } +} + +MP_WEAK void board_enter_stop(void) { + // Disable NPU interrupt + NVIC_DisableIRQ(NPU_IRQ_NUMBER); + NVIC_ClearPendingIRQ(NPU_IRQ_NUMBER); + + // Soft-reset NPU + NPU->RESET = 0x00000000; + + // Wait until reset + uint32_t data = 0; + do { + // Poll channel0 status registers + data = NPU->STATUS; + } while (data); + + // Set default value, enables off for clocks and power. + NPU->CMD = 0x0000000C; +} + +MP_WEAK void board_enter_standby(void) { + +} + +MP_WEAK void board_exit_standby(void) { + +} diff --git a/ports/alif/boards/OPENMV_AE3/board.ld.S b/ports/alif/boards/OPENMV_AE3/board.ld.S new file mode 100644 index 0000000000000..0d09bb15f874f --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/board.ld.S @@ -0,0 +1,14 @@ +#include "mcu/ensemble.ld.S" + +/* Define ROMFS partition locations. */ +#if CORE_M55_HP +/* The HP core has access to the external OSPI flash and MRAM ROMFS partitions. */ +_micropy_hw_romfs_part0_start = 0xa1000000; +_micropy_hw_romfs_part0_size = 16M; +_micropy_hw_romfs_part1_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part1_size = LENGTH(MRAM_FS); +#else +/* The HP core has access to the MRAM ROMFS partition. */ +_micropy_hw_romfs_part0_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part0_size = LENGTH(MRAM_FS); +#endif diff --git a/ports/alif/boards/OPENMV_AE3/mpconfigboard.h b/ports/alif/boards/OPENMV_AE3/mpconfigboard.h new file mode 100644 index 0000000000000..cbdbd063ed3df --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/mpconfigboard.h @@ -0,0 +1,85 @@ +#define MICROPY_HW_BOARD_NAME "OpenMV-AE3" +#define MICROPY_HW_MCU_NAME "AE302F80F55D5AE" + +#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) +typedef intptr_t mp_int_t; // must be pointer size +typedef uintptr_t mp_uint_t; // must be pointer size +typedef intptr_t mp_off_t; + +#define MICROPY_HW_USB_MSC (CORE_M55_HP) +#define MICROPY_HW_ENABLE_HW_I2C (1) +#define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) + +// ROMFS partitions +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (CORE_M55_HP) + +// I2C buses +#define MICROPY_HW_I2C1_SCL (pin_P0_5) +#define MICROPY_HW_I2C1_SDA (pin_P0_4) + +#define MICROPY_HW_I2C2_SCL (pin_P5_1) +#define MICROPY_HW_I2C2_SDA (pin_P5_0) + +#define MICROPY_HW_I2C3_SCL (pin_P1_1) +#define MICROPY_HW_I2C3_SDA (pin_P1_0) + +// SPI buses +#define MICROPY_HW_SPI0_MISO (pin_P5_0) +#define MICROPY_HW_SPI0_MOSI (pin_P5_1) +// #define MICROPY_HW_SPI0_NSS (pin_P5_2) +#define MICROPY_HW_SPI0_SCK (pin_P5_3) + +// UART buses +#define MICROPY_HW_UART1_TX (pin_P0_5) +#define MICROPY_HW_UART1_RX (pin_P0_4) +#define MICROPY_HW_UART3_TX (pin_P1_3) +#define MICROPY_HW_UART3_RX (pin_P1_2) +#define MICROPY_HW_UART3_RTS (pin_P7_3) +#define MICROPY_HW_UART3_CTS (pin_P7_2) +#define MICROPY_HW_UART4_TX (pin_P5_1) +#define MICROPY_HW_UART4_RX (pin_P5_0) +#define MICROPY_HW_UART5_TX (pin_P5_3) +#define MICROPY_HW_UART5_RX (pin_P5_2) + +#define MICROPY_HW_USB_VID 0x37C5 +#define MICROPY_HW_USB_PID 0x16E3 + +#define MICROPY_HW_USB_MANUFACTURER_STRING "OpenMV" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "OpenMV Camera" +#define MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING "OpenMV" + +extern void board_startup(void); +#define MICROPY_BOARD_STARTUP board_startup + +extern void board_early_init(void); +#define MICROPY_BOARD_EARLY_INIT board_early_init + +extern void board_enter_bootloader(void); +#define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) board_enter_bootloader() + +extern void board_enter_stop(void); +#define MICROPY_BOARD_ENTER_STOP board_enter_stop + +extern void board_enter_standby(void); +#define MICROPY_BOARD_ENTER_STANDBY board_enter_standby + +extern void board_exit_standby(void); +#define MICROPY_BOARD_EXIT_STANDBY board_exit_standby + +// This is used for alif.Flash() and USB MSC. +#define MICROPY_HW_FLASH_STORAGE_BASE_ADDR (0) +#define MICROPY_HW_FLASH_STORAGE_BYTES (32 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_FS_BYTES (16 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_ROMFS_BYTES (16 * 1024 * 1024) + +// Murata 1YN configuration +#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/w43439_sdio_1yn_7_95_59_combined.h" +#define CYW43_WIFI_NVRAM_INCLUDE_FILE "lib/cyw43-driver/firmware/wifi_nvram_1yn.h" +#define CYW43_BT_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/cyw43_btfw_1yn.h" +#define CYW43_BT_UART_BAUDRATE_DOWNLOAD_FIRMWARE (2000000) +#define CYW43_BT_UART_BAUDRATE_ACTIVE_USE (2000000) + +// Bluetooth config +#define MICROPY_HW_BLE_UART_ID (0) +#define MICROPY_HW_BLE_UART_BAUDRATE (115200) diff --git a/ports/alif/boards/OPENMV_AE3/mpconfigboard.mk b/ports/alif/boards/OPENMV_AE3/mpconfigboard.mk new file mode 100644 index 0000000000000..83cc17dd15ed9 --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/mpconfigboard.mk @@ -0,0 +1,22 @@ +# TODO: alif_ensemble-cmsis-dfp only supports AE722F80F55D5XX at the moment. +MCU_SERIES = E7 +MCU_VARIANT = AE722F80F55D5XX +JLINK_DEV = AE302F80F55D5_HP +LD_FILE = boards/OPENMV_AE3/board.ld.S +PORT = /dev/ttyUSB0 + +ALIF_TOOLKIT_CFG_PART = AE302F80F55D5AE +ALIF_TOOLKIT_CFG_FILE = \"app-device-config-ae3.json\" + +CORE_M55_HP := $(if $(filter M55_HP,$(MCU_CORE)),1,0) + +# MicroPython settings +MICROPY_FLOAT_IMPL = float +MICROPY_PY_BLUETOOTH = $(CORE_M55_HP) +MICROPY_BLUETOOTH_NIMBLE = $(CORE_M55_HP) +MICROPY_PY_LWIP = $(CORE_M55_HP) +MICROPY_PY_NETWORK_CYW43 = $(CORE_M55_HP) +MICROPY_PY_SSL = $(CORE_M55_HP) +MICROPY_SSL_MBEDTLS = $(CORE_M55_HP) +MICROPY_PY_OPENAMP = 1 +MICROPY_PY_OPENAMP_REMOTEPROC = 1 diff --git a/ports/alif/boards/OPENMV_AE3/ospi_xip_user.h b/ports/alif/boards/OPENMV_AE3/ospi_xip_user.h new file mode 100644 index 0000000000000..c0bd9afeb5cea --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/ospi_xip_user.h @@ -0,0 +1,6 @@ +// This file is needed by ospi_xip/source/ospi/ospi_drv.c. +#define OSPI_XIP_ENABLE_AES_DECRYPTION (0) +#define OSPI_XIP_RX_SAMPLE_DELAY (4) +#define OSPI_XIP_DDR_DRIVE_EDGE (0) +// floor(1/4 OSPI clock cycle + 3.6ns) * 2 +#define OSPI_XIP_RXDS_DELAY (12) diff --git a/ports/alif/boards/OPENMV_AE3/pins.csv b/ports/alif/boards/OPENMV_AE3/pins.csv new file mode 100644 index 0000000000000..ab4f1a34f7e11 --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/pins.csv @@ -0,0 +1,69 @@ +# USB multiplexer +USB_D_SEL,P15_0 +USB_D_SEL_FB,P15_1 + +# LEDs +LED_RED,P0_0 +LED_GREEN,P6_3 +LED_BLUE,P6_0 + +# User switch +USR_SW,P15_7 + +# Flash on OSPI0 +FLASH_RESET,P3_3 +FLASH_CS,P3_2 +FLASH_SCLK_P,P3_0 +FLASH_SCLK_N,P3_1 +FLASH_DQSM,P1_6 +FLASH_D0,P2_0 +FLASH_D1,P2_1 +FLASH_D2,P2_2 +FLASH_D3,P2_3 +FLASH_D4,P2_4 +FLASH_D5,P2_5 +FLASH_D6,P2_6 +FLASH_D7,P2_7 + +# Murata 1YN +BT_UART_RX,P1_4 +BT_UART_TX,P1_5 +BT_UART_CTS,P6_6 +BT_UART_RTS,P6_7 +BT_REG_ON,P5_7 +BT_DEV_WAKE,P9_2 +BT_HOST_WAKE,P13_3 +WL_REG_ON,P10_4 +WL_HOST_WAKE,P11_0 +WL_I2S_SDI,P12_0 +WL_I2S_SDO,P12_1 +WL_I2S_SCLK,P12_2 +WL_I2S_WS,P12_3 +WL_IRQ,P9_6 +WL_MISO,P12_4 +WL_MOSI,P12_5 +WL_SCLK,P12_6 +WL_CS,P12_7 + +P0,P5_1 +P1,P5_0 +P2,P5_3 +P3,P5_2 +P4,P0_5 +P5,P0_4 +P6,P7_2 +P7,P7_3 +P8,P1_2 +P9,P1_3 + +# UART buses +UART1_TX,P0_5 +UART1_RX,P0_4 +UART3_TX,P1_3 +UART3_RX,P1_2 +UART3_RTS,P7_3 +UART3_CTS,P7_2 +UART4_TX,P5_1 +UART4_RX,P5_0 +UART5_TX,P5_3 +UART5_RX,P5_2 From 547207ddc889a395f831a45f508d676aff197e17 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 20 Feb 2025 12:24:19 +0100 Subject: [PATCH 527/546] github/workflows: Add Alif port to CI. Signed-off-by: iabdalkader --- .github/workflows/ports_alif.yml | 33 ++++++++++++++++++++++++++++++++ tools/ci.sh | 15 +++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 .github/workflows/ports_alif.yml diff --git a/.github/workflows/ports_alif.yml b/.github/workflows/ports_alif.yml new file mode 100644 index 0000000000000..0e96e7d816e50 --- /dev/null +++ b/.github/workflows/ports_alif.yml @@ -0,0 +1,33 @@ +name: alif port + +on: + push: + pull_request: + paths: + - '.github/workflows/*.yml' + - 'tools/**' + - 'py/**' + - 'extmod/**' + - 'shared/**' + - 'lib/**' + - 'drivers/**' + - 'ports/alif/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_alif: + strategy: + fail-fast: false + matrix: + ci_func: # names are functions in ci.sh + - alif_ae3_build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install packages + run: source tools/ci.sh && ci_alif_setup + - name: Build ci_${{matrix.ci_func }} + run: source tools/ci.sh && ci_${{ matrix.ci_func }} diff --git a/tools/ci.sh b/tools/ci.sh index 948eeeaef2d22..cfc9754837f76 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -871,3 +871,18 @@ function ci_zephyr_run_tests { # - inf_nan_arith fails pow(-1, nan) test (cd tests && ./run-tests.py -t 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) } + +######################################################################################## +# ports/alif + +function ci_alif_setup { + ci_gcc_arm_setup +} + +function ci_alif_ae3_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/alif BOARD=OPENMV_AE3 MCU_CORE=M55_HP submodules + make ${MAKEOPTS} -C ports/alif BOARD=OPENMV_AE3 MCU_CORE=M55_HE submodules + make ${MAKEOPTS} -C ports/alif BOARD=OPENMV_AE3 MCU_CORE=M55_DUAL + make ${MAKEOPTS} -C ports/alif BOARD=ALIF_ENSEMBLE MCU_CORE=M55_DUAL +} From 2ad592530256ebfdab6459e918748b729a3de7de Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 21 Oct 2024 13:31:57 +1100 Subject: [PATCH 528/546] tests/ports/alif_hardware: Add flash testing script. This test is not intended to be run automatically and does not have a corresponding .exp file. Signed-off-by: Damien George --- tests/ports/alif_hardware/flash_test.py | 149 ++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 tests/ports/alif_hardware/flash_test.py diff --git a/tests/ports/alif_hardware/flash_test.py b/tests/ports/alif_hardware/flash_test.py new file mode 100644 index 0000000000000..5be3fc2d02f3e --- /dev/null +++ b/tests/ports/alif_hardware/flash_test.py @@ -0,0 +1,149 @@ +# MIT license; Copyright (c) 2024 OpenMV LLC. +# +# Note: this test completely erases the filesystem! +# It is intended to be run manually. + +import alif +import hashlib +import os +import sys +import time +import vfs + +hash_algo = "sha256" + + +def flash_make_filesystem(): + try: + vfs.umount("/flash") + except: + pass + bdev = alif.Flash() + vfs.VfsFat.mkfs(bdev) + vfs.mount(vfs.VfsFat(bdev), "/flash") + sys.path.append("/flash") + sys.path.append("/flash/lib") + os.chdir("/flash") + + +def flash_block_test(): + try: + vfs.umount("/flash") + except: + pass + dev = alif.Flash() + + data512 = os.urandom(512) + buf512 = bytearray(512) + block_numbers = tuple(range(32)) + tuple(range(250, 266)) + + print("Block read/write integrity: ", end="") + ok = True + for block_n in block_numbers: + dev.writeblocks(block_n, data512) + dev.readblocks(block_n, buf512) + if buf512 != data512: + ok = False + print(ok) + + print("Block read back integrity: ", end="") + ok = True + for block_n in block_numbers: + dev.readblocks(block_n, buf512) + if buf512 != data512: + ok = False + print(ok) + + N = 16 * 1024 + data_big = os.urandom(N) + t0 = time.ticks_us() + dev.writeblocks(0, data_big) + dt = time.ticks_diff(time.ticks_us(), t0) + print(f"Block write speed: {len(data_big) / 1024 / dt * 1_000_000} KiB/sec") + + buf_big = bytearray(N) + t0 = time.ticks_us() + dev.readblocks(0, buf_big) + dt = time.ticks_diff(time.ticks_us(), t0) + print(f"Block read speed: {len(buf_big) / 1024 / dt * 1_000_000} KiB/sec") + + if buf_big != data_big: + raise RuntimeError("big block read-back failed") + + try: + import uctypes + + xip = memoryview(dev) + except: + xip = None + if xip is not None: + t0 = time.ticks_us() + buf_big[:] = xip[: len(buf_big)] + dt = time.ticks_diff(time.ticks_us(), t0) + print(f"XIP read speed: {len(buf_big) / 1024 / dt * 1_000_000} KiB/sec") + + if buf_big != data_big: + raise RuntimeError("XIP read-back failed") + for i in range(len(buf_big)): + if xip[i] != data_big[i]: + raise RuntimeError("XIP byte-wise read-back failed") + + +def flash_write_verify(path, size=1024 * 1024, block_size=1024): + hash_sum = getattr(hashlib, hash_algo)() + block_count = size // block_size + + print("-" * 16) + print(f"Writing file {size=} {block_size=}") + t0 = time.ticks_ms() + total_size = 0 + with open(path, "wb") as file: + for i in range(block_count): + buf = os.urandom(block_size) + # Update digest + hash_sum.update(buf) + total_size += file.write(buf) + if i % (block_count // 16) == 0: + print(f"{i}/{block_count}", end="\r") + dt = time.ticks_diff(time.ticks_ms(), t0) + print(f"Flash write finished: {total_size / 1024 / dt * 1000} KiB/sec") + digest = hash_sum.digest() + + print("Reading file... ", end="") + hash_sum = getattr(hashlib, hash_algo)() + buf = bytearray(block_size) + t0 = time.ticks_ms() + total_size = 0 + with open(path, "rb") as file: + for i in range(block_count): + total_size += file.readinto(buf) + dt = time.ticks_diff(time.ticks_ms(), t0) + print(f"finished: {total_size / 1024 / dt * 1000} KiB/sec") + + print("Verifying file... ", end="") + hash_sum = getattr(hashlib, hash_algo)() + t0 = time.ticks_ms() + total_size = 0 + with open(path, "rb") as file: + for i in range(block_count): + buf = file.read(block_size) + total_size += len(buf) + # Update digest + hash_sum.update(buf) + dt = time.ticks_diff(time.ticks_ms(), t0) + print(f"finished: {total_size / 1024 / dt * 1000} KiB/sec; ", end="") + + if digest != hash_sum.digest(): + raise RuntimeError(f"{hash_algo} checksum verify failed") + + print(f"{hash_algo} checksum verified") + + +if __name__ == "__main__": + flash_block_test() + flash_make_filesystem() + flash_write_verify("test0.bin", size=64 * 1024, block_size=1024) + flash_write_verify("test1.bin", size=128 * 1024, block_size=1024) + flash_write_verify("test2.bin", size=64 * 1024, block_size=2048) + flash_write_verify("test4.bin", size=256 * 1024, block_size=4096) + flash_write_verify("test4.bin", size=512 * 1024, block_size=16384) From 037f2dad72a7d11d461330873b50bb9ccf4fed69 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 17 Mar 2025 13:18:20 +1100 Subject: [PATCH 529/546] tests: Update UART and SPI tests to work on Alif boards. Signed-off-by: Damien George --- tests/extmod/machine_spi_rate.py | 5 ++++- tests/extmod/machine_uart_irq_txidle.py | 5 ++++- tests/extmod/machine_uart_tx.py | 6 +++++- tests/extmod_hardware/machine_uart_irq_rx.py | 6 +++++- tests/extmod_hardware/machine_uart_irq_rxidle.py | 6 +++++- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/tests/extmod/machine_spi_rate.py b/tests/extmod/machine_spi_rate.py index 7022955a3da75..c65095f22a1a5 100644 --- a/tests/extmod/machine_spi_rate.py +++ b/tests/extmod/machine_spi_rate.py @@ -13,7 +13,10 @@ # Configure pins based on the port/board details. # Values are tuples of (spi_id, sck, mosi, miso) -if "pyboard" in sys.platform: +if "alif" in sys.platform: + MAX_DELTA_MS = 20 + spi_instances = ((0, None, None, None),) +elif "pyboard" in sys.platform: spi_instances = ( (1, None, None, None), # "explicit choice of sck/mosi/miso is not implemented" (2, None, None, None), diff --git a/tests/extmod/machine_uart_irq_txidle.py b/tests/extmod/machine_uart_irq_txidle.py index feb95fabaa44f..084e9825768a8 100644 --- a/tests/extmod/machine_uart_irq_txidle.py +++ b/tests/extmod/machine_uart_irq_txidle.py @@ -12,7 +12,10 @@ import time, sys # Configure pins based on the target. -if "rp2" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + tx_pin = None +elif "rp2" in sys.platform: uart_id = 0 tx_pin = "GPIO0" rx_pin = "GPIO1" diff --git a/tests/extmod/machine_uart_tx.py b/tests/extmod/machine_uart_tx.py index e9652f3c7ac21..f0cc912da66e0 100644 --- a/tests/extmod/machine_uart_tx.py +++ b/tests/extmod/machine_uart_tx.py @@ -14,7 +14,11 @@ timing_margin_us = 100 # Configure pins based on the target. -if "esp32" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + pins = {} + bit_margin = 1 +elif "esp32" in sys.platform: uart_id = 1 pins = {} timing_margin_us = 400 diff --git a/tests/extmod_hardware/machine_uart_irq_rx.py b/tests/extmod_hardware/machine_uart_irq_rx.py index bf34900bd0826..ecc95e62ae570 100644 --- a/tests/extmod_hardware/machine_uart_irq_rx.py +++ b/tests/extmod_hardware/machine_uart_irq_rx.py @@ -15,7 +15,11 @@ byte_by_byte = False # Configure pins based on the target. -if "esp32" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + tx_pin = None + rx_pin = None +elif "esp32" in sys.platform: uart_id = 1 tx_pin = 4 rx_pin = 5 diff --git a/tests/extmod_hardware/machine_uart_irq_rxidle.py b/tests/extmod_hardware/machine_uart_irq_rxidle.py index 182ab24ebe0c4..af2412c75eedc 100644 --- a/tests/extmod_hardware/machine_uart_irq_rxidle.py +++ b/tests/extmod_hardware/machine_uart_irq_rxidle.py @@ -14,7 +14,11 @@ import time, sys # Configure pins based on the target. -if "esp32" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + tx_pin = None + rx_pin = None +elif "esp32" in sys.platform: uart_id = 1 tx_pin = 4 rx_pin = 5 From 1aa9b3d94bd66a625173b6182df8a5308279b6d0 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Mon, 7 Apr 2025 23:01:06 +0200 Subject: [PATCH 530/546] tools/mpremote: Add recursive remove functionality to filesystem cmds. mpremote now supports `mpremote rm -r`. Addresses #9802 and #16845. Signed-off-by: Jos Verlinde --- tools/mpremote/mpremote/commands.py | 28 +++++++++++++++++++++++++++- tools/mpremote/mpremote/main.py | 2 +- tools/mpremote/mpremote/transport.py | 6 +++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/tools/mpremote/mpremote/commands.py b/tools/mpremote/mpremote/commands.py index e385d050927f2..690b2ea723ea4 100644 --- a/tools/mpremote/mpremote/commands.py +++ b/tools/mpremote/mpremote/commands.py @@ -1,4 +1,5 @@ import binascii +import errno import hashlib import os import sys @@ -300,6 +301,28 @@ def _mkdir(a, *b): do_filesystem_cp(state, src_path_joined, dest_path_joined, False, check_hash) +def do_filesystem_recursive_rm(state, path, args): + if state.transport.fs_isdir(path): + for entry in state.transport.fs_listdir(path): + do_filesystem_recursive_rm(state, _remote_path_join(path, entry.name), args) + if path: + try: + state.transport.fs_rmdir(path) + if args.verbose: + print(f"removed directory: '{path}'") + except OSError as e: + if e.errno != errno.EINVAL: # not vfs mountpoint + raise CommandError( + f"rm -r: cannot remove :{path} {os.strerror(e.errno) if e.errno else ''}" + ) from e + if args.verbose: + print(f"skipped: '{path}' (vfs mountpoint)") + else: + state.transport.fs_rmfile(path) + if args.verbose: + print(f"removed: '{path}'") + + def do_filesystem(state, args): state.ensure_raw_repl() state.did_action() @@ -352,7 +375,10 @@ def do_filesystem(state, args): elif command == "mkdir": state.transport.fs_mkdir(path) elif command == "rm": - state.transport.fs_rmfile(path) + if args.recursive: + do_filesystem_recursive_rm(state, path, args) + else: + state.transport.fs_rmfile(path) elif command == "rmdir": state.transport.fs_rmdir(path) elif command == "touch": diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index bdae8f163656b..b30a1a21354cc 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -182,7 +182,7 @@ def argparse_rtc(): def argparse_filesystem(): cmd_parser = argparse.ArgumentParser(description="execute filesystem commands on the device") - _bool_flag(cmd_parser, "recursive", "r", False, "recursive copy (for cp command only)") + _bool_flag(cmd_parser, "recursive", "r", False, "recursive (for cp and rm commands)") _bool_flag( cmd_parser, "force", diff --git a/tools/mpremote/mpremote/transport.py b/tools/mpremote/mpremote/transport.py index c8fdc54a8891a..8d30c7f517ff9 100644 --- a/tools/mpremote/mpremote/transport.py +++ b/tools/mpremote/mpremote/transport.py @@ -24,7 +24,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -import ast, hashlib, os, sys +import ast, errno, hashlib, os, sys from collections import namedtuple @@ -63,6 +63,10 @@ def _convert_filesystem_error(e, info): return FileExistsError(info) if "OSError" in e.error_output and "ENODEV" in e.error_output: return FileNotFoundError(info) + if "OSError" in e.error_output and "EINVAL" in e.error_output: + return OSError(errno.EINVAL, info) + if "OSError" in e.error_output and "EPERM" in e.error_output: + return OSError(errno.EPERM, info) return e From 72d4c409418b2d35be41b7b04f1802457309bc6d Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Mon, 7 Apr 2025 23:01:21 +0200 Subject: [PATCH 531/546] tools/mpremote/tests: Add tests for mpremote rm -r. Signed-off-by: Jos Verlinde --- tools/mpremote/tests/test_filesystem.sh | 64 +++++++++++++++++ tools/mpremote/tests/test_filesystem.sh.exp | 76 +++++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/tools/mpremote/tests/test_filesystem.sh b/tools/mpremote/tests/test_filesystem.sh index afeb7c91da8d6..a20d77dfea399 100755 --- a/tools/mpremote/tests/test_filesystem.sh +++ b/tools/mpremote/tests/test_filesystem.sh @@ -170,3 +170,67 @@ EOF $MPREMOTE resume cp -r "${TMP}/package" : $MPREMOTE resume ls : :package :package/subpackage $MPREMOTE resume exec "import package; package.x(); package.y()" + +echo ----- +# Test rm -r functionality +# start with a fresh ramdisk before each test +# rm -r MCU current working directory +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume touch :a.py +$MPREMOTE resume touch :b.py +$MPREMOTE resume cp -r "${TMP}/package" : +$MPREMOTE resume rm -r -v : +$MPREMOTE resume ls : +$MPREMOTE resume ls :/ramdisk + +echo ----- +# rm -r relative subfolder +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume touch :a.py +$MPREMOTE resume mkdir :testdir +$MPREMOTE resume cp -r "${TMP}/package" :testdir/package +$MPREMOTE resume ls :testdir +$MPREMOTE resume ls :testdir/package +$MPREMOTE resume rm -r :testdir/package +$MPREMOTE resume ls :/ramdisk +$MPREMOTE resume ls :testdir + +echo ----- +# rm -r non-existent path +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume ls : +$MPREMOTE resume rm -r :nonexistent || echo "expect error" + +echo ----- +# rm -r absolute root +# no -v to generate same output on stm32 and other ports +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume touch :a.py +$MPREMOTE resume touch :b.py +$MPREMOTE resume cp -r "${TMP}/package" : +$MPREMOTE resume cp -r "${TMP}/package" :package2 +$MPREMOTE resume rm -r :/ || echo "expect error" +$MPREMOTE resume ls : +$MPREMOTE resume ls :/ramdisk + +echo ----- +# rm -r relative mountpoint +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume touch :a.py +$MPREMOTE resume touch :b.py +$MPREMOTE resume cp -r "${TMP}/package" : +$MPREMOTE resume exec "import os;os.chdir('/')" +$MPREMOTE resume rm -r -v :ramdisk +$MPREMOTE resume ls :/ramdisk + +echo ----- +# rm -r absolute mountpoint +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume touch :a.py +$MPREMOTE resume touch :b.py +$MPREMOTE resume cp -r "${TMP}/package" : +$MPREMOTE resume exec "import os;os.chdir('/')" +$MPREMOTE resume rm -r -v :/ramdisk +$MPREMOTE resume ls :/ramdisk + +echo ----- \ No newline at end of file diff --git a/tools/mpremote/tests/test_filesystem.sh.exp b/tools/mpremote/tests/test_filesystem.sh.exp index 82fe7d6bf78c5..16d98c4ded8ec 100644 --- a/tools/mpremote/tests/test_filesystem.sh.exp +++ b/tools/mpremote/tests/test_filesystem.sh.exp @@ -191,3 +191,79 @@ ls :package/subpackage 23 y.py x y2 +----- +touch :a.py +touch :b.py +cp ${TMP}/package : +rm : +removed: './a.py' +removed: './b.py' +removed: './package/subpackage/__init__.py' +removed: './package/subpackage/y.py' +removed directory: './package/subpackage' +removed: './package/__init__.py' +removed: './package/x.py' +removed directory: './package' +ls : +ls :/ramdisk +----- +touch :a.py +mkdir :testdir +cp ${TMP}/package :testdir/package +ls :testdir + 0 package/ +ls :testdir/package + 0 subpackage/ + 43 __init__.py + 22 x.py +rm :testdir/package +ls :/ramdisk + 0 a.py + 0 testdir/ +ls :testdir +----- +ls : +rm :nonexistent +mpremote: rm: nonexistent: No such file or directory. +expect error +----- +touch :a.py +touch :b.py +cp ${TMP}/package : +cp ${TMP}/package :package2 +rm :/ +mpremote: rm -r: cannot remove :/ Operation not permitted +expect error +ls : +ls :/ramdisk +----- +touch :a.py +touch :b.py +cp ${TMP}/package : +rm :ramdisk +removed: 'ramdisk/a.py' +removed: 'ramdisk/b.py' +removed: 'ramdisk/package/subpackage/__init__.py' +removed: 'ramdisk/package/subpackage/y.py' +removed directory: 'ramdisk/package/subpackage' +removed: 'ramdisk/package/__init__.py' +removed: 'ramdisk/package/x.py' +removed directory: 'ramdisk/package' +skipped: 'ramdisk' (vfs mountpoint) +ls :/ramdisk +----- +touch :a.py +touch :b.py +cp ${TMP}/package : +rm :/ramdisk +removed: '/ramdisk/a.py' +removed: '/ramdisk/b.py' +removed: '/ramdisk/package/subpackage/__init__.py' +removed: '/ramdisk/package/subpackage/y.py' +removed directory: '/ramdisk/package/subpackage' +removed: '/ramdisk/package/__init__.py' +removed: '/ramdisk/package/x.py' +removed directory: '/ramdisk/package' +skipped: '/ramdisk' (vfs mountpoint) +ls :/ramdisk +----- From ef8282c717be8363e9a04ebf2b9d843e2485604f Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Mon, 7 Apr 2025 23:14:17 +0200 Subject: [PATCH 532/546] docs/reference/mpremote: Update docs for mpremote rm -r. Signed-off-by: Jos Verlinde --- docs/reference/mpremote.rst | 28 ++++++++++++++++++++++++---- tools/mpremote/tests/README.md | 15 +++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/docs/reference/mpremote.rst b/docs/reference/mpremote.rst index ef23cd85c21a3..32ca5c246a7a4 100644 --- a/docs/reference/mpremote.rst +++ b/docs/reference/mpremote.rst @@ -229,7 +229,7 @@ The full list of supported commands are: - ``ls`` to list the current directory - ``ls `` to list the given directories - ``cp [-rf] `` to copy files - - ``rm `` to remove files on the device + - ``rm [-r] `` to remove files or folders on the device - ``mkdir `` to create directories on the device - ``rmdir `` to remove directories on the device - ``touch `` to create the files (if they don't already exist) @@ -238,15 +238,35 @@ The full list of supported commands are: The ``cp`` command uses a convention where a leading ``:`` represents a remote path. Without a leading ``:`` means a local path. This is based on the convention used by the `Secure Copy Protocol (scp) client - `_. All other commands - implicitly assume the path is a remote path, but the ``:`` can be optionally - used for clarity. + `_. So for example, ``mpremote fs cp main.py :main.py`` copies ``main.py`` from the current local directory to the remote filesystem, whereas ``mpremote fs cp :main.py main.py`` copies ``main.py`` from the device back to the current directory. + The ``mpremote rm -r`` command accepts both relative and absolute paths. + Use ``:`` to refer to the current remote working directory (cwd) to allow a + directory tree to be removed from the device's default path (eg ``/flash``, ``/``). + Use ``-v/--verbose`` to see the files being removed. + + For example: + + - ``mpremote rm -r :libs`` will remove the ``libs`` directory and all its + child items from the device. + - ``mpremote rm -rv :/sd`` will remove all files from a mounted SDCard and result + in a non-blocking warning. The mount will be retained. + - ``mpremote rm -rv :/`` will remove all files on the device, including any + located in mounted vfs such as ``/sd`` or ``/flash``. After removing all folders + and files, this will also return an error to mimic unix ``rm -rf /`` behaviour. + + .. warning:: + There is no supported way to undelete files removed by ``mpremote rm -r :``. + Please use with caution. + + All other commands implicitly assume the path is a remote path, but the ``:`` + can be optionally used for clarity. + All of the filesystem sub-commands take multiple path arguments, so if there is another command in the sequence, you must use ``+`` to terminate the arguments, e.g. diff --git a/tools/mpremote/tests/README.md b/tools/mpremote/tests/README.md index 5b924d2fb8701..d1d77f1fb52df 100644 --- a/tools/mpremote/tests/README.md +++ b/tools/mpremote/tests/README.md @@ -4,11 +4,26 @@ This directory contains a set of tests for `mpremote`. Requirements: - A device running MicroPython connected to a serial port on the host. +- The device you are testing against must be flashed with a firmware of the same build + as `mpremote`. +- If the device has an SDcard or other vfs mounted, the vfs's filesystem must be empty + to pass the filesystem test. - Python 3.x, `bash` and various Unix tools such as `find`, `mktemp`, `sed`, `sort`, `tr`. +- To test on Windows, you can either: + - Run the (Linux) tests in WSL2 against a USB device that is passed though to WSL2. + - Use the `Git Bash` terminal to run the tests against a device connected to a COM + port. _Note:_ While the tests will run in `Git Bash`, several will throw false + positive errors due to differences in the way that TMP files are logged and and + several other details. To run the tests do: + $ cd tools/mpremote/tests $ ./run-mpremote-tests.sh +To run a single test do: + + $ ./run-mpremote-tests.sh test_filesystem.sh + Each test should print "OK" if it passed. Otherwise it will print "CRASH", or "FAIL" and a diff of the expected and actual test output. From e7edf0783e945f4107dcc7f68543ed05e92189a4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 9 Apr 2025 21:30:17 +1000 Subject: [PATCH 533/546] drivers/memory/spiflash: Allow a board/port to configure chip params. This commit allows the user of this driver to dynamically configure the SPI flash chip parameters. For this, enable `MICROPY_HW_SPIFLASH_CHIP_PARAMS` and then set the `mp_spiflash_t::chip_params` element to point to a valid `mp_spiflash_chip_params_t` struct. Signed-off-by: Damien George --- drivers/memory/spiflash.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/memory/spiflash.h b/drivers/memory/spiflash.h index edd7d49330da1..a22742fc6f2b9 100644 --- a/drivers/memory/spiflash.h +++ b/drivers/memory/spiflash.h @@ -29,6 +29,11 @@ #include "drivers/bus/spi.h" #include "drivers/bus/qspi.h" +// Whether to enable dynamic configuration of SPI flash through mp_spiflash_chip_params_t. +#ifndef MICROPY_HW_SPIFLASH_CHIP_PARAMS +#define MICROPY_HW_SPIFLASH_CHIP_PARAMS (0) +#endif + #define MP_SPIFLASH_ERASE_BLOCK_SIZE (4096) // must be a power of 2 enum { @@ -66,8 +71,20 @@ typedef struct _mp_spiflash_config_t { #endif } mp_spiflash_config_t; +#if MICROPY_HW_SPIFLASH_CHIP_PARAMS +typedef struct _mp_spiflash_chip_params_t { + uint32_t jedec_id; + uint8_t memory_size_bytes_log2; + uint8_t qspi_prescaler; + uint8_t qread_num_dummy; +} mp_spiflash_chip_params_t; +#endif + typedef struct _mp_spiflash_t { const mp_spiflash_config_t *config; + #if MICROPY_HW_SPIFLASH_CHIP_PARAMS + const mp_spiflash_chip_params_t *chip_params; + #endif volatile uint32_t flags; } mp_spiflash_t; From b078569cffb4b32585806b42ef7a0501bc4d0bca Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Apr 2025 12:47:48 +1100 Subject: [PATCH 534/546] drivers/memory/spiflash: Allow a board/port to detect SPI flash. This commit allows the user of this driver to intercept the SPI flash initialisation routine and possibly take some action based on the JEDEC id, for example change the `mp_spiflash_t::chip_params` element. To do this, enable `MICROPY_HW_SPIFLASH_DETECT_DEVICE` and define a function called `mp_spiflash_detect()`. Signed-off-by: Damien George --- drivers/memory/spiflash.c | 12 +++++++++--- drivers/memory/spiflash.h | 10 ++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/memory/spiflash.c b/drivers/memory/spiflash.c index 09ab7157b88eb..b4133fe2f068f 100644 --- a/drivers/memory/spiflash.c +++ b/drivers/memory/spiflash.c @@ -31,6 +31,10 @@ #include "py/mphal.h" #include "drivers/memory/spiflash.h" +#if defined(CHECK_DEVID) +#error "CHECK_DEVID no longer supported, use MICROPY_HW_SPIFLASH_DETECT_DEVICE instead" +#endif + #define QSPI_QE_MASK (0x02) #define USE_WR_DELAY (1) @@ -198,11 +202,13 @@ void mp_spiflash_init(mp_spiflash_t *self) { mp_hal_delay_ms(1); #endif - #if defined(CHECK_DEVID) - // Validate device id + #if MICROPY_HW_SPIFLASH_DETECT_DEVICE + // Attempt to detect SPI flash based on its JEDEC id. uint32_t devid; int ret = mp_spiflash_read_cmd(self, CMD_RD_DEVID, 3, &devid); - if (ret != 0 || devid != CHECK_DEVID) { + ret = mp_spiflash_detect(self, ret, devid); + if (ret != 0) { + // Could not read device id. mp_spiflash_release_bus(self); return; } diff --git a/drivers/memory/spiflash.h b/drivers/memory/spiflash.h index a22742fc6f2b9..d98047c89f07c 100644 --- a/drivers/memory/spiflash.h +++ b/drivers/memory/spiflash.h @@ -34,6 +34,11 @@ #define MICROPY_HW_SPIFLASH_CHIP_PARAMS (0) #endif +// Whether to enable detection of SPI flash during initialisation. +#ifndef MICROPY_HW_SPIFLASH_DETECT_DEVICE +#define MICROPY_HW_SPIFLASH_DETECT_DEVICE (0) +#endif + #define MP_SPIFLASH_ERASE_BLOCK_SIZE (4096) // must be a power of 2 enum { @@ -91,6 +96,11 @@ typedef struct _mp_spiflash_t { void mp_spiflash_init(mp_spiflash_t *self); void mp_spiflash_deepsleep(mp_spiflash_t *self, int value); +#if MICROPY_HW_SPIFLASH_DETECT_DEVICE +// A board/port should define this function to perform actions based on the JEDEC id. +int mp_spiflash_detect(mp_spiflash_t *spiflash, int ret, uint32_t devid); +#endif + // These functions go direct to the SPI flash device int mp_spiflash_erase_block(mp_spiflash_t *self, uint32_t addr); int mp_spiflash_read(mp_spiflash_t *self, uint32_t addr, size_t len, uint8_t *dest); From 2c0240e068b66b5f7b063125a7977cbca03d9483 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Apr 2025 12:47:40 +1100 Subject: [PATCH 535/546] drivers/bus/qspi: Make num_dummy configurable for quad reads. Signed-off-by: Damien George --- drivers/bus/qspi.h | 2 +- drivers/bus/softqspi.c | 6 +++--- drivers/memory/spiflash.c | 14 ++++++++++++-- ports/stm32/boards/STM32F769DISC/board_init.c | 4 +++- ports/stm32/octospi.c | 4 ++-- ports/stm32/qspi.c | 14 +++++++++----- ports/stm32/qspi.h | 2 +- 7 files changed, 31 insertions(+), 15 deletions(-) diff --git a/drivers/bus/qspi.h b/drivers/bus/qspi.h index 7ba2e750943c5..32b2890e3fc4a 100644 --- a/drivers/bus/qspi.h +++ b/drivers/bus/qspi.h @@ -45,7 +45,7 @@ typedef struct _mp_qspi_proto_t { int (*write_cmd_data)(void *self, uint8_t cmd, size_t len, uint32_t data); int (*write_cmd_addr_data)(void *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src); int (*read_cmd)(void *self, uint8_t cmd, size_t len, uint32_t *dest); - int (*read_cmd_qaddr_qdata)(void *self, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest); + int (*read_cmd_qaddr_qdata)(void *self, uint8_t cmd, uint32_t addr, uint8_t num_dummy, size_t len, uint8_t *dest); } mp_qspi_proto_t; typedef struct _mp_soft_qspi_obj_t { diff --git a/drivers/bus/softqspi.c b/drivers/bus/softqspi.c index 5cfc4db5e3383..06dcd03b02dda 100644 --- a/drivers/bus/softqspi.c +++ b/drivers/bus/softqspi.c @@ -189,13 +189,13 @@ static int mp_soft_qspi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_ return 0; } -static int mp_soft_qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) { +static int mp_soft_qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, uint8_t num_dummy, size_t len, uint8_t *dest) { mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in; - uint8_t cmd_buf[7] = {cmd}; + uint8_t cmd_buf[16] = {cmd}; uint8_t addr_len = mp_spi_set_addr_buff(&cmd_buf[1], addr); CS_LOW(self); mp_soft_qspi_transfer(self, 1, cmd_buf, NULL); - mp_soft_qspi_qwrite(self, addr_len + 3, &cmd_buf[1]); // 3/4 addr bytes, 1 extra byte (0), 2 dummy bytes (4 dummy cycles) + mp_soft_qspi_qwrite(self, addr_len + 1 + num_dummy, &cmd_buf[1]); // 3/4 addr bytes, 1 extra byte (0), N dummy bytes (2*N dummy cycles) mp_soft_qspi_qread(self, len, dest); CS_HIGH(self); return 0; diff --git a/drivers/memory/spiflash.c b/drivers/memory/spiflash.c index b4133fe2f068f..1ae0bbbc6792a 100644 --- a/drivers/memory/spiflash.c +++ b/drivers/memory/spiflash.c @@ -35,6 +35,14 @@ #error "CHECK_DEVID no longer supported, use MICROPY_HW_SPIFLASH_DETECT_DEVICE instead" #endif +// The default number of dummy bytes for quad-read is 2. This can be changed by enabling +// MICROPY_HW_SPIFLASH_CHIP_PARAMS and configuring the value in mp_spiflash_chip_params_t. +#if MICROPY_HW_SPIFLASH_CHIP_PARAMS +#define MICROPY_HW_SPIFLASH_QREAD_NUM_DUMMY(spiflash) (spiflash->chip_params->qread_num_dummy) +#else +#define MICROPY_HW_SPIFLASH_QREAD_NUM_DUMMY(spiflash) (2) +#endif + #define QSPI_QE_MASK (0x02) #define USE_WR_DELAY (1) @@ -115,7 +123,8 @@ static int mp_spiflash_transfer_cmd_addr_data(mp_spiflash_t *self, uint8_t cmd, mp_hal_pin_write(c->bus.u_spi.cs, 1); } else { if (dest != NULL) { - ret = c->bus.u_qspi.proto->read_cmd_qaddr_qdata(c->bus.u_qspi.data, cmd, addr, len, dest); + uint8_t num_dummy = MICROPY_HW_SPIFLASH_QREAD_NUM_DUMMY(self); + ret = c->bus.u_qspi.proto->read_cmd_qaddr_qdata(c->bus.u_qspi.data, cmd, addr, num_dummy, len, dest); } else { ret = c->bus.u_qspi.proto->write_cmd_addr_data(c->bus.u_qspi.data, cmd, addr, len, src); } @@ -186,7 +195,8 @@ void mp_spiflash_init(mp_spiflash_t *self) { mp_hal_pin_output(self->config->bus.u_spi.cs); self->config->bus.u_spi.proto->ioctl(self->config->bus.u_spi.data, MP_SPI_IOCTL_INIT); } else { - self->config->bus.u_qspi.proto->ioctl(self->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT, 0); + uint8_t num_dummy = MICROPY_HW_SPIFLASH_QREAD_NUM_DUMMY(self); + self->config->bus.u_qspi.proto->ioctl(self->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT, num_dummy); } mp_spiflash_acquire_bus(self); diff --git a/ports/stm32/boards/STM32F769DISC/board_init.c b/ports/stm32/boards/STM32F769DISC/board_init.c index 578dc9adb6c82..001b18bc06783 100644 --- a/ports/stm32/boards/STM32F769DISC/board_init.c +++ b/ports/stm32/boards/STM32F769DISC/board_init.c @@ -3,6 +3,8 @@ // This configuration is needed for mboot to be able to write to the external QSPI flash +#define QSPI_QREAD_NUM_DUMMY (2) + #if MICROPY_HW_SPIFLASH_ENABLE_CACHE static mp_spiflash_cache_t spi_bdev_cache; #endif @@ -21,6 +23,6 @@ spi_bdev_t spi_bdev; // This init function is needed to memory map the QSPI flash early in the boot process void board_early_init(void) { - qspi_init(); + qspi_init(QSPI_QREAD_NUM_DUMMY); qspi_memory_map(); } diff --git a/ports/stm32/octospi.c b/ports/stm32/octospi.c index 345c4a2374128..861941325c615 100644 --- a/ports/stm32/octospi.c +++ b/ports/stm32/octospi.c @@ -283,7 +283,7 @@ static int octospi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_t *de return 0; } -static int octospi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) { +static int octospi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, uint8_t num_dummy, size_t len, uint8_t *dest) { (void)self_in; #if defined(MICROPY_HW_OSPIFLASH_IO1) && !defined(MICROPY_HW_OSPIFLASH_IO2) && !defined(MICROPY_HW_OSPIFLASH_IO4) @@ -293,7 +293,7 @@ static int octospi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t add uint32_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2; uint32_t dmode = 2; // data on 2-lines uint32_t admode = 2; // address on 2-lines - uint32_t dcyc = 4; // 4 dummy cycles + uint32_t dcyc = 2 * num_dummy; // 2N dummy cycles if (cmd == 0xeb || cmd == 0xec) { // Convert to 2-line command. diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c index 7334ece4f2181..1360d9b490d29 100644 --- a/ports/stm32/qspi.c +++ b/ports/stm32/qspi.c @@ -62,6 +62,8 @@ #define QSPI_ADSIZE 2 #endif +static uint8_t qspi_num_dummy; + static inline void qspi_mpu_disable_all(void) { // Configure MPU to disable access to entire QSPI region, to prevent CPU // speculative execution from accessing this region and modifying QSPI registers. @@ -110,7 +112,9 @@ static inline void qspi_mpu_enable_mapped(void) { mpu_config_end(irq_state); } -void qspi_init(void) { +void qspi_init(uint8_t num_dummy) { + qspi_num_dummy = num_dummy; + qspi_mpu_disable_all(); // Configure pins @@ -158,7 +162,7 @@ void qspi_memory_map(void) { | 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction | 3 << QUADSPI_CCR_FMODE_Pos // memory-mapped mode | 3 << QUADSPI_CCR_DMODE_Pos // data on 4 lines - | 4 << QUADSPI_CCR_DCYC_Pos // 4 dummy cycles + | (2 * qspi_num_dummy) << QUADSPI_CCR_DCYC_Pos // 2N dummy cycles | 0 << QUADSPI_CCR_ABSIZE_Pos // 8-bit alternate byte | 3 << QUADSPI_CCR_ABMODE_Pos // alternate byte on 4 lines | QSPI_ADSIZE << QUADSPI_CCR_ADSIZE_Pos @@ -193,7 +197,7 @@ static int qspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) { (void)self_in; switch (cmd) { case MP_QSPI_IOCTL_INIT: - qspi_init(); + qspi_init(arg); break; case MP_QSPI_IOCTL_BUS_ACQUIRE: // Disable memory-mapped region during bus access @@ -369,7 +373,7 @@ static int qspi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_t *dest) return 0; } -static int qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) { +static int qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, uint8_t num_dummy, size_t len, uint8_t *dest) { (void)self_in; uint8_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2; @@ -383,7 +387,7 @@ static int qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, | 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction | 1 << QUADSPI_CCR_FMODE_Pos // indirect read mode | 3 << QUADSPI_CCR_DMODE_Pos // data on 4 lines - | 4 << QUADSPI_CCR_DCYC_Pos // 4 dummy cycles + | (2 * num_dummy) << QUADSPI_CCR_DCYC_Pos // 2N dummy cycles | 0 << QUADSPI_CCR_ABSIZE_Pos // 8-bit alternate byte | 3 << QUADSPI_CCR_ABMODE_Pos // alternate byte on 4 lines | adsize << QUADSPI_CCR_ADSIZE_Pos // 32 or 24-bit address size diff --git a/ports/stm32/qspi.h b/ports/stm32/qspi.h index a2d6b9f328db2..c8a3181653db7 100644 --- a/ports/stm32/qspi.h +++ b/ports/stm32/qspi.h @@ -33,7 +33,7 @@ extern const mp_qspi_proto_t qspi_proto; -void qspi_init(void); +void qspi_init(uint8_t num_dummy); void qspi_memory_map(void); void qspi_memory_map_exit(void); void qspi_memory_map_restart(void); From 1d83c8175680b911ab24ba834b7f8bcadaa5aa79 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 1 Apr 2025 14:31:26 +1100 Subject: [PATCH 536/546] stm32/vfs_rom_ioctl: Allow ROMFS configuration to be dynamic. Options for a board to configure ROMFS are: - Leave ROMFS disabled, do nothing. - Enable by defining `MICROPY_HW_ROMFS_ENABLE_PARTx` to 1 and then in the linker script define `_micropy_hw_romfs_partX_start` and `_micropy_hw_romfs_partX_size`. - Enable by defining `MICROPY_HW_ROMFS_ENABLE_PARTx` to 1 and also define `MICROPY_HW_ROMFS_PARTx_START` and `MICROPY_HW_ROMFS_PARTx_SIZE` which can be arbitrary expressions (not necessarily static) Signed-off-by: Damien George --- ports/stm32/vfs_rom_ioctl.c | 41 ++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/ports/stm32/vfs_rom_ioctl.c b/ports/stm32/vfs_rom_ioctl.c index 1fa4408c53796..7592aa22d622a 100644 --- a/ports/stm32/vfs_rom_ioctl.c +++ b/ports/stm32/vfs_rom_ioctl.c @@ -36,28 +36,32 @@ #if MICROPY_VFS_ROM_IOCTL -#if MICROPY_HW_ROMFS_ENABLE_PART0 && !defined(MICROPY_HW_ROMFS_PART0_START) -#define MICROPY_HW_ROMFS_PART0_START (uintptr_t)(&_micropy_hw_romfs_part0_start) -#define MICROPY_HW_ROMFS_PART0_SIZE (uintptr_t)(&_micropy_hw_romfs_part0_size) +#if MICROPY_HW_ROMFS_ENABLE_PART0 && defined(MICROPY_HW_ROMFS_PART0_START) +#define ROMFS0_DYNAMIC (1) +static MP_DEFINE_MEMORYVIEW_OBJ(romfs0_obj, 'B', 0, (uintptr_t)-1, (void *)-1); +#elif MICROPY_HW_ROMFS_ENABLE_PART0 && !defined(MICROPY_HW_ROMFS_PART0_START) +#define ROMFS0_DYNAMIC (0) extern uint8_t _micropy_hw_romfs_part0_start; extern uint8_t _micropy_hw_romfs_part0_size; +static const MP_DEFINE_MEMORYVIEW_OBJ(romfs0_obj, 'B', 0, (uintptr_t)&_micropy_hw_romfs_part0_size, (void *)&_micropy_hw_romfs_part0_start); #endif -#if MICROPY_HW_ROMFS_ENABLE_PART1 && !defined(MICROPY_HW_ROMFS_PART1_START) -#define MICROPY_HW_ROMFS_PART1_START (uintptr_t)(&_micropy_hw_romfs_part1_start) -#define MICROPY_HW_ROMFS_PART1_SIZE (uintptr_t)(&_micropy_hw_romfs_part1_size) +#if MICROPY_HW_ROMFS_ENABLE_PART1 && defined(MICROPY_HW_ROMFS_PART1_START) +#define ROMFS1_DYNAMIC (1) +static MP_DEFINE_MEMORYVIEW_OBJ(romfs1_obj, 'B', 0, (uintptr_t)-1, (void *)-1); +#elif MICROPY_HW_ROMFS_ENABLE_PART1 && !defined(MICROPY_HW_ROMFS_PART1_START) +#define ROMFS1_DYNAMIC (0) extern uint8_t _micropy_hw_romfs_part1_start; extern uint8_t _micropy_hw_romfs_part1_size; +static const MP_DEFINE_MEMORYVIEW_OBJ(romfs1_obj, 'B', 0, (uintptr_t)&_micropy_hw_romfs_part1_size, (void *)&_micropy_hw_romfs_part1_start); #endif -#define ROMFS_MEMORYVIEW(base, size) {{&mp_type_memoryview}, 'B', 0, (size), (void *)(base)} - -static const mp_obj_array_t romfs_obj_table[] = { +static const mp_obj_array_t *romfs_obj_table[] = { #if MICROPY_HW_ROMFS_ENABLE_PART0 - ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART0_START, MICROPY_HW_ROMFS_PART0_SIZE), + &romfs0_obj, #endif #if MICROPY_HW_ROMFS_ENABLE_PART1 - ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART1_START, MICROPY_HW_ROMFS_PART1_SIZE), + &romfs1_obj, #endif }; @@ -76,7 +80,20 @@ mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); } - const mp_obj_array_t *romfs_obj = &romfs_obj_table[romfs_id]; + #if ROMFS0_DYNAMIC + if (romfs_id == 0) { + romfs0_obj.items = (void *)MICROPY_HW_ROMFS_PART0_START; + romfs0_obj.len = MICROPY_HW_ROMFS_PART0_SIZE; + } + #endif + #if ROMFS1_DYNAMIC + if (romfs_id == 1) { + romfs1_obj.items = (void *)MICROPY_HW_ROMFS_PART1_START; + romfs1_obj.len = MICROPY_HW_ROMFS_PART1_SIZE; + } + #endif + + const mp_obj_array_t *romfs_obj = romfs_obj_table[romfs_id]; uintptr_t romfs_base = (uintptr_t)romfs_obj->items; uintptr_t romfs_len = romfs_obj->len; From aa0945698b714c25f85b1234e0b03f34ac76c24c Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 13 Mar 2025 11:09:01 +1100 Subject: [PATCH 537/546] stm32/qspi: Allow SPI flash size to be decided at runtime. Allows `MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2` and `MICROPY_HW_QSPI_MPU_REGION_SIZE` to be arbitrary expressions, eg function calls. The `storage.h` header needs to be included in case access to `spi_bdev_t` is needed by the macros. Signed-off-by: Damien George --- ports/stm32/qspi.c | 77 ++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c index 1360d9b490d29..781aae803ef21 100644 --- a/ports/stm32/qspi.c +++ b/ports/stm32/qspi.c @@ -31,6 +31,7 @@ #include "mpu.h" #include "qspi.h" #include "pin_static_af.h" +#include "storage.h" #if defined(MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2) @@ -50,18 +51,11 @@ #define MICROPY_HW_QSPI_CS_HIGH_CYCLES 2 // nCS stays high for 2 cycles #endif +// Region size in units of 1024*1024 bytes. #ifndef MICROPY_HW_QSPI_MPU_REGION_SIZE #define MICROPY_HW_QSPI_MPU_REGION_SIZE ((1 << (MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 - 3)) >> 20) #endif -#if (MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 - 3 - 1) >= 24 -#define QSPI_CMD 0xec -#define QSPI_ADSIZE 3 -#else -#define QSPI_CMD 0xeb -#define QSPI_ADSIZE 2 -#endif - static uint8_t qspi_num_dummy; static inline void qspi_mpu_disable_all(void) { @@ -83,32 +77,32 @@ static inline void qspi_mpu_enable_mapped(void) { // other enabled region overlaps the disabled subregion, and the access is // unprivileged or the background region is disabled, the MPU issues a fault. uint32_t irq_state = mpu_config_start(); - #if MICROPY_HW_QSPI_MPU_REGION_SIZE > 128 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0xFF, MPU_REGION_SIZE_256MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 64 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_256MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 32 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_256MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 16 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 8 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_32MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 4 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_32MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 2 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_32MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 1 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_32MB)); - mpu_config_region(MPU_REGION_QSPI3, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_16MB)); - #else - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_32MB)); - mpu_config_region(MPU_REGION_QSPI3, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_4MB)); - #endif + if (MICROPY_HW_QSPI_MPU_REGION_SIZE > 128) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0xFF, MPU_REGION_SIZE_256MB)); + } else if (MICROPY_HW_QSPI_MPU_REGION_SIZE > 64) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_256MB)); + } else if (MICROPY_HW_QSPI_MPU_REGION_SIZE > 32) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_256MB)); + } else if (MICROPY_HW_QSPI_MPU_REGION_SIZE > 16) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + } else if (MICROPY_HW_QSPI_MPU_REGION_SIZE > 8) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_32MB)); + } else if (MICROPY_HW_QSPI_MPU_REGION_SIZE > 4) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_32MB)); + } else if (MICROPY_HW_QSPI_MPU_REGION_SIZE > 2) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_32MB)); + } else if (MICROPY_HW_QSPI_MPU_REGION_SIZE > 1) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_32MB)); + mpu_config_region(MPU_REGION_QSPI3, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_16MB)); + } else { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_32MB)); + mpu_config_region(MPU_REGION_QSPI3, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_4MB)); + } mpu_config_end(irq_state); } @@ -155,6 +149,17 @@ void qspi_init(uint8_t num_dummy) { void qspi_memory_map(void) { // Enable memory-mapped mode + // Work out command to use for reads, based on size of the memory. + uint8_t cmd; + uint8_t adsize; + if ((MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 - 3 - 1) >= 24) { + cmd = 0xec; + adsize = 3; + } else { + cmd = 0xeb; + adsize = 2; + } + QUADSPI->ABR = 0; // disable continuous read mode QUADSPI->CCR = @@ -165,10 +170,10 @@ void qspi_memory_map(void) { | (2 * qspi_num_dummy) << QUADSPI_CCR_DCYC_Pos // 2N dummy cycles | 0 << QUADSPI_CCR_ABSIZE_Pos // 8-bit alternate byte | 3 << QUADSPI_CCR_ABMODE_Pos // alternate byte on 4 lines - | QSPI_ADSIZE << QUADSPI_CCR_ADSIZE_Pos + | adsize << QUADSPI_CCR_ADSIZE_Pos | 3 << QUADSPI_CCR_ADMODE_Pos // address on 4 lines | 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line - | QSPI_CMD << QUADSPI_CCR_INSTRUCTION_Pos + | cmd << QUADSPI_CCR_INSTRUCTION_Pos ; qspi_mpu_enable_mapped(); From de08190cb736f3bb1027d15bdf88135e49603c23 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Apr 2025 12:49:54 +1100 Subject: [PATCH 538/546] stm32/mboot: Allow USB strings to be dynamic. Signed-off-by: Damien George --- ports/stm32/mboot/main.c | 76 +++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 16 deletions(-) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 9bb2dcfcaf408..01f8892a514c8 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -429,38 +429,82 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed) { || defined(STM32F723xx) \ || defined(STM32F732xx) \ || defined(STM32F733xx) -#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT +#define INTERNAL_FLASH_LAYOUT "@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg" #elif defined(STM32F765xx) || defined(STM32F767xx) || defined(STM32F769xx) -#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/04*032Kg,01*128Kg,07*256Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT +#define INTERNAL_FLASH_LAYOUT "@Internal Flash /0x08000000/04*032Kg,01*128Kg,07*256Kg" #elif defined(STM32G0) -#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/256*02Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT +#define INTERNAL_FLASH_LAYOUT "@Internal Flash /0x08000000/256*02Kg" #elif defined(STM32H5) -#define FLASH_LAYOUT_TEMPLATE "@Internal Flash /0x08000000/???*08Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT +#define INTERNAL_FLASH_LAYOUT "@Internal Flash /0x08000000/???*08Kg" +#define INTERNAL_FLASH_LAYOUT_HAS_TEMPLATE (1) #elif defined(STM32H743xx) -#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/16*128Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT +#define INTERNAL_FLASH_LAYOUT "@Internal Flash /0x08000000/16*128Kg" #elif defined(STM32H750xx) -#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/01*128Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT +#define INTERNAL_FLASH_LAYOUT "@Internal Flash /0x08000000/01*128Kg" #elif defined(STM32WB) -#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/256*04Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT +#define INTERNAL_FLASH_LAYOUT "@Internal Flash /0x08000000/256*04Kg" #endif -#if !defined(FLASH_LAYOUT_STR) +#if INTERNAL_FLASH_LAYOUT_HAS_TEMPLATE \ + || defined(MBOOT_SPIFLASH_LAYOUT_DYNAMIC_MAX_LEN) \ + || defined(MBOOT_SPIFLASH2_LAYOUT_DYNAMIC_MAX_LEN) -#define FLASH_LAYOUT_STR_ALLOC (sizeof(FLASH_LAYOUT_TEMPLATE)) +#ifndef MBOOT_SPIFLASH_LAYOUT_DYNAMIC_MAX_LEN +#define MBOOT_SPIFLASH_LAYOUT_DYNAMIC_MAX_LEN (sizeof(MBOOT_SPIFLASH_LAYOUT) - 1) +#endif + +#ifndef MBOOT_SPIFLASH2_LAYOUT_DYNAMIC_MAX_LEN +#define MBOOT_SPIFLASH2_LAYOUT_DYNAMIC_MAX_LEN (sizeof(MBOOT_SPIFLASH2_LAYOUT) - 1) +#endif + +#define FLASH_LAYOUT_STR_ALLOC \ + ( \ + (sizeof(INTERNAL_FLASH_LAYOUT) - 1) \ + + MBOOT_SPIFLASH_LAYOUT_DYNAMIC_MAX_LEN \ + + MBOOT_SPIFLASH2_LAYOUT_DYNAMIC_MAX_LEN \ + + 1 \ + ) // Build the flash layout string from a template with total flash size inserted. -static size_t build_flash_layout_str(char *buf) { - size_t len = FLASH_LAYOUT_STR_ALLOC - 1; - memcpy(buf, FLASH_LAYOUT_TEMPLATE, len + 1); +static size_t build_flash_layout_str(uint8_t *buf) { + const char *internal_layout = INTERNAL_FLASH_LAYOUT; + size_t internal_layout_len = strlen(internal_layout); + + const char *spiflash_layout = MBOOT_SPIFLASH_LAYOUT; + size_t spiflash_layout_len = strlen(spiflash_layout); + + const char *spiflash2_layout = MBOOT_SPIFLASH2_LAYOUT; + size_t spiflash2_layout_len = strlen(spiflash2_layout); + + uint8_t *buf_orig = buf; + + memcpy(buf, internal_layout, internal_layout_len); + buf += internal_layout_len; + + #if INTERNAL_FLASH_LAYOUT_HAS_TEMPLATE unsigned int num_sectors = FLASH_SIZE / FLASH_SECTOR_SIZE; - buf += 31; // location of "???" in FLASH_LAYOUT_TEMPLATE + uint8_t *buf_size = buf_orig + 31; // location of "???" in FLASH_LAYOUT_TEMPLATE for (unsigned int i = 0; i < 3; ++i) { - *buf-- = '0' + num_sectors % 10; + *buf_size-- = '0' + num_sectors % 10; num_sectors /= 10; } - return len; + #endif + + memcpy(buf, spiflash_layout, spiflash_layout_len); + buf += spiflash_layout_len; + + memcpy(buf, spiflash2_layout, spiflash2_layout_len); + buf += spiflash2_layout_len; + + *buf++ = '\0'; + + return buf - buf_orig; } +#else + +#define FLASH_LAYOUT_STR INTERNAL_FLASH_LAYOUT MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT + #endif static bool flash_is_modifiable_addr_range(uint32_t addr, uint32_t len) { @@ -1188,7 +1232,7 @@ static uint8_t *pyb_usbdd_StrDescriptor(USBD_HandleTypeDef *pdev, uint8_t idx, u USBD_GetString((uint8_t *)FLASH_LAYOUT_STR, str_desc, length); #else { - char buf[FLASH_LAYOUT_STR_ALLOC]; + uint8_t buf[FLASH_LAYOUT_STR_ALLOC]; build_flash_layout_str(buf); USBD_GetString((uint8_t *)buf, str_desc, length); } From ed4833d495e1a328ef35edda1666799a9c84802c Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Apr 2025 12:50:23 +1100 Subject: [PATCH 539/546] stm32/modmachine: Add SPI flash size to machine.info dump. Signed-off-by: Damien George --- ports/stm32/modmachine.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 7c1c4da60bff2..620ae468cb9b3 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -247,6 +247,14 @@ static mp_obj_t machine_info(size_t n_args, const mp_obj_t *args) { mp_printf(print, " 1=%u 2=%u m=%u\n", info.num_1block, info.num_2block, info.max_block); } + // SPI flash size + #if defined(MICROPY_HW_SPIFLASH_SIZE_BITS) + mp_printf(print, "SPI flash size: %d\n", MICROPY_HW_SPIFLASH_SIZE_BITS / 8); + #endif + #if defined(MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2) + mp_printf(print, "QSPI flash size: %d\n", 1 << (MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 - 3)); + #endif + // free space on flash { #if MICROPY_VFS_FAT From db854270719bfa4dda003d38893c1b8f39bf58de Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Apr 2025 12:48:48 +1100 Subject: [PATCH 540/546] stm32/boards/PYBD_SF6: Support boards with larger SPI flash. There are some newer PYBD_SF6 being produced which have a larger flash, namely two of 8MiB (instead of the older ones with two of 2MiB). This commit adds support for these boards. The idea is to have the same PYBD_SF6 firmware run on both old and new boards. That means autodetecting the flash at start-up and configuring all the relevant SPI/QSPI parameters, including for ROMFS and mboot. Signed-off-by: Damien George --- ports/stm32/boards/PYBD_SF6/board_init.c | 88 +++++++++++++++++++++ ports/stm32/boards/PYBD_SF6/f767.ld | 3 +- ports/stm32/boards/PYBD_SF6/mpconfigboard.h | 50 ++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/PYBD_SF6/board_init.c b/ports/stm32/boards/PYBD_SF6/board_init.c index c7a9f28006be3..836c4db8b5d24 100644 --- a/ports/stm32/boards/PYBD_SF6/board_init.c +++ b/ports/stm32/boards/PYBD_SF6/board_init.c @@ -1 +1,89 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "boardctrl.h" +#include "qspi.h" + +#if BUILDING_MBOOT +#include "mboot/mboot.h" +#endif + +// Use PYBD_SF2 as base configuration. #include "boards/PYBD_SF2/board_init.c" + +// Adesto AT25SF161 16-MBit. +static const mp_spiflash_chip_params_t chip_params_at25sf161 = { + .jedec_id = 0x01861f, + .memory_size_bytes_log2 = 21, + .qspi_prescaler = 3, // maximum frequency 104MHz + .qread_num_dummy = 2, +}; + +// Infineon S25FL064 64-MBit. +static const mp_spiflash_chip_params_t chip_params_s25fl064 = { + .jedec_id = 0x176001, + .memory_size_bytes_log2 = 23, + .qspi_prescaler = 2, // maximum frequency 108MHz + .qread_num_dummy = 4, +}; + +// Selection of possible SPI flash chips. +static const mp_spiflash_chip_params_t *const chip_params_table[] = { + &chip_params_at25sf161, + &chip_params_s25fl064, +}; + +void board_early_init_sf6(void) { + // Initialise default SPI flash parameters. + MICROPY_BOARD_SPIFLASH_CHIP_PARAMS0 = &chip_params_at25sf161; + MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1 = &chip_params_at25sf161; + + // Continue with standard board early init. + board_early_init(); +} + +int mp_spiflash_detect(mp_spiflash_t *spiflash, int ret, uint32_t devid) { + if (ret != 0) { + // Could not identify flash. Succeed anyway using default chip parameters. + return 0; + } + + // Try to detect the SPI flash based on the JEDEC id. + for (size_t i = 0; i < MP_ARRAY_SIZE(chip_params_table); ++i) { + if (devid == chip_params_table[i]->jedec_id) { + spiflash->chip_params = chip_params_table[i]; + if (spiflash->config->bus_kind == MP_SPIFLASH_BUS_QSPI) { + // Reinitialise the QSPI but to set new size, prescaler and dummy bytes. + uint8_t num_dummy = spiflash->chip_params->qread_num_dummy; + spiflash->config->bus.u_qspi.proto->ioctl(spiflash->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT, num_dummy); + } + break; + } + } + + return 0; +} diff --git a/ports/stm32/boards/PYBD_SF6/f767.ld b/ports/stm32/boards/PYBD_SF6/f767.ld index 0ee6cf76355fe..37fb3715d67ee 100644 --- a/ports/stm32/boards/PYBD_SF6/f767.ld +++ b/ports/stm32/boards/PYBD_SF6/f767.ld @@ -18,7 +18,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 2016K /* sectors 1-11 3x32K 1*128K 7*256K */ - FLASH_ROMFS (rx): ORIGIN = 0x90000000, LENGTH = 2048K /* external QSPI */ + FLASH_ROMFS (rx): ORIGIN = 0x90000000, LENGTH = 2048K /* external QSPI, at least 2MiB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 512K /* DTCM=128k, SRAM1=368K, SRAM2=16K */ } @@ -39,6 +39,5 @@ _heap_end = _sstack; /* ROMFS location */ _micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); -_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); INCLUDE common_bl.ld diff --git a/ports/stm32/boards/PYBD_SF6/mpconfigboard.h b/ports/stm32/boards/PYBD_SF6/mpconfigboard.h index 45261523bdaa0..8cd1e52d3bd78 100644 --- a/ports/stm32/boards/PYBD_SF6/mpconfigboard.h +++ b/ports/stm32/boards/PYBD_SF6/mpconfigboard.h @@ -69,3 +69,53 @@ #define MICROPY_HW_ETH_RMII_TX_EN (pyb_pin_W8) #define MICROPY_HW_ETH_RMII_TXD0 (pyb_pin_W45) #define MICROPY_HW_ETH_RMII_TXD1 (pyb_pin_W49) + +// The below code reconfigures SPI flash for dynamic size detection. + +#undef MICROPY_BOARD_EARLY_INIT +#undef MICROPY_HW_SPIFLASH_SIZE_BITS +#undef MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 +#undef MBOOT_SPIFLASH_BYTE_SIZE +#undef MBOOT_SPIFLASH_LAYOUT +#undef MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE +#undef MBOOT_SPIFLASH2_BYTE_SIZE +#undef MBOOT_SPIFLASH2_LAYOUT +#undef MBOOT_SPIFLASH2_ERASE_BLOCKS_PER_PAGE + +// These are convenience macros to refer to the SPI flash chip parameters for the external SPI flash. +#define MICROPY_BOARD_SPIFLASH_CHIP_PARAMS0 (spi_bdev.spiflash.chip_params) // SPI flash #1, R/W storage +#define MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1 (spi_bdev2.spiflash.chip_params) // SPI flash #2, memory mapped + +// Early init is needed to initialise the default SPI flash chip parameters. +#define MICROPY_BOARD_EARLY_INIT board_early_init_sf6 + +// Enable dynamic detection of SPI flash. +#define MICROPY_HW_SPIFLASH_CHIP_PARAMS (1) +#define MICROPY_HW_SPIFLASH_DETECT_DEVICE (1) + +// Settings for SPI flash #1. +#define MICROPY_HW_SPIFLASH_SIZE_BITS (1 << (MICROPY_BOARD_SPIFLASH_CHIP_PARAMS0->memory_size_bytes_log2 + 3)) + +// Settings for SPI flash #2. +#define MICROPY_HW_QSPI_PRESCALER (MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1->qspi_prescaler) +#define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1->memory_size_bytes_log2 + 3) + +// ROMFS partition 0 is dynamically sized (SPI flash #2). +#define MICROPY_HW_ROMFS_PART0_START (uintptr_t)(&_micropy_hw_romfs_part0_start) +#define MICROPY_HW_ROMFS_PART0_SIZE (1 << MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1->memory_size_bytes_log2) + +// Mboot SPI flash #1 configuration. +#define MBOOT_SPIFLASH_LAYOUT_DYNAMIC_MAX_LEN (20) +#define MBOOT_SPIFLASH_LAYOUT (MICROPY_BOARD_SPIFLASH_CHIP_PARAMS0->memory_size_bytes_log2 == 21 ? "/0x80000000/512*4Kg" : "/0x80000000/2048*4Kg") +#define MBOOT_SPIFLASH_BYTE_SIZE (1 << MICROPY_BOARD_SPIFLASH_CHIP_PARAMS0->memory_size_bytes_log2) +#define MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE (1) + +// Mboot SPI flash #2 configuration. +#define MBOOT_SPIFLASH2_LAYOUT_DYNAMIC_MAX_LEN (20) +#define MBOOT_SPIFLASH2_LAYOUT (MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1->memory_size_bytes_log2 == 21 ? "/0x90000000/512*4Kg" : "/0x90000000/2048*4Kg") +#define MBOOT_SPIFLASH2_BYTE_SIZE (1 << MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1->memory_size_bytes_log2) +#define MBOOT_SPIFLASH2_ERASE_BLOCKS_PER_PAGE (1) + +extern unsigned char _micropy_hw_romfs_part0_start; + +void board_early_init_sf6(void); From 0b3ad98ea97bc504bb594639f5fcced5b5397eec Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Apr 2025 13:54:01 +1000 Subject: [PATCH 541/546] mimxrt/Makefile: Fix dependencies for generation of flexram_config.s. Prior to this fix the following would fail: $ make build-TEENSY40/flexram_config.s because it didn't create the build directory before generating the file. Also, make `hal/resethandler_MIMXRT10xx.S` have an explicit dependency on `flexram_config.s` rather than the latter just being forced to be built before everything else. Signed-off-by: Damien George --- ports/mimxrt/Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 5c54295028083..c0358387f975f 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -327,6 +327,8 @@ SRC_SS = \ SRC_S += shared/runtime/gchelper_thumb2.s \ +hal/resethandler_MIMXRT10xx.S: $(GEN_FLEXRAM_CONFIG_SRC) + # ============================================================================= # QSTR Sources # ============================================================================= @@ -521,16 +523,16 @@ $(BUILD)/firmware.uf2: $(BUILD)/firmware.elf # any of the objects. The normal dependency generation will deal with the # case when pins.h is modified. But when it doesn't exist, we don't know # which source files might need it. -$(OBJ): | $(GEN_PINS_HDR) $(GEN_FLEXRAM_CONFIG_SRC) +$(OBJ): | $(GEN_PINS_HDR) # With conditional pins, we may need to regenerate qstrdefs.h when config # options change. $(HEADER_BUILD)/qstrdefs.generated.h: $(BOARD_DIR)/mpconfigboard.h -$(GEN_FLEXRAM_CONFIG_SRC): +$(GEN_FLEXRAM_CONFIG_SRC): $(HEADER_BUILD) $(ECHO) "Create $@" $(Q)$(PYTHON) $(MAKE_FLEXRAM_LD) -d $(TOP)/$(MCU_DIR)/$(MCU_SERIES)$(MCU_CORE).h \ - -f $(TOP)/$(MCU_DIR)/$(MCU_SERIES)$(MCU_CORE)_features.h -l boards/$(MCU_SERIES).ld -c $(MCU_SERIES) > $(GEN_FLEXRAM_CONFIG_SRC) + -f $(TOP)/$(MCU_DIR)/$(MCU_SERIES)$(MCU_CORE)_features.h -l boards/$(MCU_SERIES).ld -c $(MCU_SERIES) > $@ # Use a pattern rule here so that make will only call make-pins.py once to make # both pins_gen.c and pins.h From 9ee2ef5108102ce2f5851fba06da3dcab585f501 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Apr 2025 15:35:39 +1000 Subject: [PATCH 542/546] py/emitinlinerv32: Move include of asmrv32.h to within feature guard. Otherwise, when compiling on 16-bit systems (where `mp_uint_t` is 16 bits wide) the compiler warns about "left shift count >= width of type", from the static inline functions that have RV32_ENCODE_TYPE_xxx macros which do a lot of bit shifting. Signed-off-by: Damien George --- py/emitinlinerv32.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/py/emitinlinerv32.c b/py/emitinlinerv32.c index a9a81ddf16c4c..a539242b84d95 100644 --- a/py/emitinlinerv32.c +++ b/py/emitinlinerv32.c @@ -30,12 +30,13 @@ #include #include -#include "py/asmrv32.h" #include "py/emit.h" #include "py/misc.h" #if MICROPY_EMIT_INLINE_RV32 +#include "py/asmrv32.h" + typedef enum { // define rules with a compile function #define DEF_RULE(rule, comp, kind, ...) PN_##rule, From 9f3062799633dc8fa4cc556fc48a135cd5d4c2d4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 14 Apr 2025 14:32:41 +1000 Subject: [PATCH 543/546] lib/micropython-lib: Update submodule to latest. This brings in: - requests: do not leak header modifications when calling request - mip: allow relative URLs in package.json - mip: make mip.install() skip /rom*/lib directories - umqtt.simple: restore legacy ssl/ssl_params arguments - nrf24l01: increase startup delay - nrf24l01: properly handle timeout - nrf24l01: optimize status reading - lora-sx126x: fix invert_iq_rx / invert_iq_tx behaviour - unix-ffi/json: accept both str and bytes as arg for json.loads() - unix-ffi/machine: use libc if librt is not present - requests: use the host in the redirect url, not the one in headers - aiohttp: fix header case sensitivity - aiohttp: allow headers to be passed to a WebSocketClient - usb-device-cdc: optimise writing small data so it doesn't require alloc - inspect: fix isgenerator logic - inspect: implement iscoroutinefunction and iscoroutine Signed-off-by: Damien George --- lib/micropython-lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/micropython-lib b/lib/micropython-lib index e4cf09527bce7..5b496e944ec04 160000 --- a/lib/micropython-lib +++ b/lib/micropython-lib @@ -1 +1 @@ -Subproject commit e4cf09527bce7569f5db742cf6ae9db68d50c6a9 +Subproject commit 5b496e944ec045177afa1620920a168410b7f60b From f498a16c7db6d4b2de200b3e0856528dfe0613c3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Apr 2025 00:28:30 +1000 Subject: [PATCH 544/546] all: Bump version to 1.25.0. Signed-off-by: Damien George --- py/mpconfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index dfa32ebf7614a..dcf0e4f05c97f 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -32,7 +32,7 @@ #define MICROPY_VERSION_MAJOR 1 #define MICROPY_VERSION_MINOR 25 #define MICROPY_VERSION_MICRO 0 -#define MICROPY_VERSION_PRERELEASE 1 +#define MICROPY_VERSION_PRERELEASE 0 // Combined version as a 32-bit number for convenience to allow version // comparison. Doesn't include prerelease state. From b874e04166db24e32346afa51c0bbfbef8387c50 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sat, 21 Jun 2025 10:08:24 -0400 Subject: [PATCH 545/546] micropython v1.25.0 merge: fix tests, fix typos, fix includes --- ports/litex/common-hal/microcontroller/Pin.h | 1 + ports/unix/variants/coverage/mpconfigvariant.mk | 3 ++- ports/zephyr-cp/common-hal/microcontroller/Pin.h | 1 + py/emitnative.c | 2 +- tests/extmod/re_sub.py | 3 ++- tests/misc/non_compliant.py.exp | 2 -- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ports/litex/common-hal/microcontroller/Pin.h b/ports/litex/common-hal/microcontroller/Pin.h index 542f3e979b5bc..860a8c9ccadb6 100644 --- a/ports/litex/common-hal/microcontroller/Pin.h +++ b/ports/litex/common-hal/microcontroller/Pin.h @@ -7,6 +7,7 @@ #pragma once #include "py/mphal.h" +#include "py/obj.h" typedef struct { diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index 579e42cc05cf9..6ec0e85377ccf 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -12,7 +12,8 @@ CFLAGS += \ LDFLAGS += -fprofile-arcs -ftest-coverage FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py -USER_C_MODULES = $(TOP)/examples/usercmodule +# CIRCUITPY-CHANGE: don't include user C modules +# USER_C_MODULES = $(TOP)/examples/usercmodule # CIRCUITPY-CHANGE: use CircuitPython bindings and implementations SRC_QRIO := $(patsubst ../../%,%,$(wildcard ../../shared-bindings/qrio/*.c ../../shared-module/qrio/*.c ../../lib/quirc/lib/*.c)) diff --git a/ports/zephyr-cp/common-hal/microcontroller/Pin.h b/ports/zephyr-cp/common-hal/microcontroller/Pin.h index eb0304dc72fde..d38ab9bd2009c 100644 --- a/ports/zephyr-cp/common-hal/microcontroller/Pin.h +++ b/ports/zephyr-cp/common-hal/microcontroller/Pin.h @@ -7,6 +7,7 @@ #pragma once #include "py/mphal.h" +#include "py/obj.h" #include diff --git a/py/emitnative.c b/py/emitnative.c index 8565e79c82606..a888418e5dfed 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -93,7 +93,7 @@ #endif #ifndef N_XTENSAWIN -#define N_XTENSWIN (0) +#define N_XTENSAWIN (0) #endif #ifndef N_PRELUDE_AS_BYTES_OBJ diff --git a/tests/extmod/re_sub.py b/tests/extmod/re_sub.py index ecaa66d83d8a7..3959949724d98 100644 --- a/tests/extmod/re_sub.py +++ b/tests/extmod/re_sub.py @@ -49,7 +49,8 @@ def A(): print(re.sub("a", "b", "c")) # with maximum substitution count specified -if sys.implementation.name != "micropython": +# CIRCUITPY-CHANGE: was "micropython" +if sys.implementation.name != "circuitpython": # On CPython 3.13 and later the substitution count must be a keyword argument. print(re.sub("a", "b", "1a2a3a", count=2)) else: diff --git a/tests/misc/non_compliant.py.exp b/tests/misc/non_compliant.py.exp index deefe78a712d0..006ddee1b7322 100644 --- a/tests/misc/non_compliant.py.exp +++ b/tests/misc/non_compliant.py.exp @@ -13,8 +13,6 @@ NotImplementedError NotImplementedError NotImplementedError NotImplementedError -b'\x01\x02' -b'\x01\x00' NotImplementedError AttributeError TypeError From 2d9f3044ff287776f03569940d58fab7111b36f7 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sat, 21 Jun 2025 12:50:21 -0400 Subject: [PATCH 546/546] shrink wiznet boards --- ports/raspberrypi/boards/wiznet_w5100s_evb_pico/mpconfigboard.mk | 1 + ports/raspberrypi/boards/wiznet_w5500_evb_pico/mpconfigboard.mk | 1 + 2 files changed, 2 insertions(+) diff --git a/ports/raspberrypi/boards/wiznet_w5100s_evb_pico/mpconfigboard.mk b/ports/raspberrypi/boards/wiznet_w5100s_evb_pico/mpconfigboard.mk index 70011c41b0cb7..4753e28eb0d0d 100644 --- a/ports/raspberrypi/boards/wiznet_w5100s_evb_pico/mpconfigboard.mk +++ b/ports/raspberrypi/boards/wiznet_w5100s_evb_pico/mpconfigboard.mk @@ -9,5 +9,6 @@ CHIP_FAMILY = rp2 EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 +CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_SSL = 1 CIRCUITPY_USB_HOST = 0 diff --git a/ports/raspberrypi/boards/wiznet_w5500_evb_pico/mpconfigboard.mk b/ports/raspberrypi/boards/wiznet_w5500_evb_pico/mpconfigboard.mk index b3ac9854d3d96..837246198a42c 100644 --- a/ports/raspberrypi/boards/wiznet_w5500_evb_pico/mpconfigboard.mk +++ b/ports/raspberrypi/boards/wiznet_w5500_evb_pico/mpconfigboard.mk @@ -9,5 +9,6 @@ CHIP_FAMILY = rp2 EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 +CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_SSL = 1 CIRCUITPY_USB_HOST = 0