From be8d5fc27b30ade25bdd272368fd8b91e4d4b338 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 29 Jul 2025 10:23:31 +1000 Subject: [PATCH 001/177] esp32/README: Update the README details to account for newer chips. Noted while adding C2 support that some of these comments are a bit out of date. Spun out to its own commit, and also mention C5 as well. This change also adds some recommendation on which ESP32 board to pick, as we occasionally see issues or questions that would be non-issues on a board with more RAM (and for small production or personal projects the savings of picking a cheaper ESP32 chip are basically neglible). This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/README.md | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 5a2bb9240d932..55964febeaff4 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -5,16 +5,17 @@ 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-C2 (aka ESP8684), ESP32-C3, ESP32-C6, ESP32-S2 -and ESP32-S3 (ESP8266 is supported by a separate MicroPython port). +Currently supports ESP32, ESP32-C2 (aka ESP8684), ESP32-C3, ESP32-C5, 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. +- REPL (Python prompt) over UART0 and/or the integrated USB peripheral. - Many of MicroPython's features are enabled: unicode, arbitrary-precision integers, single-precision floats, complex numbers, frozen bytecode, as well as many of the internal modules. -- Internal filesystem using the flash (currently 2M in size). +- Internal filesystem using the remaining flash area. +- Threading support via the _thread module (built on native FreeRTOS tasks), + with GIL enabled. - The machine module with GPIO, UART, SPI, software I2C, ADC, DAC, PWM, TouchPad, WDT and Timer. - The network module with WLAN (WiFi) support. @@ -22,6 +23,26 @@ Supported features include: Initial development of this ESP32 port was sponsored in part by Microbric Pty Ltd. +Choosing a suitable chip +------------------------ + +ESP32 chips are not all the same. The different ESP32 families have different +capabilities and resources available. In particular, the ESP32-C2 and ESP32-S2 +(without external SPIRAM) have the least RAM. They can still run MicroPython +well but may run out of RAM if a program uses a lot of resources, eg if it +needs many complex code modules, multiple TLS connections, large memory +buffers for a display, etc. + +ESP32 boards with external "SPIRAM" (also called PSRAM) have megabytes of RAM +available - significantly more than any board without external SPIRAM. SPIRAM is +supported on original ESP32, ESP32-S2 and ESP32-S3 chips. Not every ESP32 board +has SPIRAM included, even if the chip supports it. + +If you don't need particular hardware features but are looking for a board with +the usual Wi-Fi and BLE support, many peripherals and plenty of RAM then +recommend finding a board based on ESP32-S3 with SPIRAM included (usually 2MB, +4MB or 8MB). + Setting up ESP-IDF and the build environment -------------------------------------------- From de2ace72e943c8b38d77b7d68e8fe73e48459f76 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 19 Nov 2025 17:59:25 +1100 Subject: [PATCH 002/177] esp32/modespnow: Fix espnow rate setting. Only set the rate on interfaces that are active. It seems ESP-IDF 5.4.x or so added checks that the interface is enabled, whereas previous versions silently did nothing. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/modespnow.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/ports/esp32/modespnow.c b/ports/esp32/modespnow.c index ab50032ffeb51..5d048f6b2c7d7 100644 --- a/ports/esp32/modespnow.c +++ b/ports/esp32/modespnow.c @@ -187,6 +187,16 @@ static void send_cb(const esp_now_send_info_t *tx_info, esp_now_send_status_t st static void recv_cb(const esp_now_recv_info_t *recv_info, const uint8_t *msg, int msg_len); +// Return the current wifi mode, or raise ValueError +static wifi_mode_t get_wifi_mode(void) { + wifi_mode_t mode; + if (esp_wifi_get_mode(&mode) != ESP_OK || mode == WIFI_MODE_NULL) { + // network.WLAN STA or AP must be set active(True) before ESP-NOW can be used + mp_raise_OSError(MP_ENOENT); + } + return mode; +} + // ESPNow.init(): Initialise the data buffers and ESP-NOW functions. // Initialise the Espressif ESPNOW software stack, register callbacks and // allocate the recv data buffers. @@ -197,7 +207,6 @@ static mp_obj_t espnow_init(mp_obj_t _) { self->recv_buffer = m_new_obj(ringbuf_t); ringbuf_alloc(self->recv_buffer, self->recv_buffer_size); - esp_initialise_wifi(); // Call the wifi init code in network_wlan.c check_esp_err(esp_now_init()); check_esp_err(esp_now_register_recv_cb(recv_cb)); check_esp_err(esp_now_register_send_cb(send_cb)); @@ -260,9 +269,13 @@ static mp_obj_t espnow_config(size_t n_args, const mp_obj_t *pos_args, mp_map_t self->recv_timeout_ms = args[ARG_timeout_ms].u_int; } if (args[ARG_rate].u_int >= 0) { - esp_initialise_wifi(); // Call the wifi init code in network_wlan.c - check_esp_err(esp_wifi_config_espnow_rate(ESP_IF_WIFI_STA, args[ARG_rate].u_int)); - check_esp_err(esp_wifi_config_espnow_rate(ESP_IF_WIFI_AP, args[ARG_rate].u_int)); + wifi_mode_t mode = get_wifi_mode(); + if (mode == WIFI_MODE_STA || mode == WIFI_MODE_APSTA) { + check_esp_err(esp_wifi_config_espnow_rate(ESP_IF_WIFI_STA, args[ARG_rate].u_int)); + } + if (mode == WIFI_MODE_AP || mode == WIFI_MODE_APSTA) { + check_esp_err(esp_wifi_config_espnow_rate(ESP_IF_WIFI_AP, args[ARG_rate].u_int)); + } } if (args[ARG_get].u_obj == MP_OBJ_NULL) { return mp_const_none; From 7e10d22134dabf41e2208aea292774c852d95d13 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 20 Nov 2025 12:52:56 +1100 Subject: [PATCH 003/177] esp32,docs: Add constants and documentation for espnow data rates. Changes are: - Add constants for some of the supported ESP-NOW data rates. - Add constants for switching an ESP32 WLAN radio in/out of Long Range mode. - Document the new constants and their usage. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/library/espnow.rst | 49 ++++++++++++++++++++++++++++++++--- docs/library/network.WLAN.rst | 32 +++++++++++++++++++++++ ports/esp32/modespnow.c | 16 ++++++++++++ ports/esp32/network_wlan.c | 14 ++++++++++ 4 files changed, 107 insertions(+), 4 deletions(-) diff --git a/docs/library/espnow.rst b/docs/library/espnow.rst index 84e9e9465de96..4b4b058f87a89 100644 --- a/docs/library/espnow.rst +++ b/docs/library/espnow.rst @@ -164,13 +164,15 @@ Configuration wait forever. The timeout can also be provided as arg to `recv()`/`irecv()`/`recvinto()`. - *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. + See also `espnow-long-range`. .. data:: Returns: @@ -574,6 +576,45 @@ Constants espnow.MAX_TOTAL_PEER_NUM(=20) espnow.MAX_ENCRYPT_PEER_NUM(=6) +The following constants correspond to different transmit data rates on ESP32 +only. Lower data rates are generally more reliable over long distances: + +.. data:: espnow.RATE_LORA_250K + espnow.RATE_LORA_500K + + See `espnow-long-range`. + +.. data:: espnow.RATE_1M + espnow.RATE_2M + espnow.RATE_5M + espnow.RATE_6M + espnow.RATE_11M + espnow.RATE_12M + espnow.RATE_24M + espnow.RATE_54M + +Unless using the two proprietary long range data rates, only the sender must +configure the data rate. + +.. _espnow-long-range: + +Long Range Mode +--------------- + +(ESP32 Only, except ESP32-C2) + +To use the `espnow.RATE_LORA_250K` and `espnow.RATE_LORA_500K` data rates, +first set the `WLAN` interface object to long-range mode, i.e.:: + + import network, espnow + sta = network.WLAN(network.WLAN.IF_STA) + sta.active(True) + sta.config(channel=6, protocol=WLAN.PROTOCOL_LR) # Set on sender & receiver + e = espnow.ESPNow() + e.config(rate=espnow.RATE_LORA_250K) # Needed on sender only + +For more information about the limitations of long-range mode, see `WLAN.PROTOCOL_LR`. + Exceptions ---------- diff --git a/docs/library/network.WLAN.rst b/docs/library/network.WLAN.rst index ee0ef490fda84..27d6b383a36a6 100644 --- a/docs/library/network.WLAN.rst +++ b/docs/library/network.WLAN.rst @@ -145,6 +145,7 @@ Methods reconnects Number of reconnect attempts to make (integer, 0=none, -1=unlimited) txpower Maximum transmit power in dBm (integer or float) pm WiFi Power Management setting (see below for allowed values) + protocol (ESP32 Only.) WiFi Low level 802.11 protocol. See `WLAN.PROTOCOL_DEFAULTS`. ============= =========== Constants @@ -161,3 +162,34 @@ Constants * ``PM_POWERSAVE``: enable WiFi power management with additional power savings and reduced WiFi performance * ``PM_NONE``: disable wifi power management + + +ESP32 Protocol Constants +------------------------ + +The following ESP32-only constants relate to the ``WLAN.config(protocol=...)`` +network interface parameter: + +.. data:: WLAN.PROTOCOL_DEFAULTS + + A bitmap representing all of the default 802.11 Wi-Fi modes supported by + the chip. Consult `ESP-IDF Wi-Fi Protocols`_ documentation for details. + +.. data:: WLAN.PROTOCOL_LR + + This value corresponds to the `Espressif proprietary "long-range" mode`_, + which is not compatible with standard Wi-Fi devices. By setting this + protocol it's possible for an ESP32 STA in long-range mode to connect to + an ESP32 AP in long-range mode, or to use `ESP-NOW long range modes + `. + + This mode can be bitwise ORed with some standard 802.11 protocol bits + (including `WLAN.PROTOCOL_DEFAULTS`) in order to support a mix of standard + Wi-Fi modes as well as LR mode, consult the `Espressif long-range + documentation`_ for more details. + + Long range mode is not supported on ESP32-C2. + +.. _ESP-IDF Wi-Fi Protocols: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/wifi.html#wi-fi-protocol-mode +.. _Espressif proprietary "long-range" mode: +.. _Espressif long-range documentation: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/wifi.html#long-range-lr diff --git a/ports/esp32/modespnow.c b/ports/esp32/modespnow.c index 5d048f6b2c7d7..725374ea77c55 100644 --- a/ports/esp32/modespnow.c +++ b/ports/esp32/modespnow.c @@ -817,6 +817,22 @@ static const mp_rom_map_elem_t espnow_globals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_KEY_LEN), MP_ROM_INT(ESP_NOW_KEY_LEN)}, { MP_ROM_QSTR(MP_QSTR_MAX_TOTAL_PEER_NUM), MP_ROM_INT(ESP_NOW_MAX_TOTAL_PEER_NUM)}, { MP_ROM_QSTR(MP_QSTR_MAX_ENCRYPT_PEER_NUM), MP_ROM_INT(ESP_NOW_MAX_ENCRYPT_PEER_NUM)}, + + #if !CONFIG_IDF_TARGET_ESP32C2 + { MP_ROM_QSTR(MP_QSTR_RATE_LORA_250K), MP_ROM_INT(WIFI_PHY_RATE_LORA_250K)}, + { MP_ROM_QSTR(MP_QSTR_RATE_LORA_500K), MP_ROM_INT(WIFI_PHY_RATE_LORA_500K)}, + #endif + // Note: specifying long preamble versions for the lower bit rates apart + // from the non-802.11b 6Mbit rate, for more robust error correction + { MP_ROM_QSTR(MP_QSTR_RATE_1M), MP_ROM_INT(WIFI_PHY_RATE_1M_L)}, + { MP_ROM_QSTR(MP_QSTR_RATE_2M), MP_ROM_INT(WIFI_PHY_RATE_2M_L)}, + { MP_ROM_QSTR(MP_QSTR_RATE_5M), MP_ROM_INT(WIFI_PHY_RATE_5M_L)}, + { MP_ROM_QSTR(MP_QSTR_RATE_6M), MP_ROM_INT(WIFI_PHY_RATE_6M)}, + { MP_ROM_QSTR(MP_QSTR_RATE_11M), MP_ROM_INT(WIFI_PHY_RATE_11M_L)}, + { MP_ROM_QSTR(MP_QSTR_RATE_12M), MP_ROM_INT(WIFI_PHY_RATE_12M)}, + { MP_ROM_QSTR(MP_QSTR_RATE_24M), MP_ROM_INT(WIFI_PHY_RATE_24M)}, + { MP_ROM_QSTR(MP_QSTR_RATE_54M), MP_ROM_INT(WIFI_PHY_RATE_54M)}, + }; static MP_DEFINE_CONST_DICT(espnow_globals_dict, espnow_globals_dict_table); diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index e20af4806c43d..07b16e91cb941 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -79,6 +79,15 @@ static bool mdns_initialised = false; static uint8_t conf_wifi_sta_reconnects = 0; static uint8_t wifi_sta_reconnects; +// The rules for this default are defined in the documentation of esp_wifi_set_protocol() +// rather than in code, so we have to recreate them here. +#if CONFIG_SOC_WIFI_HE_SUPPORT +// Note: No Explicit support for 5GHz here, yet +#define WIFI_PROTOCOL_DEFAULT (WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AX) +#else +#define WIFI_PROTOCOL_DEFAULT (WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N) +#endif + // This function is called by the system-event task and so runs in a different // thread to the main MicroPython task. It must not raise any Python exceptions. static void network_wlan_wifi_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { @@ -771,6 +780,11 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_SEC_WPA_ENT), MP_ROM_INT(WIFI_AUTH_WPA_ENTERPRISE) }, #endif + { MP_ROM_QSTR(MP_QSTR_PROTOCOL_DEFAULT), MP_ROM_INT(WIFI_PROTOCOL_DEFAULT) }, + #if !CONFIG_IDF_TARGET_ESP32C2 + { MP_ROM_QSTR(MP_QSTR_PROTOCOL_LR), MP_ROM_INT(WIFI_PROTOCOL_LR) }, + #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) }, { MP_ROM_QSTR(MP_QSTR_PM_POWERSAVE), MP_ROM_INT(WIFI_PS_MAX_MODEM) }, From 938e2c0f2b79c983202cbeccd0bc91e13eff13d2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 20 Nov 2025 13:01:42 +1100 Subject: [PATCH 004/177] tests/multi_espnow: Add test case for espnow rate changes. Uses constants added in previous commit. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/library/espnow.rst | 2 +- tests/multi_espnow/75_rate.py | 92 +++++++++++++++++++++++++++++++ tests/multi_espnow/75_rate.py.exp | 31 +++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 tests/multi_espnow/75_rate.py create mode 100644 tests/multi_espnow/75_rate.py.exp diff --git a/docs/library/espnow.rst b/docs/library/espnow.rst index 4b4b058f87a89..14a92c11400b9 100644 --- a/docs/library/espnow.rst +++ b/docs/library/espnow.rst @@ -172,7 +172,7 @@ Configuration api-reference/network/esp_wifi.html#_CPPv415wifi_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. - See also `espnow-long-range`. + See also `espnow-long-range`. This API currently doesn't work on ESP32-C6. .. data:: Returns: diff --git a/tests/multi_espnow/75_rate.py b/tests/multi_espnow/75_rate.py new file mode 100644 index 0000000000000..9c05a24bd0098 --- /dev/null +++ b/tests/multi_espnow/75_rate.py @@ -0,0 +1,92 @@ +# Test configuring transmit rates for ESP-NOW on ESP32 + +import sys +import time + +try: + import network + import espnow +except ImportError: + print("SKIP") + raise SystemExit + +# ESP8266 doesn't support multiple ESP-NOW data rates +if sys.platform == "esp8266": + print("SKIP") + raise SystemExit + +# Currently the config(rate=...) implementation is not compatible with ESP32-C6 +# (this test passes when C6 is receiver, but not if C6 is sender.) +if "ESP32C6" in sys.implementation._machine: + print("SKIP") + raise SystemExit + +# ESP32-C2 doesn't support Long Range mode. This test is currently written assuming +# LR mode can be enabled. +if "ESP32C2" in sys.implementation._machine: + print("SKIP") + raise SystemExit + + +timeout_ms = 1000 +default_pmk = b"MicroPyth0nRules" +CHANNEL = 9 + + +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) + # Enable both default 802.11 modes and Long Range modes + sta.config(protocol=network.WLAN.PROTOCOL_LR | network.WLAN.PROTOCOL_DEFAULT) + return sta, e + + +# Receiver +def instance0(): + sta, e = init_sta() + multitest.globals(PEER=sta.config("mac")) + multitest.next() + while True: + peer, msg = e.recv(timeout_ms) + if peer is None: + print("Timeout") + break + # Note that we don't have any way in Python to tell what data rate this message + # was received with, so we're assuming the rate was correct. + print(msg) + e.active(False) + + +# Sender +def instance1(): + sta, e = init_sta() + multitest.next() + peer = PEER + + e.add_peer(peer) + # Test normal, non-LR rates + for msg, rate in ( + (b"default rate", None), + (b"5Mbit", espnow.RATE_5M), + (b"11Mbit", espnow.RATE_11M), + (b"24Mbit", espnow.RATE_24M), + (b"54Mbit", espnow.RATE_54M), + (b"250K LR", espnow.RATE_LORA_250K), + (b"500K LR", espnow.RATE_LORA_500K), + # switch back to non-LR rates to check it's all OK + (b"1Mbit again", espnow.RATE_1M), + (b"11Mbit again", espnow.RATE_11M), + ): + if rate is not None: + e.config(rate=rate) + for _ in range(3): + e.send(peer, msg) + time.sleep_ms(50) # give messages some time to be received before continuing + e.del_peer(peer) + + e.active(False) diff --git a/tests/multi_espnow/75_rate.py.exp b/tests/multi_espnow/75_rate.py.exp new file mode 100644 index 0000000000000..5a275ee6b28bc --- /dev/null +++ b/tests/multi_espnow/75_rate.py.exp @@ -0,0 +1,31 @@ +--- instance0 --- +b'default rate' +b'default rate' +b'default rate' +b'5Mbit' +b'5Mbit' +b'5Mbit' +b'11Mbit' +b'11Mbit' +b'11Mbit' +b'24Mbit' +b'24Mbit' +b'24Mbit' +b'54Mbit' +b'54Mbit' +b'54Mbit' +b'250K LR' +b'250K LR' +b'250K LR' +b'500K LR' +b'500K LR' +b'500K LR' +b'1Mbit again' +b'1Mbit again' +b'1Mbit again' +b'11Mbit again' +b'11Mbit again' +b'11Mbit again' +Timeout +--- instance1 --- + From c22ff43753c0679d375db32c9f8752558c8d7511 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 19:06:26 +0000 Subject: [PATCH 005/177] github/workflows: Bump actions/checkout from 5 to 6. Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/biome.yml | 2 +- .github/workflows/code_formatting.yml | 2 +- .github/workflows/code_size.yml | 2 +- .github/workflows/codespell.yml | 2 +- .github/workflows/commit_formatting.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/examples.yml | 2 +- .github/workflows/mpremote.yml | 2 +- .github/workflows/mpy_format.yml | 2 +- .github/workflows/ports.yml | 2 +- .github/workflows/ports_alif.yml | 2 +- .github/workflows/ports_cc3200.yml | 2 +- .github/workflows/ports_esp32.yml | 2 +- .github/workflows/ports_esp8266.yml | 2 +- .github/workflows/ports_mimxrt.yml | 2 +- .github/workflows/ports_nrf.yml | 2 +- .github/workflows/ports_powerpc.yml | 2 +- .github/workflows/ports_qemu.yml | 6 ++-- .github/workflows/ports_renesas-ra.yml | 2 +- .github/workflows/ports_rp2.yml | 2 +- .github/workflows/ports_samd.yml | 2 +- .github/workflows/ports_stm32.yml | 2 +- .github/workflows/ports_unix.yml | 40 ++++++++++++------------- .github/workflows/ports_webassembly.yml | 2 +- .github/workflows/ports_windows.yml | 6 ++-- .github/workflows/ports_zephyr.yml | 2 +- .github/workflows/ruff.yml | 2 +- 27 files changed, 50 insertions(+), 50 deletions(-) diff --git a/.github/workflows/biome.yml b/.github/workflows/biome.yml index fea9c9c8bd7bc..0cde10acc9182 100644 --- a/.github/workflows/biome.yml +++ b/.github/workflows/biome.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Biome uses: biomejs/setup-biome@v2 with: diff --git a/.github/workflows/code_formatting.yml b/.github/workflows/code_formatting.yml index 8a18ca034540c..95653d941f91f 100644 --- a/.github/workflows/code_formatting.yml +++ b/.github/workflows/code_formatting.yml @@ -10,7 +10,7 @@ jobs: code-formatting: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 - name: Install packages run: tools/ci.sh c_code_formatting_setup diff --git a/.github/workflows/code_size.yml b/.github/workflows/code_size.yml index f82d1d0c73567..aed416767883d 100644 --- a/.github/workflows/code_size.yml +++ b/.github/workflows/code_size.yml @@ -25,7 +25,7 @@ jobs: build: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 100 - name: Install packages diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 688134b42515c..c413a430a5b29 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -6,7 +6,7 @@ jobs: codespell: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 # codespell version should be kept in sync with .pre-commit-config.yml - run: pip install --user codespell==2.4.1 tomli - run: codespell diff --git a/.github/workflows/commit_formatting.yml b/.github/workflows/commit_formatting.yml index ca63fc796b0cc..6abc3612a0004 100644 --- a/.github/workflows/commit_formatting.yml +++ b/.github/workflows/commit_formatting.yml @@ -10,7 +10,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 100 - uses: actions/setup-python@v6 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index aa05cfb13873b..79755b741973f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 - name: Install Python packages run: pip install -r docs/requirements.txt diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index d16122b720b6c..4adeaae2e5e4b 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -18,7 +18,7 @@ jobs: embedding: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build run: make -C examples/embedding -f micropython_embed.mk && make -C examples/embedding - name: Run diff --git a/.github/workflows/mpremote.yml b/.github/workflows/mpremote.yml index 36904764c991b..5b9f1ff130a27 100644 --- a/.github/workflows/mpremote.yml +++ b/.github/workflows/mpremote.yml @@ -11,7 +11,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: # Setting this to zero means fetch all history and tags, # which hatch-vcs can use to discover the version tag. diff --git a/.github/workflows/mpy_format.yml b/.github/workflows/mpy_format.yml index e692a853e7b2c..b51110db67762 100644 --- a/.github/workflows/mpy_format.yml +++ b/.github/workflows/mpy_format.yml @@ -17,7 +17,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh mpy_format_setup - name: Test mpy-tool.py diff --git a/.github/workflows/ports.yml b/.github/workflows/ports.yml index d4e89bd1aa924..5e71d4d076ae3 100644 --- a/.github/workflows/ports.yml +++ b/.github/workflows/ports.yml @@ -17,6 +17,6 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build ports download metadata run: mkdir boards && ./tools/autobuild/build-downloads.py . ./boards diff --git a/.github/workflows/ports_alif.yml b/.github/workflows/ports_alif.yml index eea8f53900ebc..6fb225937a90d 100644 --- a/.github/workflows/ports_alif.yml +++ b/.github/workflows/ports_alif.yml @@ -26,7 +26,7 @@ jobs: - alif_ae3_build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh alif_setup - name: Build ci_${{matrix.ci_func }} diff --git a/.github/workflows/ports_cc3200.yml b/.github/workflows/ports_cc3200.yml index c57309c23a6d0..194483ec218d1 100644 --- a/.github/workflows/ports_cc3200.yml +++ b/.github/workflows/ports_cc3200.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh cc3200_setup - name: Build diff --git a/.github/workflows/ports_esp32.yml b/.github/workflows/ports_esp32.yml index 578bf33af5b58..eea82126d4f29 100644 --- a/.github/workflows/ports_esp32.yml +++ b/.github/workflows/ports_esp32.yml @@ -32,7 +32,7 @@ jobs: - esp32_build_c2_c5_c6 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - id: idf_ver name: Read the ESP-IDF version (including Python version) diff --git a/.github/workflows/ports_esp8266.yml b/.github/workflows/ports_esp8266.yml index 96cf0c5a5cd3f..eb7f59cdc4900 100644 --- a/.github/workflows/ports_esp8266.yml +++ b/.github/workflows/ports_esp8266.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh esp8266_setup && tools/ci.sh esp8266_path >> $GITHUB_PATH - name: Build diff --git a/.github/workflows/ports_mimxrt.yml b/.github/workflows/ports_mimxrt.yml index bcbaf3de066c9..fd80f3f632925 100644 --- a/.github/workflows/ports_mimxrt.yml +++ b/.github/workflows/ports_mimxrt.yml @@ -24,7 +24,7 @@ jobs: run: working-directory: 'micropython repo' # test build with space in path steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: path: 'micropython repo' - name: Install packages diff --git a/.github/workflows/ports_nrf.yml b/.github/workflows/ports_nrf.yml index 995f65933e0b0..bec9a5dfb5b8f 100644 --- a/.github/workflows/ports_nrf.yml +++ b/.github/workflows/ports_nrf.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh nrf_setup - name: Build diff --git a/.github/workflows/ports_powerpc.yml b/.github/workflows/ports_powerpc.yml index 27417fb3c3cf2..a883d026806b5 100644 --- a/.github/workflows/ports_powerpc.yml +++ b/.github/workflows/ports_powerpc.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh powerpc_setup - name: Build diff --git a/.github/workflows/ports_qemu.yml b/.github/workflows/ports_qemu.yml index b038b03e71fed..0ed95dbe5f93a 100644 --- a/.github/workflows/ports_qemu.yml +++ b/.github/workflows/ports_qemu.yml @@ -30,7 +30,7 @@ jobs: - thumb_hardfp runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh qemu_setup_arm - name: Build and run test suite ci_qemu_build_arm_${{ matrix.ci_func }} @@ -42,7 +42,7 @@ jobs: build_and_test_rv32: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh qemu_setup_rv32 - name: Build and run test suite @@ -54,7 +54,7 @@ jobs: build_and_test_rv64: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh qemu_setup_rv64 - name: Build and run test suite diff --git a/.github/workflows/ports_renesas-ra.yml b/.github/workflows/ports_renesas-ra.yml index 600b8ea8046bd..920691eca70e1 100644 --- a/.github/workflows/ports_renesas-ra.yml +++ b/.github/workflows/ports_renesas-ra.yml @@ -21,7 +21,7 @@ jobs: build_renesas_ra_board: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh renesas_ra_setup - name: Build diff --git a/.github/workflows/ports_rp2.yml b/.github/workflows/ports_rp2.yml index 0837c06c97bd4..ea19e2da7ffc9 100644 --- a/.github/workflows/ports_rp2.yml +++ b/.github/workflows/ports_rp2.yml @@ -24,7 +24,7 @@ jobs: run: working-directory: 'micropython repo' # test build with space in path steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: path: 'micropython repo' - name: Install packages diff --git a/.github/workflows/ports_samd.yml b/.github/workflows/ports_samd.yml index d159fde175d54..eb806ceb04400 100644 --- a/.github/workflows/ports_samd.yml +++ b/.github/workflows/ports_samd.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh samd_setup - name: Build diff --git a/.github/workflows/ports_stm32.yml b/.github/workflows/ports_stm32.yml index eae3bae871f45..45aa0c6130483 100644 --- a/.github/workflows/ports_stm32.yml +++ b/.github/workflows/ports_stm32.yml @@ -28,7 +28,7 @@ jobs: - stm32_misc_build runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh stm32_setup - name: Build ci_${{matrix.ci_func }} diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index deee7b265fcb7..19d6dcf85651e 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -23,7 +23,7 @@ jobs: minimal: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build run: tools/ci.sh unix_minimal_build - name: Run main test suite @@ -35,7 +35,7 @@ jobs: reproducible: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build with reproducible date run: tools/ci.sh unix_minimal_build env: @@ -46,7 +46,7 @@ jobs: standard: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build run: tools/ci.sh unix_standard_build - name: Run main test suite @@ -58,7 +58,7 @@ jobs: standard_v2: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build run: tools/ci.sh unix_standard_v2_build - name: Run main test suite @@ -70,7 +70,7 @@ jobs: coverage: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests. # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default. @@ -105,7 +105,7 @@ jobs: coverage_32bit: runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh unix_32bit_setup - name: Build @@ -123,7 +123,7 @@ jobs: nanbox: runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh unix_32bit_setup - name: Build @@ -137,7 +137,7 @@ jobs: longlong: runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh unix_32bit_setup - name: Build @@ -151,7 +151,7 @@ jobs: float: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build run: tools/ci.sh unix_float_build - name: Run main test suite @@ -163,7 +163,7 @@ jobs: gil_enabled: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build run: tools/ci.sh unix_gil_enabled_build - name: Run main test suite @@ -175,7 +175,7 @@ jobs: stackless_clang: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh unix_clang_setup - name: Build @@ -189,7 +189,7 @@ jobs: float_clang: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh unix_clang_setup - name: Build @@ -203,7 +203,7 @@ jobs: settrace_stackless: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests. # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default. @@ -220,7 +220,7 @@ jobs: repr_b: runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh unix_32bit_setup - name: Build @@ -234,7 +234,7 @@ jobs: macos: runs-on: macos-26 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: python-version: '3.8' @@ -250,7 +250,7 @@ jobs: # ubuntu-22.04 is needed for older libffi. runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh unix_qemu_mips_setup - name: Build @@ -265,7 +265,7 @@ jobs: # ubuntu-22.04 is needed for older libffi. runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh unix_qemu_arm_setup - name: Build @@ -280,7 +280,7 @@ jobs: # ubuntu-22.04 is needed for older libffi. runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh unix_qemu_riscv64_setup - name: Build @@ -294,7 +294,7 @@ jobs: sanitize_address: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 # 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. @@ -319,7 +319,7 @@ jobs: sanitize_undefined: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests. # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default. diff --git a/.github/workflows/ports_webassembly.yml b/.github/workflows/ports_webassembly.yml index 6bfbb5aec6c8e..f6619cc8976df 100644 --- a/.github/workflows/ports_webassembly.yml +++ b/.github/workflows/ports_webassembly.yml @@ -21,7 +21,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh webassembly_setup - name: Build diff --git a/.github/workflows/ports_windows.yml b/.github/workflows/ports_windows.yml index e4e0152d3e9da..793eba36a7b6b 100644 --- a/.github/workflows/ports_windows.yml +++ b/.github/workflows/ports_windows.yml @@ -59,7 +59,7 @@ jobs: - uses: microsoft/setup-msbuild@v2 with: vs-version: ${{ matrix.vs_version }} - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build mpy-cross.exe run: msbuild mpy-cross\mpy-cross.vcxproj -maxcpucount -property:Configuration=${{ matrix.configuration }} -property:Platform=${{ matrix.platform }} - name: Update submodules @@ -126,7 +126,7 @@ jobs: git diffutils path-type: inherit # Remove when setup-python is removed - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build mpy-cross.exe run: make -C mpy-cross -j2 - name: Update submodules @@ -144,7 +144,7 @@ jobs: cross-build-on-linux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install packages run: tools/ci.sh windows_setup - name: Build diff --git a/.github/workflows/ports_zephyr.yml b/.github/workflows/ports_zephyr.yml index 3579f4e1bc90c..812f4cbf47ba2 100644 --- a/.github/workflows/ports_zephyr.yml +++ b/.github/workflows/ports_zephyr.yml @@ -37,7 +37,7 @@ jobs: docker-images: false tool-cache: true swap-storage: false - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - id: versions name: Read Zephyr version run: source tools/ci.sh && echo "ZEPHYR=$ZEPHYR_VERSION" | tee "$GITHUB_OUTPUT" diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 633b0cdf82ef4..6a8d4055a91b0 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -6,7 +6,7 @@ jobs: ruff: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 # ruff version should be kept in sync with .pre-commit-config.yaml & also micropython-lib - run: pipx install ruff==0.11.6 - run: ruff check --output-format=github . From d1caa9df07d07a9c2897d106fe5fb7f4b595203e Mon Sep 17 00:00:00 2001 From: Alex Tran Date: Fri, 21 Nov 2025 21:37:05 -0800 Subject: [PATCH 006/177] unix/modsocket: Add IP ADD and DROP MEMBERSHIP to socket constants. Add the IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP to modsocket in the Unix port so that the directives take on the values defined in the system headers. This is needed because the values of these directives are different for MacOS vs other Unix systems. Fixes issue #8456. Signed-off-by: Alex Tran --- ports/unix/modsocket.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/unix/modsocket.c b/ports/unix/modsocket.c index e7f25cdd43db7..660ace79b52ce 100644 --- a/ports/unix/modsocket.c +++ b/ports/unix/modsocket.c @@ -709,6 +709,9 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = { C(SO_KEEPALIVE), C(SO_LINGER), C(SO_REUSEADDR), + + C(IP_ADD_MEMBERSHIP), + C(IP_DROP_MEMBERSHIP), #undef C }; From 4633d2a0374f79c3306e60b603377606664e8a24 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 19 Nov 2025 02:50:12 +0100 Subject: [PATCH 007/177] tools/ci.sh: Add zsh and fish shell completion support. This commit adds custom command completion functions for both the zsh and fish shell. The behaviour for those new completions follow the existing completion for the bash shell, including the way to generate the completion alias (with appropriately named command line switches). Signed-off-by: Alessandro Gatti --- docs/develop/gettingstarted.rst | 10 +++++++++- tools/ci.sh | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/develop/gettingstarted.rst b/docs/develop/gettingstarted.rst index cb479458e1102..a6afc5cad84c7 100644 --- a/docs/develop/gettingstarted.rst +++ b/docs/develop/gettingstarted.rst @@ -338,7 +338,15 @@ If you use the bash shell, you can add a ``ci`` command with tab completion: .. code-block:: bash - $ eval `tools/ci.sh --bash-completion` + $ eval $(tools/ci.sh --bash-completion) + +For the zsh shell, replace ``--bash-completion`` with ``--zsh-completion``. +For the fish shell, replace ``--bash-completion`` with ``--fish-completion``. + +Then, typing: + +.. code-block:: bash + $ ci unix_cov This will complete the ci step name to ``unix_coverage_``. diff --git a/tools/ci.sh b/tools/ci.sh index 63eed15bea2be..fa7a529b212f6 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -1049,6 +1049,14 @@ function _ci_bash_completion { echo "alias ci=\"$(readlink -f "$0")\"; complete -W '$(grep '^function ci_' $0 | awk '{print $2}' | sed 's/^ci_//')' ci" } +function _ci_zsh_completion { + echo "alias ci=\"$(readlink -f "$0"\"); _complete_mpy_ci_zsh() { compadd $(grep '^function ci_' $0 | awk '{sub(/^ci_/,"",$2); print $2}' | tr '\n' ' ') }; autoload -Uz compinit; compinit; compdef _complete_mpy_ci_zsh $(readlink -f "$0")" +} + +function _ci_fish_completion { + echo "alias ci=\"$(readlink -f "$0"\"); complete -c ci -p $(readlink -f "$0") -f -a '$(grep '^function ci_' $(readlink -f "$0") | awk '{sub(/^ci_/,"",$2); print $2}' | tr '\n' ' ')'" +} + function _ci_main { case "$1" in (-h|-?|--help) @@ -1057,6 +1065,12 @@ function _ci_main { (--bash-completion) _ci_bash_completion ;; + (--zsh-completion) + _ci_zsh_completion + ;; + (--fish-completion) + _ci_fish_completion + ;; (-*) echo "Unknown option: $1" 1>&2 exit 1 From 1c47db32e4096c4fdb978771ef108d93bff28ace Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Thu, 6 Nov 2025 16:44:40 +0100 Subject: [PATCH 008/177] docs/library: Document OrderedDict.popitem()'s CPython differences. Fixes issue #18370. Signed-off-by: Jos Verlinde --- docs/library/collections.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/library/collections.rst b/docs/library/collections.rst index 6a23e456c66a7..f973e521e2ea5 100644 --- a/docs/library/collections.rst +++ b/docs/library/collections.rst @@ -101,3 +101,19 @@ Classes a 2 w 5 b 3 + + .. method:: OrderedDict.popitem() + + Remove and return a (key, value) pair from the dictionary. + Pairs are returned in LIFO order. + + .. admonition:: Difference to CPython + :class: attention + + ``OrderedDict.popitem()`` does not support the ``last=False`` argument and + will always remove and return the last item if present. + + A workaround for this is to use ``pop()`` to remove the first item:: + + first_key = next(iter(d)) + d.pop(first_key) From 9b3b3a53ed52340809f9b87f0c215bbd8e87f9a9 Mon Sep 17 00:00:00 2001 From: Alex Tran Date: Mon, 24 Nov 2025 16:57:00 -0800 Subject: [PATCH 009/177] docs/library: Fix typos under I2CTarget irq method description. There were a few typos in the documentation for the `I2CTarget.irq` method description where `IRQ_ADDR_MATCH_READ` was used, however `IRQ_ADDR_MATCH_WRITE` should have been used. Fixes issue #18470. Signed-off-by: Alex Tran --- docs/library/machine.I2CTarget.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/library/machine.I2CTarget.rst b/docs/library/machine.I2CTarget.rst index 0e4af84cb687c..2765b98143a5f 100644 --- a/docs/library/machine.I2CTarget.rst +++ b/docs/library/machine.I2CTarget.rst @@ -126,7 +126,7 @@ General Methods - ``IRQ_ADDR_MATCH_READ`` indicates that the target was addressed by a controller for a read transaction. - - ``IRQ_ADDR_MATCH_READ`` indicates that the target was addressed by a + - ``IRQ_ADDR_MATCH_WRITE`` indicates that the target was addressed by a controller for a write transaction. - ``IRQ_READ_REQ`` indicates that the controller is requesting data, and this request must be satisfied by calling `I2CTarget.write` with the data to be @@ -141,7 +141,7 @@ General Methods Note the following restrictions: - - ``IRQ_ADDR_MATCH_READ``, ``IRQ_ADDR_MATCH_READ``, ``IRQ_READ_REQ`` and + - ``IRQ_ADDR_MATCH_READ``, ``IRQ_ADDR_MATCH_WRITE``, ``IRQ_READ_REQ`` and ``IRQ_WRITE_REQ`` must be handled by a hard IRQ callback (with the *hard* argument set to ``True``). This is because these events have very strict timing requirements and must usually be satisfied synchronously with the hardware event. From d5ad2cdcca3e94e4d1822dbedc2c3f42057a6ce4 Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Tue, 2 Sep 2025 23:26:53 +1000 Subject: [PATCH 010/177] docs/library: Add machine.DAC documentation. Fixes issue #7915. Signed-off-by: Matt Trentini --- docs/library/machine.DAC.rst | 68 ++++++++++++++++++++++++++++++++++++ docs/library/machine.rst | 1 + 2 files changed, 69 insertions(+) create mode 100644 docs/library/machine.DAC.rst diff --git a/docs/library/machine.DAC.rst b/docs/library/machine.DAC.rst new file mode 100644 index 0000000000000..befcba8323060 --- /dev/null +++ b/docs/library/machine.DAC.rst @@ -0,0 +1,68 @@ +.. currentmodule:: machine +.. _machine.DAC: + +class DAC -- digital to analog conversion +========================================= + +The DAC is used to output an analog voltage based on a digital value. + +The output voltage will be between 0 and 3.3V. + +DAC is currently supported on ESP32 [#esp32_dac]_, SAMD and Renesas RA. + +.. note:: + The STM32 port has similar functionality to ``machine.DAC``. See + :ref:`pyb.DAC ` for details. + +Example usage (ESP32):: + + from machine import DAC + + dac = DAC(pin) # create a DAC object acting on a pin + dac.write(128) # write a value to the DAC + dac.write(255) # output maximum value, 3.3V + +Constructors +------------ + +.. class:: DAC(id) + + Construct a new DAC object. + + ``id`` is a pin object (ESP32 and Renesas RA) or an index to a DAC resource (SAMD). + +.. note:: + On the ESP32, DAC functionality is available on pins 25 and 26. On the + ESP32-S2, pins 17 and 18. See :ref:`ESP32 Quickref ` + for more details. + +.. note:: + SAMD21 has one DAC resource, SAMD51 has two. See :ref:`SAMD Quickref ` + for more details. + +Methods +------- + +.. method:: DAC.write(value) + + Output an analog voltage to the pin connected to the DAC. + + ``value`` is a representation of the desired output; a linear interpolation of + 0-3.3V, though the range differs depending on the port and micro, see below: + + +--------------+------+--------+ + | *Port/micro* | Bits | Range | + +==============+======+========+ + | ESP32 | 8 | 0-255 | + +--------------+------+--------+ + | SAMD21 | 10 | 0-1023 | + +--------------+------+--------+ + | SAMD51 | 12 | 0-4095 | + +--------------+------+--------+ + | Renesas RA | 12 | 0-4095 | + +--------------+------+--------+ + +.. rubric:: Footnotes + +.. [#esp32_dac] The original ESP32 and ESP32-S2 *only*, since DAC hardware is + not present on other microcontrollers in the family. diff --git a/docs/library/machine.rst b/docs/library/machine.rst index 7acaddde815bc..69eda917e5abb 100644 --- a/docs/library/machine.rst +++ b/docs/library/machine.rst @@ -260,6 +260,7 @@ Classes machine.Signal.rst machine.ADC.rst machine.ADCBlock.rst + machine.DAC.rst machine.PWM.rst machine.UART.rst machine.SPI.rst From bd111ccd4b4932ea94ba7c66007840cb88e3b4a7 Mon Sep 17 00:00:00 2001 From: Vdragon Date: Thu, 20 Nov 2025 21:01:20 +0100 Subject: [PATCH 011/177] zephyr: Allow a custom dts. Allows using custom DTS things such as bindings. Signed-off-by: Vdragon --- ports/zephyr/CMakeLists.txt | 2 ++ ports/zephyr/dts/bindings/vendor-prefixes.txt | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 ports/zephyr/dts/bindings/vendor-prefixes.txt diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index 9db0a95c663de..cf35ba73da278 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -35,6 +35,8 @@ string(TOUPPER ZEPHYR_${BOARD} MICROPY_BOARD) include(${MICROPY_DIR}/py/py.cmake) include(${MICROPY_DIR}/extmod/extmod.cmake) +list(APPEND DTS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/dts) + if (CONFIG_MICROPY_FROZEN_MODULES) cmake_path(ABSOLUTE_PATH CONFIG_MICROPY_FROZEN_MANIFEST BASE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) set(MICROPY_FROZEN_MANIFEST ${CONFIG_MICROPY_FROZEN_MANIFEST}) diff --git a/ports/zephyr/dts/bindings/vendor-prefixes.txt b/ports/zephyr/dts/bindings/vendor-prefixes.txt new file mode 100644 index 0000000000000..f2dc741d997d6 --- /dev/null +++ b/ports/zephyr/dts/bindings/vendor-prefixes.txt @@ -0,0 +1,15 @@ +# Device tree binding vendor prefix registry. Keep this list in +# alphabetical order. +# +# This isn't an exhaustive list, but you should add new prefixes to it +# before using them to avoid name-space collisions. +# +# The contents of this file are parsed during documentation generation. +# Anything that starts with a '#' is treated as a comment and ignored. +# Non-empty lines should be in this format: +# +# + +# zephyr-keep-sorted-start +micropython MicroPython Project +# zephyr-keep-sorted-stop From 3d9a3e89cf4a32fdc6fca2a8edd91714b5b67665 Mon Sep 17 00:00:00 2001 From: Vdragon Date: Sat, 25 Oct 2025 23:39:33 +0200 Subject: [PATCH 012/177] zephyr: Add support for GC split-heap. Adds the ability to use PSRAM and non-contiguous memory. Example usage, add this to dts overlay: / { heap_psram { compatible = "micropython,heap"; size = ; memory-region = <&psram>; }; heap_sram1 { compatible = "micropython,heap"; size = ; memory-region = <&sram1>; }; }; Signed-off-by: Vdragon --- .../micropython/micropython,heap.yaml | 23 +++++++++++++++++ ports/zephyr/main.c | 25 +++++++++++++++++++ ports/zephyr/mpconfigport.h | 4 +++ 3 files changed, 52 insertions(+) create mode 100644 ports/zephyr/dts/bindings/micropython/micropython,heap.yaml diff --git a/ports/zephyr/dts/bindings/micropython/micropython,heap.yaml b/ports/zephyr/dts/bindings/micropython/micropython,heap.yaml new file mode 100644 index 0000000000000..333baa96686ac --- /dev/null +++ b/ports/zephyr/dts/bindings/micropython/micropython,heap.yaml @@ -0,0 +1,23 @@ +# Copyright (c) 2025 MASSDRIVER EI (massdriver.space) +# SPDX-License-Identifier: Apache-2.0 + +description: | + MicroPython Heap + Used to define micropython heap areas. + +compatible: "micropython,heap" + +include: [base.yaml] + +properties: + size: + type: int + required: true + description: | + Size of the heap + + memory-region: + type: phandle + required: true + description: | + Memory region to place the heap in diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 0fa4e97b24186..c82814e04b5d0 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -58,6 +58,28 @@ #include "extmod/vfs.h" #endif +#if MICROPY_ENABLE_GC +#if MICROPY_GC_SPLIT_HEAP && DT_HAS_COMPAT_STATUS_OKAY(micropython_heap) + +#include + +#define MICROPY_HEAP_NAME(node) CONCAT(DT_NODE_FULL_NAME_TOKEN(node), _heap) + +#define MICROPY_HEAP_DEFINE(node) \ + static char MICROPY_HEAP_NAME(node)[DT_PROP(node, size)] \ + Z_GENERIC_SECTION(LINKER_DT_NODE_REGION_NAME(DT_PROP(node, memory_region))); + +DT_FOREACH_STATUS_OKAY(micropython_heap, MICROPY_HEAP_DEFINE) + +#define MICROPY_HEAP_ADD(node) \ + gc_add((void *)&MICROPY_HEAP_NAME(node), \ + (void *)&(MICROPY_HEAP_NAME(node)[DT_PROP(node, size) - 1])); + +#elif DT_HAS_COMPAT_STATUS_OKAY(micropython_heap) +#error Has additional Heap but split heap is not enabled +#endif +#endif + #include "modmachine.h" #include "modzephyr.h" @@ -107,6 +129,9 @@ int real_main(void) { mp_cstack_init_with_sp_here(CONFIG_MAIN_STACK_SIZE); #if MICROPY_ENABLE_GC gc_init(heap, heap + sizeof(heap)); + #if MICROPY_GC_SPLIT_HEAP && DT_HAS_COMPAT_STATUS_OKAY(micropython_heap) + DT_FOREACH_STATUS_OKAY(micropython_heap, MICROPY_HEAP_ADD) + #endif #endif mp_init(); diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index 7f84973488786..56a442c26972e 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -151,6 +151,10 @@ void mp_hal_signal_event(void); #define MICROPY_PY_MACHINE_ADC_READ_UV (1) #endif +#if DT_HAS_COMPAT_STATUS_OKAY(micropython_heap) +#define MICROPY_GC_SPLIT_HEAP (1) +#endif + typedef long mp_off_t; #define MP_STATE_PORT MP_STATE_VM From 65f994e26a852eecc2b641db4d35b5e1676d4442 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 4 Nov 2025 20:18:16 +0100 Subject: [PATCH 013/177] py/compile: Allow NULL emitter table entries. This commit fixes a regression introduced in 1b92bda5b8e5e2db8e57c4b7134930e52bc5207a, where a new architecture was added to mpy-cross but it had no matching native emitter exists. The result was that the architecture emitter entry point would be correctly calculated according to the native architecture index, but if the emitters entry points table was not updated to match the new number of architectures an out of bound access may be performed. Unfortunately adding RV64IMC shifted the debug emitter index further down the table, and that table wasn't updated to reflect the lack of an emitter for RV64. Adding a NULL entry there would cause a NULL pointer access as there was no need to perform any check about the emitter entry point function's validity until now. Signed-off-by: Alessandro Gatti --- py/compile.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/py/compile.c b/py/compile.c index 945ee2b2dee85..6d7e9c4a14643 100644 --- a/py/compile.c +++ b/py/compile.c @@ -103,6 +103,7 @@ static const emit_method_table_t *emit_native_table[] = { &emit_native_xtensa_method_table, &emit_native_xtensawin_method_table, &emit_native_rv32_method_table, + NULL, &emit_native_debug_method_table, }; @@ -3571,6 +3572,13 @@ void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool case MP_EMIT_OPT_NATIVE_PYTHON: case MP_EMIT_OPT_VIPER: if (emit_native == NULL) { + // The check looks like this to work around a false + // warning in GCC 13 (and possibly later), where it + // assumes that the check will always fail. + if ((uintptr_t)NATIVE_EMITTER_TABLE == (uintptr_t)NULL) { + comp->compile_error = mp_obj_new_exception_msg(&mp_type_NotImplementedError, MP_ERROR_TEXT("cannot emit native code for this architecture")); + goto emit_finished; + } emit_native = NATIVE_EMITTER(new)(&comp->emit_common, &comp->compile_error, &comp->next_label, max_num_labels); } comp->emit_method_table = NATIVE_EMITTER_TABLE; @@ -3603,6 +3611,10 @@ void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool } } + #if MICROPY_EMIT_NATIVE +emit_finished: + #endif + if (comp->compile_error != MP_OBJ_NULL) { // if there is no line number for the error then use the line // number for the start of this scope From 6b661ca3f6d877cfca224cc311cb84eba2d2b380 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 5 Nov 2025 21:58:51 +0100 Subject: [PATCH 014/177] github/workflows: Test mpy-cross debug emitter. This commit adds a new workflow step to the CI, to test the debug emitter provided by mpy-cross. The checks being done are limited to make sure that the debug emitter does not crash and emits opcodes for a simple test file that is guaranteed to work for all configurations. Signed-off-by: Alessandro Gatti --- .github/workflows/mpy_format.yml | 4 ++++ tools/ci.sh | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/.github/workflows/mpy_format.yml b/.github/workflows/mpy_format.yml index b51110db67762..ab668c1cb5ea0 100644 --- a/.github/workflows/mpy_format.yml +++ b/.github/workflows/mpy_format.yml @@ -6,6 +6,8 @@ on: paths: - '.github/workflows/*.yml' - 'examples/**' + - 'mpy-cross/**' + - 'py/**' - 'tests/**' - 'tools/**' @@ -22,3 +24,5 @@ jobs: run: tools/ci.sh mpy_format_setup - name: Test mpy-tool.py run: tools/ci.sh mpy_format_test + - name: Test mpy-cross debug emitter + run: tools/ci.sh mpy_cross_debug_emitter diff --git a/tools/ci.sh b/tools/ci.sh index fa7a529b212f6..e34940f758947 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -173,6 +173,15 @@ function ci_mpy_format_test { $micropython ./tools/mpy-tool.py -x -d examples/natmod/features1/features1.mpy } +function ci_mpy_cross_debug_emitter { + make ${MAKEOPTS} -C mpy-cross + mpy_cross=./mpy-cross/build/mpy-cross + + # Make sure the debug emitter does not crash or fail for simple files + $mpy_cross -X emit=native -march=debug ./tests/basics/0prelim.py | \ + grep -E "ENTRY|EXIT" | wc -l | grep "^2$" +} + ######################################################################################## # ports/cc3200 From b087cb41e85641b9aa530dc2001e151750219f0b Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 24 Nov 2025 11:51:28 +0100 Subject: [PATCH 015/177] stm32/mpconfigboard_common: Define TinyUSB MCU type for N6. Allows using TinyUSB stack on N6. Note there's still an issue with TinyUSB on the N6: `pyb_usbd_init()` can't be called multiple times (on soft-reboot). Signed-off-by: iabdalkader --- ports/stm32/mpconfigboard_common.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index eefd5c05c1b2b..1ce42055d80f9 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -542,6 +542,9 @@ #define MICROPY_HW_MAX_UART (10) #define MICROPY_HW_MAX_LPUART (1) +#define CFG_TUSB_MCU OPT_MCU_STM32N6 +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) + // Configuration for STM32U5 series #elif defined(STM32U5) From cad9bb3a2b5c73637d67591c43ee10c39719fa5e Mon Sep 17 00:00:00 2001 From: Mike Tolkachev Date: Mon, 13 Oct 2025 11:06:24 -0300 Subject: [PATCH 016/177] stm32: Add support for STM32F469xx MCUs. Signed-off-by: Mike Tolkachev --- ports/stm32/adc.c | 2 +- ports/stm32/boards/stm32f469xi.ld | 35 +++++++++++++++++++++++++++++++ ports/stm32/mpu.h | 2 +- ports/stm32/powerctrl.c | 6 +++--- 4 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 ports/stm32/boards/stm32f469xi.ld diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index 326fc97b25104..e9be7021322d5 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -138,7 +138,7 @@ defined(STM32F413xx) || defined(STM32F427xx) || \ defined(STM32F429xx) || defined(STM32F437xx) || \ defined(STM32F439xx) || defined(STM32F446xx) || \ - defined(STM32F479xx) + defined(STM32F479xx) || defined(STM32F469xx) #define VBAT_DIV (4) #elif defined(STM32F722xx) || defined(STM32F723xx) || \ defined(STM32F732xx) || defined(STM32F733xx) || \ diff --git a/ports/stm32/boards/stm32f469xi.ld b/ports/stm32/boards/stm32f469xi.ld new file mode 100644 index 0000000000000..4da9bf16f1a8f --- /dev/null +++ b/ports/stm32/boards/stm32f469xi.ld @@ -0,0 +1,35 @@ +/* + GNU linker script for STM32F469xI (2Mbyte) +*/ + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Entire flash */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* Sector 0 */ + FLASH_FS (r) : ORIGIN = 0x08004000, LENGTH = 112K /* Sectors 1, 2, 3, 4 */ + FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 1920K /* Sectors 5 - 23 */ + CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K /* CCM RAM used for storage cache */ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K /* SRAM1, SRAM2, SRAM3 */ +} + +/* produce a link error if there is not this amount of RAM for these sections */ +_minimum_stack_size = 2K; +_minimum_heap_size = 16K; + +/* Define the stack. The stack is full descending so begins just above last byte + of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ +_estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve; +_sstack = _estack - 16K; /* tunable */ + +/* RAM extents for the garbage collector */ +_ram_start = ORIGIN(RAM); +_ram_end = ORIGIN(RAM) + LENGTH(RAM); +_heap_start = _ebss; /* heap starts just after statically allocated memory */ +_heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(CCMRAM); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/mpu.h b/ports/stm32/mpu.h index 8713fe8370c5b..1d43e87f8d7b1 100644 --- a/ports/stm32/mpu.h +++ b/ports/stm32/mpu.h @@ -28,7 +28,7 @@ #include "irq.h" -#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32WB) +#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F469xx) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32WB) #define MPU_REGION_ETH (MPU_REGION_NUMBER0) #define MPU_REGION_QSPI1 (MPU_REGION_NUMBER1) diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index 7211ef873cd52..a63c57f4a78bb 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -798,13 +798,13 @@ void powerctrl_enter_stop_mode(void) { #if defined(STM32H7) || \ defined(STM32F427xx) || defined(STM32F437xx) || \ - defined(STM32F429xx) || defined(STM32F439xx) || \ + defined(STM32F429xx) || defined(STM32F439xx) || defined(STM32F469xx) || \ defined(STM32WB55xx) || defined(STM32WB35xx) // Disable SysTick Interrupt // Note: This seems to be required at least on the H7 REV Y, // otherwise the MCU will leave stop mode immediately on entry. // Note: According to ST Errata ES0206 Rev 18, Section 2.2.1 this is needed - // for STM32F427xx, STM32F437xx, STM32F429xx and STM32F439xx + // for STM32F427xx, STM32F437xx, STM32F429xx, STM32F439xx, and STM32F469xx // Note: According to ST Errata ES0394 Rev 11, Section 2.2.17 this is needed // for STM32WB55xx and STM32WB35xx SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; @@ -1039,7 +1039,7 @@ void powerctrl_enter_stop_mode(void) { #if defined(STM32H7) || \ defined(STM32F427xx) || defined(STM32F437xx) || \ - defined(STM32F429xx) || defined(STM32F439xx) || \ + defined(STM32F429xx) || defined(STM32F439xx) || defined(STM32F469xx) || \ defined(STM32WB55xx) || defined(STM32WB35xx) // Enable SysTick Interrupt SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; From b4d546d82e01d16664b2d17d9f145cb4768460c9 Mon Sep 17 00:00:00 2001 From: Mike Tolkachev Date: Mon, 13 Oct 2025 11:06:24 -0300 Subject: [PATCH 017/177] stm32/boards/STM32F469DISC: Add new board definition files. Signed-off-by: Mike Tolkachev --- ports/stm32/boards/STM32F469DISC/bdev.c | 45 ++++++ ports/stm32/boards/STM32F469DISC/board_init.c | 44 ++++++ .../boards/STM32F469DISC/mpconfigboard.h | 133 +++++++++++++++++ .../boards/STM32F469DISC/mpconfigboard.mk | 20 +++ ports/stm32/boards/STM32F469DISC/pins.csv | 135 ++++++++++++++++++ .../boards/STM32F469DISC/stm32f4xx_hal_conf.h | 41 ++++++ 6 files changed, 418 insertions(+) create mode 100644 ports/stm32/boards/STM32F469DISC/bdev.c create mode 100644 ports/stm32/boards/STM32F469DISC/board_init.c create mode 100644 ports/stm32/boards/STM32F469DISC/mpconfigboard.h create mode 100644 ports/stm32/boards/STM32F469DISC/mpconfigboard.mk create mode 100644 ports/stm32/boards/STM32F469DISC/pins.csv create mode 100644 ports/stm32/boards/STM32F469DISC/stm32f4xx_hal_conf.h diff --git a/ports/stm32/boards/STM32F469DISC/bdev.c b/ports/stm32/boards/STM32F469DISC/bdev.c new file mode 100644 index 0000000000000..40e4917e26d21 --- /dev/null +++ b/ports/stm32/boards/STM32F469DISC/bdev.c @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Mike Tolkachev + * + * 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 "storage.h" +#include "qspi.h" + +#if MICROPY_HW_SPIFLASH_ENABLE_CACHE +static mp_spiflash_cache_t spi_bdev_cache; +#endif + +// External SPI flash uses QSPI interface +const mp_spiflash_config_t spiflash_config = { + .bus_kind = MP_SPIFLASH_BUS_QSPI, + .bus.u_qspi.data = NULL, + .bus.u_qspi.proto = &qspi_proto, + #if MICROPY_HW_SPIFLASH_ENABLE_CACHE + .cache = &spi_bdev_cache, + #endif +}; + +// SPI flash device instance +spi_bdev_t spi_bdev; diff --git a/ports/stm32/boards/STM32F469DISC/board_init.c b/ports/stm32/boards/STM32F469DISC/board_init.c new file mode 100644 index 0000000000000..e4f75f218f80d --- /dev/null +++ b/ports/stm32/boards/STM32F469DISC/board_init.c @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Mike Tolkachev + * + * 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 "storage.h" +#include "boardctrl.h" +#include "qspi.h" + +// Micron N25Q128A13EF840F of original STM32F469I-DISCO board +static const mp_spiflash_chip_params_t chip_params_n25q128a13ef840f = { + .jedec_id = 0, // Not used for detection + .memory_size_bytes_log2 = MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2, + .qspi_prescaler = MICROPY_HW_QSPI_PRESCALER, + .qread_num_dummy = MICROPY_HW_QSPIFLASH_DUMMY_CYCLES +}; + +// Early board initialization hook called before file system is mounted +void STM32F469DISC_board_early_init(void) { + // Initialize QSPI flash device parameters + MICROPY_HW_BDEV_SPIFLASH->spiflash.chip_params = &chip_params_n25q128a13ef840f; +} diff --git a/ports/stm32/boards/STM32F469DISC/mpconfigboard.h b/ports/stm32/boards/STM32F469DISC/mpconfigboard.h new file mode 100644 index 0000000000000..1668618ce2b43 --- /dev/null +++ b/ports/stm32/boards/STM32F469DISC/mpconfigboard.h @@ -0,0 +1,133 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Mike Tolkachev + * + * 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 board is configured to communicate over USB port, not ST-Link + +#define MICROPY_HW_BOARD_NAME "F469DISC" +#define MICROPY_HW_MCU_NAME "STM32F469" + +// Use external QSPI flash for storage by default +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) + +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_USB (1) +#define MICROPY_HW_ENABLE_SDCARD (1) + +#define MICROPY_BOARD_EARLY_INIT STM32F469DISC_board_early_init +void STM32F469DISC_board_early_init(void); + +// QSPI flash storage configuration +#if !BUILDING_MBOOT +#define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) +#endif +#define MICROPY_HW_SPIFLASH_SOFT_RESET (1) +#define MICROPY_HW_SPIFLASH_SIZE_BITS (128 * 1024 * 1024) +#define MICROPY_HW_SPIFLASH_CHIP_PARAMS (1) // enable extended parameters +#define MICROPY_HW_QSPI_PRESCALER (3) +#define MICROPY_HW_QSPIFLASH_DUMMY_CYCLES (4) +#define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (27) +#define MICROPY_HW_QSPIFLASH_CS (pyb_pin_QSPI_CS) +#define MICROPY_HW_QSPIFLASH_SCK (pyb_pin_QSPI_CLK) +#define MICROPY_HW_QSPIFLASH_IO0 (pyb_pin_QSPI_D0) +#define MICROPY_HW_QSPIFLASH_IO1 (pyb_pin_QSPI_D1) +#define MICROPY_HW_QSPIFLASH_IO2 (pyb_pin_QSPI_D2) +#define MICROPY_HW_QSPIFLASH_IO3 (pyb_pin_QSPI_D3) + +// QSPI flash block device configuration +extern const struct _mp_spiflash_config_t spiflash_config; +extern struct _spi_bdev_t spi_bdev; +#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 + +// HSE is 8MHz +#define MICROPY_HW_CLK_PLLM (8) +#define MICROPY_HW_CLK_PLLN (336) +#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) +#define MICROPY_HW_CLK_PLLQ (7) + +#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_6 + +// UART config +#define MICROPY_HW_UART3_NAME "YB" +#define MICROPY_HW_UART3_TX (pin_B10) +#define MICROPY_HW_UART3_RX (pin_B11) +#define MICROPY_HW_UART6_NAME "YA" +#define MICROPY_HW_UART6_TX (pin_G14) +#define MICROPY_HW_UART6_RX (pin_G9) +#define MICROPY_HW_UART2_NAME "UART2" +#define MICROPY_HW_UART2_TX (pin_A2) // Needed to enable AF +#define MICROPY_HW_UART2_RX (pin_A3) // Dummy, not routed on PCB +#define MICROPY_HW_UART2_CK (pin_A4) + +// I2C buses +#define MICROPY_HW_I2C1_SCL (pin_B8) +#define MICROPY_HW_I2C1_SDA (pin_B9) + +// SPI +#define MICROPY_HW_SPI2_NSS (pin_B9) +#define MICROPY_HW_SPI2_SCK (pin_D3) +#define MICROPY_HW_SPI2_MISO (pin_B14) +#define MICROPY_HW_SPI2_MOSI (pin_B15) + +// CAN buses +#define MICROPY_HW_CAN1_TX (pin_B9) +#define MICROPY_HW_CAN1_RX (pin_B8) + +// USRSW is pulled low. Pressing the button makes the input go high. +#define MICROPY_HW_USRSW_PIN (pin_A0) +#define MICROPY_HW_USRSW_PULL (GPIO_NOPULL) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_RISING) +#define MICROPY_HW_USRSW_PRESSED (1) + +// LEDs +#define MICROPY_HW_LED1 (pin_G6) // green +#define MICROPY_HW_LED2 (pin_D4) // orange +#define MICROPY_HW_LED3 (pin_D5) // red +#define MICROPY_HW_LED4 (pin_K3) // blue +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) + +// SD Card SDMMC +#define MICROPY_HW_SDCARD_CK (pin_C12) +#define MICROPY_HW_SDCARD_CMD (pin_D2) +#define MICROPY_HW_SDCARD_D0 (pin_C8) +#define MICROPY_HW_SDCARD_D1 (pin_C9) +#define MICROPY_HW_SDCARD_D2 (pin_C10) +#define MICROPY_HW_SDCARD_D3 (pin_C11) +#define MICROPY_HW_SDCARD_DETECT_PIN (pin_G2) +#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_PULLUP) +#define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_RESET) + +// USB config +#define MICROPY_HW_USB_FS (1) +#define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9) +#define MICROPY_HW_USB_OTG_ID_PIN (pin_A10) diff --git a/ports/stm32/boards/STM32F469DISC/mpconfigboard.mk b/ports/stm32/boards/STM32F469DISC/mpconfigboard.mk new file mode 100644 index 0000000000000..bc95739c7db11 --- /dev/null +++ b/ports/stm32/boards/STM32F469DISC/mpconfigboard.mk @@ -0,0 +1,20 @@ +# MCU settings +MCU_SERIES = f4 +CMSIS_MCU = STM32F469xx +MICROPY_FLOAT_IMPL = double +AF_FILE = boards/stm32f479_af.csv + +ifeq ($(USE_MBOOT),1) +# When using Mboot all the text goes together after the filesystem +LD_FILES = boards/stm32f469xi.ld boards/common_blifs.ld +TEXT0_ADDR = 0x08020000 +else +# When not using Mboot the ISR text goes first, then the rest after the filesystem +LD_FILES = boards/stm32f469xi.ld boards/common_ifs.ld +TEXT0_ADDR = 0x08000000 +TEXT1_ADDR = 0x08020000 +endif + +# MicroPython settings +MICROPY_PY_SSL = 1 +MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/stm32/boards/STM32F469DISC/pins.csv b/ports/stm32/boards/STM32F469DISC/pins.csv new file mode 100644 index 0000000000000..055581ba944b1 --- /dev/null +++ b/ports/stm32/boards/STM32F469DISC/pins.csv @@ -0,0 +1,135 @@ +A0,PB1 +A1,PC2 +A2,PC3 +A3,PC4 +A4,PC5 +A5,PA4 +D0,PG9 +D1,PG14 +D2,PG13 +D3,PA1 +D4,PG12 +D5,PA2 +D6,PA6 +D7,PG11 +D8,PG10 +D9,PA7 +D10,PH6 +D11,PB15 +D12,PB14 +D13,PD3 +D14,PB9 +D15,PB8 +LED1,PG6 +LED2,PD4 +LED3,PD5 +LED4,PK3 +SW,PA0 +TP1,PH2 +TP2,PI8 +TP3,PH15 +AUDIO_INT,PD6 +AUDIO_SDA,PH8 +AUDIO_SCL,PH7 +EXT_SDA,PB9 +EXT_SCL,PB8 +EXT_RST,PG3 +SD_D0,PC8 +SD_D1,PC9 +SD_D2,PC10 +SD_D3,PC11 +SD_CK,PC12 +SD_CMD,PD2 +SD_SW,PC2 +LCD_BL_CTRL,PK3 +LCD_INT,PI13 +LCD_SDA,PH8 +LCD_SCL,PH7 +OTG_FS_POWER,PD5 +OTG_FS_OVER_CURRENT,PD4 +OTG_HS_OVER_CURRENT,PE3 +USB_VBUS,PA9 +USB_ID,PA10 +USB_DM,PA11 +USB_DP,PA12 +USB_HS_CLK,PA5 +USB_HS_STP,PC0 +USB_HS_NXT,PH4 +USB_HS_DIR,PI11 +USB_HS_D0,PA3 +USB_HS_D1,PB0 +USB_HS_D2,PB1 +USB_HS_D3,PB10 +USB_HS_D4,PB11 +USB_HS_D5,PB12 +USB_HS_D6,PB13 +USB_HS_D7,PB5 +UART1_TX,PB10 +UART1_RX,PB11 +UART6_TX,PG14 +UART6_RX,PG9 +CAN2_TX,PB13 +CAN2_RX,PB12 +QSPI_CS,PB6 +QSPI_CLK,PF10 +QSPI_D0,PF8 +QSPI_D1,PF9 +QSPI_D2,PF7 +QSPI_D3,PF6 +FMC_SDCKE0,PH2 +FMC_SDNE0,PH3 +FMC_SDCLK,PG8 +FMC_SDNCAS,PG15 +FMC_SDNRAS,PF11 +FMC_SDNWE,PH5 +FMC_BA0,PG4 +FMC_BA1,PG5 +FMC_NBL0,PE0 +FMC_NBL1,PE1 +FMC_NBL2,PI4 +FMC_NBL3,PI5 +FMC_A0,PF0 +FMC_A1,PF1 +FMC_A2,PF2 +FMC_A3,PF3 +FMC_A4,PF4 +FMC_A5,PF5 +FMC_A6,PF12 +FMC_A7,PF13 +FMC_A8,PF14 +FMC_A9,PF15 +FMC_A10,PG0 +FMC_A11,PG1 +FMC_A12,PG2 +FMC_D0,PD14 +FMC_D1,PD15 +FMC_D2,PD0 +FMC_D3,PD1 +FMC_D4,PE7 +FMC_D5,PE8 +FMC_D6,PE9 +FMC_D7,PE10 +FMC_D8,PE11 +FMC_D9,PE12 +FMC_D10,PE13 +FMC_D11,PE14 +FMC_D12,PE15 +FMC_D13,PD8 +FMC_D14,PD9 +FMC_D15,PD10 +FMC_D16,PH8 +FMC_D17,PH9 +FMC_D18,PH10 +FMC_D19,PH11 +FMC_D20,PH12 +FMC_D21,PH13 +FMC_D22,PH14 +FMC_D23,PH15 +FMC_D24,PI0 +FMC_D25,PI1 +FMC_D26,PI2 +FMC_D27,PI3 +FMC_D28,PI6 +FMC_D29,PI7 +FMC_D30,PI9 +FMC_D31,PI10 diff --git a/ports/stm32/boards/STM32F469DISC/stm32f4xx_hal_conf.h b/ports/stm32/boards/STM32F469DISC/stm32f4xx_hal_conf.h new file mode 100644 index 0000000000000..b5a54cbf7c172 --- /dev/null +++ b/ports/stm32/boards/STM32F469DISC/stm32f4xx_hal_conf.h @@ -0,0 +1,41 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2025 Mike Tolkachev + */ + +#ifndef MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H + +// Oscillator values in Hz +#define HSE_VALUE (8000000) +#define LSE_VALUE (32768) +#define EXTERNAL_CLOCK_VALUE (12288000) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#define HAL_DMA2D_MODULE_ENABLED +#define HAL_LTDC_MODULE_ENABLED +#define HAL_QSPI_MODULE_ENABLED +#define HAL_DSI_MODULE_ENABLED + +#include "boards/stm32f4xx_hal_conf_base.h" + +#ifdef HAL_DMA2D_MODULE_ENABLED +#include "stm32f4xx_hal_dma2d.h" +#endif /* HAL_DMA2D_MODULE_ENABLED */ + +#ifdef HAL_LTDC_MODULE_ENABLED +#include "stm32f4xx_hal_ltdc.h" +#endif /* HAL_LTDC_MODULE_ENABLED */ + +#ifdef HAL_QSPI_MODULE_ENABLED +#include "stm32f4xx_hal_qspi.h" +#endif /* HAL_QSPI_MODULE_ENABLED */ + +#ifdef HAL_DSI_MODULE_ENABLED +#include "stm32f4xx_hal_dsi.h" +#endif /* HAL_DSI_MODULE_ENABLED */ + +#endif // MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H From 22cff2f6f590cdcd70c795ad95bf6678c3e80435 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 27 Nov 2025 18:30:46 +1100 Subject: [PATCH 018/177] stm32/boards/STM32F469DISC: Add board.json file. Signed-off-by: Damien George --- ports/stm32/boards/STM32F469DISC/board.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 ports/stm32/boards/STM32F469DISC/board.json diff --git a/ports/stm32/boards/STM32F469DISC/board.json b/ports/stm32/boards/STM32F469DISC/board.json new file mode 100644 index 0000000000000..9530e1e8128e7 --- /dev/null +++ b/ports/stm32/boards/STM32F469DISC/board.json @@ -0,0 +1,15 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [], + "images": [ + "stm32f469disc.jpg" + ], + "mcu": "stm32f4", + "product": "Discovery F469", + "thumbnail": "", + "url": "https://www.st.com/en/evaluation-tools/32f469idiscovery.html", + "vendor": "ST Microelectronics" +} From 9b73e344ebdb9f086013ff45e9dc73af33d2c0d1 Mon Sep 17 00:00:00 2001 From: Yuuki NAGAO Date: Sat, 26 Jul 2025 08:39:41 +0900 Subject: [PATCH 019/177] stm32/boards/WEACTSTUDIO_MINI_STM32H743: Add WeAct H743VI board support. This change adds WeAct Studio Mini STM32H743 board support to the STM32 port. Some of the work from PR #12540 is combined here. WeAct Studio Mini STM32H43 board: https://github.com/WeActStudio/MiniSTM32H7xx This board uses STM32H743VI: https://www.st.com/en/microcontrollers-microprocessors/stm32h743vi.html Signed-off-by: Yuuki NAGAO --- .../boards/WEACTSTUDIO_MINI_STM32H743/bdev.c | 47 +++++ .../WEACTSTUDIO_MINI_STM32H743/board.json | 13 ++ .../WEACTSTUDIO_MINI_STM32H743/board_init.c | 16 ++ .../WEACTSTUDIO_MINI_STM32H743/deploy.md | 19 ++ .../WEACTSTUDIO_MINI_STM32H743/manifest.py | 4 + .../mpconfigboard.h | 171 ++++++++++++++++++ .../mpconfigboard.mk | 22 +++ .../WEACTSTUDIO_MINI_STM32H743/pins.csv | 100 ++++++++++ .../stm32h7xx_hal_conf.h | 19 ++ .../weact_stm32h743.ld | 33 ++++ 10 files changed, 444 insertions(+) create mode 100644 ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/bdev.c create mode 100644 ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/board.json create mode 100644 ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/board_init.c create mode 100644 ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/deploy.md create mode 100644 ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/manifest.py create mode 100644 ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/mpconfigboard.h create mode 100644 ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/mpconfigboard.mk create mode 100644 ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/pins.csv create mode 100644 ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/stm32h7xx_hal_conf.h create mode 100644 ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/weact_stm32h743.ld diff --git a/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/bdev.c b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/bdev.c new file mode 100644 index 0000000000000..b70142300772c --- /dev/null +++ b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/bdev.c @@ -0,0 +1,47 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2019 Damien P. George + */ + +#include "storage.h" +#include "spi.h" +#include "qspi.h" +#include "py/mpconfig.h" + +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, +}; + +#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; + +// Second external SPI flash uses hardware QSPI interface +const mp_spiflash_config_t spiflash2_config = { + .bus_kind = MP_SPIFLASH_BUS_QSPI, + .bus.u_qspi.data = NULL, + .bus.u_qspi.proto = &qspi_proto, + #if MICROPY_HW_SPIFLASH_ENABLE_CACHE + .cache = &spi_bdev_cache, + #endif +}; + +spi_bdev_t spi_bdev2; diff --git a/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/board.json b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/board.json new file mode 100644 index 0000000000000..11a6e08152f06 --- /dev/null +++ b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/board.json @@ -0,0 +1,13 @@ +{ + "deploy": [ + "deploy.md" + ], + "features": ["External Flash", "DAC", "Display","microSD", "USB", "USB-C"], + "images": [ + "weact_stm32h743.jpg" + ], + "mcu": "stm32h7", + "product": "Mini STM32H743", + "url": "https://github.com/WeActStudio/MiniSTM32H7xx", + "vendor": "WeAct Studio" +} diff --git a/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/board_init.c b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/board_init.c new file mode 100644 index 0000000000000..0adf04982c8a4 --- /dev/null +++ b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/board_init.c @@ -0,0 +1,16 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2019 Damien P. George + */ + +#include "py/mphal.h" +#include "storage.h" + +void WeAct_Core_early_init(void) { + // Turn off the USB switch. + mp_hal_pin_output(pyb_pin_OTG_FS_POWER); + mp_hal_pin_low(pyb_pin_OTG_FS_POWER); + + // Explicitly init SPI2 because it's not enabled as a block device + spi_bdev_ioctl(&spi_bdev2, BDEV_IOCTL_INIT, (uint32_t)&spiflash2_config); +} diff --git a/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/deploy.md b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/deploy.md new file mode 100644 index 0000000000000..a4572bfe30617 --- /dev/null +++ b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/deploy.md @@ -0,0 +1,19 @@ +### WeAct Studio STM32H7xx + +WeAct Studio make a number of STM32H7xx-based boards, they can all be updated +using +[DFU](https://en.wikipedia.org/wiki/USB?useskin=vector#Device_Firmware_Upgrade_mechanism). + +### DFU update + +Hold the Boot button - the middle of the cluster of three buttons - while the +board is reset (either by connecting USB or by pressing reset). Release the Boot +button shortly after the board has reset. The board ought to now be in DFU mode +and detectable as such from a connected computer. + +Use a tool like [`dfu-util`](https://dfu-util.sourceforge.net/) to update the +firmware: + +```bash +dfu-util --alt 0 -D firmware.dfu +``` diff --git a/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/manifest.py b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/manifest.py new file mode 100644 index 0000000000000..7d163e1847964 --- /dev/null +++ b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/manifest.py @@ -0,0 +1,4 @@ +include("$(PORT_DIR)/boards/manifest.py") + +# Currently this file is a placeholder. +# It would be good to extend to add an LCD driver. diff --git a/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/mpconfigboard.h b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/mpconfigboard.h new file mode 100644 index 0000000000000..ad9f3bc45ee08 --- /dev/null +++ b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/mpconfigboard.h @@ -0,0 +1,171 @@ +#define MICROPY_HW_BOARD_NAME "WEACTSTUDIO_MINI_STM32H743" +#define MICROPY_HW_MCU_NAME "STM32H743VIT6" + +#define MICROPY_FATFS_EXFAT (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_ADC (1) +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_USB (1) +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_ENABLE_SERVO (1) +#define MICROPY_HW_ENABLE_TIMER (1) +#define MICROPY_HW_ENABLE_SDCARD (1) +#define MICROPY_HW_ENABLE_MMCARD (0) + +// 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_PART0 (1) + +// Flash storage config +#define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) +// Disable internal filesystem to use spiflash. +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) + +// W25Q64 for storage +#define MICROPY_HW_SPIFLASH_SIZE_BYTES (8 * 1024 * 1024) + +// SPI flash #1, for R/W storage +#define MICROPY_HW_SPIFLASH_CS (pin_D6) +#define MICROPY_HW_SPIFLASH_SCK (pin_B3) +#define MICROPY_HW_SPIFLASH_MOSI (pin_D7) +#define MICROPY_HW_SPIFLASH_MISO (pin_B4) + +// External SPI Flash configuration +#define MICROPY_HW_SPI_IS_RESERVED(id) (id == 1) + +// SPI flash #1, block device config +extern const struct _mp_spiflash_config_t spiflash_config; +extern struct _spi_bdev_t spi_bdev; + +#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) + +// SPI flash #2, to be memory mapped +#define MICROPY_HW_QSPI_PRESCALER (2) // 120 MHz +#define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (26) +#define MICROPY_HW_QSPIFLASH_CS (pin_B6) +#define MICROPY_HW_QSPIFLASH_SCK (pin_B2) +#define MICROPY_HW_QSPIFLASH_IO0 (pin_D11) +#define MICROPY_HW_QSPIFLASH_IO1 (pin_D12) +#define MICROPY_HW_QSPIFLASH_IO2 (pin_E2) +#define MICROPY_HW_QSPIFLASH_IO3 (pin_D13) + +// SPI flash #2, block device config +extern const struct _mp_spiflash_config_t spiflash2_config; +extern struct _spi_bdev_t spi_bdev2; + +#define MICROPY_BOARD_EARLY_INIT WeAct_Core_early_init + +// This board has 25MHz HSE. +// The following gives a 480MHz CPU speed. +#define MICROPY_HW_CLK_USE_HSE (1) +#define MICROPY_HW_CLK_PLLM (5) +#define MICROPY_HW_CLK_PLLN (192) +#define MICROPY_HW_CLK_PLLP (2) +#define MICROPY_HW_CLK_PLLQ (20) +#define MICROPY_HW_CLK_PLLR (2) +#define MICROPY_HW_CLK_PLLVCI (RCC_PLL1VCIRANGE_2) +#define MICROPY_HW_CLK_PLLVCO (RCC_PLL1VCOWIDE) +#define MICROPY_HW_CLK_PLLFRAC (0) + +// The USB clock is set using PLL3 +#define MICROPY_HW_CLK_PLL3M (5) +#define MICROPY_HW_CLK_PLL3N (192) +#define MICROPY_HW_CLK_PLL3P (2) +#define MICROPY_HW_CLK_PLL3Q (20) +#define MICROPY_HW_CLK_PLL3R (2) +#define MICROPY_HW_CLK_PLL3VCI (RCC_PLL3VCIRANGE_2) +#define MICROPY_HW_CLK_PLL3VCO (RCC_PLL3VCOWIDE) +#define MICROPY_HW_CLK_PLL3FRAC (0) + +// 32kHz crystal for RTC +#define MICROPY_HW_RTC_USE_LSE (1) +#define MICROPY_HW_RTC_USE_US (0) + +// 6 wait states +#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_6 + +// UART +#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_UART3_TX (pin_B10) +#define MICROPY_HW_UART3_RX (pin_B11) +#define MICROPY_HW_UART4_TX (pin_C11) +#define MICROPY_HW_UART4_RX (pin_C10) +#define MICROPY_HW_UART5_TX (pin_B12) // or SPI2 +#define MICROPY_HW_UART5_RX (pin_B13) // or SPI2 +#define MICROPY_HW_UART6_TX (pin_C6) +#define MICROPY_HW_UART6_RX (pin_C7) +#define MICROPY_HW_UART7_TX (pin_E8) +#define MICROPY_HW_UART7_RX (pin_E7) + +// I2C buses +#define MICROPY_HW_I2C1_SCL (pin_B8) +#define MICROPY_HW_I2C1_SDA (pin_B9) + +#define MICROPY_HW_I2C2_SCL (pin_B10) +#define MICROPY_HW_I2C2_SDA (pin_B11) + +// SPI buses +// NOTE: SPI1 is used for the SPI flash. +#define MICROPY_HW_SPI1_NSS (pin_D6) +#define MICROPY_HW_SPI1_SCK (pin_B3) +#define MICROPY_HW_SPI1_MISO (pin_B4) +#define MICROPY_HW_SPI1_MOSI (pin_D7) + +#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) +// NOTE: SPI3 is used for the QSPI flash. +#define MICROPY_HW_SPI3_NSS (pin_A4) +#define MICROPY_HW_SPI3_SCK (pin_B3) +#define MICROPY_HW_SPI3_MISO (pin_B4) +#define MICROPY_HW_SPI3_MOSI (pin_B5) +// NOTE: SPI4 is used for the ST7735 LCD. +#define MICROPY_HW_SPI4_NSS (pin_E11) +#define MICROPY_HW_SPI4_SCK (pin_E12) +#define MICROPY_HW_SPI4_MISO (pin_E13) +#define MICROPY_HW_SPI4_MOSI (pin_E14) + +// CAN buses +#define MICROPY_HW_CAN1_TX (pin_B9) +#define MICROPY_HW_CAN1_RX (pin_B8) + +// USRSW is pulled low. Pressing the button makes the input go high. +#define MICROPY_HW_USRSW_PIN (pin_C13) // K1 on the board. +#define MICROPY_HW_USRSW_PULL (GPIO_PULLDOWN) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_RISING) +#define MICROPY_HW_USRSW_PRESSED (1) + +// LEDs +#define MICROPY_HW_LED1 (pin_E3) // the only controllable LED on the board. +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) + +// SD Card SDMMC +#define MICROPY_HW_SDCARD_SDMMC (1) +#define MICROPY_HW_SDCARD_CK (pin_C12) +#define MICROPY_HW_SDCARD_CMD (pin_D2) +#define MICROPY_HW_SDCARD_D0 (pin_C8) +#define MICROPY_HW_SDCARD_D1 (pin_C9) +#define MICROPY_HW_SDCARD_D2 (pin_C10) +#define MICROPY_HW_SDCARD_D3 (pin_C11) + +// SD card detect switch +#define MICROPY_HW_SDCARD_DETECT_PIN (pin_D4) +#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_PULLUP) +#define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_SET) + +// USB config +#define MICROPY_HW_USB_FS (1) + +void WeAct_Core_early_init(void); diff --git a/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/mpconfigboard.mk b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/mpconfigboard.mk new file mode 100644 index 0000000000000..c13c3ddad72cc --- /dev/null +++ b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/mpconfigboard.mk @@ -0,0 +1,22 @@ +USE_MBOOT ?= 0 + +# MCU settings +MCU_SERIES = h7 +CMSIS_MCU = STM32H743xx +MICROPY_FLOAT_IMPL = double +AF_FILE = boards/stm32h743_af.csv + +ifeq ($(USE_MBOOT),1) +# When using Mboot everything goes after the bootloader +LD_FILES = boards/stm32h723.ld boards/common_bl.ld +TEXT0_ADDR = 0x08020000 +else +# When not using Mboot everything goes at the start of flash +LD_FILES = boards/WEACTSTUDIO_MINI_STM32H743/weact_stm32h743.ld boards/common_basic.ld +TEXT0_ADDR = 0x08000000 +endif + +# MicroPython settings +MICROPY_VFS_LFS2 = 1 + +FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/pins.csv b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/pins.csv new file mode 100644 index 0000000000000..61b188cc6b68d --- /dev/null +++ b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/pins.csv @@ -0,0 +1,100 @@ +A0,PA0 +A1,PA1 +A2,PA2 +A3,PA3 +A4,PA4 +A5,PA5 +A6,PA6 +A7,PA7 +A8,PA8 +A9,PA9 +A10,PA10 +A11,PA11 +A12,PA12 +A15,PA15 +B0,PB0 +B1,PB1 +B2,PB2 +B3,PB3 +B4,PB4 +B5,PB5 +B6,PB6 +B7,PB7 +B8,PB8 +B9,PB9 +B10,PB10 +B11,PB11 +B12,PB12 +B13,PB13 +B14,PB14 +B15,PB15 +C0,PC0 +C1,PC1 +C2,PC2 +C3,PC3 +C4,PC4 +C5,PC5 +C6,PC6 +C7,PC7 +C8,PC8 +C9,PC9 +C10,PC10 +C11,PC11 +C12,PC12 +C13,PC13 +D0,PD0 +D1,PD1 +D2,PD2 +D3,PD3 +D4,PD4 +D5,PD5 +D6,PD6 +D7,PD7 +D8,PD8 +D9,PD9 +D10,PD10 +D11,PD11 +D12,PD12 +D13,PD13 +D14,PD14 +D15,PD15 +E0,PE0 +E1,PE1 +E2,PE2 +E3,PE3 +E4,PE4 +E5,PE5 +E6,PE6 +E7,PE7 +E8,PE8 +E9,PE9 +E10,PE10 +E11,PE11 +E11,PE11 +E12,PE12 +E13,PE13 +E14,PE14 +E15,PE15 +LED_BLUE,PE3 +KEY_1,PC13 +QSPI_CS,PB6 +QSPI_CLK,PB2 +QSPI_D0,PD11 +QSPI_D1,PD12 +QSPI_D2,PE2 +QSPI_D3,PD13 +USB_DM,PA11 +USB_DP,PA12 +OSC32_IN,PC14 +OSC32_OUT,PC15 +SDIO_D0,PC8 +SDIO_D1,PC9 +SDIO_D2,PC10 +SDIO_D3,PC11 +SDIO_CMD,PD2 +SDIO_CK,PC12 +SD_SW,PD4 +OTG_FS_POWER,PD10 +OTG_FS_OVER_CURRENT,PG7 +USB_VBUS,PA9 +USB_ID,PA10 diff --git a/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/stm32h7xx_hal_conf.h b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/stm32h7xx_hal_conf.h new file mode 100644 index 0000000000000..c8f60c56055ef --- /dev/null +++ b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/stm32h7xx_hal_conf.h @@ -0,0 +1,19 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2019 Damien P. George + */ +#ifndef MICROPY_INCLUDED_STM32H7XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32H7XX_HAL_CONF_H + +// Oscillator values in Hz +#define HSE_VALUE (25000000) +#define LSE_VALUE (32768) +#define EXTERNAL_CLOCK_VALUE (12288000) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (5000) +#define LSE_STARTUP_TIMEOUT (5000) + +#include "boards/stm32h7xx_hal_conf_base.h" + +#endif // MICROPY_INCLUDED_STM32H7XX_HAL_CONF_H diff --git a/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/weact_stm32h743.ld b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/weact_stm32h743.ld new file mode 100644 index 0000000000000..356d69171a5e0 --- /dev/null +++ b/ports/stm32/boards/WEACTSTUDIO_MINI_STM32H743/weact_stm32h743.ld @@ -0,0 +1,33 @@ +/* + GNU linker script for WeAct Studio STM32H743 +*/ + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* sectors (0-15) */ + FLASH_APP (rx) : ORIGIN = 0x08020000, LENGTH = 1920K /* sectors (1-15) */ + FLASH_ROMFS (rx): ORIGIN = 0x90000000, LENGTH = 8192K /* external QSPI */ + DTCM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K /* Used for storage cache */ + RAM (xrw) : ORIGIN = 0x24000000, LENGTH = 512K /* AXI SRAM */ + RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K +} + +/* produce a link error if there is not this amount of RAM for these sections */ +_minimum_stack_size = 2K; +_minimum_heap_size = 16K; + +/* Define the stack. The stack is full descending so begins just above last byte + of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ +_estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve; +_sstack = _estack - 16K; /* tunable */ + +/* RAM extents for the garbage collector */ +_ram_start = ORIGIN(RAM); +_ram_end = ORIGIN(RAM) + LENGTH(RAM); +_heap_start = _ebss; /* heap starts just after statically allocated memory */ +_heap_end = _sstack; + +/* ROMFS location */ +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); From 14b5080515ec483f0f7be843a089eea835e4519f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 27 Nov 2025 15:00:12 +1100 Subject: [PATCH 020/177] shared/tinyusb: Add optional port-specific hook on USBD init. This allows a port to do hardware initialization just before the TinyUSB stack is brought up. That means the hardware is only turned on when it's needed. Signed-off-by: Damien George --- shared/tinyusb/mp_usbd.h | 5 +++++ shared/tinyusb/mp_usbd_runtime.c | 2 ++ 2 files changed, 7 insertions(+) diff --git a/shared/tinyusb/mp_usbd.h b/shared/tinyusb/mp_usbd.h index 311575b3ad8ba..d73cb5ade828c 100644 --- a/shared/tinyusb/mp_usbd.h +++ b/shared/tinyusb/mp_usbd.h @@ -45,6 +45,10 @@ #define MICROPY_WRAP_TUD_EVENT_HOOK_CB(name) name #endif +#ifndef MICROPY_HW_TINYUSB_LL_INIT +#define MICROPY_HW_TINYUSB_LL_INIT() +#endif + #if MICROPY_HW_ENABLE_USBDEV #include "py/obj.h" @@ -102,6 +106,7 @@ void mp_usbd_task_callback(mp_sched_node_t *node); static inline void mp_usbd_init(void) { // Without runtime USB support, this can be a thin wrapper wrapper around tusb_init() // which is called in the below helper function. + MICROPY_HW_TINYUSB_LL_INIT(); mp_usbd_init_tud(); } diff --git a/shared/tinyusb/mp_usbd_runtime.c b/shared/tinyusb/mp_usbd_runtime.c index 72e011732d400..39344729da1cb 100644 --- a/shared/tinyusb/mp_usbd_runtime.c +++ b/shared/tinyusb/mp_usbd_runtime.c @@ -430,6 +430,8 @@ void mp_usbd_init(void) { } if (need_usb) { + // Call any port-specific initialization code. + MICROPY_HW_TINYUSB_LL_INIT(); // The following will call tusb_init(), which is safe to call redundantly. mp_usbd_init_tud(); // Reconnect if mp_usbd_deinit() has disconnected. From f43810b1cd4142474a99df4e08d51a97eb9af00a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 27 Nov 2025 23:24:24 +1100 Subject: [PATCH 021/177] stm32/usbd_conf: Clean up USBD hardware initialization functions. Break the FS and HS initialization routines out into separate functions, and call them as necessary from the TinyUSB or STM USB helper functions. Signed-off-by: Damien George --- ports/stm32/usbd_conf.c | 55 +++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/ports/stm32/usbd_conf.c b/ports/stm32/usbd_conf.c index 76142789d413a..be56b2202ce5a 100644 --- a/ports/stm32/usbd_conf.c +++ b/ports/stm32/usbd_conf.c @@ -62,16 +62,8 @@ PCD_HandleTypeDef pcd_hs_handle; #define OTG_HS_IRQn USB1_OTG_HS_IRQn #endif -#if MICROPY_HW_TINYUSB_STACK -void pyb_usbd_init(void) -#else -void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) -#endif -{ - #if MICROPY_HW_USB_FS - #if MICROPY_HW_STM_USB_STACK - if (hpcd->Instance == USB_OTG_FS) - #endif +#if MICROPY_HW_USB_FS +static void mp_usbd_ll_init_fs(void) { { // Configure USB GPIO's. @@ -192,17 +184,12 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) #endif #endif #endif - - #if MICROPY_HW_STM_USB_STACK - return; - #endif } - #endif +} +#endif // MICROPY_HW_USB_FS - #if MICROPY_HW_USB_HS - #if MICROPY_HW_STM_USB_STACK - if (hpcd->Instance == USB_OTG_HS) - #endif +#if MICROPY_HW_USB_HS +static void mp_usbd_ll_init_hs(void) { { #if MICROPY_HW_USB_HS_IN_FS @@ -342,10 +329,36 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) NVIC_SetPriority(OTG_HS_IRQn, IRQ_PRI_OTG_HS); HAL_NVIC_EnableIRQ(OTG_HS_IRQn); } - #endif // MICROPY_HW_USB_HS } +#endif // MICROPY_HW_USB_HS + +#if MICROPY_HW_TINYUSB_STACK + +void pyb_usbd_init(void) { + #if MICROPY_HW_USB_FS + mp_usbd_ll_init_fs(); + #endif + + #if MICROPY_HW_USB_HS + mp_usbd_ll_init_hs(); + #endif +} + +#elif MICROPY_HW_STM_USB_STACK -#if MICROPY_HW_STM_USB_STACK +void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { + #if MICROPY_HW_USB_FS + if (hpcd->Instance == USB_OTG_FS) { + mp_usbd_ll_init_fs(); + } + #endif + + #if MICROPY_HW_USB_HS + if (hpcd->Instance == USB_OTG_HS) { + mp_usbd_ll_init_hs(); + } + #endif +} /** * @brief DeInitializes the PCD MSP. From 63c94fe73ed5c52d4d7ccbf4ddcc2a1e3178d21b Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 27 Nov 2025 15:00:32 +1100 Subject: [PATCH 022/177] stm32: Fix init sequence of USB hardware and TinyUSB stack. This commit fixes the initialization sequence for TinyUSB when enabled on the stm32 port: - Following other ports, `mp_usbd_init()` should be called just after running `boot.py`, to give the user a chance to configure USB. - Hardware initialization (via `pyb_usbd_init()`) should only occur once, the first time TinyUSB is started up. This is achieved by adding a hook to the shared TinyUSB bindings to call `pyb_usbd_init()`, and only do the hardware init if TinyUSB was not already initialized. Also, `pyb_usbd_init()` is renamed `mp_usbd_ll_init()` to make it match with the rest of the stared TinyUSB binding code. Signed-off-by: Damien George --- ports/stm32/main.c | 11 +++++------ ports/stm32/mpconfigboard_common.h | 1 + ports/stm32/mphalport.h | 1 + ports/stm32/usbd_conf.c | 8 +++++++- ports/stm32/usbd_conf.h | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 6f7413694b0b4..8085a5e25763e 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -611,14 +611,9 @@ void stm32_main(uint32_t reset_mode) { pyb_can_init0(); #endif - #if MICROPY_HW_ENABLE_USB - #if MICROPY_HW_TINYUSB_STACK - pyb_usbd_init(); - mp_usbd_init(); - #else + #if MICROPY_HW_STM_USB_STACK && MICROPY_HW_ENABLE_USB pyb_usb_init0(); #endif - #endif #if MICROPY_PY_MACHINE_I2S machine_i2s_init0(); @@ -690,6 +685,10 @@ void stm32_main(uint32_t reset_mode) { } #endif + #if MICROPY_HW_TINYUSB_STACK && MICROPY_HW_ENABLE_USBDEV + mp_usbd_init(); + #endif + #if MICROPY_HW_HAS_MMA7660 // MMA accel: init and reset accel_init(); diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 1ce42055d80f9..e21f474d7afb6 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -261,6 +261,7 @@ #if MICROPY_HW_TINYUSB_STACK #ifndef MICROPY_HW_ENABLE_USBDEV #define MICROPY_HW_ENABLE_USBDEV (1) +#define MICROPY_HW_TINYUSB_LL_INIT mp_usbd_ll_init #endif #ifndef MICROPY_HW_USB_CDC diff --git a/ports/stm32/mphalport.h b/ports/stm32/mphalport.h index 098a848fb843b..50aa456971181 100644 --- a/ports/stm32/mphalport.h +++ b/ports/stm32/mphalport.h @@ -1,6 +1,7 @@ // We use the ST Cube HAL library for most hardware peripherals #include STM32_HAL_H #include "pin.h" +#include "usbd_conf.h" #include "py/ringbuf.h" #include "shared/runtime/interrupt_char.h" diff --git a/ports/stm32/usbd_conf.c b/ports/stm32/usbd_conf.c index be56b2202ce5a..e5ac9311d13dc 100644 --- a/ports/stm32/usbd_conf.c +++ b/ports/stm32/usbd_conf.c @@ -32,6 +32,7 @@ #include "usbd_core.h" #include "py/obj.h" #include "py/mphal.h" +#include "shared/tinyusb/mp_usbd.h" #include "irq.h" #include "usb.h" @@ -334,7 +335,12 @@ static void mp_usbd_ll_init_hs(void) { #if MICROPY_HW_TINYUSB_STACK -void pyb_usbd_init(void) { +void mp_usbd_ll_init(void) { + // Only initialize the USB hardware once. + if (tusb_inited()) { + return; + } + #if MICROPY_HW_USB_FS mp_usbd_ll_init_fs(); #endif diff --git a/ports/stm32/usbd_conf.h b/ports/stm32/usbd_conf.h index cb0457982d5eb..5829d68701088 100644 --- a/ports/stm32/usbd_conf.h +++ b/ports/stm32/usbd_conf.h @@ -65,7 +65,7 @@ #define USBD_HS_NUM_FIFO (1 + USBD_HS_NUM_TX_FIFO) #if MICROPY_HW_TINYUSB_STACK -void pyb_usbd_init(void); +void mp_usbd_ll_init(void); #endif #endif // MICROPY_INCLUDED_STM32_USBD_CONF_H From 41acdd8083effcd513ee245075280b78af1ad938 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 26 Nov 2025 13:02:16 +1100 Subject: [PATCH 023/177] stm32/rtc: Make sure RTC is using LSE on N6 MCUs. Signed-off-by: Damien George --- ports/stm32/rtc.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c index 72abcd75f9936..ae33f834cbf71 100644 --- a/ports/stm32/rtc.c +++ b/ports/stm32/rtc.c @@ -134,19 +134,27 @@ void rtc_init_start(bool force_init) { if (!force_init) { bool rtc_running = false; #if defined(STM32N6) + // Note: the low-level boot on the N6 seems to always enable the RTC and the LSI, and + // switch the RTC to LSI mode. So the logic below needs to account for that: + // - if LSE is ready then switch back to the LSE + // - even if LSI is ready, don't use it if the board is configured to use LSE + uint32_t rtc_clock_source = LL_RCC_GetRTCClockSource(); if (LL_RCC_IsEnabledRTC() - && LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSE && LL_RCC_LSE_IsReady()) { // LSE is enabled & ready --> no need to (re-)init RTC rtc_running = true; + if (rtc_clock_source != LL_RCC_RTC_CLKSOURCE_LSE) { + LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE); + } // remove Backup Domain write protection HAL_PWR_EnableBkUpAccess(); // Clear source Reset Flag __HAL_RCC_CLEAR_RESET_FLAGS(); // provide some status information rtc_info |= 0x40000; - } else if (LL_RCC_IsEnabledRTC() - && LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSI) { + } else if (!rtc_use_lse + && LL_RCC_IsEnabledRTC() + && rtc_clock_source == LL_RCC_RTC_CLKSOURCE_LSI) { // LSI configured as the RTC clock source --> no need to (re-)init RTC rtc_running = true; // remove Backup Domain write protection From 9c2b0cd2e3c9bfc96fe23f4a395f8f8115768fea Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 30 Nov 2025 08:25:17 +1100 Subject: [PATCH 024/177] stm32/usbd_conf: Fix build for boards with USB disabled. Some boards (eg NUCLEO_G0B1RE and NUCLEO_G474RE) have USB disabled but still configure MICROPY_HW_USB_FS/HS for the cases where USB does get enabled. Such a configuration should not build any of the code in `usbd_conf.c`, nor the USB interrupt handlers. Signed-off-by: Damien George --- ports/stm32/stm32_it.c | 4 ++++ ports/stm32/usbd_conf.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index 19778020d9aff..17b95326460e4 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -301,6 +301,8 @@ void DebugMon_Handler(void) { /* file (startup_stm32f4xx.s). */ /******************************************************************************/ +#if MICROPY_HW_STM_USB_STACK || MICROPY_HW_TINYUSB_STACK + #if defined(STM32G0) #if MICROPY_HW_USB_FS @@ -499,6 +501,8 @@ void OTG_HS_WKUP_IRQHandler(void) { #endif // !defined(STM32L0) +#endif // MICROPY_HW_STM_USB_STACK || MICROPY_HW_TINYUSB_STACK + /** * @brief This function handles PPP interrupt request. * @param None diff --git a/ports/stm32/usbd_conf.c b/ports/stm32/usbd_conf.c index e5ac9311d13dc..46d7985253d38 100644 --- a/ports/stm32/usbd_conf.c +++ b/ports/stm32/usbd_conf.c @@ -36,7 +36,7 @@ #include "irq.h" #include "usb.h" -#if MICROPY_HW_USB_FS || MICROPY_HW_USB_HS +#if MICROPY_HW_STM_USB_STACK || MICROPY_HW_TINYUSB_STACK #if BUILDING_MBOOT // TinyUSB not used in mboot From f63e64f4bc7097b4d41494d51198f41011e0daf0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 30 Nov 2025 08:34:20 +1100 Subject: [PATCH 025/177] tools/ci.sh: Install latest ARM toolchain for stm32 CI. This is needed to build Cortex-M55 (STM32N6) based boards. Signed-off-by: Damien George --- .github/workflows/ports_stm32.yml | 2 +- tools/ci.sh | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ports_stm32.yml b/.github/workflows/ports_stm32.yml index 45aa0c6130483..2ed730eb4e8d0 100644 --- a/.github/workflows/ports_stm32.yml +++ b/.github/workflows/ports_stm32.yml @@ -30,7 +30,7 @@ jobs: steps: - uses: actions/checkout@v6 - name: Install packages - run: tools/ci.sh stm32_setup + run: tools/ci.sh stm32_setup && tools/ci.sh stm32_path >> $GITHUB_PATH - name: Build ci_${{matrix.ci_func }} run: tools/ci.sh ${{ matrix.ci_func }} diff --git a/tools/ci.sh b/tools/ci.sh index e34940f758947..eb658400b4d16 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -494,12 +494,19 @@ function ci_samd_build { # ports/stm32 function ci_stm32_setup { - ci_gcc_arm_setup + # Use a recent version of the ARM toolchain, to work with Cortex-M55. + wget https://developer.arm.com/-/media/Files/downloads/gnu/14.3.rel1/binrel/arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi.tar.xz + xzcat arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi.tar.xz | tar x + pip3 install pyelftools pip3 install ar pip3 install pyhy } +function ci_stm32_path { + echo $(pwd)/arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi/bin +} + function ci_stm32_pyb_build { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/stm32 MICROPY_PY_NETWORK_WIZNET5K=5200 submodules From 2fbd72ad78a490bab19797e768707f0190fbd4a9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 13 Oct 2025 12:18:44 +1100 Subject: [PATCH 026/177] tools/ci.sh: Build all stm32 MCU families in stm32 CI functions. Currently the CI for stm32 only tests building about half of the available MCU families. This commit adds the remaining families to the stm32 CI jobs. Signed-off-by: Damien George --- tools/ci.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/ci.sh b/tools/ci.sh index eb658400b4d16..098efbf9a23f9 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -508,6 +508,8 @@ function ci_stm32_path { } function ci_stm32_pyb_build { + # This function builds the following MCU families: F4, F7. + make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/stm32 MICROPY_PY_NETWORK_WIZNET5K=5200 submodules make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF2 submodules @@ -522,6 +524,8 @@ function ci_stm32_pyb_build { } function ci_stm32_nucleo_build { + # This function builds the following MCU families: F0, H5, H7, L0, L4, WB. + make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_H743ZI submodules git submodule update --init lib/mynewt-nimble @@ -548,9 +552,17 @@ function ci_stm32_nucleo_build { } function ci_stm32_misc_build { + # This function builds the following MCU families: G0, G4, H7, L1, N6, U5, WL. + make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/stm32 BOARD=ARDUINO_GIGA submodules make ${MAKEOPTS} -C ports/stm32 BOARD=ARDUINO_GIGA + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_G0B1RE + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_G474RE + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_L152RE + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_N657X0 + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_U5A5ZJ_Q + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_WL55 } ######################################################################################## From 83131c106d2a7d78055fb83f9a17c9c9c348ea73 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Tue, 18 Nov 2025 12:06:55 +0100 Subject: [PATCH 027/177] pyproject.toml: Update ruff configuration to target Python 3.8. As per 4c9ce826cbfbd99cff10cc933064f87a13bee4ac the tests now target Python 3.8 syntax and features, so update the ruff configuration to match. Changes in this commit: - Update to Python 3.8 syntax. - Ignore import not at top of module warnings. - Exclude common SDK folders. - Exclude cpydiff test with intentional error. Also see: https://github.com/micropython/micropython-lib/pull/1059 Signed-off-by: Jos Verlinde --- pyproject.toml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f3693eae1b420..1cf57166e1437 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,11 +22,17 @@ ACKNOWLEDGEMENTS,\ [tool.ruff] # Exclude third-party code from linting and formatting -extend-exclude = ["lib"] +extend-exclude = [ + "lib", + "esp-idf", + "pico-sdk", + "emsdk", + "tests/cpydiff/syntax_assign_expr.py" # intentionally incorrect CPython code +] # Include Python source files that don't end with .py extend-include = ["tools/cc1"] line-length = 99 -target-version = "py37" +target-version = "py38" [tool.ruff.lint] exclude = [ # Ruff finds Python SyntaxError in these files @@ -44,7 +50,7 @@ exclude = [ # Ruff finds Python SyntaxError in these files "tests/micropython/viper_args.py", ] extend-select = ["C9", "PLC"] -ignore = [ +extend-ignore = [ "E401", "E402", "E722", @@ -54,6 +60,7 @@ ignore = [ "F403", "F405", "PLC0206", + "PLC0415", # conditional imports are common in MicroPython ] mccabe.max-complexity = 40 @@ -64,6 +71,8 @@ mccabe.max-complexity = 40 # manifest.py files are evaluated with some global names pre-defined "**/manifest.py" = ["F821"] "ports/**/boards/**/manifest_*.py" = ["F821"] +# Uses assignment expressions. +"tests/cpydiff/syntax_assign_expr.py" = ["F821"] [tool.ruff.format] # Exclude third-party code, and exclude the following tests: From 521b2f86bede90f5f30b6c9ce6a75e987a5f021d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 21 Nov 2025 15:32:25 +1100 Subject: [PATCH 028/177] shared/tinyusb: Remove USBD_RHPORT constant. TinyUSB defines TUD_OPT_RHPORT which is the same thing, make shorter definition RHPORT in the two files which use it. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- extmod/machine_usb_device.c | 12 +++++++----- shared/tinyusb/mp_usbd_runtime.c | 10 ++++++---- shared/tinyusb/tusb_config.h | 6 ++---- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/extmod/machine_usb_device.c b/extmod/machine_usb_device.c index 5019cd98779f5..3d4cde942cb9f 100644 --- a/extmod/machine_usb_device.c +++ b/extmod/machine_usb_device.c @@ -42,6 +42,8 @@ #define HAS_BUILTIN_DRIVERS (MICROPY_HW_USB_CDC || MICROPY_HW_USB_MSC) +#define RHPORT TUD_OPT_RHPORT + const mp_obj_type_t machine_usb_device_type; static mp_obj_t usb_device_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { @@ -111,11 +113,11 @@ static mp_obj_t usb_device_submit_xfer(mp_obj_t self, mp_obj_t ep, mp_obj_t buff mp_raise_ValueError(MP_ERROR_TEXT("ep")); } - if (!usbd_edpt_claim(USBD_RHPORT, ep_addr)) { + if (!usbd_edpt_claim(RHPORT, ep_addr)) { mp_raise_OSError(MP_EBUSY); } - result = usbd_edpt_xfer(USBD_RHPORT, ep_addr, buf_info.buf, buf_info.len); + result = usbd_edpt_xfer(RHPORT, ep_addr, buf_info.buf, buf_info.len); if (result) { // Store the buffer object until the transfer completes @@ -168,14 +170,14 @@ static mp_obj_t usb_device_stall(size_t n_args, const mp_obj_t *args) { usb_device_check_active(self); - mp_obj_t res = mp_obj_new_bool(usbd_edpt_stalled(USBD_RHPORT, epnum)); + mp_obj_t res = mp_obj_new_bool(usbd_edpt_stalled(RHPORT, epnum)); if (n_args == 3) { // Set stall state mp_obj_t stall = args[2]; if (mp_obj_is_true(stall)) { - usbd_edpt_stall(USBD_RHPORT, epnum); + usbd_edpt_stall(RHPORT, epnum); } else { - usbd_edpt_clear_stall(USBD_RHPORT, epnum); + usbd_edpt_clear_stall(RHPORT, epnum); } } diff --git a/shared/tinyusb/mp_usbd_runtime.c b/shared/tinyusb/mp_usbd_runtime.c index 39344729da1cb..ef6bd87eddac1 100644 --- a/shared/tinyusb/mp_usbd_runtime.c +++ b/shared/tinyusb/mp_usbd_runtime.c @@ -44,6 +44,8 @@ #include "device/usbd_pvt.h" #endif +#define RHPORT TUD_OPT_RHPORT + static bool in_usbd_task; // Flags if mp_usbd_task() is currently running // Some top-level functions that manage global TinyUSB USBD state, not the @@ -233,7 +235,7 @@ static uint16_t _runtime_dev_claim_itfs(tusb_desc_interface_t const *itf_desc, u } else if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) { // Open any endpoints that we come across if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) { - bool r = usbd_edpt_open(USBD_RHPORT, (const void *)p_desc); + bool r = usbd_edpt_open(RHPORT, (const void *)p_desc); if (!r) { mp_obj_t exc = mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(MP_ENODEV)); usbd_pend_exception(exc); @@ -322,7 +324,7 @@ static bool runtime_dev_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_cont // Check if callback returned any data to submit if (mp_get_buffer(cb_res, &buf_info, dir == TUSB_DIR_IN ? MP_BUFFER_READ : MP_BUFFER_RW)) { - result = tud_control_xfer(USBD_RHPORT, + result = tud_control_xfer(RHPORT, request, buf_info.buf, buf_info.len); @@ -468,7 +470,7 @@ static void mp_usbd_disconnect(mp_obj_usb_device_t *usbd) { for (int epnum = 0; epnum < CFG_TUD_ENDPPOINT_MAX; epnum++) { for (int dir = 0; dir < 2; dir++) { if (usbd->xfer_data[epnum][dir] != mp_const_none) { - usbd_edpt_stall(USBD_RHPORT, tu_edpt_addr(epnum, dir)); + usbd_edpt_stall(RHPORT, tu_edpt_addr(epnum, dir)); usbd->xfer_data[epnum][dir] = mp_const_none; } } @@ -479,7 +481,7 @@ static void mp_usbd_disconnect(mp_obj_usb_device_t *usbd) { // Ensure no pending static CDC writes, as these can cause TinyUSB to crash tud_cdc_write_clear(); // Prevent cdc write flush from initiating any new transfers while disconnecting - usbd_edpt_stall(USBD_RHPORT, USBD_CDC_EP_IN); + usbd_edpt_stall(RHPORT, USBD_CDC_EP_IN); #endif bool was_connected = tud_connected(); diff --git a/shared/tinyusb/tusb_config.h b/shared/tinyusb/tusb_config.h index 0cc5ef03985ef..40cb00ca485ed 100644 --- a/shared/tinyusb/tusb_config.h +++ b/shared/tinyusb/tusb_config.h @@ -83,7 +83,7 @@ #ifndef CFG_TUD_CDC_TX_BUFSIZE #define CFG_TUD_CDC_TX_BUFSIZE ((CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED) ? 512 : 256) #endif -#endif +#endif // CFG_TUD_CDC // MSC Configuration #if CFG_TUD_MSC @@ -92,9 +92,7 @@ #endif // Set MSC EP buffer size to FatFS block size to avoid partial read/writes (offset arg). #define CFG_TUD_MSC_BUFSIZE (MICROPY_FATFS_MAX_SS) -#endif - -#define USBD_RHPORT (0) // Currently only one port is supported +#endif // CFG_TUD_MSC // Define built-in interface, string and endpoint numbering based on the above config From 604cda5d545564523c4deaedb2e93a464e3dcf8f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 21 Nov 2025 16:28:11 +1100 Subject: [PATCH 029/177] esp32: Support building with network and/or bluetooth disabled. (and a smaller binary size as a result) Signed-off-by: Angus Gratton --- ports/esp32/main.c | 6 +++++- ports/esp32/modsocket.c | 5 +++++ ports/esp32/mpconfigport.h | 16 +++++++++++----- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 41eea29b08ec3..7460bf3593896 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -90,7 +90,8 @@ int vprintf_null(const char *format, va_list ap) { return 0; } -time_t platform_mbedtls_time(time_t *timer) { +#if MICROPY_SSL_MBEDTLS +static time_t platform_mbedtls_time(time_t *timer) { // mbedtls_time requires time in seconds from EPOCH 1970 struct timeval tv; @@ -98,6 +99,7 @@ time_t platform_mbedtls_time(time_t *timer) { return tv.tv_sec + TIMEUTILS_SECONDS_1970_TO_2000; } +#endif void mp_task(void *pvParameter) { volatile uint32_t sp = (uint32_t)esp_cpu_get_sp(); @@ -114,8 +116,10 @@ void mp_task(void *pvParameter) { #endif machine_init(); + #if MICROPY_SSL_MBEDTLS // Configure time function, for mbedtls certificate time validation. mbedtls_platform_set_time(platform_mbedtls_time); + #endif esp_err_t err = esp_event_loop_create_default(); if (err != ESP_OK) { diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 2050d1d04d4d6..d8ebd3a895401 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -55,6 +55,9 @@ #include "lwip/igmp.h" #include "esp_log.h" +// See note at bottom of file about why this isn't MICROPY_PY_SOCKET +#if MICROPY_PY_NETWORK + #define SOCKET_POLL_US (100000) #define MDNS_QUERY_TIMEOUT_MS (5000) #define MDNS_LOCAL_SUFFIX ".local" @@ -1028,3 +1031,5 @@ const mp_obj_module_t mp_module_socket = { // this will not conflict with the common implementation provided by // extmod/mod{lwip,socket}.c. MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_socket, mp_module_socket); + +#endif // MICROPY_PY_NETWORK diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 7b973ebb969d2..c0bd4516e5842 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -92,6 +92,9 @@ #endif #ifndef MICROPY_PY_BLUETOOTH #define MICROPY_PY_BLUETOOTH (1) +#endif + +#if MICROPY_PY_BLUETOOTH #define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS (1) #define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK (1) // Event stack size is the RTOS stack size minus an allowance for the stack used @@ -102,7 +105,8 @@ #define MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING (1) #define MICROPY_BLUETOOTH_NIMBLE (1) #define MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY (1) -#endif +#endif // MICROPY_PY_BLUETOOTH + #define MICROPY_PY_RANDOM_SEED_INIT_FUNC (esp_random()) #define MICROPY_PY_OS_INCLUDEFILE "ports/esp32/modos.c" #define MICROPY_PY_OS_DUPTERM (1) @@ -158,7 +162,9 @@ #define MICROPY_PY_MACHINE_UART_IRQ (1) #define MICROPY_PY_MACHINE_WDT (1) #define MICROPY_PY_MACHINE_WDT_INCLUDEFILE "ports/esp32/machine_wdt.c" +#ifndef MICROPY_PY_NETWORK #define MICROPY_PY_NETWORK (1) +#endif #ifndef MICROPY_PY_NETWORK_HOSTNAME_DEFAULT #if CONFIG_IDF_TARGET_ESP32 #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-esp32" @@ -189,10 +195,10 @@ #ifndef MICROPY_HW_ESP_NEW_I2C_DRIVER #define MICROPY_HW_ESP_NEW_I2C_DRIVER (0) #endif -#define MICROPY_PY_SSL (1) -#define MICROPY_SSL_MBEDTLS (1) -#define MICROPY_PY_WEBSOCKET (1) -#define MICROPY_PY_WEBREPL (1) +#define MICROPY_PY_SSL (MICROPY_PY_NETWORK) +#define MICROPY_SSL_MBEDTLS (MICROPY_PY_SSL) +#define MICROPY_PY_WEBSOCKET (MICROPY_PY_NETWORK) +#define MICROPY_PY_WEBREPL (MICROPY_PY_NETWORK) #define MICROPY_PY_ONEWIRE (1) #define MICROPY_PY_SOCKET_EVENTS (MICROPY_PY_WEBREPL) #define MICROPY_PY_BLUETOOTH_RANDOM_ADDR (1) From e6f1f7871380950657947a64f6ba0ae724258dc0 Mon Sep 17 00:00:00 2001 From: Vincent1-python Date: Fri, 15 Aug 2025 17:26:05 +0800 Subject: [PATCH 030/177] esp32: Add support for ESP32-P4. This commit adds support for ESP32-P4 SoCs. Signed-off-by: Vincent1-python Signed-off-by: Angus Gratton Signed-off-by: Damien George --- ports/esp32/README.md | 2 +- ports/esp32/boards/make-pins.py | 4 +- ports/esp32/boards/sdkconfig.p4 | 15 +++ ports/esp32/boards/sdkconfig.p4_wifi_c5 | 2 + ports/esp32/boards/sdkconfig.p4_wifi_c6 | 2 + ports/esp32/boards/sdkconfig.p4_wifi_common | 59 ++++++++++++ ports/esp32/esp32_common.cmake | 3 +- ports/esp32/lockfiles/dependencies.lock.esp32 | 2 +- .../esp32/lockfiles/dependencies.lock.esp32c2 | 2 +- .../esp32/lockfiles/dependencies.lock.esp32c3 | 2 +- .../esp32/lockfiles/dependencies.lock.esp32c5 | 2 +- .../esp32/lockfiles/dependencies.lock.esp32c6 | 2 +- .../esp32/lockfiles/dependencies.lock.esp32p4 | 93 +++++++++++++++++++ .../esp32/lockfiles/dependencies.lock.esp32s2 | 2 +- .../esp32/lockfiles/dependencies.lock.esp32s3 | 2 +- ports/esp32/machine_adc.c | 15 +++ ports/esp32/machine_hw_spi.c | 4 + ports/esp32/machine_pin.c | 2 + ports/esp32/machine_pin.h | 60 ++++++++++++ ports/esp32/machine_timer.c | 3 + ports/esp32/machine_touchpad.c | 67 ++++++++++++- ports/esp32/machine_uart.c | 8 ++ ports/esp32/main.c | 3 +- ports/esp32/main/idf_component.yml | 11 ++- ports/esp32/modesp32.c | 4 +- ports/esp32/modmachine.c | 9 ++ ports/esp32/mpconfigport.h | 21 ++++- ports/esp32/network_lan.c | 10 +- ports/esp32/uart.c | 4 + ports/esp32/usb.c | 7 +- shared/tinyusb/tusb_config.h | 2 +- 31 files changed, 393 insertions(+), 31 deletions(-) create mode 100644 ports/esp32/boards/sdkconfig.p4 create mode 100644 ports/esp32/boards/sdkconfig.p4_wifi_c5 create mode 100644 ports/esp32/boards/sdkconfig.p4_wifi_c6 create mode 100644 ports/esp32/boards/sdkconfig.p4_wifi_common create mode 100644 ports/esp32/lockfiles/dependencies.lock.esp32p4 diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 55964febeaff4..b5cd1c2a8c6bb 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -6,7 +6,7 @@ microcontrollers. It uses the ESP-IDF framework and MicroPython runs as a task under FreeRTOS. Currently supports ESP32, ESP32-C2 (aka ESP8684), ESP32-C3, ESP32-C5, ESP32-C6, -ESP32-S2 and ESP32-S3. ESP8266 is supported by a separate MicroPython port. +ESP32-P4, ESP32-S2 and ESP32-S3. ESP8266 is supported by a separate MicroPython port. Supported features include: - REPL (Python prompt) over UART0 and/or the integrated USB peripheral. diff --git a/ports/esp32/boards/make-pins.py b/ports/esp32/boards/make-pins.py index 49b10f0ce15c9..c7375e67d66bb 100755 --- a/ports/esp32/boards/make-pins.py +++ b/ports/esp32/boards/make-pins.py @@ -7,8 +7,8 @@ import boardgen -# Pins start at zero, and the highest pin index on any ESP32* chip is 48. -NUM_GPIOS = 49 +# Pins start at zero, and the highest pin index on any ESP32* chip is 54. +NUM_GPIOS = 55 class Esp32Pin(boardgen.Pin): diff --git a/ports/esp32/boards/sdkconfig.p4 b/ports/esp32/boards/sdkconfig.p4 new file mode 100644 index 0000000000000..3ec0ff70579e4 --- /dev/null +++ b/ports/esp32/boards/sdkconfig.p4 @@ -0,0 +1,15 @@ +# Flash +CONFIG_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y + +# Memory +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MEMTEST= +CONFIG_SPIRAM_IGNORE_NOTFOUND=y +CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC=y +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=50768 + +# ULP: not fixed +CONFIG_SOC_ULP_SUPPORTED=n +CONFIG_ULP_COPROC_ENABLED=n +CONFIG_ULP_COPROC_TYPE_FSM=n diff --git a/ports/esp32/boards/sdkconfig.p4_wifi_c5 b/ports/esp32/boards/sdkconfig.p4_wifi_c5 new file mode 100644 index 0000000000000..bee529f9b13e3 --- /dev/null +++ b/ports/esp32/boards/sdkconfig.p4_wifi_c5 @@ -0,0 +1,2 @@ +# Most settings are in sdkconfig.p4_wifi_common +CONFIG_SLAVE_IDF_TARGET_ESP32C5=y diff --git a/ports/esp32/boards/sdkconfig.p4_wifi_c6 b/ports/esp32/boards/sdkconfig.p4_wifi_c6 new file mode 100644 index 0000000000000..4a80725c53dc9 --- /dev/null +++ b/ports/esp32/boards/sdkconfig.p4_wifi_c6 @@ -0,0 +1,2 @@ +# Most settings are in sdkconfig.p4_wifi_common +CONFIG_SLAVE_IDF_TARGET_ESP32C6=y diff --git a/ports/esp32/boards/sdkconfig.p4_wifi_common b/ports/esp32/boards/sdkconfig.p4_wifi_common new file mode 100644 index 0000000000000..b7bd0bffa0524 --- /dev/null +++ b/ports/esp32/boards/sdkconfig.p4_wifi_common @@ -0,0 +1,59 @@ +# This sdkconfig file has the common settings for an ESP32-P4 +# host with an external ESP-Hosted Wi-Fi/BT interface. + +# Wifi +CONFIG_ESP_HOSTED_ENABLED=y +CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=16 +CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=64 +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=64 +CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y +CONFIG_ESP_WIFI_TX_BA_WIN=32 +CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP_WIFI_RX_BA_WIN=32 + +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65534 +CONFIG_LWIP_TCP_WND_DEFAULT=65534 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=64 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=64 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64 + +CONFIG_LWIP_TCP_SACK_OUT=y + +# Bluetooth Support +CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID=y +CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y +CONFIG_ESP_HOSTED_NIMBLE_HCI_VHCI=y +CONFIG_ESP_WIFI_REMOTE_ENABLED=y +CONFIG_SLAVE_SOC_WIFI_SUPPORTED=y +CONFIG_SLAVE_SOC_WIFI_WAPI_SUPPORT=y +CONFIG_SLAVE_SOC_WIFI_CSI_SUPPORT=y +CONFIG_SLAVE_SOC_WIFI_MESH_SUPPORT=y +CONFIG_SLAVE_SOC_WIFI_LIGHT_SLEEP_CLK_WIDTH=12 +CONFIG_SLAVE_SOC_WIFI_HW_TSF=y +CONFIG_SLAVE_SOC_WIFI_FTM_SUPPORT=y +CONFIG_SLAVE_FREERTOS_UNICORE=y +CONFIG_SLAVE_SOC_WIFI_GCMP_SUPPORT=y +CONFIG_SLAVE_IDF_TARGET_ARCH_RISCV=y +CONFIG_SLAVE_SOC_WIFI_HE_SUPPORT=y +CONFIG_SLAVE_SOC_WIFI_MAC_VERSION_NUM=2 +CONFIG_ESP_WIFI_REMOTE_LIBRARY_HOSTED=y + +CONFIG_ESP_HOSTED_P4_DEV_BOARD_FUNC_BOARD=y + +# BLE +CONFIG_ESP_ENABLE_BT=y +CONFIG_BT_ENABLED=y +CONFIG_BT_NIMBLE_ENABLED=y +CONFIG_BT_CONTROLLER_DISABLED=y +CONFIG_BT_BLUEDROID_ENABLED=n +CONFIG_BT_NIMBLE_TRANSPORT_UART=n +CONFIG_BT_NIMBLE_LOG_LEVEL_ERROR=y + +CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="MPY ESP32" +CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4 + +CONFIG_BT_HCI_LOG_DEBUG_EN=y + +# Increase NimBLE task stack size from the default, because Python code +# (BLE IRQ handlers) will most likely run on this task. +CONFIG_BT_NIMBLE_TASK_STACK_SIZE=6144 diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 807d712830af2..a52498a7fec53 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -23,7 +23,7 @@ if(CONFIG_IDF_TARGET_ARCH_RISCV) endif() if(NOT DEFINED MICROPY_PY_TINYUSB) - if(CONFIG_IDF_TARGET_ESP32S2 OR CONFIG_IDF_TARGET_ESP32S3) + if(CONFIG_IDF_TARGET_ESP32S2 OR CONFIG_IDF_TARGET_ESP32S3 OR CONFIG_IDF_TARGET_ESP32P4) set(MICROPY_PY_TINYUSB ON) endif() endif() @@ -167,6 +167,7 @@ list(APPEND IDF_COMPONENTS esp_adc esp_app_format esp_common + esp_driver_touch_sens esp_eth esp_event esp_hw_support diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32 b/ports/esp32/lockfiles/dependencies.lock.esp32 index 4b0b2d9729d85..3f3a416671648 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32 @@ -30,6 +30,6 @@ direct_dependencies: - espressif/lan867x - espressif/mdns - idf -manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 +manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 target: esp32 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c2 b/ports/esp32/lockfiles/dependencies.lock.esp32c2 index 5c5cea0c9a189..9530e74e9728c 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32c2 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c2 @@ -16,6 +16,6 @@ dependencies: direct_dependencies: - espressif/mdns - idf -manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 +manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 target: esp32c2 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c3 b/ports/esp32/lockfiles/dependencies.lock.esp32c3 index 4c4c869c27ea3..5edbe537d8ba1 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32c3 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c3 @@ -16,6 +16,6 @@ dependencies: direct_dependencies: - espressif/mdns - idf -manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 +manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 target: esp32c3 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c5 b/ports/esp32/lockfiles/dependencies.lock.esp32c5 index 6f24d013470eb..3b52c82b1436b 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32c5 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c5 @@ -16,6 +16,6 @@ dependencies: direct_dependencies: - espressif/mdns - idf -manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 +manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 target: esp32c5 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c6 b/ports/esp32/lockfiles/dependencies.lock.esp32c6 index b7435e1076718..1c84d5bfd5fd9 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32c6 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c6 @@ -16,6 +16,6 @@ dependencies: direct_dependencies: - espressif/mdns - idf -manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 +manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 target: esp32c6 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32p4 b/ports/esp32/lockfiles/dependencies.lock.esp32p4 new file mode 100644 index 0000000000000..8923760482fd0 --- /dev/null +++ b/ports/esp32/lockfiles/dependencies.lock.esp32p4 @@ -0,0 +1,93 @@ +dependencies: + espressif/eppp_link: + component_hash: 41f6519edda527ec6a0553c872ebaf8fc6d3812523c9d4c8d1660ad21c720abe + dependencies: + - name: espressif/esp_serial_slave_link + registry_url: https://components.espressif.com + require: private + version: ^1.1.0 + - name: idf + require: private + version: '>=5.2' + source: + registry_url: https://components.espressif.com + type: service + version: 1.1.3 + espressif/esp_hosted: + component_hash: f32400eec7f35652052ae79ecb301148d4011769e94eb8d47262fb22fce933d2 + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.2.4 + espressif/esp_serial_slave_link: + component_hash: ac1776806de0a6e371c84e87898bb983e19ce62aa7f1e2e5c4a3b0234a575d2c + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.1.2 + espressif/esp_wifi_remote: + component_hash: 4ed1ebe454d63ddb4a91bbd8db74a6f883c85a863db1f79770c57fbd2e5c134c + dependencies: + - name: espressif/eppp_link + registry_url: https://components.espressif.com + require: private + version: '>=0.1' + - name: espressif/esp_hosted + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32h2, esp32p4] + version: '>=0.0.6' + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.15.2 + espressif/mdns: + component_hash: 46ee81d32fbf850462d8af1e83303389602f6a6a9eddd2a55104cb4c063858ed + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.0 + espressif/tinyusb: + component_hash: ee1c962cff61eb975d508258d509974d58031cc27ff0d6c4117a67a613a49594 + dependencies: + - name: idf + version: '>=5.0' + source: + git: https://github.com/micropython/tinyusb-espressif.git + path: . + type: git + targets: + - esp32s2 + - esp32s3 + - esp32p4 + - esp32h4 + version: e4c0ec3caab3d9c25374de7047653b9ced8f14ff + idf: + source: + type: idf + version: 5.5.1 +direct_dependencies: +- espressif/esp_hosted +- espressif/esp_wifi_remote +- espressif/mdns +- espressif/tinyusb +- idf +manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 +target: esp32p4 +version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32s2 b/ports/esp32/lockfiles/dependencies.lock.esp32s2 index a13d9fd401d47..a7bb041539d99 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32s2 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32s2 @@ -32,6 +32,6 @@ direct_dependencies: - espressif/mdns - espressif/tinyusb - idf -manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 +manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 target: esp32s2 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32s3 b/ports/esp32/lockfiles/dependencies.lock.esp32s3 index d5e7045b77a69..9a8b179f72b1c 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32s3 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32s3 @@ -32,6 +32,6 @@ direct_dependencies: - espressif/mdns - espressif/tinyusb - idf -manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 +manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 target: esp32s3 version: 2.0.0 diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index 432df3d3a20e6..ff80762ebd7d8 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -136,6 +136,21 @@ static const machine_adc_obj_t madc_obj[] = { {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_7, GPIO_NUM_18}, {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_8, GPIO_NUM_19}, {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_9, GPIO_NUM_20}, + #elif CONFIG_IDF_TARGET_ESP32P4 + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_16}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_17}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_2, GPIO_NUM_18}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_3, GPIO_NUM_19}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_4, GPIO_NUM_20}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_5, GPIO_NUM_21}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_6, GPIO_NUM_22}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_7, GPIO_NUM_23}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_0, GPIO_NUM_49}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_1, GPIO_NUM_50}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_2, GPIO_NUM_51}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_3, GPIO_NUM_52}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_4, GPIO_NUM_53}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_5, GPIO_NUM_54}, #endif }; diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c index dcf8b3942a70b..aea6bd00fe890 100644 --- a/ports/esp32/machine_hw_spi.c +++ b/ports/esp32/machine_hw_spi.c @@ -73,6 +73,10 @@ #define MICROPY_HW_SPI2_SCK (36) #define MICROPY_HW_SPI2_MOSI (35) #define MICROPY_HW_SPI2_MISO (37) +#elif CONFIG_IDF_TARGET_ESP32P4 +#define MICROPY_HW_SPI2_SCK (43) +#define MICROPY_HW_SPI2_MOSI (44) +#define MICROPY_HW_SPI2_MISO (39) #endif #endif diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index efe6733194cbf..74ee15a24acce 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -55,6 +55,8 @@ #define GPIO_FIRST_NON_OUTPUT (34) #elif CONFIG_IDF_TARGET_ESP32S2 #define GPIO_FIRST_NON_OUTPUT (46) +#elif CONFIG_IDF_TARGET_ESP32P4 +#define GPIO_FIRST_NON_OUTPUT (54) #endif // Return the gpio_num_t index for a given machine_pin_obj_t pointer. diff --git a/ports/esp32/machine_pin.h b/ports/esp32/machine_pin.h index 9e247a7367f0c..4e21b032e66cb 100644 --- a/ports/esp32/machine_pin.h +++ b/ports/esp32/machine_pin.h @@ -216,6 +216,66 @@ #define MICROPY_HW_ENABLE_GPIO47 (1) #define MICROPY_HW_ENABLE_GPIO48 (1) #endif +#elif CONFIG_IDF_TARGET_ESP32P4 +#define MICROPY_HW_ENABLE_GPIO0 (1) +#define MICROPY_HW_ENABLE_GPIO1 (1) +#define MICROPY_HW_ENABLE_GPIO2 (1) +#define MICROPY_HW_ENABLE_GPIO3 (1) +#define MICROPY_HW_ENABLE_GPIO4 (1) +#define MICROPY_HW_ENABLE_GPIO5 (1) +#define MICROPY_HW_ENABLE_GPIO6 (1) +#define MICROPY_HW_ENABLE_GPIO7 (1) +#define MICROPY_HW_ENABLE_GPIO8 (1) +#define MICROPY_HW_ENABLE_GPIO9 (1) +#define MICROPY_HW_ENABLE_GPIO10 (1) +#define MICROPY_HW_ENABLE_GPIO11 (1) +#define MICROPY_HW_ENABLE_GPIO12 (1) +#define MICROPY_HW_ENABLE_GPIO13 (1) +#define MICROPY_HW_ENABLE_GPIO14 (1) +#define MICROPY_HW_ENABLE_GPIO15 (1) +#define MICROPY_HW_ENABLE_GPIO16 (1) +#define MICROPY_HW_ENABLE_GPIO17 (1) +#define MICROPY_HW_ENABLE_GPIO18 (1) +#define MICROPY_HW_ENABLE_GPIO19 (1) +#define MICROPY_HW_ENABLE_GPIO20 (1) +#define MICROPY_HW_ENABLE_GPIO21 (1) +#define MICROPY_HW_ENABLE_GPIO22 (1) +#define MICROPY_HW_ENABLE_GPIO23 (1) +#if !MICROPY_HW_ESP_USB_SERIAL_JTAG +// Note: ESP32-P4 can switch USJ to 26/27 instead, but +// this isn't supported +#define MICROPY_HW_ENABLE_GPIO24 (1) +#define MICROPY_HW_ENABLE_GPIO25 (1) +#endif +#define MICROPY_HW_ENABLE_GPIO26 (1) +#define MICROPY_HW_ENABLE_GPIO27 (1) +#define MICROPY_HW_ENABLE_GPIO28 (1) +#define MICROPY_HW_ENABLE_GPIO29 (1) +#define MICROPY_HW_ENABLE_GPIO30 (1) +#define MICROPY_HW_ENABLE_GPIO31 (1) +#define MICROPY_HW_ENABLE_GPIO32 (1) +#define MICROPY_HW_ENABLE_GPIO33 (1) +#define MICROPY_HW_ENABLE_GPIO34 (1) +#define MICROPY_HW_ENABLE_GPIO35 (1) +#define MICROPY_HW_ENABLE_GPIO36 (1) +#define MICROPY_HW_ENABLE_GPIO37 (1) +#define MICROPY_HW_ENABLE_GPIO38 (1) +#define MICROPY_HW_ENABLE_GPIO39 (1) +#define MICROPY_HW_ENABLE_GPIO40 (1) +#define MICROPY_HW_ENABLE_GPIO41 (1) +#define MICROPY_HW_ENABLE_GPIO42 (1) +#define MICROPY_HW_ENABLE_GPIO43 (1) +#define MICROPY_HW_ENABLE_GPIO44 (1) +#define MICROPY_HW_ENABLE_GPIO45 (1) +#define MICROPY_HW_ENABLE_GPIO46 (1) +#define MICROPY_HW_ENABLE_GPIO47 (1) +#define MICROPY_HW_ENABLE_GPIO48 (1) +#define MICROPY_HW_ENABLE_GPIO49 (1) +#define MICROPY_HW_ENABLE_GPIO50 (1) +#define MICROPY_HW_ENABLE_GPIO51 (1) +#define MICROPY_HW_ENABLE_GPIO52 (1) +#define MICROPY_HW_ENABLE_GPIO53 (1) +#define MICROPY_HW_ENABLE_GPIO54 (1) #endif diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index ea9ce5469b0b7..b9cd80f48efed 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -47,6 +47,9 @@ #define TIMER_FLAGS 0 +#if CONFIG_IDF_TARGET_ESP32P4 +static uint8_t __DECLARE_RCC_ATOMIC_ENV __attribute__ ((unused)); +#endif 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); diff --git a/ports/esp32/machine_touchpad.c b/ports/esp32/machine_touchpad.c index 299c489f5a831..88b34d64ff070 100644 --- a/ports/esp32/machine_touchpad.c +++ b/ports/esp32/machine_touchpad.c @@ -41,8 +41,11 @@ #if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 only #include "driver/touch_pad.h" -#elif SOC_TOUCH_SENSOR_VERSION == 2 // All other SoCs with touch, to date +#elif SOC_TOUCH_SENSOR_VERSION == 2 // most ESP32 #include "driver/touch_sensor.h" +#elif SOC_TOUCH_SENSOR_VERSION == 3 // At present, it can only be used on ESP32P4. +#include "driver/touch_sens.h" +#include "soc/touch_sensor_channel.h" #else #error "Unknown touch hardware version" #endif @@ -50,9 +53,18 @@ typedef struct _mtp_obj_t { mp_obj_base_t base; gpio_num_t gpio_id; + #if SOC_TOUCH_SENSOR_VERSION == 1 || SOC_TOUCH_SENSOR_VERSION == 2 touch_pad_t touchpad_id; + #elif SOC_TOUCH_SENSOR_VERSION == 3 + int touchpad_id; + #endif } mtp_obj_t; +#if SOC_TOUCH_SENSOR_VERSION == 3 +static touch_sensor_handle_t touch_sens_handle; +static touch_channel_handle_t touch_chan_handle[15]; +#endif + static const mtp_obj_t touchpad_obj[] = { #if CONFIG_IDF_TARGET_ESP32 {{&machine_touchpad_type}, GPIO_NUM_4, TOUCH_PAD_NUM0}, @@ -80,6 +92,21 @@ 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}, + #elif CONFIG_IDF_TARGET_ESP32P4 + {{&machine_touchpad_type}, GPIO_NUM_2, TOUCH_PAD_GPIO2_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_3, TOUCH_PAD_GPIO3_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_4, TOUCH_PAD_GPIO4_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_5, TOUCH_PAD_GPIO5_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_6, TOUCH_PAD_GPIO6_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_7, TOUCH_PAD_GPIO7_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_8, TOUCH_PAD_GPIO8_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_9, TOUCH_PAD_GPIO9_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_10, TOUCH_PAD_GPIO10_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_11, TOUCH_PAD_GPIO11_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_12, TOUCH_PAD_GPIO12_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_13, TOUCH_PAD_GPIO13_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_14, TOUCH_PAD_GPIO14_CHANNEL}, + {{&machine_touchpad_type}, GPIO_NUM_15, TOUCH_PAD_GPIO15_CHANNEL}, #else #error "Please add GPIO mapping for this SoC" #endif @@ -102,14 +129,45 @@ static mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ static int initialized = 0; if (!initialized) { + #if SOC_TOUCH_SENSOR_VERSION == 1 || SOC_TOUCH_SENSOR_VERSION == 2 touch_pad_init(); touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); + #elif SOC_TOUCH_SENSOR_VERSION == 3 + touch_sensor_sample_config_t sample_cfg[1] = { + TOUCH_SENSOR_V3_DEFAULT_SAMPLE_CONFIG(1, 1, 1), + }; + touch_sensor_config_t sens_cfg = TOUCH_SENSOR_DEFAULT_BASIC_CONFIG(1, sample_cfg); + check_esp_err(touch_sensor_new_controller(&sens_cfg, &touch_sens_handle)); + touch_sensor_filter_config_t filter_cfg = TOUCH_SENSOR_DEFAULT_FILTER_CONFIG(); + check_esp_err(touch_sensor_config_filter(touch_sens_handle, &filter_cfg)); + #endif initialized = 1; } + #if SOC_TOUCH_SENSOR_VERSION == 3 + else { + // Stop the touch controller so a new channel can be added. + check_esp_err(touch_sensor_stop_continuous_scanning(touch_sens_handle)); + check_esp_err(touch_sensor_disable(touch_sens_handle)); + } + #endif #if SOC_TOUCH_SENSOR_VERSION == 1 esp_err_t err = touch_pad_config(self->touchpad_id, 0); #elif SOC_TOUCH_SENSOR_VERSION == 2 esp_err_t err = touch_pad_config(self->touchpad_id); + #elif SOC_TOUCH_SENSOR_VERSION == 3 + touch_channel_config_t chan_cfg = { + .active_thresh = {1000}, + }; + esp_err_t err = ESP_OK; + if (touch_chan_handle[self->touchpad_id] != NULL) { + err = touch_sensor_del_channel(touch_chan_handle[self->touchpad_id]); + touch_chan_handle[self->touchpad_id] = NULL; + } + if (err == ESP_OK) { + err = touch_sensor_new_channel(touch_sens_handle, self->touchpad_id, &chan_cfg, &touch_chan_handle[self->touchpad_id]); + } + check_esp_err(touch_sensor_enable(touch_sens_handle)); + check_esp_err(touch_sensor_start_continuous_scanning(touch_sens_handle)); #endif if (err == ESP_OK) { #if SOC_TOUCH_SENSOR_VERSION == 2 @@ -121,6 +179,7 @@ static mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ mp_raise_ValueError(MP_ERROR_TEXT("Touch pad error")); } +#if SOC_TOUCH_SENSOR_VERSION == 1 || SOC_TOUCH_SENSOR_VERSION == 2 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_SENSOR_VERSION == 1 @@ -135,6 +194,7 @@ static mp_obj_t mtp_config(mp_obj_t self_in, mp_obj_t value_in) { mp_raise_ValueError(MP_ERROR_TEXT("Touch pad error")); } MP_DEFINE_CONST_FUN_OBJ_2(mtp_config_obj, mtp_config); +#endif static mp_obj_t mtp_read(mp_obj_t self_in) { mtp_obj_t *self = self_in; @@ -144,6 +204,9 @@ static mp_obj_t mtp_read(mp_obj_t self_in) { #elif SOC_TOUCH_SENSOR_VERSION == 2 uint32_t value; esp_err_t err = touch_pad_read_raw_data(self->touchpad_id, &value); + #elif SOC_TOUCH_SENSOR_VERSION == 3 + uint32_t value; + esp_err_t err = touch_channel_read_data(touch_chan_handle[self->touchpad_id], TOUCH_CHAN_DATA_TYPE_SMOOTH, &value); #endif if (err == ESP_OK) { return MP_OBJ_NEW_SMALL_INT(value); @@ -154,7 +217,9 @@ MP_DEFINE_CONST_FUN_OBJ_1(mtp_read_obj, mtp_read); static const mp_rom_map_elem_t mtp_locals_dict_table[] = { // instance methods + #if SOC_TOUCH_SENSOR_VERSION == 1 || SOC_TOUCH_SENSOR_VERSION == 2 { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&mtp_config_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mtp_read_obj) }, }; diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index ea403f4221835..a2034b749271f 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -293,6 +293,14 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, self->tx = 5; break; #endif + #if SOC_UART_HP_NUM > 3 + case UART_NUM_3: + break; + #endif + #if SOC_UART_HP_NUM > 4 + case UART_NUM_4: + break; + #endif case UART_NUM_MAX: assert(0); // Range is checked in mp_machine_uart_make_new, value should be unreachable } diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 7460bf3593896..12835f313e7bb 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -108,7 +108,8 @@ void mp_task(void *pvParameter) { #endif #if MICROPY_HW_ESP_USB_SERIAL_JTAG usb_serial_jtag_init(); - #elif MICROPY_HW_ENABLE_USBDEV + #endif + #if MICROPY_HW_ENABLE_USBDEV usb_phy_init(); #endif #if MICROPY_HW_ENABLE_UART_REPL diff --git a/ports/esp32/main/idf_component.yml b/ports/esp32/main/idf_component.yml index 77904865baae1..cb75296fe51ab 100644 --- a/ports/esp32/main/idf_component.yml +++ b/ports/esp32/main/idf_component.yml @@ -1,13 +1,20 @@ -## IDF Component Manager Manifest File dependencies: espressif/mdns: "~1.1.0" espressif/tinyusb: rules: - - if: "target in [esp32s2, esp32s3]" + - if: "target in [esp32s2, esp32s3, esp32p4]" # Temporary workaround for https://github.com/hathach/tinyusb/issues/3154 # Can be removed once fix is released in espressif/tinyusb git: https://github.com/micropython/tinyusb-espressif.git version: cherrypick/dwc2_zlp_fix + espressif/esp_hosted: + rules: + - if: "target == esp32p4" + version: "2.2.4" + espressif/esp_wifi_remote: + rules: + - if: "target == esp32p4" + version: "0.15.2" espressif/lan867x: version: "~1.0.0" rules: diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 1d002fc84bb03..a2ad9267cb268 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -196,7 +196,7 @@ static mp_obj_t esp32_wake_on_gpio(size_t n_args, const mp_obj_t *pos_args, mp_m } static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_gpio_obj, 0, esp32_wake_on_gpio); -#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP +#if SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP static mp_obj_t esp32_gpio_deep_sleep_hold(const mp_obj_t enable) { if (mp_obj_is_true(enable)) { gpio_deep_sleep_hold_en(); @@ -331,7 +331,7 @@ static const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_wake_on_ulp), MP_ROM_PTR(&esp32_wake_on_ulp_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_wake_on_gpio), MP_ROM_PTR(&esp32_wake_on_gpio_obj) }, - #if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP + #if SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP { MP_ROM_QSTR(MP_QSTR_gpio_deep_sleep_hold), MP_ROM_PTR(&esp32_gpio_deep_sleep_hold_obj) }, #endif #if CONFIG_IDF_TARGET_ESP32 diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 62f0bf73cf945..ec972bfc68ee7 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -260,8 +260,12 @@ static mp_int_t mp_machine_reset_cause(void) { } #if MICROPY_ESP32_USE_BOOTLOADER_RTC +#if !CONFIG_IDF_TARGET_ESP32P4 #include "soc/rtc_cntl_reg.h" #include "usb.h" +#else +#include "soc/lp_system_reg.h" +#endif #if CONFIG_IDF_TARGET_ESP32S3 #include "esp32s3/rom/usb/usb_dc.h" #include "esp32s3/rom/usb/usb_persist.h" @@ -274,8 +278,13 @@ MP_NORETURN static void machine_bootloader_rtc(void) { usb_dc_prepare_persist(); chip_usb_set_persist_flags(USBDC_BOOT_DFU); #endif + #if !CONFIG_IDF_TARGET_ESP32P4 REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); esp_restart(); + #else + REG_WRITE(LP_SYSTEM_REG_SYS_CTRL_REG, LP_SYSTEM_REG_FORCE_DOWNLOAD_BOOT); + esp_restart(); + #endif } #endif diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index c0bd4516e5842..55503ff0baed3 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -180,6 +180,8 @@ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-esp32c5" #elif CONFIG_IDF_TARGET_ESP32C6 #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-esp32c6" +#elif CONFIG_IDF_TARGET_ESP32P4 +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-esp32p4" #endif #endif #define MICROPY_PY_NETWORK_INCLUDEFILE "ports/esp32/modnetwork.h" @@ -257,6 +259,17 @@ #define MICROPY_HW_USB_PRODUCT_FS_STRING "Espressif Device" #endif +#if CONFIG_IDF_TARGET_ESP32P4 +// By default, ESP32-P4 uses the HS USB PHY (RHPORT1) for TinyUSB +// and configures the full speed USB port as USB Serial/JTAG device +#ifndef MICROPY_HW_USB_HS +#define MICROPY_HW_USB_HS 1 +#endif // MICROPY_HW_USB_HS +#if MICROPY_HW_USB_HS && !defined(CFG_TUSB_RHPORT0_MODE) && !defined(CFG_TUSB_RHPORT1_MODE) +#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) +#endif +#endif + #endif // MICROPY_HW_ENABLE_USBDEV // Enable stdio over native USB peripheral CDC via TinyUSB @@ -265,14 +278,14 @@ #endif // Enable stdio over USB Serial/JTAG peripheral +// (SOC_USB_OTG_PERIPH_NUM is only 2 on the ESP32-P4, which supports both native USB & Serial/JTAG simultaneously) #ifndef MICROPY_HW_ESP_USB_SERIAL_JTAG -#define MICROPY_HW_ESP_USB_SERIAL_JTAG (SOC_USB_SERIAL_JTAG_SUPPORTED && !MICROPY_HW_USB_CDC) +#define MICROPY_HW_ESP_USB_SERIAL_JTAG (SOC_USB_SERIAL_JTAG_SUPPORTED && (!MICROPY_HW_USB_CDC || SOC_USB_OTG_PERIPH_NUM > 1)) #endif -#if MICROPY_HW_USB_CDC && MICROPY_HW_ESP_USB_SERIAL_JTAG +#if MICROPY_HW_USB_CDC && MICROPY_HW_ESP_USB_SERIAL_JTAG && (SOC_USB_OTG_PERIPH_NUM <= 1) #error "Invalid build config: Can't enable both native USB and USB Serial/JTAG peripheral" #endif - // type definitions for the specific machine #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p))) @@ -351,7 +364,7 @@ typedef long mp_off_t; #ifndef MICROPY_BOARD_ENTER_BOOTLOADER // RTC has a register to trigger bootloader on these targets -#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32P4 #define MICROPY_ESP32_USE_BOOTLOADER_RTC (1) #define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) machine_bootloader_rtc() #endif diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c index 309ee0b14a23c..37f72c26349fe 100644 --- a/ports/esp32/network_lan.c +++ b/ports/esp32/network_lan.c @@ -182,13 +182,13 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar } eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); - #if CONFIG_IDF_TARGET_ESP32 + #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32P4 eth_esp32_emac_config_t esp32_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); #endif esp_eth_mac_t *mac = NULL; - #if CONFIG_IDF_TARGET_ESP32 + #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32P4 // Dynamic ref_clk configuration. if (args[ARG_ref_clk_mode].u_int != -1) { // Map the GPIO_MODE constants to EMAC_CLK constants. @@ -223,7 +223,7 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar #endif switch (args[ARG_phy_type].u_int) { - #if CONFIG_IDF_TARGET_ESP32 + #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32P4 case PHY_LAN8710: case PHY_LAN8720: self->phy = esp_eth_phy_new_lan87xx(&phy_config); @@ -251,7 +251,7 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar self->phy = esp_eth_phy_new_generic(&phy_config); break; #endif - #endif // CONFIG_IDF_TARGET_ESP32 + #endif // CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_ETH_USE_SPI_ETHERNET #if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL case PHY_KSZ8851SNL: { @@ -286,7 +286,7 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar #endif } - #if CONFIG_IDF_TARGET_ESP32 + #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32P4 if (!IS_SPI_PHY(args[ARG_phy_type].u_int)) { if (self->mdc_pin == -1 || self->mdio_pin == -1) { mp_raise_ValueError(MP_ERROR_TEXT("mdc and mdio must be specified")); diff --git a/ports/esp32/uart.c b/ports/esp32/uart.c index 491314e04d106..6e00fa3aa2609 100644 --- a/ports/esp32/uart.c +++ b/ports/esp32/uart.c @@ -40,6 +40,10 @@ static void uart_irq_handler(void *arg); +#if CONFIG_IDF_TARGET_ESP32P4 +static uint8_t __DECLARE_RCC_ATOMIC_ENV __attribute__ ((unused)); +#endif + // Declaring the HAL structure on the stack saves a tiny amount of static RAM #define REPL_HAL_DEFN() { .dev = UART_LL_GET_HW(MICROPY_HW_UART_REPL) } diff --git a/ports/esp32/usb.c b/ports/esp32/usb.c index b90f53aa49daa..bca2305f19c7b 100644 --- a/ports/esp32/usb.c +++ b/ports/esp32/usb.c @@ -42,18 +42,17 @@ void usb_phy_init(void) { // ref: https://github.com/espressif/esp-usb/blob/4b6a798d0bed444fff48147c8dcdbbd038e92892/device/esp_tinyusb/tinyusb.c // Configure USB PHY - usb_phy_config_t phy_conf = { + static const usb_phy_config_t phy_conf = { .controller = USB_PHY_CTRL_OTG, .otg_mode = USB_OTG_MODE_DEVICE, + .target = USB_PHY_TARGET_INT, }; - // Internal USB PHY - phy_conf.target = USB_PHY_TARGET_INT; // Init ESP USB Phy usb_new_phy(&phy_conf, &phy_hdl); } -#if CONFIG_IDF_TARGET_ESP32S3 +#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4 void usb_usj_mode(void) { // Switch the USB PHY back to Serial/Jtag mode, disabling OTG support // This should be run before jumping to bootloader. diff --git a/shared/tinyusb/tusb_config.h b/shared/tinyusb/tusb_config.h index 40cb00ca485ed..8abd021548d5d 100644 --- a/shared/tinyusb/tusb_config.h +++ b/shared/tinyusb/tusb_config.h @@ -59,7 +59,7 @@ #define MICROPY_HW_USB_MSC_INQUIRY_REVISION_STRING "1.00" #endif -#ifndef CFG_TUSB_RHPORT0_MODE +#if !defined(CFG_TUSB_RHPORT0_MODE) && !defined(CFG_TUSB_RHPORT1_MODE) #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE) #endif From 50a06b64042feb5387b7d520152f5f09b1537a68 Mon Sep 17 00:00:00 2001 From: Vincent1-python Date: Fri, 15 Aug 2025 17:26:05 +0800 Subject: [PATCH 031/177] esp32/boards/ESP32_GENERIC_P4: Add board definition for ESP32P4. Includes a base variant with LAN, and C5_WIFI and C6_WIFI variants with LAN, WiFi and BLE. And builds this board in the esp32 CI, to cover the P4 support. Signed-off-by: Vincent1-python Signed-off-by: Angus Gratton Signed-off-by: Damien George --- .github/workflows/ports_esp32.yml | 1 + .../esp32/boards/ESP32_GENERIC_P4/board.json | 21 +++++++++++++ ports/esp32/boards/ESP32_GENERIC_P4/board.md | 2 ++ .../ESP32_GENERIC_P4/mpconfigboard.cmake | 6 ++++ .../boards/ESP32_GENERIC_P4/mpconfigboard.h | 31 +++++++++++++++++++ .../mpconfigvariant_C5_WIFI.cmake | 14 +++++++++ .../mpconfigvariant_C6_WIFI.cmake | 14 +++++++++ tools/ci.sh | 7 +++++ 8 files changed, 96 insertions(+) create mode 100644 ports/esp32/boards/ESP32_GENERIC_P4/board.json create mode 100644 ports/esp32/boards/ESP32_GENERIC_P4/board.md create mode 100644 ports/esp32/boards/ESP32_GENERIC_P4/mpconfigboard.cmake create mode 100644 ports/esp32/boards/ESP32_GENERIC_P4/mpconfigboard.h create mode 100644 ports/esp32/boards/ESP32_GENERIC_P4/mpconfigvariant_C5_WIFI.cmake create mode 100644 ports/esp32/boards/ESP32_GENERIC_P4/mpconfigvariant_C6_WIFI.cmake diff --git a/.github/workflows/ports_esp32.yml b/.github/workflows/ports_esp32.yml index eea82126d4f29..6b3700ed5eee5 100644 --- a/.github/workflows/ports_esp32.yml +++ b/.github/workflows/ports_esp32.yml @@ -30,6 +30,7 @@ jobs: - esp32_build_cmod_spiram_s2 - esp32_build_s3_c3 - esp32_build_c2_c5_c6 + - esp32_build_p4 runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 diff --git a/ports/esp32/boards/ESP32_GENERIC_P4/board.json b/ports/esp32/boards/ESP32_GENERIC_P4/board.json new file mode 100644 index 0000000000000..00761d511f701 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_P4/board.json @@ -0,0 +1,21 @@ +{ + "deploy": [ + "../deploy.md" + ], + "deploy_options": { + "flash_offset": "0x2000" + }, + "docs": "", + "features": [ + "BLE", + "WiFi" + ], + "images": [ + "esp32p4_devkitmini.jpg" + ], + "mcu": "esp32p4", + "product": "ESP32-P4", + "thumbnail": "", + "url": "https://www.espressif.com/en/products/modules", + "vendor": "Espressif" +} diff --git a/ports/esp32/boards/ESP32_GENERIC_P4/board.md b/ports/esp32/boards/ESP32_GENERIC_P4/board.md new file mode 100644 index 0000000000000..22a450cb89829 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_P4/board.md @@ -0,0 +1,2 @@ +The following firmware is applicable to most development boards based on ESP32-P4, and +the development boards must be equipped with at least 16 MiB external SPI Flash. diff --git a/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigboard.cmake b/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigboard.cmake new file mode 100644 index 0000000000000..876a186decc76 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigboard.cmake @@ -0,0 +1,6 @@ +set(IDF_TARGET esp32p4) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.p4 +) diff --git a/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigboard.h b/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigboard.h new file mode 100644 index 0000000000000..b7a887848028f --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigboard.h @@ -0,0 +1,31 @@ +// Both of these can be set by mpconfigboard.cmake if a BOARD_VARIANT is +// specified. + +#ifndef MICROPY_HW_BOARD_NAME +#define MICROPY_HW_BOARD_NAME "Generic ESP32P4 module" +#endif + +#ifndef MICROPY_HW_MCU_NAME +#define MICROPY_HW_MCU_NAME "ESP32P4" +#endif + +#define MICROPY_PY_ESPNOW (0) + +#define MICROPY_HW_ENABLE_SDCARD (1) + +#ifndef USB_SERIAL_JTAG_PACKET_SZ_BYTES +#define USB_SERIAL_JTAG_PACKET_SZ_BYTES (64) +#endif + +// Enable UART REPL for modules that have an external USB-UART and don't use native USB. +#define MICROPY_HW_ENABLE_UART_REPL (1) + +#define MICROPY_PY_MACHINE_I2S (1) + +// Disable Wi-Fi and Bluetooth by default, these are re-enabled in the WIFI variants +#ifndef MICROPY_PY_NETWORK_WLAN +#define MICROPY_PY_NETWORK_WLAN (0) +#endif +#ifndef MICROPY_PY_BLUETOOTH +#define MICROPY_PY_BLUETOOTH (0) +#endif diff --git a/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigvariant_C5_WIFI.cmake b/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigvariant_C5_WIFI.cmake new file mode 100644 index 0000000000000..507ed2ccc56d1 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigvariant_C5_WIFI.cmake @@ -0,0 +1,14 @@ +set(IDF_TARGET esp32p4) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.p4 + boards/sdkconfig.p4_wifi_common + boards/sdkconfig.p4_wifi_c5 +) + +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_BOARD_NAME="Generic ESP32P4 module with WIFI module of external ESP32C5" + MICROPY_PY_NETWORK_WLAN=1 + MICROPY_PY_BLUETOOTH=1 +) diff --git a/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigvariant_C6_WIFI.cmake b/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigvariant_C6_WIFI.cmake new file mode 100644 index 0000000000000..36de6d1bf70e1 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_P4/mpconfigvariant_C6_WIFI.cmake @@ -0,0 +1,14 @@ +set(IDF_TARGET esp32p4) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.p4 + boards/sdkconfig.p4_wifi_common + boards/sdkconfig.p4_wifi_c6 +) + +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_BOARD_NAME="Generic ESP32P4 module with WIFI module of external ESP32C6" + MICROPY_PY_NETWORK_WLAN=1 + MICROPY_PY_BLUETOOTH=1 +) diff --git a/tools/ci.sh b/tools/ci.sh index 098efbf9a23f9..60e870ce65b74 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -254,6 +254,13 @@ function ci_esp32_build_c2_c5_c6 { make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_C6 } +function ci_esp32_build_p4 { + ci_esp32_build_common + + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_P4 + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_P4 BOARD_VARIANT=C6_WIFI +} + ######################################################################################## # ports/esp8266 From eb5d89cd8324407eda67066292d4cabbc4ce4cd4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 20 Nov 2025 17:33:01 +1100 Subject: [PATCH 032/177] esp32/usb_serial_jtag: Flush usb_serial_jtag TXFIFO from ISR. This was necessary to un-wedge the USJ TX path on ESP32-P4, I think because the bootloader prints a lot on this chip. I think it might be possible to hit it on other chips, though. The implementation is based on the ESP-IDF driver, which will always add an extra flush when the TXFIFO is empty in case the host is expecting a ZLP. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/usb_serial_jtag.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/ports/esp32/usb_serial_jtag.c b/ports/esp32/usb_serial_jtag.c index c4834e56f962d..2df7e20086283 100644 --- a/ports/esp32/usb_serial_jtag.c +++ b/ports/esp32/usb_serial_jtag.c @@ -71,23 +71,31 @@ static void usb_serial_jtag_handle_rx(void) { static void usb_serial_jtag_isr_handler(void *arg) { uint32_t flags = usb_serial_jtag_ll_get_intsts_mask(); - - if (flags & USB_SERIAL_JTAG_INTR_SOF) { - usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SOF); - } + usb_serial_jtag_ll_clr_intsts_mask(flags); if (flags & USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT) { - usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT); usb_serial_jtag_handle_rx(); mp_hal_wake_main_task_from_isr(); } + + if (flags & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) { + // As per the ESP-IDF driver, allow for the possibility the USJ just sent a full + // 64-bit endpoint to the host and now it's waiting for another ZLP to flush the result + // to the OS + usb_serial_jtag_ll_txfifo_flush(); + + // Disable this interrupt until next time we write into the FIFO + usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + } } void usb_serial_jtag_init(void) { + // Note: Don't clear the SERIAL_IN_EMPTY interrupt, as it's possible the + // bootloader wrote enough data to the host that we need the interrupt to flush it. usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_SOF); usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | - USB_SERIAL_JTAG_INTR_SOF); + USB_SERIAL_JTAG_INTR_SOF | USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); ESP_ERROR_CHECK(esp_intr_alloc(ETS_USB_SERIAL_JTAG_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1, usb_serial_jtag_isr_handler, NULL, NULL)); } @@ -114,10 +122,11 @@ void usb_serial_jtag_tx_strn(const char *str, size_t len) { } terminal_connected = true; l = usb_serial_jtag_ll_write_txfifo((const uint8_t *)str, l); - usb_serial_jtag_ll_txfifo_flush(); str += l; len -= l; + usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); } + usb_serial_jtag_ll_txfifo_flush(); } #endif // MICROPY_HW_ESP_USB_SERIAL_JTAG From ff7f7449d2c40a3aa1e16abf7fc337d1ee43d712 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 1 Dec 2025 10:44:51 +1100 Subject: [PATCH 033/177] py/emitglue: Add hook for RV32 arch to flush D-cache for native emitter. Eventually this cache flushing mechanism should be generalised to work the same way for all architectures. But for now, this allows ESP32 RV32 SoCs to flush the D-cache whenn needed. Signed-off-by: Damien George --- py/emitglue.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/py/emitglue.c b/py/emitglue.c index 27cbb349ef602..8a32b227ffff3 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -32,6 +32,7 @@ #include #include "py/emitglue.h" +#include "py/mphal.h" #include "py/runtime0.h" #include "py/bc.h" #include "py/objfun.h" @@ -126,6 +127,9 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons "mcr p15, 0, r0, c7, c7, 0\n" // invalidate I-cache and D-cache : : : "r0", "cc"); #endif + #elif (MICROPY_EMIT_RV32 || MICROPY_EMIT_INLINE_RV32) && defined(MP_HAL_CLEAN_DCACHE) + // Flush the D-cache. + MP_HAL_CLEAN_DCACHE(fun_data, fun_len); #endif rc->kind = kind; From bdf761329664ef09bdd33e659de7fa25f6970112 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 1 Dec 2025 10:46:43 +1100 Subject: [PATCH 034/177] esp32/mphalport: Enable D-cache flushing on P4 for native code. This is necessary to get native code running on the ESP32-P4. Signed-off-by: Damien George --- ports/esp32/esp32_common.cmake | 1 + ports/esp32/mphalport.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index a52498a7fec53..e3b1b81cae7b9 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -166,6 +166,7 @@ list(APPEND IDF_COMPONENTS driver esp_adc esp_app_format + esp_mm esp_common esp_driver_touch_sens esp_eth diff --git a/ports/esp32/mphalport.h b/ports/esp32/mphalport.h index d779b6e66c09e..a2b11520a5ae3 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 "esp_cache.h" #include "soc/gpio_reg.h" #define MICROPY_PLATFORM_VERSION "IDF" IDF_VER @@ -51,6 +52,11 @@ #define MP_TASK_COREID (1) #endif +#if CONFIG_IDF_TARGET_ESP32P4 +#define MP_HAL_CLEAN_DCACHE(data, len) \ + esp_cache_msync((void *)(data), (len), ESP_CACHE_MSYNC_FLAG_UNALIGNED | ESP_CACHE_MSYNC_FLAG_DIR_C2M) +#endif + extern TaskHandle_t mp_main_task_handle; extern ringbuf_t stdin_ringbuf; From 7357fc583b5a0407d1b4b7e07aa932f99b0eb32b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20=C5=A0imun=20Ku=C4=8Di?= Date: Thu, 18 Sep 2025 09:59:16 +0200 Subject: [PATCH 035/177] esp32/boards/SOLDERED_NULA_MINI: Add new board definition. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the upcoming Soldered NULA Mini ESP32C6 board by Soldered Electronics. Signed-off-by: Josip Šimun Kuči --- .../boards/SOLDERED_NULA_MINI/board.json | 23 +++++++++++++++++++ .../SOLDERED_NULA_MINI/mpconfigboard.cmake | 8 +++++++ .../boards/SOLDERED_NULA_MINI/mpconfigboard.h | 10 ++++++++ .../esp32/boards/SOLDERED_NULA_MINI/pins.csv | 7 ++++++ 4 files changed, 48 insertions(+) create mode 100644 ports/esp32/boards/SOLDERED_NULA_MINI/board.json create mode 100644 ports/esp32/boards/SOLDERED_NULA_MINI/mpconfigboard.cmake create mode 100644 ports/esp32/boards/SOLDERED_NULA_MINI/mpconfigboard.h create mode 100644 ports/esp32/boards/SOLDERED_NULA_MINI/pins.csv diff --git a/ports/esp32/boards/SOLDERED_NULA_MINI/board.json b/ports/esp32/boards/SOLDERED_NULA_MINI/board.json new file mode 100644 index 0000000000000..85b12e98df2ff --- /dev/null +++ b/ports/esp32/boards/SOLDERED_NULA_MINI/board.json @@ -0,0 +1,23 @@ +{ + "deploy": [ + "../deploy.md" + ], + "deploy_options": { + "flash_offset": "0" + }, + "docs": "", + "features": [ + "BLE", + "WiFi", + "USB-C", + "JST-PH" + ], + "images": [ + "soldered-nula-mini-esp32c6.jpg" + ], + "mcu": "esp32c6", + "product": "NULA Mini", + "thumbnail": "", + "url": "https://soldered.com/product/nula-mini-esp32-c6/", + "vendor": "Soldered Electronics" +} diff --git a/ports/esp32/boards/SOLDERED_NULA_MINI/mpconfigboard.cmake b/ports/esp32/boards/SOLDERED_NULA_MINI/mpconfigboard.cmake new file mode 100644 index 0000000000000..48946f7094530 --- /dev/null +++ b/ports/esp32/boards/SOLDERED_NULA_MINI/mpconfigboard.cmake @@ -0,0 +1,8 @@ +set(IDF_TARGET esp32c6) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.riscv + boards/sdkconfig.c6 + boards/sdkconfig.ble +) diff --git a/ports/esp32/boards/SOLDERED_NULA_MINI/mpconfigboard.h b/ports/esp32/boards/SOLDERED_NULA_MINI/mpconfigboard.h new file mode 100644 index 0000000000000..658919eaf4f3a --- /dev/null +++ b/ports/esp32/boards/SOLDERED_NULA_MINI/mpconfigboard.h @@ -0,0 +1,10 @@ +// This configuration is for a generic ESP32C6 board with 4MiB (or more) of flash. + +#define MICROPY_HW_BOARD_NAME "Soldered NULA Mini" +#define MICROPY_HW_MCU_NAME "ESP32C6" + +// Enable UART REPL for modules that have an external USB-UART and don't use native USB. +#define MICROPY_HW_ENABLE_UART_REPL (1) + +#define MICROPY_HW_I2C0_SCL (7) +#define MICROPY_HW_I2C0_SDA (6) diff --git a/ports/esp32/boards/SOLDERED_NULA_MINI/pins.csv b/ports/esp32/boards/SOLDERED_NULA_MINI/pins.csv new file mode 100644 index 0000000000000..1ed8bbd91ce80 --- /dev/null +++ b/ports/esp32/boards/SOLDERED_NULA_MINI/pins.csv @@ -0,0 +1,7 @@ +IO2,GPIO2 +IO3,GPIO3 +IO4,GPIO4 +IO5,GPIO5 +IO18,GPIO18 +IO19,GPIO19 +USER_BUTTON,GPIO9 From bfacf821f448c4d99055738e90de81e50dcb156c Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Fri, 25 Apr 2025 14:39:25 +1000 Subject: [PATCH 036/177] rp2/boards/WEACTSTUDIO_RP2350B_CORE: Add board.pinout. This adds an ANSI-rendered pinout for the WeAct Studio RP2350B Core board. Signed-off-by: Matt Trentini --- .../WEACTSTUDIO_RP2350B_CORE/manifest.py | 1 + .../WEACTSTUDIO_RP2350B_CORE/modules/board.py | 8 ++ tools/make_pinout_diagram/README.md | 43 ++++++++ tools/make_pinout_diagram/compress.py | 10 ++ tools/make_pinout_diagram/pinout.py | 104 ++++++++++++++++++ tools/make_pinout_diagram/weact_pinout.png | Bin 0 -> 22295 bytes 6 files changed, 166 insertions(+) create mode 100644 ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/modules/board.py create mode 100644 tools/make_pinout_diagram/README.md create mode 100644 tools/make_pinout_diagram/compress.py create mode 100644 tools/make_pinout_diagram/pinout.py create mode 100644 tools/make_pinout_diagram/weact_pinout.png diff --git a/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/manifest.py b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/manifest.py index 832942f052606..7ae2ed15d9169 100644 --- a/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/manifest.py +++ b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/manifest.py @@ -1 +1,2 @@ include("$(PORT_DIR)/boards/manifest.py") +freeze("modules") diff --git a/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/modules/board.py b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/modules/board.py new file mode 100644 index 0000000000000..8e3cc2953da88 --- /dev/null +++ b/ports/rp2/boards/WEACTSTUDIO_RP2350B_CORE/modules/board.py @@ -0,0 +1,8 @@ +import deflate +import io + +_compressed_pinout = b'(\x91\xcd\x92\xbdn\xdb0\x10\xc7w\xbdB\x96\x1b\x93M$E\xc9\x82\xa7:Q\x83"A\x1c\xd8\x89;d*\x1a\x0f\x1d\x04\x03A:t\x0b\xbc6F:HA\x91%C\xbb\x14h\x9d\x89amo\xa7\xaf\xde_\xc3\xf8\xfa\xe3\xe5\x87\x19\x8cN)\xe3\xee\x00\xf6gWS\x18\xcc\xde]]\xfe\r\xdc\xb9pc\xc7\x01\x83\x89\xf4\x97Hn\xe4\x82Q4\x8e\xceVqG\xd3O\xb0{(\xab\xeeA\xee\xceV\xba\x94\xf1\xc9|3i0\x1c\x9eUk\xaf\xbd\xffp\xa5KG\x82\xb1^\x9f\xf7\t\x8f\xa58\xd9\x8ey1P?\'\x06\x83\x9b\xaf\xdc\x12\xa4\x88\xa0\xd4\x8d\xc5\xedou\xac9*p\xd2e}\xb3\x81[\xbb\x8d\x12?\x86\xfd\xe3\xa3\xe2:\xd0\x95\xae\x1f)y\xf5/\xf7P0\xa0\xach\xb5!\xbd\x87\xeb\x12\xb4\xd7%\x99\x8b\x9f\xdf\xb3om\x93\xeb"\xff\x986\x07o\x86\xe5\x17uV\x866(CPe\x98\x8b+\x13v\x98\x98g5\x1fO\xf5M\xed\x8aL\tq\xf7\xa0\xae\xc8&J\xa9fV\x8e\xc8\xf7N\x98\x8a\xb6U\r\xc7\x06\x12\xe2\xaa\xe1\xa23\xd2Z5i\xc7\xd1\xc1\xba\x1c\rcq\xffc\xe5\xd9\xae\xc4\xd6\x17Y\xcf\x1c\xe95\xa8\x17\xe0\xeay\xb8z\xac\x83z\xbb\x87\xa7\x94\xef\xc9\x8dx\xfc,\x1e\x17\xb0a\xb6\xb0~\x03,\xc7a\xf1t\xc6\xbb\x8cJn\x125\xff-^\x0e\x16o\r\x10\x86\xc3\xe2\x83\xc1\x82\xae\xb0\x05j\x05\xd7\x16\x966\xc0\x12#,\xed\xc5\xe0\xb98l\xd8\x1dv\xa1\xe1\xb6\x85\xc5_\x0b\x10\xe2\xb0f\xad\x94\x9b\xd8\x8c\xf1\xa2\x82j\r\x0b\xf8\x1c\x02\x048\xac\x87\xc3\xb2\xee\xb0\n\xb7\x84j\x0f\xeb7\xc0r\x1c\xd6\x9c\xae\xdc\xdc\x06\xb6l\xb6\xa0\xe6\xb6\xe4\xa0l\x05\n\xa3\xf3\x130\x01\x05\xff\x0f\x10m\x00"\xc6\xce\x91\x18\xd8\x84\x99\xb2K\xde\xd6\x98"\xb9\x15\xc9Mm}y1l\x17\x9d\xb8\xc9(zm\xc4\xf63\xa0\xbb\x87j\x01\x7f]\x80\xca\x0b\xa2\x13C\x8b\xef\xbf5\xb3\xcf+\x9b\n\xaf\xa6D~\xa4\xe75=X\x13\xd0\x96\xd9\x18\xd0\x0c]\x1cm\r\xdd\xb2\xc9r\xfc\xf8\xc4\xfc\\\xd9\xe2\xc1\xf9\xb8\xc0-\x9bH\x9fu\xa3W_\x89\xee\xf0\xebV\xb9\xe9\x93\xe3\xfc\x01\x83\'\xd6K' + + +def pinout(): + print(str(deflate.DeflateIO(io.BytesIO(_compressed_pinout), deflate.ZLIB).read(), "utf-8")) diff --git a/tools/make_pinout_diagram/README.md b/tools/make_pinout_diagram/README.md new file mode 100644 index 0000000000000..bebd7df7c8c4a --- /dev/null +++ b/tools/make_pinout_diagram/README.md @@ -0,0 +1,43 @@ +# Pinout diagram + +Pinout diagrams can be a helpful tool to display the pin layout for a board. + +Rendering them using ANSI and building them into a board's firmware means they +are always conveniently available, even over a serial connection. + +![WeAct RP2350B Core pinout diagram](weact_pinout.png) + +## Overview + +`pinout.py` generates a unicode pinout diagram that uses ANSI escape characters +to add colour. It currently generates the pinout diagram for the WeActStudio +RP2350B board but is an example that could be extended for any board. + +Display the output by executing the script: + +```bash +python pinout.py +``` + + +## Compression + +`compress.py` uses zlib to _compress input_ and output a _byte string_. + +The output from `pinout.py` can be large but compresses efficiently, so the +intent is that the byte string output from `compress.py` can then be copied to +`../modules/board.py` so that the _compressed_ pinout will be included in the +firmware. + +To execute: + +```bash +python pinout.py | python compress.py +``` + +## Reference + +[Build your own Command Line with ANSI escape +codes](https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html) +provides a good reference of how to use ANSI codes, including helpful colour +lookups. diff --git a/tools/make_pinout_diagram/compress.py b/tools/make_pinout_diagram/compress.py new file mode 100644 index 0000000000000..7e95ab0352775 --- /dev/null +++ b/tools/make_pinout_diagram/compress.py @@ -0,0 +1,10 @@ +import sys +import zlib + +data_str = sys.stdin.read() +compressed = zlib.compress(bytes(data_str, "utf-8"), wbits=10) +print( + f"{len(data_str) / len(compressed):.1f}x smaller: uncompressed={len(data_str)}, compressed={len(compressed)}", + file=sys.stderr, +) +print(compressed) diff --git a/tools/make_pinout_diagram/pinout.py b/tools/make_pinout_diagram/pinout.py new file mode 100644 index 0000000000000..569f40510ccfe --- /dev/null +++ b/tools/make_pinout_diagram/pinout.py @@ -0,0 +1,104 @@ +import re + +pinout_str = """ + \u001b[44;1m WeAct Studio RP2350B Core Board \u001b[0m + + ╭── RESET + Key (GP23) ───╮ │ ╭── BOOT + ╭────────────────────────────────╮ + [26 ] [25 ] │⌾ ⌾ ╭─╮╭─╮╭─╮ ╭─[CLK] ⌾ ⌾│ [24 ] [ 23 ] + [28 ] [27 ] │⌾ ⌾ │⬤││⬤││⬤│ │ ╭─[DIO] ⌾ ⌾│ [22 ] [ 21 ] + [30 ] [29 ] │⌾ ⌾ ╰─╯╰─╯╰─╯[⏚]╮ │ │ ╭─[3V3]⌾ ⌾│ [20 ] [ 19 ] + [32 ] [31 ] │⌾ ⌾ LED ▩ ⌾ ⌾ ⌾ ⌾ ⌾ ⌾│ [18 ] [ 17 ] + [34 ] [33 ] │⌾ ⌾ (GP25) ⟋⟍ ⌾ ⌾│ [16 ] [ 15 ] + [36 ] [35 ] │⌾ ⌾ ⟋ ⟍ ⌾ ⌾│ [14 ] [ 13 ] + [38 ] [37 ] │⌾ ⌾ ⟋ ⟍ ⌾ ⌾│ [12 ] [ 11 ] + [40 ] [39 ] │⌾ ⌾ ⟍ ⟋ ⌾ ⌾│ [10 ] [ 9 ] + [42 ] [41 ] │⌾ ⌾ ⟍ ⟋ ⌾ ⌾│ [ 8 ] [ 7 ] + [44 ] [43 ] │⌾ ⌾ ⟍⟋ ⌾ ⌾│ [ 6 ] [ 5 ] + [46 ] [45 ] │⌾ ⌾ ⌾ ⌾│ [ 4 ] [ 3 ] + [RUN] [47 ] │⌾ ⌾ ⌾ ⌾│ [ 2 ] [ 1 ] + [3V3] [3V3] │⌾ ⌾ ┌──────┐ ⌾ ⌾│ [ 0 ] [VREF] + [ ⏚ ] [EN ] │▣ ⌾ │ │ ▣ ▣│ [ ⏚ ] [ ⏚ ] + [ ⏚ ] [ ⏚ ] │▣ ▣ │ │ ⌾ ⌾│ [5V ] [VBUS] + ╰────────────└──────┘────────────╯ +""" + + +def ansi_colour(wrap_str: str, colours: tuple[int | None, int | None]) -> str: + """Wrap a string in the ANSI foreground and background colour escape sequences.""" + wrapped_str = "" + + wrapped_str += "\u001b[38;5;" + str(colours[0]) + "m" if colours[0] else "" + wrapped_str += "\u001b[48;5;" + str(colours[1]) + "m" if colours[1] else "" + wrapped_str += wrap_str + wrapped_str += "\u001b[0m" if colours[0] or colours[1] else "" + + return wrapped_str + + +def add_colour(pinout_str): + symbol_colours = { + "⌾": (220, None), # Pin (Yellow) + "▣": (220, None), # Ground pin (Yellow) + "↺": (15, None), # Reset (White) + "▩": (129, None), # LED (Purple) + } + + for symbol, colours in symbol_colours.items(): + pinout_str = pinout_str.replace(symbol, ansi_colour(symbol, colours)) + return pinout_str + + +def colour_tags(matchobj: re.Match) -> str: + white_on_red = (15, 1) + white_on_peach = (15, 216) + white_on_dark_green = (15, 28) + white_on_black = (15, 16) + black_on_pink = (16, 224) + + tag_colours = { + "5V": white_on_red, + "VBUS": white_on_red, + "VREF": white_on_dark_green, + "3V3": white_on_red, + "⏚": white_on_black, + "EN": black_on_pink, + "CLK": white_on_peach, + "DIO": white_on_peach, + } + + if matchobj.group(2) not in tag_colours.keys(): + return matchobj.group(0) + + pin_colours = tag_colours[matchobj.group(2)] + + return ansi_colour(matchobj.group(1), pin_colours) + + +def replace_tags(pinout_str): + return re.sub(r"(\[\s*(\S+)\s*\])", colour_tags, pinout_str) + + +def colour_pins(matchobj: re.Match) -> str: + # Regular GPIO is green, ADC pins (40-47) are darker greeen + gpio_colours = (15, 34) # White on green + adc_colours = (15, 28) # White on dark green + pin_number = int(matchobj.group(2)) + + pin_colours = gpio_colours if pin_number < 40 else adc_colours + + return ansi_colour(matchobj.group(1), pin_colours) + + +def replace_gpio(pinout_str) -> str: + return re.sub(r"(\[\s*(\d+)\s*\])", colour_pins, pinout_str) + + +pinout_str = replace_gpio(replace_tags(add_colour(pinout_str))) +# Colours include a square bracket; temporarily change them! +pinout_str = pinout_str.replace("\u001b[", "~") +pinout_str = pinout_str.replace("[", " ").replace("]", " ") +pinout_str = pinout_str.replace("~", "\u001b[") # Put them back + +print(pinout_str) diff --git a/tools/make_pinout_diagram/weact_pinout.png b/tools/make_pinout_diagram/weact_pinout.png new file mode 100644 index 0000000000000000000000000000000000000000..35240ed9e16d09d51c09ea3d4424cf8a25a1508d GIT binary patch literal 22295 zcmd422UJwsvMy}cU_eAcB+~*4N)*Wmw1FTwNt7TUIZ4jl27<&E$yqW;lAJ+FjpPOd z0RhPwBy>Y3c&i)t*`9Ovx%d8ey!*z_7(IHidd<0N)tvQJ)mOEG)l}rj&(ohjapDBI z{3B_N6DLl=fX~^pr-3t~*u7i8*GU%*xd$hT`xsY%fBvw%uYCW+iBcFD&V&T`ZCp+1 zvCQrB*FRM>n9AzSe*cjdpLuk2RGC*A?D}ea_}j|DvWJmlVonqS41w(L2CuMU1c*x1Igs84qe@1qV58@rKGR&$>! z8&?-sl(-+XR5vfq%sLxS-rC~9*H+~6tS7>Ad26|MxmUDVGzW5NyX(|^1If|oiQb%Z zI=7j2H&#(UzM{L!OMaBR`T8bqEN-myOzpc1miOq9vtwvP!_L;m{#e&rD=L3O>bB(b z*!F^wHy4v!FYhf(;+o$BT56$Zd-LXvxw(07Z*NvsR;Q5UTw%$^*!Xxr$dB}_rRL8I zRkc4?Rt8NhC!^vvXJ&hqG)G*$))0fson0zt^-M{<@1J`B`QrodaIvR`^G_gCPELFL zk(POK^^MERvdp4|A9ENq=0wm5wE{Ksa`UW#tk%ia>DB3x#S!Kh=H-p$lCLGTW3_Qz zalNy>qVGlDw7&6d@C$ZDQm1AFeo z&6B5z=T8C0{3lPHxCDIb10U<4KZxgTfMdd+=bck0mVvGL=l8$d^aSwJ+cPKr0Is}! zN8t9!_s6pm?nv_fc&2FJnDBQBnD-R%N)0hl;ON&V{@b?+zqajmRXWUocAU0IT{c!>0d0(*)b~-=f|sn8Je#pHViKPX-Hxn!~lZ z`8JK40v#I*+K8a7KJ;Ns3*PO>gB4Ap=Bgx zuyh8mpXs^WzQAlqGnn?rFiCk~#7vg7rL>36WiC-S;lgE+*8IxgLp!Nx{S723I3$sKHMf#&`D;26iFVQ)}&!(*QQ2}Ww5!E;ij);qh?fskhXV7tQ9hm zG}3MeqF33Vt939`78c?7@a$3Py65uIfFl0zh2_`P;x>$vbp|QEH&}l)JkaL*G-Cnv zcF2rM2$Ji$GM7hL04NijL?NWcwH9^1EiryPowMj38JHKufZX1QvtDb?m;Crac)-(d zT#PYv?BPddKCY?O;)WM%+ACB=(MR~*GB!+*-gY{d1xYD{@#gr2VxA~^ds119%Qu}1 z9vZ&F{2t!=wNX5{Z*(>v$EOW$z*bU1>Eg4)gA?tzSICzJb2O5iyB%=fbhyy`tsnGP zlbD!!CUxK)HiaH+?Ua6%7P)${JqN|~I;v>(Ov#I~1It3R)II_azt+=LFmo5=hBHFu z>$G-1G=Q(|An$6&I|wq*f>T;x-wlnxc;>o$jxstwsPw?R+k0O;Fy zO)KgkNH3HfyMuQ-EV0ydJc!MY^b=xS`NFVwKLXKAw(Dq9DBGy)UGHYdjT*%_yl(KH z;(D$l1h;~l^ml!SFb;rw(#Id0;Y;4CzrG<G~oQDmWT6zKe6I=mC9(4AEc@aDNr44O_W>qyN~8}qTke$$=nXQ@PrrHJC41mnH^6xv^7wI&e zz2Ye{64kvpbfIaV7KXI06)St%Q^qEgL-r-)VpYm8a_(K?RN@!BkdUMe)DUCWWWAu0 z0cc?_o~uzRfX&+#-A}~Yo{e==kga*pXuY}DOrTdgSEpR(8hs!EB{d5-h7U;qE*(8>t$I> z#49y0@94IaI;?qJSez8U zVTSgt(iJ}HwuHX`S?lDHjc7qzJ#g{HTj}ifSJ+xh6uzl+A(K|~OQJL(+#QcVo; z(xG53c*?M3;IMdFB<{_Qz_6Ck*sz=u#>N$D7ueOuM7JNW z5~vmH!j+!SGQn>8-kR=lqg(Oxs51pNW<}M4bf|`<(Z9CEp zWLxFU(K;TkxiZ{rwh@I7Nq?~-dZKO?le&g&>GGiRo^`32%6u_B_ic~H#Z-@VpUsH! zt6gH#bB}h6y~^<7c_G~`65mnKiudi5Scq&cF`I;zaJfkGi)p;D|9u~&N z&NX-$Gr6aAXC}EzJ^pkzI+i8<$W4XmRMi7z($^<(Ku_aH`l+mgj=$_EKn=n6+Y?zfHtRvqnC;P8l2&&{swm zdPvb-)||5FOoEYcSg*xnz6mQlvK6Z%`(r-rDI!! zru6lucO5QwdpExx+3NOs$C|_v=Qi*ZpA7l_vHBa$8iy9QU#0M4szy$fqQNEzqOhDb z&RBqC*?a1eLAvN*1E+*huFFLR-5jJO@jc4z>AIqKe#z5zVbHadd-`0G;&Yhg1_01@uI9(`g&OS1+y{-9}I?vG?gl; zURmRK%#C6HglL;G%5kit@_Lu?4Iy7=Yk7O1X=#7JW7I}{lqhlE!#5TF?IKeFX*=6j z>8Q#j;`0uowys;uV?!k58Nk{_X()4RlJ(S)DH6vK)x75{`776#t+Ja79rdZbC=a8D zQm#O_r&{eMks8G}m~XUgn?(j)vOX8uW81mSYw+V6NM)-80rL$qmI|NNnU`NZDE)%b z+AL|u#B9A8p%ubx-C+_)B$H0&xSZzvA!=C;Gcn8n7LWCezJ0_DU)){tIvW{#^O9G1 z-JE^$CYp(C38mgp(it_pZ5D+j!y+6&xl`5Oq$2ie;j+9AyVr5Im{Km-ftVB}6yl9R zKgv(ipFAGV%0)%%_(4Aqy3$@&JSVr^bVt=xE^d1C{%d5xp4rL)Vg`;oP%oKy1C@n; zpW_*n9!ha#25ZR5h1LPW=%#mfQmt|c&_4~zBY4OD8))ZjA0Zl z)XcXeFPikZd-X}~!mHkqelN-Im{Q&5cOf!I=g*{QYo5pkkI>O>f=coWqSoa$KeMM8 zuMN2Zt8hie-GBFzjQ#`G9eOWq*&N%DtJK}I5k{&u zWe{yWm0sg-NUnnUy|1M&6W=zD!fj0N+|GbDH&x)dBi?|Yuy(>-;Gp)nH>}8t9 zKl@X2$1Ec;+zi!3J6fPEii^g@=UHeW8c#IoTmYN-?}Aum?xYAiEgF(y@~UMju{Li4+}TkHBxdP(jGe?NE9LHbDz@V|T9y%TbKWC-%m#X}xTPJ-+X; z7rU9$fvqE1bl4tjM6kZp;O5$lQ;{$Yi#?-IG)VroSceh@T4`RCAlcmXIQTM54H;JK z{6nG1UvIF~ch)2qtRAK8#{@@y@Q1@RC*bcv)=)!k7OI=>V-Iz&!W>!AwO@rb^bL~La!4}2mr)0PR z0FiDX$yLq**@v{H2+uTxn!jfDvx2xb)Ew$wSDPoyaoy(Krh>A>qw8RH=Iif3R1}n7 zV8O%{;m&l!7c?HQi3dyj%xa3REuLiLQc(WMg>1_VK`+*_YfVdxD$cxF@2yLWf5I=< zjq&(K8GI41%NM|QrNbArm{_K&{zxLLMnj+&r(_8!Ji}Gkopy%{k}0_Tc2=RcW=FvO z9bamwUq!e$qlc%yj$fWn3tzxQL?HMKY)P|IHjcd(9W?dx00La9ridD^#rs_ zerEPeMT_(dRe!v}ENqaUkWaXXY?|Xsk|_S}nD^Z-?*Pz3;BEAFWNS>~=i?vcc=1+g zzF)k*z|L9PbVR1{SKY+X#`w-8hmXSa`Oy3`M0q-{_P*mQs)DWfE=IP*BswivR_K^U z@pW5Jtw}*DAklCbX69)Yd@N}YnL3*g7#ws($HoG)$~Jag3SE}dGRkh4w$YjYJ| zN);U7DC_en!VCMFN+7xGl%*AWW{wqhn<8As>8jTg5hK9L!~qKhqpqvp<$4qPz`q`BIieeTH< zjtDTRW>r;_uSR~O(r0g<@|dH#KY!PU?h;#E1v*Mto+%PuM|r+m+Czro`BBHYXdt$H7e;#wFP-O-#_?7B1h|dzWq2F8hn@;ci@bctU`h> zH5w=za2nb8W~kizb6OSq6mYp)2x%PZ6$esw*57-b-)uN^sjpyniPyn?3Cw?vEzMD* z%|Nosq9b5;wT5{?v*B53u6YFr>D&Pe;Dq6-GKx&MbBpgP2GQ0=DZq1<(8e0u4hs39 zG*|3jx94(S;%tB@+;X|V8p|VvVElFC=L#v~8a8YEZZ>PP=v|tsek%rtf>0Xd&tyqJ zTOb|FDi!-QO?Y*6wc=@XJd-Ye9KNQg&o1q*e5Qw!4>p-j#v4Yq>T5k6J^^om8EcGF zST_4^Ua((R$&Z4E%M5Q9h0;{_S)Cb5MKRi^^Kcvtyq>nl*3>a>x|d(-XvI!$QpI(*ml zMRkg5|56%TsGq!}?fIQKxq5p^AK3+zxXP3EqM}`eluVl3;V&+^{X^&T=V-p7LoGt} z?K-$kCEM%m3!*HM+}pRvbV)|G4!kgqrFJV+z4XL=oJ?Z*6%_{qfh(2GeBQASjI45# zcZ!t-Na9fkPHV%~=7H|xFpIn9xl2b&l02D{?=9ChqNU3DL+5kEu;0+8q28}h9jgcD zvYo*iGhU60V#FIh(K?L2Q1IqPU+OqSY5%=$749D!`42Y}Y)h@Zc&~8N`%+J(l&L8x z6-z1>u!Fs!L{Y9P7NO*XzRb%TDN=R4Lbs#}?x+0PnYVG=5*yvAvu7hN{API5{(t zGW9v^^;n`3w)7CC?ijgpUAiW1y%X-~9lY`2W%A8F%OAt>eg@-G<47(kVBU60OJw?X z?K{vA%IP8_7=kP1V-lIT>67Sfgk6}YOVqZrQ~7kmWn6gmXnCdGU<2R=54M$h=j8Nv z812HVzsj4?HTp{>ODJO3$Yk4@og7kl4aFsN3SCwX_&qK|6#}(FZtP)Fg=qLg%@xzE zM&2`Ar6`_J67^f`$=BqtKHEPiv%KNLQKb?eVXVfnV@4h@?T<5!tf$K>%R?ZDTv_8*{5QwZdw?3MUJ$C=NWaOH1!(iCs_?JVOM)=j?V| zZrlE1DWX1`VH!)SV`RLTSIMas!nbHt({vUb@w~}dZ2T^-40{@%8ySjbldRZG%9K+J zB!6f)w1Q3b7zY*=kyWT*aO1vr^QVh&e(BVUqjKLdM9$goI zc6%A=ow@DbV3~{jDvtNKo;x-uZ5m4(6}goK+AtDB^Ya@UOu)^BddL%S@yqXGN_@Yj zzIh}q1HpZ(hKFeecl* zKI#y0V!g+{#x;H_7S-FBtySK8B?<^L>~+b!bA?!>!8WfjfmYVMtXH zWs~e)^Zxv}zbDj@4csBuS1Z7MWaL@x{yOsKXV1)LF=6J)Az%`oTPf8CA4fqtZTkU9 z55NXjU`706g@F3=0Je}{*+A~AtSh+pZ3Vv-Hw;Rn>g_^fArIhuDN^a(7 z_=)PLwjETHG94fa%Z-|2(lt&uzCvkr4Ra@BAdIS=j*U};(&U&Vs>)|gK12D@>GUX( z;b+puj1<;6w+x4>gs@8x8Nx8eIccAVPf6CI-?EZojEXDG>(|wIkg>28 zAEVI~TFvPQWd>!RCl5VbXXWsEn2IUeJwA(T#@Sn!Xu_G(%w-_@PqZ*qkX;=|t(Ypx zB(#dsbpF%Z5bm;DTq6OEO@`4NZVx;l5+hGE3ikFt&~I}YzQxr%vhtNR8z16TW>3QN z+jFve^A!YY2KY}^b;<{TU@PR7julP(yj3AW!VQ;!`;HqshOL8Rq;8aMa2XD)&~vcT z-4svcI9ewELCHk!@a~p1!>IuJh}8u7%+j&qfi&*|QFWWlZ)LG~D^;5BzDTb0p*QbA zwpq5zTc4dvS5zRrA8GSS?^rUOZj{WL(dAOo;}kp^ZI$)7v^YVZ(!inZD%GB>qgu#f zhRch_jDtFNZk>V@#ucY)rCf*tfs{yX%1RS+l?4SETjfhc0yXCb563T*)w;j4M2@gm z42cx#!elo}n$h7U1tl|WlqbO>3ZquRW`hA=U=)`Gr4-ri@942u71R`t#GO}M_=Hw! zj;+3~STp%XjTKvqvYchvRVGh8%erdF}>D zwPF^^I@Tme$Db>(wvu;%=3<%7ICpRFYtAo;BlA&@lWK$cZgWwMYMWLt?zV4gOn--5 zmZ{7g?@{(u*Bhh#;N-tumo=_wzsTN{x5sfBv(OiywvjLFLiKl;#cU%X7Ax)7Vx1wa z+yES1c`2x#!S3eF9eiR2IAX2@j;^Kud32lbEAia%(TY@C@uQUWSHtap|8-#M?#w~| z_}WMI3!J)5@rHZbMOJF*VuvcTy8ib`PT88?<00j0eShkukcryo*{xZc%dSsoA6rE< zM~PlFQG59*kyb7AbJy*>dry^VAAdZkKalxj@+v8H;@sBt2pL+!yrjdXAMae?yrfG- zO3i)93%Pv1I^cf5;=$cuv&*E^XA6T*R?@0h)K{mO$MGhCiwz_@+aykMUNWsx`y!~* zrUVQs3x$CX?Zvw9ZhV#z-+-lh-sw)o%{lt5-Azb&X0$zxfVjG~suV29@$1Kxtgo-j zgk2r6(slIMy4HQMDU<{dKpJu1{iBD_-S0O_74_UJJd}H$slB{Z9-!d;;GLA4nd5Amp{P@ftkM5L*f^b zL{0#*@2!WYq@=9T>0vb7W3&}M8SO;9gp^~P*#h&9La&RnUazC zFaz}N%7z(=Nj>m9bxj>h&et6_*ocFB{51Zn*Xs zvM>x#MpfuE_H#8LZY~6~k#^|6RGTCD4VZI3%#Q?un_EA+8DffCUJXpv?uI4$_Bgu! zkP9CfS~qv{fm?rN7%Q(RYDBGnUE9=hu`vnPu6@VEXfD@3D{-1r*Nz7IGZ%`&!=VRz zX;AE~!=)#hfed?B1T@r5FCM77 zNJmI@7F%Ky)Kg9Y>x$siDSKTIcX1^x<@Do^-*}~v2Wdy9JXv84gx3O8*YZ|WEL-&LM5p~kJC6fp0Ya8o~7Gqlbi->&7 z7cE?_S4yK0r2EcQ6awUV_wEI&BMhVpOCEd#K`}!{Tm7l4_=l>YdonJQoQXFTw`2MOUXT7gyUVlHu%CtgQlmdyixhm+Z zY~H1+P$&iuIvg8Pg_i6usqRXQ3hK&%{LvdEew7IwoLBV zJu@kNot}2n{smKlp}`}XEJW^d_Ex;V%2t3XJI&@M9T)D-hN^IGQmeh1`{0TpZe{g0)V4!{Z_zwSt)7bf@mcUZwc4N~#K&EHjU?Ex2rID!Z{!Q7>r@p6MIf zSYEz6*}3K2wtkhJaQpfA=jER=$ILFh)WUv3iv%(9nfkg)Z zG?Xa+u$uw$yXSK~*C+8iJNBR4OrCSM5FnMGVB|y;y`A@T+XE3~8(bGrkjkK%Vvcxe zqL$8l==QnvEMcm)7y1{p0||iK^8Qu(v<#^D#;SO+w}XURtPBB8y$?o7;H*THJ#E9U zOGg0KbFa@|h0^%d#OAHltk!N1>j4i%O1(N`O!HqnlcpsgDedFDAgJMAg0h83$Fian zs^WV&(b?+zG=h0gBMl1`f^HW#jS?Skm76O}R;MEkec7)B?{7^#d3tY-qkR1?_^K8) zBzaSI4ySZWGJx>d^egF#?dGUkYN3~9lKtL0Vv0e+*rvBydrWLT*aQLei}P1EP2I%( zOBD-CE|M0Y8!rt-*xnII74JTu>9>@?0XP7kO`2)U2rSKWHo-F0a~AGuHtqt(AY2jz zuXE}eOj;$UU6xO@2K*B}dBM8*800S488t)cXFpHI^ZpczMR`b5wVB9+fSYx+kAiNa zDH#?cTFJ$V7QTA|Pl12*B>~)b7;{Deo-&T;1H%asnm&0W2VY@CE1Dmp&dFk6G+! z9A7n+%+|-nxM2 zp75AJ*nKeEPub$`j%!!OV-t#dn6eKERS+uX_&2AGSUV@}S( z;W#3EkuaX{;H4iGY{BE}ujM7BuNy0nu->1UG`y>IcA{NP+@D`@uP8TSo1g?Io+pv{ z{8w=*D6+#j8uk&jIp2=2z$|o1P0<9MI{Pa;0bd;pdRXpPnC2U~YyPUGzTW-AB^jF2 zL}hlVD(ta4bUF1dcFgHlv1&#cZ}E@1@2pAo$FE7MhLjD?h7~aI>sdcOpn>^t z7c<&wNZ6DwuPYN~l5OSGxhYwnTT-g@T0iV2rCtcbo?rTtuKXJo-O_!`C>PvQYkCIHs{a2U*TufibqvVCI`+>dl_hH_0Yo)XyT&tfb+r#RBTYSbezQ&~g z1q9W^wp%Y(-CkH&d$ra5pJDJ)8rJtlO;~%0PH(T3nhE_8_V2?{B4RV@K-#Z^RsJ1J ziWcKSbbo+<0Iu2RfCS%(E?HeO6F`g^ZgaSN=JH{b}lG}mtW!0 z5cOX-)e`r2LN-Rsb(PTOf{svI3eed*SDTH5TCDiKd-Hc2`K~PXJ{eQo+gBXp^P)G4 zO(LRS5Q{ zyqbLh!EHIui^vl9`65?L^R01LhcA%by*aW9I$xoBx821Jnw2e@txju_R0qNS{t7ym zKuv*gS*X4im|uF@`s(hJdfirZLVW$mvC)0T)|x^h$H3Lkn!PaUE?hq0RndIk8_@cw zvSjTan*i=8t?^gSL+k`G?`_g(-!W|%)K|>~b7cT1f{_v^m%YsCzhQcli8b#~858KD zh)OB}8GEiQ+_iXpK#5<{5-a*dgpUz3hg)d=2mNZC<*FCHL=>9`jj}d`3!ydbir3e_ zivj`OH{QTOmRGBm?t;mHj-o++{rOH_pM=N-ga)le94*JXD{lFQU%ZBteV0pJQx9O- zkarJ{J9P5dp1Kg!^pU%(Shs#*lDwL)NH>b2MDF`n#gCwZA(k5-(yWKAczw1Ucc0{y zadRoH_^5<;pDwmO*t#davDiyv)aaMgBNr6mv3LKvYWeZZ?mcAAU}1W^wHChZ?Yh;{ z&H_{a=^%9=YW(&`d(4_n8glYf@cCF%DAS{$9(q(J*|dv|0rPX^*111S1bf#o-!U=k zm7@pOJCuG+=-fl+qvIKcNYnl*SK(bm51NQ|rsZ$;yhnWMa@o4AtqagzP2XS7+@I-> z5afI2xU2{(X-aG~O0{tE06a?0WIWk31s3pXi-k32wywW>(v}Y-On}nLWid|le?HUN&laodsRp;T-X2D-Ca)z_k|+^IZU(Qcp&)iZkA~=fU5N9d$cG~F=&x@>w ztOu=;H~F?sO~3wojB7`~y2$;Ix>(3woSYEhHIVsI{2S0(A`}03L91q@VnvV`w(+GT-v2JCA4dhjNAn>`l75>p$&3{VQ(zJ;39?9Q5z<=J+?Q)AD~GmnpT){JUhD zefFmZ#vMFtC(b>@_JH7$6YiWHNqw`(3} z>9AFyY( z9m1UPu~0iS!)U9hUJS~|9{Bv12(!Ba?qTB+E^kaE@hK+jLqR!OZV=g_MZN3V*|ngD z!2Lb(-xxSc;gGe>Nq{^mIb!L86}wpr@G!?IMB$WXtyvZ_%g)x*Ub~>EqC~B@I5Sw#iPqB58vDD z3M6*)vtH)x#nUPT4WEL6;>f_oNz=t54Q~6Rj+2epkrFf9vVNTK(5zFw;Ftby{5IGg zmQY%k>5Ul`$%nPP{r#7CQe|2=?QuYKluwds97P=t(s`WLJynNIf;RZGP@Rh&h zmy)$l6MKBE<9l_e+bZ2hjU`J3)yww@p1ff2J1pJx5s%GQ$B?JF=rX6PD_aKhmwmf6_854*04s^!<48`x_vxUs>S{4svEARK8 zOb7Co?kg(KSYN*-pFP5xk87ues}2IVZ^$HlIkiFzX4sz7R&3Kv-913hxg6EWg!De0J}s8W*^;{ z!>xVF4yNA|;ov-KdR{2J-1pN2`cS#E*v6{q!S#`0T4_$&@FQcHeGOh(9TEB^OLuoZ z^D%@`tB(=S1HQZOOOn^t=%g=m81LDyk>hVZ4$--&_W2kQObzW5X-;n>R+{^O+X;mH zitKk~nR-hvif4`wRX?;_i89JW_n+4hNd#u)HP`Vo7P%`&W-IuhU*IP_z$9vL7#9zi zIniU(fvy0E7LgDGOociL-Q*FxHOmj--t-=>o{La`a8IT`IX6~1I&pLIUFiuRXo^hR zmZ=paq-r-tCpJF8gwYvA-@d_;({3#|bpctmJAd@W{FW($s=lkI!OWTDOCj_Zhu?qO zigmbO@adb=cAzUEUt6?s-xwAtYFnZE`0gE_&d!9>B*P;he+f)8 zquiCIY9VUuRG%eg946soZ)zt!p`5C@=9^;&Z>Jyp3gm-yQS%``DMoaiC8K!X3sc}Z@xla{h@FaLsgBmELhRb0s2Bk+*B zK_Lq<`AVOT8lP}PB`Nnr8{v1^Uf-nbPh+9SL~7kDA#yrlPYua;C58QrMW^0ED&*-o z890RmpR^;L!h7e&pLRtwPIa9ADHBcw7N+&-_x96QCv#f-||TR36RdV{R&I_K@J}~)fVOq?dm@#vC>_SM$n8UD-gT4sSmzNYz^K~WYH8TJ$D{u<|h7(xoyHJ^%xCu%q>D@@NT}| zHu_pihWeJbq;1#%S_w4@gxHK$rf@NW9RM2{m2fLm0urXIs$8(zpt$y%{1&PLcl-L^ z78CQZekHvuklB#t&%0>MXWR}3#?J+x5K{Kr-EB`NN%0Ep{m8QB3U?(&Kg z)(iPX!U8$u9oxhSCkR12Cm@;{hs}BkD-UYk(2Dd3?v;8o#`E|rlx8VZfJ-y(46Ekc zRu(O(r>q*l!5U@mMaxxE5}0J7IsE@)8~)GyxhPFt-zV21nwP(a+m~fnw=enJc+z7* zjSbJ+C8K}*v6ELj`=Q0d$wDPVixdLKX9BVCa0GYr*3OaiAzJ89FFDOe*-f_j_yo>BY3JEtIvN>TtQ6L+s z5BpatT`E3y3>JahW4>uR2)7C0vVjz|8^=l>@-{68~*e@Q1Bn8-OV9YBaB+<%?Wfec{I znv99n+kYAMeu7Y@*rEZVtV7;^$u$8UFYn$DYyh#wA@Gd(R-9LZ+_`@WUQnc;JujPL?(8sR68Mit)jhWtG7`Rs%oBlk9r5~% zgS3R-3ELO4nZ`SF_0R5Yz*X>$G4K7Bw-(;Rwa7Ol(DQ<=!XB*s~A@n~ii*c}tUo68Ec6x!}Cg`tmw~ zG;^r(EsG7BF@;-KAN>jv0f^~%TmFyX zeGA=pQ-hQUY4@WjL>$Cdq?tv)XH>Mb7kvRl1INvtB%({tD?M6g3wKv{AZC`mTiUuy zr@X$b3I%8GkAlq85Xe*?QBA_eOY7}lH`@2Tw?$7)baW5)1CvvjCJ9JbtD>IE(0hlI zc~)+E4otipjtY+?QnJ{$;-iCgQa~^Tqu%VxI=FEZ8nK#J)9X zG1dpLLX-b!u2_j=1@F!@?#~qmb)%D8(33t1*w_oO^DG2{C_%{nj!^lRuA1j=Y`oys zwnf8_q#r#Vib7*9RMHYh-ba0Igqbz*MemZu_~A8)8(y$M$XqB24L`R@d~0;nOCddrK-L%)QP-IYh>$A2Nz{o)=2aztND zAlnX1PGd@$w0f6TTH(F;mVrp(y#9E0$?}cIis%3d;G^B;0Y(I1Id@q`-nvu(#-qF= zAjXp>Hs4lV@4ia+jfEBbP{)4LN2Mf1WwzG*m(bG5EKAmMmVe6J`O?v2V_4whLurWY z1i}T4Sh=41M8kT9Ah1yzt#dKKK;$9p9OBI|p&^3XkvxrQTesda2OX?0yHbu+OdAIc zvbbb6u`Jojd5F*-tHO$&$e`Ryv*Kusn><>cYV-*&-nXjgs;`)C@+djf6aP%0V|rk9 zfyvc?X}sy_zipo?Hvbu@Sgq0xaE!~R&0`Ic^9fb5olvPnOuimK#0IDfXqI;thLJ%z z5KMsp_u@P^O`v&}qlo+PFAwp5bt6Pthyeg60(ktZiJ5!V!`2K4$HSCV>YQg@5M^V9s)$S4*hPhB@VlDh>btPyw>6*fk6TxtJ;_yE`K+V%X z4*-+UyE6Eetn%1J=#<;`8#Qi9eRsi}v3CwP?ddV@{xtc7TEUs^OhIO*2bDRnzjHDPaJmuv8947FI}CPR}_;!O|-&T{Lgai z{$!vu2^kj1Sjt2TS!nEzN8cH5HtMTu0OPf%BbkJ6#$JUgOUh5pehD&jyN2qBU2ZCuXSuXEqp-}Z$$$W#7Lv$qZYOpuB zd$S&Pf0EG3LP2SJGzXo{IsSfO=KF>XNJMx!+xU09{py>hFd9mpvC6EQ2bl=xErdS&83EDCrIDEpHYexMW-_a8+bF^<1+?NHMVuXE8laK zGj*BJDn}%k{L2Z61V$aigmH=!4)6DVKt>qQiRkh$1KKgPAd9lO zkl}TNveZSOJLM|B$ie_rs!s~PJ_q}L2BN|dJXW~lfcnfa#<7_Ocl!V|Z2?i)aqeZ` zu7a<@P8MM8BS2#4o%<+t6l7%G>k)APd}}hdH=Ec82atm2bO?sBw_7WYoHL0vbaP}* z@g_C7tkU$XBG-?@$N1Q7viR)w;W3M&Npdxp5VG>USfqbQ-J!V~8W$^v+AZ$f=s1s#BHRmGw z!mrXS;SOGq#SkWuB~rq2IF;I_+6a)?@Gny}=HmS!u1l?NBzZ>WInz4wi5(Pgk2Ra< zus^!@N8;jy+-vv2t!1>pmWa<`rR;}cFc&K^PWP{82nUKQ7x6V7){a{mBeMsHr+v~8 z8nmL9?vPTzPY)t5zkD(0A=qfTzwF^YBJaunQ=@PrC&YO`Q`bM+%Kw$qVstmNhvS)Z z@6awcpAob#Kd3*oQo#x~YrgCTxetKnSo;UpN3Q_Y|3*Bh96cr9nN&=`!a)}MG1zq%Z=Z`AvYmM+hYv%K1>ItB5F6lK*yC6 zWF8lWnsc12Uwcm|3i%U>Pbj zRDsMCgRIBlza{Nu;&dVIy}7BAZUcn6*0H^WA0iwHjZ?jX_V`CjqtD=FAQ8AagnP-- zw|M>M8c4j=N38AEruZ^|@z-YvsalVb`;YR>Auu2X4a9(Tt0>P2>XM-t$BFj@r4J;ov3 z{U7T$6O3E1hNy{ZaJub}tsx5Ns?MUvy#*%n%Mf2NpY0K@>T=^roEGXl9YqAhtQUFAOFn-Arwd@5FSq5KJd->F3p*a`#8SB`L5AIWHgc=|6 zIy(t)10>MLH;dXRdiyz}8n4~Tb#t*IpbL)w34m@yiW=CQ6`lQz2$4~ME8t#7@=pry z-XcL)fGTO~f6kHdHTuHsx{NZ88aRpfUlP$F_WS)ZyD>yxrS09+H=LZ8T8z>VC+96) z9Zjky;1hbNJN9ixnGeB@UUSwYdG|Wey~jOr&)k=XWzW2K7_A{R^F=Wkc3&)6DVhgL zUuS*%6wZWsvn7DQQ{`}&E4DxksFR%y)eqb2>`mfp@Wu*{1zyB#F1LbHE$)*%=y6B~ zTB-@H9;f}jUK!q?CpxaO%x=Ar0T^aL{Y-9;o=}?c(BfdS@fuStNhPhxWuUR8@POFZ zve&9qPSB@qV*aJ}LvbMgV>)ySOJ|7TBpUXY2w=sh#H<$KurgJ!5HHRjCpXc;iyEz9 z>Yf^)Avg9D@@bUleFf~4BI}&v&Y$vDa0I_=rUnp<0~E-3FnPb_)S)42%yQgq9!`OL zJt@93$JeXP?St-}5aj`!B5@UnZJs?Bk3>5L!WC{1p~4_H?NrU>ZS0jBugyWtmZ>YU z^$W0-y_`mnW24vFNK%4W7vO$3M4a?55^_PZr>K5j_wGITF+N761t>`z0A8ANkYrA+2n^p_p0d`k^208<=?@qfb$1BJjT_C zKTcgVKFx@DmB$ZHJXLc3HPE4b)l*9y5S0po!|fhft;_A|jKP30`~QtGQ4rkz(otn8 zC=G&HY5oITN69uh2ZGbqsc+A%s(B?6H2hDRIexlcM(|<1m@}>#YP}B6P4!;2{+Y?e zKPX8wG-`11#h6_5=n1)sYc@TlIiCtz2<3#f>BZ{v=6Z{7WdZf{CL2ec10Uh`k8sWP z$T?TjEFat)?L!exhyjz$9Q=SEPM@J4aR2x%AeJ!p{n6&5(WRs2i}+=QFjp9q(QH|^ z9Vj*fZ7%?2sY^v@Kes-(+iE=L3UK=-74JaMBQ#VHnVGXLr}Iyfdq`2;UDP$p(=Y*E zAyRl4LjX7{bKr5t+z5H)8a!9X94t=7mh!J^ATKqG5DMg^2GT=9nbZGvVhyv33a(lf z;*0YUd%iLeL%TvxH1-f6kL()4*}h<-|409451K*zx*L=tm@x35>22~se>m{!-ao8~ zR!wm}-b`5~fViTBo_j(=C_sSsg*pK5;l>Cb0QwaCGod1uV1GH~|21>@tMR|x3jw^J zojFK)ME|sk_fH{y_sqWu@%J>1laXsV>11BT=VsF13C8mcx6T52Le4b>ASC~{*LeJ& z@h$|8`?3atdb5zMt$HM6!QyM5)0Y^ZIcu^0^m0~92iB(NU8(mKO#d!fX#Qn=?W_O$ zT>RxH@n0_h4Lz|2`W>3pq#=5!#BdJ0Ldc5ATx4~A{={=9;(#j%udTiQplCY=7bDyx!Y>-@LnZPp4WREjHu0l7zz?FQ7S5=jRI^+Y$L^o|(Pw3D8C|GyUIg z%gm>2Um}0@+2?6>>m2z&ZJ8zw^+aFCDO`zvGyZ+L?`vueN%f&SF1dW2XRqAz(dnUk zMN|NAOz22XoZ0us`lsu3>NekQisJdTMB$WYM6ke8ClOGqFRdM%2bOY$&suTsb9#$m z-1)c~*MPje|^0A#??dx+4jI6h6Ue0zxmLg%yV}|$U`sT ztJVK{4353N*$rHaIeT0Br%cCSP!;-l)2|x0-_di<@0paEKSi$X=@sJz{vEGGSb$m1 z`N(R4ll`TIOFLiL)-OxpwUXcI>)s302^<6|s@u5cUi^N)Y=u*6%F}z&^zkt~Z-X>S|y2zTumz zd3R&@vG;pwUQe}tW<5s+lyodb!AV_BJqR_aACh>s+a_5QVaBgNR@IZuI$J+!>tC`g zU%TA5dTt0XML~BmBFX~?tHpA_o{TSWlMwPq{Ub!}cmex(Jt8+Rz&-+ss6HKFp;c{wwEF6aa? fjN?`cKK*A_^W!t|6+M*zI``Al)z4*}Q$iB}6cuwk literal 0 HcmV?d00001 From 8dc05cdba35063e5a9218fa71488c13b008cb4c9 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 25 Nov 2025 23:33:34 +0000 Subject: [PATCH 037/177] rp2/main: Add guard around machine_i2s_init0(). Add a #if MICROPY_PY_MACHINE_I2S guard around the call to machine_i2s_init0() in ports/rp2/main.c. This matches the existing guard around machine_i2s_deinit_all() in the same function. Signed-off-by: David Lechner --- ports/rp2/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/rp2/main.c b/ports/rp2/main.c index f01522f243807..50946b0a5ca2d 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -192,7 +192,9 @@ int main(int argc, char **argv) { machine_pin_init(); rp2_pio_init(); rp2_dma_init(); + #if MICROPY_PY_MACHINE_I2S machine_i2s_init0(); + #endif #if MICROPY_PY_BLUETOOTH mp_bluetooth_hci_init(); From 1ef76c7fba267753aa445678a36608a747f26334 Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Fri, 21 Nov 2025 10:51:30 -0700 Subject: [PATCH 038/177] rp2: Add HSTX alternate function. A follow up to 40df95357c7b3270dc60421a0078fd73b122473f / PR #17692, this commit adds the HSTX alternate pin function for GPIO12-19 on the RP2350. Signed-off-by: Dryw Wade --- ports/rp2/boards/make-pins.py | 4 +- ports/rp2/boards/rp2040_af.csv | 62 ++++++++++----------- ports/rp2/boards/rp2350_af.csv | 62 ++++++++++----------- ports/rp2/boards/rp2350b_af.csv | 98 ++++++++++++++++----------------- ports/rp2/machine_pin.c | 1 + 5 files changed, 114 insertions(+), 113 deletions(-) diff --git a/ports/rp2/boards/make-pins.py b/ports/rp2/boards/make-pins.py index 07ce052bba3ca..9f48806ffca8f 100755 --- a/ports/rp2/boards/make-pins.py +++ b/ports/rp2/boards/make-pins.py @@ -60,7 +60,7 @@ def add_af(self, af_idx, _af_name, af): m = re.match("([A-Z][A-Z0-9][A-Z]+)(([0-9]+)(_.*)?)?", af) af_fn = m.group(1) af_unit = int(m.group(3)) if m.group(3) is not None else 0 - if af_idx == 10: + if af_idx == 11: # AF11 uses UART_AUX in lieu of UART af_fn = "UART_AUX" if af_fn.startswith("QMI"): @@ -74,7 +74,7 @@ def add_af(self, af_idx, _af_name, af): # pin can only be I2C0 _or_ I2C1, both sharing the same AF # index), so each PIO unit has a distinct AF index. af_fn = "{:s}{:d}".format(af_fn, af_unit) - self._afs.append((af_idx + 1, af_fn, af_unit, af)) + self._afs.append((af_idx, af_fn, af_unit, af)) # This will be called at the start of the output (after the prefix). Use # it to emit the af objects (via the AF() macro in rp2_prefix.c). diff --git a/ports/rp2/boards/rp2040_af.csv b/ports/rp2/boards/rp2040_af.csv index 454f7ed2d12ab..13ccca28265ac 100644 --- a/ports/rp2/boards/rp2040_af.csv +++ b/ports/rp2/boards/rp2040_af.csv @@ -1,31 +1,31 @@ -Pin,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9 -GPIO0,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,,USB_OVCUR_DET -GPIO1,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,,USB_VBUS_DET -GPIO2,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,,USB_VBUS_EN -GPIO3,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,,USB_OVCUR_DET -GPIO4,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,,USB_VBUS_DET -GPIO5,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,,USB_VBUS_EN -GPIO6,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,,USB_OVCUR_DET -GPIO7,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,,USB_VBUS_DET -GPIO8,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,,USB_VBUS_EN -GPIO9,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,,USB_OVCUR_DET -GPIO10,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,,USB_VBUS_DET -GPIO11,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,,USB_VBUS_EN -GPIO12,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,,USB_OVCUR_DET -GPIO13,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,,USB_VBUS_DET -GPIO14,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM7_A,SIO,PIO0,PIO1,,USB_VBUS_EN -GPIO15,SPI1_TX,UART0_RTS,I2C1_SCL,PWM7_B,SIO,PIO0,PIO1,,USB_OVCUR_DET -GPIO16,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,,USB_VBUS_DET -GPIO17,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,,USB_VBUS_EN -GPIO18,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,,USB_OVCUR_DET -GPIO19,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,,USB_VBUS_DET -GPIO20,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,GPCK_GPIN0,USB_VBUS_EN -GPIO21,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,GPCK_GPOUT0,USB_OVCUR_DET -GPIO22,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,GPCK_GPIN1,USB_VBUS_DET -GPIO23,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,GPCK_GPOUT1,USB_VBUS_EN -GPIO24,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,GPCK_GPOUT2,USB_OVCUR_DET -GPIO25,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,GPCK_GPOUT3,USB_VBUS_DET -GPIO26,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,,USB_VBUS_EN -GPIO27,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,,USB_OVCUR_DET -GPIO28,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,,USB_VBUS_DET -GPIO29,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,,USB_VBUS_EN +Pin,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9 +GPIO0,,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,,USB_OVCUR_DET +GPIO1,,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,,USB_VBUS_DET +GPIO2,,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,,USB_VBUS_EN +GPIO3,,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,,USB_OVCUR_DET +GPIO4,,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,,USB_VBUS_DET +GPIO5,,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,,USB_VBUS_EN +GPIO6,,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,,USB_OVCUR_DET +GPIO7,,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,,USB_VBUS_DET +GPIO8,,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,,USB_VBUS_EN +GPIO9,,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,,USB_OVCUR_DET +GPIO10,,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,,USB_VBUS_DET +GPIO11,,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,,USB_VBUS_EN +GPIO12,,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,,USB_OVCUR_DET +GPIO13,,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,,USB_VBUS_DET +GPIO14,,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM7_A,SIO,PIO0,PIO1,,USB_VBUS_EN +GPIO15,,SPI1_TX,UART0_RTS,I2C1_SCL,PWM7_B,SIO,PIO0,PIO1,,USB_OVCUR_DET +GPIO16,,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,,USB_VBUS_DET +GPIO17,,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,,USB_VBUS_EN +GPIO18,,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,,USB_OVCUR_DET +GPIO19,,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,,USB_VBUS_DET +GPIO20,,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,GPCK_GPIN0,USB_VBUS_EN +GPIO21,,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,GPCK_GPOUT0,USB_OVCUR_DET +GPIO22,,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,GPCK_GPIN1,USB_VBUS_DET +GPIO23,,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,GPCK_GPOUT1,USB_VBUS_EN +GPIO24,,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,GPCK_GPOUT2,USB_OVCUR_DET +GPIO25,,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,GPCK_GPOUT3,USB_VBUS_DET +GPIO26,,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,,USB_VBUS_EN +GPIO27,,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,,USB_OVCUR_DET +GPIO28,,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,,USB_VBUS_DET +GPIO29,,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,,USB_VBUS_EN diff --git a/ports/rp2/boards/rp2350_af.csv b/ports/rp2/boards/rp2350_af.csv index cca30d9e74c8c..285c175bbde7e 100644 --- a/ports/rp2/boards/rp2350_af.csv +++ b/ports/rp2/boards/rp2350_af.csv @@ -1,31 +1,31 @@ -Pin,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11 -GPIO0,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_OVCUR_DET, -GPIO1,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,TRACECLK,USB_VBUS_DET, -GPIO2,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,TRACEDATA0,USB_VBUS_EN,UART0_TX -GPIO3,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,TRACEDATA1,USB_OVCUR_DET,UART0_RX -GPIO4,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,TRACEDATA2,USB_VBUS_DET, -GPIO5,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,TRACEDATA3,USB_VBUS_EN, -GPIO6,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_TX -GPIO7,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_RX -GPIO8,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_EN, -GPIO9,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, -GPIO10,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_TX -GPIO11,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_RX -GPIO12,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_OVCUR_DET, -GPIO13,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_VBUS_DET, -GPIO14,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM7_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_EN,UART0_TX -GPIO15,SPI1_TX,UART0_RTS,I2C1_SCL,PWM7_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_OVCUR_DET,UART0_RX -GPIO16,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, -GPIO17,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, -GPIO18,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART0_TX -GPIO19,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_DET,UART0_RX -GPIO20,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_VBUS_EN, -GPIO21,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_OVCUR_DET, -GPIO22,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_DET,UART1_TX -GPIO23,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_VBUS_EN,UART1_RX -GPIO24,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT2,USB_OVCUR_DET, -GPIO25,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT3,USB_VBUS_DET, -GPIO26,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_TX -GPIO27,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_RX -GPIO28,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, -GPIO29,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +Pin,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11 +GPIO0,,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_OVCUR_DET, +GPIO1,,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,TRACECLK,USB_VBUS_DET, +GPIO2,,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,TRACEDATA0,USB_VBUS_EN,UART0_TX +GPIO3,,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,TRACEDATA1,USB_OVCUR_DET,UART0_RX +GPIO4,,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,TRACEDATA2,USB_VBUS_DET, +GPIO5,,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,TRACEDATA3,USB_VBUS_EN, +GPIO6,,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_TX +GPIO7,,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_RX +GPIO8,,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_EN, +GPIO9,,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, +GPIO10,,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_TX +GPIO11,,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_RX +GPIO12,HSTX,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_OVCUR_DET, +GPIO13,HSTX,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_VBUS_DET, +GPIO14,HSTX,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM7_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_EN,UART0_TX +GPIO15,HSTX,SPI1_TX,UART0_RTS,I2C1_SCL,PWM7_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_OVCUR_DET,UART0_RX +GPIO16,HSTX,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO17,HSTX,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO18,HSTX,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART0_TX +GPIO19,HSTX,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_DET,UART0_RX +GPIO20,,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_VBUS_EN, +GPIO21,,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_OVCUR_DET, +GPIO22,,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_DET,UART1_TX +GPIO23,,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_VBUS_EN,UART1_RX +GPIO24,,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT2,USB_OVCUR_DET, +GPIO25,,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT3,USB_VBUS_DET, +GPIO26,,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_TX +GPIO27,,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_RX +GPIO28,,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO29,,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, diff --git a/ports/rp2/boards/rp2350b_af.csv b/ports/rp2/boards/rp2350b_af.csv index 3b9053f58cd4d..d30762f5853f7 100644 --- a/ports/rp2/boards/rp2350b_af.csv +++ b/ports/rp2/boards/rp2350b_af.csv @@ -1,49 +1,49 @@ -Pin,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11 -GPIO0,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_OVCUR_DET, -GPIO1,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,TRACECLK,USB_VBUS_DET, -GPIO2,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,TRACEDATA0,USB_VBUS_EN,UART0_TX -GPIO3,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,TRACEDATA1,USB_OVCUR_DET,UART0_RX -GPIO4,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,TRACEDATA2,USB_VBUS_DET, -GPIO5,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,TRACEDATA3,USB_VBUS_EN, -GPIO6,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_TX -GPIO7,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_RX -GPIO8,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_EN, -GPIO9,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, -GPIO10,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_TX -GPIO11,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_RX -GPIO12,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_OVCUR_DET, -GPIO13,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_VBUS_DET, -GPIO14,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM7_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_EN,UART0_TX -GPIO15,SPI1_TX,UART0_RTS,I2C1_SCL,PWM7_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_OVCUR_DET,UART0_RX -GPIO16,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, -GPIO17,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, -GPIO18,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART0_TX -GPIO19,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_DET,UART0_RX -GPIO20,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_VBUS_EN, -GPIO21,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_OVCUR_DET, -GPIO22,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_DET,UART1_TX -GPIO23,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_VBUS_EN,UART1_RX -GPIO24,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT2,USB_OVCUR_DET, -GPIO25,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT3,USB_VBUS_DET, -GPIO26,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_TX -GPIO27,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_RX -GPIO28,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, -GPIO29,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, -GPIO30,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM7_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART0_TX -GPIO31,SPI1_TX,UART0_RTS,I2C1_SCL,PWM7_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART0_RX -GPIO32,SPI0_RX,UART0_TX,I2C0_SDA,PWM8_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, -GPIO33,SPI0_CS,UART0_RX,I2C0_SCL,PWM8_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, -GPIO34,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM9_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART0_TX -GPIO35,SPI0_TX,UART0_RTS,I2C1_SCL,PWM9_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART0_RX -GPIO36,SPI0_RX,UART1_TX,I2C0_SDA,PWM10_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, -GPIO37,SPI0_CS,UART1_RX,I2C0_SCL,PWM10_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, -GPIO38,SPI0_SCK,UART1_CTS,I2C1_SCL,PWM11_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_TX -GPIO39,SPI0_TX,UART1_RTS,I2C1_SCL,PWM11_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_RX -GPIO40,SPI1_RX,UART1_TX,I2C0_SDA,PWM8_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, -GPIO41,SPI1_CS,UART1_RX,I2C0_SCL,PWM8_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, -GPIO42,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM9_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_TX -GPIO43,SPI1_TX,UART1_RTS,I2C1_SCL,PWM9_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_RX -GPIO44,SPI1_RX,UART0_TX,I2C0_SDA,PWM10_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, -GPIO45,SPI1_CS,UART0_RX,I2C0_SCL,PWM10_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, -GPIO46,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM11_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART0_TX -GPIO47,SPI1_TX,UART0_RTS,I2C1_SCL,PWM11_B,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_EN,UART0_RX +Pin,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11 +GPIO0,,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_OVCUR_DET, +GPIO1,,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,TRACECLK,USB_VBUS_DET, +GPIO2,,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,TRACEDATA0,USB_VBUS_EN,UART0_TX +GPIO3,,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,TRACEDATA1,USB_OVCUR_DET,UART0_RX +GPIO4,,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,TRACEDATA2,USB_VBUS_DET, +GPIO5,,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,TRACEDATA3,USB_VBUS_EN, +GPIO6,,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_TX +GPIO7,,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_RX +GPIO8,,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_EN, +GPIO9,,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, +GPIO10,,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_TX +GPIO11,,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_RX +GPIO12,HSTX,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_OVCUR_DET, +GPIO13,HSTX,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_VBUS_DET, +GPIO14,HSTX,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM7_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_EN,UART0_TX +GPIO15,HSTX,SPI1_TX,UART0_RTS,I2C1_SCL,PWM7_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_OVCUR_DET,UART0_RX +GPIO16,HSTX,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO17,HSTX,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO18,HSTX,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART0_TX +GPIO19,HSTX,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_DET,UART0_RX +GPIO20,,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_VBUS_EN, +GPIO21,,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_OVCUR_DET, +GPIO22,,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_DET,UART1_TX +GPIO23,,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_VBUS_EN,UART1_RX +GPIO24,,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT2,USB_OVCUR_DET, +GPIO25,,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT3,USB_VBUS_DET, +GPIO26,,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_TX +GPIO27,,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_RX +GPIO28,,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO29,,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO30,,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM7_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART0_TX +GPIO31,,SPI1_TX,UART0_RTS,I2C1_SCL,PWM7_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART0_RX +GPIO32,,SPI0_RX,UART0_TX,I2C0_SDA,PWM8_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO33,,SPI0_CS,UART0_RX,I2C0_SCL,PWM8_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, +GPIO34,,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM9_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART0_TX +GPIO35,,SPI0_TX,UART0_RTS,I2C1_SCL,PWM9_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART0_RX +GPIO36,,SPI0_RX,UART1_TX,I2C0_SDA,PWM10_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, +GPIO37,,SPI0_CS,UART1_RX,I2C0_SCL,PWM10_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO38,,SPI0_SCK,UART1_CTS,I2C1_SCL,PWM11_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_TX +GPIO39,,SPI0_TX,UART1_RTS,I2C1_SCL,PWM11_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_RX +GPIO40,,SPI1_RX,UART1_TX,I2C0_SDA,PWM8_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO41,,SPI1_CS,UART1_RX,I2C0_SCL,PWM8_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO42,,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM9_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_TX +GPIO43,,SPI1_TX,UART1_RTS,I2C1_SCL,PWM9_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_RX +GPIO44,,SPI1_RX,UART0_TX,I2C0_SDA,PWM10_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO45,,SPI1_CS,UART0_RX,I2C0_SCL,PWM10_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, +GPIO46,,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM11_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART0_TX +GPIO47,,SPI1_TX,UART0_RTS,I2C1_SCL,PWM11_B,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_EN,UART0_RX diff --git a/ports/rp2/machine_pin.c b/ports/rp2/machine_pin.c index e1a184f322cbb..336e6ff5a233d 100644 --- a/ports/rp2/machine_pin.c +++ b/ports/rp2/machine_pin.c @@ -521,6 +521,7 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_ALT_GPCK), MP_ROM_INT(GPIO_FUNC_GPCK) }, { MP_ROM_QSTR(MP_QSTR_ALT_USB), MP_ROM_INT(GPIO_FUNC_USB) }, #if PICO_RP2350 + { MP_ROM_QSTR(MP_QSTR_ALT_HSTX), MP_ROM_INT(GPIO_FUNC_HSTX) }, { MP_ROM_QSTR(MP_QSTR_ALT_XIP_CS1), MP_ROM_INT(GPIO_FUNC_XIP_CS1) }, { MP_ROM_QSTR(MP_QSTR_ALT_CORESIGHT_TRACE), MP_ROM_INT(GPIO_FUNC_CORESIGHT_TRACE) }, { MP_ROM_QSTR(MP_QSTR_ALT_UART_AUX), MP_ROM_INT(GPIO_FUNC_UART_AUX) }, From b24c9cf2a9d8b0d12a17ea3c7d83b133483a7024 Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Mon, 24 Nov 2025 14:00:34 -0700 Subject: [PATCH 039/177] rp2/rp2_dma: Properly close DMA channels. Clears the control registers and aborts the closed channel upon a call to `.close()` and `.__del__()` (GC collect). Fixes issue #18446. Signed-off-by: Dryw Wade --- ports/rp2/rp2_dma.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/rp2/rp2_dma.c b/ports/rp2/rp2_dma.c index 94c61e226e695..471cf24ea2ce5 100644 --- a/ports/rp2/rp2_dma.c +++ b/ports/rp2/rp2_dma.c @@ -427,6 +427,14 @@ static mp_obj_t rp2_dma_close(mp_obj_t self_in) { uint8_t channel = self->channel; if (channel != CHANNEL_CLOSED) { + // Reset this channel's registers to their default values (zeros). + dma_channel_config config = { .ctrl = 0 }; + dma_channel_configure(channel, &config, NULL, NULL, 0, false); + + // Abort this channel. Must be done after clearing EN bit in control + // register due to errata RP2350-E5. + dma_channel_abort(channel); + // Clean up interrupt handler to ensure garbage collection mp_irq_obj_t *irq = MP_STATE_PORT(rp2_dma_irq_obj[channel]); MP_STATE_PORT(rp2_dma_irq_obj[channel]) = MP_OBJ_NULL; From 2db2f536acd1b5701ca23d9cd065ef5479adf467 Mon Sep 17 00:00:00 2001 From: Anson Mansfield Date: Fri, 21 Mar 2025 17:03:27 +0000 Subject: [PATCH 040/177] rp2/rp2_pio: Fix support for pin wrapping and RP2350B upper-bank pins. On RP2350B where there are more than 32 pins, using `pio_sm_set_pins_with_mask()` and `pio_sm_set_pindirs_with_mask()` is not correct because their arguments are `uint32_t` and higher bits get lost when `pio.gpio_base(16)` is used. This commit fixes the issue by using the 64-bit API functions on RP2350B. It also makes sure pin wrapping is supported, i.e. using [30, 31, 0, 1] or [46, 47, 16, 17] as contiguous pin ranges for a PIO program. Fixes issue #16199. Signed-off-by: Anson Mansfield --- ports/rp2/rp2_pio.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/ports/rp2/rp2_pio.c b/ports/rp2/rp2_pio.c index 611e74a1587d2..81351431cb937 100644 --- a/ports/rp2/rp2_pio.c +++ b/ports/rp2/rp2_pio.c @@ -271,12 +271,32 @@ static void asm_pio_get_pins(PIO pio, const char *type, mp_obj_t prog_pins, mp_o } } +static inline uint32_t rotl32a(const uint32_t x, const int k) { + return (x << k) | (x >> (32 - k)); +} + static void asm_pio_init_gpio(PIO pio, uint32_t sm, asm_pio_config_t *config) { - uint32_t pinmask = ((1 << config->count) - 1) << (config->base - pio_get_gpio_base(pio)); - pio_sm_set_pins_with_mask(pio, sm, config->pinvals << (config->base - pio_get_gpio_base(pio)), pinmask); - pio_sm_set_pindirs_with_mask(pio, sm, config->pindirs << (config->base - pio_get_gpio_base(pio)), pinmask); - for (size_t i = 0; i < config->count; ++i) { - gpio_set_function(config->base + i, GPIO_FUNC_PIO0 + pio_get_index(pio)); + uint gpio_base = pio_get_gpio_base(pio); + uint32_t pinmask = rotl32a(~(-1 << config->count), config->base - gpio_base); + uint32_t pinvals = rotl32a(config->pinvals, config->base - gpio_base); + uint32_t pindirs = rotl32a(config->pindirs, config->base - gpio_base); + + #if !PICO_PIO_USE_GPIO_BASE + // optimization: avoid 64-bit arithmetic on RP2040 and RP2350A + pio_sm_set_pins_with_mask(pio, sm, pinvals, pinmask); + pio_sm_set_pindirs_with_mask(pio, sm, pindirs, pinmask); + #else + uint64_t pinmask64 = (uint64_t)pinmask << gpio_base; + uint64_t pinvals64 = (uint64_t)pinvals << gpio_base; + uint64_t pindirs64 = (uint64_t)pindirs << gpio_base; + pio_sm_set_pins_with_mask64(pio, sm, pinvals64, pinmask64); + pio_sm_set_pindirs_with_mask64(pio, sm, pindirs64, pinmask64); + #endif + + for (size_t i = 0; i < 32; ++i) { + if (pinmask & (1 << i)) { + gpio_set_function(gpio_base + i, GPIO_FUNC_PIO0 + pio_get_index(pio)); + } } } From e6a7dc111438d9e9d76f2d6c9384373c382d9c27 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 30 Nov 2025 16:20:56 +1100 Subject: [PATCH 041/177] rp2/mpconfigport: Enable MD5, SHA1 and cryptolib on all boards. This fixes a regression introduced by PR #17926 / commit b5fcb33eaa682bb666c839cd4fb301175cc3564f which accidentally disabled `hashlib.sha1` and the `cryptolib` module on rp2 boards that don't have networking enabled, eg RPI_PICO. `hashlib.md5` is enabled to keep the configuration the same as boards that do have networking enabled. Signed-off-by: Damien George --- ports/rp2/mpconfigport.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 6ce90e4f1f473..8cc12aff1c263 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -151,6 +151,9 @@ #define MICROPY_PY_OS_URANDOM (1) #define MICROPY_PY_RE_MATCH_GROUPS (1) #define MICROPY_PY_RE_MATCH_SPAN_START_END (1) +#define MICROPY_PY_HASHLIB_MD5 (1) +#define MICROPY_PY_HASHLIB_SHA1 (1) +#define MICROPY_PY_CRYPTOLIB (1) #define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1) #define MICROPY_PY_TIME_TIME_TIME_NS (1) #define MICROPY_PY_TIME_INCLUDEFILE "ports/rp2/modtime.c" From 797925c057bddc855b089397cb7fba024341caef Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 3 Dec 2025 10:39:29 +1100 Subject: [PATCH 042/177] esp32: Fix board images for ESP32_GENERIC_[C2|C5|P4]. These recently added boards had copy-paste image names, change them to match the images pending addition to micropython-media. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/boards/ESP32_GENERIC_C2/board.json | 2 +- ports/esp32/boards/ESP32_GENERIC_C5/board.json | 2 +- ports/esp32/boards/ESP32_GENERIC_P4/board.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/esp32/boards/ESP32_GENERIC_C2/board.json b/ports/esp32/boards/ESP32_GENERIC_C2/board.json index c496396ef1aea..ab05c93881d0a 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C2/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_C2/board.json @@ -12,7 +12,7 @@ "WiFi" ], "images": [ - "esp32c2_devkitmini.jpg" + "esp8684_devkitc.jpg" ], "mcu": "esp32c2", "product": "ESP32-C2", diff --git a/ports/esp32/boards/ESP32_GENERIC_C5/board.json b/ports/esp32/boards/ESP32_GENERIC_C5/board.json index 371da3929c52d..9ee69f0ba654a 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C5/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_C5/board.json @@ -12,7 +12,7 @@ "WiFi" ], "images": [ - "esp32c5_devkitmini.jpg" + "esp32c5_devkitc.jpg" ], "mcu": "esp32c5", "product": "ESP32-C5", diff --git a/ports/esp32/boards/ESP32_GENERIC_P4/board.json b/ports/esp32/boards/ESP32_GENERIC_P4/board.json index 00761d511f701..f0b5478de31bd 100644 --- a/ports/esp32/boards/ESP32_GENERIC_P4/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_P4/board.json @@ -11,7 +11,7 @@ "WiFi" ], "images": [ - "esp32p4_devkitmini.jpg" + "esp32_p4_function_ev_board.jpg" ], "mcu": "esp32p4", "product": "ESP32-P4", From a94f8114f3bd6a01093b99a75ef62ff619a61a23 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 3 Dec 2025 10:41:47 +1100 Subject: [PATCH 043/177] esp32/boards/ESP32_GENERIC_P4: Add variants to board.json and .md files. Add variants to the new ESP32_GENERIC_P4 board, so they appear on the download page. Signed-off-by: Damien George --- ports/esp32/boards/ESP32_GENERIC_P4/board.json | 4 ++++ ports/esp32/boards/ESP32_GENERIC_P4/board.md | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/ports/esp32/boards/ESP32_GENERIC_P4/board.json b/ports/esp32/boards/ESP32_GENERIC_P4/board.json index f0b5478de31bd..480b4fbfd1d19 100644 --- a/ports/esp32/boards/ESP32_GENERIC_P4/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_P4/board.json @@ -17,5 +17,9 @@ "product": "ESP32-P4", "thumbnail": "", "url": "https://www.espressif.com/en/products/modules", + "variants": { + "C5_WIFI": "Support for external C5 WiFi/BLE", + "C6_WIFI": "Support for external C6 WiFi/BLE" + }, "vendor": "Espressif" } diff --git a/ports/esp32/boards/ESP32_GENERIC_P4/board.md b/ports/esp32/boards/ESP32_GENERIC_P4/board.md index 22a450cb89829..29f72832aa62a 100644 --- a/ports/esp32/boards/ESP32_GENERIC_P4/board.md +++ b/ports/esp32/boards/ESP32_GENERIC_P4/board.md @@ -1,2 +1,11 @@ The following firmware is applicable to most development boards based on ESP32-P4, and the development boards must be equipped with at least 16 MiB external SPI Flash. + +This board has multiple variants available: + +* If your board has a standalone ESP32-P4 processor (or the board has a coprocessor but + you do not want to use it) then choose the generic variant (first heading below). +* If your board has an external ESP32-C5 coprocessor for WiFi and BLE then choose the + "C5 WiFi/BLE" variant. +* If your board has an external ESP32-C6 coprocessor for WiFi and BLE then choose the + "C6 WiFi/BLE" variant. From 5ea9a2662d9f530f88d566aec76cfaef5f6d392d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 3 Dec 2025 11:23:34 +1100 Subject: [PATCH 044/177] esp32: Don't disable component manager when updating submodules. - Reverts the change from ec527a1 - since later change cccac2cc we no longer exit CMake early to get the submodule list, so it's OK to run component manager during this phase. - Fixes issue where "make submodules BOARD=ESP32_GENERIC_S3" (or any other board that depends on USB) would fail due to missing component(s). This work was funded through GitHub Sponsors. 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 d0984127ce344..e767f96c29072 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -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: - $(Q)IDF_COMPONENT_MANAGER=0 idf.py $(IDFPY_FLAGS) -B $(BUILD)/submodules -D UPDATE_SUBMODULES=1 reconfigure + $(Q)idf.py $(IDFPY_FLAGS) -B $(BUILD)/submodules -D UPDATE_SUBMODULES=1 reconfigure From 0b1a6bebae857053a0c6cc865e3ca38a8f5f28df Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 5 Nov 2025 11:22:18 +1100 Subject: [PATCH 045/177] docs/library/machine.Timer: Explain the id parameter in more detail. As noted in discussion on PR #18263, the id parameter is optional on ports that support virtual timers. Add some more general explanation of hardware vs virtual timers, and remove redundant documentation about timer callbacks in favour of the isr_rules page. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- docs/library/machine.Timer.rst | 60 +++++++++++++++++------------- docs/library/machine.TimerWiPy.rst | 13 ++----- docs/library/machine.rst | 8 ---- 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/docs/library/machine.Timer.rst b/docs/library/machine.Timer.rst index 5d228ea7b67b5..69eea9d8d1f4f 100644 --- a/docs/library/machine.Timer.rst +++ b/docs/library/machine.Timer.rst @@ -4,37 +4,47 @@ class Timer -- control hardware timers ====================================== -Hardware timers deal with timing of periods and events. Timers are perhaps -the most flexible and heterogeneous kind of hardware in MCUs and SoCs, -differently greatly from a model to a model. MicroPython's Timer class -defines a baseline operation of executing a callback with a given period -(or once after some delay), and allow specific boards to define more -non-standard behaviour (which thus won't be portable to other boards). +Timer class provides the ability to trigger a Python callback function after an +expiry time, or periodically at a regular interval. -See discussion of :ref:`important constraints ` on -Timer callbacks. - -.. note:: - - Memory can't be allocated inside irq handlers (an interrupt) and so - exceptions raised within a handler don't give much information. See - :func:`micropython.alloc_emergency_exception_buf` for how to get around - this limitation, which applies to all callbacks of Timers created with - ``hard=True``. +The available features and restrictions of Timer objects vary depending on the +MicroPython board and port. If you are using a WiPy board please refer to :ref:`machine.TimerWiPy ` instead of this class. +Timer Types +----------- + +There are two types of Timer in MicroPython, but not all ports support both: + +- Virtual timers. These are managed in software, and are generally more + flexible. Multiple virtual timers can be constructed and active at once. The + ``id`` of a virtual timer is ``-1``. Not all ports support virtual timers, but + it's recommended to use them when available. +- Hardware timers. Hardware timers have integer ``id`` values starting at ``0``. + The number of available ``id`` values is determined by the hardware. Hardware + timers may be more accurate for very fine sub-millisecond timing (especially + when ``hard=True`` is supported and set, see :ref:`isr_rules`.) Most + microcontroller ports support hardware timers, except Zephyr and RP2 which + only support virtual timers. + Constructors ------------ .. class:: Timer(id, /, ...) - Construct a new timer object of the given ``id``. ``id`` of -1 constructs a - virtual timer (if supported by a board). + Construct a new Timer object with the given ``id``. + + On ports which support virtual timers the ``id`` parameter is optional - the + default value is ``-1`` which constructs a virtual timer. + + On ports which support hardware timers, setting the ``id`` parameter to a + non-negative integer determines which timer to use. + ``id`` shall not be passed as a keyword argument. - See ``init`` for parameters of initialisation. + Any additional parameters are handled the same as :func:`Timer.init()`. Methods ------- @@ -73,22 +83,22 @@ Methods - ``callback`` - The callable to call upon expiration of the timer period. The callback must take one argument, which is passed the Timer object. + The ``callback`` argument shall be specified. Otherwise an exception will occur upon timer expiration: ``TypeError: 'NoneType' object isn't callable`` - ``hard`` can be one of: - - ``True`` - The callback will be executed in hard interrupt - context, which minimises delay and jitter but is subject to the - limitations described in :ref:`isr_rules` including being unable - to allocate on the heap. + - ``True`` - The callback will be executed in hard interrupt context, + which minimises delay and jitter but is subject to the limitations + described in :ref:`isr_rules`. Not all ports support hard interrupts, + see the port documentation for more information. - ``False`` - The callback will be scheduled as a soft interrupt, allowing it to allocate but possibly also introducing garbage-collection delays and jitter. - The default value of this option is port-specific for historical - reasons. + The default value of this parameter is port-specific for historical reasons. .. method:: Timer.deinit() diff --git a/docs/library/machine.TimerWiPy.rst b/docs/library/machine.TimerWiPy.rst index 54280a5994ad5..17215d502038c 100644 --- a/docs/library/machine.TimerWiPy.rst +++ b/docs/library/machine.TimerWiPy.rst @@ -18,16 +18,6 @@ defines a baseline operation of executing a callback with a given period (or once after some delay), and allow specific boards to define more non-standard behaviour (which thus won't be portable to other boards). -See discussion of :ref:`important constraints ` on -Timer callbacks. - -.. note:: - - Memory can't be allocated inside irq handlers (an interrupt) and so - exceptions raised within a handler don't give much information. See - :func:`micropython.alloc_emergency_exception_buf` for how to get around this - limitation. - Constructors ------------ @@ -134,6 +124,9 @@ Methods ``TimerWiPy.ONE_SHOT``. In the case that mode is ``TimerWiPy.PWM`` then trigger must be equal to ``TimerWiPy.MATCH``. + Note that callback handlers are hard interrupts, and the constraints described in :ref:`isr_rules` + apply when they are executed. + Returns a callback object. .. method:: timerchannel.freq([value]) diff --git a/docs/library/machine.rst b/docs/library/machine.rst index 69eda917e5abb..31acb74920c49 100644 --- a/docs/library/machine.rst +++ b/docs/library/machine.rst @@ -11,14 +11,6 @@ and unrestricted access to and control of hardware blocks on a system malfunction, lockups, crashes of your board, and in extreme cases, hardware damage. -.. _machine_callbacks: - -A note of callbacks used by functions and class methods of :mod:`machine` module: -all these callbacks should be considered as executing in an interrupt context. -This is true for both physical devices with IDs >= 0 and "virtual" devices -with negative IDs like -1 (these "virtual" devices are still thin shims on -top of real hardware and real hardware interrupts). See :ref:`isr_rules`. - Memory access ------------- From 3f796b687b4ca2b1bf7db7ed0220dbf6b8a55b14 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 4 Dec 2025 00:33:04 +1100 Subject: [PATCH 046/177] tools/autobuild: Make firmware destination directory configurable. The destination directory for the firmware built by `autobuild.sh` is currently hard-coded to `/tmp/autobuild-firmware-$$`. Now that there are many boards built by this script, the `/tmp` partition can run out of space. This commit makes the destination directory configurable via the `MICROPY_AUTOBUILD_DEST` environment variable. Signed-off-by: Damien George --- tools/autobuild/autobuild.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/autobuild/autobuild.sh b/tools/autobuild/autobuild.sh index 9c94d887f045e..ed748fc01e179 100755 --- a/tools/autobuild/autobuild.sh +++ b/tools/autobuild/autobuild.sh @@ -7,6 +7,9 @@ # - IDF_PATH_V50 must be set # - MICROPY_AUTOBUILD_MICROPYTHON_REPO must be set to location of micropython repository # - MICROPY_AUTOBUILD_MAKE must be set to the make command to use, eg "make -j2" +# - MICROPY_AUTOBUILD_DEST must be set to a directory name to place the output firmware +# (this directory will be created, and removed at the end if firmware is copied to a +# remote machine using MICROPY_AUTOBUILD_REMOTE_MACHINE and MICROPY_AUTOBUILD_REMOTE_DIR) # # Optional settings: # - MICROPY_AUTOBUILD_REMOTE_MACHINE can be set to a remote ssh machine to copy files to @@ -27,6 +30,11 @@ if [ -z "$MICROPY_AUTOBUILD_MAKE" ]; then exit 1 fi +if [ -z "$MICROPY_AUTOBUILD_DEST" ]; then + echo "must set MICROPY_AUTOBUILD_DEST" + exit 1 +fi + ######################################## # Initialisation @@ -37,7 +45,7 @@ AUTODIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" source ${AUTODIR}/build-boards.sh # make local directory to put firmware -LOCAL_FIRMWARE=/tmp/autobuild-firmware-$$ +LOCAL_FIRMWARE=${MICROPY_AUTOBUILD_DEST} mkdir -p ${LOCAL_FIRMWARE} # get latest MicroPython From 37bac07f5da59ac7352c28de813704831494e02b Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Nov 2025 16:30:12 +1100 Subject: [PATCH 047/177] tests/serial_test.py: Factor common reset code into send_script func. Signed-off-by: Damien George --- tests/serial_test.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/serial_test.py b/tests/serial_test.py index 455d2277f64dd..006e6fec9cedf 100755 --- a/tests/serial_test.py +++ b/tests/serial_test.py @@ -86,6 +86,8 @@ def drain_input(ser): def send_script(ser, script): + ser.write(b"\x03\x01\x04") # break, raw-repl, soft-reboot + drain_input(ser) chunk_size = 32 for i in range(0, len(script), chunk_size): ser.write(script[i : i + chunk_size]) @@ -109,8 +111,6 @@ def read_test(ser_repl, ser_data, bufsize, nbuf): READ_TIMEOUT_S = 2 # Load and run the read_test_script. - ser_repl.write(b"\x03\x01\x04") # break, raw-repl, soft-reboot - drain_input(ser_repl) script = bytes(read_test_script % (bufsize, nbuf), "ascii") send_script(ser_repl, script) @@ -166,8 +166,6 @@ def write_test(ser_repl, ser_data, bufsize, nbuf, verified): global test_passed # Load and run the write_test_script. - ser_repl.write(b"\x03\x01\x04") # break, raw-repl, soft-reboot - drain_input(ser_repl) if verified: script = write_test_script_verified else: From 9939eae599df0c285b5f4f998b00a72b37b0d748 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Nov 2025 16:30:43 +1100 Subject: [PATCH 048/177] tests/serial_test.py: Add a serial echo test. The existing `serial_test.py` script tests data in/out throughput and reliability. But it only tests data sizes that are a power of two, which may not catch certain errors with USB transmission (because FS packet size is 64 bytes). This commit adds a new echo sub-test to the `serial_test.py` script. It sends out data to the target and gets the target to echo it back, and then compares the result (the echo'd data should be equal to the sent data). It does this for data packets between 1 and 520 (inclusive) bytes, which covers USB FS and HS packet sizes (64 and 512 respectively). It uses random data for the test, but seeded by a constant seed so that it's deterministic. If there's an error then it prints out all the sent and echo'd data to make it easier to see where it went wrong (eg if the previous packet was repeated). Signed-off-by: Damien George --- tests/serial_test.py | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tests/serial_test.py b/tests/serial_test.py index 006e6fec9cedf..3b5940d91a907 100755 --- a/tests/serial_test.py +++ b/tests/serial_test.py @@ -8,12 +8,25 @@ # The `serial-device` will default to /dev/ttyACM0. import argparse +import random import serial import sys import time run_tests_module = __import__("run-tests") +echo_test_script = """ +import sys +bytes_min=%u +bytes_max=%u +repeat=%u +b=memoryview(bytearray(bytes_max)) +for n in range(bytes_min,bytes_max+1): + for _ in range(repeat): + n2 = sys.stdin.readinto(b[:n]) + sys.stdout.write(b[:n2]) +""" + read_test_script = """ bin = True try: @@ -100,6 +113,59 @@ def send_script(ser, script): raise TestError("could not send script", response) +def echo_test(ser_repl, ser_data): + global test_passed + + # Make the test data deterministic. + random.seed(0) + + # Set parameters for the test. + # Go just a bit above the size of a USB high-speed packet. + bytes_min = 1 + bytes_max = 520 + num_repeat = 1 + + # Load and run the write_test_script. + script = bytes(echo_test_script % (bytes_min, bytes_max, num_repeat), "ascii") + send_script(ser_repl, script) + + # A selection of printable bytes for echo data. + printable_bytes = list(range(48, 58)) + list(range(65, 91)) + list(range(97, 123)) + + # Write data to the device and record the echo'd data. + # Use a different selection of random printable characters for each + # echo, to make it easier to debug when the echo doesn't match. + num_errors = 0 + echo_results = [] + for num_bytes in range(bytes_min, bytes_max + 1): + print(f"DATA ECHO: {num_bytes} / {bytes_max}", end="\r") + for repeat in range(num_repeat): + rand_bytes = list(random.choice(printable_bytes) for _ in range(8)) + buf = bytes(random.choice(rand_bytes) for _ in range(num_bytes)) + ser_data.write(buf) + buf2 = ser_data.read(len(buf)) + match = buf == buf2 + num_errors += not match + echo_results.append((match, buf, buf2)) + if num_errors > 8: + # Stop early if there are too many errors. + break + ser_repl.write(b"\x03") + + # Print results. + if all(match for match, _, _ in echo_results): + print("DATA ECHO: OK for {}-{} bytes at a time".format(bytes_min, bytes_max)) + else: + test_passed = False + print("DATA ECHO: FAIL ") + for match, buf, buf2 in echo_results: + print(" sent", len(buf), buf) + if match: + print(" echo match") + else: + print(" echo", len(buf), buf2) + + def read_test(ser_repl, ser_data, bufsize, nbuf): global test_passed @@ -212,6 +278,12 @@ def do_test(dev_repl, dev_data=None, time_per_subtest=1): ser_repl = serial.Serial(dev_repl, baudrate=115200, timeout=1) ser_data = serial.Serial(dev_data, baudrate=115200, timeout=1) + # Do echo test first, and abort if it doesn't pass. + echo_test(ser_repl, ser_data) + if not test_passed: + return + + # Do read and write throughput test. for test_func, test_args, bufsize in ( (read_test, (), 256), (write_test, (True,), 128), From d357eff2bc83c7b00f7a044a255ca574976e0aa1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 2 Dec 2025 12:18:34 +1100 Subject: [PATCH 049/177] tests/net_inet: Update micropython.org certificate for SSL tests. Signed-off-by: Damien George --- tests/net_inet/mpycert.der | Bin 1290 -> 1290 bytes tests/net_inet/ssl_cert.py | 52 ++++++++++++++++++------------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/net_inet/mpycert.der b/tests/net_inet/mpycert.der index 0b0eabc9bc8135de25785cb8574ab78e03947aa9..f583b217ae5d960d2ba534fbbbf2a5b1e492777f 100644 GIT binary patch delta 850 zcmV-Y1Figu3W^FKFoFdJFoFW^paTK{0s;{L!V)q|aH+JBaoj-n;)NzWks!1IGLh9G zf7+NRbgjOW{yX3v;^j+qt1J?K?WY4Wem0lv=rNC_6O9X*E*{MRQ9VrMC~gT=F{-5^ zU2^m-2f|H2Jl_E$xp+7A8f7vx=7w>d1n-0hTfG7rnd_zj0Ge}b)w~n8%xOf2%um4?NqlV7^*QP8jLiDsxUZei#+W?``^y3*#^6q-} zC!XcaoGqgAf1DSs?-MsU(X_}cLDq)A>Fgo+SsQ=3I((m80i9w5!YI88tvt#Q0_+=; z_5oQG0JSOdEsk$7=$lNfcs}~v=?u}*lVbv>e~)$03D0VFNM$mT-nB%?g?986gp2~e zcI&h8;`wfbs8*5Tl1umD>Hv61k9Csc&p>jJ?- z@c8vkWFUK}<1xg*XbDG)*tvM1%1%nYX(TF3H+xY7YdSwti?h_ytyoTz?#o|q4KYbb zy2L-963HMv5rhYYwD|HkBdl=%K<9sre?~NdYEIAK4r5=sX_`j~5F-9dNVk!7RLhed z*h-7GdzuHqL{v@f1|5kwp8d%mZ_}suv*I-dNz|Jo6cBLfS)xA zUM;_Ln_?yxoRKjp3-f=?!(xkQ$9MF93;V`d^#f*0rcj&E@1^_#v|vpGOJPsNe;@~o zNU$;Www%$)t#=sQvd0g;;6@1E;7i`QxVFa@zBXw;hGP#cFy2ti?#B4t-r= zx2WvF=ks!1IF_G0F zf4YafTpIv6%gPGP)MpU+0X(HnrD4k^%2N9k0k^p*^(Y?gC$l6NIcEtB2<+fGuse4S zz4PTcR6z_*u0htBbly?h?|c7Py{NYn_TDko@^Z;?Y72vOm7GQZWE7e6vDqTo&8U*9 z942}{{_j`?dkB?*+i?$$`{OYQy9vHfe@$PHk#;>=ot}g8>BMHU;Ra?yNEm=+pf?zr zn}jl#v9i6y_!R)1Cy4vvV;7cenxdX~KE%NnGG0P~pV9p#`jaKy3iD!a+Z$ zShz36T$`o437+x(>#&76{iq7>MZ|dPWh%nYUp(I9TumGI3nwe83qp5OPkCw`0*Bd? z_5oQG#m`2h>h#0FdTf=%EwEmaFDB!;lVbv>e@^0wT?zvnkT}rwANg@y_`{E~H;8&~ zuq7YuzPH$h29D_ogw1llf%E*z&r?Y4z4mEB)TI$VHOLdPk{tzK>MqpS_({X9^_iBO z?8TQTRbT9 z47O}D2u;^G?MN*y1!%5-UGGfX*dRe)Xz{yR&EpQR3^9kBL*7Y4;m8C((L5W6MKEco zsd5f^5*#tuJ|kD%r%#~p9D&hyA8*#uxu~-P7J)fSpH;5XYUH!O>ZR46OQXrwe>-yX zO7>K%4E#9vV1VClHOh6NROBzbZWqZKr8l*rn!4=tz;#AXcc|!DR*_~cZr&+@)~1&Q zUSvhSEY9j=LPW5`Pp$B@+yly)n+bxIf(>JeX89R%_|>_I5aNlL2{rkm9KMosBXsGV cAJ;odnuJ&o!t/dev/null # The certificate is from Let's Encrypt: -# 1 s:C=US, O=Let's Encrypt, CN=R11 +# 1 s:C=US, O=Let's Encrypt, CN=R12 # i:C=US, O=Internet Security Research Group, CN=ISRG Root X1 # a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption # v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT @@ -17,39 +17,39 @@ # Then convert to hex format using: for i in range(0,len(data),40):print(data[i:i+40].hex()) ca_cert_chain = bytes.fromhex( - "30820506308202eea0030201020211008a7d3e13d62f30ef2386bd29076b34f8300d06092a864886" + "30820506308202eea003020102021100c212324b70a9b49171dc40f7e285263c300d06092a864886" "f70d01010b0500304f310b300906035504061302555331293027060355040a1320496e7465726e65" "742053656375726974792052657365617263682047726f7570311530130603550403130c49535247" "20526f6f74205831301e170d3234303331333030303030305a170d3237303331323233353935395a" "3033310b300906035504061302555331163014060355040a130d4c6574277320456e637279707431" - "0c300a0603550403130352313130820122300d06092a864886f70d01010105000382010f00308201" - "0a0282010100ba87bc5c1b0039cbca0acdd46710f9013ca54ea561cb26ca52fb1501b7b928f5281e" - "ed27b324183967090c08ece03ab03b770ebdf3e53954410c4eae41d69974de51dbef7bff58bda8b7" - "13f6de31d5f272c9726a0b8374959c4600641499f3b1d922d9cda892aa1c267a3ffeef58057b0895" - "81db710f8efbe33109bb09be504d5f8f91763d5a9d9e83f2e9c466b3e106664348188065a037189a" - "9b843297b1b2bdc4f815009d2788fbe26317966c9b27674bc4db285e69c279f0495ce02450e1c4bc" - "a105ac7b406d00b4c2413fa758b82fc55c9ba5bb099ef1feebb08539fda80aef45c478eb652ac2cf" - "5f3cdee35c4d1bf70b272baa0b4277534f796a1d87d90203010001a381f83081f5300e0603551d0f" + "0c300a0603550403130352313230820122300d06092a864886f70d01010105000382010f00308201" + "0a0282010100da982874adbe94fe3be01ee2e54b75ab2c127feda703327e3697ece8318fa5138d0b" + "992e1ecd01513d4ce5286e095531aaa5225d72f42d07c24d403cdf0123b97837f51a653234e68671" + "9d04ef84085bbd021a99eba601009a73906d8fa207a0d097d3da456181353d14f9c4c05f6adc0b96" + "1ab09fe32aeabd2ad698c79b71ab3b740f3cdbb260be5a4b4e18e9db2a735c8961659efeed3ca6cb" + "4e6fe49ef90046b3ff194d2a63b38e66c6188570c750656f3b74e548830f08585d2d239d5ea3fee8" + "db00a1d2f4e3194df2ee7af6279ee5cd9c2da2f27f9c17adef133739d1b4c82c41d686c0e9ec21f8" + "591b7fb93a7c9f5c019d6204c228bd0aad3cca10ec1b0203010001a381f83081f5300e0603551d0f" "0101ff040403020186301d0603551d250416301406082b0601050507030206082b06010505070301" - "30120603551d130101ff040830060101ff020100301d0603551d0e04160414c5cf46a4eaf4c3c07a" - "6c95c42db05e922f26e3b9301f0603551d2304183016801479b459e67bb6e5e40173800888c81a58" + "30120603551d130101ff040830060101ff020100301d0603551d0e0416041400b529f22d8e6f31e8" + "9b4cad783efadce90cd1d2301f0603551d2304183016801479b459e67bb6e5e40173800888c81a58" "f6e99b6e303206082b0601050507010104263024302206082b060105050730028616687474703a2f" "2f78312e692e6c656e63722e6f72672f30130603551d20040c300a3008060667810c010201302706" "03551d1f0420301e301ca01aa0188616687474703a2f2f78312e632e6c656e63722e6f72672f300d" - "06092a864886f70d01010b050003820201004ee2895d0a031c9038d0f51ff9715cf8c38fb237887a" - "6fb0251fedbeb7d886068ee90984cd72bf81f3fccacf5348edbdf66942d4a5113e35c813b2921d05" - "5fea2ed4d8f849c3adf599969cef26d8e1b4240b48204dfcd354b4a9c621c8e1361bff77642917b9" - "f04bef5deacd79d0bf90bfbe23b290da4aa9483174a9440be1e2f62d8371a4757bd294c10519461c" - "b98ff3c47448252a0de5f5db43e2db939bb919b41f2fdf6a0e8f31d3630fbb29dcdd662c3fb01b67" - "51f8413ce44db9acb8a49c6663f5ab85231dcc53b6ab71aedcc50171da36ee0a182a32fd09317c8f" - "f673e79c9cb54a156a77825acfda8d45fe1f2a6405303e73c2c60cb9d63b634aab4603fe99c04640" - "276063df503a0747d8154a9fea471f995a08620cb66c33084dd738ed482d2e0568ae805def4cdcd8" - "20415f68f1bb5acde30eb00c31879b43de4943e1c8043fd13c1b87453069a8a9720e79121c31d83e" - "2357dda74fa0f01c81d1771f6fd6d2b9a8b3031681394b9f55aed26ae4b3bfeaa5d59f4ba3c9d63b" - "72f34af654ab0cfc38f76080df6e35ca75a154e42fbc6e17c91aa537b5a29abaecf4c075464f77a8" - "e8595691662d6ede2981d6a697055e6445be2cceea644244b0c34fadf0b4dc03ca999b098295820d" - "638a66f91972f8d5b98910e289980935f9a21cbe92732374e99d1fd73b4a9a845810c2f3a7e235ec" - "7e3b45ce3046526bc0c0" + "06092a864886f70d01010b050003820201008f75d009cf6a7648653292deb544c88576f415848c02" + "bf76ebb3f1e2f96e84a85691e1924bf7e1ea0078488f7592e3e4467b1b602b20afa0ce14e5450d6a" + "e05286a4f3da1414a9a95ff16d46f952501740e9e41e7de61558fea98bfceff59e63e066e2c3773b" + "1f01872694ed4010dcb799ecdd57d35c7141ee30200004dc954b5028879992feaa8094b6060814f8" + "1c837e7440c5085a0c4f5cd1849dc4fddb59deee796e234d95f292d498296a5ceb02c142f0f8f54e" + "64207ba8e331c4c06809478bd8b978a0ca4e4abe69242a4b377b51036b3a3f528bb3d4d2ad584e93" + "eecb5f6f0d314948bac43f9f12c9203d11840785b4f8f23823ac710040e77f8d4634826a4ecfe00e" + "635fba699a47091022fe4b48b7917554cb931ee416eb53cf7bde364dbff6b1ebe64ae9333c8d69a2" + "98bea87fa3ab5fb654e84d96a9acf3b05acb1b7a3693249bce5852809f350a5e2dbf749b6226179c" + "9131290bf37fcdc3628b68c777f47f0bfbc659f503664ba6509bd0efa5fc02b4604d034b614fc520" + "078b48b031f5b69cd1c9ad7718dcb2c70fbee04608dee04bdeb9b8b6c716be36693f86684b748113" + "8950c56a7a02acc548a50e7d5d61e4cdd166a075c7055ee889b5631923bb50b490ecc275373e75a6" + "1b83252800214ec0d33acb9ceac08ff75fae51164610af0206eec0b657d40dac8cd8d7a0f3876ec3" + "e2cbe94ed4a17cfd763b" ) From d3391b7632d76a728c723ad58d74371677201531 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 5 Dec 2025 13:20:19 +1100 Subject: [PATCH 050/177] py/asmbase: Cast prior to bitwise invert when the type is widened. Add a cast to fix build error of mpy-cross on Windows CI. Prior to this fix the failure was: D:\a\micropython\micropython\py\asmbase.c(105,56): warning C4319: '~': zero extending 'unsigned int' to 'size_t' of greater size [D:\a\micropython\micropython\mpy-cross\mpy-cross.vcxproj] Signed-off-by: Damien George --- py/asmbase.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/asmbase.c b/py/asmbase.c index f1b823fa368f8..07dbf4430f9db 100644 --- a/py/asmbase.c +++ b/py/asmbase.c @@ -102,7 +102,7 @@ void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label) { // align must be a multiple of 2 void mp_asm_base_align(mp_asm_base_t *as, unsigned int align) { - as->code_offset = (as->code_offset + align - 1) & (~(align - 1)); + as->code_offset = (as->code_offset + align - 1) & (~(size_t)(align - 1)); } // this function assumes a little endian machine From 45385994752c1ee30d115bd77165449e918b3daf Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 5 Dec 2025 16:03:51 +1100 Subject: [PATCH 051/177] py/emitinlinerv32: Change mask arg of is_in_signed_mask to uint32_t. Prior to this change mpy-cross would fail to build under Windows with: D:\a\micropython\micropython\py\emitinlinerv32.c(398,40): warning C4319: '~': zero extending 'unsigned int' to 'mp_uint_t' of greater size [D:\a\micropython\micropython\mpy-cross\mpy-cross.vcxproj] Signed-off-by: Damien George --- py/emitinlinerv32.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py/emitinlinerv32.c b/py/emitinlinerv32.c index a5f4c49c29409..e81b152087de3 100644 --- a/py/emitinlinerv32.c +++ b/py/emitinlinerv32.c @@ -390,9 +390,9 @@ static const opcode_t OPCODES[] = { // 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) { +static bool is_in_signed_mask(uint32_t mask, mp_uint_t value) { + uint32_t leading_zeroes = mp_clz(mask); + if (leading_zeroes == 0) { return true; } mp_uint_t positive_mask = ~(mask & ~(1U << (31 - leading_zeroes))); From 84061266ece6ea9fae7da45e47cc6e1c51182c8a Mon Sep 17 00:00:00 2001 From: Anson Mansfield Date: Fri, 5 Dec 2025 15:26:25 -0500 Subject: [PATCH 052/177] py/builtinhelp: Don't print removed sentinel entries. This fixes the test used by the help function to iterate over its argument's attribute to use the proper `mp_map_slot_is_filled` function to check if a slot in the map is filled; the previous test only checked for `MP_OBJ_NULL` keys and would attempt to print the null value whenever a `MP_OBJ_SENTINEL` key marking a deleted entry was present. Fixes: #18061 Fixes: #18481 Signed-off-by: Anson Mansfield --- py/builtinhelp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/py/builtinhelp.c b/py/builtinhelp.c index dc4fe582f75ef..5a84b3d7d2456 100644 --- a/py/builtinhelp.c +++ b/py/builtinhelp.c @@ -152,9 +152,8 @@ static void mp_help_print_obj(const mp_obj_t obj) { } if (map != NULL) { for (uint i = 0; i < map->alloc; i++) { - mp_obj_t key = map->table[i].key; - if (key != MP_OBJ_NULL) { - mp_help_print_info_about_object(key, map->table[i].value); + if (mp_map_slot_is_filled(map, i)) { + mp_help_print_info_about_object(map->table[i].key, map->table[i].value); } } } From c25667f6a9de7b85cede4eb651cb5195c62eef80 Mon Sep 17 00:00:00 2001 From: Anson Mansfield Date: Fri, 5 Dec 2025 15:25:23 -0500 Subject: [PATCH 053/177] tests/basics/builtin_help.py: Test correct handling of deleted entries. This test reproduces the bug that gave rise to the esp32 segfaults in issues #18061 and #18481 on all platforms. Signed-off-by: Anson Mansfield --- tests/basics/builtin_help.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/basics/builtin_help.py b/tests/basics/builtin_help.py index 6ec39653fe993..3d4e4f26bd2bb 100644 --- a/tests/basics/builtin_help.py +++ b/tests/basics/builtin_help.py @@ -14,4 +14,10 @@ help(micropython) # help for a module help('modules') # list available modules +class A: + x = 1 + y = 2 + del x +help(A) + print('done') # so last bit of output is predictable From 4d7c2fd18638565768eef011ef5dc6752cf34a61 Mon Sep 17 00:00:00 2001 From: Yuuki NAGAO Date: Sun, 7 Dec 2025 13:07:20 +0900 Subject: [PATCH 054/177] stm32/system_stm32: Fix clock config for STM32G4. Add peripheral clock selection for ADC345 to use analog port under ADC3. Fixes #18527. Signed-off-by: Yuuki NAGAO --- ports/stm32/system_stm32.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/stm32/system_stm32.c b/ports/stm32/system_stm32.c index 514bf91786c13..5386771991524 100644 --- a/ports/stm32/system_stm32.c +++ b/ports/stm32/system_stm32.c @@ -535,13 +535,14 @@ MP_WEAK void SystemClock_Config(void) { MICROPY_BOARD_FATAL_ERROR("HAL_RCC_ClockConfig"); } PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC | RCC_PERIPHCLK_LPUART1 - | RCC_PERIPHCLK_RNG | RCC_PERIPHCLK_ADC12 + | RCC_PERIPHCLK_RNG | RCC_PERIPHCLK_ADC12 | RCC_PERIPHCLK_ADC345 | RCC_PERIPHCLK_FDCAN | RCC_PERIPHCLK_USB; PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_HSI48; PeriphClkInitStruct.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_PCLK1; PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_HSE; PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_HSI48; PeriphClkInitStruct.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK; + PeriphClkInitStruct.Adc345ClockSelection = RCC_ADC345CLKSOURCE_SYSCLK; PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { MICROPY_BOARD_FATAL_ERROR("HAL_RCCEx_PeriphCLKConfig"); From 504adc689bf1e56783b394b91ecc9d7a38ccf72e Mon Sep 17 00:00:00 2001 From: Steve Sanbeg Date: Sat, 6 Dec 2025 17:28:26 -0500 Subject: [PATCH 055/177] stm32/boards/NUCLEO_G474RE: Restore disabled modules on g474re. Among other things, the framebuf module is missing on NUCEO_G474RE. This board seems to disable a lot of modules, while other *E boards with the same flash configuration (eg NUCLEO_F411RE) don't seem to disable any modules in this way. So, remove all of the lines disabling modules to make it consistent with other boards. Signed-off-by: Steve Sanbeg --- ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.h b/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.h index e807a54780d98..66e5339450706 100644 --- a/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.h @@ -9,16 +9,6 @@ #define MICROPY_HW_HAS_SWITCH (1) #define MICROPY_HW_HAS_FLASH (0) // QSPI extflash not mounted -#define MICROPY_PY_ASYNCIO (0) -#define MICROPY_PY_DEFLATE (0) -#define MICROPY_PY_BINASCII (0) -#define MICROPY_PY_HASHLIB (0) -#define MICROPY_PY_JSON (0) -#define MICROPY_PY_RE (0) -#define MICROPY_PY_FRAMEBUF (0) -#define MICROPY_PY_SOCKET (0) -#define MICROPY_PY_NETWORK (0) - // The board has an 24MHz HSE, the following gives 170MHz CPU speed #define MICROPY_HW_CLK_PLLM (6) #define MICROPY_HW_CLK_PLLN (85) From 365cdb6f56dd15693f43c7a0a45eca60dfcfe4fa Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 4 Dec 2025 12:41:51 +1100 Subject: [PATCH 056/177] esp32/boards: Enable ESP32P4_REV_MIN_0 option for P4 boards. The ESP32-P4 currently comes in three revisions (0.0, 0.1 and 1.0) and all of them are out in the wild. Even though the IDF defaults to a minimum of 0.1 we would like to support as many as possible, so configure MicroPython to work down to revision 0.0. The firmware only grows by 32 bytes when enabling this option. Signed-off-by: Damien George --- ports/esp32/boards/sdkconfig.p4 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/esp32/boards/sdkconfig.p4 b/ports/esp32/boards/sdkconfig.p4 index 3ec0ff70579e4..042eec40cf7f8 100644 --- a/ports/esp32/boards/sdkconfig.p4 +++ b/ports/esp32/boards/sdkconfig.p4 @@ -1,3 +1,6 @@ +# Select the minimum chip revision to cover all possible boards. +CONFIG_ESP32P4_REV_MIN_0=y + # Flash CONFIG_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y From 7702f7f59daf8dd11b4134c05edd34002204cd99 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 7 Dec 2025 23:56:42 +1100 Subject: [PATCH 057/177] esp32/main: Update esp_hosted component to latest version 2.7.0. This updates the esp_hosted component for ESP32-P4 boards to use the latest version 2.7.0. Testing on a P4 board with C6 WiFi shows there are no regressions for WiFi or BLE. Also rename the `CONFIG_ESP_ENABLE_BT` option to the new `CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE` option used by the component. This change is made partly to work around a current issue with the IDF component manager, that esp_hosted versions after 2.0.17 and prior to 2.7.0 have all disappeared. Signed-off-by: Damien George --- ports/esp32/boards/sdkconfig.p4_wifi_common | 2 +- ports/esp32/lockfiles/dependencies.lock.esp32 | 2 +- ports/esp32/lockfiles/dependencies.lock.esp32c2 | 2 +- ports/esp32/lockfiles/dependencies.lock.esp32c3 | 2 +- ports/esp32/lockfiles/dependencies.lock.esp32c5 | 2 +- ports/esp32/lockfiles/dependencies.lock.esp32c6 | 2 +- ports/esp32/lockfiles/dependencies.lock.esp32p4 | 6 +++--- ports/esp32/lockfiles/dependencies.lock.esp32s2 | 2 +- ports/esp32/lockfiles/dependencies.lock.esp32s3 | 2 +- ports/esp32/main/idf_component.yml | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ports/esp32/boards/sdkconfig.p4_wifi_common b/ports/esp32/boards/sdkconfig.p4_wifi_common index b7bd0bffa0524..a55e941ba6847 100644 --- a/ports/esp32/boards/sdkconfig.p4_wifi_common +++ b/ports/esp32/boards/sdkconfig.p4_wifi_common @@ -41,7 +41,7 @@ CONFIG_ESP_WIFI_REMOTE_LIBRARY_HOSTED=y CONFIG_ESP_HOSTED_P4_DEV_BOARD_FUNC_BOARD=y # BLE -CONFIG_ESP_ENABLE_BT=y +CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y CONFIG_BT_CONTROLLER_DISABLED=y diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32 b/ports/esp32/lockfiles/dependencies.lock.esp32 index 3f3a416671648..8ba25c77011c2 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32 @@ -30,6 +30,6 @@ direct_dependencies: - espressif/lan867x - espressif/mdns - idf -manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 +manifest_hash: 482087bc40f0e187795a9ef9ad08ef15a585b4cdabc296c715a9d19284622150 target: esp32 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c2 b/ports/esp32/lockfiles/dependencies.lock.esp32c2 index 9530e74e9728c..8a366af342369 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32c2 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c2 @@ -16,6 +16,6 @@ dependencies: direct_dependencies: - espressif/mdns - idf -manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 +manifest_hash: 482087bc40f0e187795a9ef9ad08ef15a585b4cdabc296c715a9d19284622150 target: esp32c2 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c3 b/ports/esp32/lockfiles/dependencies.lock.esp32c3 index 5edbe537d8ba1..3aa99692d9f35 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32c3 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c3 @@ -16,6 +16,6 @@ dependencies: direct_dependencies: - espressif/mdns - idf -manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 +manifest_hash: 482087bc40f0e187795a9ef9ad08ef15a585b4cdabc296c715a9d19284622150 target: esp32c3 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c5 b/ports/esp32/lockfiles/dependencies.lock.esp32c5 index 3b52c82b1436b..2fb130b8e5282 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32c5 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c5 @@ -16,6 +16,6 @@ dependencies: direct_dependencies: - espressif/mdns - idf -manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 +manifest_hash: 482087bc40f0e187795a9ef9ad08ef15a585b4cdabc296c715a9d19284622150 target: esp32c5 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c6 b/ports/esp32/lockfiles/dependencies.lock.esp32c6 index 1c84d5bfd5fd9..c81806909a631 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32c6 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c6 @@ -16,6 +16,6 @@ dependencies: direct_dependencies: - espressif/mdns - idf -manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 +manifest_hash: 482087bc40f0e187795a9ef9ad08ef15a585b4cdabc296c715a9d19284622150 target: esp32c6 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32p4 b/ports/esp32/lockfiles/dependencies.lock.esp32p4 index 8923760482fd0..aea6ec2cc3607 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32p4 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32p4 @@ -14,7 +14,7 @@ dependencies: type: service version: 1.1.3 espressif/esp_hosted: - component_hash: f32400eec7f35652052ae79ecb301148d4011769e94eb8d47262fb22fce933d2 + component_hash: 53544436deb5fcbfdbf4a8e2c643cdbe31126556781784278c016d674df99cd2 dependencies: - name: idf require: private @@ -22,7 +22,7 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 2.2.4 + version: 2.7.0 espressif/esp_serial_slave_link: component_hash: ac1776806de0a6e371c84e87898bb983e19ce62aa7f1e2e5c4a3b0234a575d2c dependencies: @@ -88,6 +88,6 @@ direct_dependencies: - espressif/mdns - espressif/tinyusb - idf -manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 +manifest_hash: 482087bc40f0e187795a9ef9ad08ef15a585b4cdabc296c715a9d19284622150 target: esp32p4 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32s2 b/ports/esp32/lockfiles/dependencies.lock.esp32s2 index a7bb041539d99..8717181c10ac7 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32s2 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32s2 @@ -32,6 +32,6 @@ direct_dependencies: - espressif/mdns - espressif/tinyusb - idf -manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 +manifest_hash: 482087bc40f0e187795a9ef9ad08ef15a585b4cdabc296c715a9d19284622150 target: esp32s2 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32s3 b/ports/esp32/lockfiles/dependencies.lock.esp32s3 index 9a8b179f72b1c..0b8b8e92bd57b 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32s3 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32s3 @@ -32,6 +32,6 @@ direct_dependencies: - espressif/mdns - espressif/tinyusb - idf -manifest_hash: cc42b6ea8bc1d77d04370604f0b1c1a93a4f2c9b9200690722458faedaee68a4 +manifest_hash: 482087bc40f0e187795a9ef9ad08ef15a585b4cdabc296c715a9d19284622150 target: esp32s3 version: 2.0.0 diff --git a/ports/esp32/main/idf_component.yml b/ports/esp32/main/idf_component.yml index cb75296fe51ab..176e29c3c4862 100644 --- a/ports/esp32/main/idf_component.yml +++ b/ports/esp32/main/idf_component.yml @@ -10,7 +10,7 @@ dependencies: espressif/esp_hosted: rules: - if: "target == esp32p4" - version: "2.2.4" + version: "2.7.0" espressif/esp_wifi_remote: rules: - if: "target == esp32p4" From 2bd337ef18efdcb04c28adc2b03b26432ae24715 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 4 Dec 2025 16:23:43 +1100 Subject: [PATCH 058/177] lib/micropython-lib: Update submodule to latest. This brings in: - usb-device: raise RuntimeError when DCD error occurs - usb-device-hid: return True after submit_xfer - inspect: implement a very basic signature function - datetime: apply localtz patch to include naive date/time support - datetime: optimize for code size - sdcard: updating sector calculation for SDXC - mip: optimize _CHUNK_SIZE const for code size - aioble/examples: change variable name to _ADV_INTERVAL_US - aioble: fix typo in README in aioble.ADDR_PUBLIC - copy: fix typo in _deepcopy_dispatch - requests: update example for fetching using requests - all: fix formatting errors in docstrings - CONTRIBUTING: add guidelines for module documentation and versioning - cbor2: silence missing `__eq__` warning - pyproject.toml: reorganize ruff lint settings for newer ruff - all: correct various typos in comments and docs - lora: fix import error detection for missing drivers - inspect: support closures/generators/async-funcs in inspect.signature - usb-device-hid: fix descriptor protocol config and set correct default - usb-device-hid: use report protocol after report descriptor requested - umqtt.simple: add unsubscribe method - aiohttp: correctly handle WebSocket message fragmentation - iperf3: fix use as a CLI on the unix port of MicroPython - iperf3: factor out data transfer logic to separate function - iperf3: fix server UDP mode - unix-ffi/socket: remove ip add/drop membership from socket - tarfile: add basic unittest for tarfile.TarFile - tarfile: fix FileSection.skip to not rely on extended readinto args - argparse: Add support for custom argument types - pyproject.toml: add codespell configuration, CI and precommit - all: fix spelling and typos in comments and docstring - aioble/examples: change variable name to _ADV_INTERVAL_US 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 34c4ee1647ac4..6ae440a8a1442 160000 --- a/lib/micropython-lib +++ b/lib/micropython-lib @@ -1 +1 @@ -Subproject commit 34c4ee1647ac4b177ae40adf0ec514660e433dc0 +Subproject commit 6ae440a8a144233e6e703f6759b7e7a0afaa37a4 From 78ff170de9e32c79db6e64d3e33d2bd60002bdcd Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Dec 2025 01:44:16 +1100 Subject: [PATCH 059/177] all: Bump version to 1.27.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 97c40389b32f0..798d4089aa119 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -41,7 +41,7 @@ #define MICROPY_VERSION_MAJOR 1 #define MICROPY_VERSION_MINOR 27 #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 099cc9f50722f6ef542a9065b7c92d0f964fe2b1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 15 Dec 2025 10:15:42 +1100 Subject: [PATCH 060/177] all: Bump version to 1.28.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 798d4089aa119..666b15573d9c1 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -39,9 +39,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 27 +#define MICROPY_VERSION_MINOR 28 #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 9274f80130b483df6b1d3af5f40bce34a0c602ef Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Dec 2025 12:19:20 +1100 Subject: [PATCH 061/177] esp32/boards/ESP32_GENERIC_S3: Reinstate old FLASH_4M variant. Commit 6201e77999b3614518abc4b21773e735d9b0b0ee removed the ESP32_GENERIC_S3 FLASH_4M variant from `board.json`. But the firmware still exists on the download server, and it makes sense to still keep those old versions available for download, just like all other older versions are still available. This commit introduce a new scheme for `board.json` whereby old variants that are no longer built can be moved to the "old_variants" section. This keeps them available on the download page, allowing a way to deprecate individual board variants without removing them entirely. An optional string can be added to the old variant to describe why it's obsolete and what to use instead. Signed-off-by: Damien George --- ports/esp32/boards/ESP32_GENERIC_S3/board.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.json b/ports/esp32/boards/ESP32_GENERIC_S3/board.json index 7a546d35fcd4f..b383dfa068a58 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.json @@ -22,5 +22,8 @@ "vendor": "Espressif", "variants": { "SPIRAM_OCT": "Support for Octal-SPIRAM" + }, + "old_variants": { + "FLASH_4M": ["4MiB flash", "Use the standard variant instead."] } } From 53052857e2e5845cbf78f778d40ccd59f4f2e3ef Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Dec 2025 11:47:25 +1100 Subject: [PATCH 062/177] esp8266/boards/ESP8266_GENERIC: Remove OTA board variant. The OTA variant of the ESP8266_GENERIC board was never fully completed in its functionality. It relies on the https://github.com/pfalcon/yaota8266 component which was also never fully finished and has been unmaintained for many years. This commit removes this variant and it's associated build support. It makes it an "old_variant" so the existing historical firmware is still listed on the download page. Signed-off-by: Damien George --- ports/esp8266/Makefile | 10 ---------- ports/esp8266/boards/ESP8266_GENERIC/board.json | 4 +++- ports/esp8266/boards/ESP8266_GENERIC/board.md | 10 ---------- .../boards/ESP8266_GENERIC/mpconfigvariant_OTA.mk | 10 ---------- ports/esp8266/modesp.c | 9 --------- 5 files changed, 3 insertions(+), 40 deletions(-) delete mode 100644 ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_OTA.mk diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index 1c9de01503abd..abdc9076a0165 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -208,20 +208,10 @@ erase: reset: echo -e "\r\nimport machine; machine.reset()\r\n" >$(PORT) -ifeq ($(BOARD_VARIANT),OTA) -$(FWBIN): $(BUILD)/firmware.elf - $(ECHO) "Create $@" - $(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 > $@ - $(Q)$(PYTHON) $(YAOTA8266)/ota-client/ota_client.py sign $@ -else $(FWBIN): $(BUILD)/firmware.elf $(ECHO) "Create $@" $(Q)$(ESPTOOL) elf2image $^ $(Q)$(PYTHON) makeimg.py $(BUILD)/firmware.elf-0x00000.bin $(BUILD)/firmware.elf-0x[0-5][1-f]000.bin $@ -endif $(BUILD)/firmware.elf: $(OBJ) $(ECHO) "LINK $@" diff --git a/ports/esp8266/boards/ESP8266_GENERIC/board.json b/ports/esp8266/boards/ESP8266_GENERIC/board.json index 8f2aa1240f280..84c4ff4951013 100644 --- a/ports/esp8266/boards/ESP8266_GENERIC/board.json +++ b/ports/esp8266/boards/ESP8266_GENERIC/board.json @@ -15,10 +15,12 @@ "thumbnail": "", "url": "https://www.espressif.com/en/products/modules", "variants": { - "OTA": "OTA compatible", "FLASH_1M": "1MiB flash", "FLASH_512K": "512kiB flash", "FLASH_2M_ROMFS": "2MiB flash with ROMFS" }, + "old_variants": { + "OTA": ["OTA compatible", "OTA firmware is no longer supported."] + }, "vendor": "Espressif" } diff --git a/ports/esp8266/boards/ESP8266_GENERIC/board.md b/ports/esp8266/boards/ESP8266_GENERIC/board.md index e8d63fcead0c4..b83d2910cd673 100644 --- a/ports/esp8266/boards/ESP8266_GENERIC/board.md +++ b/ports/esp8266/boards/ESP8266_GENERIC/board.md @@ -14,13 +14,3 @@ Note: v1.12-334 and newer (including v1.13) require an ESP8266 module with upgrading from older firmware please backup your files first, and either erase all flash before upgrading, or after upgrading execute `vfs.VfsLfs2.mkfs(bdev)`. - -### OTA builds -Over-The-Air (OTA) builds of the ESP8266 firmware are also provided. - -The first time you use this build you need to flash one of the "initial image" -images using esptool.py as described above. After that, you can update the -firmware over the air using the "OTA update" file in conjunction with the -ota-client script from yaota8266. The "OTA update" files are digitally signed -and will only work with the provided "initial image" files, and vice versa. -(Note: this feature is work-in-progress.) diff --git a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_OTA.mk b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_OTA.mk deleted file mode 100644 index 759eab3dc750b..0000000000000 --- a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigvariant_OTA.mk +++ /dev/null @@ -1,10 +0,0 @@ -LD_FILES = boards/esp8266_ota.ld - -MICROPY_PY_ESPNOW ?= 1 -MICROPY_PY_BTREE ?= 1 -MICROPY_VFS_LFS2 ?= 1 - -# Note: Implicitly uses the port manifest. - -# Configure mpconfigboard.h. -CFLAGS += -DMICROPY_ESP8266_1M diff --git a/ports/esp8266/modesp.c b/ports/esp8266/modesp.c index e1f9d39687593..724545202d8a0 100644 --- a/ports/esp8266/modesp.c +++ b/ports/esp8266/modesp.c @@ -159,10 +159,6 @@ static mp_obj_t esp_flash_size(void) { } static MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_size_obj, esp_flash_size); -// If there's just 1 loadable segment at the start of flash, -// we assume there's a yaota8266 bootloader. -#define IS_OTA_FIRMWARE() ((*(uint32_t *)0x40200000 & 0xff00) == 0x100) - extern byte _firmware_size[]; #if MICROPY_VFS_ROM_IOCTL extern uint8_t _micropy_hw_romfs_part0_size; @@ -180,11 +176,6 @@ static MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_user_start_obj, esp_flash_user_start) static mp_obj_t esp_check_fw(void) { MD5_CTX ctx; char *fw_start = (char *)0x40200000; - if (IS_OTA_FIRMWARE()) { - // Skip yaota8266 bootloader - fw_start += 0x3c000; - } - uint32_t size = *(uint32_t *)(fw_start + 0x8ffc); printf("size: %d\n", size); if (size > 1024 * 1024) { From b78fac161e34eb953c499eb17630e67355a9508a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 10 Dec 2025 12:39:01 +1100 Subject: [PATCH 063/177] tests/ports/esp32/check_err_str.py: Preallocate global variable. This test fails on all esp32 boards without this fix, because the try/except that runs with the heap locked attempts to increase the size of the globals dict when assigning to the exception variable `e`. Fix that by preallocating the global variable `e`. Signed-off-by: Damien George --- tests/ports/esp32/check_err_str.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ports/esp32/check_err_str.py b/tests/ports/esp32/check_err_str.py index 055eecf7476ae..9efe1afa1961b 100644 --- a/tests/ports/esp32/check_err_str.py +++ b/tests/ports/esp32/check_err_str.py @@ -1,3 +1,5 @@ +# This tests checks the behaviour of the `check_esp_err`/`check_esp_err_` C function. + try: from esp32 import Partition as p import micropython @@ -23,6 +25,7 @@ # same but with out of memory condition by locking the heap exc = "FAILED TO RAISE" +e = None # preallocate entry in globals dict micropython.heap_lock() try: fun(part) @@ -34,6 +37,7 @@ # same again but having an emergency buffer micropython.alloc_emergency_exception_buf(256) exc = "FAILED TO RAISE" +e = None # preallocate entry in globals dict micropython.heap_lock() try: fun(part) From ef567dc9281e6ffff991bf4cd18df48c9043b207 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 30 Nov 2025 16:24:01 -0600 Subject: [PATCH 064/177] tests/basics/string_fstring.py: Test fstring nested replacement fields. This does not actually cover any additional lines, but it does cover new functionality not previously covered. Signed-off-by: Jeff Epler --- tests/basics/string_fstring.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/basics/string_fstring.py b/tests/basics/string_fstring.py index d94cc0cd3e630..200065e9d9d1f 100644 --- a/tests/basics/string_fstring.py +++ b/tests/basics/string_fstring.py @@ -79,3 +79,14 @@ def foo(a, b): # Raw f-strings. print(rf"\r\a\w {'f'} \s\t\r\i\n\g") print(fr"\r{x}") + +# Format specifiers with nested replacement fields +space = 5 +prec = 2 +print(f"{3.14:{space}.{prec}}") + +space_prec = "5.2" +print(f"{3.14:{space_prec}}") + +radix = "x" +print(f"{314:{radix}}") From 750a366f205f5c688532a4a7f4376240f3cdb78a Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 10 Dec 2025 05:10:39 +0100 Subject: [PATCH 065/177] tests/run-tests.py: Skip list sort stress test for ESP8266. This commit marks the "stress/list_sort.py" test to be skipped when running on ESP8266. The test just takes too long without yielding to the OS whilst doing the sort, causing the internal software watchdog to kick in and reboot the board. Signed-off-by: Alessandro Gatti --- tests/run-tests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/run-tests.py b/tests/run-tests.py index fba011fb54cb6..aed67cbac8e09 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -180,6 +180,9 @@ def open(self, path, mode): # Tests to skip on specific targets. # These are tests that are difficult to detect that they should not be run on the given target. platform_tests_to_skip = { + "esp8266": ( + "stress/list_sort.py", # watchdog kicks in because it takes too long + ), "minimal": ( "basics/class_inplace_op.py", # all special methods not supported "basics/subclass_native_init.py", # native subclassing corner cases not support From dbf59db5a10768b374d11f3a5d7cc40dc7afaa79 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 10 Dec 2025 05:59:16 +0100 Subject: [PATCH 066/177] tests/extmod/vfs_blockdev_invalid.py: Handle low memory conditions. This commit modifies the "extmod/vfs_blockdev_invalid" test to better behave on boards with low available memory. Before these changes the test would fail on ESP8266 (at least), due to low memory, but in a way that could not be easily solved as the error occurred in the middle of the test. The test has been rewritten to delay its output until the very end, so if a low memory condition occurs and needs to stop execution then no real output will show up before the skip marker. Signed-off-by: Alessandro Gatti --- tests/extmod/vfs_blockdev_invalid.py | 54 ++++++++++++++++++------ tests/extmod/vfs_blockdev_invalid.py.exp | 29 +------------ 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/tests/extmod/vfs_blockdev_invalid.py b/tests/extmod/vfs_blockdev_invalid.py index 29d6bd6b2f9f7..955f8495b3f14 100644 --- a/tests/extmod/vfs_blockdev_invalid.py +++ b/tests/extmod/vfs_blockdev_invalid.py @@ -46,12 +46,16 @@ def ioctl(self, op, arg): try: bdev = RAMBlockDevice(50) except MemoryError: - print("SKIP") + print("SKIP-TOO-LARGE") raise SystemExit -def test(vfs_class): - print(vfs_class) +ERROR_EIO = (OSError, "[Errno 5] EIO") +ERROR_EINVAL = (OSError, "[Errno 22] EINVAL") +ERROR_TYPE = (TypeError, "can't convert str to int") + + +def test(vfs_class, test_data): bdev.read_res = 0 # reset function results bdev.write_res = 0 @@ -61,17 +65,15 @@ def test(vfs_class): with fs.open("test", "w") as f: f.write("a" * 64) - for res in (0, -5, 5, 33, "invalid"): - # -5 is a legitimate negative failure (EIO), positive integer - # is not - + for res, error_open, error_read in test_data: # This variant will fail on open bdev.read_res = res try: with fs.open("test", "r") as f: - print("opened") + assert error_open is None except Exception as e: - print(type(e), e) + assert error_open is not None + assert (type(e), str(e)) == error_open # This variant should succeed on open, may fail on read # unless the filesystem cached the contents already @@ -79,11 +81,35 @@ def test(vfs_class): try: with fs.open("test", "r") as f: bdev.read_res = res - print("read 1", f.read(1)) - print("read rest", f.read()) + assert f.read(1) == "a" + assert f.read() == "a" * 63 + assert error_read is None except Exception as e: - print(type(e), e) + assert error_read is not None + assert (type(e), str(e)) == error_read -test(vfs.VfsLfs2) -test(vfs.VfsFat) +try: + test( + vfs.VfsLfs2, + ( + (0, None, None), + (-5, ERROR_EIO, None), + (5, ERROR_EINVAL, None), + (33, ERROR_EINVAL, None), + ("invalid", ERROR_TYPE, None), + ), + ) + test( + vfs.VfsFat, + ( + (0, None, None), + (-5, ERROR_EIO, ERROR_EIO), + (5, ERROR_EIO, ERROR_EIO), + (33, ERROR_EIO, ERROR_EIO), + ("invalid", ERROR_TYPE, ERROR_TYPE), + ), + ) + print("OK") +except MemoryError: + print("SKIP-TOO-LARGE") diff --git a/tests/extmod/vfs_blockdev_invalid.py.exp b/tests/extmod/vfs_blockdev_invalid.py.exp index 0ea0353501de3..d86bac9de59ab 100644 --- a/tests/extmod/vfs_blockdev_invalid.py.exp +++ b/tests/extmod/vfs_blockdev_invalid.py.exp @@ -1,28 +1 @@ - -opened -read 1 a -read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - [Errno 5] EIO -read 1 a -read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - [Errno 22] EINVAL -read 1 a -read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - [Errno 22] EINVAL -read 1 a -read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - can't convert str to int -read 1 a -read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - -opened -read 1 a -read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - [Errno 5] EIO - [Errno 5] EIO - [Errno 5] EIO - [Errno 5] EIO - [Errno 5] EIO - [Errno 5] EIO - can't convert str to int - can't convert str to int +OK From 9c9b99686edfc2809d09eebfb012eaeed5e3bb96 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 17 Dec 2025 03:57:52 +0100 Subject: [PATCH 067/177] tests/target_wiring: Provide an ESP8266 target wiring module. This commit introduces ESP8266 support for target wiring tests, fixing execution of relevant tests that once failed on that platform. ESP8266 boards need to have GPIO4 and GPIO5 connected together to provide a UART loopback, in order to test whether UART data effectively flows through. The wiring-enabled UART transmission timing test was also updated with measurements compatible with a few ESP8266 test boards. Signed-off-by: Alessandro Gatti --- tests/extmod/machine_uart_tx.py | 2 ++ tests/target_wiring/esp8266.py | 7 +++++++ 2 files changed, 9 insertions(+) create mode 100644 tests/target_wiring/esp8266.py diff --git a/tests/extmod/machine_uart_tx.py b/tests/extmod/machine_uart_tx.py index 70d57be46477c..1ff9af64bd4ce 100644 --- a/tests/extmod/machine_uart_tx.py +++ b/tests/extmod/machine_uart_tx.py @@ -19,6 +19,8 @@ bit_margin = 1 elif "esp32" in sys.platform: timing_margin_us = 400 +elif "esp8266" in sys.platform: + timing_margin_us = 4100 elif "mimxrt" in sys.platform: initial_delay_ms = 20 # UART sends idle frame after init, so wait for that bit_margin = 1 diff --git a/tests/target_wiring/esp8266.py b/tests/target_wiring/esp8266.py new file mode 100644 index 0000000000000..336deb3dda018 --- /dev/null +++ b/tests/target_wiring/esp8266.py @@ -0,0 +1,7 @@ +# Target wiring for general esp8266 board. +# +# Connect: +# - GPIO4 to GPIO5 + +uart_loopback_args = (1,) +uart_loopback_kwargs = {} From 8b8cd825605a6cf93e7de3db1c871ba56bd2abfc Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 27 Nov 2025 21:17:12 -0600 Subject: [PATCH 068/177] py/misc: Remove unused mp_check function. This is unused since 007f127a61ea058ca010b85883072bdefe0234c0 "all: Simplify mp_int_t/mp_uint_t definition". Signed-off-by: Jeff Epler --- py/misc.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/py/misc.h b/py/misc.h index 5188b8030db5c..f3688c73e4583 100644 --- a/py/misc.h +++ b/py/misc.h @@ -411,11 +411,6 @@ static inline uint32_t mp_ctz(uint32_t x) { return _BitScanForward(&tz, x) ? tz : 0; } -// Workaround for 'warning C4127: conditional expression is constant'. -static inline bool mp_check(bool value) { - return value; -} - static inline uint32_t mp_popcount(uint32_t x) { return __popcnt(x); } @@ -424,7 +419,6 @@ static inline uint32_t mp_popcount(uint32_t 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 __has_builtin(__builtin_popcount) #define mp_popcount(x) __builtin_popcount(x) #else From 50a5fe0020cc8d16fd9b4c108bd992209cb041f6 Mon Sep 17 00:00:00 2001 From: Thomas Kiss Date: Tue, 9 Dec 2025 21:12:56 +0100 Subject: [PATCH 069/177] docs/library/network.WLAN: Fix typo for ESP32 protocol constants. Signed-off-by: Thomas Kiss --- docs/library/network.WLAN.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/library/network.WLAN.rst b/docs/library/network.WLAN.rst index 27d6b383a36a6..e4653bc35ca52 100644 --- a/docs/library/network.WLAN.rst +++ b/docs/library/network.WLAN.rst @@ -145,7 +145,7 @@ Methods reconnects Number of reconnect attempts to make (integer, 0=none, -1=unlimited) txpower Maximum transmit power in dBm (integer or float) pm WiFi Power Management setting (see below for allowed values) - protocol (ESP32 Only.) WiFi Low level 802.11 protocol. See `WLAN.PROTOCOL_DEFAULTS`. + protocol (ESP32 Only.) WiFi Low level 802.11 protocol. See `WLAN.PROTOCOL_DEFAULT`. ============= =========== Constants @@ -170,7 +170,7 @@ ESP32 Protocol Constants The following ESP32-only constants relate to the ``WLAN.config(protocol=...)`` network interface parameter: -.. data:: WLAN.PROTOCOL_DEFAULTS +.. data:: WLAN.PROTOCOL_DEFAULT A bitmap representing all of the default 802.11 Wi-Fi modes supported by the chip. Consult `ESP-IDF Wi-Fi Protocols`_ documentation for details. @@ -184,7 +184,7 @@ network interface parameter: `. This mode can be bitwise ORed with some standard 802.11 protocol bits - (including `WLAN.PROTOCOL_DEFAULTS`) in order to support a mix of standard + (including `WLAN.PROTOCOL_DEFAULT`) in order to support a mix of standard Wi-Fi modes as well as LR mode, consult the `Espressif long-range documentation`_ for more details. From e67d4a2a777f09215cdda928ec259696b86fcb81 Mon Sep 17 00:00:00 2001 From: jetpax Date: Mon, 15 Dec 2025 19:41:05 -0800 Subject: [PATCH 070/177] esp32/machine_sdcard: Fix SDMMC slot assignment for non-default slots. The SDMMC_HOST_DEFAULT() macro sets slot to SDMMC_HOST_SLOT_1, but this was not being overridden when the user specified a different slot number. This caused SDMMC initialization to fail on chips like ESP32-P4 when trying to use slot 0. This commit ensures the slot number passed to the SDCard constructor is properly assigned to the host configuration structure. Tested on ESP32-P4 with SD card on slot 0. Signed-off-by: jetpax --- ports/esp32/machine_sdcard.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/esp32/machine_sdcard.c b/ports/esp32/machine_sdcard.c index 0f8bd844692b4..4785a7254a865 100644 --- a/ports/esp32/machine_sdcard.c +++ b/ports/esp32/machine_sdcard.c @@ -166,6 +166,8 @@ static esp_err_t sdcard_ensure_card_init(sdcard_card_obj_t *self, bool force) { esp_err_t err = sdmmc_card_init(&(self->host), &(self->card)); if (err == ESP_OK) { + // Ensure card's host structure has the correct slot after init + self->card.host.slot = self->host.slot; self->flags |= SDCARD_CARD_FLAGS_CARD_INIT_DONE; } else { self->flags &= ~SDCARD_CARD_FLAGS_CARD_INIT_DONE; @@ -307,6 +309,7 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args else { sdmmc_host_t _temp_host = SDMMC_HOST_DEFAULT(); _temp_host.max_freq_khz = freq / 1000; + _temp_host.slot = slot_num; self->host = _temp_host; } #endif From f0895f0ea0d3daea42161bc29c3ff8abeb101e57 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 4 Nov 2025 09:46:16 +0100 Subject: [PATCH 071/177] py/emitnative: Optimise register clearing. This commit introduces a new generic ASM API function to clear a register (i.e. clearing all the registers' bits). The native emitter used to perform a XOR operation to clear a given register, but different platform have more optimised method to achieve the same result taking up less space - either for the generated code or for the code generator itself. Arm, RV32, X86, and X64 already had an already optimised generator and generated optimised code. The code generator when build for Thumb takes less space generating a constant immediate move rather than a XOR operation, even though both operations would distill down to a single narrow opcode. On Xtensa the situation is almost the same as Thumb, with the exception that a constant immediate move would take one byte less than a XOR operation. Signed-off-by: Alessandro Gatti --- py/asmarm.h | 2 ++ py/asmrv32.h | 2 +- py/asmthumb.h | 2 ++ py/asmx64.h | 2 ++ py/asmx86.h | 2 ++ py/asmxtensa.h | 2 ++ py/emitnative.c | 22 ++++++++-------------- py/emitndebug.c | 3 +++ 8 files changed, 22 insertions(+), 15 deletions(-) diff --git a/py/asmarm.h b/py/asmarm.h index 405457d4408a8..5ae952ee8a7db 100644 --- a/py/asmarm.h +++ b/py/asmarm.h @@ -230,6 +230,8 @@ void asm_arm_bx_reg(asm_arm_t *as, uint reg_src); #define ASM_STORE16_REG_REG_REG(as, reg_val, reg_base, reg_index) asm_arm_strh_reg_reg_reg((as), (reg_val), (reg_base), (reg_index)) #define ASM_STORE32_REG_REG_REG(as, reg_val, reg_base, reg_index) asm_arm_str_reg_reg_reg((as), (reg_val), (reg_base), (reg_index)) +#define ASM_CLR_REG(as, reg_dest) asm_arm_eor_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_dest)) + #endif // GENERIC_ASM_API #endif // MICROPY_INCLUDED_PY_ASMARM_H diff --git a/py/asmrv32.h b/py/asmrv32.h index 6f709daa11bce..1100d098019ff 100644 --- a/py/asmrv32.h +++ b/py/asmrv32.h @@ -804,7 +804,7 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_ #define ASM_STORE32_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_store_reg_reg_offset(state, rd, rs, offset, 2) #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) +#define ASM_CLR_REG(state, rd) asm_rv32_emit_optimised_xor(state, rd, rd) #define ASM_LOAD8_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_load_reg_reg_reg(state, rd, rs1, rs2, 0) #define ASM_LOAD16_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_load_reg_reg_reg(state, rd, rs1, rs2, 1) #define ASM_LOAD32_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_load_reg_reg_reg(state, rd, rs1, rs2, 2) diff --git a/py/asmthumb.h b/py/asmthumb.h index 5edf6573e1a6a..88f4e399bc85e 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -485,6 +485,8 @@ void asm_thumb_b_rel12(asm_thumb_t *as, int rel); asm_thumb_str_rlo_rlo_rlo((as), (reg_val), (reg_base), (reg_index)); \ } while (0) +#define ASM_CLR_REG(as, reg_dest) asm_thumb_mov_rlo_i8((as), (reg_dest), 0) + #endif // GENERIC_ASM_API #endif // MICROPY_INCLUDED_PY_ASMTHUMB_H diff --git a/py/asmx64.h b/py/asmx64.h index d80c5dcc13f1a..efc3027b170ce 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -221,6 +221,8 @@ void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r32); #define ASM_STORE32_REG_REG(as, reg_src, reg_base) ASM_STORE32_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) #define ASM_STORE32_REG_REG_OFFSET(as, reg_src, reg_base, dword_offset) asm_x64_mov_r32_to_mem32((as), (reg_src), (reg_base), 4 * (dword_offset)) +#define ASM_CLR_REG(as, reg_dest) asm_x64_xor_r64_r64((as), (reg_dest), (reg_dest)) + #endif // GENERIC_ASM_API #endif // MICROPY_INCLUDED_PY_ASMX64_H diff --git a/py/asmx86.h b/py/asmx86.h index d2e078ad51d29..80a67794d2044 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -216,6 +216,8 @@ void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r #define ASM_STORE32_REG_REG(as, reg_src, reg_base) ASM_STORE32_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) #define ASM_STORE32_REG_REG_OFFSET(as, reg_src, reg_base, dword_offset) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 4 * (dword_offset)) +#define ASM_CLR_REG(as, reg_dest) asm_x86_xor_r32_r32((as), (reg_dest), (reg_dest)) + #endif // GENERIC_ASM_API #endif // MICROPY_INCLUDED_PY_ASMX86_H diff --git a/py/asmxtensa.h b/py/asmxtensa.h index 559b3cacd5d45..15f8b4d92e2c6 100644 --- a/py/asmxtensa.h +++ b/py/asmxtensa.h @@ -464,6 +464,8 @@ void asm_xtensa_l32r(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t label); asm_xtensa_op_s32i_n((as), (reg_val), (reg_base), 0); \ } while (0) +#define ASM_CLR_REG(as, reg_dest) asm_xtensa_op_movi_n((as), (reg_dest), 0) + #endif // GENERIC_ASM_API #endif // MICROPY_INCLUDED_PY_ASMXTENSA_H diff --git a/py/emitnative.c b/py/emitnative.c index a33ec01ec0f94..6cf01dcab1328 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -282,17 +282,13 @@ 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 - -#if N_RV32 +#ifdef REG_ZERO #define ASM_MOV_LOCAL_MP_OBJ_NULL(as, local_num, reg_temp) \ ASM_MOV_LOCAL_REG(as, local_num, REG_ZERO) #else +#define REG_ZERO REG_TEMP0 #define ASM_MOV_LOCAL_MP_OBJ_NULL(as, local_num, reg_temp) \ - ASM_MOV_REG_IMM(as, reg_temp, (mp_uint_t)MP_OBJ_NULL); \ + ASM_CLR_REG(as, reg_temp); \ ASM_MOV_LOCAL_REG(as, local_num, reg_temp) #endif @@ -1145,7 +1141,7 @@ static void emit_native_leave_exc_stack(emit_t *emit, bool start_of_handler) { // Optimisation: PC is already cleared by global exc handler return; } - ASM_XOR_REG_REG(emit->as, REG_RET, REG_RET); + ASM_CLR_REG(emit->as, REG_RET); } else { // Found new active handler, get its PC ASM_MOV_REG_PCREL(emit->as, REG_RET, e->label); @@ -1242,8 +1238,7 @@ 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_CLR_REG(emit->as, REG_ZERO); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_UNWIND(emit), REG_ZERO); + ASM_MOV_LOCAL_MP_OBJ_NULL(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 @@ -1263,8 +1258,7 @@ 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_CLR_REG(emit->as, REG_ZERO); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_PC(emit), REG_ZERO); + ASM_MOV_LOCAL_MP_OBJ_NULL(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 @@ -1945,7 +1939,7 @@ static void emit_native_delete_attr(emit_t *emit, qstr qst) { vtype_kind_t vtype_base; emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = base assert(vtype_base == VTYPE_PYOBJ); - ASM_XOR_REG_REG(emit->as, REG_ARG_3, REG_ARG_3); // arg3 = value (null for delete) + ASM_CLR_REG(emit->as, REG_ARG_3); // arg3 = value (null for delete) emit_call_with_qstr_arg(emit, MP_F_STORE_ATTR, qst, REG_ARG_2); // arg2 = attribute name emit_post(emit); } @@ -2091,7 +2085,7 @@ static void emit_native_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t exc // No finally, handle the jump ourselves // First, restore the exception handler address for the jump if (e < emit->exc_stack) { - ASM_XOR_REG_REG(emit->as, REG_RET, REG_RET); + ASM_CLR_REG(emit->as, REG_RET); } else { ASM_MOV_REG_PCREL(emit->as, REG_RET, e->label); } diff --git a/py/emitndebug.c b/py/emitndebug.c index e49c5cdbffa7b..2144d14e6b57a 100644 --- a/py/emitndebug.c +++ b/py/emitndebug.c @@ -271,6 +271,9 @@ static void asm_debug_setcc_reg_reg_reg(asm_debug_t *as, int op, int reg1, int r #define ASM_STORE32_REG_REG(as, reg_src, reg_base) \ asm_debug_reg_reg(as, "store32", reg_src, reg_base) +#define ASM_CLR_REG(as, reg_dest) \ + asm_debug_reg(as, "clr", reg_dest) + // Word indices of REG_LOCAL_x in nlr_buf_t #define NLR_BUF_IDX_LOCAL_1 (5) // rbx From cb9d8fcc3162b6a4935200c73f58661e9d55888b Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 28 Oct 2025 05:29:38 +0100 Subject: [PATCH 072/177] tests/run-tests.py: Discover RV32 extension inlineasm tests. This commit extends the test runner to automatically discover inline assembler tests for known RV32 extensions, and checks whether to add the discovered tests to the enabled tests list. Automatic discovery requires that inline assembler tests for RV32 extensions follow a specific pattern both for filenames and for the tests' output in case of success. A valid RV32 extension test must have: * A code fragment that checks for support of the extension on the running target in "/tests/feature_check", called "inlineasm_rv32_.py" that should print the string "rv32_" if the extension is supported * A matching expected result file in "/tests/feature_check" called "inlineasm_rv32_.py.exp" that must contain the string "rv32_" (without quotes) * A regular MicroPython test file in "/tests/inlineasm/rv32" called "asm_ext_.py" For example, to test the Zba extension, there must be a file called "/tests/feature_check/inlineasm_rv32_zba.py" that should print the string "rv32_zba" if the extension is supported, together with a file called "/test/feature_check/inlineasm_rv32_zba.py.exp" that contains the string "rv32_zba" in it, and finally there must be a regular MicroPython test file called "/tests/inlineasm/rv32/asm_ext_zba.py". Signed-off-by: Alessandro Gatti --- tests/inlineasm/rv32/{asmzba.py => asm_ext_zba.py} | 0 .../rv32/{asmzba.py.exp => asm_ext_zba.py.exp} | 0 tests/run-tests.py | 13 +++++++++---- 3 files changed, 9 insertions(+), 4 deletions(-) rename tests/inlineasm/rv32/{asmzba.py => asm_ext_zba.py} (100%) rename tests/inlineasm/rv32/{asmzba.py.exp => asm_ext_zba.py.exp} (100%) diff --git a/tests/inlineasm/rv32/asmzba.py b/tests/inlineasm/rv32/asm_ext_zba.py similarity index 100% rename from tests/inlineasm/rv32/asmzba.py rename to tests/inlineasm/rv32/asm_ext_zba.py diff --git a/tests/inlineasm/rv32/asmzba.py.exp b/tests/inlineasm/rv32/asm_ext_zba.py.exp similarity index 100% rename from tests/inlineasm/rv32/asmzba.py.exp rename to tests/inlineasm/rv32/asm_ext_zba.py.exp diff --git a/tests/run-tests.py b/tests/run-tests.py index aed67cbac8e09..d4c6d445e95c3 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -950,10 +950,15 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("inlineasm/thumb/asmfpsqrt.py") if args.inlineasm_arch == "rv32": - # Check if @micropython.asm_rv32 supports Zba instructions, and skip such tests if it doesn't - output = run_feature_check(pyb, args, "inlineasm_rv32_zba.py") - if output != b"rv32_zba\n": - skip_tests.add("inlineasm/rv32/asmzba.py") + # Discover extension-specific inlineasm tests and add them to the + # list of tests to run if applicable. + for extension in RV32_ARCH_FLAGS: + try: + output = run_feature_check(pyb, args, "inlineasm_rv32_{}.py".format(extension)) + if output.strip() != "rv32_{}".format(extension).encode(): + skip_tests.add("inlineasm/rv32/asm_ext_{}.py".format(extension)) + except FileNotFoundError: + pass # Check if emacs repl is supported, and skip such tests if it's not t = run_feature_check(pyb, args, "repl_emacs_check.py") From d938d5af4e1d4a860c80d3ecfbe0288c60858cb9 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 10 Nov 2025 21:12:37 +0100 Subject: [PATCH 073/177] qemu/Makefile: Allow usage of a custom QEMU binary to run code. This commit introduces a new optional makefile variable to let the build system know that, when running code, a custom QEMU binary must be used instead of the one provided by the system's PATH. Given that the CI machine won't keep up with QEMU updates unless its base image tracks a new version of QEMU itself, sometimes it is needed to use a custom QEMU build to be able to test new code in an emulated context rather than having to perform on-device testing during development. Signed-off-by: Alessandro Gatti --- ports/qemu/Makefile | 3 ++- ports/qemu/README.md | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index ba9c53841aee5..c8ce3de872046 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -153,7 +153,8 @@ endif ################################################################################ # Project specific settings and compiler/linker flags -QEMU_SYSTEM = qemu-system-$(QEMU_ARCH) +QEMU_BASE ?= qemu-system- +QEMU_SYSTEM = $(QEMU_BASE)$(QEMU_ARCH) QEMU_ARGS += -machine $(QEMU_MACHINE) -nographic -monitor null -semihosting QEMU_ARGS += $(QEMU_EXTRA) diff --git a/ports/qemu/README.md b/ports/qemu/README.md index bf5788961dbe9..a7134d0eb87d7 100644 --- a/ports/qemu/README.md +++ b/ports/qemu/README.md @@ -157,6 +157,10 @@ The following options can be specified on the `make` command line: - `CFLAGS_EXTRA`: pass in extra flags for the compiler. - `RUN_TESTS_EXTRA`: pass in extra flags for `run-tests.py` and `run-natmodtests.py` when invoked via `make test` or `make test_natmod`. +- `QEMU_BASE`: pass an optional partial path of the qemu binary to run code with, eg. + `/opt/custom-directory/qemu/bin/qemu-system-`, similar to how a cross compiler name + is passed to the MicroPython makefile. By default it will use the appropriate QEMU + binary found through the system's PATH environment variable. - `QEMU_DEBUG=1`: when running qemu (via `repl`, `run` or `test` target), qemu will block until a debugger is connected. By default it waits for a gdb connection on TCP port 1234. From 1df86516e0b4515c4d9b94811ca7da969de6dbc0 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 28 Oct 2025 05:03:44 +0100 Subject: [PATCH 074/177] py/asmrv32: Reserve a flag for the Zcmp RV32 CPU extension. This commit performs the necessary changes to handle an additional RV32 CPU extension flag, for the Zcmp extension in this case. The changes are not limited to RV32-only code, as other parts of the tooling need to be modified for this: the testing framework has to be made aware that an extra bit can be set in sys.implementation._mpy and needs to know how it is called, and "mpy-cross" must be able to actually set that flag bit in the first place via the appropriate command line argument. Signed-off-by: Alessandro Gatti --- mpy-cross/main.c | 37 +++++++++++++++++++++++++++++++------ py/asmrv32.h | 6 ++++-- py/mpconfig.h | 5 +++++ tests/run-tests.py | 5 ++++- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 38190671c6ddd..c9109788fb3da 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -145,8 +145,8 @@ static int usage(char **argv) { "-march= : set architecture for native emitter;\n" " x86, x64, armv6, armv6m, armv7m, armv7em, armv7emsp,\n" " armv7emdp, xtensa, xtensawin, rv32imc, rv64imc, host, debug\n" - "-march-flags= : set architecture-specific flags (can be either a dec/hex/bin value or a string)\n" - " supported flags for rv32imc: zba\n" + "-march-flags= : set architecture-specific flags (can be either a dec/hex/bin value or a comma-separated flags string)\n" + " supported flags for rv32imc: zba, zcmp\n" "\n" "Implementation specific options:\n", argv[0] ); @@ -259,6 +259,34 @@ static bool parse_integer(const char *value, mp_uint_t *integer) { return valid; } +#if MICROPY_EMIT_NATIVE && MICROPY_EMIT_RV32 +static bool parse_rv32_flags_string(const char *source, mp_uint_t *flags) { + assert(source && "Flag arguments string is NULL."); + assert(flags && "Collected flags pointer is NULL."); + + const char *current = source; + const char *end = source + strlen(source); + mp_uint_t collected_flags = 0; + while (current < end) { + const char *separator = strchr(current, ','); + if (separator == NULL) { + separator = end; + } + ptrdiff_t length = separator - current; + if (length == (sizeof("zba") - 1) && memcmp(current, "zba", length) == 0) { + collected_flags |= RV32_EXT_ZBA; + } else if (length == (sizeof("zcmp") - 1) && memcmp(current, "zcmp", length) == 0) { + collected_flags |= RV32_EXT_ZCMP; + } else { + return false; + } + current = separator + 1; + } + *flags = collected_flags; + return collected_flags != 0; +} +#endif + MP_NOINLINE int main_(int argc, char **argv) { pre_process_options(argc, argv); @@ -412,14 +440,11 @@ MP_NOINLINE int main_(int argc, char **argv) { if (mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_RV32IMC) { mp_dynamic_compiler.backend_options = (void *)&rv32_options; mp_uint_t raw_flags = 0; - if (parse_integer(arch_flags, &raw_flags)) { + if (parse_integer(arch_flags, &raw_flags) || parse_rv32_flags_string(arch_flags, &raw_flags)) { if ((raw_flags & ~((mp_uint_t)RV32_EXT_ALL)) == 0) { rv32_options.allowed_extensions = raw_flags; processed = true; } - } else if (strncmp(arch_flags, "zba", sizeof("zba") - 1) == 0) { - rv32_options.allowed_extensions |= RV32_EXT_ZBA; - processed = true; } } #endif diff --git a/py/asmrv32.h b/py/asmrv32.h index 1100d098019ff..ed1b5a835c5b9 100644 --- a/py/asmrv32.h +++ b/py/asmrv32.h @@ -125,8 +125,9 @@ typedef struct _asm_rv32_t { enum { RV32_EXT_NONE = 0, RV32_EXT_ZBA = 1 << 0, + RV32_EXT_ZCMP = 1 << 1, - RV32_EXT_ALL = RV32_EXT_ZBA + RV32_EXT_ALL = RV32_EXT_ZBA | RV32_EXT_ZCMP }; typedef struct _asm_rv32_backend_options_t { @@ -710,7 +711,8 @@ static inline void asm_rv32_opcode_xori(asm_rv32_t *state, mp_uint_t rd, mp_uint } #define MICROPY_RV32_EXTENSIONS \ - (MICROPY_EMIT_RV32_ZBA ? RV32_EXT_ZBA : 0) + ((MICROPY_EMIT_RV32_ZBA ? RV32_EXT_ZBA : 0) | \ + (MICROPY_EMIT_RV32_ZCMP ? RV32_EXT_ZCMP : 0)) static inline uint8_t asm_rv32_allowed_extensions(void) { uint8_t extensions = MICROPY_RV32_EXTENSIONS; diff --git a/py/mpconfig.h b/py/mpconfig.h index 666b15573d9c1..e272564a7f2e9 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -507,6 +507,11 @@ typedef uint64_t mp_uint_t; #define MICROPY_EMIT_RV32_ZBA (0) #endif +// Whether to emit RISC-V RV32 Zcmp opcodes in native code +#ifndef MICROPY_EMIT_RV32_ZCMP +#define MICROPY_EMIT_RV32_ZCMP (0) +#endif + // Whether to enable the RISC-V RV32 inline assembler #ifndef MICROPY_EMIT_INLINE_RV32 #define MICROPY_EMIT_INLINE_RV32 (0) diff --git a/tests/run-tests.py b/tests/run-tests.py index d4c6d445e95c3..7f666e09b564a 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -22,7 +22,10 @@ # are guaranteed to always work, this one should though. BASEPATH = os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: None))) -RV32_ARCH_FLAGS = {"zba": 1 << 0} +RV32_ARCH_FLAGS = { + "zba": 1 << 0, + "zcmp": 1 << 1, +} def base_path(*p): From 7a69b2d786f3a5d0ae75a7d1aa89f38be82fcd88 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 10 Nov 2025 22:43:44 +0100 Subject: [PATCH 075/177] py/asmrv32: Use Zcmp opcodes for function prologues and epilogues. This commit introduces the possibility of using Zcmp opcodes when generating function prologues and epilogues, reducing the generated code size. With the addition of selected Zcmp opcodes, each generated function can be up to 30 bytes shorter and having a faster prologue and epilogue. If Zcmp opcodes can be used then register saving is a matter of a simple CM.PUSH opcode rather than a series of C.SWSP opcodes. Conversely, register restoring is a single CM.POPRET opcode instead of a series of C.LWSP opcodes followed by a C.JR RA opcode. This should also lead to faster code given that there's only one opcode doing the registers saving rather than a series of them. For functions that allocate less than three locals then the generated code will allocate up to 12 bytes of unused stack space. Whilst this is a relatively rare occurrence for generated native and viper code, inline assembler blocks will probably incur into this penalty. Still, considering that at the moment the only targets that support Zcmp opcodes are relatively high-end MCUs (the RP2350 in RV32 mode and the ESP32P4), this is probably not much of an issue. Signed-off-by: Alessandro Gatti --- py/asmrv32.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++------ py/asmrv32.h | 20 ++++++++++++-- 2 files changed, 86 insertions(+), 11 deletions(-) diff --git a/py/asmrv32.c b/py/asmrv32.c index 1d0cea6c02616..8b643af562386 100644 --- a/py/asmrv32.c +++ b/py/asmrv32.c @@ -53,6 +53,14 @@ ((((value) & ~((1U << ((bits) - 1)) - 1)) == 0) || \ (((value) & ~((1U << ((bits) - 1)) - 1)) == ~((1U << ((bits) - 1)) - 1))) +static bool asm_rv32_allow_zba_opcodes(void) { + return asm_rv32_allowed_extensions() & RV32_EXT_ZBA; +} + +static bool asm_rv32_allow_zcmp_opcodes(void) { + return asm_rv32_allowed_extensions() & RV32_EXT_ZCMP; +} + /////////////////////////////////////////////////////////////////////////////// void asm_rv32_emit_word_opcode(asm_rv32_t *state, mp_uint_t word) { @@ -214,6 +222,14 @@ static void adjust_stack(asm_rv32_t *state, mp_int_t stack_size) { return; } + // WARNING: If REG_TEMP0 is not set to a caller-saved register, then this + // bit has to be rewritten to avoid clobbering the temporary + // register when performing the stack adjustment. + + MP_STATIC_ASSERT(((REG_TEMP0 >= ASM_RV32_REG_T0) && (REG_TEMP0 <= ASM_RV32_REG_T2)) || \ + ((REG_TEMP0 >= ASM_RV32_REG_A0) && (REG_TEMP0 <= ASM_RV32_REG_A7)) || \ + ((REG_TEMP0 >= ASM_RV32_REG_T3) && (REG_TEMP0 <= ASM_RV32_REG_T6))); + // li temporary, stack_size // c.add sp, temporary load_full_immediate(state, REG_TEMP0, stack_size); @@ -245,6 +261,45 @@ static void emit_function_epilogue(asm_rv32_t *state, mp_uint_t registers) { state->saved_registers_mask = old_saved_registers_mask; } +static mp_uint_t compute_zcmp_sequence_length(mp_uint_t registers) { + // Can only handle RA and S0..S11 and must have at least one entry. + assert((registers != 0) && (registers & (~0x0FFC0302U)) == 0 && "Invalid Zcmp registers set."); + mp_uint_t length = 32 - mp_clz(((registers & 0x00000002) >> 1) | ((registers & 0x00000300) >> 7) | ((registers & 0x0FFC0000) >> 15)); + return length == 12 ? 13 : length; +} + +#define EMIT_ASSERT(state, condition, message) assert((((state)->base.pass != MP_ASM_PASS_EMIT) ? true : (condition)) && (message)) + +static void emit_compressed_function_prologue(asm_rv32_t *state, mp_uint_t registers_mask) { + mp_uint_t sequence_length = compute_zcmp_sequence_length(registers_mask); + mp_uint_t allocated_stack = (sequence_length + 3) & (mp_uint_t)-4; + EMIT_ASSERT(state, allocated_stack >= sequence_length, "Incorrect allocated stack calculation."); + mp_uint_t tail_slack = allocated_stack - sequence_length; + mp_uint_t locals_left = (state->locals_count < tail_slack) ? 0 : (state->locals_count - tail_slack); + mp_uint_t adjustment_chunks = MIN(3, locals_left / 4); + EMIT_ASSERT(state, (adjustment_chunks * 4) <= locals_left, "Incorrect adjustment chunks rounding."); + locals_left -= adjustment_chunks * 4; + EMIT_ASSERT(state, locals_left <= (MP_INT_MAX / sizeof(uint32_t)), "Too many locals."); + mp_int_t stack_size = (mp_int_t)(locals_left * sizeof(uint32_t)); + asm_rv32_opcode_cmpush(state, MIN(3 + sequence_length, 15), adjustment_chunks); + // CM.PUSH allocates a stack block and then puts the registers *at the end* + // of the block, so for example "CM.PUSH {RA, S0-S11}, -64" will put RA at + // SP + 60, not at SP + 0. + adjust_stack(state, -stack_size); + // The stack size is expressed in bytes and as a multiple of 4, hence the + // bottom two bits are not used. Since there can be up to three adjustment + // chunks, that number can be expressed in two bits, fitting nicely in the + // existing variable. + state->stack_size = ((mp_uint_t)stack_size) | adjustment_chunks; +} + +static void emit_compressed_function_epilogue(asm_rv32_t *state, mp_uint_t registers_mask) { + mp_uint_t sequence_length = compute_zcmp_sequence_length(registers_mask); + mp_uint_t stack_size = state->stack_size & (mp_uint_t)(~0x03U); + adjust_stack(state, stack_size); + asm_rv32_opcode_cmpopret(state, MIN(3 + sequence_length, 15), state->stack_size & 0x03); +} + static bool calculate_displacement_for_label(asm_rv32_t *state, mp_uint_t label, ptrdiff_t *displacement) { assert(displacement != NULL && "Displacement pointer is NULL"); @@ -256,16 +311,24 @@ 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->locals_count = locals; state->saved_registers_mask |= (1U << REG_FUN_TABLE) | (1U << REG_LOCAL_1) | \ (1U << REG_LOCAL_2) | (1U << REG_LOCAL_3); - state->locals_count = locals; - emit_function_prologue(state, state->saved_registers_mask); + if (asm_rv32_allow_zcmp_opcodes()) { + emit_compressed_function_prologue(state, state->saved_registers_mask); + } else { + emit_function_prologue(state, state->saved_registers_mask); + } } void asm_rv32_exit(asm_rv32_t *state) { - emit_function_epilogue(state, state->saved_registers_mask); - // c.jr ra - asm_rv32_opcode_cjr(state, ASM_RV32_REG_RA); + if (asm_rv32_allow_zcmp_opcodes()) { + emit_compressed_function_epilogue(state, state->saved_registers_mask); + } else { + emit_function_epilogue(state, state->saved_registers_mask); + // c.jr ra + asm_rv32_opcode_cjr(state, ASM_RV32_REG_RA); + } } void asm_rv32_end_pass(asm_rv32_t *state) { @@ -557,10 +620,6 @@ void asm_rv32_emit_optimised_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) asm_rv32_opcode_xor(state, rd, rd, rs); } -static bool asm_rv32_allow_zba_opcodes(void) { - return asm_rv32_allowed_extensions() & RV32_EXT_ZBA; -} - static void asm_rv32_fix_up_scaled_reg_reg_reg(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t operation_size) { assert(operation_size <= 2 && "Operation size value out of range."); diff --git a/py/asmrv32.h b/py/asmrv32.h index ed1b5a835c5b9..c25b1aa4e2622 100644 --- a/py/asmrv32.h +++ b/py/asmrv32.h @@ -197,6 +197,10 @@ void asm_rv32_end_pass(asm_rv32_t *state); ((rs & 0x07) << 7) | ((imm & 0x40) >> 1) | ((imm & 0x38) << 7) | \ ((imm & 0x04) << 4)) +#define RV32_ENCODE_TYPE_CMPP(op, ft6, ft2, rlist, imm) \ + ((op & 0x03) | ((ft6 & 0x3F) << 10) | ((ft2 & 0x03) << 8) | \ + ((rlist & 0x0F) << 4) | ((imm & 0x03) << 2)) + #define RV32_ENCODE_TYPE_CR(op, ft4, rs1, rs2) \ ((op & 0x03) | ((rs2 & 0x1F) << 2) | ((rs1 & 0x1F) << 7) | ((ft4 & 0x0F) << 12)) @@ -440,6 +444,18 @@ static inline void asm_rv32_opcode_cxor(asm_rv32_t *state, mp_uint_t rd, mp_uint asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CA(0x01, 0x23, 0x01, rd, rs)); } +// CM.POPRET {REG_LIST}, IMMEDIATE +static inline void asm_rv32_opcode_cmpopret(asm_rv32_t *state, mp_uint_t reg_list, mp_uint_t immediate) { + // CMPP: 10111110 ... .. 10 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CMPP(0x02, 0x2F, 0x02, reg_list, immediate)); +} + +// CM.PUSH {REG_LIST}, -IMMEDIATE +static inline void asm_rv32_opcode_cmpush(asm_rv32_t *state, mp_uint_t reg_list, mp_uint_t immediate) { + // CMPP: 10111000 .... .. 10 + asm_rv32_emit_halfword_opcode(state, RV32_ENCODE_TYPE_CMPP(0x02, 0x2E, 0x00, reg_list, immediate)); +} + // 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 @@ -737,8 +753,8 @@ static inline uint8_t asm_rv32_allowed_extensions(void) { #define REG_TEMP2 ASM_RV32_REG_T3 #define REG_FUN_TABLE ASM_RV32_REG_S1 #define REG_LOCAL_1 ASM_RV32_REG_S3 -#define REG_LOCAL_2 ASM_RV32_REG_S4 -#define REG_LOCAL_3 ASM_RV32_REG_S5 +#define REG_LOCAL_2 ASM_RV32_REG_S2 +#define REG_LOCAL_3 ASM_RV32_REG_S4 #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); From 08096392b58411c6b30031683af7d49760c19831 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 10 Nov 2025 22:57:42 +0100 Subject: [PATCH 076/177] rp2/mpconfigport: Enable Zcmp opcodes for RP2350 in RV32 mode. This commit enables support for Zcmp opcodes when the firmware is built for the RP2350 in RV32 mode. The RP2350 explicitly supports the Zcmp extension for reducing the amount of code needed for function prologues and epilogues (see section 3.8.1.20 of the datasheet). 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 8cc12aff1c263..1bcd7384911b4 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -110,6 +110,7 @@ #elif PICO_RISCV #define MICROPY_EMIT_RV32 (1) #define MICROPY_EMIT_RV32_ZBA (1) +#define MICROPY_EMIT_RV32_ZCMP (1) #define MICROPY_EMIT_INLINE_RV32 (1) #endif From ec5f2bc686a51aad903518c0f84d7d39d3b3432c Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 10 Nov 2025 23:14:15 +0100 Subject: [PATCH 077/177] rp2/CMakeLists.txt: Set the appropriate mpy-cross flags on all targets. This commit lets the RP2 port build system use the appropriate flags to pass to "mpy-cross" when building frozen MPY files as part of the build process. Now all possible variants (RP2040, RP2350/Arm, and RP2350/RV32) have their right flags assigned, falling back the flags set of the RP2040 if a new variant is introduced. Before these changes all variants would use the RP2040 set of flags which may be a bit of an issue when building code for the RP2350 in RV32 mode. Signed-off-by: Alessandro Gatti --- ports/rp2/CMakeLists.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index c4a081e2efd54..d88a96d4a7074 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -487,7 +487,16 @@ list(APPEND MICROPY_SOURCE_QSTR ) # Define mpy-cross flags -set(MICROPY_CROSS_FLAGS -march=armv6m) +if (${PICO_PLATFORM} STREQUAL "rp2040") + set(MICROPY_CROSS_FLAGS "-march=armv6m") +elseif (${PICO_PLATFORM} STREQUAL "rp2350-arm-s") + set(MICROPY_CROSS_FLAGS "-march=armv7m") +elseif (${PICO_PLATFORM} STREQUAL "rp2350-riscv") + set(MICROPY_CROSS_FLAGS "-march=rv32imc -march-flags=zba,zcmp") +else() + message(WARNING "Unknown PICO_PLATFORM version (${PICO_PLATFORM}), falling back to rp2040") + set(MICROPY_CROSS_FLAGS "-march=armv6m") +endif() # Set the frozen manifest file if (MICROPY_USER_FROZEN_MANIFEST) From 634125820744efa33679fb95a6e441dadaa4f6a7 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 11 Dec 2025 03:42:33 +0100 Subject: [PATCH 078/177] esp32/mpconfigport: Enable Zcmp opcodes for ESP32P4. This commit enables support for Zcmp opcodes when the firmware is built to target ESP32P4 microcontrollers. The ESP32P4 explicitly supports the Zcmp extension for reducing the amount of code needed for function prologues and epilogues (see section 4.1.1.1 of the ESP32P4 datasheet). Signed-off-by: Alessandro Gatti --- ports/esp32/esp32_common.cmake | 6 +++++- ports/esp32/mpconfigport.h | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index e3b1b81cae7b9..ea60c395a7665 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -244,7 +244,11 @@ set(MICROPY_TARGET ${COMPONENT_TARGET}) if(CONFIG_IDF_TARGET_ARCH_XTENSA) set(MICROPY_CROSS_FLAGS -march=xtensawin) elseif(CONFIG_IDF_TARGET_ARCH_RISCV) - set(MICROPY_CROSS_FLAGS -march=rv32imc) + if (CONFIG_IDF_TARGET_ESP32P4) + set(MICROPY_CROSS_FLAGS "-march=rv32imc -march-flags=zcmp") + else() + set(MICROPY_CROSS_FLAGS -march=rv32imc) + endif() endif() # Set compile options for this port. diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 55503ff0baed3..69419ce009b69 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -44,6 +44,9 @@ #define MICROPY_EMIT_RV32 (0) #else #define MICROPY_EMIT_RV32 (1) +#if CONFIG_IDF_TARGET_ESP32P4 +#define MICROPY_EMIT_RV32_ZCMP (1) +#endif #endif #else #define MICROPY_EMIT_XTENSAWIN (1) From b0f3ecd96c8ca87f38366422f8cb74e0ce9fa2ec Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 20 Dec 2025 07:13:51 +0100 Subject: [PATCH 079/177] py/persistentcode: Decouple native code loading from emitters' presence. This commit lets the interpreter load MPY files containing native code even if the target platform does not have a native emitter, or if native code generation is disabled. Native code loading has been tied to native code generation being enabled as a discriminant to allow said operation. This blocks native code loading on platforms that could benefit from such a thing but they don't (and probably won't) have a native code generation target written for them (ie. AArch64 and RISC-V 64). This also forces a firmware image to have a full native code compiler present even if it doesn't need to generate anything, as native modules already have all the code they will ever need to load. There is a new configuration setting, MICROPY_PERSISTENT_CODE_LOAD_NATIVE, that if enabled it will allow loading native code modules even if code generation (MICROPY_EMIT_ and MICROPY_EMIT_INLINE_) is explicitly turned off. Signed-off-by: Alessandro Gatti --- py/bc.c | 2 +- py/emitglue.c | 4 ++-- py/emitglue.h | 4 ++-- py/mpconfig.h | 15 ++++++++++++--- py/nativeglue.c | 10 +++++----- py/nativeglue.h | 4 ++-- py/objfun.c | 10 +++------- py/objfun.h | 2 +- py/objgenerator.c | 10 +++++----- py/persistentcode.c | 14 +++++++------- py/runtime.c | 2 +- 11 files changed, 41 insertions(+), 36 deletions(-) diff --git a/py/bc.c b/py/bc.c index cea31c93bd8a0..777ff66fcd446 100644 --- a/py/bc.c +++ b/py/bc.c @@ -332,7 +332,7 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw mp_setup_code_state_helper(code_state, n_args, n_kw, args); } -#if MICROPY_EMIT_NATIVE +#if MICROPY_ENABLE_NATIVE_CODE // On entry code_state should be allocated somewhere (stack/heap) and // contain the following valid entries: // - code_state->fun_bc should contain a pointer to the function object diff --git a/py/emitglue.c b/py/emitglue.c index 8a32b227ffff3..4711e146fc51f 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -92,7 +92,7 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, #endif } -#if MICROPY_EMIT_MACHINE_CODE +#if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, const void *fun_data, mp_uint_t fun_len, mp_raw_code_t **children, #if MICROPY_PERSISTENT_CODE_SAVE @@ -201,7 +201,7 @@ mp_obj_t mp_make_function_from_proto_fun(mp_proto_fun_t proto_fun, const mp_modu // make the function, depending on the raw code kind mp_obj_t fun; switch (rc->kind) { - #if MICROPY_EMIT_NATIVE + #if MICROPY_ENABLE_NATIVE_CODE case MP_CODE_NATIVE_PY: fun = mp_obj_new_fun_native(def_args, rc->fun_data, context, rc->children); // Check for a generator function, and if so change the type of the object diff --git a/py/emitglue.h b/py/emitglue.h index 126462671b003..6e2add803391a 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -80,7 +80,7 @@ typedef struct _mp_raw_code_t { #if MICROPY_PERSISTENT_CODE_SAVE uint32_t fun_data_len; // for mp_raw_code_save uint16_t n_children; - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE uint16_t prelude_offset; #endif #if MICROPY_PY_SYS_SETTRACE @@ -110,7 +110,7 @@ typedef struct _mp_raw_code_truncated_t { #if MICROPY_PERSISTENT_CODE_SAVE uint32_t fun_data_len; uint16_t n_children; - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE uint16_t prelude_offset; #endif #if MICROPY_PY_SYS_SETTRACE diff --git a/py/mpconfig.h b/py/mpconfig.h index e272564a7f2e9..3211d4d398e05 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -411,6 +411,11 @@ typedef uint64_t mp_uint_t; #define MICROPY_PERSISTENT_CODE_LOAD (0) #endif +// Whether to support loading of persistent native code +#ifndef MICROPY_PERSISTENT_CODE_LOAD_NATIVE +#define MICROPY_PERSISTENT_CODE_LOAD_NATIVE (MICROPY_EMIT_MACHINE_CODE) +#endif + // Whether to support saving of persistent code, i.e. for mpy-cross to // generate .mpy files. Enabling this enables additional metadata on raw code // objects which is also required for sys.settrace. @@ -431,7 +436,7 @@ typedef uint64_t mp_uint_t; // Whether generated code can persist independently of the VM/runtime instance // This is enabled automatically when needed by other features #ifndef MICROPY_PERSISTENT_CODE -#define MICROPY_PERSISTENT_CODE (MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE || MICROPY_MODULE_FROZEN_MPY) +#define MICROPY_PERSISTENT_CODE (MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_LOAD_NATIVE || MICROPY_PERSISTENT_CODE_SAVE || MICROPY_MODULE_FROZEN_MPY) #endif // Whether bytecode uses a qstr_table to map internal qstr indices in the bytecode @@ -536,6 +541,10 @@ typedef uint64_t mp_uint_t; // Convenience definition for whether any native or inline assembler emitter is enabled #define MICROPY_EMIT_MACHINE_CODE (MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM) +// Convenience definition for whether native code has to be dealt with (either +// generated or loaded from a file). This does not cover inline asm code. +#define MICROPY_ENABLE_NATIVE_CODE (MICROPY_EMIT_NATIVE || MICROPY_PERSISTENT_CODE_LOAD_NATIVE) + /*****************************************************************************/ /* Compiler configuration */ @@ -2276,7 +2285,7 @@ typedef time_t mp_timestamp_t; // can be overridden if needed by defining both MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA // and MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA. #ifndef MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA -#if MICROPY_EMIT_MACHINE_CODE && MICROPY_PERSISTENT_CODE_LOAD +#if (MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE) && MICROPY_PERSISTENT_CODE_LOAD // Pointer tracking is required when loading native code is enabled. #if defined(MP_PLAT_ALLOC_EXEC) || defined(MP_PLAT_COMMIT_EXEC) // If a port defined a custom allocator or commit function for native text, then the @@ -2297,7 +2306,7 @@ typedef time_t mp_timestamp_t; #define MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA (1) #define MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA (0) #endif -#else // MICROPY_EMIT_MACHINE_CODE && MICROPY_PERSISTENT_CODE_LOAD +#else // (MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE) && MICROPY_PERSISTENT_CODE_LOAD // Pointer tracking not needed when loading native code is disabled. #define MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA (0) #define MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA (0) diff --git a/py/nativeglue.c b/py/nativeglue.c index 6bf16f1acc29a..e4aa635cf1c64 100644 --- a/py/nativeglue.c +++ b/py/nativeglue.c @@ -41,7 +41,7 @@ #define DEBUG_printf(...) (void)0 #endif -#if MICROPY_EMIT_NATIVE +#if MICROPY_ENABLE_NATIVE_CODE int mp_native_type_from_qstr(qstr qst) { switch (qst) { @@ -91,7 +91,7 @@ mp_uint_t mp_native_from_obj(mp_obj_t obj, mp_uint_t type) { #endif -#if MICROPY_EMIT_MACHINE_CODE +#if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE // convert a native value to a MicroPython object based on type mp_obj_t mp_native_to_obj(mp_uint_t val, mp_uint_t type) { @@ -115,7 +115,7 @@ mp_obj_t mp_native_to_obj(mp_uint_t val, mp_uint_t type) { #endif -#if MICROPY_EMIT_NATIVE && !MICROPY_DYNAMIC_COMPILER +#if MICROPY_ENABLE_NATIVE_CODE && !MICROPY_DYNAMIC_COMPILER #if !MICROPY_PY_BUILTINS_SET mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items) { @@ -354,8 +354,8 @@ const mp_fun_table_t mp_fun_table = { &mp_stream_write_obj, }; -#elif MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER +#elif MICROPY_ENABLE_NATIVE_CODE && MICROPY_DYNAMIC_COMPILER const int mp_fun_table; -#endif // MICROPY_EMIT_NATIVE +#endif // MICROPY_ENABLE_NATIVE_CODE diff --git a/py/nativeglue.h b/py/nativeglue.h index 2c7923c56df85..92d3889df393a 100644 --- a/py/nativeglue.h +++ b/py/nativeglue.h @@ -181,9 +181,9 @@ typedef struct _mp_fun_table_t { const mp_obj_fun_builtin_var_t *stream_write_obj; } mp_fun_table_t; -#if (MICROPY_EMIT_NATIVE && !MICROPY_DYNAMIC_COMPILER) || MICROPY_ENABLE_DYNRUNTIME +#if (MICROPY_ENABLE_NATIVE_CODE && !MICROPY_DYNAMIC_COMPILER) || MICROPY_ENABLE_DYNRUNTIME extern const mp_fun_table_t mp_fun_table; -#elif MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER +#elif MICROPY_ENABLE_NATIVE_CODE && MICROPY_DYNAMIC_COMPILER // In dynamic-compiler mode eliminate dependency on entries in mp_fun_table. // This only needs to be an independent pointer, content doesn't matter. extern const int mp_fun_table; diff --git a/py/objfun.c b/py/objfun.c index a742c5267254c..933da1e494d81 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -136,7 +136,7 @@ qstr mp_obj_fun_get_name(mp_const_obj_t fun_in) { const mp_obj_fun_bc_t *fun = MP_OBJ_TO_PTR(fun_in); const byte *bc = fun->bytecode; - #if MICROPY_EMIT_NATIVE + #if MICROPY_ENABLE_NATIVE_CODE if (fun->base.type == &mp_type_fun_native || fun->base.type == &mp_type_native_gen_wrap) { bc = mp_obj_fun_native_get_prelude_ptr(fun); } @@ -443,7 +443,7 @@ mp_obj_t mp_obj_new_fun_bc(const mp_obj_t *def_args, const byte *code, const mp_ /******************************************************************************/ /* native functions */ -#if MICROPY_EMIT_NATIVE +#if MICROPY_ENABLE_NATIVE_CODE static mp_obj_t fun_native_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_cstack_check(); @@ -472,13 +472,9 @@ MP_DEFINE_CONST_OBJ_TYPE( call, fun_native_call ); -#endif // MICROPY_EMIT_NATIVE - /******************************************************************************/ /* viper functions */ -#if MICROPY_EMIT_NATIVE - static mp_obj_t fun_viper_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_cstack_check(); mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); @@ -493,7 +489,7 @@ MP_DEFINE_CONST_OBJ_TYPE( call, fun_viper_call ); -#endif // MICROPY_EMIT_NATIVE +#endif // MICROPY_ENABLE_NATIVE_CODE /******************************************************************************/ /* inline assembler functions */ diff --git a/py/objfun.h b/py/objfun.h index f16ef76b80e70..8494eb4bf6608 100644 --- a/py/objfun.h +++ b/py/objfun.h @@ -53,7 +53,7 @@ typedef struct _mp_obj_fun_asm_t { mp_obj_t mp_obj_new_fun_bc(const mp_obj_t *def_args, const byte *code, const mp_module_context_t *cm, struct _mp_raw_code_t *const *raw_code_table); void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest); -#if MICROPY_EMIT_NATIVE +#if MICROPY_ENABLE_NATIVE_CODE static inline mp_obj_t mp_obj_new_fun_native(const mp_obj_t *def_args, const void *fun_data, const mp_module_context_t *mc, struct _mp_raw_code_t *const *child_table) { mp_obj_fun_bc_t *o = (mp_obj_fun_bc_t *)MP_OBJ_TO_PTR(mp_obj_new_fun_bc(def_args, (const byte *)fun_data, mc, child_table)); diff --git a/py/objgenerator.c b/py/objgenerator.c index df9701094b80c..e2a02d494ed1c 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -87,7 +87,7 @@ MP_DEFINE_CONST_OBJ_TYPE( /******************************************************************************/ // native generator wrapper -#if MICROPY_EMIT_NATIVE +#if MICROPY_ENABLE_NATIVE_CODE // Based on mp_obj_gen_instance_t. typedef struct _mp_obj_gen_instance_native_t { @@ -139,7 +139,7 @@ MP_DEFINE_CONST_OBJ_TYPE( NATIVE_GEN_WRAP_TYPE_ATTR ); -#endif // MICROPY_EMIT_NATIVE +#endif // MICROPY_ENABLE_NATIVE_CODE /******************************************************************************/ /* generator instance */ @@ -175,7 +175,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ // If the generator is started, allow sending a value. void *state_start = self->code_state.state - 1; - #if MICROPY_EMIT_NATIVE + #if MICROPY_ENABLE_NATIVE_CODE if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) { state_start = ((mp_obj_gen_instance_native_t *)self)->code_state.state - 1; } @@ -197,7 +197,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ mp_vm_return_kind_t ret_kind; - #if MICROPY_EMIT_NATIVE + #if MICROPY_ENABLE_NATIVE_CODE if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) { // A native generator. typedef uintptr_t (*mp_fun_native_gen_t)(void *, mp_obj_t); @@ -235,7 +235,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ case MP_VM_RETURN_EXCEPTION: { self->code_state.ip = 0; - #if MICROPY_EMIT_NATIVE + #if MICROPY_ENABLE_NATIVE_CODE if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) { *ret_val = ((mp_obj_gen_instance_native_t *)self)->code_state.state[0]; } else diff --git a/py/persistentcode.c b/py/persistentcode.c index b12c100743392..d83386736b21c 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -76,7 +76,7 @@ typedef struct _bytecode_prelude_t { static int read_byte(mp_reader_t *reader); static size_t read_uint(mp_reader_t *reader); -#if MICROPY_EMIT_MACHINE_CODE +#if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA @@ -222,7 +222,7 @@ static mp_obj_t mp_obj_new_str_static(const mp_obj_type_t *type, const byte *dat static mp_obj_t load_obj(mp_reader_t *reader) { byte obj_type = read_byte(reader); - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE if (obj_type == MP_PERSISTENT_OBJ_FUN_TABLE) { return MP_OBJ_FROM_PTR(&mp_fun_table); } else @@ -295,14 +295,14 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co bool has_children = !!(kind_len & 4); size_t fun_data_len = kind_len >> 3; - #if !MICROPY_EMIT_MACHINE_CODE + #if !(MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE) if (kind != MP_CODE_BYTECODE) { mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); } #endif uint8_t *fun_data = NULL; - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE size_t prelude_offset = 0; mp_uint_t native_scope_flags = 0; mp_uint_t native_n_pos_args = 0; @@ -322,7 +322,7 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co read_bytes(reader, fun_data, fun_data_len); } - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE } else { // Allocate memory for native data and load it size_t fun_alloc; @@ -349,7 +349,7 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co size_t n_children = 0; mp_raw_code_t **children = NULL; - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE // Load optional BSS/rodata for viper. uint8_t *rodata = NULL; uint8_t *bss = NULL; @@ -404,7 +404,7 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co #endif scope_flags); - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE } else { const uint8_t *prelude_ptr = NULL; #if MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE diff --git a/py/runtime.c b/py/runtime.c index ddc1ef7d3ead0..8d7e11e5f0dab 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -123,7 +123,7 @@ void mp_init(void) { MP_STATE_VM(mp_module_builtins_override_dict) = NULL; #endif - #if MICROPY_EMIT_MACHINE_CODE && (MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA) + #if (MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE) && (MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA) MP_STATE_VM(persistent_code_root_pointers) = MP_OBJ_NULL; #endif From 5ae928a2b4ebaab62971280ce531b4e47014f247 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 19 Dec 2025 21:20:49 +0100 Subject: [PATCH 080/177] py/persistentcode: Detect the target architecture from compiler defines. This commit replaces the target architecture definition method, from relying on defines in MicroPython's configuration, to using the host compiler's environment definitions instead. Before these changes the target type was inferred from which kind of native emitter was enabled, since there can be only just one available at all times and it has to be the correct one otherwise generated code would crash the target. However, with the introduction of RV64 there is now a platform without an emitter, and thus RV64 targets would not be able to report their platform via "sys.implementation._mpy". The target is reported only if the interpreter is configured to load external MPY code. Now a series of compile definitions are used to detect the target platform the firmware image is compiled for, making the target definition more accurate and much harder to force the interpreter to report the wrong target architecture due to a misconfiguration. Whilst this is probably not directly affecting user-facing code, some infrastructure components like the testing framework and its associated feature scripts rely on this feature to properly execute test runs. This is also a concern for situations in which loading external binary code is needed but all code generation functions have to be disabled. Signed-off-by: Alessandro Gatti --- py/persistentcode.h | 55 ++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/py/persistentcode.h b/py/persistentcode.h index 8fd7068e3cd52..1e0bbbd272ffd 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -48,31 +48,44 @@ #define MPY_FEATURE_DECODE_ARCH(feat) (((feat) >> 2) & 0x2F) // Define the host architecture -#if MICROPY_EMIT_X86 - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X86) -#elif MICROPY_EMIT_X64 - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X64) -#elif MICROPY_EMIT_THUMB - #if defined(__thumb2__) - #if defined(__ARM_FP) && (__ARM_FP & 8) == 8 - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMDP) - #elif defined(__ARM_FP) && (__ARM_FP & 4) == 4 - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMSP) +#if MICROPY_PERSISTENT_CODE_LOAD_NATIVE + #if defined(__i386__) || defined(_M_IX86) + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X86) + #elif defined(__x86_64__) || defined(_M_X64) + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X64) + #elif defined(__thumb2__) || defined(__thumb__) + #if defined(__thumb2__) + #if defined(__ARM_FP) && (__ARM_FP & 8) == 8 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMDP) + #elif defined(__ARM_FP) && (__ARM_FP & 4) == 4 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMSP) + #else + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EM) + #endif #else - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EM) + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6M) + #endif + #define MPY_FEATURE_ARCH_TEST(x) (MP_NATIVE_ARCH_ARMV6M <= (x) && (x) <= MPY_FEATURE_ARCH) + #elif defined(__arm__) + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6) + #elif defined(__xtensa__) + #include + #if XCHAL_HAVE_WINDOWED + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSAWIN) + #else + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSA) + #endif + #elif defined(__riscv) + #if __riscv_xlen == 32 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_RV32IMC) + #elif __riscv_xlen == 64 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_RV64IMC) + #else + #error "Unsupported RISC-V architecture." #endif #else - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6M) + #error "Unsupported native architecture." #endif - #define MPY_FEATURE_ARCH_TEST(x) (MP_NATIVE_ARCH_ARMV6M <= (x) && (x) <= MPY_FEATURE_ARCH) -#elif MICROPY_EMIT_ARM - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6) -#elif MICROPY_EMIT_XTENSA - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSA) -#elif MICROPY_EMIT_XTENSAWIN - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSAWIN) -#elif MICROPY_EMIT_RV32 - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_RV32IMC) #else #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_NONE) #endif From 9e9da6cb0d794e85dbafcabd39972528afc15474 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 20 Dec 2025 10:02:57 +0100 Subject: [PATCH 081/177] py/emitglue: Flush caches when loading native code without emitters. This commit forces a data/instruction cache flush after loading native code blocks even if there's no emitter available. Caches flush upon native code load was still tied to the presence of a native emitter, but this assumption is no longer valid due to previous commits decoupling native code loading from code generation availability. Signed-off-by: Alessandro Gatti --- py/emitglue.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py/emitglue.c b/py/emitglue.c index 4711e146fc51f..a72f94c88bded 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -107,14 +107,14 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons // Some architectures require flushing/invalidation of the I/D caches, // so that the generated native code which was created in data RAM will // be available for execution from instruction RAM. - #if MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB + #if MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB || (MP_NATIVE_ARCH_ARMV6M <= MPY_FEATURE_ARCH && MPY_FEATURE_ARCH <= MP_NATIVE_ARCH_ARMV7EMDP) #if __ICACHE_PRESENT == 1 // Flush D-cache, so the code emitted is stored in RAM. MP_HAL_CLEAN_DCACHE(fun_data, fun_len); // Invalidate I-cache, so the newly-created code is reloaded from RAM. SCB_InvalidateICache(); #endif - #elif MICROPY_EMIT_ARM + #elif MICROPY_EMIT_ARM || (MPY_FEATURE_ARCH == MP_NATIVE_ARCH_ARMV6) #if (defined(__linux__) && defined(__GNUC__)) || __ARM_ARCH == 7 __builtin___clear_cache((void *)fun_data, (char *)fun_data + fun_len); #elif defined(__arm__) @@ -127,7 +127,7 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons "mcr p15, 0, r0, c7, c7, 0\n" // invalidate I-cache and D-cache : : : "r0", "cc"); #endif - #elif (MICROPY_EMIT_RV32 || MICROPY_EMIT_INLINE_RV32) && defined(MP_HAL_CLEAN_DCACHE) + #elif (MICROPY_EMIT_RV32 || MICROPY_EMIT_INLINE_RV32 || (MPY_FEATURE_ARCH == MP_NATIVE_ARCH_RV32IMC)) && defined(MP_HAL_CLEAN_DCACHE) // Flush the D-cache. MP_HAL_CLEAN_DCACHE(fun_data, fun_len); #endif From 2b82f512d21b6d57f0ec803f892ce743f8864aa0 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 20 Dec 2025 07:23:31 +0100 Subject: [PATCH 082/177] unix/alloc: Map executable memory for code loading when needed. This commit lets the Unix port use the new MICROPY_PERSISTENT_CODE_LOAD_NATIVE configuration entry. The Unix port needs to allocate memory with specific flags that let the operating system run executable code from there. This functionality was gated behind the presence of a native emitter and thus its inclusion condition had to be updated. Signed-off-by: Alessandro Gatti --- ports/unix/alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/unix/alloc.c b/ports/unix/alloc.c index 9ab2ca04ebc97..7e1b261261e2b 100644 --- a/ports/unix/alloc.c +++ b/ports/unix/alloc.c @@ -32,7 +32,7 @@ #include "py/mpstate.h" -#if MICROPY_EMIT_NATIVE +#if MICROPY_ENABLE_NATIVE_CODE #if defined(__OpenBSD__) || defined(__MACH__) #define MAP_ANONYMOUS MAP_ANON @@ -80,4 +80,4 @@ void mp_unix_free_exec(void *ptr, size_t size) { MP_REGISTER_ROOT_POINTER(void *mmap_region_head); -#endif // MICROPY_EMIT_NATIVE +#endif // MICROPY_ENABLE_NATIVE_CODE From ecf88732938de7996d7d8f705fc9f9d9bed0850e Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 20 Dec 2025 09:26:02 +0100 Subject: [PATCH 083/177] qemu/Makefile: Allow overriding the test natmods list. This commit modifies the QEMU port makefile to let the user provide their own list of natmods to test as part of "test_natmod". The makefile now will replace the list of natmods to test with the contents of the "TEST_NATMODS" command line variable, so if there's a specific subset of natmods causing problems test runs can be limited to that very subset. "TEST_NATMODS" accepts a whitespace-separated list of natmod names for which there are available matching tests in "tests/extmod" (eg. make test_natmod TEST_NATMODS="btree heapq"). Signed-off-by: Alessandro Gatti --- ports/qemu/Makefile | 5 ++++- ports/qemu/README.md | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index c8ce3de872046..3bc46ffefdc4b 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -164,6 +164,9 @@ QEMU_DEBUG_ARGS ?= -s QEMU_ARGS += -S $(QEMU_DEBUG_ARGS) $(QEMU_DEBUG_EXTRA) endif +# Allow selecting a subset of natmods to test. +TEST_NATMODS ?= btree deflate framebuf heapq random_basic re + INC += -I. INC += -I$(TOP) INC += -I$(BUILD) @@ -265,7 +268,7 @@ test_full: $(BUILD)/firmware.elf test_natmod: $(BUILD)/firmware.elf $(eval DIRNAME=ports/$(notdir $(CURDIR))) cd $(TOP)/tests && \ - for natmod in btree deflate framebuf heapq random_basic re; do \ + for natmod in $(TEST_NATMODS); do \ ./run-natmodtests.py -t execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_EXTRA) extmod/$$natmod*.py; \ done diff --git a/ports/qemu/README.md b/ports/qemu/README.md index a7134d0eb87d7..0f53c000082a9 100644 --- a/ports/qemu/README.md +++ b/ports/qemu/README.md @@ -167,5 +167,8 @@ 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. +- `TEST_NATMODS`: pass an optional list of space-separated names of natmods to test, + so only the given subset of example natmods will be used by `test_natmod` (for + example, `make test_natmod TEST_NATMODS="btree heapq re"`). - `MICROPY_HEAP_SIZE`: pass in an optional value (in bytes) for overriding the GC heap size used by the port. From 14ea6d7fd867557be3f801b5bb2d2e6e2721b3e7 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 20 Dec 2025 10:10:14 +0100 Subject: [PATCH 084/177] esp8266/modesp: Allocate executable memory when needed. This commit lets the ESP8266 port use the new MICROPY_PERSISTENT_CODE_LOAD_NATIVE configuration entry. The ESP8266 port needs a special procedure to allocate memory used to hold executable native code. This functionality was gated behind the presence of a native emitter and thus its inclusion condition had to be updated. Signed-off-by: Alessandro Gatti --- ports/esp8266/modesp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/esp8266/modesp.c b/ports/esp8266/modesp.c index 724545202d8a0..4b62175509418 100644 --- a/ports/esp8266/modesp.c +++ b/ports/esp8266/modesp.c @@ -234,7 +234,7 @@ static mp_obj_t esp_esf_free_bufs(mp_obj_t idx_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(esp_esf_free_bufs_obj, esp_esf_free_bufs); -#if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA +#if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA || MICROPY_PERSISTENT_CODE_LOAD_NATIVE // We provide here a way of committing executable data to a region from // which it can be executed by the CPU. There are 2 such writable regions: @@ -358,7 +358,7 @@ static const mp_rom_map_elem_t esp_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_malloc), MP_ROM_PTR(&esp_malloc_obj) }, { MP_ROM_QSTR(MP_QSTR_free), MP_ROM_PTR(&esp_free_obj) }, { MP_ROM_QSTR(MP_QSTR_esf_free_bufs), MP_ROM_PTR(&esp_esf_free_bufs_obj) }, - #if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA + #if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA || MICROPY_PERSISTENT_CODE_LOAD_NATIVE { MP_ROM_QSTR(MP_QSTR_set_native_code_location), MP_ROM_PTR(&esp_set_native_code_location_obj) }, #endif From 14152e7f49fffdfaa404bf33e00ad247c70e1a4a Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 20 Dec 2025 10:42:44 +0100 Subject: [PATCH 085/177] nrf/main: Allocate executable memory when needed. This commit lets the nRF port use the new MICROPY_PERSISTENT_CODE_LOAD_NATIVE configuration entry. The nRF port needs a special procedure to allocate memory used to hold executable native code. This functionality was gated behind the presence of a native emitter and thus its inclusion condition had to be updated. Signed-off-by: Alessandro Gatti --- ports/nrf/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/nrf/main.c b/ports/nrf/main.c index e5d7828a82a58..7278a646ab6ec 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -368,7 +368,7 @@ void MP_WEAK __assert_func(const char *file, int line, const char *func, const c __fatal_error("Assertion failed"); } -#if MICROPY_EMIT_MACHINE_CODE +#if MICROPY_EMIT_INLINE_THUMB || MICROPY_ENABLE_NATIVE_CODE void *nrf_native_code_commit(void *buf, unsigned int len, void *reloc) { (void)len; if (reloc) { From 7173ddee5f979a3c54be7905d6c00aac29a90fda Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 20 Dec 2025 17:03:18 +0100 Subject: [PATCH 086/177] tests/run-natmodtests.py: Explicitly open prelude file. This change lets the natmod test runner report status information on session end if a prelude script file is chosen. The script serialises its input data as part of the end of session reporting data, but since the prelude file is not a serialisable object serialisation would fail (it's a file handle as far as the argument container structure is aware). Now the file is explicitly open by the script rather than relying on argparse's file handle argument class wrapper. Signed-off-by: Alessandro Gatti --- tests/run-natmodtests.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index 50e8d1272914e..e35e580dc1d4c 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -133,13 +133,6 @@ def detect_architecture(target): def run_tests(target_truth, target, args, resolved_arch): - global injected_import_hook_code - - prelude = "" - if args.begin: - prelude = args.begin.read() - injected_import_hook_code = injected_import_hook_code.replace("{import_prelude}", prelude) - test_results = [] for test_file in args.files: # Find supported test @@ -223,6 +216,8 @@ def run_tests(target_truth, target, args, resolved_arch): def main(): + global injected_import_hook_code + cmd_parser = argparse.ArgumentParser( description="Run dynamic-native-module tests under MicroPython", epilog=run_tests_module.test_instance_epilog, @@ -240,7 +235,7 @@ def main(): cmd_parser.add_argument( "-b", "--begin", - type=argparse.FileType("rt"), + metavar="PROLOGUE", default=None, help="prologue python file to execute before module import", ) @@ -253,6 +248,12 @@ def main(): cmd_parser.add_argument("files", nargs="*", help="input test files") args = cmd_parser.parse_args() + prologue = "" + if args.begin: + with open(args.begin, "rt") as source: + prologue = source.read() + injected_import_hook_code = injected_import_hook_code.replace("{import_prelude}", prologue) + target_truth = TargetSubprocess([CPYTHON3]) target = run_tests_module.get_test_instance( From 8fb848f3aacd0d2216e02954cff4e9e15b0915a2 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 22 Dec 2025 08:34:41 +0100 Subject: [PATCH 087/177] mpy-cross/mpconfigport: Explicitly disable native code loading. This commit makes use of the new native code loading changes to provide a test bed for a target with the native compiler framework available but without no code loading capabilities. mpy-cross already fully disables native code loading, however those changes do not really propagate anywhere outside `py/persistentcode`. Recent changes in the code loading framework actually touch the core code in a few more places, but no default CI target is configured to run with the new native code loading defines switched off. Given that mpy-cross doesn't really need to load code anyway, it is a good target for setting up a configuration that goes a bit deeper when it comes to disabling code loading. Since mpy-cross is built several times during the CI process, it can be repurposed as an early warning system for issues related to these new changes. Normal operation should not be affected in any way. Signed-off-by: Alessandro Gatti --- mpy-cross/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index febd18cc2b059..9c23257260cae 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -28,6 +28,7 @@ #define MICROPY_ALLOC_PATH_MAX (PATH_MAX) #define MICROPY_PERSISTENT_CODE_LOAD (0) +#define MICROPY_PERSISTENT_CODE_LOAD_NATIVE (0) #define MICROPY_PERSISTENT_CODE_SAVE (1) #ifndef MICROPY_PERSISTENT_CODE_SAVE_FILE From 984027b88e90323b9aa96ec793d5295a8873cd83 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 17 Nov 2025 03:30:47 +0100 Subject: [PATCH 088/177] tools/mpy_ld.py: Fix R_RISCV_GOT32_PCREL handling. This commit fixes the implementation of the R_RISCV_GOT32_PCREL RISC-V relocation type when linking native modules for RV32IMC. The previous implementation of R_RISCV_GOT32_PCREL ended up not being fully updated when the initial RV32 support was checked in. Said relocation was not emitted in the sample natmods that ship with the MicroPython source tree, and since they're the testbed for CI jobs that should check RV32 natmod support, this was not caught. On the other hand, nobody raised an issue about this problem yet, so hopefully it didn't cause much trouble. Signed-off-by: Alessandro Gatti --- tools/mpy_ld.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index af8450a842432..20418c67487a4 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -829,10 +829,10 @@ def do_relocation_data(env, text_addr, r): R_RISCV_SUB16: ("riscv_addsub", " Date: Tue, 30 Dec 2025 17:31:29 +0100 Subject: [PATCH 089/177] tools/mpy_ld.py: Fix handling of R_RISCV_TLSDESC_LOAD_LO12. This commit fixes handling of the R_RISCV_TLSDESC_LOAD_LO12 RISC-V object file relocation, fixing a couple of mistakes in its usage. The condition check for TLS relocations presence and their rejection when found in object files skipped checking for R_RISCV_TLSDESC_LOAD_LO12 relocations, which is part of the set of unsupported TLS relocations and thus needing an object file rejection. Interestingly, that relocation name constant was actually misspelled in the file - since it was skipped in the list of relocations being checked its wrong name did pass unnoticed until now. This is not a critical change as the linker will raise an error about an unknown relocation type rather than report a more descriptive message to the user, but it's nice to have nonetheless. Signed-off-by: Alessandro Gatti --- tools/mpy_ld.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 20418c67487a4..b3b28e453d5d1 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -121,7 +121,7 @@ R_RISCV_SET_ULEB128 = 60 R_RISCV_SUB_ULEB128 = 61 R_RISCV_TLSDESC_HI20 = 62 -R_RISCC_TLSDESC_LOAD_LO12 = 63 +R_RISCV_TLSDESC_LOAD_LO12 = 63 R_RISCV_TLSDESC_ADD_LO12 = 64 R_RISCV_TLSDESC_CALL = 65 @@ -682,6 +682,7 @@ def do_relocation_text(env, text_addr, r): elif env.arch.name == "EM_RISCV" and r_info_type in ( R_RISCV_TLS_GD_HI20, R_RISCV_TLSDESC_HI20, + R_RISCV_TLSDESC_LOAD_LO12, R_RISCV_TLSDESC_ADD_LO12, R_RISCV_TLSDESC_CALL, ): From e939d3ec76eba180deb36696a69cf5ea34c9876d Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 17 Nov 2025 03:42:06 +0100 Subject: [PATCH 090/177] tools/mpy_ld.py: Add RV64 natmod support. This commit adds the ability to compile native modules for the RV64 platform, using "rv64imc" as its architecture name (eg. "make ARCH=rv64imc" should build a RV64 natmod). The rest of 64-bits relocations needed to build a native module are now implemented, and all sample native modules build without errors or warnings. The same Picolibc caveats on RV32 also apply on RV64, thus the documentation was updated accordingly. RV64 native modules are also built as part of the CI process, but not yet executed as the QEMU port is not yet able to load and run them. Signed-off-by: Alessandro Gatti --- docs/develop/natmod.rst | 11 ++++++----- examples/natmod/btree/Makefile | 2 +- examples/natmod/deflate/Makefile | 2 +- examples/natmod/features0/Makefile | 2 +- examples/natmod/features1/Makefile | 2 +- examples/natmod/features2/Makefile | 2 +- examples/natmod/features3/Makefile | 2 +- examples/natmod/features4/Makefile | 2 +- examples/natmod/framebuf/Makefile | 2 +- examples/natmod/heapq/Makefile | 2 +- examples/natmod/random/Makefile | 2 +- examples/natmod/re/Makefile | 2 +- py/dynruntime.mk | 21 ++++++++++++++++++++- tests/run-natmodtests.py | 1 + tools/ci.sh | 9 +++++++-- tools/mpy_ld.py | 15 ++++++++++++--- 16 files changed, 57 insertions(+), 22 deletions(-) diff --git a/docs/develop/natmod.rst b/docs/develop/natmod.rst index 072d78b2076b4..142e475366f7b 100644 --- a/docs/develop/natmod.rst +++ b/docs/develop/natmod.rst @@ -41,6 +41,7 @@ options for the ``ARCH`` variable, see below): * ``xtensa`` (non-windowed, eg ESP8266) * ``xtensawin`` (windowed with window size 8, eg ESP32, ESP32S3) * ``rv32imc`` (RISC-V 32 bits with compressed instructions, eg ESP32C3, ESP32C6) +* ``rv64imc`` (RISC-V 64 bits with compressed instructions) 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 @@ -190,7 +191,7 @@ The file ``Makefile`` contains: # Source files (.c or .py) SRC = factorial.c - # Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin, rv32imc) + # Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin, rv32imc, rv64imc) ARCH = x64 # Include to get the rules for compiling and linking the module @@ -232,15 +233,15 @@ Using Picolibc when building modules ------------------------------------ Using `Picolibc `_ as your C standard -library is not only supported, but in fact it is the default for the rv32imc -platform. However, there are a couple of things worth mentioning to make sure -you don't run into problems later when building code. +library is not only supported, but in fact it is the default for the rv32imc and +rv64imc platforms. However, there are a couple of things worth mentioning to make +sure you don't run into problems later when building code. Some pre-built Picolibc versions (for example, those provided by Ubuntu Linux as the ``picolibc-arm-none-eabi``, ``picolibc-riscv64-unknown-elf``, and ``picolibc-xtensa-lx106-elf`` packages) assume thread-local storage (TLS) is available at runtime, but unfortunately MicroPython modules do not support that -on some architectures (namely ``rv32imc``). This means that some +on some architectures (namely ``rv32imc`` and ``rv64imc``). This means that some functionalities provided by Picolibc will default to use TLS, returning an error either during compilation or during linking. diff --git a/examples/natmod/btree/Makefile b/examples/natmod/btree/Makefile index 1910c67c1fc24..4ded62bafdeed 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, rv32imc) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc) ARCH ?= x64 BTREE_DIR = $(MPY_DIR)/lib/berkeley-db-1.xx diff --git a/examples/natmod/deflate/Makefile b/examples/natmod/deflate/Makefile index 5823aa4d45bf2..0574bbaf41272 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, rv32imc) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc) ARCH ?= x64 ifeq ($(ARCH),armv6m) diff --git a/examples/natmod/features0/Makefile b/examples/natmod/features0/Makefile index fb01b8d031aaa..788d035eb8bff 100644 --- a/examples/natmod/features0/Makefile +++ b/examples/natmod/features0/Makefile @@ -7,7 +7,7 @@ MOD = features0 # Source files (.c or .py) SRC = features0.c -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc) ARCH = x64 # Include to get the rules for compiling and linking the module diff --git a/examples/natmod/features1/Makefile b/examples/natmod/features1/Makefile index 49040511020eb..47deeed8f2ae2 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, rv32imc) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc) ARCH = x64 # Include to get the rules for compiling and linking the module diff --git a/examples/natmod/features2/Makefile b/examples/natmod/features2/Makefile index 5ddb74087b71a..efd096c4ede71 100644 --- a/examples/natmod/features2/Makefile +++ b/examples/natmod/features2/Makefile @@ -7,7 +7,7 @@ MOD = features2 # Source files (.c or .py) SRC = main.c prod.c test.py -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc) ARCH = x64 # Link with libm.a and libgcc.a from the toolchain diff --git a/examples/natmod/features3/Makefile b/examples/natmod/features3/Makefile index 3573f41caca82..85d1a13421fa8 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, rv32imc) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc) 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 34fc3a7ef7bf4..4eb657b7220b8 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, rv32imc) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc) ARCH = x64 # Include to get the rules for compiling and linking the module diff --git a/examples/natmod/framebuf/Makefile b/examples/natmod/framebuf/Makefile index 35453c0bb4b4c..a86efef41f42b 100644 --- a/examples/natmod/framebuf/Makefile +++ b/examples/natmod/framebuf/Makefile @@ -7,7 +7,7 @@ MOD = framebuf_$(ARCH) # Source files (.c or .py) SRC = framebuf.c -# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc) ARCH ?= x64 ifeq ($(ARCH),armv6m) diff --git a/examples/natmod/heapq/Makefile b/examples/natmod/heapq/Makefile index 61e2fc8fcc0e0..345359abb3ca2 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, rv32imc) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc) ARCH = x64 include $(MPY_DIR)/py/dynruntime.mk diff --git a/examples/natmod/random/Makefile b/examples/natmod/random/Makefile index bffbb32d60ef1..27d8ec935fe38 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, rv32imc) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc) ARCH ?= x64 ifeq ($(ARCH),xtensa) diff --git a/examples/natmod/re/Makefile b/examples/natmod/re/Makefile index 6535693dcb88c..c5f05e64ab4b6 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, rv32imc) +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc) ARCH = x64 ifeq ($(ARCH),armv6m) diff --git a/py/dynruntime.mk b/py/dynruntime.mk index 030728cfc9adf..866d5fae7b13e 100644 --- a/py/dynruntime.mk +++ b/py/dynruntime.mk @@ -106,7 +106,7 @@ else ifeq ($(ARCH),rv32imc) # rv32imc CROSS = riscv64-unknown-elf- CFLAGS_ARCH += -march=rv32imac -mabi=ilp32 -mno-relax -# If Picolibc is available then select it explicitly. Ubuntu 22.04 ships its +# If Picolibc is available then select it explicitly. Ubuntu 24.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. @@ -120,6 +120,25 @@ endif MICROPY_FLOAT_IMPL ?= none +else ifeq ($(ARCH),rv64imc) + +# rv64imc +CROSS = riscv64-unknown-elf- +CFLAGS_ARCH += -march=rv64imac -mabi=lp64 -mno-relax +# If Picolibc is available then select it explicitly. Ubuntu 24.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_ARCH += -specs=$(PICOLIBC_SPECS) +USE_PICOLIBC := 1 +PICOLIBC_ARCH := rv64imac +PICOLIBC_ABI := lp64 +endif + +MICROPY_FLOAT_IMPL ?= none + else $(error architecture '$(ARCH)' not supported) endif diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index e35e580dc1d4c..cd6c643bf9b08 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -40,6 +40,7 @@ "xtensa", "xtensawin", "rv32imc", + "rv64imc", ) ARCH_MAPPINGS = {"armv7em": "armv7m"} diff --git a/tools/ci.sh b/tools/ci.sh index 60e870ce65b74..4ade31160b50f 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -378,6 +378,8 @@ function ci_qemu_setup_rv64 { ci_gcc_riscv_setup sudo apt-get update sudo apt-get install qemu-system + sudo pip3 install pyelftools + sudo pip3 install ar qemu-system-riscv64 --version } @@ -436,6 +438,9 @@ function ci_qemu_build_rv64 { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV64 submodules make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV64 test + + # Test building native .mpy with rv64imc architecture. + ci_native_mpy_modules_build rv64imc } ######################################################################################## @@ -669,9 +674,9 @@ function ci_native_mpy_modules_build { make -C examples/natmod/$natmod ARCH=$arch done - # features2 requires soft-float on rv32imc and xtensa. + # features2 requires soft-float on rv32imc, rv64imc, and xtensa. make -C examples/natmod/features2 ARCH=$arch clean - if [ $arch = "rv32imc" ] || [ $arch = "xtensa" ]; then + if [ $arch = "rv32imc" ] || [ $arch = "rv64imc" ] || [ $arch = "xtensa" ]; then make -C examples/natmod/features2 ARCH=$arch MICROPY_FLOAT_IMPL=float else make -C examples/natmod/features2 ARCH=$arch diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index b3b28e453d5d1..b363c6a25f7f0 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -49,6 +49,7 @@ MP_NATIVE_ARCH_XTENSA = 9 MP_NATIVE_ARCH_XTENSAWIN = 10 MP_NATIVE_ARCH_RV32IMC = 11 +MP_NATIVE_ARCH_RV64IMC = 12 MP_PERSISTENT_OBJ_STR = 5 MP_SCOPE_FLAG_VIPERRELOC = 0x10 MP_SCOPE_FLAG_VIPERRODATA = 0x20 @@ -62,6 +63,7 @@ R_X86_64_64 = 1 R_XTENSA_32 = 1 R_386_PC32 = 2 +R_RISCV_64 = 2 R_X86_64_PC32 = 2 R_ARM_ABS32 = 2 R_386_GOT32 = 3 @@ -175,7 +177,7 @@ def asm_jump_xtensa(entry): return struct.pack("> 8) -def asm_jump_rv32(entry): +def asm_jump_riscv(entry): # This could be 6 bytes shorter, but the code currently cannot # support a trampoline with varying length depending on the offset. @@ -261,7 +263,14 @@ def __init__(self, name, mpy_feature, word_size, arch_got, asm_jump, *, separate MP_NATIVE_ARCH_RV32IMC << 2, 4, (R_RISCV_32, R_RISCV_GOT_HI20, R_RISCV_GOT32_PCREL), - asm_jump_rv32, + asm_jump_riscv, + ), + "rv64imc": ArchData( + "EM_RISCV", + MP_NATIVE_ARCH_RV64IMC << 2, + 8, + (R_RISCV_64, R_RISCV_GOT_HI20, R_RISCV_GOT32_PCREL), + asm_jump_riscv, ), } @@ -779,7 +788,7 @@ def do_relocation_data(env, text_addr, r): 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 + and r_info_type in (R_RISCV_32, R_RISCV_64) ): # Relocation in data.rel.ro to internal/external symbol if env.arch.word_size == 4: From 9af103300fb05da0bbd52ff10e36552ebbb23d27 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 17 Nov 2025 03:57:05 +0100 Subject: [PATCH 091/177] qemu: Enable loading natmods on RV64. This commit lets the QEMU port's VIRT_RV64 board load and run native modules, via the appropriate configuration changes in mpconfigport.h. Now the CI test job for the QEMU/RV64 port can also run natmods and see whether they actually work, so the CI tasks script has been updated to bring RV64 to parity with RV32 as far as CI checks go. Documentation was also updated, since now all supported boards in the QEMU port should be able to run natmod tests. Signed-off-by: Alessandro Gatti --- ports/qemu/README.md | 3 +-- ports/qemu/mpconfigport.h | 8 +++++++- tools/ci.sh | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ports/qemu/README.md b/ports/qemu/README.md index 0f53c000082a9..e6c16e3662d95 100644 --- a/ports/qemu/README.md +++ b/ports/qemu/README.md @@ -142,8 +142,7 @@ tests against the serial device, for example: $ ./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 not supported for the -`VIRT_RV64` board): +can also be tested with this command: $ make test_natmod diff --git a/ports/qemu/mpconfigport.h b/ports/qemu/mpconfigport.h index dcaf6a5c51e6f..522c59263285d 100644 --- a/ports/qemu/mpconfigport.h +++ b/ports/qemu/mpconfigport.h @@ -39,10 +39,16 @@ #define MICROPY_EMIT_INLINE_THUMB (1) #endif #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) -#elif defined(__riscv) && (__riscv_xlen == 32) +#elif defined(__riscv) +#if (__riscv_xlen == 32) #define MICROPY_EMIT_RV32 (1) #define MICROPY_EMIT_RV32_ZBA (1) #define MICROPY_EMIT_INLINE_RV32 (1) +#elif (__riscv_xlen == 64) +#define MICROPY_PERSISTENT_CODE_LOAD_NATIVE (1) +#else +#error "Unsupported RISC-V platform!" +#endif #endif #define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) diff --git a/tools/ci.sh b/tools/ci.sh index 4ade31160b50f..1856733f09ed1 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -439,8 +439,9 @@ function ci_qemu_build_rv64 { make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV64 submodules make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV64 test - # Test building native .mpy with rv64imc architecture. + # Test building and running native .mpy with rv64imc architecture. ci_native_mpy_modules_build rv64imc + make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV64 test_natmod } ######################################################################################## From d44685e103784a123a0f18aa54cfb6ea41987ad9 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 1 Jan 2026 11:16:43 +0100 Subject: [PATCH 092/177] unix/mpconfigport: Enable natmod loading for RISC-V builds. This commit lets the Unix port load native modules when built for both RV32 and RV64. Since RV64 doesn't have a native emitter, the new native code loading configuration settings have been used to allow such a feature. RV32 is fully supported by the native emitter framework but the emitter wasn't enabled for Unix builds (and therefore no natmod support). RV64 builds have been tested on QEMU and on a MilkV-Duo board, and whilst RV32 isn't really tested, its native code loading functionality is routinely tested both on bare-metal QEMU and on microcontroller boards. Since the code for RV64 under Unix and RV32 is essentially the same, having RV64 working is a strong indicator that RV32 will work too. Signed-off-by: Alessandro Gatti --- ports/unix/mpconfigport.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 854da1dbd4217..e290935bca93b 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -72,6 +72,12 @@ #if !defined(MICROPY_EMIT_ARM) && defined(__arm__) && !defined(__thumb2__) #define MICROPY_EMIT_ARM (1) #endif +#if !defined(MICROPY_EMIT_RV32) && defined(__riscv) && __riscv_xlen == 32 + #define MICROPY_EMIT_RV32 (1) +#endif +#if !defined(MICROPY_PERSISTENT_CODE_LOAD_NATIVE) && defined(__riscv) && __riscv_xlen == 64 + #define MICROPY_PERSISTENT_CODE_LOAD_NATIVE (1) +#endif // Cannot include , as it may lead to symbol name clashes #if _FILE_OFFSET_BITS == 64 && !defined(__LP64__) @@ -93,7 +99,7 @@ typedef long mp_off_t; // Always enable GC. #define MICROPY_ENABLE_GC (1) -#if !(defined(MICROPY_GCREGS_SETJMP) || defined(__x86_64__) || defined(__i386__) || defined(__thumb2__) || defined(__thumb__) || defined(__arm__) || (defined(__riscv) && (__riscv_xlen == 64))) +#if !(defined(MICROPY_GCREGS_SETJMP) || defined(__x86_64__) || defined(__i386__) || defined(__thumb2__) || defined(__thumb__) || defined(__arm__) || (defined(__riscv) && __riscv_xlen <= 64)) // Fall back to setjmp() implementation for discovery of GC pointers in registers. #define MICROPY_GCREGS_SETJMP (1) #endif From d871c970c0402a26dcec04477136914e3d0ee782 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 1 Jan 2026 11:28:52 +0100 Subject: [PATCH 093/177] tools/ci.sh: Run natmod tests as part of CI for Unix/RV64. This commit updates the test procedure for the Unix port targeting the RV64 platform to also run the battery of native modules test that is part of the QEMU port. Unfortunately this required a few changes to the CI setup since the Unix port was still using an older version of Ubuntu LTS than the RISC-V natmods build infrastructure expects. Updating the OS version just for the RV64 Unix target brought a couple of issues when building the code (an extra package is now needed to let FFI build) and running tests (QEMU binfmt support requires a new setup). Signed-off-by: Alessandro Gatti --- .github/workflows/ports_unix.yml | 3 +-- tools/ci.sh | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index 19d6dcf85651e..6ef6c28d3317f 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -277,8 +277,7 @@ jobs: run: tests/run-tests.py --print-failures qemu_riscv64: - # ubuntu-22.04 is needed for older libffi. - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Install packages diff --git a/tools/ci.sh b/tools/ci.sh index 1856733f09ed1..ec4b57a8f5641 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -946,26 +946,34 @@ function ci_unix_qemu_arm_run_tests { } function ci_unix_qemu_riscv64_setup { + ci_gcc_riscv_setup sudo apt-get update - sudo apt-get install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu - sudo apt-get install qemu-user - qemu-riscv64 --version - sudo mkdir /etc/qemu-binfmt - sudo ln -s /usr/riscv64-linux-gnu/ /etc/qemu-binfmt/riscv64 + sudo apt-get install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu libltdl-dev + sudo pip3 install pyelftools + sudo pip3 install ar + sudo apt-get install qemu-user-static + qemu-riscv64-static --version + sudo mkdir -p /usr/gnemul + sudo ln -s /usr/riscv64-linux-gnu /usr/gnemul/qemu-riscv64 } function ci_unix_qemu_riscv64_build { ci_unix_build_helper "${CI_UNIX_OPTS_QEMU_RISCV64[@]}" ci_unix_build_ffi_lib_helper riscv64-linux-gnu-gcc + ci_native_mpy_modules_build rv64imc } function ci_unix_qemu_riscv64_run_tests { # Issues with RISCV-64 tests: - # - thread/stress_aes.py takes around 140 seconds + # - misc/sys_settrace_features.py doesn't work with CPython 3.12 + # - thread/stress_aes.py takes around 180 seconds # - thread/stress_recurse.py is flaky # - thread/thread_gc1.py is flaky file ./ports/unix/build-coverage/micropython - (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython MICROPY_TEST_TIMEOUT=180 ./run-tests.py --exclude 'thread/stress_recurse.py|thread/thread_gc1.py') + pushd tests + MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython MICROPY_TEST_TIMEOUT=200 ./run-tests.py --exclude 'misc/sys_settrace_features.py|thread/stress_recurse.py|thread/thread_gc1.py' + MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-natmodtests.py extmod/btree*.py extmod/deflate*.py extmod/framebuf*.py extmod/heapq*.py extmod/random_basic*.py extmod/re*.py + popd } function ci_unix_repr_b_build { From b857a9131e5c1b82e130af75ae2640e12c89fa39 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 21 Nov 2024 17:03:37 +0100 Subject: [PATCH 094/177] mimxrt: Add support for VfsRom filesystem. The default size for the ROM partition is 256k, and it is placed in flash between the text and writable vfs sections. Its size can be changed in `mpconfigboard.mk` by defining `MICROPY_HW_ROMFS_BYTES` to a different value, but it must not be smaller than a single sector. The MIMXRT1170_EVK and SEEED ARCH MIX ROMFS boards use a larger size of 512kB. ROMFS support is disabled for MIMXRT1050_EVKB due to issues with Hyperflash. The extents of the ROM partition are defined by the linker-level symbols `_micropy_hw_romfs_part0_start` and `_micropy_hw_romfs_part0_size`, following existing ports. Signed-off-by: Damien George Signed-off-by: robert-hh --- ports/mimxrt/Makefile | 10 +++- ports/mimxrt/boards/MIMXRT1011.ld | 4 +- ports/mimxrt/boards/MIMXRT1015.ld | 4 +- ports/mimxrt/boards/MIMXRT1021.ld | 4 +- .../boards/MIMXRT1050_EVK/mpconfigboard.mk | 1 + ports/mimxrt/boards/MIMXRT1052.ld | 4 +- ports/mimxrt/boards/MIMXRT1062.ld | 4 +- ports/mimxrt/boards/MIMXRT1064.ld | 4 +- .../boards/MIMXRT1170_EVK/mpconfigboard.mk | 1 + ports/mimxrt/boards/MIMXRT1176.ld | 4 +- .../boards/SEEED_ARCH_MIX/mpconfigboard.mk | 1 + ports/mimxrt/mimxrt_flash.c | 49 +++++++++++++++++++ ports/mimxrt/mpconfigport.h | 3 ++ 13 files changed, 84 insertions(+), 9 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index fea7e56da22f4..52003ed4a4d36 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -311,6 +311,11 @@ ifeq ($(USE_UF2_BOOTLOADER),1) CFLAGS += -DMICROPY_MACHINE_UF2_BOOTLOADER=1 endif +# Set the default size of the VfsRom file system. Can be changed at board level +ifeq ($(MICROPY_HW_ROMFS_BYTES),) + MICROPY_HW_ROMFS_BYTES ?= 0x40000 +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 @@ -494,9 +499,10 @@ LDFLAGS += \ # LDDEFINES are used for link time adaptation of linker scripts, utilizing # the C preprocessor. Therefore keep LDDEFINES separated from LDFLAGS! -LDDEFINES = \ +LDDEFINES += \ -DMICROPY_HW_FLASH_BASE=$(MICROPY_HW_FLASH_BASE) \ - -DMICROPY_HW_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) + -DMICROPY_HW_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) \ + -DMICROPY_HW_ROMFS_BYTES=$(MICROPY_HW_ROMFS_BYTES) ifdef MICROPY_HW_FLASH_RESERVED LDDEFINES += -DMICROPY_HW_FLASH_RESERVED=$(MICROPY_HW_FLASH_RESERVED) diff --git a/ports/mimxrt/boards/MIMXRT1011.ld b/ports/mimxrt/boards/MIMXRT1011.ld index 0e961a49433f2..dba02f0ec39c4 100644 --- a/ports/mimxrt/boards/MIMXRT1011.ld +++ b/ports/mimxrt/boards/MIMXRT1011.ld @@ -18,8 +18,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +_micropy_hw_romfs_part0_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +_micropy_hw_romfs_part0_size = MICROPY_HW_ROMFS_BYTES; +text_size = ((_micropy_hw_romfs_part0_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00008000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1015.ld b/ports/mimxrt/boards/MIMXRT1015.ld index 58b88e0fe3084..70959685fa0e3 100644 --- a/ports/mimxrt/boards/MIMXRT1015.ld +++ b/ports/mimxrt/boards/MIMXRT1015.ld @@ -18,8 +18,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +_micropy_hw_romfs_part0_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +_micropy_hw_romfs_part0_size = MICROPY_HW_ROMFS_BYTES; +text_size = ((_micropy_hw_romfs_part0_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00008000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1021.ld b/ports/mimxrt/boards/MIMXRT1021.ld index 78add04c0c26c..78e8e22a0ff4d 100644 --- a/ports/mimxrt/boards/MIMXRT1021.ld +++ b/ports/mimxrt/boards/MIMXRT1021.ld @@ -18,8 +18,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +_micropy_hw_romfs_part0_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +_micropy_hw_romfs_part0_size = MICROPY_HW_ROMFS_BYTES; +text_size = ((_micropy_hw_romfs_part0_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00010000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk index 8b048c85eaa98..3631df4b0d4d3 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk @@ -10,6 +10,7 @@ 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_HW_ROMFS_BYTES = 0 # Disabled for a board with Hyperflash MICROPY_PY_LWIP = 1 MICROPY_PY_SSL = 1 diff --git a/ports/mimxrt/boards/MIMXRT1052.ld b/ports/mimxrt/boards/MIMXRT1052.ld index ea034d713e2f6..0370d805acc3e 100644 --- a/ports/mimxrt/boards/MIMXRT1052.ld +++ b/ports/mimxrt/boards/MIMXRT1052.ld @@ -20,8 +20,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00200000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +_micropy_hw_romfs_part0_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +_micropy_hw_romfs_part0_size = MICROPY_HW_ROMFS_BYTES; +text_size = ((_micropy_hw_romfs_part0_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1062.ld b/ports/mimxrt/boards/MIMXRT1062.ld index 3d7e6d0634196..686d1bdc845d4 100644 --- a/ports/mimxrt/boards/MIMXRT1062.ld +++ b/ports/mimxrt/boards/MIMXRT1062.ld @@ -20,8 +20,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +_micropy_hw_romfs_part0_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +_micropy_hw_romfs_part0_size = MICROPY_HW_ROMFS_BYTES; +text_size = ((_micropy_hw_romfs_part0_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1064.ld b/ports/mimxrt/boards/MIMXRT1064.ld index 7c35cb60c750b..16f6f19b5add8 100644 --- a/ports/mimxrt/boards/MIMXRT1064.ld +++ b/ports/mimxrt/boards/MIMXRT1064.ld @@ -14,8 +14,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +_micropy_hw_romfs_part0_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +_micropy_hw_romfs_part0_size = MICROPY_HW_ROMFS_BYTES; +text_size = ((_micropy_hw_romfs_part0_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk index ef4ab683a215c..f534406fcdefe 100644 --- a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk @@ -9,6 +9,7 @@ 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_ROMFS_BYTES = 0x80000 # 512kB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x4000000 # 64MB diff --git a/ports/mimxrt/boards/MIMXRT1176.ld b/ports/mimxrt/boards/MIMXRT1176.ld index 4d114ef96fa4a..57243b68da797 100644 --- a/ports/mimxrt/boards/MIMXRT1176.ld +++ b/ports/mimxrt/boards/MIMXRT1176.ld @@ -30,8 +30,10 @@ m_core1_image_start = vfs_start - 0x00040000; m_core1_image_size = 0x00040000; #endif -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +_micropy_hw_romfs_part0_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +_micropy_hw_romfs_part0_size = MICROPY_HW_ROMFS_BYTES; +text_size = ((_micropy_hw_romfs_part0_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk index 1bc38ccbad7b5..1cdfe8fe80582 100644 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk +++ b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk @@ -7,6 +7,7 @@ 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_ROMFS_BYTES = 0x80000 # 512kB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB diff --git a/ports/mimxrt/mimxrt_flash.c b/ports/mimxrt/mimxrt_flash.c index fdd48e280bc15..3819d8b904359 100644 --- a/ports/mimxrt/mimxrt_flash.c +++ b/ports/mimxrt/mimxrt_flash.c @@ -5,6 +5,7 @@ * * Copyright (c) 2020-2021 Damien P. George * Copyright (c) 2021-2023 Philipp Ebensberger + * Copyright (c) 2021-2024 Robert Hammelrath * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,6 +30,7 @@ #include "py/runtime.h" #include "extmod/vfs.h" +#include "py/mperrno.h" #include "modmimxrt.h" #include "flash.h" #include BOARD_FLASH_OPS_HEADER_H @@ -60,6 +62,19 @@ static mp_obj_t mimxrt_flash_make_new(const mp_obj_type_t *type, size_t n_args, return MP_OBJ_FROM_PTR(&mimxrt_flash_obj); } +static mp_int_t mimxrt_flash_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + mimxrt_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)((uintptr_t)&__flash_start + self->flash_base); + bufinfo->len = self->flash_size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Write unsupported. + return 1; + } +} + // readblocks(block_num, buf, [offset]) // read size of buffer number of bytes from block (with offset) into buffer static mp_obj_t mimxrt_flash_readblocks(size_t n_args, const mp_obj_t *args) { @@ -151,5 +166,39 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_QSTR_Flash, MP_TYPE_FLAG_NONE, make_new, mimxrt_flash_make_new, + buffer, mimxrt_flash_get_buffer, locals_dict, &mimxrt_flash_locals_dict ); + +#if MICROPY_VFS_ROM + +extern uint8_t _micropy_hw_romfs_part0_start; +extern uint8_t _micropy_hw_romfs_part0_size; + +// Put VfsRom file system between the code space and the VFS file system. +// The size is defined in Makefile(s) as linker symbol MICROPY_HW_ROMFS_BYTES. +// For machine.mem32 the absolute address is required, for the flash functions +// erase and write the offset to the flash start address. +#define MICROPY_HW_ROMFS_BASE ((uintptr_t)&_micropy_hw_romfs_part0_start - (uintptr_t)&__flash_start) +#define MICROPY_HW_ROMFS_BYTES ((uintptr_t)&_micropy_hw_romfs_part0_size) + +static mimxrt_flash_obj_t mimxrt_flash_romfs_obj = { + .base = { &mimxrt_flash_type }, +}; + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + if (MICROPY_HW_ROMFS_BYTES <= 0) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + 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: + mimxrt_flash_romfs_obj.flash_base = MICROPY_HW_ROMFS_BASE; + mimxrt_flash_romfs_obj.flash_size = MICROPY_HW_ROMFS_BYTES; + return MP_OBJ_FROM_PTR(&mimxrt_flash_romfs_obj); + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} +#endif // MICROPY_VFS_ROM diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 0a623a6318700..45316b904b62f 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -60,6 +60,9 @@ uint32_t trng_random_u32(void); #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_VFS (1) +#ifndef MICROPY_VFS_ROM +#define MICROPY_VFS_ROM (1) +#endif // Control over Python builtins #define MICROPY_PY_BUILTINS_HELP_TEXT mimxrt_help_text From c07b178dbda65b86a72a81dfd1106c24a2f252d6 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 19 Nov 2024 08:48:29 +0100 Subject: [PATCH 095/177] samd: Add support for VfsRom filesystem for SAMD21 and SAMD51. The flash driver is update to support the new `mp_vfs_rom_ioctl()` function, and the buffer protocol is added to `samd_flash_type` (it is needed for VfsRom on devices without external flash). For SAMD21, only boards with external SPI flash have VfsRom enabled, due to size constraints. For such boards, the VfsRom filesystem has a size of 12k and is placed at the upper end of the flash. The `onewire`, `ds18x20` and `dht` drivers have been removed from frozen bytecode as they can be placed into the VfsRom files when needed. For SAMD51, the VfsRom filesystem has a default size of 64k for SAMD51x19 and 256K for SAMD51x20. It is placed at the upper end of the flash. For boards with external SPI flash, the code size is reduced from 496K to 432K. If that is not sufficient for some boards or configurations, it can be changed for each board or board variant. Tested with ADAFRUIT_ITSYBITSY_M0_EXPRESS, ADAFRUIT_ITSYBITSY_M4_EXPRESS, SPARKFUN_SAMD51_THING_PLUS, SEEED_XIAO_SAMD21, SAMD_GENERIC_D51X19, and SAMD_GENERIC_D51X20. Signed-off-by: Damien George Signed-off-by: robert-hh --- ports/samd/Makefile | 6 +- .../mpconfigboard.mk | 2 +- .../mpconfigboard.mk | 5 +- .../mpconfigboard.mk | 2 +- .../mpconfigboard.mk | 5 +- .../mpconfigboard.mk | 5 +- .../mpconfigvariant_SPIFLASH.mk | 2 +- ports/samd/boards/MINISAM_M4/mpconfigboard.mk | 5 +- .../SAMD21_XPLAINED_PRO/mpconfigboard.mk | 2 +- .../SAMD_GENERIC_D51X19/mpconfigboard.mk | 4 +- .../SAMD_GENERIC_D51X20/mpconfigboard.mk | 3 +- .../SEEED_WIO_TERMINAL/mpconfigboard.mk | 5 +- .../SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk | 2 +- .../mpconfigboard.mk | 5 +- ports/samd/boards/samd21x18a.ld | 17 ++- ports/samd/boards/samd51x19a.ld | 17 ++- ports/samd/boards/samd51x20a.ld | 17 ++- ports/samd/mcu/samd21/manifest.py | 3 - ports/samd/mcu/samd21/mpconfigmcu.h | 5 - ports/samd/mcu/samd21/mpconfigmcu.mk | 7 +- ports/samd/mcu/samd51/manifest.py | 3 - ports/samd/mcu/samd51/mpconfigmcu.h | 8 +- ports/samd/mcu/samd51/mpconfigmcu.mk | 1 + ports/samd/mpconfigport.h | 3 + ports/samd/samd_flash.c | 121 +++++++++++++++--- ports/samd/samd_soc.c | 4 + 26 files changed, 197 insertions(+), 62 deletions(-) diff --git a/ports/samd/Makefile b/ports/samd/Makefile index bec530d803f7e..3344be7502637 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -90,7 +90,7 @@ CFLAGS += $(CFLAGS_EXTRA) CFLAGS += -DMICROPY_HW_CODESIZE=$(strip $(subst K,' ', $(MICROPY_HW_CODESIZE))) LDFLAGS += -nostdlib $(addprefix -T,$(LD_FILES)) -Map=$@.map --cref -LDFLAGS += --defsym=_codesize=$(MICROPY_HW_CODESIZE) +LDFLAGS += --defsym=_codesize=$(MICROPY_HW_CODESIZE) --defsym=_micropy_hw_romfs_part0_size=$(MICROPY_HW_ROMFS_BYTES) LIBS += $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) @@ -103,6 +103,10 @@ CFLAGS += -Os -DNDEBUG LDFLAGS += --gc-sections --print-memory-usage CFLAGS += -fdata-sections -ffunction-sections endif +# Disable VFSROM support if the size was set to 0 +ifeq ($(MICROPY_HW_ROMFS_BYTES),0) + CFLAGS += -DMICROPY_VFS_ROM=0 +endif # Flags for optional C++ source code CXXFLAGS += $(filter-out -std=c99,$(CFLAGS)) diff --git a/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.mk index e2895c7c0c2f9..6e1c7375a2e6a 100644 --- a/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.mk +++ b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.mk @@ -5,4 +5,4 @@ TEXT0 = 0x2000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 248K +MICROPY_HW_CODESIZE ?= 236K diff --git a/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.mk index 6ec2d43dedbd8..dbce935140cc5 100644 --- a/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.mk +++ b/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.mk @@ -5,4 +5,7 @@ TEXT0 = 0x4000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 496K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_ROMFS_BYTES +# The default for MICROPY_HW_ROMFS_BYTES is 64K +MICROPY_HW_CODESIZE ?= 432K diff --git a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.mk index e2895c7c0c2f9..6e1c7375a2e6a 100644 --- a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.mk +++ b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.mk @@ -5,4 +5,4 @@ TEXT0 = 0x2000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 248K +MICROPY_HW_CODESIZE ?= 236K diff --git a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.mk index 740154a6d6825..fb6ec0eb27985 100644 --- a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.mk +++ b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.mk @@ -5,4 +5,7 @@ TEXT0 = 0x4000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 496K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_ROMFS_BYTES +# The default for MICROPY_HW_ROMFS_BYTES is 64K +MICROPY_HW_CODESIZE ?= 432K diff --git a/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.mk index 43ca5a59cc636..9ab91a8521e6a 100644 --- a/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.mk +++ b/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.mk @@ -7,4 +7,7 @@ TEXT0 = 0x4000 MICROPY_PY_NETWORK ?= 1 MICROPY_PY_NETWORK_NINAW10 ?= 1 -MICROPY_HW_CODESIZE ?= 496K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_ROMFS_BYTES +# The default for MICROPY_HW_ROMFS_BYTES is 64K +MICROPY_HW_CODESIZE ?= 432K diff --git a/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigvariant_SPIFLASH.mk b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigvariant_SPIFLASH.mk index 69537d5bf3c51..b5d11f7e89969 100644 --- a/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigvariant_SPIFLASH.mk +++ b/ports/samd/boards/ADAFRUIT_QTPY_SAMD21/mpconfigvariant_SPIFLASH.mk @@ -1,2 +1,2 @@ CFLAGS += -DMICROPY_HW_SPIFLASH=1 -MICROPY_HW_CODESIZE ?= 232K +MICROPY_HW_CODESIZE ?= 236K diff --git a/ports/samd/boards/MINISAM_M4/mpconfigboard.mk b/ports/samd/boards/MINISAM_M4/mpconfigboard.mk index 54948627d2b67..a1b97af0564b0 100644 --- a/ports/samd/boards/MINISAM_M4/mpconfigboard.mk +++ b/ports/samd/boards/MINISAM_M4/mpconfigboard.mk @@ -6,4 +6,7 @@ TEXT0 = 0x4000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 496K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_ROMFS_BYTES +# The default for MICROPY_HW_ROMFS_BYTES is 64K +MICROPY_HW_CODESIZE ?= 432K diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk index cc43c22cea588..a396c543a42c8 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk @@ -3,4 +3,4 @@ CMSIS_MCU = SAMD21J18A LD_FILES = boards/samd21x18a.ld sections.ld TEXT0 = 0x2000 -MICROPY_HW_CODESIZE ?= 248K +MICROPY_HW_CODESIZE ?= 236K diff --git a/ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.mk b/ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.mk index 1a20643214f1a..5c3664267de88 100644 --- a/ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.mk +++ b/ports/samd/boards/SAMD_GENERIC_D51X19/mpconfigboard.mk @@ -6,6 +6,6 @@ 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 +# 496k - MICROPY_HW_CODESIZE - MICROPY_HW_ROMFS_BYTES +# The default for MICROPY_HW_ROMFS_BYTES is 64K MICROPY_HW_CODESIZE ?= 368K diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk index b240c2587f8d0..2e000225d54fd 100644 --- a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk +++ b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk @@ -7,5 +7,6 @@ TEXT0 = 0x4000 # MicroPython settings # The size of a MCU flash filesystem will be # 1008k - MICROPY_HW_CODESIZE -# The default for MICROPY_HW_VFSROMSIZE is 64K +# The default for MICROPY_HW_ROMFS_BYTES is 64K MICROPY_HW_CODESIZE ?= 752K +MICROPY_HW_ROMFS_BYTES ?= 256K diff --git a/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.mk b/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.mk index 7bf70ac669566..bb14efe7e8013 100644 --- a/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.mk +++ b/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.mk @@ -5,4 +5,7 @@ TEXT0 = 0x4000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 496K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_ROMFS_BYTES +# The default for MICROPY_HW_ROMFS_BYTES is 64K +MICROPY_HW_CODESIZE ?= 432K diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk index 6ea327a650d40..6e1c7375a2e6a 100644 --- a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk +++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk @@ -5,4 +5,4 @@ TEXT0 = 0x2000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 232K +MICROPY_HW_CODESIZE ?= 236K diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.mk b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.mk index 263e582694495..2ee0b2cca5e22 100644 --- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.mk +++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.mk @@ -5,4 +5,7 @@ TEXT0 = 0x4000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 1008K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_ROMFS_BYTES +MICROPY_HW_CODESIZE ?= 752K +MICROPY_HW_ROMFS_BYTES ?= 256K diff --git a/ports/samd/boards/samd21x18a.ld b/ports/samd/boards/samd21x18a.ld index 3ab051569fb18..5bb031d9c0713 100644 --- a/ports/samd/boards/samd21x18a.ld +++ b/ports/samd/boards/samd21x18a.ld @@ -3,8 +3,8 @@ */ /* -_codesize is defined in mpconfigmcu.mk or mpconfigboard.mk as -MICROPY_HW_CODESIZE and is set in Makefile +_codesize and _micropy_hw_romfs_part0_size are defined in mpconfigmcu.mk or mpconfigboard.mk +as MICROPY_HW_CODESIZE and MICROPY_HW_ROMFS_BYTES and are set in Makefile. */ _flashsize = 256K; /* The physical flash size */ @@ -21,8 +21,17 @@ MEMORY _estack = ORIGIN(RAM) + LENGTH(RAM) - 8; _sstack = _estack - 8K; -_oflash_fs = ORIGIN(FLASH) + _codesize; -_sflash_fs = _flashsize - _codesize - _bootloader; +/* +The VfsROM file system is placed at the end of the flash. +For device with SPI flash the number for _sflash_fs might be 0 and +the origin beyond the end of the flash. That does not matter since +these devices do not use the MCU flash file system. +*/ + +_oflash_fs = ORIGIN(FLASH) + _codesize + _micropy_hw_romfs_part0_size; +_sflash_fs = _flashsize - _codesize - _bootloader - _micropy_hw_romfs_part0_size; + +_micropy_hw_romfs_part0_start = ORIGIN(FLASH) + _codesize; _sheap = _ebss; _eheap = _sstack; diff --git a/ports/samd/boards/samd51x19a.ld b/ports/samd/boards/samd51x19a.ld index 30bc8e33281fb..77ae8656d4a78 100644 --- a/ports/samd/boards/samd51x19a.ld +++ b/ports/samd/boards/samd51x19a.ld @@ -3,8 +3,8 @@ */ /* -_codesize is defined in mpconfigmcu.mk or mpconfigboard.mk as -MICROPY_HW_CODESIZE and is set in Makefile +_codesize and _micropy_hw_romfs_part0_size are defined in mpconfigmcu.mk or mpconfigboard.mk +as MICROPY_HW_CODESIZE and MICROPY_HW_ROMFS_BYTES and are set in Makefile. */ _flashsize = 512K; /* The physical flash size */ @@ -21,8 +21,17 @@ MEMORY _estack = ORIGIN(RAM) + LENGTH(RAM) - 8; _sstack = _estack - 16K; -_oflash_fs = ORIGIN(FLASH) + _codesize; -_sflash_fs = _flashsize - _codesize - _bootloader; +/* +The VfsROM file system is placed at the end of the flash. +For device with SPI flash the number for _sflash_fs might be 0 and +the origin beyond the end of the flash. That does not matter since +these devices do not use the MCU flash file system. +*/ + +_oflash_fs = ORIGIN(FLASH) + _codesize + _micropy_hw_romfs_part0_size; +_sflash_fs = _flashsize - _codesize - _bootloader - _micropy_hw_romfs_part0_size; + +_micropy_hw_romfs_part0_start = ORIGIN(FLASH) + _codesize; _sheap = _ebss; _eheap = _sstack; diff --git a/ports/samd/boards/samd51x20a.ld b/ports/samd/boards/samd51x20a.ld index 472ab316c6fde..595042128d550 100644 --- a/ports/samd/boards/samd51x20a.ld +++ b/ports/samd/boards/samd51x20a.ld @@ -3,8 +3,8 @@ */ /* -_codesize is defined in mpconfigmcu.mk or mpconfigboard.mk as -MICROPY_HW_CODESIZE and is set in Makefile +_codesize and _micropy_hw_romfs_part0_size are defined in mpconfigmcu.mk or mpconfigboard.mk +as MICROPY_HW_CODESIZE and MICROPY_HW_ROMFS_BYTES and are set in Makefile. */ _flashsize = 1024K; /* The physical flash size */ @@ -21,8 +21,17 @@ MEMORY _estack = ORIGIN(RAM) + LENGTH(RAM) - 8; _sstack = _estack - 16K; -_oflash_fs = ORIGIN(FLASH) + _codesize; -_sflash_fs = _flashsize - _codesize - _bootloader; +/* +The VfsROM file system is placed at the end of the flash. +For device with SPI flash the number for _sflash_fs might be 0 and +the origin beyond the end of the flash. That does not matter since +these devices do not use the MCU flash file system. +*/ + +_oflash_fs = ORIGIN(FLASH) + _codesize + _micropy_hw_romfs_part0_size; +_sflash_fs = _flashsize - _codesize - _bootloader - _micropy_hw_romfs_part0_size; + +_micropy_hw_romfs_part0_start = ORIGIN(FLASH) + _codesize; _sheap = _ebss; _eheap = _sstack; diff --git a/ports/samd/mcu/samd21/manifest.py b/ports/samd/mcu/samd21/manifest.py index 2a19a843f8a9b..8ad1e38ba099a 100644 --- a/ports/samd/mcu/samd21/manifest.py +++ b/ports/samd/mcu/samd21/manifest.py @@ -1,5 +1,2 @@ include("$(PORT_DIR)/boards/manifest.py") include("$(MPY_DIR)/extmod/asyncio") -require("onewire") -require("ds18x20") -require("dht") diff --git a/ports/samd/mcu/samd21/mpconfigmcu.h b/ports/samd/mcu/samd21/mpconfigmcu.h index a29d5c0a04db4..a757893943b73 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.h +++ b/ports/samd/mcu/samd21/mpconfigmcu.h @@ -2,11 +2,6 @@ #include "samd21.h" #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES) -#if MICROPY_HW_CODESIZE == 248 -#define SAMD21_EXTRA_FEATURES 1 -#else -#define SAMD21_EXTRA_FEATURES 0 -#endif // MicroPython emitters #define MICROPY_EMIT_THUMB (SAMD21_EXTRA_FEATURES) diff --git a/ports/samd/mcu/samd21/mpconfigmcu.mk b/ports/samd/mcu/samd21/mpconfigmcu.mk index 34209775c2565..b809a47679ab2 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.mk +++ b/ports/samd/mcu/samd21/mpconfigmcu.mk @@ -6,8 +6,13 @@ MPY_CROSS_MCU_ARCH = armv6m MICROPY_HW_CODESIZE ?= 184K -ifeq ($(MICROPY_HW_CODESIZE), 248K) +ifeq ($(MICROPY_HW_CODESIZE), 236K) FROZEN_MANIFEST ?= mcu/$(MCU_SERIES_LOWER)/manifest.py +MICROPY_HW_ROMFS_BYTES ?= 12K +CFLAGS_MCU += -DSAMD21_EXTRA_FEATURES=1 +else +MICROPY_HW_ROMFS_BYTES ?= 0 +CFLAGS_MCU += -DSAMD21_EXTRA_FEATURES=0 endif MICROPY_VFS_LFS1 ?= 1 diff --git a/ports/samd/mcu/samd51/manifest.py b/ports/samd/mcu/samd51/manifest.py index 2a19a843f8a9b..8ad1e38ba099a 100644 --- a/ports/samd/mcu/samd51/manifest.py +++ b/ports/samd/mcu/samd51/manifest.py @@ -1,5 +1,2 @@ include("$(PORT_DIR)/boards/manifest.py") include("$(MPY_DIR)/extmod/asyncio") -require("onewire") -require("ds18x20") -require("dht") diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h index a1ff208eb5939..974a40f7aa76c 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.h +++ b/ports/samd/mcu/samd51/mpconfigmcu.h @@ -21,10 +21,10 @@ unsigned long trng_random_u32(void); #endif // fatfs configuration used in ffconf.h -#define MICROPY_FATFS_ENABLE_LFN (1) -#define MICROPY_FATFS_RPATH (2) -#define MICROPY_FATFS_MAX_SS (4096) -#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ +#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_RPATH (2) +#define MICROPY_FATFS_MAX_SS (4096) +#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #define VFS_BLOCK_SIZE_BYTES (2048) // diff --git a/ports/samd/mcu/samd51/mpconfigmcu.mk b/ports/samd/mcu/samd51/mpconfigmcu.mk index 9bef5eca166ab..f10faec47f434 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.mk +++ b/ports/samd/mcu/samd51/mpconfigmcu.mk @@ -5,6 +5,7 @@ CFLAGS_MCU += -DCFG_TUSB_MCU=OPT_MCU_SAMD51 MPY_CROSS_MCU_ARCH = armv7m MICROPY_HW_CODESIZE ?= 368K +MICROPY_HW_ROMFS_BYTES ?= 64K MICROPY_VFS_LFS2 ?= 1 MICROPY_VFS_FAT ?= 1 diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 32009ae82f298..131861d2bd8cc 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -86,6 +86,9 @@ #define MICROPY_PY_OS_INCLUDEFILE "ports/samd/modos.c" #define MICROPY_READER_VFS (1) #define MICROPY_VFS (1) +#ifndef MICROPY_VFS_ROM +#define MICROPY_VFS_ROM (1) +#endif #ifndef MICROPY_PY_MACHINE_ADC #define MICROPY_PY_MACHINE_ADC (1) #endif diff --git a/ports/samd/samd_flash.c b/ports/samd/samd_flash.c index f68bdf140f490..c99df9d0f8990 100644 --- a/ports/samd/samd_flash.c +++ b/ports/samd/samd_flash.c @@ -26,11 +26,13 @@ #include +#include "py/objarray.h" #include "py/runtime.h" #include "extmod/vfs.h" +#include "py/mperrno.h" #include "samd_soc.h" -#if MICROPY_HW_MCUFLASH +#if MICROPY_HW_MCUFLASH || MICROPY_VFS_ROM // ASF 4 #include "hal_flash.h" @@ -45,7 +47,6 @@ #endif static struct flash_descriptor flash_desc; -static mp_int_t BLOCK_SIZE = VFS_BLOCK_SIZE_BYTES; // Board specific: mpconfigboard.h extern const mp_obj_type_t samd_flash_type; typedef struct _samd_flash_obj_t { @@ -54,8 +55,9 @@ typedef struct _samd_flash_obj_t { uint32_t flash_size; } samd_flash_obj_t; +#if MICROPY_HW_MCUFLASH +static mp_int_t BLOCK_SIZE = VFS_BLOCK_SIZE_BYTES; // Board specific: mpconfigboard.h extern uint8_t _oflash_fs, _sflash_fs; - // Build a Flash storage at top. static samd_flash_obj_t samd_flash_obj = { .base = { &samd_flash_type }, @@ -63,9 +65,31 @@ static samd_flash_obj_t samd_flash_obj = { .flash_size = (uint32_t)&_sflash_fs, // Get from MCU-Specific loader script. }; +static mp_obj_t samd_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // No args required. bdev=Flash(). Start Addr & Size defined in samd_flash_obj. + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + // Return singleton object. + return MP_OBJ_FROM_PTR(&samd_flash_obj); +} + +static mp_int_t samd_flash_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + samd_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)((uintptr_t)self->flash_base); + bufinfo->len = self->flash_size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Write unsupported. + return 1; + } +} +#endif // MICROPY_HW_MCUFLASH + // Flash init (from cctpy) // Method is needed for when MP starts up in _boot.py -static void samd_flash_init(void) { +void samd_flash_init(void) { #ifdef SAMD51 hri_mclk_set_AHBMASK_NVMCTRL_bit(MCLK); #endif @@ -76,23 +100,14 @@ static void samd_flash_init(void) { flash_init(&flash_desc, NVMCTRL); } -static mp_obj_t samd_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - // No args required. bdev=Flash(). Start Addr & Size defined in samd_flash_obj. - mp_arg_check_num(n_args, n_kw, 0, 0, false); - - samd_flash_init(); - - // Return singleton object. - return MP_OBJ_FROM_PTR(&samd_flash_obj); -} - +#if MICROPY_HW_MCUFLASH // Function for ioctl. static mp_obj_t eraseblock(uint32_t sector_in) { // Destination address aligned with page start to be erased. - uint32_t DEST_ADDR = sector_in; // Number of pages to be erased. - mp_int_t PAGE_SIZE = flash_get_page_size(&flash_desc); // adf4 API call + uint32_t dest_addr = sector_in; // Number of pages to be erased. + mp_int_t page_size = flash_get_page_size(&flash_desc); // adf4 API call - flash_erase(&flash_desc, DEST_ADDR, (BLOCK_SIZE / PAGE_SIZE)); + flash_erase(&flash_desc, dest_addr, (BLOCK_SIZE / page_size)); return mp_const_none; } @@ -131,7 +146,7 @@ static mp_obj_t samd_flash_writeblocks(size_t n_args, const mp_obj_t *args) { } // Write data to flash (adf4 API) flash_write(&flash_desc, offset, bufinfo.buf, bufinfo.len); - // TODO check return value + return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(samd_flash_writeblocks_obj, 3, 4, samd_flash_writeblocks); @@ -176,7 +191,75 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_QSTR_Flash, MP_TYPE_FLAG_NONE, make_new, samd_flash_make_new, + buffer, samd_flash_get_buffer, locals_dict, &samd_flash_locals_dict ); +#endif -#endif // MICROPY_HW_MCUFLASH +#if MICROPY_VFS_ROM +// +// Uses object-based capabilities for devices using the internal flash +// for the regular file system and ioctl function for devices with +// external flash. +// +extern uint8_t _micropy_hw_romfs_part0_start, _micropy_hw_romfs_part0_size; + +#define MICROPY_HW_ROMFS_BASE ((uint32_t)&_micropy_hw_romfs_part0_start) +#define MICROPY_HW_ROMFS_BYTES ((uint32_t)&_micropy_hw_romfs_part0_size) + +#if MICROPY_HW_MCUFLASH +static samd_flash_obj_t samd_flash_romfs_obj = { + .base = { &samd_flash_type }, + .flash_base = MICROPY_HW_ROMFS_BASE, // Get from MCU-Specific loader script. + .flash_size = MICROPY_HW_ROMFS_BYTES, // Get from MCU-Specific loader script. +}; +#else +static const MP_DEFINE_MEMORYVIEW_OBJ(samd_flash_romfs_obj, 'B', 0, MICROPY_HW_ROMFS_BYTES, (void *)MICROPY_HW_ROMFS_BASE); +#endif + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + if (MICROPY_HW_ROMFS_BYTES <= 0) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + 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(&samd_flash_romfs_obj); + + #if !MICROPY_HW_MCUFLASH + + case 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_addr = MICROPY_HW_ROMFS_BASE; + uint32_t bytes_used = mp_obj_get_int(args[2]); + mp_int_t page_size = flash_get_page_size(&flash_desc); // adf4 API call + flash_erase(&flash_desc, dest_addr, (bytes_used + page_size - 1) / page_size); + return MP_OBJ_NEW_SMALL_INT(4); + } + + case MP_VFS_ROM_IOCTL_WRITE: { + // Write data to flash. + if (n_args < 4) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest_addr = MICROPY_HW_ROMFS_BASE + mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + flash_write(&flash_desc, dest_addr, bufinfo.buf, bufinfo.len); + return MP_OBJ_NEW_SMALL_INT(0); + } + + #endif + + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} +#endif + +#endif // MICROPY_HW_MCUFLASH || MICROPY_VFS_ROM diff --git a/ports/samd/samd_soc.c b/ports/samd/samd_soc.c index fb6eb2083a304..761eb8d1aae6a 100644 --- a/ports/samd/samd_soc.c +++ b/ports/samd/samd_soc.c @@ -39,6 +39,7 @@ #include "tusb.h" extern void machine_rtc_start(bool force); +extern void samd_flash_init(void); static void usb_init(void) { // Init USB clock @@ -120,6 +121,9 @@ void samd_init(void) { mp_hal_ticks_cpu_enable(); #endif machine_rtc_start(false); + #if MICROPY_HW_MCUFLASH || MICROPY_VFS_ROM + samd_flash_init(); + #endif } #if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_I2C_TARGET || MICROPY_PY_MACHINE_SPI || MICROPY_PY_MACHINE_UART From cee950673d1f04ddd1b33bfb682c1c8623ea81a0 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 11 Dec 2024 14:21:18 +0100 Subject: [PATCH 096/177] renesas-ra: Add support for VfsRom filesystem. The ROM partition is taken from the last part of the flash text section, so the existing writable filesystem is untouched. VfsRom file system sizes: - EK_RA4M1: 12K - RA4M1_CLICKER: 12k - EK_RA4W1: 64k - EK_RA6M1: 64k - EK_RA6M2: 256k - VK_RA6M5: 384k - ARDUINO_PORTENTA_C33: 256k Tested with Weact RA4M1 core board with EK_RA4M1 firmware and EK_RA6M2. Signed-off-by: robert-hh Signed-off-by: Damien George --- .../boards/ARDUINO_PORTENTA_C33/ra6m5.ld | 6 +- ports/renesas-ra/boards/EK_RA4M1/ra4m1_ek.ld | 10 ++- ports/renesas-ra/boards/EK_RA4W1/ra4w1_ek.ld | 10 ++- ports/renesas-ra/boards/EK_RA6M1/ra6m1_ek.ld | 6 +- ports/renesas-ra/boards/EK_RA6M2/ra6m2_ek.ld | 11 ++- .../boards/RA4M1_CLICKER/ra4m1_clicker.ld | 6 +- ports/renesas-ra/boards/VK_RA6M5/vk_ra6m5.ld | 6 +- ports/renesas-ra/mpconfigboard_common.h | 5 ++ ports/renesas-ra/storage.c | 68 +++++++++++++++++-- 9 files changed, 115 insertions(+), 13 deletions(-) diff --git a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/ra6m5.ld b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/ra6m5.ld index c1fac5106c37e..a937e01cdf514 100644 --- a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/ra6m5.ld +++ b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/ra6m5.ld @@ -6,7 +6,8 @@ MEMORY { FLASH_BOOT (r) : ORIGIN = 0x00000000, LENGTH = 0x00010000 /* 64K */ - FLASH (rx) : ORIGIN = 0x00010000, LENGTH = 0x000f0000 /* 960KB */ + FLASH (rx) : ORIGIN = 0x00010000, LENGTH = 0x000B0000 /* 704KB/2MB */ + FLASH_ROMFS (r) : ORIGIN = 0x000C0000, LENGTH = 0x00040000 /* 256KB/2MB */ FLASH_FS (r) : ORIGIN = 0x00100000, LENGTH = 0x00100000 /* 1MB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00080000 /* 512KB */ OSPI_RAM (rwx) : ORIGIN = 0x68000000, LENGTH = 0x00800000 /* 8MB/8MB */ @@ -304,3 +305,6 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); diff --git a/ports/renesas-ra/boards/EK_RA4M1/ra4m1_ek.ld b/ports/renesas-ra/boards/EK_RA4M1/ra4m1_ek.ld index 52f8acf93eb33..e6e4cb3d31559 100644 --- a/ports/renesas-ra/boards/EK_RA4M1/ra4m1_ek.ld +++ b/ports/renesas-ra/boards/EK_RA4M1/ra4m1_ek.ld @@ -3,9 +3,14 @@ */ /* Linker script to configure memory regions. */ +/* + * FLASH_ROMFS and FLASH_FS must start and length must be a multiple + * of the physical sector size of that region (2k). + */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00037000 /* 220KB/256KB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00034000 /* 208KB/256KB */ + FLASH_ROMFS (r) : ORIGIN = 0x00034000, LENGTH = 0x00003000 /* 12KB/256KB */ FLASH_FS (r) : ORIGIN = 0x00037000, LENGTH = 0x00009000 /* 36KB/256KB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 /* 32KB */ DATA_FLASH (rx) : ORIGIN = 0x40100000, LENGTH = 0x00002000 /* 8KB */ @@ -300,3 +305,6 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); diff --git a/ports/renesas-ra/boards/EK_RA4W1/ra4w1_ek.ld b/ports/renesas-ra/boards/EK_RA4W1/ra4w1_ek.ld index 1241b5bc2302b..7c021207a40ed 100644 --- a/ports/renesas-ra/boards/EK_RA4W1/ra4w1_ek.ld +++ b/ports/renesas-ra/boards/EK_RA4W1/ra4w1_ek.ld @@ -3,9 +3,14 @@ */ /* Linker script to configure memory regions. */ +/* + * FLASH_ROMFS and FLASH_FS must start and length must be a multiple + * of the physical sector size of that region (2k). + */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00070000 /* 448KB/512KB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00060000 /* 384KB/512KB */ + FLASH_ROMFS (r) : ORIGIN = 0x00060000, LENGTH = 0x00010000 /* 64KB/512KB */ FLASH_FS (r) : ORIGIN = 0x00070000, LENGTH = 0x00010000 /* 64KB/512KB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00018000 /* 96KB */ DATA_FLASH (rx) : ORIGIN = 0x40100000, LENGTH = 0x00002000 /* 8KB */ @@ -300,3 +305,6 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); diff --git a/ports/renesas-ra/boards/EK_RA6M1/ra6m1_ek.ld b/ports/renesas-ra/boards/EK_RA6M1/ra6m1_ek.ld index c7d85ed3db0a1..876525d717b2c 100644 --- a/ports/renesas-ra/boards/EK_RA6M1/ra6m1_ek.ld +++ b/ports/renesas-ra/boards/EK_RA6M1/ra6m1_ek.ld @@ -5,7 +5,8 @@ /* Linker script to configure memory regions. */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00070000 /* 448KB/512KB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00060000 /* 384KB/512KB */ + FLASH_ROMFS (r) : ORIGIN = 0x00060000, LENGTH = 0x00010000 /* 64KB/512KB */ FLASH_FS (r) : ORIGIN = 0x00070000, LENGTH = 0x00010000 /* 64KB/512KB */ RAM (rwx) : ORIGIN = 0x1FFE0000, LENGTH = 0x00040000 /* 256KB */ DATA_FLASH (rx) : ORIGIN = 0x40100000, LENGTH = 0x00002000 /* 8KB */ @@ -300,3 +301,6 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); diff --git a/ports/renesas-ra/boards/EK_RA6M2/ra6m2_ek.ld b/ports/renesas-ra/boards/EK_RA6M2/ra6m2_ek.ld index 086f6630c110a..423cf63b70105 100644 --- a/ports/renesas-ra/boards/EK_RA6M2/ra6m2_ek.ld +++ b/ports/renesas-ra/boards/EK_RA6M2/ra6m2_ek.ld @@ -3,9 +3,15 @@ */ /* Linker script to configure memory regions. */ + +/* + * FLASH_ROMFS and FLASH_FS must start and length must be a multiple + * of the physical sector size of that region (32k). + */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x000e0000 /* 896KB/1MB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x000A0000 /* 768KB/1MB */ + FLASH_ROMFS (r) : ORIGIN = 0x000A0000, LENGTH = 0x00040000 /* 256KB/1MB */ FLASH_FS (r) : ORIGIN = 0x000e0000, LENGTH = 0x00020000 /* 128KB/1MB */ RAM (rwx) : ORIGIN = 0x1FFE0000, LENGTH = 0x00060000 /* 384KB */ DATA_FLASH (rx) : ORIGIN = 0x40100000, LENGTH = 0x00008000 /* 32KB */ @@ -300,3 +306,6 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); diff --git a/ports/renesas-ra/boards/RA4M1_CLICKER/ra4m1_clicker.ld b/ports/renesas-ra/boards/RA4M1_CLICKER/ra4m1_clicker.ld index 52f8acf93eb33..92d34ba336662 100644 --- a/ports/renesas-ra/boards/RA4M1_CLICKER/ra4m1_clicker.ld +++ b/ports/renesas-ra/boards/RA4M1_CLICKER/ra4m1_clicker.ld @@ -5,7 +5,8 @@ /* Linker script to configure memory regions. */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00037000 /* 220KB/256KB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00034000 /* 208KB/256KB */ + FLASH_ROMFS (r) : ORIGIN = 0x00034000, LENGTH = 0x00003000 /* 12KB/256KB */ FLASH_FS (r) : ORIGIN = 0x00037000, LENGTH = 0x00009000 /* 36KB/256KB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 /* 32KB */ DATA_FLASH (rx) : ORIGIN = 0x40100000, LENGTH = 0x00002000 /* 8KB */ @@ -300,3 +301,6 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); diff --git a/ports/renesas-ra/boards/VK_RA6M5/vk_ra6m5.ld b/ports/renesas-ra/boards/VK_RA6M5/vk_ra6m5.ld index 8363d2e743f24..82e9bbb4bd2b2 100644 --- a/ports/renesas-ra/boards/VK_RA6M5/vk_ra6m5.ld +++ b/ports/renesas-ra/boards/VK_RA6M5/vk_ra6m5.ld @@ -5,7 +5,8 @@ /* Linker script to configure memory regions. */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00100000 /* 1MB/2MB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x000A0000 /* 640KB/2MB */ + FLASH_ROMFS (r) : ORIGIN = 0x000A0000, LENGTH = 0x00060000 /* 384KB/2MB */ FLASH_FS (r) : ORIGIN = 0x00100000, LENGTH = 0x00100000 /* 1MB/2MB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00080000 /* 512KB */ OSPI_RAM (rwx) : ORIGIN = 0x68000000, LENGTH = 0x00800000 /* 8MB/8MB */ @@ -306,3 +307,6 @@ _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); _micropy_hw_external_flash_storage_start = ORIGIN(QSPI_FLASH); _micropy_hw_external_flash_storage_end = ORIGIN(QSPI_FLASH) + LENGTH(QSPI_FLASH); + +_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS); diff --git a/ports/renesas-ra/mpconfigboard_common.h b/ports/renesas-ra/mpconfigboard_common.h index 479c9f61d1a68..a1aa4c0539e03 100644 --- a/ports/renesas-ra/mpconfigboard_common.h +++ b/ports/renesas-ra/mpconfigboard_common.h @@ -154,6 +154,11 @@ #define MICROPY_HW_UART_IS_RESERVED(uart_id) (false) #endif +// Whether to support the VFSROM file system +#ifndef MICROPY_VFS_ROM +#define MICROPY_VFS_ROM (1) +#endif + /*****************************************************************************/ // General configuration diff --git a/ports/renesas-ra/storage.c b/ports/renesas-ra/storage.c index 12416e4dc7a9f..4262a5ab651e9 100644 --- a/ports/renesas-ra/storage.c +++ b/ports/renesas-ra/storage.c @@ -28,6 +28,7 @@ #include #include +#include "py/objarray.h" #include "py/runtime.h" #include "py/mperrno.h" #include "extmod/vfs_fat.h" @@ -36,6 +37,13 @@ #include "led.h" #include "storage.h" #include "irq.h" +#include "flash.h" + +typedef struct _pyb_flash_obj_t { + mp_obj_base_t base; + uint32_t start; // in bytes + uint32_t len; // in bytes +} pyb_flash_obj_t; #if MICROPY_HW_ENABLE_STORAGE @@ -236,12 +244,6 @@ int storage_readblocks_ext(uint8_t *dest, uint32_t block, uint32_t offset, uint3 } #endif -typedef struct _pyb_flash_obj_t { - mp_obj_base_t base; - uint32_t start; // in bytes - uint32_t len; // in bytes -} pyb_flash_obj_t; - // This Flash object represents the entire available flash, with emulated partition table at start const pyb_flash_obj_t pyb_flash_obj = { { &pyb_flash_type }, @@ -427,3 +429,57 @@ void pyb_flash_init_vfs(fs_user_mount_t *vfs) { } #endif + +#if MICROPY_VFS_ROM + +extern uint32_t _micropy_hw_romfs_part0_start, _micropy_hw_romfs_part0_size; + +#define MICROPY_HW_ROMFS_BASE ((uint32_t)&_micropy_hw_romfs_part0_start) +#define MICROPY_HW_ROMFS_BYTES ((uint32_t)&_micropy_hw_romfs_part0_size) +#define VFSROM_BLOCK_SIZE (2048) + +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) { + if (MICROPY_HW_ROMFS_BYTES <= 0) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + 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: { + // Erase sectors in given range. + if (n_args < 3) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = MICROPY_HW_ROMFS_BASE; + uint32_t dest_max = dest + mp_obj_get_int(args[2]); + uint32_t sec_size = sector_size(dest); + for (; dest < dest_max; dest += sec_size) { + flash_erase(dest, sec_size); + } + return MP_OBJ_NEW_SMALL_INT(4); // minimum write size + } + + case MP_VFS_ROM_IOCTL_WRITE: { + // Write data to flash. + if (n_args < 4) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = MICROPY_HW_ROMFS_BASE + mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + flash_write(dest, bufinfo.buf, bufinfo.len); + return MP_OBJ_NEW_SMALL_INT(0); + } + + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} + +#endif // MICROPY_VFS_ROM From 610552010dd7c2f728b2ea8f900ef969348a3a92 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 25 Nov 2024 21:49:04 +0100 Subject: [PATCH 097/177] nrf: Add support for VfsRom filesystem. The implementation uses the object based capabilities, which avoids complication about different flash block sizes. The ROM partition is placed between the text and writable filesystem sections, and the latter size is unchanged. VfsRom sizes are: - NRF51x22: 12K - NRF52832: 128K - NRF52840: 256K - NRF9160: 256K Use frozen `_boot.py` to set up and mount the filesystem, replacing a mix of C-Code and Python code. The mkfs part has been simplified to save code. Tested with Microbit and Arduino Nano Connect. Signed-off-by: Damien George Signed-off-by: robert-hh --- ports/nrf/boards/memory.ld | 6 ++-- ports/nrf/boards/nrf51x22_256k_16k.ld | 1 + ports/nrf/boards/nrf51x22_256k_32k.ld | 1 + ports/nrf/boards/nrf52832_512k_64k.ld | 1 + ports/nrf/boards/nrf52840_1M_256k.ld | 1 + ports/nrf/boards/nrf9160_1M_256k.ld | 1 + ports/nrf/main.c | 18 ++--------- ports/nrf/modules/manifest.py | 2 +- ports/nrf/modules/nrf/flashbdev.c | 44 +++++++++++++++++++++++++++ ports/nrf/modules/scripts/_boot.py | 28 +++++++++++++++++ ports/nrf/modules/scripts/_mkfs.py | 23 -------------- ports/nrf/mpconfigport.h | 5 +++ 12 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 ports/nrf/modules/scripts/_boot.py delete mode 100644 ports/nrf/modules/scripts/_mkfs.py diff --git a/ports/nrf/boards/memory.ld b/ports/nrf/boards/memory.ld index 9c4c9c8bd1267..df95b951b076d 100644 --- a/ports/nrf/boards/memory.ld +++ b/ports/nrf/boards/memory.ld @@ -8,10 +8,12 @@ _head_size = DEFINED(_sd_size) ? _sd_size : _bootloader_head_size; _head_ram = DEFINED(_sd_ram) ? _sd_ram : _bootloader_head_ram_size; _sd_size = DEFINED(_sd_size) ? _sd_size : 0; _sd_ram = DEFINED(_sd_ram) ? _sd_ram : 0; +_micropy_hw_romfs_part0_size = DEFINED(_micropy_hw_romfs_part0_size) ? _micropy_hw_romfs_part0_size : 0; _fs_size = DEFINED(_fs_size) ? _fs_size : 64K; /* TODO: set to 0 if not using the filesystem */ -_app_size = _flash_size - _head_size - _fs_size - _bootloader_tail_size; +_app_size = _flash_size - _head_size - _micropy_hw_romfs_part0_size - _fs_size - _bootloader_tail_size; _app_start = _head_size; -_fs_start = _head_size + _app_size; +_micropy_hw_romfs_part0_start = _head_size + _app_size; +_fs_start = _micropy_hw_romfs_part0_start + _micropy_hw_romfs_part0_size; _fs_end = _fs_start + _fs_size; _app_ram_start = 0x20000000 + _head_ram; _app_ram_size = _ram_size - _head_ram; diff --git a/ports/nrf/boards/nrf51x22_256k_16k.ld b/ports/nrf/boards/nrf51x22_256k_16k.ld index 8a40ae0f17307..4c54def8e803b 100644 --- a/ports/nrf/boards/nrf51x22_256k_16k.ld +++ b/ports/nrf/boards/nrf51x22_256k_16k.ld @@ -6,6 +6,7 @@ GROUP(-lgcc -lc -lnosys) _flash_size = 256K; _ram_size = 16K; +_micropy_hw_romfs_part0_size = 12K; /* Default stack size when there is no SoftDevice */ _stack_size = 4K; diff --git a/ports/nrf/boards/nrf51x22_256k_32k.ld b/ports/nrf/boards/nrf51x22_256k_32k.ld index 06c0914035b4a..b61c2a55056a3 100644 --- a/ports/nrf/boards/nrf51x22_256k_32k.ld +++ b/ports/nrf/boards/nrf51x22_256k_32k.ld @@ -6,6 +6,7 @@ GROUP(-lgcc -lc -lnosys) _flash_size = 256K; _ram_size = 32K; +_micropy_hw_romfs_part0_size = 12K; /* Default stack size when there is no SoftDevice */ _stack_size = 4K; diff --git a/ports/nrf/boards/nrf52832_512k_64k.ld b/ports/nrf/boards/nrf52832_512k_64k.ld index 22804df5cdb5e..0599c9bda3b3b 100644 --- a/ports/nrf/boards/nrf52832_512k_64k.ld +++ b/ports/nrf/boards/nrf52832_512k_64k.ld @@ -4,6 +4,7 @@ _flash_size = 512K; _ram_size = 64K; +_micropy_hw_romfs_part0_size = 128K; /* produce a link error if there is not this amount of RAM for these sections */ _stack_size = 8K; diff --git a/ports/nrf/boards/nrf52840_1M_256k.ld b/ports/nrf/boards/nrf52840_1M_256k.ld index 16d61af6a30b9..21d4518243484 100644 --- a/ports/nrf/boards/nrf52840_1M_256k.ld +++ b/ports/nrf/boards/nrf52840_1M_256k.ld @@ -4,6 +4,7 @@ _flash_size = 1M; _ram_size = 256K; +_micropy_hw_romfs_part0_size = 256K; /* produce a link error if there is not this amount of RAM for these sections */ _stack_size = 8K; diff --git a/ports/nrf/boards/nrf9160_1M_256k.ld b/ports/nrf/boards/nrf9160_1M_256k.ld index 6347095a899ce..d8f0aa5cb01f0 100644 --- a/ports/nrf/boards/nrf9160_1M_256k.ld +++ b/ports/nrf/boards/nrf9160_1M_256k.ld @@ -7,6 +7,7 @@ _ram_size = 256K; _sd_size = 0x00008000; _sd_ram = 0x00020000; _fs_size = 80K; +_micropy_hw_romfs_part0_size = 256K; /* produce a link error if there is not this amount of RAM for these sections */ _stack_size = 32K; diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 7278a646ab6ec..1013005e1987d 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -177,22 +177,8 @@ void MP_NORETURN _start(void) { #if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE flashbdev_init(); - - // Try to mount the flash on "/flash" and chdir to it for the boot-up directory. - mp_obj_t mount_point = MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash); - int ret = mp_vfs_mount_and_chdir_protected((mp_obj_t)&nrf_flash_obj, mount_point); - - if ((ret == -MP_ENODEV) || (ret == -MP_EIO)) { - pyexec_frozen_module("_mkfs.py", false); // Frozen script for formatting flash filesystem. - ret = mp_vfs_mount_and_chdir_protected((mp_obj_t)&nrf_flash_obj, mount_point); - } - - if (ret != 0) { - printf("MPY: can't mount flash\n"); - } else { - mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash)); - mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash_slash_lib)); - } + // Execute _boot.py to set up the filesystem. + pyexec_frozen_module("_boot.py", false); #endif #if MICROPY_MBFS diff --git a/ports/nrf/modules/manifest.py b/ports/nrf/modules/manifest.py index 7ba0f80d884d0..631ad19a8db8a 100644 --- a/ports/nrf/modules/manifest.py +++ b/ports/nrf/modules/manifest.py @@ -1,2 +1,2 @@ -module("_mkfs.py", base_path="$(PORT_DIR)/modules/scripts", opt=3) +module("_boot.py", base_path="$(PORT_DIR)/modules/scripts", opt=3) include("$(MPY_DIR)/extmod/asyncio") diff --git a/ports/nrf/modules/nrf/flashbdev.c b/ports/nrf/modules/nrf/flashbdev.c index 41833b228e2ff..b28314b4a62b3 100644 --- a/ports/nrf/modules/nrf/flashbdev.c +++ b/ports/nrf/modules/nrf/flashbdev.c @@ -183,12 +183,26 @@ static mp_obj_t nrf_flashbdev_make_new(const mp_obj_type_t *type, size_t n_args, return MP_OBJ_FROM_PTR(self); } +static mp_int_t nrf_flashbdev_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + nrf_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)self->start; + bufinfo->len = self->len; + bufinfo->typecode = 'B'; + return 0; + } else { + // Unsupported. + return 1; + } +} + MP_DEFINE_CONST_OBJ_TYPE( nrf_flashbdev_type, MP_QSTR_Flash, MP_TYPE_FLAG_NONE, make_new, nrf_flashbdev_make_new, print, nrf_flashbdev_print, + buffer, nrf_flashbdev_get_buffer, locals_dict, &nrf_flashbdev_locals_dict ); @@ -202,4 +216,34 @@ void flashbdev_init(void) { nrf_flash_obj.len = num_pages * FLASH_PAGESIZE; } +#if MICROPY_VFS_ROM + +extern byte _micropy_hw_romfs_part0_start[]; +extern byte _micropy_hw_romfs_part0_size[]; + +#define MICROPY_HW_ROMFS_BASE ((uint32_t)_micropy_hw_romfs_part0_start) +#define MICROPY_HW_ROMFS_BYTES ((uint32_t)_micropy_hw_romfs_part0_size) + +static nrf_flash_obj_t nrf_flash_romfs_obj = { + .base = { &nrf_flashbdev_type }, + .start = MICROPY_HW_ROMFS_BASE, // Get from MCU-Specific loader script. + .len = MICROPY_HW_ROMFS_BYTES, // Get from MCU-Specific loader script. +}; + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + if (MICROPY_HW_ROMFS_BYTES <= 0) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + 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(&nrf_flash_romfs_obj); + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} + +#endif // MICROPY_VFS_ROM + #endif // MICROPY_PY_NRF && MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE diff --git a/ports/nrf/modules/scripts/_boot.py b/ports/nrf/modules/scripts/_boot.py new file mode 100644 index 0000000000000..793c56ee149a3 --- /dev/null +++ b/ports/nrf/modules/scripts/_boot.py @@ -0,0 +1,28 @@ +def setup_fs(): + import gc + import vfs + import sys + import nrf + import os + + fs_type = getattr(vfs, "VfsLfs2", getattr(vfs, "VfsLfs1", getattr(vfs, "VfsFat", None))) + try: + bdev = nrf.Flash() + vfs.mount(bdev, "/flash") + except: + if fs_type is not None: + try: + fs_type.mkfs(bdev) + vfs.mount(bdev, "/flash") + except: + return + + os.chdir("/flash") + sys.path.append("/flash") + sys.path.append("/flash/lib") + + gc.collect() + + +setup_fs() +del setup_fs diff --git a/ports/nrf/modules/scripts/_mkfs.py b/ports/nrf/modules/scripts/_mkfs.py deleted file mode 100644 index 601f9558eb7b5..0000000000000 --- a/ports/nrf/modules/scripts/_mkfs.py +++ /dev/null @@ -1,23 +0,0 @@ -import vfs, nrf - -try: - from vfs import VfsLfs1 - - vfs.VfsLfs1.mkfs(nrf.Flash()) -except ImportError: - try: - from vfs import VfsLfs2 - - vfs.VfsLfs2.mkfs(nrf.Flash()) - except ImportError: - try: - from vfs import VfsFat - - vfs.VfsFat.mkfs(nrf.Flash()) - except ImportError: - pass - except OSError as e: - if e.args[0] == 5: # I/O Error - flashbdev_size = (nrf.Flash.ioctl(4, 0) * nrf.Flash.ioctl(5, 0)) // 1024 - print() - print("Is `FS_SIZE=%iK` enough for FAT filesystem?" % flashbdev_size) diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index cce3f86cfd244..bb517c19c5599 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -69,6 +69,11 @@ #define MICROPY_VFS (CORE_FEAT) #endif +// VfsROM filesystem +#ifndef MICROPY_VFS_ROM +#define MICROPY_VFS_ROM (CORE_FEAT) +#endif + // micro:bit filesystem #ifndef MICROPY_MBFS #define MICROPY_MBFS (!MICROPY_VFS) From 049cdd0dd3d27e771907fff82967df78d88f0aa7 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 14 Nov 2025 03:33:47 +0100 Subject: [PATCH 098/177] cc3200/mods/pybpin: Reduce footprint of AF pin structures. This commit performs a minor change to the "pin_af_t" structure, changing the data type used to store alternate function pin name QSTRs in order to have a smaller impact on the .rodata section of the firmware. The data type used to store the QSTR name of the pin is changed from "qstr" to "qstr_short_t", that is able to store the same name string index but in half the size. Other pin-related structures in the port that also store QSTRs do not benefit from a narrower data type, as their members are mostly word-aligned and thus the linker is forced to insert padding bytes between entries. Signed-off-by: Alessandro Gatti --- ports/cc3200/mods/pybpin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/cc3200/mods/pybpin.h b/ports/cc3200/mods/pybpin.h index 74f0af2b3ca65..5e55d2cf95a3b 100644 --- a/ports/cc3200/mods/pybpin.h +++ b/ports/cc3200/mods/pybpin.h @@ -88,7 +88,7 @@ enum { }; typedef struct { - qstr name; + qstr_short_t name; int8_t idx; uint8_t fn; uint8_t unit; From 8bda61a3afe3f2406d80de509d5a98b2f010fad4 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 14 Nov 2025 03:46:01 +0100 Subject: [PATCH 099/177] mimxrt/pin: Reduce footprint of pin structures. This commit performs a few minor changes to the structures used to store board pin information, in order to reduce the impact on the .rodata section of the firmware of instances of those structures. The data type used to store the QSTR name of alternate function pins ("machine_pin_af_obj_t") is changed from "qstr" to "qstr_short_t", that is able to store the same name string index but in half the size. Regular pin objects structure ("machine_pin_obj_t") instead has its QSTR variable holder changed from "qstr" to "qstr_short_t", and some elements of that structure have been rearranged to remove enough padding bytes inside structure elements to let the linker allocate less data for instances of that structure. Signed-off-by: Alessandro Gatti --- ports/mimxrt/pin.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/mimxrt/pin.h b/ports/mimxrt/pin.h index 13b313c41f409..7ee2841465988 100644 --- a/ports/mimxrt/pin.h +++ b/ports/mimxrt/pin.h @@ -106,7 +106,7 @@ enum { typedef struct { mp_obj_base_t base; - qstr name; // port name + qstr_short_t name; // port name uint8_t af_mode; // alternate function uint8_t input_daisy; void *instance; // pointer to peripheral instance for alternate function @@ -121,15 +121,15 @@ typedef struct { typedef struct { mp_obj_base_t base; - qstr name; // pad name GPIO_Type *gpio; // gpio instance for pin uint32_t pin; // pin number uint32_t muxRegister; uint32_t configRegister; - uint8_t af_list_len; // length of available alternate functions list - uint8_t adc_list_len; // length of available ADC options list const machine_pin_af_obj_t *af_list; // pointer to list with alternate functions const machine_pin_adc_obj_t *adc_list; // pointer to list with ADC options + qstr_short_t name; // pad name + uint8_t af_list_len; // length of available alternate functions list + uint8_t adc_list_len; // length of available ADC options list } machine_pin_obj_t; typedef struct _machine_pin_irq_obj_t { From fb8ecaecf56a2e393688a8907bd987cb1d7d763a Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 14 Nov 2025 03:57:46 +0100 Subject: [PATCH 100/177] renesas-ra/pin: Reduce footprint of pin structures. This commit performs a few minor changes to the structures used to store board pin information, in order to reduce the impact on the .rodata section of the firmware of instances of those structures. The pin objects structure ("machine_pin_obj_t") instead has its QSTR variable holder changed from "qstr" to "qstr_short_t", and some elements of that structure have been rearranged to remove enough padding bytes inside structure elements to let the linker allocate less data for instances of that structure. Signed-off-by: Alessandro Gatti --- ports/renesas-ra/pin.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/renesas-ra/pin.h b/ports/renesas-ra/pin.h index 76d6c0b1e430d..649f1f2fcea75 100644 --- a/ports/renesas-ra/pin.h +++ b/ports/renesas-ra/pin.h @@ -41,9 +41,9 @@ typedef struct { typedef struct { mp_obj_base_t base; - qstr name; - uint8_t pin; const machine_pin_adc_obj_t *ad; + qstr_short_t name; + uint8_t pin; } machine_pin_obj_t; // Include all of the individual pin objects From 31deee90608e76c0e0829db8e819f03ae85b1bfb Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 14 Nov 2025 04:19:11 +0100 Subject: [PATCH 101/177] rp2/machine_pin: Reduce footprint of pin structures. This commit performs a few minor changes to the structures used to store board pin information, in order to reduce the impact on the .rodata section of the firmware of instances of those structures. The data type used to store the QSTR name of both regular and alternate function pins ("machine_pin_obj_t" and "machine_pin_af_obj_t") is changed from "qstr" to "qstr_short_t", that is able to store the same name string index but in half the size. Signed-off-by: Alessandro Gatti --- ports/rp2/machine_pin.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/rp2/machine_pin.h b/ports/rp2/machine_pin.h index 2d85bdcadc165..47c0d328e56c1 100644 --- a/ports/rp2/machine_pin.h +++ b/ports/rp2/machine_pin.h @@ -40,7 +40,7 @@ enum { typedef struct _machine_pin_af_obj_t { mp_obj_base_t base; - qstr name; + qstr_short_t name; uint8_t idx : 4; uint8_t fn : 4; uint8_t unit : 8; @@ -48,7 +48,7 @@ typedef struct _machine_pin_af_obj_t { typedef struct _machine_pin_obj_t { mp_obj_base_t base; - qstr name; + qstr_short_t name; uint8_t id : 6; #if MICROPY_HW_PIN_EXT_COUNT uint8_t is_ext : 1; From 6572ce84c95b72dac92cc587ca16babf92afe59e Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 14 Nov 2025 04:27:17 +0100 Subject: [PATCH 102/177] samd/pin_af: Reduce footprint of pin structures. This commit performs a few minor changes to the structures used to store board pin information, in order to reduce the impact on the .rodata section of the firmware of instances of those structures. The pin objects structure ("machine_pin_obj_t") instead has its QSTR variable holder changed from "qstr" to "qstr_short_t", and some elements of that structure have been rearranged to remove enough padding bytes inside structure elements to let the linker allocate less data for instances of that structure. Signed-off-by: Alessandro Gatti --- ports/samd/boards/pins_prefix.c | 4 ++-- ports/samd/pin_af.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/samd/boards/pins_prefix.c b/ports/samd/boards/pins_prefix.c index 4f0095d332130..24378593393bc 100644 --- a/ports/samd/boards/pins_prefix.c +++ b/ports/samd/boards/pins_prefix.c @@ -11,11 +11,11 @@ #if defined(MCU_SAMD21) #define PIN(p_name, p_eic, p_adc0, p_sercom1, p_sercom2, p_tcc1, p_tcc2) \ - {{&machine_pin_type}, PIN_##p_name, MP_QSTR_##p_name, p_eic, p_adc0, p_sercom1, p_sercom2, p_tcc1, p_tcc2 } + {{&machine_pin_type}, MP_QSTR_##p_name, PIN_##p_name, p_eic, p_adc0, p_sercom1, p_sercom2, p_tcc1, p_tcc2 } #elif defined(MCU_SAMD51) #define PIN(p_name, p_eic, p_adc0, p_adc1, p_sercom1, p_sercom2, p_tc, p_tcc1, p_tcc2) \ - {{&machine_pin_type}, PIN_##p_name, MP_QSTR_##p_name, p_eic, p_adc0, p_adc1, p_sercom1, p_sercom2, p_tc, p_tcc1, p_tcc2 } + {{&machine_pin_type}, MP_QSTR_##p_name, PIN_##p_name, p_eic, p_adc0, p_adc1, p_sercom1, p_sercom2, p_tc, p_tcc1, p_tcc2 } #endif diff --git a/ports/samd/pin_af.h b/ports/samd/pin_af.h index bc65e8ae23bf9..11ef75c63f51e 100644 --- a/ports/samd/pin_af.h +++ b/ports/samd/pin_af.h @@ -32,8 +32,8 @@ typedef struct _machine_pin_obj_t { mp_obj_base_t base; + qstr_short_t name; uint8_t pin_id; - qstr name; uint8_t eic; uint8_t adc0; uint8_t sercom1; @@ -50,8 +50,8 @@ typedef struct _machine_pin_obj_t { typedef struct _machine_pin_obj_t { mp_obj_base_t base; + qstr_short_t name; uint8_t pin_id; - qstr name; uint8_t eic; uint8_t adc0; uint8_t adc1; From 11861e96a949a912d2a22c920191333da67452ed Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 15 Nov 2025 17:04:01 +0100 Subject: [PATCH 103/177] nrf/modules/machine/pin: Reduce footprint of pin structures. This commit performs a few minor changes to the structures used to store board pin information, in order to reduce the impact on the .rodata section of the firmware of instances of those structures. The "pin_obj_t" structure, holding pin information, had an unused word-sized field ("pull") that isn't used by any of the board definitions, yet it takes up space in each pin structure being compiled in the final firmware image. Images for all available boards were built successfully with this change, so there should be no unwanted issues arising from shrinking that structure. Signed-off-by: Alessandro Gatti --- ports/nrf/modules/machine/pin.h | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/nrf/modules/machine/pin.h b/ports/nrf/modules/machine/pin.h index 41579011b5d84..a67c39d2848cf 100644 --- a/ports/nrf/modules/machine/pin.h +++ b/ports/nrf/modules/machine/pin.h @@ -56,7 +56,6 @@ typedef struct { uint32_t adc_channel : 5; // Some ARM processors use 32 bits/PORT uint32_t adc_num : 3; // 1 bit per ADC const pin_af_obj_t *af; - uint32_t pull; } pin_obj_t; extern const mp_obj_type_t pin_type; From d468ccce62e4d48fc726ef9f883159f2ca3baa47 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sun, 30 Nov 2025 11:05:57 +0100 Subject: [PATCH 104/177] extmod/modopenamp: Rework trace buffer setup procedure. This commit reworks the setup procedure for the OpenAMP trace buffer, used by the libmetal framework to provide cross-core logging data if needed. Before these changes, the buffer was provided by MicroPython, as a fixed size 128 bytes chunk that was accidentally put into the .rodata section, making it not usable for its intended purpose. Now, a buffer placed in .bss with a default size of 128 bytes is provided by MicroPython unless chosen otherwise. A user-chosen buffer pointer can be provided to MicroPython using the MICROPY_PY_OPENAMP_TRACE_BUF preprocessor definition. If what MicroPython provides by default is fine, the buffer size can be overridden with a new value for the MICROPY_PY_OPENAMP_TRACE_BUF_LEN preprocessor definition instead. Signed-off-by: Alessandro Gatti --- extmod/modopenamp.c | 9 +++++---- extmod/modopenamp.h | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/extmod/modopenamp.c b/extmod/modopenamp.c index 7d5841c40001d..b5e813495ba20 100644 --- a/extmod/modopenamp.c +++ b/extmod/modopenamp.c @@ -80,11 +80,12 @@ #define VRING_BUFF_ADDR (METAL_SHM_ADDR + 0x2000) #define VRING_BUFF_SIZE (METAL_SHM_SIZE - 0x2000) -#if MICROPY_PY_OPENAMP_HOST -static const char openamp_trace_buf[128]; +#if MICROPY_PY_OPENAMP_HOST && MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE +#ifndef MICROPY_PY_OPENAMP_TRACE_BUF +static char openamp_trace_buf[MICROPY_PY_OPENAMP_TRACE_BUF_LEN]; #define MICROPY_PY_OPENAMP_TRACE_BUF ((uint32_t)openamp_trace_buf) -#define MICROPY_PY_OPENAMP_TRACE_BUF_LEN sizeof(MICROPY_PY_OPENAMP_TRACE_BUF) -#endif // MICROPY_PY_OPENAMP_HOST +#endif // MICROPY_PY_OPENAMP_TRACE_BUF +#endif // MICROPY_PY_OPENAMP_HOST && MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE #endif // MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE diff --git a/extmod/modopenamp.h b/extmod/modopenamp.h index 8f677788f90be..463399507b964 100644 --- a/extmod/modopenamp.h +++ b/extmod/modopenamp.h @@ -47,6 +47,12 @@ #define MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE (1) #endif +// Set the default trace buffer size, making it 128 bytes long unless +// marked otherwise. +#ifndef MICROPY_PY_OPENAMP_TRACE_BUF_LEN +#define MICROPY_PY_OPENAMP_TRACE_BUF_LEN (128) +#endif + // For ports that don't define a custom image store, this enables a generic // VFS-based image store that supports loading elf files from storage. #ifndef MICROPY_PY_OPENAMP_REMOTEPROC_STORE_ENABLE From 3980a0c01f3157526bbe4b16fbf7ba018058d0e2 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sun, 7 Dec 2025 01:06:16 +0100 Subject: [PATCH 105/177] py/misc: Add byte-swapping macros. This commit adds byte-swapping macros for 16-, 32-, and 64-bit values. Modern compilers are usually able to detect manual byte/endian swaps and will generate optimised code sequences if they know how to do that. However, in certain situations it may be helpful to let the compiler generate an optimised sequence if it doesn't recognise the pattern. Most compilers provide built-in byte swap operations for 16, 32, and 64 bit values, accessed via "__builtin_bswap", but if those are not available a fallback implementation is provided. Signed-off-by: Alessandro Gatti --- py/misc.h | 26 ++++++++++++++++++++++++++ py/mpconfig.h | 4 ++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/py/misc.h b/py/misc.h index f3688c73e4583..ab266084c8bd9 100644 --- a/py/misc.h +++ b/py/misc.h @@ -553,4 +553,30 @@ static inline bool mp_sub_ll_overflow(long long int lhs, long long int rhs, long #define MP_SANITIZER_BUILD (MP_UBSAN || MP_ASAN) #endif +// halfword/word/longword swapping macros + +#if __has_builtin(__builtin_bswap16) +#define MP_BSWAP16(x) __builtin_bswap16(x) +#else +#define MP_BSWAP16(x) ((uint16_t)((((x) & 0xFF) << 8) | (((x) >> 8) & 0xFF))) +#endif + +#if __has_builtin(__builtin_bswap32) +#define MP_BSWAP32(x) __builtin_bswap32(x) +#else +#define MP_BSWAP32(x) \ + ((uint32_t)((((x) & 0xFF) << 24) | (((x) & 0xFF00) << 8) | \ + (((x) >> 8) & 0xFF00) | (((x) >> 24) & 0xFF))) +#endif + +#if __has_builtin(__builtin_bswap64) +#define MP_BSWAP64(x) __builtin_bswap64(x) +#else +#define MP_BSWAP64(x) \ + ((uint64_t)((((x) & 0xFF) << 56) | (((x) & 0xFF00) << 40) | \ + (((x) & 0xFF0000) << 24) | (((x) & 0xFF000000) << 8) | \ + (((x) >> 8) & 0xFF000000) | (((x) >> 24) & 0xFF0000) | \ + (((x) >> 40) & 0xFF00) | (((x) >> 56) & 0xFF))) +#endif + #endif // MICROPY_INCLUDED_PY_MISC_H diff --git a/py/mpconfig.h b/py/mpconfig.h index 3211d4d398e05..2a71c73162890 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -2394,7 +2394,7 @@ typedef time_t mp_timestamp_t; #ifndef MP_HTOBE16 #if MP_ENDIANNESS_LITTLE -#define MP_HTOBE16(x) ((uint16_t)((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))) +#define MP_HTOBE16(x) MP_BSWAP16(x) #define MP_BE16TOH(x) MP_HTOBE16(x) #else #define MP_HTOBE16(x) (x) @@ -2404,7 +2404,7 @@ typedef time_t mp_timestamp_t; #ifndef MP_HTOBE32 #if MP_ENDIANNESS_LITTLE -#define MP_HTOBE32(x) ((uint32_t)((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) >> 8) & 0xff00) | (((x) >> 24) & 0xff))) +#define MP_HTOBE32(x) MP_BSWAP32(x) #define MP_BE32TOH(x) MP_HTOBE32(x) #else #define MP_HTOBE32(x) (x) From 678dbd26f88928c403d6e55215bd3f560da649fb Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 30 Dec 2025 21:17:41 +0100 Subject: [PATCH 106/177] alif/Makefile: Do not hardcode the python interpreter name. This commit fixes a build issue occurring on systems where the Python 3.x interpreter isn't aliased to 'python' but has a different name (usually `python3'). The main Linux distribution where this occurs is Ubuntu LTS, where the Python 3.x interpreter is available as 'python3' since at least two major versions (so since about five years now). MicroPython's makefile environment setup file ('py/mkenv.mk') already defines a variable called '$(PYTHON)' that defaults to 'python3', so the Makefile was updated to use that instead of hardcoding the interpreter name. Without these changes, building a firmware image on Ubuntu LTS fails with this error: make: python: No such file or directory make: *** [Makefile:95: build-ALIF_ENSEMBLE/firmware.toc.bin] Error 127 This also allows overriding the interpreter name to something else if there's the need to perform tests with different interpreter versions or the like. Signed-off-by: Alessandro Gatti --- ports/alif/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/alif/Makefile b/ports/alif/Makefile index d258b27b1d578..5eff6ccd9ad4d 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -92,7 +92,7 @@ $(BUILD)/$(ALIF_TOC_CONFIG): mcu/$(ALIF_TOC_CONFIG).in | $(BUILD) $(Q)$(CPP) -P -E $(ALIF_TOC_CFLAGS) - < mcu/$(ALIF_TOC_CONFIG).in > $@ $(ALIF_TOC_BIN): $(ALIF_TOC_APPS) - $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \ + $(Q)$(PYTHON) $(ALIF_TOOLS)/app-gen-toc.py \ --filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \ --output-dir $(BUILD) \ --firmware-dir $(BUILD) \ @@ -105,7 +105,7 @@ $(BUILD)/firmware.zip: $(ALIF_TOC_BIN) $(ALIF_TOC_APPS) .PHONY: deploy deploy: $(ALIF_TOC_BIN) $(ECHO) "Writing $< to the board" - $(Q)python $(ALIF_TOOLS)/app-write-mram.py \ + $(Q)$(PYTHON) $(ALIF_TOOLS)/app-write-mram.py \ --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ --port $(PORT) \ --pad \ @@ -117,13 +117,13 @@ deploy-jlink: $(ALIF_TOC_APPS) .PHONY: maintenance maintenance: - $(Q)python $(ALIF_TOOLS)/maintenance.py \ + $(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 \ + $(Q)$(PYTHON) $(ALIF_TOOLS)/updateSystemPackage.py \ --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ --port $(PORT) From 2554e4172fea2eb129e251d9c6ee54d3cd66f1b7 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Tue, 30 Dec 2025 21:21:54 +0100 Subject: [PATCH 107/177] alif/Makefile: Enable job server support on Ubuntu LTS. This commit modifies the Alif port's Makefile to propagate to child make tasks the number of parallel jobs to run. On Ubuntu LTS 24.04, building the Alif port passing a parallel jobs number to make's command line will result in the command emitting this warning: make[1]: warning: jobserver unavailable: using -j1. Add '+' to parent make rule. And building the two cores' firmware images using one compile job at a time instead of the number provided to the command line. Signed-off-by: Alessandro Gatti --- ports/alif/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 5eff6ccd9ad4d..e7a4d2f104c72 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -82,10 +82,10 @@ $(BUILD): $(MKDIR) -p $@ $(BUILD)/M55_HP/firmware.bin: - make -f alif.mk BUILD=$(BUILD)/M55_HP MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0 + $(MAKE) -f alif.mk BUILD=$(BUILD)/M55_HP MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0 $(BUILD)/M55_HE/firmware.bin: - make -f alif.mk BUILD=$(BUILD)/M55_HE MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1 + $(MAKE) -f alif.mk BUILD=$(BUILD)/M55_HE MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1 $(BUILD)/$(ALIF_TOC_CONFIG): mcu/$(ALIF_TOC_CONFIG).in | $(BUILD) $(ECHO) "Preprocess toc config $@" From ab5f47c54d895ec77dabcad23a5c2aa5102e080c Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 6 Dec 2025 09:20:58 +0100 Subject: [PATCH 108/177] alif/ospi_flash: Remove workaround for P10_7 OSPI pinmux issue. P5_6 doesn't have that alt function, and only has 7 alt functions. This workaround was never really needed, it was introduced in DFP because the wrong P10_7 alt function was used, in original DFP sources. This has been removed from DFP starting with 1.3.4, so remove it here also. Signed-off-by: iabdalkader --- ports/alif/mcu/ensemble_pin_alt.csv | 3 +-- ports/alif/ospi_flash.c | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/ports/alif/mcu/ensemble_pin_alt.csv b/ports/alif/mcu/ensemble_pin_alt.csv index 34f13e86f6d3c..751f8e4b60052 100644 --- a/ports/alif/mcu/ensemble_pin_alt.csv +++ b/ports/alif/mcu/ensemble_pin_alt.csv @@ -45,8 +45,7 @@ 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_6,GPIO,,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, diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index f78002eb0ef97..64c2438c2f1f2 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -274,11 +274,6 @@ int ospi_flash_init(void) { 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_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_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_D0, unit), true); From 1174dda51923a2ed7c0fac4e9ffc4b8340129763 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 20 Dec 2025 13:12:26 +0100 Subject: [PATCH 109/177] alif/boards/OPENMV_AE3: Update romfs partition size. Update romfs partition to reflect the latest layout. Signed-off-by: iabdalkader --- ports/alif/boards/OPENMV_AE3/board.ld.S | 4 ++-- ports/alif/boards/OPENMV_AE3/mpconfigboard.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/alif/boards/OPENMV_AE3/board.ld.S b/ports/alif/boards/OPENMV_AE3/board.ld.S index 0d09bb15f874f..539e24b1de260 100644 --- a/ports/alif/boards/OPENMV_AE3/board.ld.S +++ b/ports/alif/boards/OPENMV_AE3/board.ld.S @@ -3,8 +3,8 @@ /* 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_part0_start = 0xa0800000; +_micropy_hw_romfs_part0_size = 24M; _micropy_hw_romfs_part1_start = ORIGIN(MRAM_FS); _micropy_hw_romfs_part1_size = LENGTH(MRAM_FS); #else diff --git a/ports/alif/boards/OPENMV_AE3/mpconfigboard.h b/ports/alif/boards/OPENMV_AE3/mpconfigboard.h index 0495bc81c8a62..a4981ce167e55 100644 --- a/ports/alif/boards/OPENMV_AE3/mpconfigboard.h +++ b/ports/alif/boards/OPENMV_AE3/mpconfigboard.h @@ -67,8 +67,8 @@ extern void board_exit_standby(void); // 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) +#define MICROPY_HW_FLASH_STORAGE_FS_BYTES (8 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_ROMFS_BYTES (24 * 1024 * 1024) // Murata 1YN configuration #define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/w43439_sdio_1yn_7_95_59_combined.h" From 75eb197f9d1e6780e71ca0c6673461ad5a79f353 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Sat, 27 Dec 2025 22:49:27 -0800 Subject: [PATCH 110/177] alif/boards/OPENMV_AE3: Make JTAG pins controllable as GPIOs. Allows user control of JTAG pins on the AE3 which are exposed via the B2B header. Signed-off-by: Kwabena W. Agyeman --- ports/alif/boards/OPENMV_AE3/pins.csv | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/alif/boards/OPENMV_AE3/pins.csv b/ports/alif/boards/OPENMV_AE3/pins.csv index 360b27af813c5..9ebd3b726bdd8 100644 --- a/ports/alif/boards/OPENMV_AE3/pins.csv +++ b/ports/alif/boards/OPENMV_AE3/pins.csv @@ -55,6 +55,10 @@ P6,P7_2 P7,P7_3 P8,P1_2 P9,P1_3 +P10,P4_4 +P11,P4_7 +P13,P4_5 +P14,P4_6 # UART buses UART1_TX,P0_5 From 1947759c943176c88e35dc27ab3faf1680471f8b Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 23 Dec 2025 10:12:15 +0100 Subject: [PATCH 111/177] renesas-ra/boards/ARDUINO_PORTENTA_C33: Increase TinyUSB queue size. Default queue size is too small that it overflows easily if debugging printfs are enabled. Increase TinyUSB event queue size to 128. Signed-off-by: iabdalkader --- .../renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.mk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.mk b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.mk index 9bb336d65e9d5..69333eab4abe6 100644 --- a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.mk +++ b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.mk @@ -3,8 +3,9 @@ MCU_SERIES = m33 LD_FILES = boards/ARDUINO_PORTENTA_C33/ra6m5.ld CFLAGS += -DCFG_TUH_MAX_SPEED=OPT_MODE_FULL_SPEED \ -DCFG_TUD_MAX_SPEED=OPT_MODE_HIGH_SPEED \ - -DCFG_TUSB_RHPORT0_MODE=0\ - -DCFG_TUSB_RHPORT1_MODE=OPT_MODE_DEVICE + -DCFG_TUSB_RHPORT0_MODE=0 \ + -DCFG_TUSB_RHPORT1_MODE=OPT_MODE_DEVICE \ + -DCFG_TUD_TASK_QUEUE_SZ=128 # MicroPython settings MICROPY_VFS_FAT = 1 From 2cb41ff97b14011714b33a265995da2709ae3584 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 19:05:32 +0000 Subject: [PATCH 112/177] github/workflows: Bump actions/upload-artifact from 4 to 6. Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/code_size.yml | 2 +- .github/workflows/mpremote.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/code_size.yml b/.github/workflows/code_size.yml index aed416767883d..7d3329613fac2 100644 --- a/.github/workflows/code_size.yml +++ b/.github/workflows/code_size.yml @@ -41,7 +41,7 @@ jobs: run: echo $PR_NUMBER > pr_number - name: Upload diff if: github.event_name == 'pull_request' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: code-size-report path: | diff --git a/.github/workflows/mpremote.yml b/.github/workflows/mpremote.yml index 5b9f1ff130a27..25cf56d587d5b 100644 --- a/.github/workflows/mpremote.yml +++ b/.github/workflows/mpremote.yml @@ -22,7 +22,7 @@ jobs: - name: Build mpremote wheel run: cd tools/mpremote && python -m build --wheel - name: Archive mpremote wheel - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: mpremote path: | From 99c59fabdbd4f7aaadffd29f1d5aca9ebf341046 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 19:05:36 +0000 Subject: [PATCH 113/177] github/workflows: Bump actions/cache from 4 to 5. Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ports_esp32.yml | 2 +- .github/workflows/ports_zephyr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ports_esp32.yml b/.github/workflows/ports_esp32.yml index 6b3700ed5eee5..95572eedfb673 100644 --- a/.github/workflows/ports_esp32.yml +++ b/.github/workflows/ports_esp32.yml @@ -41,7 +41,7 @@ jobs: - name: Cached ESP-IDF install id: cache_esp_idf - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | ./esp-idf/ diff --git a/.github/workflows/ports_zephyr.yml b/.github/workflows/ports_zephyr.yml index 812f4cbf47ba2..571a443e903f8 100644 --- a/.github/workflows/ports_zephyr.yml +++ b/.github/workflows/ports_zephyr.yml @@ -43,7 +43,7 @@ jobs: run: source tools/ci.sh && echo "ZEPHYR=$ZEPHYR_VERSION" | tee "$GITHUB_OUTPUT" - name: Cached Zephyr Workspace id: cache_workspace - uses: actions/cache@v4 + uses: actions/cache@v5 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 From 0aa6115aacf415da1f7039a341c0cd21799cdb0b Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 9 Oct 2025 08:15:35 -0500 Subject: [PATCH 114/177] github/workflows: Introduce and use ci_esp32_idf_ver helper. Signed-off-by: Jeff Epler --- .github/workflows/ports_esp32.yml | 4 ++-- tools/ci.sh | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ports_esp32.yml b/.github/workflows/ports_esp32.yml index 95572eedfb673..92ac6df3b61ca 100644 --- a/.github/workflows/ports_esp32.yml +++ b/.github/workflows/ports_esp32.yml @@ -36,8 +36,8 @@ jobs: - uses: actions/checkout@v6 - id: idf_ver - 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: Read the ESP-IDF version (including Python version) and set outputs.IDF_VER + run: tools/ci.sh esp32_idf_ver | tee "${GITHUB_OUTPUT}" - name: Cached ESP-IDF install id: cache_esp_idf diff --git a/tools/ci.sh b/tools/ci.sh index ec4b57a8f5641..8b12225660112 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -206,6 +206,10 @@ PYTHON_VER=$(${PYTHON:-python} --version | cut -d' ' -f2) export IDF_CCACHE_ENABLE=1 +function ci_esp32_idf_ver { + echo "IDF_VER=${IDF_VER}-py${PYTHON_VER}" +} + function ci_esp32_idf_setup { echo "Using ESP-IDF version $IDF_VER" git clone --depth 1 --branch $IDF_VER https://github.com/espressif/esp-idf.git From 6c7edce76cadb2c54dae5c9d6e48c78e50aa815d Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 8 Oct 2025 08:39:24 -0500 Subject: [PATCH 115/177] github/workflows: Add esp32 to code size report. Add an esp32 build (specifically ESP32_GENERIC) to the CI code-size check. Multiple new steps must be done to prepare for building esp32, and caching is used to speed up both the install of the IDF and the build process. Signed-off-by: Jeff Epler --- .github/workflows/code_size.yml | 24 ++++++++++++++++++++++++ .github/workflows/ports_esp32.yml | 2 ++ tools/ci.sh | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code_size.yml b/.github/workflows/code_size.yml index 7d3329613fac2..066f6806c4859 100644 --- a/.github/workflows/code_size.yml +++ b/.github/workflows/code_size.yml @@ -30,6 +30,30 @@ jobs: fetch-depth: 100 - name: Install packages run: tools/ci.sh code_size_setup + + # Needs to be kept in synch with ports_esp32.yml + - id: idf_ver + name: Read the ESP-IDF version (including Python version) and set outputs.IDF_VER + run: tools/ci.sh esp32_idf_ver | tee "${GITHUB_OUTPUT}" + - name: Cached ESP-IDF install + id: cache_esp_idf + uses: actions/cache@v4 + with: + path: | + ./esp-idf/ + ~/.espressif/ + !~/.espressif/dist/ + ~/.cache/pip/ + key: esp-idf-${{ steps.idf_ver.outputs.IDF_VER }} + - name: Install ESP-IDF packages + if: steps.cache_esp_idf.outputs.cache-hit != 'true' + run: tools/ci.sh esp32_idf_setup + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: code_size + - name: Build run: tools/ci.sh code_size_build - name: Compute code size difference diff --git a/.github/workflows/ports_esp32.yml b/.github/workflows/ports_esp32.yml index 92ac6df3b61ca..87ab6dbe35543 100644 --- a/.github/workflows/ports_esp32.yml +++ b/.github/workflows/ports_esp32.yml @@ -35,6 +35,7 @@ jobs: steps: - uses: actions/checkout@v6 + # Needs to be kept in synch with code_size.yml - id: idf_ver name: Read the ESP-IDF version (including Python version) and set outputs.IDF_VER run: tools/ci.sh esp32_idf_ver | tee "${GITHUB_OUTPUT}" @@ -54,6 +55,7 @@ jobs: if: steps.cache_esp_idf.outputs.cache-hit != 'true' run: tools/ci.sh esp32_idf_setup + # Needs to be kept in synch with code_size.yml - name: ccache uses: hendrikmuhs/ccache-action@v1.2 with: diff --git a/tools/ci.sh b/tools/ci.sh index 8b12225660112..85d736552a269 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -87,7 +87,7 @@ function _ci_is_git_merge { function ci_code_size_build { # check the following ports for the change in their code size # Override the list by setting PORTS_TO_CHECK in the environment before invoking ci. - : ${PORTS_TO_CHECK:=bmusxpdv} + : ${PORTS_TO_CHECK:=bmus3xpdv} 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" From 569ebaa8283aa7ea28392943986413102c890b8c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 27 Nov 2025 20:46:29 -0600 Subject: [PATCH 116/177] tools/ci.sh: Put embedding build rules in ci.sh. To make this CI step runnable locally by `ci.sh`. Signed-off-by: Jeff Epler --- .github/workflows/examples.yml | 4 +--- examples/embedding/.gitignore | 4 ++++ tools/ci.sh | 9 +++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 examples/embedding/.gitignore diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 4adeaae2e5e4b..4627247fb9f2a 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -20,6 +20,4 @@ jobs: steps: - uses: actions/checkout@v6 - name: Build - run: make -C examples/embedding -f micropython_embed.mk && make -C examples/embedding - - name: Run - run: ./examples/embedding/embed | grep "hello world" + run: tools/ci.sh embedding_build diff --git a/examples/embedding/.gitignore b/examples/embedding/.gitignore new file mode 100644 index 0000000000000..78bd33eb13e74 --- /dev/null +++ b/examples/embedding/.gitignore @@ -0,0 +1,4 @@ +# Files created by ci.sh embed_build +embed +main.o +micropython_embed diff --git a/tools/ci.sh b/tools/ci.sh index 85d736552a269..82eb09428125f 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -194,6 +194,15 @@ function ci_cc3200_build { make ${MAKEOPTS} -C ports/cc3200 BTARGET=bootloader BTYPE=release } +######################################################################################## +# ports/embed + +function ci_embedding_build { + make ${MAKEOPTS} -C examples/embedding -f micropython_embed.mk + make ${MAKEOPTS} -C examples/embedding + ./examples/embedding/embed | grep "hello world" +} + ######################################################################################## # ports/esp32 From 6dea53cb277c0cf37b2e749a21b56ea9ba6e0531 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 2 Jan 2026 10:27:33 +0100 Subject: [PATCH 117/177] tools/ci.sh: Update Unix/MIPS target to Ubuntu 24.04 LTS. This commit updates the Unix/MIPS target's environment to use the latest available LTS version of Ubuntu Linux (24.04). Since the new OS updated some key components used in the CI build process, a few modifications have been made to the setup and build procedure. Newer QEMU's sysroot location changed and the "static" variant of the QEMU emulators has to be used to make binfmt work, and updated autotools versions split some macros into an optional package that has to be manually installed. Signed-off-by: Alessandro Gatti --- .github/workflows/ports_unix.yml | 8 ++++++-- tools/ci.sh | 14 +++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index 6ef6c28d3317f..96fa1e76ec5ed 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -247,10 +247,14 @@ jobs: run: tests/run-tests.py --print-failures qemu_mips: - # ubuntu-22.04 is needed for older libffi. - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + # 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: Install packages run: tools/ci.sh unix_qemu_mips_setup - name: Build diff --git a/tools/ci.sh b/tools/ci.sh index 82eb09428125f..0e3111473e35a 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -913,11 +913,11 @@ function ci_unix_macos_run_tests { function ci_unix_qemu_mips_setup { sudo apt-get update - sudo apt-get install gcc-mips-linux-gnu g++-mips-linux-gnu libc6-mips-cross - sudo apt-get install qemu-user - qemu-mips --version - sudo mkdir /etc/qemu-binfmt - sudo ln -s /usr/mips-linux-gnu/ /etc/qemu-binfmt/mips + sudo apt-get install gcc-mips-linux-gnu g++-mips-linux-gnu libc6-mips-cross libltdl-dev + sudo apt-get install qemu-user-static + qemu-mips-static --version + sudo mkdir -p /usr/gnemul + sudo ln -s /usr/mips-linux-gnu /usr/gnemul/qemu-mips } function ci_unix_qemu_mips_build { @@ -927,11 +927,11 @@ function ci_unix_qemu_mips_build { function ci_unix_qemu_mips_run_tests { # Issues with MIPS tests: - # - thread/stress_aes.py takes around 50 seconds + # - thread/stress_aes.py takes around 90 seconds # - thread/stress_recurse.py is flaky # - thread/thread_gc1.py is flaky file ./ports/unix/build-coverage/micropython - (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython MICROPY_TEST_TIMEOUT=90 ./run-tests.py --exclude 'thread/stress_recurse.py|thread/thread_gc1.py') + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython MICROPY_TEST_TIMEOUT=180 ./run-tests.py --exclude 'thread/stress_recurse.py|thread/thread_gc1.py') } function ci_unix_qemu_arm_setup { From 16844bb027952aac701cb1dcb004dde7849de118 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 2 Jan 2026 10:33:13 +0100 Subject: [PATCH 118/177] tools/ci.sh: Update Unix/Arm target to Ubuntu 24.04 LTS. This commit updates the Unix/Arm target's environment to use the latest available LTS version of Ubuntu Linux (24.04). Since the new OS updated some key components used in the CI build process, a few modifications have been made to the setup and build procedure. Newer QEMU's sysroot location changed and the "static" variant of the QEMU emulators has to be used to make binfmt work, and updated autotools versions split some macros into an optional package that has to be manually installed. Signed-off-by: Alessandro Gatti --- .github/workflows/ports_unix.yml | 8 ++++++-- tools/ci.sh | 13 ++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index 96fa1e76ec5ed..f083a9d0f1931 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -266,10 +266,14 @@ jobs: run: tests/run-tests.py --print-failures qemu_arm: - # ubuntu-22.04 is needed for older libffi. - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + # 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: Install packages run: tools/ci.sh unix_qemu_arm_setup - name: Build diff --git a/tools/ci.sh b/tools/ci.sh index 0e3111473e35a..f9f40e5dd4eef 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -936,11 +936,11 @@ function ci_unix_qemu_mips_run_tests { function ci_unix_qemu_arm_setup { sudo apt-get update - sudo apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi - sudo apt-get install qemu-user - qemu-arm --version - sudo mkdir /etc/qemu-binfmt - sudo ln -s /usr/arm-linux-gnueabi/ /etc/qemu-binfmt/arm + sudo apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libltdl-dev + sudo apt-get install qemu-user-static + qemu-arm-static --version + sudo mkdir -p /usr/gnemul + sudo ln -s /usr/arm-linux-gnueabi /usr/gnemul/qemu-arm } function ci_unix_qemu_arm_build { @@ -950,12 +950,11 @@ function ci_unix_qemu_arm_build { function ci_unix_qemu_arm_run_tests { # Issues with ARM tests: - # - (i)listdir does not work, it always returns the empty list (it's an issue with the underlying C call) # - thread/stress_aes.py takes around 70 seconds # - thread/stress_recurse.py is flaky # - thread/thread_gc1.py is flaky file ./ports/unix/build-coverage/micropython - (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython MICROPY_TEST_TIMEOUT=90 ./run-tests.py --exclude 'vfs_posix.*\.py|thread/stress_recurse.py|thread/thread_gc1.py') + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython MICROPY_TEST_TIMEOUT=90 ./run-tests.py --exclude 'thread/stress_recurse.py|thread/thread_gc1.py') } function ci_unix_qemu_riscv64_setup { From 31fe86546092b1fa07904c7f6ede6e2a29c0a0be Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 5 Jan 2026 14:25:55 +0100 Subject: [PATCH 119/177] tools/ci.sh: Use CPython 3.11 for Unix/RV64. This commit forces the installation of CPython 3.11 instead of CPython 3.12 in the OS image for Unix/RV64 CI jobs. CPython 3.12 is not compatible with settrace tests, but it is the CPython version that is installed by default in "ubuntu-latest" (which is Ubuntu 24.04 LTS right now). Updating the base image for the RV64 tests also disabled settrace tests to work around its incompatibility, however turns out there is a way to force CI to set up a base OS image with an arbitrary CPython version. Now the RV64 CI jobs are now going to be executed using CPython 3.11, and thus the settrace tests can be removed from the ignore list. Signed-off-by: Alessandro Gatti --- .github/workflows/ports_unix.yml | 5 +++++ tools/ci.sh | 7 +++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index f083a9d0f1931..5f9aa26d1b9ff 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -288,6 +288,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + # 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: Install packages run: tools/ci.sh unix_qemu_riscv64_setup - name: Build diff --git a/tools/ci.sh b/tools/ci.sh index f9f40e5dd4eef..31ef77c260300 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -961,8 +961,8 @@ function ci_unix_qemu_riscv64_setup { ci_gcc_riscv_setup sudo apt-get update sudo apt-get install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu libltdl-dev - sudo pip3 install pyelftools - sudo pip3 install ar + python3 -m pip install pyelftools + python3 -m pip install ar sudo apt-get install qemu-user-static qemu-riscv64-static --version sudo mkdir -p /usr/gnemul @@ -977,13 +977,12 @@ function ci_unix_qemu_riscv64_build { function ci_unix_qemu_riscv64_run_tests { # Issues with RISCV-64 tests: - # - misc/sys_settrace_features.py doesn't work with CPython 3.12 # - thread/stress_aes.py takes around 180 seconds # - thread/stress_recurse.py is flaky # - thread/thread_gc1.py is flaky file ./ports/unix/build-coverage/micropython pushd tests - MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython MICROPY_TEST_TIMEOUT=200 ./run-tests.py --exclude 'misc/sys_settrace_features.py|thread/stress_recurse.py|thread/thread_gc1.py' + MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython MICROPY_TEST_TIMEOUT=200 ./run-tests.py --exclude 'thread/stress_recurse.py|thread/thread_gc1.py' MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-natmodtests.py extmod/btree*.py extmod/deflate*.py extmod/framebuf*.py extmod/heapq*.py extmod/random_basic*.py extmod/re*.py popd } From 26c16969ab954db4d8d79bed154e3d45c12c087f Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 6 Jan 2026 09:12:39 -0600 Subject: [PATCH 120/177] github/workflows: Use same Ubuntu for code_size as ports_esp32. Also make sure code_size runs when the esp32 port source changes, as per the other ports that are built as part of code_size. Signed-off-by: Jeff Epler Signed-off-by: Damien George --- .github/workflows/code_size.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code_size.yml b/.github/workflows/code_size.yml index 066f6806c4859..20f47a8a62efc 100644 --- a/.github/workflows/code_size.yml +++ b/.github/workflows/code_size.yml @@ -10,6 +10,7 @@ on: - 'shared/**' - 'lib/**' - 'ports/bare-arm/**' + - 'ports/esp32/**' - 'ports/mimxrt/**' - 'ports/minimal/**' - 'ports/rp2/**' @@ -23,7 +24,7 @@ concurrency: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: From 21e4c036afc2798871f1d61331e639140f6f5e95 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Wed, 7 Jan 2026 12:04:41 +0100 Subject: [PATCH 121/177] tools/ci.sh: Update Unix/x86 target to Ubuntu 24.04 LTS. This commit updates the Unix/x86 target's environment to use the latest available LTS version of Ubuntu Linux (24.04). Since the new OS updated some key components used in the CI build process, a few modifications have been made to the setup and build procedure. The new OS introduces a CPython version that is known to not be compatible with a subset of settrace tests, so even though the OS is updated, an older version of CPython is installed as part of the image provisioning process. Signed-off-by: Alessandro Gatti --- .github/workflows/ports_unix.yml | 28 ++++++++++++++++++++++++---- tools/ci.sh | 5 ++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index 5f9aa26d1b9ff..bd04163ded8f5 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -103,9 +103,14 @@ jobs: run: tests/run-tests.py --print-failures coverage_32bit: - runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + # 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: Install packages run: tools/ci.sh unix_32bit_setup - name: Build @@ -121,9 +126,14 @@ jobs: run: tests/run-tests.py --print-failures nanbox: - runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + # 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: Install packages run: tools/ci.sh unix_32bit_setup - name: Build @@ -135,9 +145,14 @@ jobs: run: tests/run-tests.py --print-failures longlong: - runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + # 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: Install packages run: tools/ci.sh unix_32bit_setup - name: Build @@ -218,9 +233,14 @@ jobs: run: tests/run-tests.py --print-failures repr_b: - runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + # 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: Install packages run: tools/ci.sh unix_32bit_setup - name: Build diff --git a/tools/ci.sh b/tools/ci.sh index 31ef77c260300..588bb31638c56 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -777,9 +777,8 @@ 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 pip3 install setuptools - sudo pip3 install pyelftools - sudo pip3 install ar + python -m pip install pyelftools + python -m pip install ar gcc --version python3 --version } From 0fd084363a81c369a1487acd62f41f69199a7dae Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 21 Nov 2025 09:26:23 +0100 Subject: [PATCH 122/177] tools/mpy_ld.py: Optimise MPY trampoline sizes if possible. This commit changes the way native modules' trampoline code sequence is emitted, generating an optimised code sequence to jump to the entry symbol. Turns out the address of the entry point is known even before the segments are built and the address of the entry point doesn't change when processing the module on anything but Xtensa. This means that the jump trampoline doesn't have to be a dummy fixed-size block to be filled in later, but it can be the final trampoline being used in the module. On Xtensa the address of the symbol is offset by the length of the literals pool, but since the trampoline being generated is always the shortest one said platform is left alone (handling distances greater than 128KiB would require more extensive changes). Signed-off-by: Alessandro Gatti --- tools/mpy_ld.py | 119 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 39 deletions(-) diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index b363c6a25f7f0..f35a7f7862260 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -131,66 +131,93 @@ # Architecture configuration +def fit_signed(bits, value): + return (value >> bits) == 0 or (value >> bits) == -1 + + +# Note: all trampoline jump function arguments are raw offsets calculated from +# the start of the text segment with no relocation applied beforehand. + + def asm_jump_x86(entry): - return struct.pack("> 11 == 0 or b_off >> 11 == -1: + if fit_signed(11, entry): # Signed value fits in 12 bits. - b0 = 0xE000 | (b_off >> 1 & 0x07FF) - b1 = 0 - b2 = 0 - b3 = 0 + b0 = 0xE000 | ((entry >> 1) & 0x07FF) + return struct.pack(" # pop {r0, pc} - b_off -= 2 # skip "push {r0, lr}" + entry += 2 # skip "push {r0, lr}" b0 = 0xB400 | 0x0100 | 0x0001 # push, lr, r0 - b1 = 0xF000 | (((b_off) >> 12) & 0x07FF) - b2 = 0xF800 | (((b_off) >> 1) & 0x07FF) + b1 = 0xF000 | ((entry >> 12) & 0x07FF) + b2 = 0xF800 | ((entry >> 1) & 0x07FF) b3 = 0xBC00 | 0x0100 | 0x0001 # pop, pc, r0 - return struct.pack("> 11 == 0 or b_off >> 11 == -1: + if fit_signed(11, entry): # Signed value fits in 12 bits - b0 = 0xE000 | (b_off >> 1 & 0x07FF) - b1 = 0 + b0 = 0xE000 | ((entry >> 1) & 0x07FF) + return struct.pack("> 12 & 0x07FF) - b1 = 0xB800 | (b_off >> 1 & 0x7FF) - return struct.pack("> 12) & 0x07FF) + b1 = 0xB800 | ((entry >> 1) & 0x07FF) + return struct.pack("> 8) + if fit_signed(17, entry): + jump_op = (entry - 4) << 6 | 6 + return struct.pack("> 8) + else: + raise LinkError("Large jumps are not yet supported on Xtensa") def asm_jump_riscv(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( - "> 2) + | ((entry & 0x80) >> 1) + | ((entry & 0x40) << 1) + | ((entry & 0x20) >> 3) + | ((entry & 0x10) << 7), + ) + else: + # auipc t6, HI(entry) + # jalr zero, t6, LO(entry) + upper, lower = split_riscv_address(entry + 8) + return struct.pack( + " Date: Fri, 28 Nov 2025 06:25:57 +0100 Subject: [PATCH 123/177] tools/mpy_ld.py: Write architecture flags to output natmod if needed. This commit lets "tools/mpy_ld.py" store architecture flags in generated MPY files if explicitly requested, like "mpy-cross" does. To achieve this, a new command-line option ("--arch-flags") was added to receive the architecture flags value, accepting the same arguments' format as "mpy-cross", and performing the same input validation. The rest of the MPY toolchain was also modified to let the user pass the arch flags to standard native module makefiles. Given that there's already a well-established "ARCH" argument, "ARCH_FLAGS" was chosen to pass the optional flags to "mpy_ld.py". Finally, documentation was updated to mention the new variable. Signed-off-by: Alessandro Gatti --- docs/develop/natmod.rst | 16 +++++++++--- docs/reference/mpyfiles.rst | 3 ++- py/dynruntime.mk | 3 +++ tools/mpy_ld.py | 51 ++++++++++++++++++++++++++++++++----- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/docs/develop/natmod.rst b/docs/develop/natmod.rst index 142e475366f7b..e0f7bdaaa8939 100644 --- a/docs/develop/natmod.rst +++ b/docs/develop/natmod.rst @@ -43,9 +43,14 @@ options for the ``ARCH`` variable, see below): * ``rv32imc`` (RISC-V 32 bits with compressed instructions, eg ESP32C3, ESP32C6) * ``rv64imc`` (RISC-V 64 bits with compressed instructions) +If the chosen platform supports explicit architecture flags and you want to let +the output .mpy file carry those flags' value, you must pass them to the +``ARCH_FLAGS`` flags variable when building the .mpy file. + 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 -details about .mpy files see :ref:`mpy_files`. +and the corresponding file can only be imported on that architecture (and if +architecture flags are present, only if they match the target's capabilities). +For more details about .mpy files see :ref:`mpy_files`. Native code must be compiled as position independent code (PIC) and use a global offset table (GOT), although the details of this varies from architecture to @@ -124,7 +129,8 @@ The filesystem layout consists of two main parts, the source files and the Makef location of the MicroPython repository (to find header files, the relevant Makefile fragment, and the ``mpy_ld.py`` tool), ``MOD`` as the name of the module, ``SRC`` as the list of source files, optionally specify the machine architecture via ``ARCH``, - and then include ``py/dynruntime.mk``. + along with optional machine architecture flags specified via ``ARCH_FLAGS``, and + then include ``py/dynruntime.mk``. Minimal example --------------- @@ -217,6 +223,10 @@ Without modifying the Makefile you can specify the target architecture via:: $ make ARCH=armv7m +Same applies for optional architecture flags via:: + + $ make ARCH=rv32imc ARCH_FLAGS=zba + Module usage in MicroPython --------------------------- diff --git a/docs/reference/mpyfiles.rst b/docs/reference/mpyfiles.rst index 58e45e158523c..978a798350a78 100644 --- a/docs/reference/mpyfiles.rst +++ b/docs/reference/mpyfiles.rst @@ -193,7 +193,8 @@ MPY files is used to indicate that no specific extensions are needed, and saves one byte in the final output binary. See also the ``-march-flags`` command-line option in both ``mpy-tool.py`` and -``mpy-cross`` to set this value when creating MPY files. +``mpy-cross``, and the ``--arch-flags`` command-line option in ``mpy_ld.py`` to +set this value when creating MPY files. The global qstr and constant tables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/py/dynruntime.mk b/py/dynruntime.mk index 866d5fae7b13e..3902acbd0b32a 100644 --- a/py/dynruntime.mk +++ b/py/dynruntime.mk @@ -194,6 +194,9 @@ endif ifneq ($(MPY_EXTERN_SYM_FILE),) MPY_LD_FLAGS += --externs "$(realpath $(MPY_EXTERN_SYM_FILE))" endif +ifneq ($(ARCH_FLAGS),) +MPY_LD_FLAGS += --arch-flags "$(ARCH_FLAGS)" +endif CFLAGS += $(CFLAGS_EXTRA) diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index f35a7f7862260..02d2431c4ee1c 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -38,6 +38,7 @@ # MicroPython constants MPY_VERSION = 6 MPY_SUB_VERSION = 3 +MPY_ARCH_FLAGS = 0x40 MP_CODE_BYTECODE = 2 MP_CODE_NATIVE_VIPER = 4 MP_NATIVE_ARCH_X86 = 1 @@ -1379,7 +1380,7 @@ def write_reloc(self, base, offset, dest, n): self.write_uint(n) -def build_mpy(env, fmpy, native_qstr_vals): +def build_mpy(env, fmpy, native_qstr_vals, arch_flags): # Rewrite the entry trampoline if the proper value isn't known earlier, and # ensure the trampoline size remains the same. if env.arch.delayed_entry_offset: @@ -1399,12 +1400,16 @@ def build_mpy(env, fmpy, native_qstr_vals): out = MPYOutput() out.open(fmpy) + header_flags = env.arch.mpy_feature | MPY_SUB_VERSION + if arch_flags != 0: + header_flags |= MPY_ARCH_FLAGS + # MPY: header - out.write_bytes( - bytearray( - [ord("M"), MPY_VERSION, env.arch.mpy_feature | MPY_SUB_VERSION, MP_SMALL_INT_BITS] - ) - ) + out.write_bytes(bytearray([ord("M"), MPY_VERSION, header_flags, MP_SMALL_INT_BITS])) + + # MPY: arch flags + if arch_flags != 0: + out.write_uint(arch_flags) # MPY: n_qstr out.write_uint(1 + len(native_qstr_vals)) @@ -1553,7 +1558,7 @@ def do_link(args): load_object_file(env, f, obj_name) link_objects(env, len(native_qstr_vals)) - build_mpy(env, args.output, native_qstr_vals) + build_mpy(env, args.output, native_qstr_vals, args.arch_flags) except LinkError as er: print("LinkError:", er.args[0]) sys.exit(1) @@ -1603,6 +1608,35 @@ def parse_linkerscript(source): return symbols +RV32_EXTENSIONS = { + "zba": 1 << 0, + "zcmp": 1 << 1, +} + + +def validate_arch_flags(args): + if args.arch_flags is None: + args.arch_flags = 0 + return + if args.arch != "rv32imc": + raise ValueError('Architecture "{}" does not support extra flags'.format(args.arch)) + if (args.arch_flags.startswith("0") and len(args.arch_flags) > 2) or args.arch_flags.isdigit(): + if args.arch_flags[1] in "bB": + base = 2 + elif args.arch_flags[1] in "xX": + base = 16 + else: + base = 10 + args.arch_flags = int(args.arch_flags, base) + else: + flags_value = 0 + for flag in args.arch_flags.lower().split(","): + if flag not in RV32_EXTENSIONS: + raise ValueError('Invalid architecture flags value "{}"'.format(flag)) + flags_value |= RV32_EXTENSIONS[flag] + args.arch_flags = flags_value + + def main(): import argparse @@ -1611,6 +1645,7 @@ def main(): "--verbose", "-v", action="count", default=1, help="increase verbosity" ) cmd_parser.add_argument("--arch", default="x64", help="architecture") + cmd_parser.add_argument("--arch-flags", default=None, help="optional architecture flags") 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( @@ -1632,6 +1667,8 @@ def main(): global log_level log_level = args.verbose + validate_arch_flags(args) + if args.preprocess: do_preprocess(args) else: From 83996f057fecfdfdded66eeff2aa6193ee7a2f8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:06:20 +0000 Subject: [PATCH 124/177] github/workflows: Bump actions/cache from 4 to 5. Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/code_size.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code_size.yml b/.github/workflows/code_size.yml index 20f47a8a62efc..cf6838b7ae1d3 100644 --- a/.github/workflows/code_size.yml +++ b/.github/workflows/code_size.yml @@ -38,7 +38,7 @@ jobs: run: tools/ci.sh esp32_idf_ver | tee "${GITHUB_OUTPUT}" - name: Cached ESP-IDF install id: cache_esp_idf - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | ./esp-idf/ From 3a487c32ba858278d3231b9c57529f1d4ba13810 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 11 Sep 2025 12:01:19 +1000 Subject: [PATCH 125/177] all: Fix spelling of Micropython -> MicroPython. Signed-off-by: Damien George --- ports/mimxrt/machine_pwm.c | 2 +- ports/zephyr/README.md | 8 ++++---- ports/zephyr/src/usbd.c | 2 +- tools/mpremote/mpremote/mip.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ports/mimxrt/machine_pwm.c b/ports/mimxrt/machine_pwm.c index df76ed2b8bd34..9a2e39fd82708 100644 --- a/ports/mimxrt/machine_pwm.c +++ b/ports/mimxrt/machine_pwm.c @@ -375,7 +375,7 @@ static void configure_pwm(machine_pwm_obj_t *self) { } } -// Micropython API functions +// MicroPython API functions // static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md index f3fcd8f70d3a5..be5e322ca5598 100644 --- a/ports/zephyr/README.md +++ b/ports/zephyr/README.md @@ -211,8 +211,8 @@ run the following after you built an image with the previous command: File Systems ------------ -The Zephyr Micropython port provides 2 options for handling filesystems on the device: -The first is the Micropython filesystem management, which uses Micropython's filesystem code and +The Zephyr MicroPython port provides 2 options for handling filesystems on the device: +The first is the MicroPython filesystem management, which uses MicroPython's filesystem code and relies on zephyr's FlashArea API, this is enabled by default when `CONFIG_FLASH` and `CONFIG_FLASH_MAP` are turned on. The second option is using Zephyr's Filesystem management: @@ -241,13 +241,13 @@ Then, a fstab must be added to the dts overlay, for example: }; }; -It is then possible to use the FS like a normal Micropython filesystem: +It is then possible to use the FS like a normal MicroPython filesystem: import vfs, zephyr zfs = zephyr.FileSystem(zephyr.FileSystem.fstab()[0]) vfs.mount(zfs, "/zephyr") -You may disable Micropython's File system code to save space: +You may disable MicroPython's File system code to save space: CONFIG_MICROPY_VFS_FAT=n CONFIG_MICROPY_VFS_LFS1=n diff --git a/ports/zephyr/src/usbd.c b/ports/zephyr/src/usbd.c index 36b07a8638fd0..118463a0756a5 100644 --- a/ports/zephyr/src/usbd.c +++ b/ports/zephyr/src/usbd.c @@ -56,7 +56,7 @@ USBD_DEVICE_DEFINE(mp_usbd, USBD_DESC_LANG_DEFINE(mp_lang); USBD_DESC_MANUFACTURER_DEFINE(mp_mfr, "Zephyr Project"); -USBD_DESC_PRODUCT_DEFINE(mp_product, "Micropython on Zephyr RTOS"); +USBD_DESC_PRODUCT_DEFINE(mp_product, "MicroPython on Zephyr RTOS"); USBD_DESC_SERIAL_NUMBER_DEFINE(mp_sn); USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); diff --git a/tools/mpremote/mpremote/mip.py b/tools/mpremote/mpremote/mip.py index fa7974053f44d..0d12ae651ecca 100644 --- a/tools/mpremote/mpremote/mip.py +++ b/tools/mpremote/mpremote/mip.py @@ -1,4 +1,4 @@ -# Micropython package installer +# MicroPython package installer # Ported from micropython-lib/micropython/mip/mip.py. # MIT license; Copyright (c) 2022 Jim Mussared From e52916daaa455cc338ad01ad541eb6dc81a19a0d Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 11 Sep 2025 11:41:24 +1000 Subject: [PATCH 126/177] github/workflows: Add check for misspelling of "MicroPython". "MicroPython" is sometimes misspelled as "Micropython". Add an explicit check for that as part of CI (note that codespell doesn't consider case when spelling, hence the need to do it this way). Signed-off-by: Damien George --- .github/workflows/codespell.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index c413a430a5b29..e3a9c79bd5e78 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -10,4 +10,9 @@ jobs: # codespell version should be kept in sync with .pre-commit-config.yml - run: pip install --user codespell==2.4.1 tomli - run: codespell - + # additionally check for misspelling of "MicroPython" + - run: | + if git grep -n Micropython -- ":(exclude).github/workflows/codespell.yml"; then + echo "Please correct capitalisation of MicroPython on the above lines" + exit 1 + fi From b105677ac49b1d240d9e735ff9616aa78edd71fe Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 11 Dec 2025 11:58:11 +1100 Subject: [PATCH 127/177] esp32/machine_pin: Rename legacy CONFIG_ESP32_SPIRAM_SUPPORT option. Signed-off-by: Damien George --- ports/esp32/machine_pin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/machine_pin.h b/ports/esp32/machine_pin.h index 4e21b032e66cb..c63630eb85daf 100644 --- a/ports/esp32/machine_pin.h +++ b/ports/esp32/machine_pin.h @@ -49,7 +49,7 @@ #define MICROPY_HW_ENABLE_GPIO13 (1) #define MICROPY_HW_ENABLE_GPIO14 (1) #define MICROPY_HW_ENABLE_GPIO15 (1) -#if !CONFIG_ESP32_SPIRAM_SUPPORT +#if !CONFIG_SPIRAM #define MICROPY_HW_ENABLE_GPIO16 (1) #define MICROPY_HW_ENABLE_GPIO17 (1) #endif From 85a15f995c661d6499b89aa8fdb21681db9f09b6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 11 Dec 2025 11:58:42 +1100 Subject: [PATCH 128/177] esp32/machine_uart: Change default UART(1) pins on ESP32 with SPIRAM. SPIRAM usually uses GPIO 9 and 10 on ESP32, so don't use them as default UART pins. Fixes issue #18544. Signed-off-by: Damien George --- docs/esp32/quickref.rst | 3 +++ ports/esp32/machine_uart.c | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 0cf7d81a7eaac..b4961fd4ed432 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -380,6 +380,9 @@ tx 1 10 17 rx 3 9 16 ===== ===== ===== ===== +On ESP32 with SPIRAM, the default pins for UART1 are ``tx=5`` and ``rx=4`` +to avoid possible conflicts with the SPIRAM pins. + PWM (pulse width modulation) ---------------------------- diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index a2034b749271f..4a0782f7443b5 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -278,8 +278,14 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, self->tx = UART_PIN_NO_CHANGE; // GPIO 1 break; case UART_NUM_1: + #if CONFIG_IDF_TARGET_ESP32 && CONFIG_SPIRAM + // ESP32 usually uses pins 9 and 10 for SPIRAM bus, so avoid those pins as defaults. + self->rx = 4; + self->tx = 5; + #else self->rx = 9; self->tx = 10; + #endif break; #if SOC_UART_HP_NUM > 2 case UART_NUM_2: From da6a18ffe04ff7a9538ffaa915bca4712beb9d63 Mon Sep 17 00:00:00 2001 From: Elvis Pfutzenreuter Date: Sat, 27 Dec 2025 12:10:33 -0300 Subject: [PATCH 129/177] esp32/esp32_rmt: Update FIXME on RMT module. Signed-off-by: Elvis Pfutzenreuter --- ports/esp32/esp32_rmt.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index 85a98c2910bac..8b77e2ac6fa63 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -193,14 +193,13 @@ static void esp32_rmt_deactivate(esp32_rmt_obj_t *self) { if (self->enabled) { // FIXME: panics in ESP32 if called while TX is ongoing and TX sequence is long (>300ms) // Does not panic in ESP32-S3, ESP32-C3 and ESP32-C6. - // Tested with ESP-IDF up to 5.5 - // ESP-IDF issue: https://github.com/espressif/esp-idf/issues/17692 + // Happens with ESP-IDF up to 5.5.1. Fixed in ESP-IDF 5.5.2. + // ESP-IDF GitHub issue: https://github.com/espressif/esp-idf/issues/17692 // - // Cause is Interrupt WDT to trigger because ESP-IDF rmt_disable() disables - // interrupts and spinlocks until the ongoing TX sequence is finished. - // - // Workaround is never try to stop RMT sequences longer than 300ms (which are unusual - // anyway). Or apply the patch mentioned at the GitHub issue to ESP-IDF. + // Workarounds: + // - recompile with ESP-IDF 5.5.2 or better + // - never try to stop RMT sequences longer than 300ms + // - apply to ESP-IDF the patch mentioned at the GitHub issue rmt_disable(self->channel); self->enabled = false; } From 07540a8fd19a56106bc76dd8ab73c6efc7aaba07 Mon Sep 17 00:00:00 2001 From: FH Date: Thu, 1 Jan 2026 21:12:46 +0100 Subject: [PATCH 130/177] esp32/modesp32: Update available RTC pins for ESP32C6. According to https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/#demo1-deep-sleep-with-external-wake-up and https://docs.espressif.com/projects/esp-idf/en/stable/esp32c6/api-reference/peripherals/gpio.html ESP32C6 supports RTC/LP on Pins 0-7. Signed-off-by: FH --- ports/esp32/modesp32.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ports/esp32/modesp32.h b/ports/esp32/modesp32.h index 60c386565b8aa..6b6a6578a7512 100644 --- a/ports/esp32/modesp32.h +++ b/ports/esp32/modesp32.h @@ -32,6 +32,21 @@ ) #define RTC_LAST_EXT_PIN 21 +#elif CONFIG_IDF_TARGET_ESP32C6 + + #define RTC_VALID_EXT_PINS \ + ( \ + (1ll << 0) | \ + (1ll << 1) | \ + (1ll << 2) | \ + (1ll << 3) | \ + (1ll << 4) | \ + (1ll << 5) | \ + (1ll << 6) | \ + (1ll << 7) \ + ) + #define RTC_LAST_EXT_PIN 7 + #else #define RTC_VALID_EXT_PINS \ From e48b98537fdf0000c6ea3b03d0a6ce831431795c Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 17 Dec 2025 14:39:06 +1100 Subject: [PATCH 131/177] stm32/main: Enable all AHB5 GRP1 clocks in low power mode. The main functional change here is to make sure that the SDMMC1/2 clocks are enabled in low power mode; they were not previously enabled for SD card use, only WLAN. It doesn't hurt to unconditionally enable the clocks in low power, like all the other peripherals. Signed-off-by: Damien George --- ports/stm32/eth.c | 5 ----- ports/stm32/main.c | 2 +- ports/stm32/sdio.c | 4 ---- ports/stm32/usbd_conf.c | 2 -- 4 files changed, 1 insertion(+), 12 deletions(-) diff --git a/ports/stm32/eth.c b/ports/stm32/eth.c index 60f2a23deca07..7baaa89c6274f 100644 --- a/ports/stm32/eth.c +++ b/ports/stm32/eth.c @@ -370,11 +370,6 @@ static int eth_mac_init(eth_t *self) { __HAL_RCC_ETH1RX_CLK_SLEEP_ENABLE(); #elif defined(STM32N6) __HAL_RCC_ETH1_RELEASE_RESET(); - - __HAL_RCC_ETH1_CLK_SLEEP_ENABLE(); - __HAL_RCC_ETH1MAC_CLK_SLEEP_ENABLE(); - __HAL_RCC_ETH1TX_CLK_SLEEP_ENABLE(); - __HAL_RCC_ETH1RX_CLK_SLEEP_ENABLE(); #else __HAL_RCC_ETHMAC_RELEASE_RESET(); diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 8085a5e25763e..6ae8061c4135f 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -409,13 +409,13 @@ void stm32_main(uint32_t reset_mode) { LL_MEM_EnableClockLowPower(LL_MEM_AXISRAM1 | LL_MEM_AXISRAM2 | LL_MEM_AXISRAM3 | LL_MEM_AXISRAM4 | LL_MEM_AXISRAM5 | LL_MEM_AXISRAM6 | LL_MEM_AHBSRAM1 | LL_MEM_AHBSRAM2 | LL_MEM_BKPSRAM | LL_MEM_FLEXRAM | LL_MEM_CACHEAXIRAM | LL_MEM_VENCRAM | LL_MEM_BOOTROM); - LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_XSPI2 | LL_AHB5_GRP1_PERIPH_XSPIM); LL_APB4_GRP1_EnableClock(LL_APB4_GRP1_PERIPH_RTC | LL_APB4_GRP1_PERIPH_RTCAPB); LL_APB4_GRP1_EnableClockLowPower(LL_APB4_GRP1_PERIPH_RTC | LL_APB4_GRP1_PERIPH_RTCAPB); // Enable some AHB peripherals during sleep. LL_AHB1_GRP1_EnableClockLowPower(LL_AHB1_GRP1_PERIPH_ALL); // GPDMA1, ADC12 LL_AHB4_GRP1_EnableClockLowPower(LL_AHB4_GRP1_PERIPH_ALL); // GPIOA-Q, PWR, CRC + LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_ALL); // DMA2D, ETH, FMC, GFXMMU, GPU2D, HPDMA, XSPI, JPEG, MCE, CACHEAXI, NPU, OTG, PSSI, SDMMC // Enable some APB peripherals during sleep. LL_APB1_GRP1_EnableClockLowPower(LL_APB1_GRP1_PERIPH_ALL); // I2C, I3C, LPTIM, SPI, TIM, UART, WWDG diff --git a/ports/stm32/sdio.c b/ports/stm32/sdio.c index de82ceadc5f33..4d18102e1542f 100644 --- a/ports/stm32/sdio.c +++ b/ports/stm32/sdio.c @@ -131,10 +131,6 @@ void sdio_init(uint32_t irq_pri) { mp_hal_pin_config_alt_static(MICROPY_HW_SDIO_CMD, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_CMD); SDMMC_CLK_ENABLE(); // enable SDIO peripheral - #if defined(STM32N6) - LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_SDMMC1); - LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_SDMMC2); - #endif SDMMC_TypeDef *SDIO = SDMMC; #if defined(STM32F7) diff --git a/ports/stm32/usbd_conf.c b/ports/stm32/usbd_conf.c index 46d7985253d38..d481aec1e490b 100644 --- a/ports/stm32/usbd_conf.c +++ b/ports/stm32/usbd_conf.c @@ -279,8 +279,6 @@ static void mp_usbd_ll_init_hs(void) { LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_OTG1); LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_OTGPHY1); - LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_OTG1); - LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_OTGPHY1); // Select 24MHz clock. MODIFY_REG(USB1_HS_PHYC->USBPHYC_CR, USB_USBPHYC_CR_FSEL, 2 << USB_USBPHYC_CR_FSEL_Pos); From bc7d264e869aa39d9789ea330614981c1f38e49e Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 17 Dec 2025 14:40:24 +1100 Subject: [PATCH 132/177] stm32/sdcard: Use high speed mode for SD transfers on H5/H7/N6. This doubles the speed of SD card transfers. Signed-off-by: Damien George --- ports/stm32/sdcard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index b91fa3a9c284e..3e755bf9f2602 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -105,7 +105,7 @@ #define SDIO_HARDWARE_FLOW_CONTROL_ENABLE SDMMC_HARDWARE_FLOW_CONTROL_ENABLE #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) -#define SDIO_TRANSFER_CLK_DIV SDMMC_NSpeed_CLK_DIV +#define SDIO_TRANSFER_CLK_DIV SDMMC_HSPEED_CLK_DIV #define SDIO_USE_GPDMA 0 #else #define SDIO_TRANSFER_CLK_DIV SDMMC_TRANSFER_CLK_DIV From e9bd1e2e082b2fddc5b67574ecf88025203f86a4 Mon Sep 17 00:00:00 2001 From: Oliver Joos Date: Thu, 19 Aug 2021 00:35:24 +0200 Subject: [PATCH 133/177] stm32: Add support for all STM32F412xx MCUs. And fix typo in the usage string of plli2svalues.py. Signed-off-by: Oliver Joos --- ports/stm32/adc.c | 4 +++- ports/stm32/boards/plli2svalues.py | 5 ++++- ports/stm32/timer.c | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index e9be7021322d5..a5163537e058a 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -134,7 +134,9 @@ defined(STM32F407xx) || defined(STM32F417xx) || \ defined(STM32F401xC) || defined(STM32F401xE) #define VBAT_DIV (2) -#elif defined(STM32F411xE) || defined(STM32F412Zx) || \ +#elif defined(STM32F411xE) || \ + defined(STM32F412Cx) || defined(STM32F412Rx) || \ + defined(STM32F412Vx) || defined(STM32F412Zx) || \ defined(STM32F413xx) || defined(STM32F427xx) || \ defined(STM32F429xx) || defined(STM32F437xx) || \ defined(STM32F439xx) || defined(STM32F446xx) || \ diff --git a/ports/stm32/boards/plli2svalues.py b/ports/stm32/boards/plli2svalues.py index e5ea4e8dfd49b..f872c60ebb5c5 100644 --- a/ports/stm32/boards/plli2svalues.py +++ b/ports/stm32/boards/plli2svalues.py @@ -24,6 +24,9 @@ def __init__(self, range_plli2sn, range_plli2sr): "stm32f401xe", "stm32f407xx", "stm32f411xe", + "stm32f412cx", + "stm32f412rx", + "stm32f412vx", "stm32f412zx", "stm32f413xx", "stm32f427xx", @@ -163,7 +166,7 @@ def main(): if mcu_series in mcu_support_plli2s: if len(argv) not in (1, 2): - print("usage: pllvalues.py [-c] [-m ] ") + print("usage: plli2svalues.py [-c] [-m ] ") sys.exit(1) if argv[0].startswith("hse:"): diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c index aa41092829809..5308061732428 100644 --- a/ports/stm32/timer.c +++ b/ports/stm32/timer.c @@ -889,7 +889,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { #endif #if defined(TIM6) - #if defined(STM32F412Zx) || defined(STM32L1) + #if defined(STM32F412Cx) || defined(STM32F412Rx) || defined(STM32F412Vx) || defined(STM32F412Zx) || defined(STM32L1) TIM_ENTRY(6, TIM6_IRQn), #elif defined(STM32G0) TIM_ENTRY(6, TIM6_DAC_LPTIM1_IRQn), From 4e98486830adf0be003701ec2f2bae4424f9875d Mon Sep 17 00:00:00 2001 From: Oliver Joos Date: Thu, 19 Aug 2021 00:38:19 +0200 Subject: [PATCH 134/177] stm32/boards: Add linker script for STMF412xE with 512k flash. The existing linker script for F412 is renamed to separate F412xG (with 1MB RAM) from F412xE (with 512K). Signed-off-by: Oliver Joos --- .../boards/NUCLEO_F412ZG/mpconfigboard.mk | 2 +- ports/stm32/boards/stm32f412xe.ld | 35 +++++++++++++++++++ .../boards/{stm32f412zx.ld => stm32f412xg.ld} | 2 +- 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 ports/stm32/boards/stm32f412xe.ld rename ports/stm32/boards/{stm32f412zx.ld => stm32f412xg.ld} (96%) diff --git a/ports/stm32/boards/NUCLEO_F412ZG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F412ZG/mpconfigboard.mk index 2fbb4ddbe2dd9..1deebe6f1e96c 100644 --- a/ports/stm32/boards/NUCLEO_F412ZG/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F412ZG/mpconfigboard.mk @@ -1,7 +1,7 @@ MCU_SERIES = f4 CMSIS_MCU = STM32F412Zx AF_FILE = boards/stm32f412_af.csv -LD_FILES = boards/stm32f412zx.ld boards/common_ifs.ld +LD_FILES = boards/stm32f412xg.ld boards/common_ifs.ld TEXT0_ADDR = 0x08000000 TEXT1_ADDR = 0x08020000 diff --git a/ports/stm32/boards/stm32f412xe.ld b/ports/stm32/boards/stm32f412xe.ld new file mode 100644 index 0000000000000..15b8d78f15f6b --- /dev/null +++ b/ports/stm32/boards/stm32f412xe.ld @@ -0,0 +1,35 @@ +/* + GNU linker script for STM32F412xe (512kB flash, 256kB RAM) +*/ + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K /* entire flash */ + FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ + FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 64K /* sectors 1,2,3,4: 16k+16k+16k+16k(of 64k)=64k */ + FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 384K /* sectors 5,6,7 are 128K */ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 240K + FS_CACHE (xrw) : ORIGIN = 0x2003c000, LENGTH = 16K +} + +/* produce a link error if there is not this amount of RAM for these sections */ +_minimum_stack_size = 2K; +_minimum_heap_size = 16K; + +/* Define the stack. The stack is full descending so begins just above last byte + of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ +_estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve; +_sstack = _estack - 16K; /* tunable */ + +/* RAM extents for the garbage collector */ +_ram_start = ORIGIN(RAM); +_ram_end = ORIGIN(RAM) + LENGTH(RAM); +_heap_start = _ebss; /* heap starts just after statically allocated memory */ +_heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(FS_CACHE); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(FS_CACHE) + LENGTH(FS_CACHE); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32f412zx.ld b/ports/stm32/boards/stm32f412xg.ld similarity index 96% rename from ports/stm32/boards/stm32f412zx.ld rename to ports/stm32/boards/stm32f412xg.ld index b67f1c3e25353..5b5ccb1d42317 100644 --- a/ports/stm32/boards/stm32f412zx.ld +++ b/ports/stm32/boards/stm32f412xg.ld @@ -1,5 +1,5 @@ /* - GNU linker script for STM32F412zx (1MB flash, 256kB RAM) + GNU linker script for STM32F412xg (1MB flash, 256kB RAM) */ /* Specify the memory areas */ From ee0b5ab2dc4f39376b062d883739bc38c366a9c5 Mon Sep 17 00:00:00 2001 From: Oliver Joos Date: Thu, 27 Nov 2025 14:33:40 +0100 Subject: [PATCH 135/177] stm32/boards/NUCLEO_H753ZI: Add NUCLEO_H753ZI board support. Configuration: - Clock is 8MHz from STLINK MCO, CPU runs at 400MHz - REPL on USB and on UART connected to ST-Link interface - Storage is configured for internal flash memory - 3x LEDs and 1x user button - Ethernet Product page: https://www.st.com/en/evaluation-tools/nucleo-h753zi.html Signed-off-by: Oliver Joos --- ports/stm32/adc.c | 2 +- ports/stm32/boards/NUCLEO_H753ZI/board.json | 15 ++ ports/stm32/boards/NUCLEO_H753ZI/board_init.c | 1 + .../boards/NUCLEO_H753ZI/mpconfigboard.h | 7 + .../boards/NUCLEO_H753ZI/mpconfigboard.mk | 5 + ports/stm32/boards/NUCLEO_H753ZI/pins.csv | 130 ++++++++++++++++++ .../boards/NUCLEO_H753ZI/stm32h7xx_hal_conf.h | 1 + ports/stm32/mboot/main.c | 2 +- ports/stm32/stm32.mk | 2 +- 9 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 ports/stm32/boards/NUCLEO_H753ZI/board.json create mode 100644 ports/stm32/boards/NUCLEO_H753ZI/board_init.c create mode 100644 ports/stm32/boards/NUCLEO_H753ZI/mpconfigboard.h create mode 100644 ports/stm32/boards/NUCLEO_H753ZI/mpconfigboard.mk create mode 100644 ports/stm32/boards/NUCLEO_H753ZI/pins.csv create mode 100644 ports/stm32/boards/NUCLEO_H753ZI/stm32h7xx_hal_conf.h diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index a5163537e058a..ec55175af7313 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -156,7 +156,7 @@ defined(STM32H743xx) || defined(STM32H747xx) || \ defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || \ defined(STM32H7B3xx) || defined(STM32H7B3xxQ) || \ - defined(STM32H750xx) + defined(STM32H750xx) || defined(STM32H753xx) #define VBAT_DIV (4) #elif defined(STM32L432xx) || \ defined(STM32L451xx) || defined(STM32L452xx) || \ diff --git a/ports/stm32/boards/NUCLEO_H753ZI/board.json b/ports/stm32/boards/NUCLEO_H753ZI/board.json new file mode 100644 index 0000000000000..ec3e75c0de694 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H753ZI/board.json @@ -0,0 +1,15 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [], + "images": [ + "nucleo_h753zi.jpg" + ], + "mcu": "stm32h7", + "product": "Nucleo H753ZI", + "thumbnail": "", + "url": "https://www.st.com/en/evaluation-tools/nucleo-h753zi.html", + "vendor": "ST Microelectronics" +} diff --git a/ports/stm32/boards/NUCLEO_H753ZI/board_init.c b/ports/stm32/boards/NUCLEO_H753ZI/board_init.c new file mode 100644 index 0000000000000..04caaaca9e4fa --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H753ZI/board_init.c @@ -0,0 +1 @@ +#include "boards/NUCLEO_H743ZI/board_init.c" diff --git a/ports/stm32/boards/NUCLEO_H753ZI/mpconfigboard.h b/ports/stm32/boards/NUCLEO_H753ZI/mpconfigboard.h new file mode 100644 index 0000000000000..a92fd6b0355ea --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H753ZI/mpconfigboard.h @@ -0,0 +1,7 @@ +#include "boards/NUCLEO_H743ZI2/mpconfigboard.h" + +#undef MICROPY_HW_BOARD_NAME +#define MICROPY_HW_BOARD_NAME "NUCLEO_H753ZI" + +#undef MICROPY_HW_MCU_NAME +#define MICROPY_HW_MCU_NAME "STM32H753" diff --git a/ports/stm32/boards/NUCLEO_H753ZI/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_H753ZI/mpconfigboard.mk new file mode 100644 index 0000000000000..10c72088217eb --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H753ZI/mpconfigboard.mk @@ -0,0 +1,5 @@ +FROZEN_MANIFEST ?= boards/NUCLEO_H743ZI/manifest.py + +include boards/NUCLEO_H743ZI/mpconfigboard.mk + +CMSIS_MCU = STM32H753xx diff --git a/ports/stm32/boards/NUCLEO_H753ZI/pins.csv b/ports/stm32/boards/NUCLEO_H753ZI/pins.csv new file mode 100644 index 0000000000000..450d6e432ae3f --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H753ZI/pins.csv @@ -0,0 +1,130 @@ +A0,PA3 +A1,PC0 +A2,PC3 +A3,PB1 +A4,PC2 +A5,PF10 +A6,PF4 +A7,PF5 +A8,PF6 +D0,PB7 +D1,PB6 +D2,PG14 +D3,PE13 +D4,PE14 +D5,PE11 +D6,PE9 +D7,PG12 +D8,PF3 +D9,PD15 +D10,PD14 +D11,PB5 +D12,PA6 +D13,PA7 +D14,PB9 +D15,PB8 +D16,PC6 +D17,PB15 +D18,PB13 +D19,PB12 +D20,PA15 +D21,PC7 +D22,PB5 +D23,PB3 +D24,PA4 +D25,PB4 +D26,PG6 +D27,PB2 +D28,PD13 +D29,PD12 +D30,PD11 +D31,PE2 +D32,PA0 +D33,PB0 +D34,PE0 +D35,PB11 +D36,PB10 +D37,PE15 +D38,PE6 +D39,PE12 +D40,PE10 +D41,PE7 +D42,PE8 +D43,PC8 +D44,PC9 +D45,PC10 +D46,PC11 +D47,PC12 +D48,PD2 +D49,PG2 +D50,PG3 +D51,PD7 +D52,PD6 +D53,PD5 +D54,PD4 +D55,PD3 +D56,PE2 +D57,PE4 +D58,PE5 +D59,PE6 +D60,PE3 +D61,PF8 +D62,PF7 +D63,PF9 +D64,PG1 +D65,PG0 +D66,PD1 +D67,PD0 +D68,PF0 +D69,PF1 +D70,PF2 +D71,PE9 +D72,PB2 +DAC1,PA4 +DAC2,PA5 +LED1,PB0 +LED2,PE1 +LED3,PB14 +SW,PC13 +I2C1_SDA,PB9 +I2C1_SCL,PB8 +I2C2_SDA,PF0 +I2C2_SCL,PF1 +I2C4_SCL,PF14 +I2C4_SDA,PF15 +SD_D0,PC8 +SD_D1,PC9 +SD_D2,PC10 +SD_D3,PC11 +SD_CMD,PD2 +SD_CK,PC12 +SD_SW,PG2 +OTG_FS_POWER,PD10 +OTG_FS_OVER_CURRENT,PG7 +USB_VBUS,PA9 +USB_ID,PA10 +USB_DM,PA11 +USB_DP,PA12 +UART2_TX,PD5 +UART2_RX,PD6 +UART2_RTS,PD4 +UART2_CTS,PD3 +UART3_TX,PD8 +UART3_RX,PD9 +UART5_TX,PB6 +UART5_RX,PB12 +UART6_TX,PC6 +UART6_RX,PC7 +UART7_TX,PF7 +UART7_RX,PF6 +UART8_TX,PE1 +UART8_RX,PE0 +ETH_MDC,PC1 +ETH_MDIO,PA2 +ETH_RMII_REF_CLK,PA1 +ETH_RMII_CRS_DV,PA7 +ETH_RMII_RXD0,PC4 +ETH_RMII_RXD1,PC5 +ETH_RMII_TX_EN,PG11 +ETH_RMII_TXD0,PG13 +ETH_RMII_TXD1,PB13 diff --git a/ports/stm32/boards/NUCLEO_H753ZI/stm32h7xx_hal_conf.h b/ports/stm32/boards/NUCLEO_H753ZI/stm32h7xx_hal_conf.h new file mode 100644 index 0000000000000..61f202e6298b1 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H753ZI/stm32h7xx_hal_conf.h @@ -0,0 +1 @@ +#include "boards/NUCLEO_H743ZI/stm32h7xx_hal_conf.h" diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index e40413e4e7a27..7251bc1562e90 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -443,7 +443,7 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed) { #elif defined(STM32H5) #define INTERNAL_FLASH_LAYOUT "@Internal Flash /0x08000000/???*08Kg" #define INTERNAL_FLASH_LAYOUT_HAS_TEMPLATE (1) -#elif defined(STM32H743xx) +#elif defined(STM32H743xx) || defined(STM32H753xx) #define INTERNAL_FLASH_LAYOUT "@Internal Flash /0x08000000/16*128Kg" #elif defined(STM32H750xx) #define INTERNAL_FLASH_LAYOUT "@Internal Flash /0x08000000/01*128Kg" diff --git a/ports/stm32/stm32.mk b/ports/stm32/stm32.mk index b63cb0cc51b31..a1532d2776d32 100644 --- a/ports/stm32/stm32.mk +++ b/ports/stm32/stm32.mk @@ -43,7 +43,7 @@ ifneq ($(BUILDING_MBOOT),1) # Select hardware floating-point support. SUPPORTS_HARDWARE_FP_SINGLE = 0 SUPPORTS_HARDWARE_FP_DOUBLE = 0 -ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx STM32H747xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ STM32N657xx)) +ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx STM32H747xx STM32H750xx STM32H753xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ STM32N657xx)) CFLAGS_CORTEX_M += -mfpu=fpv5-d16 -mfloat-abi=hard -mfp16-format=ieee SUPPORTS_HARDWARE_FP_SINGLE = 1 SUPPORTS_HARDWARE_FP_DOUBLE = 1 From 6b13e6c4987c6fd103847e7b36a3f0fcd0ff42d9 Mon Sep 17 00:00:00 2001 From: Oliver Joos Date: Mon, 15 Dec 2025 00:03:31 +0100 Subject: [PATCH 136/177] stm32/boards/NUCLEO_H7x3: Fix st-flash and add openocd configuration. To connect STM32H7 that is in SLEEPD1 state (e.g. during REPL) debuggers like st-flash or openocd must assert SRST. Signed-off-by: Oliver Joos --- .../boards/NUCLEO_H723ZG/mpconfigboard.mk | 4 ++ .../boards/NUCLEO_H743ZI/mpconfigboard.mk | 4 ++ .../boards/openocd_stm32h7_dual_bank.cfg | 46 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 ports/stm32/boards/openocd_stm32h7_dual_bank.cfg diff --git a/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk index b05da481f65ee..56f30c42bd352 100644 --- a/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk @@ -24,3 +24,7 @@ MICROPY_VFS_LFS2 = 1 MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py + +# Flash tool configuration +STFLASH = st-flash --connect-under-reset +OPENOCD_CONFIG = boards/openocd_stm32h7_dual_bank.cfg diff --git a/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk index 35e62c470ac5e..da991c5879e33 100644 --- a/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk @@ -24,3 +24,7 @@ MICROPY_VFS_LFS2 = 1 MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py + +# Flash tool configuration +STFLASH = st-flash --connect-under-reset +OPENOCD_CONFIG = boards/openocd_stm32h7_dual_bank.cfg diff --git a/ports/stm32/boards/openocd_stm32h7_dual_bank.cfg b/ports/stm32/boards/openocd_stm32h7_dual_bank.cfg new file mode 100644 index 0000000000000..8bb3116990bb1 --- /dev/null +++ b/ports/stm32/boards/openocd_stm32h7_dual_bank.cfg @@ -0,0 +1,46 @@ +# This script configures OpenOCD for use with an ST-Link V3 programmer/debugger +# and an STM32H7 target microcontroller with 2MB RAM (dual bank). +# +# To flash your firmware: +# +# $ openocd -f boards/openocd_stm32h7_dual_bank.cfg \ +# -c "stm_flash build-BOARD/firmware.bin 0x08000000 +# +# For a gdb server on port 3333: +# +# $ openocd -f boards/openocd_stm32h7_dual_bank.cfg + + +source [find interface/stlink-dap.cfg] +transport select dapdirect_swd +source [find target/stm32h7x_dual_bank.cfg] +reset_config srst_only connect_assert_srst +init + +proc stm_flash { BIN0 ADDR0 {BIN1 ""} {ADDR1 ""} } { + reset halt + sleep 100 + wait_halt 2 + flash write_image erase $BIN0 $ADDR0 + sleep 100 + verify_image $BIN0 $ADDR0 + sleep 100 + if {$BIN1 ne ""} { + flash write_image erase $BIN1 $ADDR1 + sleep 100 + verify_image $BIN1 $ADDR1 + sleep 100 + } + reset run + shutdown +} + +proc stm_erase {} { + reset halt + sleep 100 + stm32h7x mass_erase 0 + sleep 100 + stm32h7x mass_erase 1 + sleep 100 + shutdown +} From 230bbbbdf5793008d2cdfa40b33f7473312c1d55 Mon Sep 17 00:00:00 2001 From: Oliver Joos Date: Thu, 18 Dec 2025 13:47:37 +0100 Subject: [PATCH 137/177] stm32/boards/NUCLEO_H7x3: Add UART1, remove UART5, slow down PLL1Q. STM32CubeMX shows a conflict of UART5 with ETH MII. However UART1 can be used with TX and RX on the pin headers. PLL1Q is reduced from 200 to 100 MHz because it may be used for FDCAN or SDMMC. FDCAN needs <= 150 MHz, and 100 MHz is enough for an SDCard connected to pin headers to work reliably. Signed-off-by: Oliver Joos --- ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.h | 8 +++++--- ports/stm32/boards/NUCLEO_H743ZI/pins.csv | 2 ++ ports/stm32/boards/NUCLEO_H743ZI2/pins.csv | 2 ++ ports/stm32/boards/NUCLEO_H753ZI/pins.csv | 2 ++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.h b/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.h index 4c053828fab5e..9078a3ef77ff9 100644 --- a/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.h @@ -17,7 +17,7 @@ void NUCLEO_H743ZI_board_early_init(void); #define MICROPY_HW_CLK_PLLM (4) #define MICROPY_HW_CLK_PLLN (400) #define MICROPY_HW_CLK_PLLP (2) -#define MICROPY_HW_CLK_PLLQ (4) +#define MICROPY_HW_CLK_PLLQ (8) #define MICROPY_HW_CLK_PLLR (2) #define MICROPY_HW_CLK_PLLVCI (RCC_PLL1VCIRANGE_1) #define MICROPY_HW_CLK_PLLVCO (RCC_PLL1VCOWIDE) @@ -37,14 +37,16 @@ void NUCLEO_H743ZI_board_early_init(void); #define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_4 // UART config +#define MICROPY_HW_UART1_TX (pin_B6) +#define MICROPY_HW_UART1_RX (pin_B15) #define MICROPY_HW_UART2_TX (pin_D5) #define MICROPY_HW_UART2_RX (pin_D6) #define MICROPY_HW_UART2_RTS (pin_D4) #define MICROPY_HW_UART2_CTS (pin_D3) #define MICROPY_HW_UART3_TX (pin_D8) #define MICROPY_HW_UART3_RX (pin_D9) -#define MICROPY_HW_UART5_TX (pin_B6) -#define MICROPY_HW_UART5_RX (pin_B12) +// #define MICROPY_HW_UART5_TX (pin_B6) // conflict with UART1_TX +// #define MICROPY_HW_UART5_RX (pin_B12) // conflict with Ethernet MII mode #define MICROPY_HW_UART6_TX (pin_C6) #define MICROPY_HW_UART6_RX (pin_C7) #define MICROPY_HW_UART7_TX (pin_F7) diff --git a/ports/stm32/boards/NUCLEO_H743ZI/pins.csv b/ports/stm32/boards/NUCLEO_H743ZI/pins.csv index d3647ca42a3ea..9c1ad7d71031f 100644 --- a/ports/stm32/boards/NUCLEO_H743ZI/pins.csv +++ b/ports/stm32/boards/NUCLEO_H743ZI/pins.csv @@ -105,6 +105,8 @@ USB_VBUS,PA9 USB_ID,PA10 USB_DM,PA11 USB_DP,PA12 +UART1_TX,PB6 +UART1_RX,PB15 UART2_TX,PD5 UART2_RX,PD6 UART2_RTS,PD4 diff --git a/ports/stm32/boards/NUCLEO_H743ZI2/pins.csv b/ports/stm32/boards/NUCLEO_H743ZI2/pins.csv index 450d6e432ae3f..7d964c1ab682d 100644 --- a/ports/stm32/boards/NUCLEO_H743ZI2/pins.csv +++ b/ports/stm32/boards/NUCLEO_H743ZI2/pins.csv @@ -105,6 +105,8 @@ USB_VBUS,PA9 USB_ID,PA10 USB_DM,PA11 USB_DP,PA12 +UART1_TX,PB6 +UART1_RX,PB15 UART2_TX,PD5 UART2_RX,PD6 UART2_RTS,PD4 diff --git a/ports/stm32/boards/NUCLEO_H753ZI/pins.csv b/ports/stm32/boards/NUCLEO_H753ZI/pins.csv index 450d6e432ae3f..7d964c1ab682d 100644 --- a/ports/stm32/boards/NUCLEO_H753ZI/pins.csv +++ b/ports/stm32/boards/NUCLEO_H753ZI/pins.csv @@ -105,6 +105,8 @@ USB_VBUS,PA9 USB_ID,PA10 USB_DM,PA11 USB_DP,PA12 +UART1_TX,PB6 +UART1_RX,PB15 UART2_TX,PD5 UART2_RX,PD6 UART2_RTS,PD4 From e66a1a3f4aa6b0e976f6fe9ac17679407cd27080 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 12 Jan 2026 13:44:37 +0100 Subject: [PATCH 138/177] tests/basics/try_finally: Fix try/finally flow tests under CPython 3.14. This commit fixes tests that will always fail when using CPython 3.14 to get a known-good output to compare MicroPython's behaviour against. Starting from CPython 3.14, statements inside a `finally` block that will prevent the execution flow to reach the block's last statement will raise a SyntaxWarning. That text would end up in the comparison data and thus make known-good tests fail. Given that those tests explicitly exercise flow control interruptions in finally blocks, there is no real workaround that can be applied to the tests themselves. Therefore those tests will now check MicroPython's behaviour against expected output files recorded from the tests' output with CPython 3.11. Signed-off-by: Alessandro Gatti --- tests/basics/try_finally_break.py.exp | 25 ++++++++++++++++ tests/basics/try_finally_break2.py.exp | 17 +++++++++++ tests/basics/try_finally_continue.py.exp | 9 ++++++ tests/basics/try_finally_return2.py.exp | 19 ++++++++++++ tests/basics/try_finally_return3.py.exp | 23 ++++++++++++++ tests/basics/try_finally_return4.py.exp | 38 ++++++++++++++++++++++++ tests/basics/try_finally_return5.py.exp | 3 ++ 7 files changed, 134 insertions(+) create mode 100644 tests/basics/try_finally_break.py.exp create mode 100644 tests/basics/try_finally_break2.py.exp create mode 100644 tests/basics/try_finally_continue.py.exp create mode 100644 tests/basics/try_finally_return2.py.exp create mode 100644 tests/basics/try_finally_return3.py.exp create mode 100644 tests/basics/try_finally_return4.py.exp create mode 100644 tests/basics/try_finally_return5.py.exp diff --git a/tests/basics/try_finally_break.py.exp b/tests/basics/try_finally_break.py.exp new file mode 100644 index 0000000000000..d0b3698548968 --- /dev/null +++ b/tests/basics/try_finally_break.py.exp @@ -0,0 +1,25 @@ +1 +2 +5 +a 1 +1 +iter 0 +1 +None +1 +2 +None +1 +2 +None +1 +3 +4 +7 +1 +2 +4 +7 +1 +4 +7 diff --git a/tests/basics/try_finally_break2.py.exp b/tests/basics/try_finally_break2.py.exp new file mode 100644 index 0000000000000..bc5e931fdb4fd --- /dev/null +++ b/tests/basics/try_finally_break2.py.exp @@ -0,0 +1,17 @@ +4 0 0 1 +4 0 0 2 +4 0 0 3 +4 0 0 4 +4 1 0 1 +4 1 0 2 +4 1 0 3 +4 1 0 4 +4 2 0 1 +4 2 0 2 +4 2 0 3 +4 2 0 4 +4 3 0 1 +4 3 0 2 +4 3 0 3 +4 3 0 4 +None diff --git a/tests/basics/try_finally_continue.py.exp b/tests/basics/try_finally_continue.py.exp new file mode 100644 index 0000000000000..2901997b1aa18 --- /dev/null +++ b/tests/basics/try_finally_continue.py.exp @@ -0,0 +1,9 @@ +4 0 +continue +4 1 +continue +4 2 +continue +4 3 +continue +None diff --git a/tests/basics/try_finally_return2.py.exp b/tests/basics/try_finally_return2.py.exp new file mode 100644 index 0000000000000..b4c364f81d9b7 --- /dev/null +++ b/tests/basics/try_finally_return2.py.exp @@ -0,0 +1,19 @@ +finally +0 +finally 1 +finally 2 +2 +finally 1 +finally 2 +1 +finally 1 +finally 2 +2 +finally +0 +finally +0 +finally +0 +finally +0 diff --git a/tests/basics/try_finally_return3.py.exp b/tests/basics/try_finally_return3.py.exp new file mode 100644 index 0000000000000..5fef648ea6769 --- /dev/null +++ b/tests/basics/try_finally_return3.py.exp @@ -0,0 +1,23 @@ +1 +42 +1 +2 +42 +1 +2 +3 +42 +2 +1 +42 +2 +1 +42 +1 +3 +2 +42 +1 +3 +2 +42 diff --git a/tests/basics/try_finally_return4.py.exp b/tests/basics/try_finally_return4.py.exp new file mode 100644 index 0000000000000..f92572ff6111a --- /dev/null +++ b/tests/basics/try_finally_return4.py.exp @@ -0,0 +1,38 @@ +1 +2 +3 +4 +5 +None +1 +2 +3 +5 +42 +1 +2 +5 +43 +1 +2 +5 +43 +1 +2 +5 +caught +1 +2 +5 +caught +1 +2 +3 +4 +5 +None +1 +2 +3 +5 +42 diff --git a/tests/basics/try_finally_return5.py.exp b/tests/basics/try_finally_return5.py.exp new file mode 100644 index 0000000000000..dfa403cfd47db --- /dev/null +++ b/tests/basics/try_finally_return5.py.exp @@ -0,0 +1,3 @@ +4 0 +return +43 From eae83df201f8bbf4c97a8bd05ee7e930ad5d2233 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 15 Jan 2026 20:33:58 +0100 Subject: [PATCH 139/177] tests/float/math_fun: Fix domain error tests with CPython 3.14. This commit makes tests exercising certain math functions' limit work when using CPython 3.14 to validate the tests' output. CPython 3.14 introduced more descriptive messages when math domain error conditions are encountered rather than a single generic message. This breaks the tests in question as MicroPython uses a single error message when reporting these conditions (both to closely follow CPython and to save firmware space). The math domain tests now look for an error pattern that is compatible with both CPython 3.14 and previous versions, converting messages in the newer format into the previous one. This makes the tests' behaviour under MicroPython comparable with CPython for the foreseeable future. Signed-off-by: Alessandro Gatti --- tests/float/math_fun.py | 3 +++ tests/float/math_fun_special.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/float/math_fun.py b/tests/float/math_fun.py index 789158c0e2b66..05f8be08fa0f4 100644 --- a/tests/float/math_fun.py +++ b/tests/float/math_fun.py @@ -43,6 +43,9 @@ ans = "{:.5g}".format(function(value)) except ValueError as e: ans = str(e) + if ans.startswith("expected a "): + # CPython 3.14 changed messages to be more detailed; convert them back to simple ones + ans = "math domain error" print("{}({:.5g}) = {}".format(function_name, value, ans)) tuple_functions = [ diff --git a/tests/float/math_fun_special.py b/tests/float/math_fun_special.py index ecacedec552ec..fcf6175af73a0 100644 --- a/tests/float/math_fun_special.py +++ b/tests/float/math_fun_special.py @@ -51,6 +51,9 @@ ans = "{:.4g}".format(function(value)) except ValueError as e: ans = str(e) + if ans.startswith("expected a "): + # CPython 3.14 changed messages to be more detailed; convert them back to simple ones + ans = "math domain error" # a tiny error in REPR_C value for 1.5204998778 causes a wrong rounded value if is_REPR_C and function_name == "erfc" and ans == "1.521": ans = "1.52" From b14d129a163d980ab4ae3e3e509bcf6b1232b663 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 15 Jan 2026 20:47:38 +0100 Subject: [PATCH 140/177] tests/float/complex1.py: Fix CPython 3.14 deprecation. This commit fixes a test exercising complex numbers creation, working around a deprecation introduced in CPython 3.14. Creating complex numbers using a complex number as the real part in the constructor is deprecated in CPython 3.14, but that construction method is still supported by MicroPython and covered by tests. To work around this, the specific constructor is extracted into its own test, providing an expected output file recorded using CPython 3.11. Signed-off-by: Alessandro Gatti --- tests/float/complex1.py | 1 - tests/float/complex1_micropython.py | 6 ++++++ tests/float/complex1_micropython.py.exp | 1 + tests/run-tests.py | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 tests/float/complex1_micropython.py create mode 100644 tests/float/complex1_micropython.py.exp diff --git a/tests/float/complex1.py b/tests/float/complex1.py index 0a1d98b9af3eb..4decea2ac6bf1 100644 --- a/tests/float/complex1.py +++ b/tests/float/complex1.py @@ -20,7 +20,6 @@ print(complex("nanj")) print(complex("nan-infj")) print(complex(1, 2)) -print(complex(1j, 2j)) # unary ops print(bool(1j)) diff --git a/tests/float/complex1_micropython.py b/tests/float/complex1_micropython.py new file mode 100644 index 0000000000000..6b92f593ef2b2 --- /dev/null +++ b/tests/float/complex1_micropython.py @@ -0,0 +1,6 @@ +# test basic complex number functionality + +# CPython 3.14 marks this constructor as deprecated, but it is still currently +# supported by MicroPython. + +print(complex(1j, 2j)) diff --git a/tests/float/complex1_micropython.py.exp b/tests/float/complex1_micropython.py.exp new file mode 100644 index 0000000000000..1defdb822cdc5 --- /dev/null +++ b/tests/float/complex1_micropython.py.exp @@ -0,0 +1 @@ +(-2+1j) diff --git a/tests/run-tests.py b/tests/run-tests.py index 7f666e09b564a..ecf9736fa3beb 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -1019,6 +1019,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if not has_complex: skip_tests.add("float/complex1.py") skip_tests.add("float/complex1_intbig.py") + skip_tests.add("float/complex1_micropython.py") skip_tests.add("float/complex_reverse_op.py") skip_tests.add("float/complex_special_methods.py") skip_tests.add("float/int_big_float.py") From f1363d85406db17c63d54df3d2612bf854148105 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Jan 2026 11:36:55 +1100 Subject: [PATCH 141/177] github: Install Python 3.11 for Windows CI runs. As of January 12 the default Python version changed from 3.9 to 3.12, and 3.12 has issues running the settrace tests. 3.13 seems to also have some issues, so setting 3.11 for now. See https://github.com/actions/runner-images/issues/13468 Signed-off-by: Angus Gratton --- .github/workflows/ports_windows.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ports_windows.yml b/.github/workflows/ports_windows.yml index 793eba36a7b6b..5318a85198153 100644 --- a/.github/workflows/ports_windows.yml +++ b/.github/workflows/ports_windows.yml @@ -45,6 +45,12 @@ jobs: env: CI_BUILD_CONFIGURATION: ${{ matrix.configuration }} steps: + - name: Install Python 3.11 + # As of 20260112 the default Python version in Windows image is 3.12, which breaks settrace tests + # Use 3.11 for now + uses: actions/setup-python@v6 + with: + python-version: '3.11' - name: Install Visual Studio ${{ matrix.visualstudio }} if: matrix.custom_vs_install shell: bash From b792efc341be16da31e1b5d855e61307e1047310 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 15 Nov 2025 08:31:44 -0600 Subject: [PATCH 142/177] tests/run-tests.py: Re-add stress_schedule.py for GitHub Actions. This test was ignored since 2020, which hid a new bug in the test for the native emitter added in 2024. Hopefully other changes to the test will make it more reliable. Signed-off-by: Jeff Epler --- tests/run-tests.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index ecf9736fa3beb..abf5752fb85ed 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -985,8 +985,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # Some tests shouldn't be run on GitHub Actions if os.getenv("GITHUB_ACTIONS") == "true": - skip_tests.add("thread/stress_schedule.py") # has reliability issues - if os.getenv("RUNNER_OS") == "Windows" and os.getenv("CI_BUILD_CONFIGURATION") == "Debug": # fails with stack overflow on Debug builds skip_tests.add("misc/sys_settrace_features.py") From cfaba3211cf37efd2f6deb08deeedfd6390193ef Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 15 Nov 2025 08:30:56 -0600 Subject: [PATCH 143/177] tests/thread/stress_schedule.py: Decrease backoff time. Locally, this changes the duration of the test from about 2.7s to 0.3s. Signed-off-by: Jeff Epler --- tests/thread/stress_schedule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/thread/stress_schedule.py b/tests/thread/stress_schedule.py index 362d71aa12e38..10ba67d0bb33b 100644 --- a/tests/thread/stress_schedule.py +++ b/tests/thread/stress_schedule.py @@ -35,7 +35,7 @@ def thread(): micropython.schedule(task, None) except RuntimeError: # Queue full, back off. - time.sleep_ms(10) + time.sleep_ms(1) for i in range(8): From 16ccf55fd8eead8a5fdae5c4b9da9ad9af6c92f4 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 15 Nov 2025 17:25:11 -0600 Subject: [PATCH 144/177] unix/modtime: Handle pending events during sleep(0). This ensures that zero-duration sleeps run scheduled callbacks. Signed-off-by: Jeff Epler --- ports/unix/modtime.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/unix/modtime.c b/ports/unix/modtime.c index 4f0550dbea765..1a9bfcd18e985 100644 --- a/ports/unix/modtime.c +++ b/ports/unix/modtime.c @@ -95,6 +95,7 @@ static mp_obj_t mp_time_sleep(mp_obj_t arg) { tv.tv_sec = (suseconds_t)ipart; int res; while (1) { + mp_handle_pending(true); MP_THREAD_GIL_EXIT(); res = sleep_select(0, NULL, NULL, NULL, &tv); MP_THREAD_GIL_ENTER(); @@ -104,7 +105,6 @@ static mp_obj_t mp_time_sleep(mp_obj_t arg) { if (res != -1 || errno != EINTR) { break; } - mp_handle_pending(true); // printf("select: EINTR: %ld:%ld\n", tv.tv_sec, tv.tv_usec); #else break; @@ -114,13 +114,13 @@ static mp_obj_t mp_time_sleep(mp_obj_t arg) { #else int seconds = mp_obj_get_int(arg); for (;;) { + mp_handle_pending(true); MP_THREAD_GIL_EXIT(); seconds = sleep(seconds); MP_THREAD_GIL_ENTER(); if (seconds == 0) { break; } - mp_handle_pending(true); } #endif return mp_const_none; From 8e3ee303ce8bb05b784a3e100f2fac77dec10a49 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 18 Nov 2025 21:50:02 -0600 Subject: [PATCH 145/177] unix/unix_mphal: Ensure mp_hal_delay_ms handles pending tasks. This ensures that zero-duration delays run scheduled callbacks. Signed-off-by: Jeff Epler --- ports/unix/unix_mphal.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index 5f5abc2e056f6..39311c98729f5 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -242,9 +242,13 @@ uint64_t mp_hal_time_ns(void) { #ifndef mp_hal_delay_ms void mp_hal_delay_ms(mp_uint_t ms) { - mp_uint_t start = mp_hal_ticks_ms(); - while (mp_hal_ticks_ms() - start < ms) { - mp_event_wait_ms(1); + if (ms) { + mp_uint_t start = mp_hal_ticks_ms(); + while (mp_hal_ticks_ms() - start < ms) { + mp_event_wait_ms(1); + } + } else { + mp_handle_pending(true); } } #endif From baf75e21d30d38c4ebad16ff1f7d83807431b57c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 27 Nov 2025 19:40:51 -0600 Subject: [PATCH 146/177] docs/library: Document that sleep(0)/sleep_ms(0) run the scheduler. Signed-off-by: Jeff Epler --- docs/library/micropython.rst | 4 ++++ docs/library/time.rst | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/docs/library/micropython.rst b/docs/library/micropython.rst index 4d5a064a7a70e..b9e910ca26175 100644 --- a/docs/library/micropython.rst +++ b/docs/library/micropython.rst @@ -130,6 +130,10 @@ Functions a critical region but they will not be executed until that region is exited. An example of a critical region is a preempting interrupt handler (an IRQ). + - Inside native code functions, scheduled functions are not called unless + the native code calls a function that specifically does so. + - ``time.sleep`` and ``time.sleep_ms``, including zero-duration sleeps, + will call scheduled functions. A use for this function is to schedule a callback from a preempting IRQ. Such an IRQ puts restrictions on the code that runs in the IRQ (for example diff --git a/docs/library/time.rst b/docs/library/time.rst index b53bb133ec408..8f413fca92b53 100644 --- a/docs/library/time.rst +++ b/docs/library/time.rst @@ -71,6 +71,9 @@ Functions other boards may not accept a floating-point argument, for compatibility with them use `sleep_ms()` and `sleep_us()` functions. + Calling ``sleep``, including ``sleep(0)`` is guaranteed to call pending callback + functions. + .. function:: sleep_ms(ms) Delay for given number of milliseconds, should be positive or 0. @@ -80,6 +83,9 @@ Functions interrupt handlers or other threads. Passing in 0 for *ms* will still allow this other processing to occur. Use `sleep_us()` for more precise delays. + Calling ``sleep_ms``, including ``sleep_ms(0)`` is guaranteed to call + pending callback functions. + .. function:: sleep_us(us) Delay for given number of microseconds, should be positive or 0. From 8248d1180664a999068d744b8e66d8c4d0fca969 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 27 Nov 2025 20:17:28 -0600 Subject: [PATCH 147/177] qemu/mphalport: Ensure mp_hal_delay_ms(0) handles pending tasks. This ensures that zero-duration delays run scheduled callbacks. Signed-off-by: Jeff Epler --- ports/qemu/mphalport.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ports/qemu/mphalport.c b/ports/qemu/mphalport.c index bd3d653eed55b..a4fa32e297f4e 100644 --- a/ports/qemu/mphalport.c +++ b/ports/qemu/mphalport.c @@ -25,6 +25,7 @@ */ #include "py/mphal.h" +#include "py/runtime.h" #include "py/stream.h" #include "shared/runtime/semihosting_arm.h" #include "uart.h" @@ -82,8 +83,12 @@ mp_uint_t mp_hal_ticks_us(void) { } void mp_hal_delay_ms(mp_uint_t ms) { - mp_uint_t start = mp_hal_ticks_ms(); - while (mp_hal_ticks_ms() - start < ms) { + if (ms) { + mp_uint_t start = mp_hal_ticks_ms(); + while (mp_hal_ticks_ms() - start < ms) { + } + } else { + mp_handle_pending(true); } } From 2782d4598de38d69878b121a0ec33c1820396599 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 27 Nov 2025 20:40:47 -0600 Subject: [PATCH 148/177] extmod/modselect: Handle pending events before entering poll. This allows tests like `asyncio_event_queue.py` to succeed under the native emitter when poll is enabled. Signed-off-by: Jeff Epler --- docs/library/micropython.rst | 3 ++- docs/library/select.rst | 6 ++++++ extmod/modselect.c | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/library/micropython.rst b/docs/library/micropython.rst index b9e910ca26175..2da9a6e3fd76f 100644 --- a/docs/library/micropython.rst +++ b/docs/library/micropython.rst @@ -132,7 +132,8 @@ Functions handler (an IRQ). - Inside native code functions, scheduled functions are not called unless the native code calls a function that specifically does so. - - ``time.sleep`` and ``time.sleep_ms``, including zero-duration sleeps, + - Certain functions including ``poll.poll``, ``poll.ipoll``, + ``time.sleep`` and ``time.sleep_ms`` (including zero-duration sleeps) will call scheduled functions. A use for this function is to schedule a callback from a preempting IRQ. diff --git a/docs/library/select.rst b/docs/library/select.rst index 57adbb49afd20..6df1ff9c23060 100644 --- a/docs/library/select.rst +++ b/docs/library/select.rst @@ -76,6 +76,9 @@ Methods In case of timeout, an empty list is returned. + Calling ``poll.poll`` is guaranteed to call pending callback functions + before entering the polling loop. + .. admonition:: Difference to CPython :class: attention @@ -93,6 +96,9 @@ Methods won't be processed until new mask is set with `poll.modify()`. This behaviour is useful for asynchronous I/O schedulers. + Calling ``poll.ipoll`` is guaranteed to call pending callback functions + before entering the polling loop. + .. admonition:: Difference to CPython :class: attention diff --git a/extmod/modselect.c b/extmod/modselect.c index d06157e585ae1..229f8f737b517 100644 --- a/extmod/modselect.c +++ b/extmod/modselect.c @@ -342,6 +342,8 @@ static mp_uint_t poll_set_poll_until_ready_or_timeout(poll_set_t *poll_set, size mp_uint_t start_ticks = mp_hal_ticks_ms(); bool has_timeout = timeout != (mp_uint_t)-1; + mp_handle_pending(true); + #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS for (;;) { From f3874842ab9d568fc1eca813ff8e368a69aa3a84 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 27 Nov 2025 19:40:06 -0600 Subject: [PATCH 149/177] tests/micropython: Add new schedule_sleep.py test. This variant of `schedule.py` explicitly calls a zero sleep. The existing variant is kept to ensure the scheduler is called between bytecodes. Signed-off-by: Jeff Epler --- tests/micropython/schedule.py | 2 + tests/micropython/schedule_sleep.py | 72 +++++++++++++++++++++++++ tests/micropython/schedule_sleep.py.exp | 4 ++ tests/run-tests.py | 2 +- 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/micropython/schedule_sleep.py create mode 100644 tests/micropython/schedule_sleep.py.exp diff --git a/tests/micropython/schedule.py b/tests/micropython/schedule.py index f3dd32661267b..e629edb3eb3e9 100644 --- a/tests/micropython/schedule.py +++ b/tests/micropython/schedule.py @@ -1,4 +1,6 @@ # test micropython.schedule() function +# this test should be manually kept in synch with +# tests/micrpython/schedule_sleep.py. try: import micropython diff --git a/tests/micropython/schedule_sleep.py b/tests/micropython/schedule_sleep.py new file mode 100644 index 0000000000000..9aadde7b0848c --- /dev/null +++ b/tests/micropython/schedule_sleep.py @@ -0,0 +1,72 @@ +# test micropython.schedule() function +# this is the same as tests/micropython/schedule.py but the busy loops are +# replaced with sleep/sleep_ms which allows the test to succeed when run under +# the native emitter. + +try: + import micropython + import time + + micropython.schedule +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +# Basic test of scheduling a function. + + +def callback(arg): + global done + print(arg) + done = True + + +done = False +micropython.schedule(callback, 1) +while not done: + time.sleep(0) + +# Test that callbacks can be scheduled from within a callback, but +# that they don't execute until the outer callback is finished. + + +def callback_inner(arg): + global done + print("inner") + done += 1 + + +def callback_outer(arg): + global done + micropython.schedule(callback_inner, 0) + # need a loop so that the VM can check for pending events + for i in range(2): + pass + print("outer") + done += 1 + + +done = 0 +micropython.schedule(callback_outer, 0) +while done != 2: + time.sleep(0) + +# Test that scheduling too many callbacks leads to an exception. To do this we +# must schedule from within a callback to guarantee that the scheduler is locked. + + +def callback(arg): + global done + try: + for i in range(100): + micropython.schedule(lambda x: x, None) + except RuntimeError: + print("RuntimeError") + done = True + + +done = False +micropython.schedule(callback, None) +while not done: + time.sleep_ms(0) diff --git a/tests/micropython/schedule_sleep.py.exp b/tests/micropython/schedule_sleep.py.exp new file mode 100644 index 0000000000000..c4a3e1227e275 --- /dev/null +++ b/tests/micropython/schedule_sleep.py.exp @@ -0,0 +1,4 @@ +1 +outer +inner +RuntimeError diff --git a/tests/run-tests.py b/tests/run-tests.py index abf5752fb85ed..5aaa680ecd407 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -164,7 +164,7 @@ def open(self, path, mode): "basics/exception_chain.py", # These require stack-allocated slice optimisation. "micropython/heapalloc_slice.py", - # These require running the scheduler. + # These require implicitly running the scheduler between bytecodes. "micropython/schedule.py", "extmod/asyncio_event_queue.py", "extmod/asyncio_iterator_event.py", From 78ec31b1751a9abfaee31590ce5814b45e56cbfb Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 1 Dec 2025 08:46:07 -0600 Subject: [PATCH 150/177] tests/run-tests.py: Re-enable some asyncio tests for the native emitter. These tests no longer fail under the native emitter. Signed-off-by: Jeff Epler --- tests/run-tests.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index 5aaa680ecd407..6dfba2c540a49 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -166,8 +166,6 @@ def open(self, path, mode): "micropython/heapalloc_slice.py", # These require implicitly running the scheduler between bytecodes. "micropython/schedule.py", - "extmod/asyncio_event_queue.py", - "extmod/asyncio_iterator_event.py", # These require sys.exc_info(). "misc/sys_exc_info.py", # These require sys.settrace(). From f918e336452b8506ca30887fc37e92733718e3bb Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 1 Dec 2025 08:46:19 -0600 Subject: [PATCH 151/177] tests/thread/stress_schedule.py: Remove decorator/inaccurate comment. This test can now run correctly with the native emitter because the `thread()` function will run the scheduler during the call to `time.sleep_ms(1)`. Signed-off-by: Jeff Epler --- tests/thread/stress_schedule.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/thread/stress_schedule.py b/tests/thread/stress_schedule.py index 10ba67d0bb33b..fca9b38df89fa 100644 --- a/tests/thread/stress_schedule.py +++ b/tests/thread/stress_schedule.py @@ -27,8 +27,6 @@ def task(x): n += 1 -# This function must always use the bytecode emitter so it bounces the GIL when running. -@micropython.bytecode def thread(): while thread_run: try: From 894d7a54058610ebcfbd32339ea4bd116c4ec8ba Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 20 Aug 2025 17:23:31 +1000 Subject: [PATCH 152/177] extmod/modlwip: Fix latent bug with partially created socket object. Sockets have a finaliser, so they must be created carefully. Signed-off-by: Damien George --- extmod/modlwip.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 4b1c1b8f3a5ec..2a43256f3c80b 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -1091,15 +1091,10 @@ static mp_obj_t lwip_socket_accept(mp_obj_t self_in) { mp_raise_OSError(MP_EOPNOTSUPP); } - // Create new socket object, do it here because we must not raise an out-of-memory - // exception when the LWIP concurrency lock is held - lwip_socket_obj_t *socket2 = mp_obj_malloc_with_finaliser(lwip_socket_obj_t, &lwip_socket_type); - MICROPY_PY_LWIP_ENTER if (socket->pcb.tcp == NULL) { MICROPY_PY_LWIP_EXIT - m_del_obj(lwip_socket_obj_t, socket2); mp_raise_OSError(MP_EBADF); } @@ -1107,7 +1102,6 @@ static mp_obj_t lwip_socket_accept(mp_obj_t self_in) { struct tcp_pcb *listener = socket->pcb.tcp; if (listener->state != LISTEN) { MICROPY_PY_LWIP_EXIT - m_del_obj(lwip_socket_obj_t, socket2); mp_raise_OSError(MP_EINVAL); } @@ -1124,7 +1118,6 @@ static mp_obj_t lwip_socket_accept(mp_obj_t self_in) { } if (socket_is_timedout(socket, ticks_start)) { MICROPY_PY_LWIP_EXIT - m_del_obj(lwip_socket_obj_t, socket2); if (socket->timeout == 0) { mp_raise_OSError(MP_EAGAIN); } else { @@ -1135,13 +1128,20 @@ static mp_obj_t lwip_socket_accept(mp_obj_t self_in) { } // We get a new pcb handle... - socket2->pcb.tcp = *incoming_connection; + struct tcp_pcb *pcb_new = *incoming_connection; if (++socket->incoming.connection.iget >= socket->incoming.connection.alloc) { socket->incoming.connection.iget = 0; } *incoming_connection = NULL; + MICROPY_PY_LWIP_EXIT + // ...and set up the new socket for it. + // + // Creating the new socket object must be done in one step due to the finaliser, and + // outside the lwIP concurrency lock in case it raises an out-of-memory exception. + lwip_socket_obj_t *socket2 = mp_obj_malloc_with_finaliser(lwip_socket_obj_t, &lwip_socket_type); + socket2->pcb.tcp = pcb_new; socket2->domain = MOD_NETWORK_AF_INET; socket2->type = MOD_NETWORK_SOCK_STREAM; socket2->incoming.tcp.pbuf = NULL; @@ -1149,10 +1149,12 @@ static mp_obj_t lwip_socket_accept(mp_obj_t self_in) { socket2->state = STATE_CONNECTED; socket2->recv_offset = 0; socket2->callback = MP_OBJ_NULL; + + MICROPY_PY_LWIP_REENTER + tcp_arg(socket2->pcb.tcp, (void *)socket2); tcp_err(socket2->pcb.tcp, _lwip_tcp_error); tcp_recv(socket2->pcb.tcp, _lwip_tcp_recv); - tcp_accepted(listener); MICROPY_PY_LWIP_EXIT From 33474a6fdb3ce9853dd105e69c9c5a1484909a99 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 20 Aug 2025 18:24:57 +1000 Subject: [PATCH 153/177] extmod/modlwip: Adjust logic for determining a listening socket. This should be equivalent logic, and is a bit simpler and clearer now. Signed-off-by: Damien George --- extmod/modlwip.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 2a43256f3c80b..2d9cda8f170dc 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -371,11 +371,7 @@ static struct tcp_pcb *volatile *lwip_socket_incoming_array(lwip_socket_obj_t *s } static void lwip_socket_free_incoming(lwip_socket_obj_t *socket) { - bool socket_is_listener = - socket->type == MOD_NETWORK_SOCK_STREAM - && socket->pcb.tcp->state == LISTEN; - - if (!socket_is_listener) { + if (socket->state != STATE_LISTENING) { if (socket->type == MOD_NETWORK_SOCK_STREAM) { if (socket->incoming.tcp.pbuf != NULL) { pbuf_free(socket->incoming.tcp.pbuf); @@ -1648,7 +1644,7 @@ static mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ tcp_err(socket->pcb.tcp, NULL); tcp_recv(socket->pcb.tcp, NULL); - if (socket->pcb.tcp->state != LISTEN) { + if (socket->state != STATE_LISTENING) { // Schedule a callback to abort the connection if it's not cleanly closed after // the given timeout. The callback must be set before calling tcp_close since // the latter may free the pcb; if it doesn't then the callback will be active. From c696ca91e06dabd756694a160671a3e8930391e9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 11 Dec 2025 11:06:09 +1100 Subject: [PATCH 154/177] extmod/modlwip: Narrow error_lookup_table type to int8_t. Reduces code size by about 60 bytes. Signed-off-by: Damien George --- extmod/modlwip.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 2d9cda8f170dc..e4bb720dbac52 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -213,7 +213,7 @@ static MP_DEFINE_CONST_OBJ_TYPE( // TODO: We just know that change happened somewhere between 1.4.0 and 1.4.1, // investigate in more detail. #if LWIP_VERSION_MACRO < 0x01040100 -static const int error_lookup_table[] = { +static const int8_t error_lookup_table[] = { 0, /* ERR_OK 0 No error, everything OK. */ MP_ENOMEM, /* ERR_MEM -1 Out of memory error. */ MP_ENOBUFS, /* ERR_BUF -2 Buffer error. */ @@ -234,7 +234,7 @@ static const int error_lookup_table[] = { MP_EBADF, /* _ERR_BADF -16 Closed socket (null pcb) */ }; #elif LWIP_VERSION_MACRO < 0x02000000 -static const int error_lookup_table[] = { +static const int8_t error_lookup_table[] = { 0, /* ERR_OK 0 No error, everything OK. */ MP_ENOMEM, /* ERR_MEM -1 Out of memory error. */ MP_ENOBUFS, /* ERR_BUF -2 Buffer error. */ @@ -258,7 +258,7 @@ static const int error_lookup_table[] = { // Matches lwIP 2.0.3 #undef _ERR_BADF #define _ERR_BADF -17 -static const int error_lookup_table[] = { +static const int8_t error_lookup_table[] = { 0, /* ERR_OK 0 No error, everything OK */ MP_ENOMEM, /* ERR_MEM -1 Out of memory error */ MP_ENOBUFS, /* ERR_BUF -2 Buffer error */ From a7cd41847d8d96716eeafca6006ccd402a04e9d3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 20 Aug 2025 00:45:10 +1000 Subject: [PATCH 155/177] extmod/modlwip: Keep TCP data on remote RST. This commit fixes a long standing bug/deficiency in the lwIP socket code, whereby it would abandon all incoming TCP data if the remote sent a TCP RST. This behaviour it tested by the existing `tests/multi_net/tcp_client_rst.py` and `tests/multi_net/asyncio_tcp_client_rst.py` tests, and they both fail on boards like PYBD_SFx and RPI_PICO_W due to the deficiency. With the fix here, both of those tests now pass on lwIP targets, along with all existing socket tests. Signed-off-by: Damien George --- extmod/modlwip.c | 56 +++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index e4bb720dbac52..bcec34bca3b0c 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -348,8 +348,9 @@ typedef struct _lwip_socket_obj_t { #define STATE_LISTENING 1 #define STATE_CONNECTING 2 #define STATE_CONNECTED 3 - #define STATE_PEER_CLOSED 4 - #define STATE_ACTIVE_UDP 5 + #define STATE_ACTIVE_UDP 4 + #define STATE_PEER_CLOSED 5 // Values higher than this must also be closed by peer + #define STATE_PEER_RST_HANDLED 6 // Negative value is lwIP error int8_t state; } lwip_socket_obj_t; @@ -370,10 +371,10 @@ static struct tcp_pcb *volatile *lwip_socket_incoming_array(lwip_socket_obj_t *s } } -static void lwip_socket_free_incoming(lwip_socket_obj_t *socket) { +static void lwip_socket_free_incoming(lwip_socket_obj_t *socket, bool free_queued_stream_data) { if (socket->state != STATE_LISTENING) { if (socket->type == MOD_NETWORK_SOCK_STREAM) { - if (socket->incoming.tcp.pbuf != NULL) { + if (free_queued_stream_data && socket->incoming.tcp.pbuf != NULL) { pbuf_free(socket->incoming.tcp.pbuf); socket->incoming.tcp.pbuf = NULL; } @@ -399,6 +400,8 @@ static void lwip_socket_free_incoming(lwip_socket_obj_t *socket) { tcp_array[i] = NULL; } } + // This socket is now a non-listening stream, so clear the relevant state. + socket->incoming.tcp.pbuf = NULL; } } @@ -487,8 +490,9 @@ static void _lwip_udp_incoming(void *arg, struct udp_pcb *upcb, struct pbuf *p, static void _lwip_tcp_error(void *arg, err_t err) { lwip_socket_obj_t *socket = (lwip_socket_obj_t *)arg; - // Free any incoming buffers or connections that are stored - lwip_socket_free_incoming(socket); + // Free any incoming buffers or connections that are stored, but keep potential + // queued TCP data in case it's read later. Will be freed by MP_STREAM_CLOSE. + lwip_socket_free_incoming(socket, false); // Pass the error code back via the connection variable. socket->state = err; // If we got here, the lwIP stack either has deallocated or will deallocate the pcb. @@ -818,9 +822,6 @@ static mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui // Helper function for recv/recvfrom to handle TCP packets static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, mp_int_t flags, int *_errno) { - // Check for any pending errors - STREAM_ERROR_CHECK(socket); - if (socket->state == STATE_LISTENING) { // original socket in listening state, not the accepted connection. *_errno = MP_ENOTCONN; @@ -828,10 +829,20 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_ } if (socket->incoming.tcp.pbuf == NULL) { + // Check for any pending errors that should propagate out on socket read. + if (socket->state < 0) { + *_errno = error_lookup_table[-socket->state]; + if (*_errno == MP_ECONNRESET) { + socket->state = STATE_PEER_RST_HANDLED; + } else { + socket->state = _ERR_BADF; + } + return MP_STREAM_ERROR; + } // Non-blocking socket or flag if (socket->timeout == 0 || (flags & MSG_DONTWAIT)) { - if (socket->state == STATE_PEER_CLOSED) { + if (socket->state >= STATE_PEER_CLOSED) { return 0; } *_errno = MP_EAGAIN; @@ -847,7 +858,7 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_ poll_sockets(); } - if (socket->state == STATE_PEER_CLOSED) { + if (socket->state >= STATE_PEER_CLOSED) { if (socket->incoming.tcp.pbuf == NULL) { // socket closed and no data left in buffer return 0; @@ -864,8 +875,6 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_ MICROPY_PY_LWIP_ENTER - assert(socket->pcb.tcp != NULL); - struct pbuf *p = socket->incoming.tcp.pbuf; mp_uint_t remaining = p->len - socket->recv_offset; @@ -888,7 +897,9 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_ } else { socket->recv_offset += len; } - tcp_recved(socket->pcb.tcp, len); + if (socket->pcb.tcp != NULL) { + tcp_recved(socket->pcb.tcp, len); + } } MICROPY_PY_LWIP_EXIT @@ -1292,8 +1303,6 @@ static mp_obj_t lwip_socket_recv_common(size_t n_args, const mp_obj_t *args, ip_ vstr_t vstr; mp_uint_t ret = 0; - lwip_socket_check_connected(socket); - vstr_init_len(&vstr, len); switch (socket->type) { @@ -1308,6 +1317,7 @@ static mp_obj_t lwip_socket_recv_common(size_t n_args, const mp_obj_t *args, ip_ #if MICROPY_PY_LWIP_SOCK_RAW case MOD_NETWORK_SOCK_RAW: #endif + lwip_socket_check_connected(socket); ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, flags, ip, port, &_errno); break; } @@ -1580,7 +1590,10 @@ static mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ } } else if (socket->type == MOD_NETWORK_SOCK_STREAM) { // For TCP sockets there is just one slot for incoming data - if (socket->incoming.tcp.pbuf != NULL) { + // The socket is also readable when in RST state + if (socket->incoming.tcp.pbuf != NULL + || socket->state == ERR_RST + || socket->state == STATE_PEER_RST_HANDLED) { ret |= MP_STREAM_POLL_RD; } } else { @@ -1619,6 +1632,8 @@ static mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ } else if (socket->state == ERR_RST) { // Socket was reset by peer, a write will return an error ret |= flags & MP_STREAM_POLL_WR; + ret |= MP_STREAM_POLL_ERR | MP_STREAM_POLL_HUP; + } else if (socket->state == STATE_PEER_RST_HANDLED) { ret |= MP_STREAM_POLL_HUP; } else if (socket->state == _ERR_BADF) { ret |= MP_STREAM_POLL_NVAL; @@ -1629,14 +1644,15 @@ static mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ } } else if (request == MP_STREAM_CLOSE) { + // Free any incoming buffers or connections that are stored + lwip_socket_free_incoming(socket, true); + if (socket->pcb.tcp == NULL) { + socket->state = _ERR_BADF; MICROPY_PY_LWIP_EXIT return 0; } - // Free any incoming buffers or connections that are stored - lwip_socket_free_incoming(socket); - switch (socket->type) { case MOD_NETWORK_SOCK_STREAM: { // Deregister callback (pcb.tcp is set to NULL below so must deregister now) From 25b400f6888a098a15c4a2811c1fd78af18032a1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 11 Dec 2025 11:39:17 +1100 Subject: [PATCH 156/177] tests/multi_net/tcp_client_rst.py: Improve and extend test. These changes test a few more things related to TCP RST: - add a second iteration to drain incoming data after TCP RST - read data after closing socket Signed-off-by: Damien George --- tests/multi_net/tcp_client_rst.py | 82 +++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/tests/multi_net/tcp_client_rst.py b/tests/multi_net/tcp_client_rst.py index 1fe994f36ceb1..6e74edc194d25 100644 --- a/tests/multi_net/tcp_client_rst.py +++ b/tests/multi_net/tcp_client_rst.py @@ -2,6 +2,12 @@ import struct, time, socket, select +try: + import errno +except ImportError: + print("SKIP") + raise SystemExit + PORT = 8000 @@ -18,27 +24,49 @@ def instance0(): s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) s.listen(1) multitest.next() - s2, _ = s.accept() - s2.setblocking(False) - poll = select.poll() - poll.register(s2, select.POLLIN) - time.sleep(0.4) - print(convert_poll_list(poll.poll(1000))) - # TODO: the following recv don't work with lwip, it abandons data upon TCP RST - try: - print(s2.recv(10)) + + for iteration in range(2): + print("server iteration", iteration) + + # Accept a connection from the client. + s2, _ = s.accept() + s2.setblocking(False) + + # Create a poller for the connected socket. + poll = select.poll() + poll.register(s2, select.POLLIN) + + # Wait for the client to send data and issue a TCP RST. + for _ in range(10): + if select.POLLIN | select.POLLERR | select.POLLHUP in convert_poll_list( + poll.poll(1000) + ): + break + time.sleep(0.1) print(convert_poll_list(poll.poll(1000))) - print(s2.recv(10)) + + # On the second test, drain the incoming data. + if iteration == 1: + for _ in range(5): + try: + print(s2.recv(10)) + except OSError as er: + # This error should only happen on the 3rd recv. + print("ECONNRESET:", er.errno == errno.ECONNRESET) + print(convert_poll_list(poll.poll(1000))) + + # Close the connection to the client. + s2.close() print(convert_poll_list(poll.poll(1000))) - print(s2.recv(10)) + + # Try to read more data from the closed socket, to make sure the error code is correct. + try: + print(s2.recv(10)) + except OSError as er: + print("EBADF:", er.errno == errno.EBADF) print(convert_poll_list(poll.poll(1000))) - except OSError as er: - print(er.errno) - print(convert_poll_list(poll.poll(1000))) - # TODO lwip raises here but apparently it shouldn't - print(s2.recv(10)) - print(convert_poll_list(poll.poll(1000))) - s2.close() + + # Close the listening socket. s.close() @@ -47,11 +75,13 @@ def instance1(): if not hasattr(socket, "SO_LINGER"): multitest.skip() multitest.next() - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) - s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) - lgr_onoff = 1 - lgr_linger = 0 - s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", lgr_onoff, lgr_linger)) - s.send(b"GET / HTTP/1.0\r\n\r\n") - time.sleep(0.2) - s.close() # This issues a TCP RST since we've set the linger option + for iteration in range(2): + print("client iteration", iteration) + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) + lgr_onoff = 1 + lgr_linger = 0 + s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", lgr_onoff, lgr_linger)) + s.send(b"GET / HTTP/1.0\r\n\r\n") + time.sleep(0.2) + s.close() # This issues a TCP RST since we've set the linger option From a5bda23aaa833245362cee3a5e8058540440f2c2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 15 Jan 2026 18:29:30 +1100 Subject: [PATCH 157/177] py/emitglue: Check for bytecode with MICROPY_PY_FUNCTION_ATTRS_CODE. When `MICROPY_PY_FUNCTION_ATTRS_CODE` is enabled, constructing a function instance through `fun_bc_make_new()` can call `mp_make_function_from_proto_fun()` with pure bytecode as the proto-fun. Signed-off-by: Damien George --- py/emitglue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/emitglue.c b/py/emitglue.c index a72f94c88bded..6a1816c2c7962 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -183,7 +183,7 @@ mp_obj_t mp_make_function_from_proto_fun(mp_proto_fun_t proto_fun, const mp_modu // def_kw_args must be MP_OBJ_NULL or a dict assert(def_args == NULL || def_args[1] == MP_OBJ_NULL || mp_obj_is_type(def_args[1], &mp_type_dict)); - #if MICROPY_MODULE_FROZEN_MPY + #if MICROPY_MODULE_FROZEN_MPY || MICROPY_PY_FUNCTION_ATTRS_CODE if (mp_proto_fun_is_bytecode(proto_fun)) { const uint8_t *bc = proto_fun; mp_obj_t fun = mp_obj_new_fun_bc(def_args, bc, context, NULL); From 03f50d39dad8624bc3cbf5c7c8b49e9ef9686464 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 15 Jan 2026 23:29:52 +1100 Subject: [PATCH 158/177] py/mpconfig: Enable MICROPY_PY_FUNCTION_ATTRS_CODE when marshal enabled. In case `MICROPY_PY_MARSHAL` is enabled manually. Otherwise the marshal module is not very usable. 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 2a71c73162890..9a2c753462e60 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1281,7 +1281,7 @@ typedef time_t mp_timestamp_t; // 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) +#define MICROPY_PY_FUNCTION_ATTRS_CODE (MICROPY_PY_MARSHAL || MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_FULL_FEATURES) #endif // Whether bound_method can just use == (feature disabled), or requires a call to From 384cc627bc633af3bb092f173d447c33c1b71952 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 15 Jan 2026 11:42:04 +1100 Subject: [PATCH 159/177] py/persistentcode: Support saving functions with children. This adds support to `mp_raw_code_save_fun_to_bytes()` so that it can handle saving functions that have children. It does this by inspecting the MP_BC_MAKE_FUNCTION/etc opcodes to work out how many children there are, and creating a tree of simplified raw code information. Signed-off-by: Damien George --- py/persistentcode.c | 105 +++++++++++++++++++++++++++++++++++--------- py/persistentcode.h | 2 +- 2 files changed, 85 insertions(+), 22 deletions(-) diff --git a/py/persistentcode.c b/py/persistentcode.c index d83386736b21c..2c7a425656623 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -844,8 +844,28 @@ static mp_opcode_t mp_opcode_decode(const uint8_t *ip) { 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; +typedef struct _mp_raw_code_simplified_t { + const uint8_t *fun_data; + struct _mp_raw_code_simplified_t *children; + size_t fun_data_len; + size_t n_children; +} mp_raw_code_simplified_t; + +static void proto_fun_to_raw_code_simplified(const void *proto_fun, bit_vector_t *qstr_table_used, bit_vector_t *obj_table_used, mp_raw_code_simplified_t *rcs) { + const uint8_t *fun_data; + mp_raw_code_t **children; + if (mp_proto_fun_is_bytecode(proto_fun)) { + fun_data = proto_fun; + children = NULL; + } else { + const mp_raw_code_t *rc = proto_fun; + if (rc->kind != MP_CODE_BYTECODE) { + mp_raise_ValueError(MP_ERROR_TEXT("function must be bytecode")); + } + fun_data = rc->fun_data; + children = rc->children; + } + const uint8_t *fun_data_top = fun_data + gc_nbytes(fun_data); // Extract function information. @@ -853,20 +873,12 @@ mp_obj_t mp_raw_code_save_fun_to_bytes(const mp_module_constants_t *consts, cons 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); + 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); + bit_vector_set(qstr_table_used, arg_name); } // Skip pass source code info and cell info. @@ -874,20 +886,74 @@ mp_obj_t mp_raw_code_save_fun_to_bytes(const mp_module_constants_t *consts, cons ip += n_info + n_cell; // Decode bytecode. + size_t n_children = 0; 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); + bit_vector_set(qstr_table_used, op.arg); + } else if (op.opcode == MP_BC_LOAD_CONST_OBJ) { + bit_vector_set(obj_table_used, op.arg); + } else if (op.opcode == MP_BC_MAKE_FUNCTION + || op.opcode == MP_BC_MAKE_FUNCTION_DEFARGS + || op.opcode == MP_BC_MAKE_CLOSURE + || op.opcode == MP_BC_MAKE_CLOSURE_DEFARGS) { + if ((mp_uint_t)op.arg + 1 > n_children) { + n_children = (mp_uint_t)op.arg + 1; + } } ip += op.size; } - mp_uint_t fun_data_len = fun_data_top - fun_data; + rcs->fun_data = fun_data; + rcs->fun_data_len = fun_data_top - fun_data; + rcs->n_children = n_children; + rcs->children = NULL; + + if (n_children) { + rcs->children = m_new(mp_raw_code_simplified_t, n_children); + for (size_t i = 0; i < n_children; ++i) { + proto_fun_to_raw_code_simplified(children[i], qstr_table_used, obj_table_used, &rcs->children[i]); + } + } +} + +static void save_raw_code_simplified(mp_print_t *print, const mp_raw_code_simplified_t *rcs) { + // Save function kind and data length. + mp_print_uint(print, rcs->fun_data_len << 3 | (rcs->n_children != 0) << 2); + + // Save function code. + mp_print_bytes(print, rcs->fun_data, rcs->fun_data_len); + + // Save (and free) children. + if (rcs->n_children) { + mp_print_uint(print, rcs->n_children); + for (size_t i = 0; i < rcs->n_children; ++i) { + save_raw_code_simplified(print, &rcs->children[i]); + } + m_del(mp_raw_code_simplified_t, rcs->children, rcs->n_children); + } +} + +mp_obj_t mp_raw_code_save_fun_to_bytes(const mp_module_constants_t *consts, mp_proto_fun_t proto_fun) { + // 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); + + #if MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_FULL + // Make sure the filename appears in the qstr table. + bit_vector_set(&qstr_table_used, 0); + #endif + + // Convert function into a simplified raw code tree. + mp_raw_code_simplified_t rcs; + proto_fun_to_raw_code_simplified(proto_fun, &qstr_table_used, &obj_table_used, &rcs); mp_print_t print; vstr_t vstr; @@ -922,11 +988,8 @@ mp_obj_t mp_raw_code_save_fun_to_bytes(const mp_module_constants_t *consts, cons 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); + // Save the bytecode data (also free the simplified raw code tree at the same time). + save_raw_code_simplified(&print, &rcs); // Create and return bytes representing the .mpy data. return mp_obj_new_bytes_from_vstr(&vstr); diff --git a/py/persistentcode.h b/py/persistentcode.h index 1e0bbbd272ffd..23cb0936f2f08 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -139,7 +139,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); +mp_obj_t mp_raw_code_save_fun_to_bytes(const mp_module_constants_t *consts, mp_proto_fun_t proto_fun); void mp_native_relocate(void *reloc, uint8_t *text, uintptr_t reloc_text); From 878f8004fd311f10e595ed1f7b7160b4b2b64516 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 15 Jan 2026 11:42:49 +1100 Subject: [PATCH 160/177] py/objfun: Support __code__ on functions with children. If a function has children then the code object returned from __code__ must contain an `mp_raw_code_t` (actually `mp_raw_code_truncated_t` is enough) that points to the child table. Signed-off-by: Damien George --- py/objfun.c | 21 +++++++++++++++++---- tests/basics/fun_code.py | 5 +++++ tests/basics/fun_code_micropython.py | 19 ------------------- tests/basics/fun_code_micropython.py.exp | 1 - 4 files changed, 22 insertions(+), 24 deletions(-) delete mode 100644 tests/basics/fun_code_micropython.py delete mode 100644 tests/basics/fun_code_micropython.py.exp diff --git a/py/objfun.c b/py/objfun.c index 933da1e494d81..996ac8616001b 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -369,11 +369,24 @@ void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { #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 (self->base.type == &mp_type_fun_bc + || self->base.type == &mp_type_gen_wrap) { #if MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_BASIC - dest[0] = mp_obj_new_code(self->context->constants, self->bytecode); + #if MICROPY_PERSISTENT_CODE_SAVE + #error "MICROPY_PERSISTENT_CODE_SAVE can't be enabled at this level of MICROPY_PY_BUILTINS_CODE" + #endif + mp_proto_fun_t proto_fun; + if (self->child_table != NULL) { + mp_raw_code_truncated_t *rc = m_new0(mp_raw_code_truncated_t, 1); + rc->kind = MP_CODE_BYTECODE; + rc->is_generator = self->base.type == &mp_type_gen_wrap; + rc->fun_data = self->bytecode; + rc->children = (mp_raw_code_t **)self->child_table; + proto_fun = rc; + } else { + proto_fun = self->bytecode; + } + dest[0] = mp_obj_new_code(self->context->constants, proto_fun); #else dest[0] = mp_obj_new_code(self->context, self->rc, true); #endif diff --git a/tests/basics/fun_code.py b/tests/basics/fun_code.py index 59e1f7ec0483d..841357e8aadde 100644 --- a/tests/basics/fun_code.py +++ b/tests/basics/fun_code.py @@ -34,3 +34,8 @@ def f(): ftype(f.__code__, None) except TypeError: print("TypeError") + +# Test __code__ on functions with children functions. +code = (lambda: (lambda: a)).__code__ +print(ftype(code, {"a": 1})()()) +print(ftype(code, {"a": 2})()()) diff --git a/tests/basics/fun_code_micropython.py b/tests/basics/fun_code_micropython.py deleted file mode 100644 index 2c319a2db8c75..0000000000000 --- a/tests/basics/fun_code_micropython.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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 deleted file mode 100644 index d169edffb4cfb..0000000000000 --- a/tests/basics/fun_code_micropython.py.exp +++ /dev/null @@ -1 +0,0 @@ -AttributeError From 93692caefa91d7aac9101a0d5724e1418ba9e41a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 15 Jan 2026 11:42:59 +1100 Subject: [PATCH 161/177] extmod/modmarshal: Support marshal.dumps of functions with children. This commit adds support to the `marshal` module to be able to dump functions that have child functions. For example: import marshal def f(): def child(): return 1 return child marshal.dumps(f.__code__) It also covers the case of marshalling functions that use list comprehensions, because a list comprehension uses a child function. This is made possible by the newly enhanced `mp_raw_code_save_fun_to_bytes()` that can now handle nested functions. Unmarshalling via `marshal.loads()` already supports nested functions because it uses the standard `mp_raw_code_load_mem()` function which is used to import mpy files (and hence can handle all possibilities). Signed-off-by: Damien George --- extmod/modmarshal.c | 12 +---- tests/extmod/marshal_fun_nested.py | 79 +++++++++++++++++++++++++++++ tests/extmod/marshal_micropython.py | 21 -------- tests/micropython/native_marshal.py | 45 ++++++++++++++++ 4 files changed, 125 insertions(+), 32 deletions(-) create mode 100644 tests/extmod/marshal_fun_nested.py delete mode 100644 tests/extmod/marshal_micropython.py create mode 100644 tests/micropython/native_marshal.py diff --git a/extmod/modmarshal.c b/extmod/modmarshal.c index 93d2bcf115065..cc7878e864d92 100644 --- a/extmod/modmarshal.c +++ b/extmod/modmarshal.c @@ -36,17 +36,7 @@ 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); + return mp_raw_code_save_fun_to_bytes(mp_code_get_constants(code), proto_fun); } else { mp_raise_ValueError(MP_ERROR_TEXT("unmarshallable object")); } diff --git a/tests/extmod/marshal_fun_nested.py b/tests/extmod/marshal_fun_nested.py new file mode 100644 index 0000000000000..fcf8f9a0fa450 --- /dev/null +++ b/tests/extmod/marshal_fun_nested.py @@ -0,0 +1,79 @@ +# Test the marshal module, with functions that have children. + +try: + import marshal + + (lambda: 0).__code__ +except (AttributeError, ImportError): + print("SKIP") + raise SystemExit + + +def f_with_child(): + def child(): + return a + + return child + + +def f_with_child_defargs(): + def child(a="default"): + return a + + return child + + +def f_with_child_closure(): + a = "closure 1" + + def child(): + return a + + a = "closure 2" + return child + + +def f_with_child_closure_defargs(): + a = "closure defargs 1" + + def child(b="defargs default"): + return (a, b) + + a = "closure defargs 1" + return child + + +def f_with_list_comprehension(a): + return [i + a for i in range(4)] + + +ftype = type(lambda: 0) + +# Test function with a child. +f = ftype(marshal.loads(marshal.dumps(f_with_child.__code__)), {"a": "global"}) +print(f()()) + +# Test function with a child that has default arguments. +f = ftype(marshal.loads(marshal.dumps(f_with_child_defargs.__code__)), {}) +print(f()()) +print(f()("non-default")) + +# Test function with a child that is a closure. +f = ftype(marshal.loads(marshal.dumps(f_with_child_closure.__code__)), {}) +print(f()()) + +# Test function with a child that is a closure and has default arguments. +f = ftype(marshal.loads(marshal.dumps(f_with_child_closure_defargs.__code__)), {}) +print(f()()) +print(f()("defargs non-default")) + +# Test function with a list comprehension (which will be an anonymous child). +f = ftype(marshal.loads(marshal.dumps(f_with_list_comprehension.__code__)), {}) +print(f(10)) + +# Test child within a module (the outer scope). +code = compile("def child(a): return a", "", "exec") +f = marshal.loads(marshal.dumps(code)) +ctx = {} +exec(f, ctx) +print(ctx["child"]("arg")) diff --git a/tests/extmod/marshal_micropython.py b/tests/extmod/marshal_micropython.py deleted file mode 100644 index 213b3bf31895d..0000000000000 --- a/tests/extmod/marshal_micropython.py +++ /dev/null @@ -1,21 +0,0 @@ -# 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/micropython/native_marshal.py b/tests/micropython/native_marshal.py new file mode 100644 index 0000000000000..09a27a374b8cf --- /dev/null +++ b/tests/micropython/native_marshal.py @@ -0,0 +1,45 @@ +# Test the marshal module in combination with native/viper functions. + +try: + import marshal + + (lambda: 0).__code__ +except (AttributeError, ImportError): + print("SKIP") + raise SystemExit + +import unittest + + +def f_native(): + @micropython.native + def g(): + pass + + return g + + +def f_viper(): + @micropython.viper + def g(): + pass + + return g + + +class Test(unittest.TestCase): + def test_native_function(self): + # Can't marshal a function with native code. + code = f_native.__code__ + with self.assertRaises(ValueError): + marshal.dumps(code) + + def test_viper_function(self): + # Can't marshal a function with viper code. + code = f_viper.__code__ + with self.assertRaises(ValueError): + marshal.dumps(code) + + +if __name__ == "__main__": + unittest.main() From 85e9d7596ac077c4c46b57f578865c51159e7777 Mon Sep 17 00:00:00 2001 From: Alon Bar-Lev Date: Sat, 9 Aug 2025 19:29:17 +0300 Subject: [PATCH 162/177] mimxrt/machine_adc: Initialize LPADC2 for rt117x. Pins may be on the LPADC2 peripheral. Signed-off-by: Alon Bar-Lev --- ports/mimxrt/machine_adc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/mimxrt/machine_adc.c b/ports/mimxrt/machine_adc.c index c332bd703128a..da5b20a8c751a 100644 --- a/ports/mimxrt/machine_adc.c +++ b/ports/mimxrt/machine_adc.c @@ -126,6 +126,7 @@ void machine_adc_init(void) { adc_config.enableAnalogPreliminary = true; adc_config.referenceVoltageSource = kLPADC_ReferenceVoltageAlt1; LPADC_Init(LPADC1, &adc_config); + LPADC_Init(LPADC2, &adc_config); } #else From 2b07661ee64ef3ad40d9faac51403e0a23352662 Mon Sep 17 00:00:00 2001 From: Alon Bar-Lev Date: Sat, 9 Aug 2025 19:29:07 +0300 Subject: [PATCH 163/177] mimxrt/machine_adc: Support ADC channel groups on rt117x. The rt117x uses the LPADC peripheral which supports both A-side and B-side channel inputs, e.g. ADC1_CH1A and ADC1_CH1B. Previously, only `kLPADC_SampleChannelSingleEndSideA` was being used during capture, while the pin may be in side B. The fix in this commit detects the side based on the pin configuration and sets `sampleChannelMode` appropriately. Signed-off-by: Alon Bar-Lev --- ports/mimxrt/boards/make-pins.py | 17 ++++++++++++----- ports/mimxrt/boards/mimxrt_prefix.c | 5 +++-- ports/mimxrt/machine_adc.c | 22 ++++++++++++++++++++-- ports/mimxrt/pin.h | 1 + 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/ports/mimxrt/boards/make-pins.py b/ports/mimxrt/boards/make-pins.py index 55c7f5bd02738..6ad60f0545bcd 100644 --- a/ports/mimxrt/boards/make-pins.py +++ b/ports/mimxrt/boards/make-pins.py @@ -61,18 +61,23 @@ def add_af(self, af_idx, af_name, af): elif af_name == "ADC": adc_regex = r"ADC(?P\d*)_IN(?P\d*)" - lpadc_regex = r"ADC(?P\d*)_CH(?P\d*)" # LPADC for MIMXRT11xx chips + lpadc_regex = r"ADC(?P\d*)_CH(?P\d*)(?P.*)" # LPADC for MIMXRT11xx chips matches = re.finditer(adc_regex, af, re.MULTILINE) for match in matches: self._adc_fns.append( - (int(match.group("instance")), int(match.group("channel")), "ADC") + (int(match.group("instance")), int(match.group("channel")), 0, "ADC") ) matches = re.finditer(lpadc_regex, af, re.MULTILINE) for match in matches: self._adc_fns.append( - (int(match.group("instance")), int(match.group("channel")), "LPADC") + ( + int(match.group("instance")), + int(match.group("channel")), + ord(match.group("channel_group")[0]) - ord("A"), + "LPADC", + ) ) # Use the PIN() macro defined in samd_prefix.c for defining the pin @@ -122,9 +127,11 @@ def print_source(self, out_source): "static const machine_pin_adc_obj_t pin_{:s}_adc[] = {{".format(self.name()), file=out_source, ) - for instance, channel, peripheral in self._adc_fns: + for instance, channel, channel_group, peripheral in self._adc_fns: print( - " PIN_ADC({:s}{:d}, {:d}),".format(peripheral, instance, channel), + " PIN_ADC({:s}{:d}, {:d}, {:d}),".format( + peripheral, instance, channel, channel_group + ), file=out_source, ) print("};", file=out_source) diff --git a/ports/mimxrt/boards/mimxrt_prefix.c b/ports/mimxrt/boards/mimxrt_prefix.c index 49d6e67dc4083..e6bba18b9f12c 100644 --- a/ports/mimxrt/boards/mimxrt_prefix.c +++ b/ports/mimxrt/boards/mimxrt_prefix.c @@ -16,10 +16,11 @@ .pad_config = (uint32_t)(_pad_config), \ } \ -#define PIN_ADC(_instance, _channel) \ +#define PIN_ADC(_instance, _channel, _channel_group) \ { \ .instance = (_instance), \ - .channel = (_channel) \ + .channel = (_channel), \ + .channel_group = (_channel_group), \ } \ #define PIN(_name, _gpio, _pin, _af_list, _adc_list_len, _adc_list) \ diff --git a/ports/mimxrt/machine_adc.c b/ports/mimxrt/machine_adc.c index da5b20a8c751a..1ebfe528612be 100644 --- a/ports/mimxrt/machine_adc.c +++ b/ports/mimxrt/machine_adc.c @@ -64,7 +64,19 @@ static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_p // Get ADC adc id for (int i = 1; i < sizeof(adc_bases) / sizeof(ADC_Type *); ++i) { if (adc_bases[i] == self->adc) { - mp_printf(print, "ADC(%u, channel=%u)", i, self->channel); + mp_printf( + print, + "ADC(%u, channel=%u" + #if defined(MIMXRT117x_SERIES) + ", channel_input=%u" + #endif + ")", + i, + self->channel + #if defined(MIMXRT117x_SERIES) + , self->channel_group + #endif + ); break; } } @@ -83,12 +95,13 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args // Extract arguments 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; + uint8_t channel_group = pin->adc_list[0].channel_group; // Create ADC Instance machine_adc_obj_t *o = mp_obj_malloc(machine_adc_obj_t, &machine_adc_type); o->adc = adc_instance; o->channel = (uint8_t)channel; - o->channel_group = 0; + o->channel_group = channel_group; o->resolution = 4096; // NOTE: currently only 12bit resolution supported return MP_OBJ_FROM_PTR(o); @@ -104,6 +117,11 @@ static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { LPADC_GetDefaultConvCommandConfig(&adc_config); adc_config.channelNumber = self->channel; adc_config.sampleScaleMode = kLPADC_SamplePartScale; + if (self->channel_group == 0) { + adc_config.sampleChannelMode = kLPADC_SampleChannelSingleEndSideA; + } else { + adc_config.sampleChannelMode = kLPADC_SampleChannelSingleEndSideB; + } LPADC_SetConvCommandConfig(self->adc, 1, &adc_config); // Set Trigger mode diff --git a/ports/mimxrt/pin.h b/ports/mimxrt/pin.h index 7ee2841465988..94cd3cad4d7c1 100644 --- a/ports/mimxrt/pin.h +++ b/ports/mimxrt/pin.h @@ -117,6 +117,7 @@ typedef struct { typedef struct { ADC_Type *instance; uint8_t channel; + uint8_t channel_group; } machine_pin_adc_obj_t; typedef struct { From 2a5a8cc16edc0ac6a6e0abc88ff5c95a28998d7e Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 14 Aug 2025 13:50:53 +1000 Subject: [PATCH 164/177] mimxrt/eth: Add DP83867 PHY driver support. Adds new PHY driver for TI DP83867 Gigabit Ethernet PHY. Signed-off-by: Andrew Leech --- ports/mimxrt/Makefile | 1 + ports/mimxrt/eth.c | 3 +- ports/mimxrt/eth.h | 1 + .../phy/device/phydp83867/fsl_phydp83867.c | 302 ++++++++++++++++++ .../phy/device/phydp83867/fsl_phydp83867.h | 165 ++++++++++ ports/mimxrt/network_lan.c | 4 + 6 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 ports/mimxrt/hal/phy/device/phydp83867/fsl_phydp83867.c create mode 100644 ports/mimxrt/hal/phy/device/phydp83867/fsl_phydp83867.h diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 52003ed4a4d36..08f99d68b53a0 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -113,6 +113,7 @@ SRC_ETH_C += \ $(MCUX_SDK_DIR)/drivers/enet/fsl_enet.c \ hal/phy/device/phydp83825/fsl_phydp83825.c \ hal/phy/device/phydp83848/fsl_phydp83848.c \ + hal/phy/device/phydp83867/fsl_phydp83867.c \ hal/phy/device/phyksz8081/fsl_phyksz8081.c \ hal/phy/device/phylan8720/fsl_phylan8720.c \ hal/phy/device/phyrtl8211f/fsl_phyrtl8211f.c \ diff --git a/ports/mimxrt/eth.c b/ports/mimxrt/eth.c index 1fbd9d3895f79..bbca2f6646523 100644 --- a/ports/mimxrt/eth.c +++ b/ports/mimxrt/eth.c @@ -44,6 +44,7 @@ #include "hal/phy/device/phyksz8081/fsl_phyksz8081.h" #include "hal/phy/device/phydp83825/fsl_phydp83825.h" #include "hal/phy/device/phydp83848/fsl_phydp83848.h" +#include "hal/phy/device/phydp83867/fsl_phydp83867.h" #include "hal/phy/device/phylan8720/fsl_phylan8720.h" #include "hal/phy/device/phyrtl8211f/fsl_phyrtl8211f.h" @@ -422,7 +423,7 @@ void eth_init_1(eth_t *self, int eth_id, const phy_operations_t *phy_ops, int ph uint32_t source_clock = eth_clock_init(eth_id, phy_clock); const machine_pin_obj_t *reset_pin = NULL; - #if defined(pin_ENET_1_INT) + #if defined(pin_ENET_1_RESET) reset_pin = pin_ENET_1_RESET; #endif const machine_pin_obj_t *int_pin = NULL; diff --git a/ports/mimxrt/eth.h b/ports/mimxrt/eth.h index afb5d5edf375e..0d9ed599bc464 100644 --- a/ports/mimxrt/eth.h +++ b/ports/mimxrt/eth.h @@ -47,6 +47,7 @@ enum { PHY_KSZ8081 = 0, PHY_DP83825, PHY_DP83848, + PHY_DP83867, PHY_LAN8720, PHY_RTL8211F, }; diff --git a/ports/mimxrt/hal/phy/device/phydp83867/fsl_phydp83867.c b/ports/mimxrt/hal/phy/device/phydp83867/fsl_phydp83867.c new file mode 100644 index 0000000000000..87943881d8e72 --- /dev/null +++ b/ports/mimxrt/hal/phy/device/phydp83867/fsl_phydp83867.c @@ -0,0 +1,302 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Andrew Leech + * + * 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 "fsl_phydp83867.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief Defines the PHY DP83867 vendor defined registers. */ +#define PHY_PHYSTS_REG 0x11U /*!< The PHY Status register. */ + +/*! @brief Defines the PHY DP83867 ID number. */ +#define PHY_CONTROL_ID1 0x2000U /*!< The PHY ID1 (upper 16 bits). */ +#define PHY_CONTROL_ID2 0xA231U /*!< The PHY ID2 (lower 16 bits). */ +#define PHY_FULL_ID 0x2000A231U /*!< Full PHY ID. */ + +/*! @brief Defines the mask flag in PHYSTS register. */ +#define PHY_PHYSTS_LINKSTATUS_MASK 0x0400U /*!< The PHY link status mask. */ +#define PHY_PHYSTS_LINKSPEED_MASK 0xC000U /*!< The PHY link speed mask. */ +#define PHY_PHYSTS_LINKDUPLEX_MASK 0x2000U /*!< The PHY link duplex mask. */ +#define PHY_PHYSTS_LINKSPEED_SHIFT 14U /*!< The link speed shift */ + +/*! @brief Link speed values from PHYSTS register. */ +#define PHY_PHYSTS_LINKSPEED_10M 0U /*!< 10M link speed. */ +#define PHY_PHYSTS_LINKSPEED_100M 1U /*!< 100M link speed. */ +#define PHY_PHYSTS_LINKSPEED_1000M 2U /*!< 1000M link speed. */ + +/*! @brief Defines the timeout macro. */ +#define PHY_READID_TIMEOUT_COUNT 1000U + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/******************************************************************************* + * Variables + ******************************************************************************/ + +const phy_operations_t phydp83867_ops = {.phyInit = PHY_DP83867_Init, + .phyWrite = PHY_DP83867_Write, + .phyRead = PHY_DP83867_Read, + .getAutoNegoStatus = PHY_DP83867_GetAutoNegotiationStatus, + .getLinkStatus = PHY_DP83867_GetLinkStatus, + .getLinkSpeedDuplex = PHY_DP83867_GetLinkSpeedDuplex, + .setLinkSpeedDuplex = PHY_DP83867_SetLinkSpeedDuplex, + .enableLoopback = PHY_DP83867_EnableLoopback}; + +/******************************************************************************* + * Code + ******************************************************************************/ + +status_t PHY_DP83867_Init(phy_handle_t *handle, const phy_config_t *config) { + uint32_t counter = PHY_READID_TIMEOUT_COUNT; + status_t result; + uint32_t regValue = 0U; + + + /* Init MDIO interface. */ + MDIO_Init(handle->mdioHandle); + + /* Assign phy address. */ + handle->phyAddr = config->phyAddr; + + + /* Check PHY ID. */ + do + { + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_ID1_REG, ®Value); + if (result != kStatus_Success) { + return result; + } + counter--; + } while ((regValue != PHY_CONTROL_ID1) && (counter != 0U)); + + if (counter == 0U) { + return kStatus_Fail; + } + + + /* Reset PHY. */ + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, PHY_BCTL_RESET_MASK); + if (result != kStatus_Success) { + return result; + } + + + /* Wait for reset to complete */ + counter = PHY_READID_TIMEOUT_COUNT; + do { + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result != kStatus_Success) { + return result; + } + counter--; + } while ((regValue & PHY_BCTL_RESET_MASK) && (counter != 0U)); + + if (counter == 0U) { + return kStatus_Fail; + } + + + if (config->autoNeg) { + /* Set the auto-negotiation. */ + result = + MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_AUTONEG_ADVERTISE_REG, + PHY_100BASETX_FULLDUPLEX_MASK | PHY_100BASETX_HALFDUPLEX_MASK | PHY_10BASETX_FULLDUPLEX_MASK | + PHY_10BASETX_HALFDUPLEX_MASK | PHY_IEEE802_3_SELECTOR_MASK); + if (result == kStatus_Success) { + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_1000BASET_CONTROL_REG, + PHY_1000BASET_FULLDUPLEX_MASK); + if (result == kStatus_Success) { + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result == kStatus_Success) { + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, + (regValue | PHY_BCTL_AUTONEG_MASK | PHY_BCTL_RESTART_AUTONEG_MASK)); + } + } + } + } else { + /* Disable isolate mode */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result != kStatus_Success) { + return result; + } + regValue &= ~PHY_BCTL_ISOLATE_MASK; + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + if (result != kStatus_Success) { + return result; + } + + /* Disable the auto-negotiation and set user-defined speed/duplex configuration. */ + result = PHY_DP83867_SetLinkSpeedDuplex(handle, config->speed, config->duplex); + } + + + return result; +} + +status_t PHY_DP83867_Write(phy_handle_t *handle, uint32_t phyReg, uint32_t data) { + return MDIO_Write(handle->mdioHandle, handle->phyAddr, phyReg, data); +} + +status_t PHY_DP83867_Read(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr) { + return MDIO_Read(handle->mdioHandle, handle->phyAddr, phyReg, dataPtr); +} + +status_t PHY_DP83867_GetAutoNegotiationStatus(phy_handle_t *handle, bool *status) { + assert(status); + + status_t result; + uint32_t regValue; + + *status = false; + + /* Check auto negotiation complete. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICSTATUS_REG, ®Value); + if (result == kStatus_Success) { + if ((regValue & PHY_BSTATUS_AUTONEGCOMP_MASK) != 0U) { + *status = true; + } + } + return result; +} + +status_t PHY_DP83867_GetLinkStatus(phy_handle_t *handle, bool *status) { + assert(status); + + status_t result; + uint32_t regValue; + + /* Read the PHY Status register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_PHYSTS_REG, ®Value); + if (result == kStatus_Success) { + if ((PHY_PHYSTS_LINKSTATUS_MASK & regValue) != 0U) { + /* Link up. */ + *status = true; + } else { + /* Link down. */ + *status = false; + } + } + return result; +} + +status_t PHY_DP83867_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex) { + assert(!((speed == NULL) && (duplex == NULL))); + + status_t result; + uint32_t regValue; + + /* Read the status register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_PHYSTS_REG, ®Value); + if (result == kStatus_Success) { + if (speed != NULL) { + switch ((regValue & PHY_PHYSTS_LINKSPEED_MASK) >> PHY_PHYSTS_LINKSPEED_SHIFT) + { + case PHY_PHYSTS_LINKSPEED_10M: + *speed = kPHY_Speed10M; + break; + case PHY_PHYSTS_LINKSPEED_100M: + *speed = kPHY_Speed100M; + break; + case PHY_PHYSTS_LINKSPEED_1000M: + *speed = kPHY_Speed1000M; + break; + default: + *speed = kPHY_Speed10M; + break; + } + } + + if (duplex != NULL) { + if ((regValue & PHY_PHYSTS_LINKDUPLEX_MASK) != 0U) { + *duplex = kPHY_FullDuplex; + } else { + *duplex = kPHY_HalfDuplex; + } + } + } + return result; +} + +status_t PHY_DP83867_SetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex) { + status_t result; + uint32_t regValue; + + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result == kStatus_Success) { + /* Disable the auto-negotiation and set according to user-defined configuration. */ + regValue &= ~PHY_BCTL_AUTONEG_MASK; + if (speed == kPHY_Speed1000M) { + regValue &= ~PHY_BCTL_SPEED0_MASK; + regValue |= PHY_BCTL_SPEED1_MASK; + } else if (speed == kPHY_Speed100M) { + regValue |= PHY_BCTL_SPEED0_MASK; + regValue &= ~PHY_BCTL_SPEED1_MASK; + } else { + regValue &= ~PHY_BCTL_SPEED0_MASK; + regValue &= ~PHY_BCTL_SPEED1_MASK; + } + if (duplex == kPHY_FullDuplex) { + regValue |= PHY_BCTL_DUPLEX_MASK; + } else { + regValue &= ~PHY_BCTL_DUPLEX_MASK; + } + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + } + return result; +} + +status_t PHY_DP83867_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable) { + /* This PHY only supports local loopback. */ + assert(mode == kPHY_LocalLoop); + + status_t result; + uint32_t regValue; + + /* Set the loop mode. */ + if (enable) { + if (speed == kPHY_Speed1000M) { + regValue = PHY_BCTL_SPEED1_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + } else if (speed == kPHY_Speed100M) { + regValue = PHY_BCTL_SPEED0_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + } else { + regValue = PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + } + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + } else { + /* First read the current status in control register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result == kStatus_Success) { + regValue &= ~PHY_BCTL_LOOP_MASK; + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, + (regValue | PHY_BCTL_RESTART_AUTONEG_MASK)); + } + } + return result; +} diff --git a/ports/mimxrt/hal/phy/device/phydp83867/fsl_phydp83867.h b/ports/mimxrt/hal/phy/device/phydp83867/fsl_phydp83867.h new file mode 100644 index 0000000000000..ba0f27daa9a6c --- /dev/null +++ b/ports/mimxrt/hal/phy/device/phydp83867/fsl_phydp83867.h @@ -0,0 +1,165 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Andrew Leech + * + * 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 _FSL_PHYDP83867_H_ +#define _FSL_PHYDP83867_H_ + +#include "fsl_phy.h" + +/*! + * @addtogroup phy_driver + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief PHY operations structure. */ +extern const phy_operations_t phydp83867_ops; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name PHY Driver + * @{ + */ + +/*! + * @brief Initializes PHY. + * + * This function initialize PHY. + * + * @param handle PHY device handle. + * @param config Pointer to structure of phy_config_t. + * @retval kStatus_Success PHY initialization succeeds + * @retval kStatus_Fail PHY initialization fails + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83867_Init(phy_handle_t *handle, const phy_config_t *config); + +/*! + * @brief PHY Write function. This function writes data over the SMI to + * the specified PHY register. This function is called by all PHY interfaces. + * + * @param handle PHY device handle. + * @param phyReg The PHY register. + * @param data The data written to the PHY register. + * @retval kStatus_Success PHY write success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83867_Write(phy_handle_t *handle, uint32_t phyReg, uint32_t data); + +/*! + * @brief PHY Read function. This interface reads data over the SMI from the + * specified PHY register. This function is called by all PHY interfaces. + * + * @param handle PHY device handle. + * @param phyReg The PHY register. + * @param dataPtr The address to store the data read from the PHY register. + * @retval kStatus_Success PHY read success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83867_Read(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr); + +/*! + * @brief Gets the PHY auto-negotiation status. + * + * @param handle PHY device handle. + * @param status The auto-negotiation status of the PHY. + * - true the auto-negotiation is over. + * - false the auto-negotiation is on-going or not started. + * @retval kStatus_Success PHY gets status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83867_GetAutoNegotiationStatus(phy_handle_t *handle, bool *status); + +/*! + * @brief Gets the PHY link status. + * + * @param handle PHY device handle. + * @param status The link up or down status of the PHY. + * - true the link is up. + * - false the link is down. + * @retval kStatus_Success PHY gets link status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83867_GetLinkStatus(phy_handle_t *handle, bool *status); + +/*! + * @brief Gets the PHY link speed and duplex. + * + * @brief This function gets the speed and duplex mode of PHY. User can give one of speed + * and duplex address parameter and set the other as NULL if only wants to get one of them. + * + * @param handle PHY device handle. + * @param speed The address of PHY link speed. + * @param duplex The link duplex of PHY. + * @retval kStatus_Success PHY gets link speed and duplex success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83867_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex); + +/*! + * @brief Sets the PHY link speed and duplex. + * + * @param handle PHY device handle. + * @param speed Specified PHY link speed. + * @param duplex Specified PHY link duplex. + * @retval kStatus_Success PHY gets status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83867_SetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex); + +/*! + * @brief Enables/disables PHY loopback. + * + * @param handle PHY device handle. + * @param mode The loopback mode to be enabled, please see "phy_loop_t". + * All loopback modes should not be set together, when one loopback mode is set + * another should be disabled. + * @param speed PHY speed for loopback mode. + * @param enable True to enable, false to disable. + * @retval kStatus_Success PHY loopback success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83867_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable); + +/* @} */ + +#if defined(__cplusplus) +} +#endif + +/*! @}*/ + +#endif /* _FSL_PHYDP83867_H_ */ diff --git a/ports/mimxrt/network_lan.c b/ports/mimxrt/network_lan.c index 7d6ae2d7277fb..46f1bfa186dd7 100644 --- a/ports/mimxrt/network_lan.c +++ b/ports/mimxrt/network_lan.c @@ -35,6 +35,7 @@ #include "hal/phy/device/phyksz8081/fsl_phyksz8081.h" #include "hal/phy/device/phydp83825/fsl_phydp83825.h" #include "hal/phy/device/phydp83848/fsl_phydp83848.h" +#include "hal/phy/device/phydp83867/fsl_phydp83867.h" #include "hal/phy/device/phylan8720/fsl_phylan8720.h" #include "hal/phy/device/phyrtl8211f/fsl_phyrtl8211f.h" @@ -113,6 +114,8 @@ static mp_obj_t network_lan_make_new(const mp_obj_type_t *type, size_t n_args, s phy_ops = &phydp83825_ops; } else if (phy_type == PHY_DP83848) { phy_ops = &phydp83848_ops; + } else if (phy_type == PHY_DP83867) { + phy_ops = &phydp83867_ops; } else if (phy_type == PHY_LAN8720) { phy_ops = &phylan8720_ops; } else if (phy_type == PHY_RTL8211F) { @@ -254,6 +257,7 @@ static const mp_rom_map_elem_t network_lan_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_PHY_KSZ8081), MP_ROM_INT(PHY_KSZ8081) }, { MP_ROM_QSTR(MP_QSTR_PHY_DP83825), MP_ROM_INT(PHY_DP83825) }, { MP_ROM_QSTR(MP_QSTR_PHY_DP83848), MP_ROM_INT(PHY_DP83848) }, + { MP_ROM_QSTR(MP_QSTR_PHY_DP83867), MP_ROM_INT(PHY_DP83867) }, { MP_ROM_QSTR(MP_QSTR_PHY_LAN8720), MP_ROM_INT(PHY_LAN8720) }, { MP_ROM_QSTR(MP_QSTR_PHY_RTL8211F), MP_ROM_INT(PHY_RTL8211F) }, { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(PHY_TX_CLK_IN) }, From 5021137f32d77229f38ae2ab39aa30b5b54dcc1c Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 11 Nov 2025 13:31:25 +1100 Subject: [PATCH 165/177] mimxrt/boards/MIMXRT1170_EVK: Remove obsolete pin defines. The definitions from pins.csv are used directly in mimxrt/eth.c Signed-off-by: Andrew Leech --- ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h index 131c5e1725477..5982326ed2817 100644 --- a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h @@ -175,10 +175,6 @@ #define ENET_1_PHY_OPS phyrtl8211f_ops // 1G Ethernet PIN definitions -// No INT pin for ENET_1G -#define ENET_1_RESET_PIN &pin_GPIO_DISP_B2_13 -#define ENET_1_INT_PIN NULL - #define IOMUX_TABLE_ENET_1 \ { IOMUXC_GPIO_DISP_B1_00_ENET_1G_RX_EN, 0, 0x08U }, \ { IOMUXC_GPIO_DISP_B1_01_ENET_1G_RX_CLK, 0, 0x08U }, \ From 77e62f627ac7edac7373c34d70f9dc158e1ef7f5 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 20 Nov 2025 13:52:09 +1100 Subject: [PATCH 166/177] mimxrt/eth: Improve Dual Ethernet configuration. Signed-off-by: Andrew Leech --- .../boards/MIMXRT1170_EVK/mpconfigboard.h | 4 +- ports/mimxrt/eth.c | 67 +++++++++++++------ ports/mimxrt/eth.h | 7 +- ports/mimxrt/mpconfigport.h | 2 +- ports/mimxrt/network_lan.c | 60 +++++++++++++---- 5 files changed, 101 insertions(+), 39 deletions(-) diff --git a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h index 5982326ed2817..1154aac83005d 100644 --- a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h @@ -167,9 +167,7 @@ { IOMUXC_GPIO_AD_33_ENET_MDIO, 0, 0x06u }, \ { IOMUXC_GPIO_AD_32_ENET_MDC, 0, 0x06u }, -// A second ETH port is present. -#define ENET_DUAL_PORT (1) -// 1G Transceiver Phy Parameters +// 1G Transceiver Phy Parameters (second ETH port) #define ENET_1_PHY_ADDRESS (1) #define ENET_1_PHY RTL8211F #define ENET_1_PHY_OPS phyrtl8211f_ops diff --git a/ports/mimxrt/eth.c b/ports/mimxrt/eth.c index bbca2f6646523..308f5407a2983 100644 --- a/ports/mimxrt/eth.c +++ b/ports/mimxrt/eth.c @@ -31,7 +31,7 @@ #include "py/mperrno.h" #include "ticks.h" -#if defined(IOMUX_TABLE_ENET) +#if defined(ENET_PHY_ADDRESS) || defined(ENET_1_PHY_ADDRESS) #include "pin.h" #include "shared/netutils/netutils.h" @@ -75,6 +75,8 @@ typedef struct _iomux_table_t { uint32_t configValue; } iomux_table_t; +#if defined(ENET_PHY_ADDRESS) + // ETH0 buffers and handles static AT_NONCACHEABLE_SECTION_ALIGN(enet_rx_bd_struct_t g_rxBuffDescrip[ENET_RXBD_NUM], ENET_BUFF_ALIGNMENT); static AT_NONCACHEABLE_SECTION_ALIGN(enet_tx_bd_struct_t g_txBuffDescrip[ENET_TXBD_NUM], ENET_BUFF_ALIGNMENT); @@ -111,7 +113,9 @@ static const iomux_table_t iomux_table_enet[] = { static uint8_t hw_addr[6]; // The MAC address field -#if defined(ENET_DUAL_PORT) +#endif // defined(ENET_PHY_ADDRESS) + +#if defined(ENET_1_PHY_ADDRESS) // ETH1 buffers and handles static AT_NONCACHEABLE_SECTION_ALIGN(enet_rx_bd_struct_t g_rxBuffDescrip_1[ENET_RXBD_NUM], ENET_BUFF_ALIGNMENT); @@ -148,17 +152,14 @@ static const iomux_table_t iomux_table_enet_1[] = { static uint8_t hw_addr_1[6]; // The MAC address field -#endif - -#if defined(ENET_DUAL_PORT) +// Define ENET_1 to the appropriate controller for this hardware #if defined MIMXRT117x_SERIES #define ENET_1 ENET_1G #else #define ENET_1 ENET2 #endif -#else -#define ENET_1 ENET -#endif + +#endif // defined(ENET_1_PHY_ADDRESS) #define PHY_AUTONEGO_TIMEOUT_US (5000000) #define PHY_SETTLE_TIME_US (1000) @@ -357,6 +358,8 @@ static void eth_phy_init(phy_handle_t *phyHandle, phy_config_t *phy_config, mp_hal_delay_us(phy_settle_time); } +#if defined(ENET_PHY_ADDRESS) + // eth_init: Set up GPIO and the transceiver void eth_init_0(eth_t *self, int eth_id, const phy_operations_t *phy_ops, int phy_addr, bool phy_clock) { // Configuration values @@ -411,7 +414,9 @@ void eth_init_0(eth_t *self, int eth_id, const phy_operations_t *phy_ops, int ph ENET_ActiveRead(ENET); } -#if defined(ENET_DUAL_PORT) +#endif // defined(ENET_PHY_ADDRESS) + +#if defined(ENET_1_PHY_ADDRESS) // eth_init: Set up GPIO and the transceiver void eth_init_1(eth_t *self, int eth_id, const phy_operations_t *phy_ops, int phy_addr, bool phy_clock) { @@ -474,7 +479,8 @@ void eth_init_1(eth_t *self, int eth_id, const phy_operations_t *phy_ops, int ph ENET_ActiveRead(ENET_1); } -#endif +#endif // defined(ENET_1_PHY_ADDRESS) + // Initialize the phy interface static int eth_mac_init(eth_t *self) { return 0; @@ -512,14 +518,26 @@ static err_t eth_send_frame_blocking(ENET_Type *base, enet_handle_t *handle, uin static err_t eth_netif_output(struct netif *netif, struct pbuf *p) { // This function should always be called from a context where PendSV-level IRQs are disabled status_t status; - ENET_Type *enet = ENET; - enet_handle_t *handle = &g_handle; + ENET_Type *enet; + enet_handle_t *handle; - #if defined ENET_DUAL_PORT + #if defined(ENET_PHY_ADDRESS) && defined(ENET_1_PHY_ADDRESS) + // Dual port: select based on netif->state if (netif->state == ð_instance1) { enet = ENET_1; handle = &g_handle_1; + } else { + enet = ENET; + handle = &g_handle; } + #elif defined(ENET_1_PHY_ADDRESS) + // Only ENET_1 available + enet = ENET_1; + handle = &g_handle_1; + #else + // Only ENET available + enet = ENET; + handle = &g_handle; #endif eth_trace(netif->state, (size_t)-1, p, NETUTILS_TRACE_IS_TX | NETUTILS_TRACE_NEWLINE); @@ -562,15 +580,18 @@ static void eth_lwip_init(eth_t *self) { ip_addr_t ipconfig[4]; self->netif.hwaddr_len = 6; + #if defined(ENET_PHY_ADDRESS) if (self == ð_instance0) { memcpy(self->netif.hwaddr, hw_addr, 6); IP4_ADDR(&ipconfig[0], 192, 168, 0, 2); - #if defined ENET_DUAL_PORT - } else { + } + #endif + #if defined(ENET_1_PHY_ADDRESS) + if (self == ð_instance1) { memcpy(self->netif.hwaddr, hw_addr_1, 6); IP4_ADDR(&ipconfig[0], 192, 168, 0, 3); - #endif } + #endif IP4_ADDR(&ipconfig[1], 255, 255, 255, 0); IP4_ADDR(&ipconfig[2], 192, 168, 0, 1); IP4_ADDR(&ipconfig[3], 8, 8, 8, 8); @@ -578,7 +599,11 @@ static void eth_lwip_init(eth_t *self) { MICROPY_PY_LWIP_ENTER n->name[0] = 'e'; + #if defined(ENET_PHY_ADDRESS) n->name[1] = (self == ð_instance0 ? '0' : '1'); + #else + n->name[1] = '1'; + #endif netif_add(n, &ipconfig[0], &ipconfig[1], &ipconfig[2], self, eth_netif_init, ethernet_input); netif_set_hostname(n, mod_network_hostname_data); netif_set_default(n); @@ -620,8 +645,10 @@ int eth_link_status(eth_t *self) { } } else { bool link; - #if defined ENET_DUAL_PORT + #if defined(ENET_PHY_ADDRESS) && defined(ENET_1_PHY_ADDRESS) PHY_GetLinkStatus(self == ð_instance0 ? &phyHandle : &phyHandle_1, &link); + #elif defined(ENET_1_PHY_ADDRESS) + PHY_GetLinkStatus(&phyHandle_1, &link); #else PHY_GetLinkStatus(&phyHandle, &link); #endif @@ -654,10 +681,12 @@ int eth_stop(eth_t *self) { } void eth_low_power_mode(eth_t *self, bool enable) { - #if defined ENET_DUAL_PORT + #if defined(ENET_PHY_ADDRESS) && defined(ENET_1_PHY_ADDRESS) ENET_EnableSleepMode(self == ð_instance0 ? ENET : ENET_1, enable); + #elif defined(ENET_1_PHY_ADDRESS) + ENET_EnableSleepMode(ENET_1, enable); #else ENET_EnableSleepMode(ENET, enable); #endif } -#endif // defined(IOMUX_TABLE_ENET) +#endif // defined(ENET_PHY_ADDRESS) || defined(ENET_1_PHY_ADDRESS) diff --git a/ports/mimxrt/eth.h b/ports/mimxrt/eth.h index 0d9ed599bc464..4c87d1b5178d4 100644 --- a/ports/mimxrt/eth.h +++ b/ports/mimxrt/eth.h @@ -28,11 +28,14 @@ #define MICROPY_INCLUDED_MIMXRT_ETH_H typedef struct _eth_t eth_t; + +#if defined(ENET_PHY_ADDRESS) extern eth_t eth_instance0; -extern eth_t eth_instance1; void eth_init_0(eth_t *self, int mac_idx, const phy_operations_t *phy_ops, int phy_addr, bool phy_clock); +#endif -#if defined(ENET_DUAL_PORT) +#if defined(ENET_1_PHY_ADDRESS) +extern eth_t eth_instance1; void eth_init_1(eth_t *self, int mac_idx, const phy_operations_t *phy_ops, int phy_addr, bool phy_clock); #endif diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 45316b904b62f..52289549d7cc3 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -179,7 +179,7 @@ uint32_t trng_random_u32(void); // Hooks to add builtins -#if defined(IOMUX_TABLE_ENET) +#if defined(ENET_PHY_ADDRESS) || defined(ENET_1_PHY_ADDRESS) extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_HW_NIC_ETH { MP_ROM_QSTR(MP_QSTR_LAN), MP_ROM_PTR(&network_lan_type) }, #else diff --git a/ports/mimxrt/network_lan.c b/ports/mimxrt/network_lan.c index 46f1bfa186dd7..b020d53e6efc7 100644 --- a/ports/mimxrt/network_lan.c +++ b/ports/mimxrt/network_lan.c @@ -28,7 +28,7 @@ #include "py/mphal.h" #include "extmod/modnetwork.h" -#if defined(IOMUX_TABLE_ENET) +#if defined(ENET_PHY_ADDRESS) || defined(ENET_1_PHY_ADDRESS) #include "fsl_phy.h" #include "eth.h" @@ -55,9 +55,13 @@ typedef struct _network_lan_obj_t { eth_t *eth; } network_lan_obj_t; +// Forward declaration of the type +extern const mp_obj_type_t network_lan_type; +#if defined(ENET_PHY_ADDRESS) static const network_lan_obj_t network_lan_eth0 = { { &network_lan_type }, ð_instance0 }; -#if defined(ENET_DUAL_PORT) +#endif +#if defined(ENET_1_PHY_ADDRESS) static const network_lan_obj_t network_lan_eth1 = { { &network_lan_type }, ð_instance1 }; #endif @@ -65,8 +69,19 @@ static void network_lan_print(const mp_print_t *print, mp_obj_t self_in, mp_prin network_lan_obj_t *self = MP_OBJ_TO_PTR(self_in); struct netif *netif = eth_netif(self->eth); int status = eth_link_status(self->eth); + int eth_id = 0; + #if defined(ENET_PHY_ADDRESS) + if (self->eth == ð_instance0) { + eth_id = 0; + } + #endif + #if defined(ENET_1_PHY_ADDRESS) + if (self->eth == ð_instance1) { + eth_id = 1; + } + #endif mp_printf(print, "", - self->eth == ð_instance0 ? 0 : 1, + eth_id, status, netif->ip_addr.addr & 0xff, netif->ip_addr.addr >> 8 & 0xff, @@ -77,8 +92,18 @@ static void network_lan_print(const mp_print_t *print, mp_obj_t self_in, mp_prin static mp_obj_t network_lan_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_phy_type, ARG_phy_addr, ARG_ref_clk_mode}; + + // Default to port 0 if ENET available, else port 1 if only ENET_1 available + #if defined(ENET_PHY_ADDRESS) + #define DEFAULT_ETH_ID 0 + #elif defined(ENET_1_PHY_ADDRESS) + #define DEFAULT_ETH_ID 1 + #else + #error "No Ethernet PHY configured" + #endif + static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_id, MP_ARG_INT, {.u_int = DEFAULT_ETH_ID} }, { MP_QSTR_phy_type, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_phy_addr, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_ref_clk_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, @@ -92,18 +117,21 @@ static mp_obj_t network_lan_make_new(const mp_obj_type_t *type, size_t n_args, s bool phy_clock; int mac_id = args[ARG_id].u_int; - // set default + // set default based on which interface is being used + #if defined(ENET_PHY_ADDRESS) if (mac_id == 0) { phy_ops = &ENET_PHY_OPS; phy_addr = ENET_PHY_ADDRESS; phy_clock = ENET_TX_CLK_OUTPUT; - #if defined(ENET_DUAL_PORT) - } else { + } + #endif + #if defined(ENET_1_PHY_ADDRESS) + if (mac_id == 1) { phy_ops = &ENET_1_PHY_OPS; phy_addr = ENET_1_PHY_ADDRESS; phy_clock = ENET_1_TX_CLK_OUTPUT; - #endif } + #endif // Select PHY driver int phy_type = args[ARG_phy_type].u_int; @@ -132,17 +160,21 @@ static mp_obj_t network_lan_make_new(const mp_obj_type_t *type, size_t n_args, s phy_clock = args[ARG_ref_clk_mode].u_int; } - // Prepare for two ETH interfaces. - const network_lan_obj_t *self; + // Initialize the appropriate ETH interface + const network_lan_obj_t *self = NULL; + #if defined(ENET_PHY_ADDRESS) if (mac_id == 0) { self = &network_lan_eth0; eth_init_0(self->eth, MP_HAL_MAC_ETH0, phy_ops, phy_addr, phy_clock); - #if defined(ENET_DUAL_PORT) - } else if (mac_id == 1) { + } + #endif + #if defined(ENET_1_PHY_ADDRESS) + if (mac_id == 1) { self = &network_lan_eth1; eth_init_1(self->eth, MP_HAL_MAC_ETH1, phy_ops, phy_addr, phy_clock); + } #endif - } else { + if (self == NULL) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid LAN interface %d"), mac_id); } @@ -275,4 +307,4 @@ MP_DEFINE_CONST_OBJ_TYPE( ); -#endif // defined(IOMUX_TABLE_ENET) +#endif // defined(ENET_PHY_ADDRESS) || defined(ENET_1_PHY_ADDRESS) From af88d65e8661b9c0a3ad9d6b4f61cde6f7948597 Mon Sep 17 00:00:00 2001 From: Algy Tynan Date: Mon, 8 Dec 2025 16:07:52 +1100 Subject: [PATCH 167/177] mimxrt: Add ALT11 pin mode support for MIMXRT1176. This adds support for the ALT11 alternate function mode on MIMXRT1176 MCUs, enabling FLEXPWM channels on GPIO_AD pins that were previously unavailable for PWM use. Changes: - Add PIN_AF_MODE_ALT11 enum to pin.h - Add ALT11 column to MIMXRT1176_af.csv with FLEXPWM mappings - Enables FLEXPWM1-4 on GPIO_AD_06 through GPIO_AD_21 This change only affects MIMXRT1176-based boards (MIMXRT1170_EVK and PHYBOARD_RT1170). Other MIMXRT family chips use alternate function modes ALT0-ALT9 and are not affected. Tested on MIMXRT1176 hardware with PWM output on GPIO_AD_14 (FLEXPWM3_PWM0_X). Signed-off-by: Algy Tynan --- docs/mimxrt/pinout.rst | 70 +++--- ports/mimxrt/boards/MIMXRT1176_af.csv | 350 +++++++++++++------------- ports/mimxrt/pin.h | 1 + 3 files changed, 214 insertions(+), 207 deletions(-) diff --git a/docs/mimxrt/pinout.rst b/docs/mimxrt/pinout.rst index d2b62d56d01d6..09953164bde4f 100644 --- a/docs/mimxrt/pinout.rst +++ b/docs/mimxrt/pinout.rst @@ -67,41 +67,43 @@ PWM pin assignment Pins are specified in the same way as for the Pin class. The following tables show the assignment of the board Pins to PWM modules: -=========== ========== ========== ====== ========== ====== ======== -Pin/ MIMXRT 1010 1015 1020 1050/60/64 1170 Metro M7 -=========== ========== ========== ====== ========== ====== ======== -D0 - Q1/1 F1/1/B - - - -D1 - Q1/0 F1/1/A - - - -D2 F1/3/B F1/3/A - F1/3/B - - -D3 F1/3/A F1/0/A F2/3/B F4/0/A F1/2/A - -D4 F1/3/A (*) Q1/2 Q2/1 F2/3/A Q4/2 F1/0/B -D5 F1/0/B (*) F1/0/B F2/3/A F1/3/A F1/2/B F1/0/A -D6 - F1/2/B F2/0/A Q3/2 F1/0/A - -D7 - - F1/0/A Q3/3 - - -D8 F1/0/A F1/1/B F1/0/B F1/1/X Q4/3 F1/3/A -D9 F1/1/B (*) F1/2/A F2/0/B F1/0/X F1/0/B F1/3/B -D10 F1/3/B - F2/2/B F1/0/B (*) F2/2/B F1/2/A -D11 F1/2/A - F2/1/A F1/1/A (*) - F1/2/B -D12 F1/2/B - F2/1/B F1/1/B (*) - F1/1/A -D13 F1/3/A - F2/2/A F1/0/A (*) F2/2/A F1/1/B -D14 F1/0/B - - F2/3/B - F1/0/B -D15 F1/0/A - - F2/3/A - F1/0/A -A0 - - F1/2/A - - - -A1 F1/3/X F1/3/B F1/2/B - - - -A2 F1/2/X F1/3/A F1/3/A - - - -A3 - F1/2/A F1/3/B - - F1/3/B -A4 - - - Q3/1 - F1/2/X -A5 - - - Q3/0 - - -D31 - - - - F1/2/B - -D32 - - - - F1/2/A - -D33 - - - - F1/1/B - -D34 - - - - F1/1/A - -D35 - - - - F1/0/B - -D36 - - - - F1/0/A - -=========== ========== ========== ====== ========== ====== ======== +=========== ========== ========== ====== ========== =========== ======== +Pin/ MIMXRT 1010 1015 1020 1050/60/64 1170 Metro M7 +=========== ========== ========== ====== ========== =========== ======== +D0 - Q1/1 F1/1/B - - - +D1 - Q1/0 F1/1/A - - - +D2 F1/3/B F1/3/A - F1/3/B - - +D3 F1/3/A F1/0/A F2/3/B F4/0/A F1/2/A (&) - +D4 F1/3/A (*) Q1/2 Q2/1 F2/3/A F1/0/X (&) F1/0/B +D5 F1/0/B (*) F1/0/B F2/3/A F1/3/A F1/2/B (&) F1/0/A +D6 - F1/2/B F2/0/A Q3/2 F1/0/A - +D7 - - F1/0/A Q3/3 F3/0/X - +D8 F1/0/A F1/1/B F1/0/B F1/1/X F1/1/X (&) F1/3/A +D9 F1/1/B (*) F1/2/A F2/0/B F1/0/X F1/0/B F1/3/B +D10 F1/3/B - F2/2/B F1/0/B (*) F2/2/B F1/2/A +D11 F1/2/A - F2/1/A F1/1/A (*) - F1/2/B +D12 F1/2/B - F2/1/B F1/1/B (*) - F1/1/A +D13 F1/3/A - F2/2/A F1/0/A (*) F2/2/A F1/1/B +D14 F1/0/B - - F2/3/B - F1/0/B +D15 F1/0/A - - F2/3/A - F1/0/A +A0 - - F1/2/A - F2/0/X - +A1 F1/3/X F1/3/B F1/2/B - F2/1/X - +A2 F1/2/X F1/3/A F1/3/A - F2/2/X - +A3 - F1/2/A F1/3/B - F2/3/X F1/3/B +A4 - - - Q3/1 F1/3/X F1/2/X +A5 - - - Q3/0 F1/2/X - +D31 - - - - F1/2/B (&) - +D32 - - - - F1/2/A (&) - +D33 - - - - F1/1/B - +D34 - - - - F1/1/A - +D35 - - - - F1/0/B - +D36 - - - - F1/0/A - +=========== ========== ========== ====== ========== =========== ======== Pins denoted with (*) are by default not wired at the board. +Pins denoted with (&) have alternative PWM options available. + ==== ========== ==== ========== Pin Teensy 4.0 Pin Teensy 4.1 ==== ========== ==== ========== @@ -317,6 +319,10 @@ Pin.cpu.GPIO_EMC_B1_29 FLEXPWM3 Channel A (*) Pin.cpu.GPIO_EMC_B1_30 FLEXPWM3 Channel B (*) Pin.cpu.GPIO_AD_00 FLEXPWM1 Channel A Pin.cpu.GPIO_AD_01 FLEXPWM1 Channel B +Pin.cpu.GPIO_AD_06 FLEXPWM1 Channel X +Pin.cpu.GPIO_AD_10 FLEXPWM2 Channel X +Pin.cpu.GPIO_AD_14 FLEXPWM3 Channel X +Pin.cpu.GPIO_AD_18 FLEXPWM4 Channel X Pin.cpu.GPIO_AD_24 FLEXPWM2 Channel A Pin.cpu.GPIO_AD_25 FLEXPWM2 Channel B ====================== ====================== diff --git a/ports/mimxrt/boards/MIMXRT1176_af.csv b/ports/mimxrt/boards/MIMXRT1176_af.csv index 5e99a49003af7..0b13e509aa276 100644 --- a/ports/mimxrt/boards/MIMXRT1176_af.csv +++ b/ports/mimxrt/boards/MIMXRT1176_af.csv @@ -1,175 +1,175 @@ -Pad,ALT0,ALT1,ALT2,ALT3,ALT4,ALT5,ALT6,ALT7,ALT8,ALT9,ALT10,ADC,ACMP,Default -GPIO_SD_B1_00,USDHC1_CMD,,XBAR1_INOUT20,GPT4_CAPTURE1,,GPIO4_IO03,FLEXSPI2_A_SS0_B,,KPP_ROW07,,GPIO10_IO03,,, -GPIO_SD_B1_01,USDHC1_CLK,,XBAR1_INOUT21,GPT4_CAPTURE2,,GPIO4_IO04,FLEXSPI2_A_SCLK,,KPP_COL07,,GPIO10_IO04,,, -GPIO_SD_B1_02,USDHC1_DATA0,,XBAR1_INOUT22,GPT4_COMPARE1,,GPIO4_IO05,FLEXSPI2_A_DATA00,,KPP_ROW06,FLEXSPI1_A_SS1_B,GPIO10_IO05,,, -GPIO_SD_B1_03,USDHC1_DATA1,,XBAR1_INOUT23,GPT4_COMPARE2,,GPIO4_IO06,FLEXSPI2_A_DATA01,,KPP_COL06,FLEXSPI1_B_SS1_B,GPIO10_IO06,,, -GPIO_SD_B1_04,USDHC1_DATA2,,XBAR1_INOUT24,GPT4_COMPARE3,,GPIO4_IO07,FLEXSPI2_A_DATA02,,FLEXSPI1_B_SS0_B,ENET_QOS_1588_EVENT2_AUX_IN,GPIO10_IO07,,, -GPIO_SD_B1_05,USDHC1_DATA3,,XBAR1_INOUT25,GPT4_CLK,,GPIO4_IO08,FLEXSPI2_A_DATA03,,FLEXSPI1_B_DQS,ENET_QOS_1588_EVENT3_AUX_IN,GPIO10_IO08,,, -GPIO_SD_B2_00,USDHC2_DATA3,FLEXSPI1_B_DATA03,ENET_1G_RX_EN,LPUART9_TXD,LPSPI4_SCK,GPIO4_IO09,,,,,GPIO10_IO09,,, -GPIO_SD_B2_01,USDHC2_DATA2,FLEXSPI1_B_DATA02,ENET_1G_RX_CLK,LPUART9_RXD,LPSPI4_PCS0,GPIO4_IO10,,,,,GPIO10_IO10,,, -GPIO_SD_B2_02,USDHC2_DATA1,FLEXSPI1_B_DATA01,ENET_1G_RX_DATA00,LPUART9_CTS_B,LPSPI4_SOUT,GPIO4_IO11,,,,,GPIO10_IO11,,, -GPIO_SD_B2_03,USDHC2_DATA0,FLEXSPI1_B_DATA00,ENET_1G_RX_DATA01,LPUART9_RTS_B,LPSPI4_SIN,GPIO4_IO12,,,,,GPIO10_IO12,,, -GPIO_SD_B2_04,USDHC2_CLK,FLEXSPI1_B_SCLK,ENET_1G_RX_DATA02,FLEXSPI1_A_SS1_B,LPSPI4_PCS1,GPIO4_IO13,,,,,GPIO10_IO13,,, -GPIO_SD_B2_05,USDHC2_CMD,FLEXSPI1_A_DQS,ENET_1G_RX_DATA03,FLEXSPI1_B_SS0_B,LPSPI4_PCS2,GPIO4_IO14,,,,,GPIO10_IO14,,, -GPIO_SD_B2_06,USDHC2_RESET_B,FLEXSPI1_A_SS0_B,ENET_1G_TX_DATA03,LPSPI4_PCS3,GPT6_CAPTURE1,GPIO4_IO15,,,,,GPIO10_IO15,,, -GPIO_SD_B2_07,USDHC2_STROBE,FLEXSPI1_A_SCLK,ENET_1G_TX_DATA02,LPUART3_CTS_B,GPT6_CAPTURE2,GPIO4_IO16,LPSPI2_SCK,,ENET_TX_ER,ENET_QOS_REF_CLK,GPIO10_IO16,,, -GPIO_SD_B2_08,USDHC2_DATA4,FLEXSPI1_A_DATA00,ENET_1G_TX_DATA01,LPUART3_RTS_B,GPT6_COMPARE1,GPIO4_IO17,LPSPI2_PCS0,,,,GPIO10_IO17,,, -GPIO_SD_B2_09,USDHC2_DATA5,FLEXSPI1_A_DATA01,ENET_1G_TX_DATA00,LPUART5_CTS_B,GPT6_COMPARE2,GPIO4_IO18,LPSPI2_SOUT,,,,GPIO10_IO18,,, -GPIO_SD_B2_10,USDHC2_DATA6,FLEXSPI1_A_DATA02,ENET_1G_TX_EN,LPUART5_RTS_B,GPT6_COMPARE3,GPIO4_IO19,LPSPI2_SIN,,,,GPIO10_IO19,,, -GPIO_SD_B2_11,USDHC2_DATA7,FLEXSPI1_A_DATA03,ENET_1G_TX_CLK_IO,ENET_1G_REF_CLK,GPT6_CLK,GPIO4_IO20,LPSPI2_PCS1,,,,GPIO10_IO20,,, -GPIO_EMC_B1_00,SEMC_DATA00,FLEXPWM4_PWM0_A,,,,GPIO1_IO00,,,FLEXIO1_D00,,GPIO7_IO00,,, -GPIO_EMC_B1_01,SEMC_DATA01,FLEXPWM4_PWM0_B,,,,GPIO1_IO01,,,FLEXIO1_D01,,GPIO7_IO01,,, -GPIO_EMC_B1_02,SEMC_DATA02,FLEXPWM4_PWM1_A,,,,GPIO1_IO02,,,FLEXIO1_D02,,GPIO7_IO02,,, -GPIO_EMC_B1_03,SEMC_DATA03,FLEXPWM4_PWM1_B,,,,GPIO1_IO03,,,FLEXIO1_D03,,GPIO7_IO03,,, -GPIO_EMC_B1_04,SEMC_DATA04,FLEXPWM4_PWM2_A,,,,GPIO1_IO04,,,FLEXIO1_D04,,GPIO7_IO04,,, -GPIO_EMC_B1_05,SEMC_DATA05,FLEXPWM4_PWM2_B,,,,GPIO1_IO05,,,FLEXIO1_D05,,GPIO7_IO05,,, -GPIO_EMC_B1_06,SEMC_DATA06,FLEXPWM2_PWM0_A,,,,GPIO1_IO06,,,FLEXIO1_D06,,GPIO7_IO06,,, -GPIO_EMC_B1_07,SEMC_DATA07,FLEXPWM2_PWM0_B,,,,GPIO1_IO07,,,FLEXIO1_D07,,GPIO7_IO07,,, -GPIO_EMC_B1_08,SEMC_DM00,FLEXPWM2_PWM1_A,,,,GPIO1_IO08,,,FLEXIO1_D08,,GPIO7_IO08,,, -GPIO_EMC_B1_09,SEMC_ADDR00,FLEXPWM2_PWM1_B,GPT5_CAPTURE1,,,GPIO1_IO09,,,FLEXIO1_D09,,GPIO7_IO09,,, -GPIO_EMC_B1_10,SEMC_ADDR01,FLEXPWM2_PWM2_A,GPT5_CAPTURE2,,,GPIO1_IO10,,,FLEXIO1_D10,,GPIO7_IO10,,, -GPIO_EMC_B1_11,SEMC_ADDR02,FLEXPWM2_PWM2_B,GPT5_COMPARE1,,,GPIO1_IO11,,,FLEXIO1_D11,,GPIO7_IO11,,, -GPIO_EMC_B1_12,SEMC_ADDR03,XBAR1_INOUT04,GPT5_COMPARE2,,,GPIO1_IO12,,,FLEXIO1_D12,,GPIO7_IO12,,, -GPIO_EMC_B1_13,SEMC_ADDR04,XBAR1_INOUT05,GPT5_COMPARE3,,,GPIO1_IO13,,,FLEXIO1_D13,,GPIO7_IO13,,, -GPIO_EMC_B1_14,SEMC_ADDR05,XBAR1_INOUT06,GPT5_CLK,,,GPIO1_IO14,,,FLEXIO1_D14,,GPIO7_IO14,,, -GPIO_EMC_B1_15,SEMC_ADDR06,XBAR1_INOUT07,,,,GPIO1_IO15,,,FLEXIO1_D15,,GPIO7_IO15,,, -GPIO_EMC_B1_16,SEMC_ADDR07,XBAR1_INOUT08,,,,GPIO1_IO16,,,FLEXIO1_D16,,GPIO7_IO16,,, -GPIO_EMC_B1_17,SEMC_ADDR08,FLEXPWM4_PWM3_A,TMR1_TIMER0,,,GPIO1_IO17,,,FLEXIO1_D17,,GPIO7_IO17,,, -GPIO_EMC_B1_18,SEMC_ADDR09,FLEXPWM4_PWM3_B,TMR2_TIMER0,,,GPIO1_IO18,,,FLEXIO1_D18,,GPIO7_IO18,,, -GPIO_EMC_B1_19,SEMC_ADDR11,FLEXPWM2_PWM3_A,TMR3_TIMER0,,,GPIO1_IO19,,,FLEXIO1_D19,,GPIO7_IO19,,, -GPIO_EMC_B1_20,SEMC_ADDR12,FLEXPWM2_PWM3_B,TMR4_TIMER0,,,GPIO1_IO20,,,FLEXIO1_D20,,GPIO7_IO20,,, -GPIO_EMC_B1_21,SEMC_BA0,FLEXPWM3_PWM3_A,,,,GPIO1_IO21,,,FLEXIO1_D21,,GPIO7_IO21,,, -GPIO_EMC_B1_22,SEMC_BA1,FLEXPWM3_PWM3_B,,,,GPIO1_IO22,,,FLEXIO1_D22,,GPIO7_IO22,,, -GPIO_EMC_B1_23,SEMC_ADDR10,FLEXPWM1_PWM0_A,,,,GPIO1_IO23,,,FLEXIO1_D23,,GPIO7_IO23,,, -GPIO_EMC_B1_24,SEMC_CAS,FLEXPWM1_PWM0_B,,,,GPIO1_IO24,,,FLEXIO1_D24,,GPIO7_IO24,,, -GPIO_EMC_B1_25,SEMC_RAS,FLEXPWM1_PWM1_A,,,,GPIO1_IO25,,,FLEXIO1_D25,,GPIO7_IO25,,, -GPIO_EMC_B1_26,SEMC_CLK,FLEXPWM1_PWM1_B,,,,GPIO1_IO26,,,FLEXIO1_D26,,GPIO7_IO26,,, -GPIO_EMC_B1_27,SEMC_CKE,FLEXPWM1_PWM2_A,,,,GPIO1_IO27,,,FLEXIO1_D27,,GPIO7_IO27,,, -GPIO_EMC_B1_28,SEMC_WE,FLEXPWM1_PWM2_B,,,,GPIO1_IO28,,,FLEXIO1_D28,,GPIO7_IO28,,, -GPIO_EMC_B1_29,SEMC_CS0,FLEXPWM3_PWM0_A,,,,GPIO1_IO29,,,FLEXIO1_D29,,GPIO7_IO29,,, -GPIO_EMC_B1_30,SEMC_DATA08,FLEXPWM3_PWM0_B,,,,GPIO1_IO30,,,FLEXIO1_D30,,GPIO7_IO30,,, -GPIO_EMC_B1_31,SEMC_DATA09,FLEXPWM3_PWM1_A,,,,GPIO1_IO31,,,FLEXIO1_D31,,GPIO7_IO31,,, -GPIO_EMC_B1_32,SEMC_DATA10,FLEXPWM3_PWM1_B,,,,GPIO2_IO00,,,,,GPIO8_IO00,,, -GPIO_EMC_B1_33,SEMC_DATA11,FLEXPWM3_PWM2_A,,,,GPIO2_IO01,,,,,GPIO8_IO01,,, -GPIO_EMC_B1_34,SEMC_DATA12,FLEXPWM3_PWM2_B,,,,GPIO2_IO02,,,,,GPIO8_IO02,,, -GPIO_EMC_B1_35,SEMC_DATA13,XBAR1_INOUT09,,,,GPIO2_IO03,,,,,GPIO8_IO03,,, -GPIO_EMC_B1_36,SEMC_DATA14,XBAR1_INOUT10,,,,GPIO2_IO04,,,,,GPIO8_IO04,,, -GPIO_EMC_B1_37,SEMC_DATA15,XBAR1_INOUT11,,,,GPIO2_IO05,,,,,GPIO8_IO05,,, -GPIO_EMC_B1_38,SEMC_DM01,FLEXPWM1_PWM3_A,TMR1_TIMER1,,,GPIO2_IO06,,,,,GPIO8_IO06,,, -GPIO_EMC_B1_39,SEMC_DQS,FLEXPWM1_PWM3_B,TMR2_TIMER1,,,GPIO2_IO07,,,,,GPIO8_IO07,,, -GPIO_EMC_B1_40,SEMC_RDY,XBAR1_INOUT12,MQS_RIGHT,LPUART6_TXD,,GPIO2_IO08,,ENET_1G_MDC,,CCM_CLKO1,GPIO8_IO08,,, -GPIO_EMC_B1_41,SEMC_CSX00,XBAR1_INOUT13,MQS_LEFT,LPUART6_RXD,FLEXSPI2_B_DATA07,GPIO2_IO09,,ENET_1G_MDIO,,CCM_CLKO2,GPIO8_IO09,,, -GPIO_EMC_B2_00,SEMC_DATA16,CCM_ENET_REF_CLK_25M,TMR3_TIMER1,LPUART6_CTS_B,FLEXSPI2_B_DATA06,GPIO2_IO10,XBAR1_INOUT20,ENET_QOS_1588_EVENT1_OUT,LPSPI1_SCK,LPI2C2_SCL,GPIO8_IO10,,, -GPIO_EMC_B2_01,SEMC_DATA17,USDHC2_CD_B,TMR4_TIMER1,LPUART6_RTS_B,FLEXSPI2_B_DATA05,GPIO2_IO11,XBAR1_INOUT21,ENET_QOS_1588_EVENT1_IN,LPSPI1_PCS0,LPI2C2_SDA,GPIO8_IO11,,, -GPIO_EMC_B2_02,SEMC_DATA18,USDHC2_WP,,VIDEO_MUX_CSI_DATA23,FLEXSPI2_B_DATA04,GPIO2_IO12,XBAR1_INOUT22,ENET_QOS_1588_EVENT1_AUX_IN,LPSPI1_SOUT,,GPIO8_IO12,,, -GPIO_EMC_B2_03,SEMC_DATA19,USDHC2_VSELECT,,VIDEO_MUX_CSI_DATA22,FLEXSPI2_B_DATA03,GPIO2_IO13,XBAR1_INOUT23,ENET_1G_TX_DATA03,LPSPI1_SIN,,GPIO8_IO13,,, -GPIO_EMC_B2_04,SEMC_DATA20,USDHC2_RESET_B,SAI2_MCLK,VIDEO_MUX_CSI_DATA21,FLEXSPI2_B_DATA02,GPIO2_IO14,XBAR1_INOUT24,ENET_1G_TX_DATA02,LPSPI3_SCK,,GPIO8_IO14,,, -GPIO_EMC_B2_05,SEMC_DATA21,GPT3_CLK,SAI2_RX_SYNC,VIDEO_MUX_CSI_DATA20,FLEXSPI2_B_DATA01,GPIO2_IO15,XBAR1_INOUT25,ENET_1G_RX_CLK,LPSPI3_PCS0,PIT1_TRIGGER0,GPIO8_IO15,,, -GPIO_EMC_B2_06,SEMC_DATA22,GPT3_CAPTURE1,SAI2_RX_BCLK,VIDEO_MUX_CSI_DATA19,FLEXSPI2_B_DATA00,GPIO2_IO16,XBAR1_INOUT26,ENET_1G_TX_ER,LPSPI3_SOUT,PIT1_TRIGGER1,GPIO8_IO16,,, -GPIO_EMC_B2_07,SEMC_DATA23,GPT3_CAPTURE2,SAI2_RX_DATA,VIDEO_MUX_CSI_DATA18,FLEXSPI2_B_DQS,GPIO2_IO17,XBAR1_INOUT27,ENET_1G_RX_DATA03,LPSPI3_SIN,PIT1_TRIGGER2,GPIO8_IO17,,, -GPIO_EMC_B2_08,SEMC_DM02,GPT3_COMPARE1,SAI2_TX_DATA,VIDEO_MUX_CSI_DATA17,FLEXSPI2_B_SS0_B,GPIO2_IO18,XBAR1_INOUT28,ENET_1G_RX_DATA02,LPSPI3_PCS1,PIT1_TRIGGER3,GPIO8_IO18,,, -GPIO_EMC_B2_09,SEMC_DATA24,GPT3_COMPARE2,SAI2_TX_BCLK,VIDEO_MUX_CSI_DATA16,FLEXSPI2_B_SCLK,GPIO2_IO19,XBAR1_INOUT29,ENET_1G_CRS,LPSPI3_PCS2,TMR1_TIMER0,GPIO8_IO19,,, -GPIO_EMC_B2_10,SEMC_DATA25,GPT3_COMPARE3,SAI2_TX_SYNC,VIDEO_MUX_CSI_FIELD,FLEXSPI2_A_SCLK,GPIO2_IO20,XBAR1_INOUT30,ENET_1G_COL,LPSPI3_PCS3,TMR1_TIMER1,GPIO8_IO20,,, -GPIO_EMC_B2_11,SEMC_DATA26,SPDIF_IN,ENET_1G_TX_DATA00,SAI3_RX_SYNC,FLEXSPI2_A_SS0_B,GPIO2_IO21,XBAR1_INOUT31,,EMVSIM1_IO,TMR1_TIMER2,GPIO8_IO21,,, -GPIO_EMC_B2_12,SEMC_DATA27,SPDIF_OUT,ENET_1G_TX_DATA01,SAI3_RX_BCLK,FLEXSPI2_A_DQS,GPIO2_IO22,XBAR1_INOUT32,,EMVSIM1_CLK,TMR1_TIMER3,GPIO8_IO22,,, -GPIO_EMC_B2_13,SEMC_DATA28,,ENET_1G_TX_EN,SAI3_RX_DATA,FLEXSPI2_A_DATA00,GPIO2_IO23,XBAR1_INOUT33,,EMVSIM1_RST,TMR2_TIMER0,GPIO8_IO23,,, -GPIO_EMC_B2_14,SEMC_DATA29,,ENET_1G_TX_CLK_IO,SAI3_TX_DATA,FLEXSPI2_A_DATA01,GPIO2_IO24,XBAR1_INOUT34,SFA_ipp_do_atx_clk_under_test,EMVSIM1_SVEN,TMR2_TIMER1,GPIO8_IO24,,, -GPIO_EMC_B2_15,SEMC_DATA30,,ENET_1G_RX_DATA00,SAI3_TX_BCLK,FLEXSPI2_A_DATA02,GPIO2_IO25,XBAR1_INOUT35,,EMVSIM1_PD,TMR2_TIMER2,GPIO8_IO25,,, -GPIO_EMC_B2_16,SEMC_DATA31,XBAR1_INOUT14,ENET_1G_RX_DATA01,SAI3_TX_SYNC,FLEXSPI2_A_DATA03,GPIO2_IO26,,,EMVSIM1_POWER_FAIL,TMR2_TIMER3,GPIO8_IO26,,, -GPIO_EMC_B2_17,SEMC_DM03,XBAR1_INOUT15,ENET_1G_RX_EN,SAI3_MCLK,FLEXSPI2_A_DATA04,GPIO2_IO27,,,WDOG1_ANY,TMR3_TIMER0,GPIO8_IO27,,, -GPIO_EMC_B2_18,SEMC_DQS4,XBAR1_INOUT16,ENET_1G_RX_ER,EWM_OUT_B,FLEXSPI2_A_DATA05,GPIO2_IO28,FLEXSPI1_A_DQS,,WDOG1_B,TMR3_TIMER1,GPIO8_IO28,,, -GPIO_EMC_B2_19,SEMC_CLKX00,ENET_MDC,ENET_1G_MDC,ENET_1G_REF_CLK,FLEXSPI2_A_DATA06,GPIO2_IO29,,,ENET_QOS_MDC,TMR3_TIMER2,GPIO8_IO29,,, -GPIO_EMC_B2_20,SEMC_CLKX01,ENET_MDIO,ENET_1G_MDIO,ENET_QOS_REF_CLK,FLEXSPI2_A_DATA07,GPIO2_IO30,,,ENET_QOS_MDIO,TMR3_TIMER3,GPIO8_IO30,,, -GPIO_SNVS_00_DIG,SNVS_TAMPER0,,,,,GPIO13_IO03,,,,,,,, -GPIO_SNVS_01_DIG,SNVS_TAMPER1,,,,,GPIO13_IO04,,,,,,,, -GPIO_SNVS_02_DIG,SNVS_TAMPER2,,,,,GPIO13_IO05,,,,,,,, -GPIO_SNVS_03_DIG,SNVS_TAMPER3,,,,,GPIO13_IO06,,,,,,,, -GPIO_SNVS_04_DIG,SNVS_TAMPER4,,,,,GPIO13_IO07,,,,,,,, -GPIO_SNVS_05_DIG,SNVS_TAMPER5,,,,,GPIO13_IO08,,,,,,,, -GPIO_SNVS_06_DIG,SNVS_TAMPER6,,,,,GPIO13_IO09,,,,,,,, -GPIO_SNVS_07_DIG,SNVS_TAMPER7,,,,,GPIO13_IO10,,,,,,,, -GPIO_SNVS_08_DIG,SNVS_TAMPER8,,,,,GPIO13_IO11,,,,,,,, -GPIO_SNVS_09_DIG,SNVS_TAMPER9,,,,,GPIO13_IO12,,,,,,,, -GPIO_LPSR_00,FLEXCAN3_TX,MIC_CLK,MQS_RIGHT,ARM_CM4_EVENTO,,GPIO6_IO00,LPUART12_TXD,SAI4_MCLK,,,GPIO12_IO00,,, -GPIO_LPSR_01,FLEXCAN3_RX,MIC_BITSTREAM0,MQS_LEFT,ARM_CM4_EVENTI,,GPIO6_IO01,LPUART12_RXD,,,,GPIO12_IO01,,, -GPIO_LPSR_02,SRC_BOOT_MODE00,LPSPI5_SCK,SAI4_TX_DATA,MQS_RIGHT,,GPIO6_IO02,,,,,GPIO12_IO02,,, -GPIO_LPSR_03,SRC_BOOT_MODE01,LPSPI5_PCS0,SAI4_TX_SYNC,MQS_LEFT,,GPIO6_IO03,,,,,GPIO12_IO03,,, -GPIO_LPSR_04,LPI2C5_SDA,LPSPI5_SOUT,SAI4_TX_BCLK,LPUART12_RTS_B,,GPIO6_IO04,LPUART11_TXD,,,,GPIO12_IO04,,, -GPIO_LPSR_05,LPI2C5_SCL,LPSPI5_SIN,SAI4_MCLK,LPUART12_CTS_B,,GPIO6_IO05,LPUART11_RXD,NMI_GLUE_NMI,,,GPIO12_IO05,,, -GPIO_LPSR_06,LPI2C6_SDA,,SAI4_RX_DATA,LPUART12_TXD,LPSPI6_PCS3,GPIO6_IO06,FLEXCAN3_TX,PIT2_TRIGGER3,LPSPI5_PCS1,,GPIO12_IO06,,, -GPIO_LPSR_07,LPI2C6_SCL,,SAI4_RX_BCLK,LPUART12_RXD,LPSPI6_PCS2,GPIO6_IO07,FLEXCAN3_RX,PIT2_TRIGGER2,LPSPI5_PCS2,,GPIO12_IO07,,, -GPIO_LPSR_08,LPUART11_TXD,FLEXCAN3_TX,SAI4_RX_SYNC,MIC_CLK,LPSPI6_PCS1,GPIO6_IO08,LPI2C5_SDA,PIT2_TRIGGER1,LPSPI5_PCS3,,GPIO12_IO08,,, -GPIO_LPSR_09,LPUART11_RXD,FLEXCAN3_RX,PIT2_TRIGGER0,MIC_BITSTREAM0,LPSPI6_PCS0,GPIO6_IO09,LPI2C5_SCL,SAI4_TX_DATA,,,GPIO12_IO09,,, -GPIO_LPSR_10,JTAG_MUX_TRSTB,LPUART11_CTS_B,LPI2C6_SDA,MIC_BITSTREAM1,LPSPI6_SCK,GPIO6_IO10,LPI2C5_SCLS,SAI4_TX_SYNC,LPUART12_TXD,,GPIO12_IO10,,, -GPIO_LPSR_11,JTAG_MUX_TDO,LPUART11_RTS_B,LPI2C6_SCL,MIC_BITSTREAM2,LPSPI6_SOUT,GPIO6_IO11,LPI2C5_SDAS,ARM_TRACE_SWO,LPUART12_RXD,,GPIO12_IO11,,, -GPIO_LPSR_12,JTAG_MUX_TDI,PIT2_TRIGGER0,,MIC_BITSTREAM3,LPSPI6_SIN,GPIO6_IO12,LPI2C5_HREQ,SAI4_TX_BCLK,LPSPI5_SCK,,GPIO12_IO12,,, -GPIO_LPSR_13,JTAG_MUX_MOD,MIC_BITSTREAM1,PIT2_TRIGGER1,,,GPIO6_IO13,,SAI4_RX_DATA,LPSPI5_PCS0,,GPIO12_IO13,,, -GPIO_LPSR_14,JTAG_MUX_TCK,MIC_BITSTREAM2,PIT2_TRIGGER2,,,GPIO6_IO14,,SAI4_RX_BCLK,LPSPI5_SOUT,,GPIO12_IO14,,, -GPIO_LPSR_15,JTAG_MUX_TMS,MIC_BITSTREAM3,PIT2_TRIGGER3,,,GPIO6_IO15,,SAI4_RX_SYNC,LPSPI5_SIN,,GPIO12_IO15,,, -WAKEUP_DIG,,,,,,GPIO13_IO00,,NMI_GLUE_NMI,,,,,, -PMIC_ON_REQ_DIG,SNVS_LP_PMIC_ON_REQ,,,,,GPIO13_IO01,,,,,,,, -PMIC_STBY_REQ_DIG,CCM_PMIC_VSTBY_REQ,,,,,GPIO13_IO02,,,,,,,, -GPIO_DISP_B1_00,VIDEO_MUX_LCDIF_CLK,ENET_1G_RX_EN,,TMR1_TIMER0,XBAR1_INOUT26,GPIO4_IO21,,,ENET_QOS_RX_EN,,GPIO10_IO21,,, -GPIO_DISP_B1_01,VIDEO_MUX_LCDIF_ENABLE,ENET_1G_RX_CLK,ENET_1G_RX_ER,TMR1_TIMER1,XBAR1_INOUT27,GPIO4_IO22,,,ENET_QOS_RX_CLK,ENET_QOS_RX_ER,GPIO10_IO22,,, -GPIO_DISP_B1_02,VIDEO_MUX_LCDIF_HSYNC,ENET_1G_RX_DATA00,LPI2C3_SCL,TMR1_TIMER2,XBAR1_INOUT28,GPIO4_IO23,,,ENET_QOS_RX_DATA00,LPUART1_TXD,GPIO10_IO23,,, -GPIO_DISP_B1_03,VIDEO_MUX_LCDIF_VSYNC,ENET_1G_RX_DATA01,LPI2C3_SDA,TMR2_TIMER0,XBAR1_INOUT29,GPIO4_IO24,,,ENET_QOS_RX_DATA01,LPUART1_RXD,GPIO10_IO24,,, -GPIO_DISP_B1_04,VIDEO_MUX_LCDIF_DATA00,ENET_1G_RX_DATA02,LPUART4_RXD,TMR2_TIMER1,XBAR1_INOUT30,GPIO4_IO25,,,ENET_QOS_RX_DATA02,LPSPI3_SCK,GPIO10_IO25,,, -GPIO_DISP_B1_05,VIDEO_MUX_LCDIF_DATA01,ENET_1G_RX_DATA03,LPUART4_CTS_B,TMR2_TIMER2,XBAR1_INOUT31,GPIO4_IO26,,,ENET_QOS_RX_DATA03,LPSPI3_SIN,GPIO10_IO26,,, -GPIO_DISP_B1_06,VIDEO_MUX_LCDIF_DATA02,ENET_1G_TX_DATA03,LPUART4_TXD,TMR3_TIMER0,XBAR1_INOUT32,GPIO4_IO27,SRC_BT_CFG00,,ENET_QOS_TX_DATA03,LPSPI3_SOUT,GPIO10_IO27,,, -GPIO_DISP_B1_07,VIDEO_MUX_LCDIF_DATA03,ENET_1G_TX_DATA02,LPUART4_RTS_B,TMR3_TIMER1,XBAR1_INOUT33,GPIO4_IO28,SRC_BT_CFG01,,ENET_QOS_TX_DATA02,LPSPI3_PCS0,GPIO10_IO28,,, -GPIO_DISP_B1_08,VIDEO_MUX_LCDIF_DATA04,ENET_1G_TX_DATA01,USDHC1_CD_B,TMR3_TIMER2,XBAR1_INOUT34,GPIO4_IO29,SRC_BT_CFG02,,ENET_QOS_TX_DATA01,LPSPI3_PCS1,GPIO10_IO29,,, -GPIO_DISP_B1_09,VIDEO_MUX_LCDIF_DATA05,ENET_1G_TX_DATA00,USDHC1_WP,TMR4_TIMER0,XBAR1_INOUT35,GPIO4_IO30,SRC_BT_CFG03,,ENET_QOS_TX_DATA00,LPSPI3_PCS2,GPIO10_IO30,,, -GPIO_DISP_B1_10,VIDEO_MUX_LCDIF_DATA06,ENET_1G_TX_EN,USDHC1_RESET_B,TMR4_TIMER1,XBAR1_INOUT36,GPIO4_IO31,SRC_BT_CFG04,,ENET_QOS_TX_EN,LPSPI3_PCS3,GPIO10_IO31,,, -GPIO_DISP_B1_11,VIDEO_MUX_LCDIF_DATA07,ENET_1G_TX_CLK_IO,ENET_1G_REF_CLK,TMR4_TIMER2,XBAR1_INOUT37,GPIO5_IO00,SRC_BT_CFG05,,ENET_QOS_TX_CLK,ENET_QOS_REF_CLK,GPIO11_IO00,,, -GPIO_DISP_B2_00,VIDEO_MUX_LCDIF_DATA08,WDOG1_B,MQS_RIGHT,ENET_1G_TX_ER,SAI1_TX_DATA03,GPIO5_IO01,SRC_BT_CFG06,,ENET_QOS_TX_ER,,GPIO11_IO01,,, -GPIO_DISP_B2_01,VIDEO_MUX_LCDIF_DATA09,USDHC1_VSELECT,MQS_LEFT,WDOG2_B,SAI1_TX_DATA02,GPIO5_IO02,SRC_BT_CFG07,,EWM_OUT_B,CCM_ENET_REF_CLK_25M,GPIO11_IO02,,, -GPIO_DISP_B2_02,VIDEO_MUX_LCDIF_DATA10,ENET_TX_DATA00,PIT1_TRIGGER3,ARM_TRACE00,SAI1_TX_DATA01,GPIO5_IO03,SRC_BT_CFG08,,ENET_QOS_TX_DATA00,,GPIO11_IO03,,, -GPIO_DISP_B2_03,VIDEO_MUX_LCDIF_DATA11,ENET_TX_DATA01,PIT1_TRIGGER2,ARM_TRACE01,SAI1_MCLK,GPIO5_IO04,SRC_BT_CFG09,,ENET_QOS_TX_DATA01,,GPIO11_IO04,,, -GPIO_DISP_B2_04,VIDEO_MUX_LCDIF_DATA12,ENET_TX_EN,PIT1_TRIGGER1,ARM_TRACE02,SAI1_RX_SYNC,GPIO5_IO05,SRC_BT_CFG10,,ENET_QOS_TX_EN,,GPIO11_IO05,,, -GPIO_DISP_B2_05,VIDEO_MUX_LCDIF_DATA13,ENET_TX_CLK,ENET_REF_CLK,ARM_TRACE03,SAI1_RX_BCLK,GPIO5_IO06,SRC_BT_CFG11,,ENET_QOS_TX_CLK,,GPIO11_IO06,,, -GPIO_DISP_B2_06,VIDEO_MUX_LCDIF_DATA14,ENET_RX_DATA00,LPUART7_TXD,ARM_TRACE_CLK,SAI1_RX_DATA00,GPIO5_IO07,,,ENET_QOS_RX_DATA00,,GPIO11_IO07,,, -GPIO_DISP_B2_07,VIDEO_MUX_LCDIF_DATA15,ENET_RX_DATA01,LPUART7_RXD,ARM_TRACE_SWO,SAI1_TX_DATA00,GPIO5_IO08,,,ENET_QOS_RX_DATA01,,GPIO11_IO08,,, -GPIO_DISP_B2_08,VIDEO_MUX_LCDIF_DATA16,ENET_RX_EN,LPUART8_TXD,ARM_CM7_EVENTO,SAI1_TX_BCLK,GPIO5_IO09,,,ENET_QOS_RX_EN,LPUART1_TXD,GPIO11_IO09,,, -GPIO_DISP_B2_09,VIDEO_MUX_LCDIF_DATA17,ENET_RX_ER,LPUART8_RXD,ARM_CM7_EVENTI,SAI1_TX_SYNC,GPIO5_IO10,,,ENET_QOS_RX_ER,LPUART1_RXD,GPIO11_IO10,,, -GPIO_DISP_B2_10,VIDEO_MUX_LCDIF_DATA18,EMVSIM2_IO,LPUART2_TXD,WDOG2_RESET_B_DEB,XBAR1_INOUT38,GPIO5_IO11,LPI2C3_SCL,,ENET_QOS_RX_ER,SPDIF_IN,GPIO11_IO11,,, -GPIO_DISP_B2_11,VIDEO_MUX_LCDIF_DATA19,EMVSIM2_CLK,LPUART2_RXD,WDOG1_RESET_B_DEB,XBAR1_INOUT39,GPIO5_IO12,LPI2C3_SDA,,ENET_QOS_CRS,SPDIF_OUT,GPIO11_IO12,,, -GPIO_DISP_B2_12,VIDEO_MUX_LCDIF_DATA20,EMVSIM2_RST,FLEXCAN1_TX,LPUART2_CTS_B,XBAR1_INOUT40,GPIO5_IO13,LPI2C4_SCL,,ENET_QOS_COL,LPSPI4_SCK,GPIO11_IO13,,, -GPIO_DISP_B2_13,VIDEO_MUX_LCDIF_DATA21,EMVSIM2_SVEN,FLEXCAN1_RX,LPUART2_RTS_B,ENET_REF_CLK,GPIO5_IO14,LPI2C4_SDA,,ENET_QOS_1588_EVENT0_OUT,LPSPI4_SIN,GPIO11_IO14,,, -GPIO_DISP_B2_14,VIDEO_MUX_LCDIF_DATA22,EMVSIM2_PD,WDOG2_B,VIDEO_MUX_EXT_DCIC1,ENET_1G_REF_CLK,GPIO5_IO15,FLEXCAN1_TX,,ENET_QOS_1588_EVENT0_IN,LPSPI4_SOUT,GPIO11_IO15,,, -GPIO_DISP_B2_15,VIDEO_MUX_LCDIF_DATA23,EMVSIM2_POWER_FAIL,WDOG1_B,VIDEO_MUX_EXT_DCIC2,PIT1_TRIGGER0,GPIO5_IO16,FLEXCAN1_RX,,ENET_QOS_1588_EVENT0_AUX_IN,LPSPI4_PCS0,GPIO11_IO16,,, -GPIO_AD_00,EMVSIM1_IO,FLEXCAN2_TX,ENET_1G_1588_EVENT1_IN,GPT2_CAPTURE1,FLEXPWM1_PWM0_A,GPIO2_IO31,LPUART7_TXD,,FLEXIO2_D00,FLEXSPI2_B_SS1_B,GPIO8_IO31,,ACMP1_IN1, -GPIO_AD_01,EMVSIM1_CLK,FLEXCAN2_RX,ENET_1G_1588_EVENT1_OUT,GPT2_CAPTURE2,FLEXPWM1_PWM0_B,GPIO3_IO00,LPUART7_RXD,,FLEXIO2_D01,FLEXSPI2_A_SS1_B,GPIO9_IO00,,ACMP1_IN2, -GPIO_AD_02,EMVSIM1_RST,LPUART7_CTS_B,ENET_1G_1588_EVENT2_IN,GPT2_COMPARE1,FLEXPWM1_PWM1_A,GPIO3_IO01,LPUART8_TXD,,FLEXIO2_D02,VIDEO_MUX_EXT_DCIC1,GPIO9_IO01,,ACMP1_IN3, -GPIO_AD_03,EMVSIM1_SVEN,LPUART7_RTS_B,ENET_1G_1588_EVENT2_OUT,GPT2_COMPARE2,FLEXPWM1_PWM1_B,GPIO3_IO02,LPUART8_RXD,,FLEXIO2_D03,VIDEO_MUX_EXT_DCIC2,GPIO9_IO02,,ACMP1_IN4, -GPIO_AD_04,EMVSIM1_PD,LPUART8_CTS_B,ENET_1G_1588_EVENT3_IN,GPT2_COMPARE3,FLEXPWM1_PWM2_A,GPIO3_IO03,WDOG1_B,,FLEXIO2_D04,TMR4_TIMER0,GPIO9_IO03,,ACMP2_IN1, -GPIO_AD_05,EMVSIM1_POWER_FAIL,LPUART8_RTS_B,ENET_1G_1588_EVENT3_OUT,GPT2_CLK,FLEXPWM1_PWM2_B,GPIO3_IO04,WDOG2_B,,FLEXIO2_D05,TMR4_TIMER1,GPIO9_IO04,,ACMP2_IN2, -GPIO_AD_06,USB_OTG2_OC,FLEXCAN1_TX,EMVSIM2_IO,GPT3_CAPTURE1,VIDEO_MUX_CSI_DATA15,GPIO3_IO05,ENET_1588_EVENT1_IN,,FLEXIO2_D06,TMR4_TIMER2,GPIO9_IO05,ADC1_CH0A,, -GPIO_AD_07,USB_OTG2_PWR,FLEXCAN1_RX,EMVSIM2_CLK,GPT3_CAPTURE2,VIDEO_MUX_CSI_DATA14,GPIO3_IO06,ENET_1588_EVENT1_OUT,,FLEXIO2_D07,TMR4_TIMER3,GPIO9_IO06,ADC1_CH0B,, -GPIO_AD_08,USBPHY2_OTG_ID,LPI2C1_SCL,EMVSIM2_RST,GPT3_COMPARE1,VIDEO_MUX_CSI_DATA13,GPIO3_IO07,ENET_1588_EVENT2_IN,,FLEXIO2_D08,,GPIO9_IO07,ADC1_CH1A,, -GPIO_AD_09,USBPHY1_OTG_ID,LPI2C1_SDA,EMVSIM2_SVEN,GPT3_COMPARE2,VIDEO_MUX_CSI_DATA12,GPIO3_IO08,ENET_1588_EVENT2_OUT,,FLEXIO2_D09,,GPIO9_IO08,ADC1_CH1B,, -GPIO_AD_10,USB_OTG1_PWR,LPI2C1_SCLS,EMVSIM2_PD,GPT3_COMPARE3,VIDEO_MUX_CSI_DATA11,GPIO3_IO09,ENET_1588_EVENT3_IN,,FLEXIO2_D10,,GPIO9_IO09,ADC1_CH2A,, -GPIO_AD_11,USB_OTG1_OC,LPI2C1_SDAS,EMVSIM2_POWER_FAIL,GPT3_CLK,VIDEO_MUX_CSI_DATA10,GPIO3_IO10,ENET_1588_EVENT3_OUT,,FLEXIO2_D11,,GPIO9_IO10,ADC1_CH2B,, -GPIO_AD_12,SPDIF_LOCK,LPI2C1_HREQ,GPT1_CAPTURE1,FLEXSPI1_B_DATA03,VIDEO_MUX_CSI_PIXCLK,GPIO3_IO11,ENET_TX_DATA03,,FLEXIO2_D12,EWM_OUT_B,GPIO9_IO11,"ADC1_CH3A,ADC2_CH3A",, -GPIO_AD_13,SPDIF_SR_CLK,PIT1_TRIGGER0,GPT1_CAPTURE2,FLEXSPI1_B_DATA02,VIDEO_MUX_CSI_MCLK,GPIO3_IO12,ENET_TX_DATA02,,FLEXIO2_D13,REF_CLK_32K,GPIO9_IO12,"ADC1_CH3B,ADC2_CH3B",, -GPIO_AD_14,SPDIF_EXT_CLK,REF_CLK_24M,GPT1_COMPARE1,FLEXSPI1_B_DATA01,VIDEO_MUX_CSI_VSYNC,GPIO3_IO13,ENET_RX_CLK,,FLEXIO2_D14,CCM_ENET_REF_CLK_25M,GPIO9_IO13,"ADC1_CH4A,ADC2_CH4A",, -GPIO_AD_15,SPDIF_IN,LPUART10_TXD,GPT1_COMPARE2,FLEXSPI1_B_DATA00,VIDEO_MUX_CSI_HSYNC,GPIO3_IO14,ENET_TX_ER,,FLEXIO2_D15,,GPIO9_IO14,"ADC1_CH4B,ADC2_CH4B",, -GPIO_AD_16,SPDIF_OUT,LPUART10_RXD,GPT1_COMPARE3,FLEXSPI1_B_SCLK,VIDEO_MUX_CSI_DATA09,GPIO3_IO15,ENET_RX_DATA03,,FLEXIO2_D16,ENET_1G_MDC,GPIO9_IO15,"ADC1_CH5A,ADC2_CH5A",, -GPIO_AD_17,SAI1_MCLK,ACMP1_OUT,GPT1_CLK,FLEXSPI1_A_DQS,VIDEO_MUX_CSI_DATA08,GPIO3_IO16,ENET_RX_DATA02,,FLEXIO2_D17,ENET_1G_MDIO,GPIO9_IO16,"ADC1_CH5B,ADC2_CH5B",ACMP1_OUT, -GPIO_AD_18,SAI1_RX_SYNC,ACMP2_OUT,LPSPI1_PCS1,FLEXSPI1_A_SS0_B,VIDEO_MUX_CSI_DATA07,GPIO3_IO17,ENET_CRS,,FLEXIO2_D18,LPI2C2_SCL,GPIO9_IO17,ADC2_CH0A,ACMP2_OUT, -GPIO_AD_19,SAI1_RX_BCLK,ACMP3_OUT,LPSPI1_PCS2,FLEXSPI1_A_SCLK,VIDEO_MUX_CSI_DATA06,GPIO3_IO18,ENET_COL,,FLEXIO2_D19,LPI2C2_SDA,GPIO9_IO18,ADC2_CH0B,ACMP3_OUT, -GPIO_AD_20,SAI1_RX_DATA00,ACMP4_OUT,LPSPI1_PCS3,FLEXSPI1_A_DATA00,VIDEO_MUX_CSI_DATA05,GPIO3_IO19,KPP_ROW07,,FLEXIO2_D20,ENET_QOS_1588_EVENT2_OUT,GPIO9_IO19,ADC2_CH1A,ACMP4_OUT, -GPIO_AD_21,SAI1_TX_DATA00,,LPSPI2_PCS1,FLEXSPI1_A_DATA01,VIDEO_MUX_CSI_DATA04,GPIO3_IO20,KPP_COL07,,FLEXIO2_D21,ENET_QOS_1588_EVENT2_IN,GPIO9_IO20,ADC2_CH1B,, -GPIO_AD_22,SAI1_TX_BCLK,,LPSPI2_PCS2,FLEXSPI1_A_DATA02,VIDEO_MUX_CSI_DATA03,GPIO3_IO21,KPP_ROW06,,FLEXIO2_D22,ENET_QOS_1588_EVENT3_OUT,GPIO9_IO21,ADC2_CH2A,, -GPIO_AD_23,SAI1_TX_SYNC,,LPSPI2_PCS3,FLEXSPI1_A_DATA03,VIDEO_MUX_CSI_DATA02,GPIO3_IO22,KPP_COL06,,FLEXIO2_D23,ENET_QOS_1588_EVENT3_IN,GPIO9_IO22,ADC2_CH2B,, -GPIO_AD_24,LPUART1_TXD,LPSPI2_SCK,VIDEO_MUX_CSI_DATA00,ENET_RX_EN,FLEXPWM2_PWM0_A,GPIO3_IO23,KPP_ROW05,,FLEXIO2_D24,LPI2C4_SCL,GPIO9_IO23,ADC2_CH6A,, -GPIO_AD_25,LPUART1_RXD,LPSPI2_PCS0,VIDEO_MUX_CSI_DATA01,ENET_RX_ER,FLEXPWM2_PWM0_B,GPIO3_IO24,KPP_COL05,,FLEXIO2_D25,LPI2C4_SDA,GPIO9_IO24,ADC2_CH6B,, -GPIO_AD_26,LPUART1_CTS_B,LPSPI2_SOUT,SEMC_CSX01,ENET_RX_DATA00,FLEXPWM2_PWM1_A,GPIO3_IO25,KPP_ROW04,,FLEXIO2_D26,ENET_QOS_MDC,GPIO9_IO25,,ACMP2_IN3, -GPIO_AD_27,LPUART1_RTS_B,LPSPI2_SIN,SEMC_CSX02,ENET_RX_DATA01,FLEXPWM2_PWM1_B,GPIO3_IO26,KPP_COL04,,FLEXIO2_D27,ENET_QOS_MDIO,GPIO9_IO26,,ACMP2_IN4, -GPIO_AD_28,LPSPI1_SCK,LPUART5_TXD,SEMC_CSX03,ENET_TX_EN,FLEXPWM2_PWM2_A,GPIO3_IO27,KPP_ROW03,,FLEXIO2_D28,VIDEO_MUX_EXT_DCIC1,GPIO9_IO27,,ACMP3_IN1, -GPIO_AD_29,LPSPI1_PCS0,LPUART5_RXD,ENET_REF_CLK,ENET_TX_CLK,FLEXPWM2_PWM2_B,GPIO3_IO28,KPP_COL03,,FLEXIO2_D29,VIDEO_MUX_EXT_DCIC2,GPIO9_IO28,,ACMP3_IN2, -GPIO_AD_30,LPSPI1_SOUT,USB_OTG2_OC,FLEXCAN2_TX,ENET_TX_DATA00,LPUART3_TXD,GPIO3_IO29,KPP_ROW02,,FLEXIO2_D30,WDOG2_RESET_B_DEB,GPIO9_IO29,,ACMP3_IN3, -GPIO_AD_31,LPSPI1_SIN,USB_OTG2_PWR,FLEXCAN2_RX,ENET_TX_DATA01,LPUART3_RXD,GPIO3_IO30,KPP_COL02,,FLEXIO2_D31,WDOG1_RESET_B_DEB,GPIO9_IO30,,ACMP3_IN4, -GPIO_AD_32,LPI2C1_SCL,USBPHY2_OTG_ID,PGMC_PMIC_RDY,ENET_MDC,USDHC1_CD_B,GPIO3_IO31,KPP_ROW01,,LPUART10_TXD,ENET_1G_MDC,GPIO9_IO31,,ACMP4_IN1, -GPIO_AD_33,LPI2C1_SDA,USBPHY1_OTG_ID,XBAR1_INOUT17,ENET_MDIO,USDHC1_WP,GPIO4_IO00,KPP_COL01,,LPUART10_RXD,ENET_1G_MDIO,GPIO10_IO00,,ACMP4_IN2, -GPIO_AD_34,ENET_1G_1588_EVENT0_IN,USB_OTG1_PWR,XBAR1_INOUT18,ENET_1588_EVENT0_IN,USDHC1_VSELECT,GPIO4_IO01,KPP_ROW00,,LPUART10_CTS_B,WDOG1_ANY,GPIO10_IO01,,ACMP4_IN3, -GPIO_AD_35,ENET_1G_1588_EVENT0_OUT,USB_OTG1_OC,XBAR1_INOUT19,ENET_1588_EVENT0_OUT,USDHC1_RESET_B,GPIO4_IO02,KPP_COL00,,LPUART10_RTS_B,FLEXSPI1_B_SS1_B,GPIO10_IO02,,ACMP4_IN4, +Pad,ALT0,ALT1,ALT2,ALT3,ALT4,ALT5,ALT6,ALT7,ALT8,ALT9,ALT10,ALT11,ADC,ACMP,Default +GPIO_SD_B1_00,USDHC1_CMD,,XBAR1_INOUT20,GPT4_CAPTURE1,,GPIO4_IO03,FLEXSPI2_A_SS0_B,,KPP_ROW07,,GPIO10_IO03,,,, +GPIO_SD_B1_01,USDHC1_CLK,,XBAR1_INOUT21,GPT4_CAPTURE2,,GPIO4_IO04,FLEXSPI2_A_SCLK,,KPP_COL07,,GPIO10_IO04,,,, +GPIO_SD_B1_02,USDHC1_DATA0,,XBAR1_INOUT22,GPT4_COMPARE1,,GPIO4_IO05,FLEXSPI2_A_DATA00,,KPP_ROW06,FLEXSPI1_A_SS1_B,GPIO10_IO05,,,, +GPIO_SD_B1_03,USDHC1_DATA1,,XBAR1_INOUT23,GPT4_COMPARE2,,GPIO4_IO06,FLEXSPI2_A_DATA01,,KPP_COL06,FLEXSPI1_B_SS1_B,GPIO10_IO06,,,, +GPIO_SD_B1_04,USDHC1_DATA2,,XBAR1_INOUT24,GPT4_COMPARE3,,GPIO4_IO07,FLEXSPI2_A_DATA02,,FLEXSPI1_B_SS0_B,ENET_QOS_1588_EVENT2_AUX_IN,GPIO10_IO07,,,, +GPIO_SD_B1_05,USDHC1_DATA3,,XBAR1_INOUT25,GPT4_CLK,,GPIO4_IO08,FLEXSPI2_A_DATA03,,FLEXSPI1_B_DQS,ENET_QOS_1588_EVENT3_AUX_IN,GPIO10_IO08,,,, +GPIO_SD_B2_00,USDHC2_DATA3,FLEXSPI1_B_DATA03,ENET_1G_RX_EN,LPUART9_TXD,LPSPI4_SCK,GPIO4_IO09,,,,,GPIO10_IO09,,,, +GPIO_SD_B2_01,USDHC2_DATA2,FLEXSPI1_B_DATA02,ENET_1G_RX_CLK,LPUART9_RXD,LPSPI4_PCS0,GPIO4_IO10,,,,,GPIO10_IO10,,,, +GPIO_SD_B2_02,USDHC2_DATA1,FLEXSPI1_B_DATA01,ENET_1G_RX_DATA00,LPUART9_CTS_B,LPSPI4_SOUT,GPIO4_IO11,,,,,GPIO10_IO11,,,, +GPIO_SD_B2_03,USDHC2_DATA0,FLEXSPI1_B_DATA00,ENET_1G_RX_DATA01,LPUART9_RTS_B,LPSPI4_SIN,GPIO4_IO12,,,,,GPIO10_IO12,,,, +GPIO_SD_B2_04,USDHC2_CLK,FLEXSPI1_B_SCLK,ENET_1G_RX_DATA02,FLEXSPI1_A_SS1_B,LPSPI4_PCS1,GPIO4_IO13,,,,,GPIO10_IO13,,,, +GPIO_SD_B2_05,USDHC2_CMD,FLEXSPI1_A_DQS,ENET_1G_RX_DATA03,FLEXSPI1_B_SS0_B,LPSPI4_PCS2,GPIO4_IO14,,,,,GPIO10_IO14,,,, +GPIO_SD_B2_06,USDHC2_RESET_B,FLEXSPI1_A_SS0_B,ENET_1G_TX_DATA03,LPSPI4_PCS3,GPT6_CAPTURE1,GPIO4_IO15,,,,,GPIO10_IO15,,,, +GPIO_SD_B2_07,USDHC2_STROBE,FLEXSPI1_A_SCLK,ENET_1G_TX_DATA02,LPUART3_CTS_B,GPT6_CAPTURE2,GPIO4_IO16,LPSPI2_SCK,,ENET_TX_ER,ENET_QOS_REF_CLK,GPIO10_IO16,,,, +GPIO_SD_B2_08,USDHC2_DATA4,FLEXSPI1_A_DATA00,ENET_1G_TX_DATA01,LPUART3_RTS_B,GPT6_COMPARE1,GPIO4_IO17,LPSPI2_PCS0,,,,GPIO10_IO17,,,, +GPIO_SD_B2_09,USDHC2_DATA5,FLEXSPI1_A_DATA01,ENET_1G_TX_DATA00,LPUART5_CTS_B,GPT6_COMPARE2,GPIO4_IO18,LPSPI2_SOUT,,,,GPIO10_IO18,,,, +GPIO_SD_B2_10,USDHC2_DATA6,FLEXSPI1_A_DATA02,ENET_1G_TX_EN,LPUART5_RTS_B,GPT6_COMPARE3,GPIO4_IO19,LPSPI2_SIN,,,,GPIO10_IO19,,,, +GPIO_SD_B2_11,USDHC2_DATA7,FLEXSPI1_A_DATA03,ENET_1G_TX_CLK_IO,ENET_1G_REF_CLK,GPT6_CLK,GPIO4_IO20,LPSPI2_PCS1,,,,GPIO10_IO20,,,, +GPIO_EMC_B1_00,SEMC_DATA00,FLEXPWM4_PWM0_A,,,,GPIO1_IO00,,,FLEXIO1_D00,,GPIO7_IO00,,,, +GPIO_EMC_B1_01,SEMC_DATA01,FLEXPWM4_PWM0_B,,,,GPIO1_IO01,,,FLEXIO1_D01,,GPIO7_IO01,,,, +GPIO_EMC_B1_02,SEMC_DATA02,FLEXPWM4_PWM1_A,,,,GPIO1_IO02,,,FLEXIO1_D02,,GPIO7_IO02,,,, +GPIO_EMC_B1_03,SEMC_DATA03,FLEXPWM4_PWM1_B,,,,GPIO1_IO03,,,FLEXIO1_D03,,GPIO7_IO03,,,, +GPIO_EMC_B1_04,SEMC_DATA04,FLEXPWM4_PWM2_A,,,,GPIO1_IO04,,,FLEXIO1_D04,,GPIO7_IO04,,,, +GPIO_EMC_B1_05,SEMC_DATA05,FLEXPWM4_PWM2_B,,,,GPIO1_IO05,,,FLEXIO1_D05,,GPIO7_IO05,,,, +GPIO_EMC_B1_06,SEMC_DATA06,FLEXPWM2_PWM0_A,,,,GPIO1_IO06,,,FLEXIO1_D06,,GPIO7_IO06,,,, +GPIO_EMC_B1_07,SEMC_DATA07,FLEXPWM2_PWM0_B,,,,GPIO1_IO07,,,FLEXIO1_D07,,GPIO7_IO07,,,, +GPIO_EMC_B1_08,SEMC_DM00,FLEXPWM2_PWM1_A,,,,GPIO1_IO08,,,FLEXIO1_D08,,GPIO7_IO08,,,, +GPIO_EMC_B1_09,SEMC_ADDR00,FLEXPWM2_PWM1_B,GPT5_CAPTURE1,,,GPIO1_IO09,,,FLEXIO1_D09,,GPIO7_IO09,,,, +GPIO_EMC_B1_10,SEMC_ADDR01,FLEXPWM2_PWM2_A,GPT5_CAPTURE2,,,GPIO1_IO10,,,FLEXIO1_D10,,GPIO7_IO10,,,, +GPIO_EMC_B1_11,SEMC_ADDR02,FLEXPWM2_PWM2_B,GPT5_COMPARE1,,,GPIO1_IO11,,,FLEXIO1_D11,,GPIO7_IO11,,,, +GPIO_EMC_B1_12,SEMC_ADDR03,XBAR1_INOUT04,GPT5_COMPARE2,,,GPIO1_IO12,,,FLEXIO1_D12,,GPIO7_IO12,,,, +GPIO_EMC_B1_13,SEMC_ADDR04,XBAR1_INOUT05,GPT5_COMPARE3,,,GPIO1_IO13,,,FLEXIO1_D13,,GPIO7_IO13,,,, +GPIO_EMC_B1_14,SEMC_ADDR05,XBAR1_INOUT06,GPT5_CLK,,,GPIO1_IO14,,,FLEXIO1_D14,,GPIO7_IO14,,,, +GPIO_EMC_B1_15,SEMC_ADDR06,XBAR1_INOUT07,,,,GPIO1_IO15,,,FLEXIO1_D15,,GPIO7_IO15,,,, +GPIO_EMC_B1_16,SEMC_ADDR07,XBAR1_INOUT08,,,,GPIO1_IO16,,,FLEXIO1_D16,,GPIO7_IO16,,,, +GPIO_EMC_B1_17,SEMC_ADDR08,FLEXPWM4_PWM3_A,TMR1_TIMER0,,,GPIO1_IO17,,,FLEXIO1_D17,,GPIO7_IO17,,,, +GPIO_EMC_B1_18,SEMC_ADDR09,FLEXPWM4_PWM3_B,TMR2_TIMER0,,,GPIO1_IO18,,,FLEXIO1_D18,,GPIO7_IO18,,,, +GPIO_EMC_B1_19,SEMC_ADDR11,FLEXPWM2_PWM3_A,TMR3_TIMER0,,,GPIO1_IO19,,,FLEXIO1_D19,,GPIO7_IO19,,,, +GPIO_EMC_B1_20,SEMC_ADDR12,FLEXPWM2_PWM3_B,TMR4_TIMER0,,,GPIO1_IO20,,,FLEXIO1_D20,,GPIO7_IO20,,,, +GPIO_EMC_B1_21,SEMC_BA0,FLEXPWM3_PWM3_A,,,,GPIO1_IO21,,,FLEXIO1_D21,,GPIO7_IO21,,,, +GPIO_EMC_B1_22,SEMC_BA1,FLEXPWM3_PWM3_B,,,,GPIO1_IO22,,,FLEXIO1_D22,,GPIO7_IO22,,,, +GPIO_EMC_B1_23,SEMC_ADDR10,FLEXPWM1_PWM0_A,,,,GPIO1_IO23,,,FLEXIO1_D23,,GPIO7_IO23,,,, +GPIO_EMC_B1_24,SEMC_CAS,FLEXPWM1_PWM0_B,,,,GPIO1_IO24,,,FLEXIO1_D24,,GPIO7_IO24,,,, +GPIO_EMC_B1_25,SEMC_RAS,FLEXPWM1_PWM1_A,,,,GPIO1_IO25,,,FLEXIO1_D25,,GPIO7_IO25,,,, +GPIO_EMC_B1_26,SEMC_CLK,FLEXPWM1_PWM1_B,,,,GPIO1_IO26,,,FLEXIO1_D26,,GPIO7_IO26,,,, +GPIO_EMC_B1_27,SEMC_CKE,FLEXPWM1_PWM2_A,,,,GPIO1_IO27,,,FLEXIO1_D27,,GPIO7_IO27,,,, +GPIO_EMC_B1_28,SEMC_WE,FLEXPWM1_PWM2_B,,,,GPIO1_IO28,,,FLEXIO1_D28,,GPIO7_IO28,,,, +GPIO_EMC_B1_29,SEMC_CS0,FLEXPWM3_PWM0_A,,,,GPIO1_IO29,,,FLEXIO1_D29,,GPIO7_IO29,,,, +GPIO_EMC_B1_30,SEMC_DATA08,FLEXPWM3_PWM0_B,,,,GPIO1_IO30,,,FLEXIO1_D30,,GPIO7_IO30,,,, +GPIO_EMC_B1_31,SEMC_DATA09,FLEXPWM3_PWM1_A,,,,GPIO1_IO31,,,FLEXIO1_D31,,GPIO7_IO31,,,, +GPIO_EMC_B1_32,SEMC_DATA10,FLEXPWM3_PWM1_B,,,,GPIO2_IO00,,,,,GPIO8_IO00,,,, +GPIO_EMC_B1_33,SEMC_DATA11,FLEXPWM3_PWM2_A,,,,GPIO2_IO01,,,,,GPIO8_IO01,,,, +GPIO_EMC_B1_34,SEMC_DATA12,FLEXPWM3_PWM2_B,,,,GPIO2_IO02,,,,,GPIO8_IO02,,,, +GPIO_EMC_B1_35,SEMC_DATA13,XBAR1_INOUT09,,,,GPIO2_IO03,,,,,GPIO8_IO03,,,, +GPIO_EMC_B1_36,SEMC_DATA14,XBAR1_INOUT10,,,,GPIO2_IO04,,,,,GPIO8_IO04,,,, +GPIO_EMC_B1_37,SEMC_DATA15,XBAR1_INOUT11,,,,GPIO2_IO05,,,,,GPIO8_IO05,,,, +GPIO_EMC_B1_38,SEMC_DM01,FLEXPWM1_PWM3_A,TMR1_TIMER1,,,GPIO2_IO06,,,,,GPIO8_IO06,,,, +GPIO_EMC_B1_39,SEMC_DQS,FLEXPWM1_PWM3_B,TMR2_TIMER1,,,GPIO2_IO07,,,,,GPIO8_IO07,,,, +GPIO_EMC_B1_40,SEMC_RDY,XBAR1_INOUT12,MQS_RIGHT,LPUART6_TXD,,GPIO2_IO08,,ENET_1G_MDC,,CCM_CLKO1,GPIO8_IO08,,,, +GPIO_EMC_B1_41,SEMC_CSX00,XBAR1_INOUT13,MQS_LEFT,LPUART6_RXD,FLEXSPI2_B_DATA07,GPIO2_IO09,,ENET_1G_MDIO,,CCM_CLKO2,GPIO8_IO09,,,, +GPIO_EMC_B2_00,SEMC_DATA16,CCM_ENET_REF_CLK_25M,TMR3_TIMER1,LPUART6_CTS_B,FLEXSPI2_B_DATA06,GPIO2_IO10,XBAR1_INOUT20,ENET_QOS_1588_EVENT1_OUT,LPSPI1_SCK,LPI2C2_SCL,GPIO8_IO10,FLEXPWM3_PWM0_A,,, +GPIO_EMC_B2_01,SEMC_DATA17,USDHC2_CD_B,TMR4_TIMER1,LPUART6_RTS_B,FLEXSPI2_B_DATA05,GPIO2_IO11,XBAR1_INOUT21,ENET_QOS_1588_EVENT1_IN,LPSPI1_PCS0,LPI2C2_SDA,GPIO8_IO11,FLEXPWM3_PWM0_B,,, +GPIO_EMC_B2_02,SEMC_DATA18,USDHC2_WP,,VIDEO_MUX_CSI_DATA23,FLEXSPI2_B_DATA04,GPIO2_IO12,XBAR1_INOUT22,ENET_QOS_1588_EVENT1_AUX_IN,LPSPI1_SOUT,,GPIO8_IO12,FLEXPWM3_PWM1_A,,, +GPIO_EMC_B2_03,SEMC_DATA19,USDHC2_VSELECT,,VIDEO_MUX_CSI_DATA22,FLEXSPI2_B_DATA03,GPIO2_IO13,XBAR1_INOUT23,ENET_1G_TX_DATA03,LPSPI1_SIN,,GPIO8_IO13,FLEXPWM3_PWM1_B,,, +GPIO_EMC_B2_04,SEMC_DATA20,USDHC2_RESET_B,SAI2_MCLK,VIDEO_MUX_CSI_DATA21,FLEXSPI2_B_DATA02,GPIO2_IO14,XBAR1_INOUT24,ENET_1G_TX_DATA02,LPSPI3_SCK,,GPIO8_IO14,FLEXPWM3_PWM2_A,,, +GPIO_EMC_B2_05,SEMC_DATA21,GPT3_CLK,SAI2_RX_SYNC,VIDEO_MUX_CSI_DATA20,FLEXSPI2_B_DATA01,GPIO2_IO15,XBAR1_INOUT25,ENET_1G_RX_CLK,LPSPI3_PCS0,PIT1_TRIGGER0,GPIO8_IO15,FLEXPWM3_PWM2_B,,, +GPIO_EMC_B2_06,SEMC_DATA22,GPT3_CAPTURE1,SAI2_RX_BCLK,VIDEO_MUX_CSI_DATA19,FLEXSPI2_B_DATA00,GPIO2_IO16,XBAR1_INOUT26,ENET_1G_TX_ER,LPSPI3_SOUT,PIT1_TRIGGER1,GPIO8_IO16,FLEXPWM3_PWM3_A,,, +GPIO_EMC_B2_07,SEMC_DATA23,GPT3_CAPTURE2,SAI2_RX_DATA,VIDEO_MUX_CSI_DATA18,FLEXSPI2_B_DQS,GPIO2_IO17,XBAR1_INOUT27,ENET_1G_RX_DATA03,LPSPI3_SIN,PIT1_TRIGGER2,GPIO8_IO17,FLEXPWM3_PWM3_B,,, +GPIO_EMC_B2_08,SEMC_DM02,GPT3_COMPARE1,SAI2_TX_DATA,VIDEO_MUX_CSI_DATA17,FLEXSPI2_B_SS0_B,GPIO2_IO18,XBAR1_INOUT28,ENET_1G_RX_DATA02,LPSPI3_PCS1,PIT1_TRIGGER3,GPIO8_IO18,,,, +GPIO_EMC_B2_09,SEMC_DATA24,GPT3_COMPARE2,SAI2_TX_BCLK,VIDEO_MUX_CSI_DATA16,FLEXSPI2_B_SCLK,GPIO2_IO19,XBAR1_INOUT29,ENET_1G_CRS,LPSPI3_PCS2,TMR1_TIMER0,GPIO8_IO19,,,, +GPIO_EMC_B2_10,SEMC_DATA25,GPT3_COMPARE3,SAI2_TX_SYNC,VIDEO_MUX_CSI_FIELD,FLEXSPI2_A_SCLK,GPIO2_IO20,XBAR1_INOUT30,ENET_1G_COL,LPSPI3_PCS3,TMR1_TIMER1,GPIO8_IO20,,,, +GPIO_EMC_B2_11,SEMC_DATA26,SPDIF_IN,ENET_1G_TX_DATA00,SAI3_RX_SYNC,FLEXSPI2_A_SS0_B,GPIO2_IO21,XBAR1_INOUT31,,EMVSIM1_IO,TMR1_TIMER2,GPIO8_IO21,,,, +GPIO_EMC_B2_12,SEMC_DATA27,SPDIF_OUT,ENET_1G_TX_DATA01,SAI3_RX_BCLK,FLEXSPI2_A_DQS,GPIO2_IO22,XBAR1_INOUT32,,EMVSIM1_CLK,TMR1_TIMER3,GPIO8_IO22,,,, +GPIO_EMC_B2_13,SEMC_DATA28,,ENET_1G_TX_EN,SAI3_RX_DATA,FLEXSPI2_A_DATA00,GPIO2_IO23,XBAR1_INOUT33,,EMVSIM1_RST,TMR2_TIMER0,GPIO8_IO23,,,, +GPIO_EMC_B2_14,SEMC_DATA29,,ENET_1G_TX_CLK_IO,SAI3_TX_DATA,FLEXSPI2_A_DATA01,GPIO2_IO24,XBAR1_INOUT34,SFA_ipp_do_atx_clk_under_test,EMVSIM1_SVEN,TMR2_TIMER1,GPIO8_IO24,,,, +GPIO_EMC_B2_15,SEMC_DATA30,,ENET_1G_RX_DATA00,SAI3_TX_BCLK,FLEXSPI2_A_DATA02,GPIO2_IO25,XBAR1_INOUT35,,EMVSIM1_PD,TMR2_TIMER2,GPIO8_IO25,,,, +GPIO_EMC_B2_16,SEMC_DATA31,XBAR1_INOUT14,ENET_1G_RX_DATA01,SAI3_TX_SYNC,FLEXSPI2_A_DATA03,GPIO2_IO26,,,EMVSIM1_POWER_FAIL,TMR2_TIMER3,GPIO8_IO26,,,, +GPIO_EMC_B2_17,SEMC_DM03,XBAR1_INOUT15,ENET_1G_RX_EN,SAI3_MCLK,FLEXSPI2_A_DATA04,GPIO2_IO27,,,WDOG1_ANY,TMR3_TIMER0,GPIO8_IO27,,,, +GPIO_EMC_B2_18,SEMC_DQS4,XBAR1_INOUT16,ENET_1G_RX_ER,EWM_OUT_B,FLEXSPI2_A_DATA05,GPIO2_IO28,FLEXSPI1_A_DQS,,WDOG1_B,TMR3_TIMER1,GPIO8_IO28,,,, +GPIO_EMC_B2_19,SEMC_CLKX00,ENET_MDC,ENET_1G_MDC,ENET_1G_REF_CLK,FLEXSPI2_A_DATA06,GPIO2_IO29,,,ENET_QOS_MDC,TMR3_TIMER2,GPIO8_IO29,,,, +GPIO_EMC_B2_20,SEMC_CLKX01,ENET_MDIO,ENET_1G_MDIO,ENET_QOS_REF_CLK,FLEXSPI2_A_DATA07,GPIO2_IO30,,,ENET_QOS_MDIO,TMR3_TIMER3,GPIO8_IO30,,,, +GPIO_SNVS_00_DIG,SNVS_TAMPER0,,,,,GPIO13_IO03,,,,,,,,, +GPIO_SNVS_01_DIG,SNVS_TAMPER1,,,,,GPIO13_IO04,,,,,,,,, +GPIO_SNVS_02_DIG,SNVS_TAMPER2,,,,,GPIO13_IO05,,,,,,,,, +GPIO_SNVS_03_DIG,SNVS_TAMPER3,,,,,GPIO13_IO06,,,,,,,,, +GPIO_SNVS_04_DIG,SNVS_TAMPER4,,,,,GPIO13_IO07,,,,,,,,, +GPIO_SNVS_05_DIG,SNVS_TAMPER5,,,,,GPIO13_IO08,,,,,,,,, +GPIO_SNVS_06_DIG,SNVS_TAMPER6,,,,,GPIO13_IO09,,,,,,,,, +GPIO_SNVS_07_DIG,SNVS_TAMPER7,,,,,GPIO13_IO10,,,,,,,,, +GPIO_SNVS_08_DIG,SNVS_TAMPER8,,,,,GPIO13_IO11,,,,,,,,, +GPIO_SNVS_09_DIG,SNVS_TAMPER9,,,,,GPIO13_IO12,,,,,,,,, +GPIO_LPSR_00,FLEXCAN3_TX,MIC_CLK,MQS_RIGHT,ARM_CM4_EVENTO,,GPIO6_IO00,LPUART12_TXD,SAI4_MCLK,,,GPIO12_IO00,,,, +GPIO_LPSR_01,FLEXCAN3_RX,MIC_BITSTREAM0,MQS_LEFT,ARM_CM4_EVENTI,,GPIO6_IO01,LPUART12_RXD,,,,GPIO12_IO01,,,, +GPIO_LPSR_02,SRC_BOOT_MODE00,LPSPI5_SCK,SAI4_TX_DATA,MQS_RIGHT,,GPIO6_IO02,,,,,GPIO12_IO02,,,, +GPIO_LPSR_03,SRC_BOOT_MODE01,LPSPI5_PCS0,SAI4_TX_SYNC,MQS_LEFT,,GPIO6_IO03,,,,,GPIO12_IO03,,,, +GPIO_LPSR_04,LPI2C5_SDA,LPSPI5_SOUT,SAI4_TX_BCLK,LPUART12_RTS_B,,GPIO6_IO04,LPUART11_TXD,,,,GPIO12_IO04,,,, +GPIO_LPSR_05,LPI2C5_SCL,LPSPI5_SIN,SAI4_MCLK,LPUART12_CTS_B,,GPIO6_IO05,LPUART11_RXD,NMI_GLUE_NMI,,,GPIO12_IO05,,,, +GPIO_LPSR_06,LPI2C6_SDA,,SAI4_RX_DATA,LPUART12_TXD,LPSPI6_PCS3,GPIO6_IO06,FLEXCAN3_TX,PIT2_TRIGGER3,LPSPI5_PCS1,,GPIO12_IO06,,,, +GPIO_LPSR_07,LPI2C6_SCL,,SAI4_RX_BCLK,LPUART12_RXD,LPSPI6_PCS2,GPIO6_IO07,FLEXCAN3_RX,PIT2_TRIGGER2,LPSPI5_PCS2,,GPIO12_IO07,,,, +GPIO_LPSR_08,LPUART11_TXD,FLEXCAN3_TX,SAI4_RX_SYNC,MIC_CLK,LPSPI6_PCS1,GPIO6_IO08,LPI2C5_SDA,PIT2_TRIGGER1,LPSPI5_PCS3,,GPIO12_IO08,,,, +GPIO_LPSR_09,LPUART11_RXD,FLEXCAN3_RX,PIT2_TRIGGER0,MIC_BITSTREAM0,LPSPI6_PCS0,GPIO6_IO09,LPI2C5_SCL,SAI4_TX_DATA,,,GPIO12_IO09,,,, +GPIO_LPSR_10,JTAG_MUX_TRSTB,LPUART11_CTS_B,LPI2C6_SDA,MIC_BITSTREAM1,LPSPI6_SCK,GPIO6_IO10,LPI2C5_SCLS,SAI4_TX_SYNC,LPUART12_TXD,,GPIO12_IO10,,,, +GPIO_LPSR_11,JTAG_MUX_TDO,LPUART11_RTS_B,LPI2C6_SCL,MIC_BITSTREAM2,LPSPI6_SOUT,GPIO6_IO11,LPI2C5_SDAS,ARM_TRACE_SWO,LPUART12_RXD,,GPIO12_IO11,,,, +GPIO_LPSR_12,JTAG_MUX_TDI,PIT2_TRIGGER0,,MIC_BITSTREAM3,LPSPI6_SIN,GPIO6_IO12,LPI2C5_HREQ,SAI4_TX_BCLK,LPSPI5_SCK,,GPIO12_IO12,,,, +GPIO_LPSR_13,JTAG_MUX_MOD,MIC_BITSTREAM1,PIT2_TRIGGER1,,,GPIO6_IO13,,SAI4_RX_DATA,LPSPI5_PCS0,,GPIO12_IO13,,,, +GPIO_LPSR_14,JTAG_MUX_TCK,MIC_BITSTREAM2,PIT2_TRIGGER2,,,GPIO6_IO14,,SAI4_RX_BCLK,LPSPI5_SOUT,,GPIO12_IO14,,,, +GPIO_LPSR_15,JTAG_MUX_TMS,MIC_BITSTREAM3,PIT2_TRIGGER3,,,GPIO6_IO15,,SAI4_RX_SYNC,LPSPI5_SIN,,GPIO12_IO15,,,, +WAKEUP_DIG,,,,,,GPIO13_IO00,,NMI_GLUE_NMI,,,,,,, +PMIC_ON_REQ_DIG,SNVS_LP_PMIC_ON_REQ,,,,,GPIO13_IO01,,,,,,,,, +PMIC_STBY_REQ_DIG,CCM_PMIC_VSTBY_REQ,,,,,GPIO13_IO02,,,,,,,,, +GPIO_DISP_B1_00,VIDEO_MUX_LCDIF_CLK,ENET_1G_RX_EN,,TMR1_TIMER0,XBAR1_INOUT26,GPIO4_IO21,,,ENET_QOS_RX_EN,,GPIO10_IO21,,,, +GPIO_DISP_B1_01,VIDEO_MUX_LCDIF_ENABLE,ENET_1G_RX_CLK,ENET_1G_RX_ER,TMR1_TIMER1,XBAR1_INOUT27,GPIO4_IO22,,,ENET_QOS_RX_CLK,ENET_QOS_RX_ER,GPIO10_IO22,,,, +GPIO_DISP_B1_02,VIDEO_MUX_LCDIF_HSYNC,ENET_1G_RX_DATA00,LPI2C3_SCL,TMR1_TIMER2,XBAR1_INOUT28,GPIO4_IO23,,,ENET_QOS_RX_DATA00,LPUART1_TXD,GPIO10_IO23,,,, +GPIO_DISP_B1_03,VIDEO_MUX_LCDIF_VSYNC,ENET_1G_RX_DATA01,LPI2C3_SDA,TMR2_TIMER0,XBAR1_INOUT29,GPIO4_IO24,,,ENET_QOS_RX_DATA01,LPUART1_RXD,GPIO10_IO24,,,, +GPIO_DISP_B1_04,VIDEO_MUX_LCDIF_DATA00,ENET_1G_RX_DATA02,LPUART4_RXD,TMR2_TIMER1,XBAR1_INOUT30,GPIO4_IO25,,,ENET_QOS_RX_DATA02,LPSPI3_SCK,GPIO10_IO25,,,, +GPIO_DISP_B1_05,VIDEO_MUX_LCDIF_DATA01,ENET_1G_RX_DATA03,LPUART4_CTS_B,TMR2_TIMER2,XBAR1_INOUT31,GPIO4_IO26,,,ENET_QOS_RX_DATA03,LPSPI3_SIN,GPIO10_IO26,,,, +GPIO_DISP_B1_06,VIDEO_MUX_LCDIF_DATA02,ENET_1G_TX_DATA03,LPUART4_TXD,TMR3_TIMER0,XBAR1_INOUT32,GPIO4_IO27,SRC_BT_CFG00,,ENET_QOS_TX_DATA03,LPSPI3_SOUT,GPIO10_IO27,,,, +GPIO_DISP_B1_07,VIDEO_MUX_LCDIF_DATA03,ENET_1G_TX_DATA02,LPUART4_RTS_B,TMR3_TIMER1,XBAR1_INOUT33,GPIO4_IO28,SRC_BT_CFG01,,ENET_QOS_TX_DATA02,LPSPI3_PCS0,GPIO10_IO28,,,, +GPIO_DISP_B1_08,VIDEO_MUX_LCDIF_DATA04,ENET_1G_TX_DATA01,USDHC1_CD_B,TMR3_TIMER2,XBAR1_INOUT34,GPIO4_IO29,SRC_BT_CFG02,,ENET_QOS_TX_DATA01,LPSPI3_PCS1,GPIO10_IO29,,,, +GPIO_DISP_B1_09,VIDEO_MUX_LCDIF_DATA05,ENET_1G_TX_DATA00,USDHC1_WP,TMR4_TIMER0,XBAR1_INOUT35,GPIO4_IO30,SRC_BT_CFG03,,ENET_QOS_TX_DATA00,LPSPI3_PCS2,GPIO10_IO30,,,, +GPIO_DISP_B1_10,VIDEO_MUX_LCDIF_DATA06,ENET_1G_TX_EN,USDHC1_RESET_B,TMR4_TIMER1,XBAR1_INOUT36,GPIO4_IO31,SRC_BT_CFG04,,ENET_QOS_TX_EN,LPSPI3_PCS3,GPIO10_IO31,,,, +GPIO_DISP_B1_11,VIDEO_MUX_LCDIF_DATA07,ENET_1G_TX_CLK_IO,ENET_1G_REF_CLK,TMR4_TIMER2,XBAR1_INOUT37,GPIO5_IO00,SRC_BT_CFG05,,ENET_QOS_TX_CLK,ENET_QOS_REF_CLK,GPIO11_IO00,,,, +GPIO_DISP_B2_00,VIDEO_MUX_LCDIF_DATA08,WDOG1_B,MQS_RIGHT,ENET_1G_TX_ER,SAI1_TX_DATA03,GPIO5_IO01,SRC_BT_CFG06,,ENET_QOS_TX_ER,,GPIO11_IO01,,,, +GPIO_DISP_B2_01,VIDEO_MUX_LCDIF_DATA09,USDHC1_VSELECT,MQS_LEFT,WDOG2_B,SAI1_TX_DATA02,GPIO5_IO02,SRC_BT_CFG07,,EWM_OUT_B,CCM_ENET_REF_CLK_25M,GPIO11_IO02,,,, +GPIO_DISP_B2_02,VIDEO_MUX_LCDIF_DATA10,ENET_TX_DATA00,PIT1_TRIGGER3,ARM_TRACE00,SAI1_TX_DATA01,GPIO5_IO03,SRC_BT_CFG08,,ENET_QOS_TX_DATA00,,GPIO11_IO03,,,, +GPIO_DISP_B2_03,VIDEO_MUX_LCDIF_DATA11,ENET_TX_DATA01,PIT1_TRIGGER2,ARM_TRACE01,SAI1_MCLK,GPIO5_IO04,SRC_BT_CFG09,,ENET_QOS_TX_DATA01,,GPIO11_IO04,,,, +GPIO_DISP_B2_04,VIDEO_MUX_LCDIF_DATA12,ENET_TX_EN,PIT1_TRIGGER1,ARM_TRACE02,SAI1_RX_SYNC,GPIO5_IO05,SRC_BT_CFG10,,ENET_QOS_TX_EN,,GPIO11_IO05,,,, +GPIO_DISP_B2_05,VIDEO_MUX_LCDIF_DATA13,ENET_TX_CLK,ENET_REF_CLK,ARM_TRACE03,SAI1_RX_BCLK,GPIO5_IO06,SRC_BT_CFG11,,ENET_QOS_TX_CLK,,GPIO11_IO06,,,, +GPIO_DISP_B2_06,VIDEO_MUX_LCDIF_DATA14,ENET_RX_DATA00,LPUART7_TXD,ARM_TRACE_CLK,SAI1_RX_DATA00,GPIO5_IO07,,,ENET_QOS_RX_DATA00,,GPIO11_IO07,,,, +GPIO_DISP_B2_07,VIDEO_MUX_LCDIF_DATA15,ENET_RX_DATA01,LPUART7_RXD,ARM_TRACE_SWO,SAI1_TX_DATA00,GPIO5_IO08,,,ENET_QOS_RX_DATA01,,GPIO11_IO08,,,, +GPIO_DISP_B2_08,VIDEO_MUX_LCDIF_DATA16,ENET_RX_EN,LPUART8_TXD,ARM_CM7_EVENTO,SAI1_TX_BCLK,GPIO5_IO09,,,ENET_QOS_RX_EN,LPUART1_TXD,GPIO11_IO09,,,, +GPIO_DISP_B2_09,VIDEO_MUX_LCDIF_DATA17,ENET_RX_ER,LPUART8_RXD,ARM_CM7_EVENTI,SAI1_TX_SYNC,GPIO5_IO10,,,ENET_QOS_RX_ER,LPUART1_RXD,GPIO11_IO10,,,, +GPIO_DISP_B2_10,VIDEO_MUX_LCDIF_DATA18,EMVSIM2_IO,LPUART2_TXD,WDOG2_RESET_B_DEB,XBAR1_INOUT38,GPIO5_IO11,LPI2C3_SCL,,ENET_QOS_RX_ER,SPDIF_IN,GPIO11_IO11,,,, +GPIO_DISP_B2_11,VIDEO_MUX_LCDIF_DATA19,EMVSIM2_CLK,LPUART2_RXD,WDOG1_RESET_B_DEB,XBAR1_INOUT39,GPIO5_IO12,LPI2C3_SDA,,ENET_QOS_CRS,SPDIF_OUT,GPIO11_IO12,,,, +GPIO_DISP_B2_12,VIDEO_MUX_LCDIF_DATA20,EMVSIM2_RST,FLEXCAN1_TX,LPUART2_CTS_B,XBAR1_INOUT40,GPIO5_IO13,LPI2C4_SCL,,ENET_QOS_COL,LPSPI4_SCK,GPIO11_IO13,,,, +GPIO_DISP_B2_13,VIDEO_MUX_LCDIF_DATA21,EMVSIM2_SVEN,FLEXCAN1_RX,LPUART2_RTS_B,ENET_REF_CLK,GPIO5_IO14,LPI2C4_SDA,,ENET_QOS_1588_EVENT0_OUT,LPSPI4_SIN,GPIO11_IO14,,,, +GPIO_DISP_B2_14,VIDEO_MUX_LCDIF_DATA22,EMVSIM2_PD,WDOG2_B,VIDEO_MUX_EXT_DCIC1,ENET_1G_REF_CLK,GPIO5_IO15,FLEXCAN1_TX,,ENET_QOS_1588_EVENT0_IN,LPSPI4_SOUT,GPIO11_IO15,,,, +GPIO_DISP_B2_15,VIDEO_MUX_LCDIF_DATA23,EMVSIM2_POWER_FAIL,WDOG1_B,VIDEO_MUX_EXT_DCIC2,PIT1_TRIGGER0,GPIO5_IO16,FLEXCAN1_RX,,ENET_QOS_1588_EVENT0_AUX_IN,LPSPI4_PCS0,GPIO11_IO16,,,, +GPIO_AD_00,EMVSIM1_IO,FLEXCAN2_TX,ENET_1G_1588_EVENT1_IN,GPT2_CAPTURE1,FLEXPWM1_PWM0_A,GPIO2_IO31,LPUART7_TXD,,FLEXIO2_D00,FLEXSPI2_B_SS1_B,GPIO8_IO31,,,ACMP1_IN1, +GPIO_AD_01,EMVSIM1_CLK,FLEXCAN2_RX,ENET_1G_1588_EVENT1_OUT,GPT2_CAPTURE2,FLEXPWM1_PWM0_B,GPIO3_IO00,LPUART7_RXD,,FLEXIO2_D01,FLEXSPI2_A_SS1_B,GPIO9_IO00,,,ACMP1_IN2, +GPIO_AD_02,EMVSIM1_RST,LPUART7_CTS_B,ENET_1G_1588_EVENT2_IN,GPT2_COMPARE1,FLEXPWM1_PWM1_A,GPIO3_IO01,LPUART8_TXD,,FLEXIO2_D02,VIDEO_MUX_EXT_DCIC1,GPIO9_IO01,,,ACMP1_IN3, +GPIO_AD_03,EMVSIM1_SVEN,LPUART7_RTS_B,ENET_1G_1588_EVENT2_OUT,GPT2_COMPARE2,FLEXPWM1_PWM1_B,GPIO3_IO02,LPUART8_RXD,,FLEXIO2_D03,VIDEO_MUX_EXT_DCIC2,GPIO9_IO02,,,ACMP1_IN4, +GPIO_AD_04,EMVSIM1_PD,LPUART8_CTS_B,ENET_1G_1588_EVENT3_IN,GPT2_COMPARE3,FLEXPWM1_PWM2_A,GPIO3_IO03,WDOG1_B,,FLEXIO2_D04,TMR4_TIMER0,GPIO9_IO03,,,ACMP2_IN1, +GPIO_AD_05,EMVSIM1_POWER_FAIL,LPUART8_RTS_B,ENET_1G_1588_EVENT3_OUT,GPT2_CLK,FLEXPWM1_PWM2_B,GPIO3_IO04,WDOG2_B,,FLEXIO2_D05,TMR4_TIMER1,GPIO9_IO04,,,ACMP2_IN2, +GPIO_AD_06,USB_OTG2_OC,FLEXCAN1_TX,EMVSIM2_IO,GPT3_CAPTURE1,VIDEO_MUX_CSI_DATA15,GPIO3_IO05,ENET_1588_EVENT1_IN,,FLEXIO2_D06,TMR4_TIMER2,GPIO9_IO05,FLEXPWM1_PWM0_X,ADC1_CH0A,, +GPIO_AD_07,USB_OTG2_PWR,FLEXCAN1_RX,EMVSIM2_CLK,GPT3_CAPTURE2,VIDEO_MUX_CSI_DATA14,GPIO3_IO06,ENET_1588_EVENT1_OUT,,FLEXIO2_D07,TMR4_TIMER3,GPIO9_IO06,FLEXPWM1_PWM1_X,ADC1_CH0B,, +GPIO_AD_08,USBPHY2_OTG_ID,LPI2C1_SCL,EMVSIM2_RST,GPT3_COMPARE1,VIDEO_MUX_CSI_DATA13,GPIO3_IO07,ENET_1588_EVENT2_IN,,FLEXIO2_D08,,GPIO9_IO07,FLEXPWM1_PWM2_X,ADC1_CH1A,, +GPIO_AD_09,USBPHY1_OTG_ID,LPI2C1_SDA,EMVSIM2_SVEN,GPT3_COMPARE2,VIDEO_MUX_CSI_DATA12,GPIO3_IO08,ENET_1588_EVENT2_OUT,,FLEXIO2_D09,,GPIO9_IO08,FLEXPWM1_PWM3_X,ADC1_CH1B,, +GPIO_AD_10,USB_OTG1_PWR,LPI2C1_SCLS,EMVSIM2_PD,GPT3_COMPARE3,VIDEO_MUX_CSI_DATA11,GPIO3_IO09,ENET_1588_EVENT3_IN,,FLEXIO2_D10,,GPIO9_IO09,FLEXPWM2_PWM0_X,ADC1_CH2A,, +GPIO_AD_11,USB_OTG1_OC,LPI2C1_SDAS,EMVSIM2_POWER_FAIL,GPT3_CLK,VIDEO_MUX_CSI_DATA10,GPIO3_IO10,ENET_1588_EVENT3_OUT,,FLEXIO2_D11,,GPIO9_IO10,FLEXPWM2_PWM1_X,ADC1_CH2B,, +GPIO_AD_12,SPDIF_LOCK,LPI2C1_HREQ,GPT1_CAPTURE1,FLEXSPI1_B_DATA03,VIDEO_MUX_CSI_PIXCLK,GPIO3_IO11,ENET_TX_DATA03,,FLEXIO2_D12,EWM_OUT_B,GPIO9_IO11,FLEXPWM2_PWM2_X,"ADC1_CH3A,ADC2_CH3A",, +GPIO_AD_13,SPDIF_SR_CLK,PIT1_TRIGGER0,GPT1_CAPTURE2,FLEXSPI1_B_DATA02,VIDEO_MUX_CSI_MCLK,GPIO3_IO12,ENET_TX_DATA02,,FLEXIO2_D13,REF_CLK_32K,GPIO9_IO12,FLEXPWM2_PWM3_X,"ADC1_CH3B,ADC2_CH3B",, +GPIO_AD_14,SPDIF_EXT_CLK,REF_CLK_24M,GPT1_COMPARE1,FLEXSPI1_B_DATA01,VIDEO_MUX_CSI_VSYNC,GPIO3_IO13,ENET_RX_CLK,,FLEXIO2_D14,CCM_ENET_REF_CLK_25M,GPIO9_IO13,FLEXPWM3_PWM0_X,"ADC1_CH4A,ADC2_CH4A",, +GPIO_AD_15,SPDIF_IN,LPUART10_TXD,GPT1_COMPARE2,FLEXSPI1_B_DATA00,VIDEO_MUX_CSI_HSYNC,GPIO3_IO14,ENET_TX_ER,,FLEXIO2_D15,,GPIO9_IO14,FLEXPWM3_PWM1_X,"ADC1_CH4B,ADC2_CH4B",, +GPIO_AD_16,SPDIF_OUT,LPUART10_RXD,GPT1_COMPARE3,FLEXSPI1_B_SCLK,VIDEO_MUX_CSI_DATA09,GPIO3_IO15,ENET_RX_DATA03,,FLEXIO2_D16,ENET_1G_MDC,GPIO9_IO15,FLEXPWM3_PWM2_X,"ADC1_CH5A,ADC2_CH5A",, +GPIO_AD_17,SAI1_MCLK,ACMP1_OUT,GPT1_CLK,FLEXSPI1_A_DQS,VIDEO_MUX_CSI_DATA08,GPIO3_IO16,ENET_RX_DATA02,,FLEXIO2_D17,ENET_1G_MDIO,GPIO9_IO16,FLEXPWM3_PWM3_X,"ADC1_CH5B,ADC2_CH5B",ACMP1_OUT, +GPIO_AD_18,SAI1_RX_SYNC,ACMP2_OUT,LPSPI1_PCS1,FLEXSPI1_A_SS0_B,VIDEO_MUX_CSI_DATA07,GPIO3_IO17,ENET_CRS,,FLEXIO2_D18,LPI2C2_SCL,GPIO9_IO17,FLEXPWM4_PWM0_X,ADC2_CH0A,ACMP2_OUT, +GPIO_AD_19,SAI1_RX_BCLK,ACMP3_OUT,LPSPI1_PCS2,FLEXSPI1_A_SCLK,VIDEO_MUX_CSI_DATA06,GPIO3_IO18,ENET_COL,,FLEXIO2_D19,LPI2C2_SDA,GPIO9_IO18,FLEXPWM4_PWM1_X,ADC2_CH0B,ACMP3_OUT, +GPIO_AD_20,SAI1_RX_DATA00,ACMP4_OUT,LPSPI1_PCS3,FLEXSPI1_A_DATA00,VIDEO_MUX_CSI_DATA05,GPIO3_IO19,KPP_ROW07,,FLEXIO2_D20,ENET_QOS_1588_EVENT2_OUT,GPIO9_IO19,FLEXPWM4_PWM2_X,ADC2_CH1A,ACMP4_OUT, +GPIO_AD_21,SAI1_TX_DATA00,,LPSPI2_PCS1,FLEXSPI1_A_DATA01,VIDEO_MUX_CSI_DATA04,GPIO3_IO20,KPP_COL07,,FLEXIO2_D21,ENET_QOS_1588_EVENT2_IN,GPIO9_IO20,FLEXPWM4_PWM3_X,ADC2_CH1B,, +GPIO_AD_22,SAI1_TX_BCLK,,LPSPI2_PCS2,FLEXSPI1_A_DATA02,VIDEO_MUX_CSI_DATA03,GPIO3_IO21,KPP_ROW06,,FLEXIO2_D22,ENET_QOS_1588_EVENT3_OUT,GPIO9_IO21,,ADC2_CH2A,, +GPIO_AD_23,SAI1_TX_SYNC,,LPSPI2_PCS3,FLEXSPI1_A_DATA03,VIDEO_MUX_CSI_DATA02,GPIO3_IO22,KPP_COL06,,FLEXIO2_D23,ENET_QOS_1588_EVENT3_IN,GPIO9_IO22,,ADC2_CH2B,, +GPIO_AD_24,LPUART1_TXD,LPSPI2_SCK,VIDEO_MUX_CSI_DATA00,ENET_RX_EN,FLEXPWM2_PWM0_A,GPIO3_IO23,KPP_ROW05,,FLEXIO2_D24,LPI2C4_SCL,GPIO9_IO23,,ADC2_CH6A,, +GPIO_AD_25,LPUART1_RXD,LPSPI2_PCS0,VIDEO_MUX_CSI_DATA01,ENET_RX_ER,FLEXPWM2_PWM0_B,GPIO3_IO24,KPP_COL05,,FLEXIO2_D25,LPI2C4_SDA,GPIO9_IO24,,ADC2_CH6B,, +GPIO_AD_26,LPUART1_CTS_B,LPSPI2_SOUT,SEMC_CSX01,ENET_RX_DATA00,FLEXPWM2_PWM1_A,GPIO3_IO25,KPP_ROW04,,FLEXIO2_D26,ENET_QOS_MDC,GPIO9_IO25,USDHC2_CD_B,,ACMP2_IN3, +GPIO_AD_27,LPUART1_RTS_B,LPSPI2_SIN,SEMC_CSX02,ENET_RX_DATA01,FLEXPWM2_PWM1_B,GPIO3_IO26,KPP_COL04,,FLEXIO2_D27,ENET_QOS_MDIO,GPIO9_IO26,USDHC2_WP,,ACMP2_IN4, +GPIO_AD_28,LPSPI1_SCK,LPUART5_TXD,SEMC_CSX03,ENET_TX_EN,FLEXPWM2_PWM2_A,GPIO3_IO27,KPP_ROW03,,FLEXIO2_D28,VIDEO_MUX_EXT_DCIC1,GPIO9_IO27,USDHC2_VSELECT,,ACMP3_IN1, +GPIO_AD_29,LPSPI1_PCS0,LPUART5_RXD,ENET_REF_CLK,ENET_TX_CLK,FLEXPWM2_PWM2_B,GPIO3_IO28,KPP_COL03,,FLEXIO2_D29,VIDEO_MUX_EXT_DCIC2,GPIO9_IO28,USDHC2_RESET_B,,ACMP3_IN2, +GPIO_AD_30,LPSPI1_SOUT,USB_OTG2_OC,FLEXCAN2_TX,ENET_TX_DATA00,LPUART3_TXD,GPIO3_IO29,KPP_ROW02,,FLEXIO2_D30,WDOG2_RESET_B_DEB,GPIO9_IO29,,,ACMP3_IN3, +GPIO_AD_31,LPSPI1_SIN,USB_OTG2_PWR,FLEXCAN2_RX,ENET_TX_DATA01,LPUART3_RXD,GPIO3_IO30,KPP_COL02,,FLEXIO2_D31,WDOG1_RESET_B_DEB,GPIO9_IO30,,,ACMP3_IN4, +GPIO_AD_32,LPI2C1_SCL,USBPHY2_OTG_ID,PGMC_PMIC_RDY,ENET_MDC,USDHC1_CD_B,GPIO3_IO31,KPP_ROW01,,LPUART10_TXD,ENET_1G_MDC,GPIO9_IO31,,,ACMP4_IN1, +GPIO_AD_33,LPI2C1_SDA,USBPHY1_OTG_ID,XBAR1_INOUT17,ENET_MDIO,USDHC1_WP,GPIO4_IO00,KPP_COL01,,LPUART10_RXD,ENET_1G_MDIO,GPIO10_IO00,,,ACMP4_IN2, +GPIO_AD_34,ENET_1G_1588_EVENT0_IN,USB_OTG1_PWR,XBAR1_INOUT18,ENET_1588_EVENT0_IN,USDHC1_VSELECT,GPIO4_IO01,KPP_ROW00,,LPUART10_CTS_B,WDOG1_ANY,GPIO10_IO01,,,ACMP4_IN3, +GPIO_AD_35,ENET_1G_1588_EVENT0_OUT,USB_OTG1_OC,XBAR1_INOUT19,ENET_1588_EVENT0_OUT,USDHC1_RESET_B,GPIO4_IO02,KPP_COL00,,LPUART10_RTS_B,FLEXSPI1_B_SS1_B,GPIO10_IO02,,,ACMP4_IN4, diff --git a/ports/mimxrt/pin.h b/ports/mimxrt/pin.h index 94cd3cad4d7c1..3ed454c54bce9 100644 --- a/ports/mimxrt/pin.h +++ b/ports/mimxrt/pin.h @@ -77,6 +77,7 @@ enum { PIN_AF_MODE_ALT8, PIN_AF_MODE_ALT9, PIN_AF_MODE_ALT10, + PIN_AF_MODE_ALT11, }; enum { From 1b2f6e3d1f68f2eb1dc4d1a63c5af7fed2628577 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 15 Jan 2026 15:12:50 +1100 Subject: [PATCH 168/177] mimxrt: Increase resolution of RTC to 1/32768 seconds. Currently the mimxrt port has a resolution of only 1 second for `machine.RTC().datetime()` and `time.time_ns()`. This means (among other things) that it fails the `tests/extmod/time_time_ns.py` test, which requires requires at least 5ms of resolution. The underlying RTC hardware is just a 64-bit counter, and the HAL functions `SNVS_LP_SRTC_GetDatetime()` and `SNVS_LP_SRTC_SetDatetime()` do conversions between y/m/d/h/m/s and this 64-bit value, which counts at a rate of 32kHz. This commit changes the RTC code to access the 64-bit counter directly and therefore improve resolution of all RTC functions to 1/32768 seconds. That makes things much simpler because it a lot of places the code wants to know the number of seconds since the Epoch. Currently it uses a combination of `SNVS_LP_SRTC_GetDatetime()` and `timeutils_seconds_since_epoch()` which converts the 64-bit counter to date-time and then back to seconds. Those operations are computationally expensive. With this commit, getting the number of seconds since the Epoch is as simple as reading the 64-bit counter and dividing by 32768. We can leverage a lot of the timeutils functions to simplify everything, and make it similar to other ports like rp2. Benefits of this change: - simpler, more efficient code to get/set RTC - `machine.RTC().datetime()` now has a non-zero value in the last slot, being the number of microseconds, and has a resolution of 1/32768 seconds - `time.time_ns()` now has a resolution of 1/32768 seconds - the `test/extmod/time_time_ns.py` test now passes Signed-off-by: Damien George --- ports/mimxrt/fatfs_port.c | 14 ++--- ports/mimxrt/machine_rtc.c | 93 +++++++++++++++-------------- ports/mimxrt/mbedtls/mbedtls_port.c | 6 +- ports/mimxrt/modmachine.h | 2 + ports/mimxrt/modtime.c | 21 ++----- ports/mimxrt/mphalport.c | 12 ++-- 6 files changed, 68 insertions(+), 80 deletions(-) diff --git a/ports/mimxrt/fatfs_port.c b/ports/mimxrt/fatfs_port.c index 7b7008667ff3e..5d3b90c28d78f 100644 --- a/ports/mimxrt/fatfs_port.c +++ b/ports/mimxrt/fatfs_port.c @@ -26,14 +26,12 @@ */ #include "lib/oofatfs/ff.h" -#include "fsl_snvs_lp.h" - +#include "shared/timeutils/timeutils.h" +#include "modmachine.h" MP_WEAK DWORD get_fattime(void) { - snvs_lp_srtc_datetime_t srtcDate; - - SNVS_LP_SRTC_GetDatetime(SNVS, &srtcDate); - - return ((srtcDate.year - 1980) << 25) | (srtcDate.month << 21) | (srtcDate.day << 16) | - (srtcDate.hour << 11) | ((srtcDate.minute << 5) | (srtcDate.second / 2)); + uint64_t seconds = machine_rtc_get_seconds(); + timeutils_struct_time_t tm; + timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); + return ((tm.tm_year - 1980) << 25) | ((tm.tm_mon) << 21) | ((tm.tm_mday) << 16) | ((tm.tm_hour) << 11) | ((tm.tm_min) << 5) | (tm.tm_sec / 2); } diff --git a/ports/mimxrt/machine_rtc.c b/ports/mimxrt/machine_rtc.c index b025e392612c7..3b7aa6ed86cda 100644 --- a/ports/mimxrt/machine_rtc.c +++ b/ports/mimxrt/machine_rtc.c @@ -73,18 +73,33 @@ void machine_rtc_alarm_on() { machine_rtc_alarm_set_en(); } -uint32_t machine_rtc_get_seconds() { - uint32_t seconds = 0; - uint32_t tmp = 0; +// Returned ticks are in units of 1/32768 seconds. +uint64_t machine_rtc_get_ticks(void) { + uint64_t ticks = 0; + uint64_t tmp = 0; // Do consecutive reads until value is correct + uint32_t state = disable_irq(); do { - seconds = tmp; - tmp = (SNVS->LPSRTCMR << 17U); - tmp |= (SNVS->LPSRTCLR >> 15U); - } while (tmp != seconds); + ticks = tmp; + tmp = (uint64_t)SNVS->LPSRTCMR << 32U; + tmp |= SNVS->LPSRTCLR; + } while (tmp != ticks); + enable_irq(state); - return seconds; + return ticks; +} + +uint64_t machine_rtc_get_seconds(void) { + return machine_rtc_get_ticks() / 32768U; +} + +// Input ticks are in units of 1/32768 seconds. +static void machine_rtc_set_ticks(uint64_t ticks) { + SNVS_LP_SRTC_StopTimer(SNVS); + SNVS->LPSRTCMR = (uint32_t)(ticks >> 32U); + SNVS->LPSRTCLR = (uint32_t)ticks; + SNVS_LP_SRTC_StartTimer(SNVS); } void machine_rtc_alarm_helper(int seconds, bool repeat) { @@ -164,18 +179,9 @@ void machine_rtc_start(void) { SNVS_LP_SRTC_StartTimer(SNVS); // If the date is not set, set it to a more recent start date, // MicroPython's first commit. - snvs_lp_srtc_datetime_t srtc_date; - SNVS_LP_SRTC_GetDatetime(SNVS, &srtc_date); - if (srtc_date.year <= 1970) { - srtc_date = (snvs_lp_srtc_datetime_t) { - .year = 2013, - .month = 10, - .day = 14, - .hour = 19, - .minute = 53, - .second = 11, - }; - SNVS_LP_SRTC_SetDatetime(SNVS, &srtc_date); + if (machine_rtc_get_ticks() < 10) { + mp_timestamp_t seconds = timeutils_seconds_since_epoch(2013, 10, 14, 19, 53, 11); + machine_rtc_set_ticks((uint64_t)seconds * 32768ULL); } } @@ -190,39 +196,34 @@ static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, s 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; - SNVS_LP_SRTC_GetDatetime(SNVS, &srtc_date); - + uint64_t ticks = machine_rtc_get_ticks(); + timeutils_struct_time_t tm; + timeutils_seconds_since_epoch_to_struct_time(ticks / 32768U, &tm); 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(timeutils_calc_weekday(srtc_date.year, srtc_date.month, 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_obj_new_int(tm.tm_year), + mp_obj_new_int(tm.tm_mon), + mp_obj_new_int(tm.tm_mday), + mp_obj_new_int(tm.tm_wday), + mp_obj_new_int(tm.tm_hour), + mp_obj_new_int(tm.tm_min), + mp_obj_new_int(tm.tm_sec), + mp_obj_new_int((ticks % 32768) * 15625U / 512U), }; return mp_obj_new_tuple(8, tuple); } else { // Set date and time. mp_obj_t *items; - mp_int_t year; mp_obj_get_array_fixed_n(args[1], 8, &items); - - snvs_lp_srtc_datetime_t srtc_date; - year = mp_obj_get_int(items[0]); - srtc_date.year = year >= 100 ? year : year + 2000; // allow 21 for 2021 - 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[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); - } - + timeutils_struct_time_t tm = { + .tm_year = mp_obj_get_int(items[0]), + .tm_mon = mp_obj_get_int(items[1]), + .tm_mday = mp_obj_get_int(items[2]), + .tm_hour = mp_obj_get_int(items[4]), + .tm_min = mp_obj_get_int(items[5]), + .tm_sec = mp_obj_get_int(items[6]), + }; + uint32_t seconds = timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + machine_rtc_set_ticks((uint64_t)seconds * 32768ULL); return mp_const_none; } } diff --git a/ports/mimxrt/mbedtls/mbedtls_port.c b/ports/mimxrt/mbedtls/mbedtls_port.c index 230e264bf9806..fa2d81001b8c2 100644 --- a/ports/mimxrt/mbedtls/mbedtls_port.c +++ b/ports/mimxrt/mbedtls/mbedtls_port.c @@ -30,8 +30,8 @@ #include "mbedtls_config_port.h" #if defined(MBEDTLS_HAVE_TIME) || defined(MBEDTLS_HAVE_TIME_DATE) -#include "fsl_snvs_lp.h" #include "shared/timeutils/timeutils.h" +#include "modmachine.h" #include "mbedtls/platform_time.h" #endif @@ -49,9 +49,7 @@ int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t #if defined(MBEDTLS_HAVE_TIME) time_t mimxrt_rtctime_seconds(time_t *timer) { // Get date and date in CPython order. - snvs_lp_srtc_datetime_t date; - SNVS_LP_SRTC_GetDatetime(SNVS, &date); - return timeutils_seconds_since_epoch(date.year, date.month, date.day, date.hour, date.minute, date.second); + return machine_rtc_get_seconds(); } mbedtls_ms_time_t mbedtls_ms_time(void) { diff --git a/ports/mimxrt/modmachine.h b/ports/mimxrt/modmachine.h index 34e93260f2dac..398dfb22609ff 100644 --- a/ports/mimxrt/modmachine.h +++ b/ports/mimxrt/modmachine.h @@ -42,6 +42,8 @@ void mimxrt_sdram_init(void); void machine_i2s_init0(); void machine_i2s_deinit_all(void); void machine_rtc_start(void); +uint64_t machine_rtc_get_ticks(void); +uint64_t machine_rtc_get_seconds(void); void machine_rtc_alarm_helper(int seconds, bool repeat); void machine_uart_set_baudrate(mp_obj_t uart, uint32_t baudrate); diff --git a/ports/mimxrt/modtime.c b/ports/mimxrt/modtime.c index fe77b8a733be2..4b439bb17d1ac 100644 --- a/ports/mimxrt/modtime.c +++ b/ports/mimxrt/modtime.c @@ -25,30 +25,17 @@ * THE SOFTWARE. */ -#include "py/obj.h" #include "shared/timeutils/timeutils.h" -#include "fsl_snvs_lp.h" +#include "modmachine.h" // Get the localtime. static void mp_time_localtime_get(timeutils_struct_time_t *tm) { // Get current date and time. - snvs_lp_srtc_datetime_t t; - SNVS_LP_SRTC_GetDatetime(SNVS, &t); - tm->tm_year = t.year; - tm->tm_mon = t.month; - tm->tm_mday = t.day; - tm->tm_hour = t.hour; - tm->tm_min = t.minute; - tm->tm_sec = t.second; - tm->tm_wday = timeutils_calc_weekday(t.year, t.month, t.day); - tm->tm_yday = timeutils_year_day(t.year, t.month, t.day); + uint64_t seconds = machine_rtc_get_seconds(); + timeutils_seconds_since_epoch_to_struct_time(seconds, tm); } // Return the number of seconds since the Epoch. static mp_obj_t mp_time_time_get(void) { - snvs_lp_srtc_datetime_t t; - SNVS_LP_SRTC_GetDatetime(SNVS, &t); - return timeutils_obj_from_timestamp( - timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second) - ); + return timeutils_obj_from_timestamp(machine_rtc_get_seconds()); } diff --git a/ports/mimxrt/mphalport.c b/ports/mimxrt/mphalport.c index c3802be30f5d7..67612229aebc9 100644 --- a/ports/mimxrt/mphalport.c +++ b/ports/mimxrt/mphalport.c @@ -34,7 +34,7 @@ #include "extmod/misc.h" #include "ticks.h" #include "tusb.h" -#include "fsl_snvs_lp.h" +#include "modmachine.h" #ifndef MICROPY_HW_STDIN_BUFFER_LEN #define MICROPY_HW_STDIN_BUFFER_LEN 512 @@ -96,10 +96,12 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { } uint64_t mp_hal_time_ns(void) { - snvs_lp_srtc_datetime_t t; - SNVS_LP_SRTC_GetDatetime(SNVS, &t); - uint64_t s = timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second); - return s * 1000000000ULL; + uint64_t ticks = machine_rtc_get_ticks(); + // Need to compute: + // nanoseconds = ticks * 1_000_000_000 / 32768 + // = ticks * 5**9 / 64 + // but split it into upper and lower 32-bit values so the multiplication doesn't overflow. + return (((ticks >> 32U) * 1953125ULL) << 26U) + (((ticks & 0xffffffff) * 1953125ULL) >> 6U); } /*******************************************************************************/ From be15be3ab56641b532f111fd1ce4389d9bd0a539 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Mon, 24 Nov 2025 14:50:46 +1100 Subject: [PATCH 169/177] docs/mimxrt: Add docs for mimxrt.Flash. This adds docs for the `mimxrt` module and the `mimxrt.Flash` class, the implementation for which was first added in dfd4324eb1b758e37cfc77da8467e7f5d72f519c The docs are simple, following the conventions used for similar classes in the rp2 and stm32 ports. Signed-off-by: Andrew Leech --- docs/library/index.rst | 10 +++++++++ docs/library/mimxrt.Flash.rst | 39 +++++++++++++++++++++++++++++++++++ docs/library/mimxrt.rst | 19 +++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 docs/library/mimxrt.Flash.rst create mode 100644 docs/library/mimxrt.rst diff --git a/docs/library/index.rst b/docs/library/index.rst index 2919378ce13b2..e64809bfa8487 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -172,6 +172,16 @@ The following libraries are specific to the ESP8266 and ESP32. espnow.rst +Libraries specific to NXP i.MXRT +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following libraries are specific to the NXP i.MXRT family of microcontrollers. + +.. toctree:: + :maxdepth: 2 + + mimxrt.rst + Libraries specific to the RP2040 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/library/mimxrt.Flash.rst b/docs/library/mimxrt.Flash.rst new file mode 100644 index 0000000000000..161ef4ff2a4b7 --- /dev/null +++ b/docs/library/mimxrt.Flash.rst @@ -0,0 +1,39 @@ +.. currentmodule:: mimxrt +.. _mimxrt.Flash: + +class Flash -- access to built-in flash storage +=============================================== + +This class gives access to the SPI flash memory. + +In most cases, to store persistent data on the device, you'll want to use a +higher-level abstraction, for example the filesystem via Python's standard file +API, but this interface is useful to :ref:`customise the filesystem +configuration ` or implement a low-level storage system for your +application. + + +Constructors +------------ + +.. class:: Flash() + + Gets the singleton object for accessing the SPI flash memory. + + +Methods +------- + +.. method:: Flash.readblocks(block_num, buf) + Flash.readblocks(block_num, buf, offset) +.. method:: Flash.writeblocks(block_num, buf) + Flash.writeblocks(block_num, buf, offset) +.. method:: Flash.ioctl(cmd, arg) + + These methods implement the simple and extended + :ref:`block protocol ` defined by + :class:`vfs.AbstractBlockDev`. + + The block size can be queried by calling ``ioctl(5, 0)``. Block numbers + are relative to the start of the user flash storage area, not the physical + start of flash memory. diff --git a/docs/library/mimxrt.rst b/docs/library/mimxrt.rst new file mode 100644 index 0000000000000..fdb4c1a1da66f --- /dev/null +++ b/docs/library/mimxrt.rst @@ -0,0 +1,19 @@ +.. currentmodule:: mimxrt + +:mod:`mimxrt` --- functionality specific to NXP i.MXRT +====================================================== + +.. module:: mimxrt + :synopsis: functionality specific to NXP i.MXRT + +The ``mimxrt`` module contains functions and classes specific to the NXP i.MXRT +family of microcontrollers. + + +Classes +------- + +.. toctree:: + :maxdepth: 1 + + mimxrt.Flash.rst From af76da96a3a68946a46ee6927f6b4570de00d1ac Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 18 Dec 2025 14:59:36 +1100 Subject: [PATCH 170/177] stm32: Default to --connect-under-reset if flashing via deploy-stlink. (Can be overridden by setting STFLASH variable.) Generalises the change from 6b13e6c4987c6fd103847e7b36a3f0fcd0ff42d9 to all stm32 boards. As we're resetting the target to flash it anyway, it shouldn't hurt to put the target into reset before trying to connect. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/stm32/Makefile | 2 +- ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk | 1 - ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk | 1 - ports/stm32/mboot/Makefile | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index b43f284b1abee..c0bf4be32e93d 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -72,7 +72,7 @@ PYDFU ?= $(TOP)/tools/pydfu.py DFU_UTIL ?= dfu-util BOOTLOADER_DFU_USB_VID ?= 0x0483 BOOTLOADER_DFU_USB_PID ?= 0xDF11 -STFLASH ?= st-flash +STFLASH ?= st-flash --connect-under-reset OPENOCD ?= openocd OPENOCD_CONFIG ?= boards/openocd_stm32f4.cfg diff --git a/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk index 56f30c42bd352..fcb235c8688df 100644 --- a/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk @@ -26,5 +26,4 @@ MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py # Flash tool configuration -STFLASH = st-flash --connect-under-reset OPENOCD_CONFIG = boards/openocd_stm32h7_dual_bank.cfg diff --git a/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk index da991c5879e33..0f528939c9d2c 100644 --- a/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.mk @@ -26,5 +26,4 @@ MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py # Flash tool configuration -STFLASH = st-flash --connect-under-reset OPENOCD_CONFIG = boards/openocd_stm32h7_dual_bank.cfg diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 7226dd353f399..c049017734feb 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -54,7 +54,7 @@ DFU=$(TOP)/tools/dfu.py PYDFU ?= $(TOP)/tools/pydfu.py BOOTLOADER_DFU_USB_VID ?= 0x0483 BOOTLOADER_DFU_USB_PID ?= 0xDF11 -STFLASH ?= st-flash +STFLASH ?= st-flash --connect-under-reset OPENOCD ?= openocd OPENOCD_CONFIG ?= boards/openocd_stm32f4.cfg From 96bce47762503839e554aa0bb8549fdb617361f0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 21 Oct 2025 22:25:12 +1100 Subject: [PATCH 171/177] tests: Factor out common helper functions to separate Python module. The test runners have evolved over time and become more and more complex. In particular `tests/run-tests.py` is rather large now. The test runners also duplicate some functionality amongst themselves. As a start to improving this situation, this commit factors out the helper functions from `run-tests.py` into a new `test_utils.py` file, and uses that new module in all test runners. There should be no functional change here. Signed-off-by: Damien George --- tests/run-internalbench.py | 21 ++- tests/run-multitests.py | 19 +- tests/run-natmodtests.py | 20 +- tests/run-perfbench.py | 20 +- tests/run-tests.py | 366 +++---------------------------------- tests/serial_test.py | 6 +- tests/test_utils.py | 358 ++++++++++++++++++++++++++++++++++++ 7 files changed, 429 insertions(+), 381 deletions(-) create mode 100644 tests/test_utils.py diff --git a/tests/run-internalbench.py b/tests/run-internalbench.py index 99c6304afe9d6..4f2d29525deb9 100755 --- a/tests/run-internalbench.py +++ b/tests/run-internalbench.py @@ -8,9 +8,14 @@ from glob import glob from collections import defaultdict -run_tests_module = __import__("run-tests") -sys.path.append(run_tests_module.base_path("../tools")) -import pyboard +from test_utils import ( + base_path, + pyboard, + test_instance_description, + test_instance_epilog, + test_directory_description, + get_test_instance, +) if os.name == "nt": MICROPYTHON = os.getenv( @@ -97,10 +102,10 @@ def main(): formatter_class=argparse.RawDescriptionHelpFormatter, description=f"""Run and manage tests for MicroPython. -{run_tests_module.test_instance_description} -{run_tests_module.test_directory_description} +{test_instance_description} +{test_directory_description} """, - epilog=run_tests_module.test_instance_epilog, + epilog=test_instance_epilog, ) cmd_parser.add_argument( "-t", "--test-instance", default="unix", help="the MicroPython instance to test" @@ -124,9 +129,7 @@ def main(): args = cmd_parser.parse_args() # Note pyboard support is copied over from run-tests.py, not tests, and likely needs revamping - pyb = run_tests_module.get_test_instance( - args.test_instance, args.baudrate, args.user, args.password - ) + pyb = get_test_instance(args.test_instance, args.baudrate, args.user, args.password) if len(args.files) == 0: if args.test_dirs: diff --git a/tests/run-multitests.py b/tests/run-multitests.py index e5458ffe0d00c..c5bb8011e76d9 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -15,7 +15,13 @@ import subprocess import tempfile -run_tests_module = __import__("run-tests") +from test_utils import ( + base_path, + pyboard, + test_instance_epilog, + convert_device_shortcut_to_real_device, + create_test_report, +) test_dir = os.path.abspath(os.path.dirname(__file__)) @@ -24,9 +30,6 @@ # accidentally importing tests like micropython/const.py sys.path.pop(0) -sys.path.insert(0, test_dir + "/../tools") -import pyboard - if os.name == "nt": CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3.exe") MICROPYTHON = os.path.abspath( @@ -554,7 +557,7 @@ def main(): cmd_parser = argparse.ArgumentParser( description="Run network tests for MicroPython", epilog=( - run_tests_module.test_instance_epilog + test_instance_epilog + "Each instance arg can optionally have custom env provided, eg. ,ENV=VAR,ENV=VAR...\n" ), formatter_class=argparse.RawTextHelpFormatter, @@ -582,7 +585,7 @@ def main(): cmd_parser.add_argument( "-r", "--result-dir", - default=run_tests_module.base_path("results"), + default=base_path("results"), help="directory for test results", ) cmd_parser.add_argument("files", nargs="+", help="input test files") @@ -612,7 +615,7 @@ def main(): print("unsupported instance string: {}".format(cmd), file=sys.stderr) sys.exit(2) else: - device = run_tests_module.convert_device_shortcut_to_real_device(cmd) + device = convert_device_shortcut_to_real_device(cmd) instances_test.append(PyInstancePyboard(device)) for _ in range(max_instances - len(instances_test)): @@ -626,7 +629,7 @@ def main(): break test_results = run_tests(test_files, instances_truth, instances_test_permutation) - all_pass &= run_tests_module.create_test_report(cmd_args, test_results) + all_pass &= create_test_report(cmd_args, test_results) finally: for i in instances_truth: diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index cd6c643bf9b08..cb0877e2cb089 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -9,7 +9,13 @@ import sys import argparse -run_tests_module = __import__("run-tests") +from test_utils import ( + base_path, + pyboard, + test_instance_epilog, + get_test_instance, + create_test_report, +) # Paths for host executables CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") @@ -110,7 +116,7 @@ def run_script(self, script): output = self.pyb.exec_(script) output = output.replace(b"\r\n", b"\n") return output, None - except run_tests_module.pyboard.PyboardError as er: + except pyboard.PyboardError as er: return b"", er @@ -221,7 +227,7 @@ def main(): cmd_parser = argparse.ArgumentParser( description="Run dynamic-native-module tests under MicroPython", - epilog=run_tests_module.test_instance_epilog, + epilog=test_instance_epilog, formatter_class=argparse.RawDescriptionHelpFormatter, ) cmd_parser.add_argument( @@ -243,7 +249,7 @@ def main(): cmd_parser.add_argument( "-r", "--result-dir", - default=run_tests_module.base_path("results"), + default=base_path("results"), help="directory for test results", ) cmd_parser.add_argument("files", nargs="*", help="input test files") @@ -257,9 +263,7 @@ def main(): target_truth = TargetSubprocess([CPYTHON3]) - target = run_tests_module.get_test_instance( - args.test_instance, args.baudrate, args.user, args.password - ) + target = get_test_instance(args.test_instance, args.baudrate, args.user, args.password) if target is None: # Use the unix port of MicroPython. target = TargetSubprocess([MICROPYTHON]) @@ -283,7 +287,7 @@ def main(): os.makedirs(args.result_dir, exist_ok=True) test_results = run_tests(target_truth, target, args, target_arch) - res = run_tests_module.create_test_report(args, test_results) + res = create_test_report(args, test_results) target.close() target_truth.close() diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py index 039d11a36111e..16182bc8a9f73 100755 --- a/tests/run-perfbench.py +++ b/tests/run-perfbench.py @@ -10,9 +10,13 @@ import argparse from glob import glob -run_tests_module = __import__("run-tests") - -prepare_script_for_target = run_tests_module.prepare_script_for_target +from test_utils import ( + base_path, + pyboard, + get_test_instance, + prepare_script_for_target, + create_test_report, +) # Paths for host executables if os.name == "nt": @@ -49,7 +53,7 @@ def run_script_on_target(target, script): try: target.enter_raw_repl() output = target.exec_(script) - except run_tests_module.pyboard.PyboardError as er: + except pyboard.PyboardError as er: err = er else: # Run local executable @@ -277,7 +281,7 @@ def main(): cmd_parser.add_argument( "-r", "--result-dir", - default=run_tests_module.base_path("results"), + default=base_path("results"), help="directory for test results", ) cmd_parser.add_argument( @@ -298,9 +302,7 @@ def main(): M = int(args.M[0]) n_average = int(args.average) - target = run_tests_module.get_test_instance( - args.test_instance, args.baudrate, args.user, args.password - ) + target = get_test_instance(args.test_instance, args.baudrate, args.user, args.password) if target is None: # Use the unix port of MicroPython. target = [MICROPYTHON, "-X", "emit=" + args.emit] @@ -328,7 +330,7 @@ def main(): os.makedirs(args.result_dir, exist_ok=True) test_results = run_benchmarks(args, target, N, M, n_average, tests) - res = run_tests_module.create_test_report(args, test_results) + res = create_test_report(args, test_results) if hasattr(target, "exit_raw_repl"): target.exit_raw_repl() diff --git a/tests/run-tests.py b/tests/run-tests.py index 6dfba2c540a49..0ff358c06e219 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -6,7 +6,6 @@ import sysconfig import platform import argparse -import inspect import json import re from glob import glob @@ -15,23 +14,29 @@ import threading import tempfile -# Maximum time to run a single test, in seconds. -TEST_TIMEOUT = float(os.environ.get("MICROPY_TEST_TIMEOUT", 30)) - -# See stackoverflow.com/questions/2632199: __file__ nor sys.argv[0] -# are guaranteed to always work, this one should though. -BASEPATH = os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: None))) +from test_utils import ( + base_path, + pyboard, + TEST_TIMEOUT, + MPYCROSS, + test_instance_description, + test_instance_epilog, + test_directory_description, + rm_f, + normalize_newlines, + set_injected_prologue, + get_results_filename, + convert_device_shortcut_to_real_device, + get_test_instance, + prepare_script_for_target, + create_test_report, +) RV32_ARCH_FLAGS = { "zba": 1 << 0, "zcmp": 1 << 1, } - -def base_path(*p): - return os.path.abspath(os.path.join(BASEPATH, *p)).replace("\\", "/") - - # Tests require at least CPython 3.3. If your default python3 executable # is of lower version, you can point MICROPY_CPYTHON3 environment var # to the correct executable. @@ -40,88 +45,22 @@ def base_path(*p): MICROPYTHON = os.getenv( "MICROPY_MICROPYTHON", base_path("../ports/windows/build-standard/micropython.exe") ) - # mpy-cross is only needed if --via-mpy command-line arg is passed - MPYCROSS = os.getenv("MICROPY_MPYCROSS", base_path("../mpy-cross/build/mpy-cross.exe")) else: CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") MICROPYTHON = os.getenv( "MICROPY_MICROPYTHON", base_path("../ports/unix/build-standard/micropython") ) - # mpy-cross is only needed if --via-mpy command-line arg is passed - MPYCROSS = os.getenv("MICROPY_MPYCROSS", base_path("../mpy-cross/build/mpy-cross")) # Use CPython options to not save .pyc files, to only access the core standard library # (not site packages which may clash with u-module names), and improve start up time. CPYTHON3_CMD = [CPYTHON3, "-BS"] -# File with the test results. -RESULTS_FILE = "_results.json" - # For diff'ing test output DIFF = os.getenv("MICROPY_DIFF", "diff -u") # Set PYTHONIOENCODING so that CPython will use utf-8 on systems which set another encoding in the locale os.environ["PYTHONIOENCODING"] = "utf-8" - -def normalize_newlines(data): - """Normalize newline variations to \\n. - - Only normalizes actual line endings, not literal \\r characters in strings. - Handles \\r\\r\\n and \\r\\n cases to ensure consistent comparison - across different platforms and terminals. - """ - if isinstance(data, bytes): - # Handle PTY double-newline issue first - data = data.replace(b"\r\r\n", b"\n") - # Then handle standard Windows line endings - data = data.replace(b"\r\n", b"\n") - # Don't convert standalone \r as it might be literal content - return data - - -# 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): - 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 - return 0 - return -1 - def readinto(self, buf): - buf[:] = memoryview(__buf)[self.off:self.off + len(buf)] - self.off += len(buf) - return len(buf) -class __FS: - def mount(self, readonly, mkfs): - pass - def umount(self): - pass - def chdir(self, path): - pass - def getcwd(self): - return "" - def stat(self, path): - if path == '__injected_test.mpy': - return (0,0,0,0,0,0,0,0,0,0) - else: - raise OSError(2) # ENOENT - def open(self, path, mode): - self.stat(path) - return __File() -vfs.mount(__FS(), '/__vfstest') -os.chdir('/__vfstest') -{import_prologue} -__import__('__injected_test') -""" - # Platforms associated with the unix port, values of `sys.platform`. PC_PLATFORMS = ("darwin", "linux", "win32") @@ -337,11 +276,6 @@ def open(self, path, mode): ) -def rm_f(fname): - if os.path.exists(fname): - os.remove(fname) - - # unescape wanted regex chars and escape unwanted ones def convert_regex_escapes(line): cs = [] @@ -366,38 +300,6 @@ def platform_to_port(platform): return platform_to_port_map.get(platform, platform) -def convert_device_shortcut_to_real_device(device): - if device.startswith("port:"): - return device.split(":", 1)[1] - elif device.startswith("a") and device[1:].isdigit(): - return "/dev/ttyACM" + device[1:] - elif device.startswith("u") and device[1:].isdigit(): - return "/dev/ttyUSB" + device[1:] - elif device.startswith("c") and device[1:].isdigit(): - return "COM" + device[1:] - else: - return device - - -def get_test_instance(test_instance, baudrate, user, password): - if test_instance == "unix": - return None - elif test_instance == "webassembly": - return PyboardNodeRunner() - else: - # Assume it's a device path. - port = convert_device_shortcut_to_real_device(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_inline_asm_arch(pyb, args): for arch in ("rv32", "thumb", "xtensa"): output = run_feature_check(pyb, args, "inlineasm_{}.py".format(arch)) @@ -503,90 +405,6 @@ def detect_target_wiring_script(pyb, args): pyb.target_wiring_script = tw_data -def prepare_script_for_target(args, *, script_text=None, force_plain=False): - if force_plain or (not args.via_mpy and args.emit == "bytecode"): - # A plain test to run as-is, no processing needed. - pass - elif args.via_mpy: - tempname = tempfile.mktemp(dir="") - mpy_filename = tempname + ".mpy" - - script_filename = tempname + ".py" - with open(script_filename, "wb") as f: - f.write(script_text) - - try: - subprocess.check_output( - [MPYCROSS] - + args.mpy_cross_flags.split() - + ["-o", mpy_filename, "-X", "emit=" + args.emit, script_filename], - stderr=subprocess.STDOUT, - ) - except subprocess.CalledProcessError as er: - return True, b"mpy-cross crash\n" + er.output - - with open(mpy_filename, "rb") as f: - script_text = b"__buf=" + bytes(repr(f.read()), "ascii") + b"\n" - - rm_f(mpy_filename) - rm_f(script_filename) - - script_text += bytes(injected_import_hook_code, "ascii") - else: - print("error: using emit={} must go via .mpy".format(args.emit)) - sys.exit(1) - - return False, script_text - - -def run_script_on_remote_target(pyb, args, test_file, is_special): - with open(test_file, "rb") as f: - script = f.read() - - # If the test is not a special test, prepend it with a print to indicate that it started. - # If the print does not execute this means that the test did not even start, eg it was - # too large for the target. - prepend_start_test = not is_special - if prepend_start_test: - if script.startswith(b"#"): - script = b"print('START TEST')" + script - else: - script = b"print('START TEST')\n" + script - - had_crash, script = prepare_script_for_target(args, script_text=script, force_plain=is_special) - - if had_crash: - return True, script - - try: - had_crash = False - pyb.enter_raw_repl() - if test_file.endswith(tests_requiring_target_wiring) and pyb.target_wiring_script: - pyb.exec_( - "import sys;sys.modules['target_wiring']=__build_class__(lambda:exec(" - + repr(pyb.target_wiring_script) - + "),'target_wiring')" - ) - output_mupy = pyb.exec_(script, timeout=TEST_TIMEOUT) - except pyboard.PyboardError as e: - had_crash = True - if not is_special and e.args[0] == "exception": - if prepend_start_test and e.args[1] == b"" and b"MemoryError" in e.args[2]: - output_mupy = b"SKIP-TOO-LARGE\n" - else: - output_mupy = e.args[1] + e.args[2] + b"CRASH" - else: - output_mupy = bytes(e.args[0], "ascii") + b"\nCRASH" - - if prepend_start_test: - if output_mupy.startswith(b"START TEST\r\n"): - output_mupy = output_mupy.removeprefix(b"START TEST\r\n") - else: - had_crash = True - - return had_crash, output_mupy - - tests_with_regex_output = [ base_path(file) for file in ( @@ -722,8 +540,9 @@ def send_get(what): else: # run via pyboard interface + requires_target_wiring = test_file.endswith(tests_requiring_target_wiring) had_crash, output_mupy = pyb.run_script_on_remote_target( - args, test_file_abspath, is_special + args, test_file_abspath, is_special, requires_target_wiring ) # canonical form for all ports/platforms is to use \n for end-of-line @@ -813,51 +632,6 @@ def value(self): return self._value -class PyboardNodeRunner: - def __init__(self): - mjs = os.getenv("MICROPY_MICROPYTHON_MJS") - if mjs is None: - mjs = base_path("../ports/webassembly/build-standard/micropython.mjs") - else: - mjs = os.path.abspath(mjs) - self.micropython_mjs = mjs - - def close(self): - pass - - def run_script_on_remote_target(self, args, test_file, is_special): - cwd = os.path.dirname(test_file) - - # Create system command list. - cmdlist = ["node"] - if test_file.endswith(".py"): - # Run a Python script indirectly via "node micropython.mjs ". - cmdlist.append(self.micropython_mjs) - if args.heapsize is not None: - cmdlist.extend(["-X", "heapsize=" + args.heapsize]) - cmdlist.append(test_file) - else: - # Run a js/mjs script directly with Node, passing in the path to micropython.mjs. - cmdlist.append(test_file) - cmdlist.append(self.micropython_mjs) - - # Run the script. - try: - had_crash = False - output_mupy = subprocess.check_output( - cmdlist, stderr=subprocess.STDOUT, timeout=TEST_TIMEOUT, cwd=cwd - ) - except subprocess.CalledProcessError as er: - had_crash = True - output_mupy = er.output + b"CRASH" - except subprocess.TimeoutExpired as er: - had_crash = True - output_mupy = (er.output or b"") + b"TIMEOUT" - - # Return the results. - return had_crash, output_mupy - - def run_tests(pyb, tests, args, result_dir, num_threads=1): testcase_count = ThreadSafeCounter() test_results = ThreadSafeCounter([]) @@ -1257,70 +1031,6 @@ def run_one_test(test_file): return test_results.value, testcase_count.value -# Print a summary of the results and save them to a JSON file. -# Returns True if everything succeeded, False otherwise. -def create_test_report(args, test_results, testcase_count=None): - passed_tests = list(r for r in test_results if r[1] == "pass") - skipped_tests = list(r for r in test_results if r[1] == "skip" and r[2] != "too large") - skipped_tests_too_large = list( - r for r in test_results if r[1] == "skip" and r[2] == "too large" - ) - failed_tests = list(r for r in test_results if r[1] == "fail") - - num_tests_performed = len(passed_tests) + len(failed_tests) - - testcase_count_info = "" - if testcase_count is not None: - testcase_count_info = " ({} individual testcases)".format(testcase_count) - print("{} tests performed{}".format(num_tests_performed, testcase_count_info)) - - print("{} tests passed".format(len(passed_tests))) - - if len(skipped_tests) > 0: - print( - "{} tests skipped: {}".format( - len(skipped_tests), " ".join(test[0] for test in skipped_tests) - ) - ) - - if len(skipped_tests_too_large) > 0: - print( - "{} tests skipped because they are too large: {}".format( - len(skipped_tests_too_large), " ".join(test[0] for test in skipped_tests_too_large) - ) - ) - - if len(failed_tests) > 0: - print( - "{} tests failed: {}".format( - len(failed_tests), " ".join(test[0] for test in failed_tests) - ) - ) - - # Serialize regex added by append_filter. - def to_json(obj): - if isinstance(obj, re.Pattern): - return obj.pattern - return obj - - with open(os.path.join(args.result_dir, RESULTS_FILE), "w") as f: - json.dump( - { - # The arguments passed on the command-line. - "args": vars(args), - # A list of all results of the form [(test, result, reason), ...]. - "results": list(test for test in test_results), - # A list of failed tests. This is deprecated, use the "results" above instead. - "failed_tests": [test[0] for test in failed_tests], - }, - f, - default=to_json, - ) - - # Return True only if all tests succeeded. - return len(failed_tests) == 0 - - class append_filter(argparse.Action): def __init__(self, option_strings, dest, **kwargs): super().__init__(option_strings, dest, default=[], **kwargs) @@ -1335,39 +1045,7 @@ def __call__(self, parser, args, value, option): args.filters.append((option, re.compile(value))) -test_instance_description = """\ -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. -""" -test_instance_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 -""" - -test_directory_description = """\ -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. -""" - - def main(): - global injected_import_hook_code - cmd_parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=f"""Run and manage tests for MicroPython. @@ -1474,7 +1152,7 @@ def main(): if args.begin: with open(args.begin, "rt") as source: prologue = source.read() - injected_import_hook_code = injected_import_hook_code.replace("{import_prologue}", prologue) + set_injected_prologue(prologue) if args.print_failures: for out in glob(os.path.join(args.result_dir, "*.out")): @@ -1497,7 +1175,7 @@ def main(): os.path.join(args.result_dir, "*.out") ): os.remove(f) - rm_f(os.path.join(args.result_dir, RESULTS_FILE)) + rm_f(get_results_filename(args)) sys.exit(0) @@ -1513,7 +1191,7 @@ def main(): ) if args.run_failures: - results_file = os.path.join(args.result_dir, RESULTS_FILE) + results_file = get_results_filename(args) if os.path.exists(results_file): with open(results_file, "r") as f: tests = list(test[0] for test in json.load(f)["results"] if test[1] == "fail") diff --git a/tests/serial_test.py b/tests/serial_test.py index 3b5940d91a907..eebea402fa4c9 100755 --- a/tests/serial_test.py +++ b/tests/serial_test.py @@ -13,7 +13,7 @@ import sys import time -run_tests_module = __import__("run-tests") +from test_utils import test_instance_epilog, convert_device_shortcut_to_real_device echo_test_script = """ import sys @@ -307,7 +307,7 @@ def main(): cmd_parser = argparse.ArgumentParser( description="Test performance and reliability of serial port communication.", - epilog=run_tests_module.test_instance_epilog, + epilog=test_instance_epilog, formatter_class=argparse.RawTextHelpFormatter, ) cmd_parser.add_argument( @@ -321,7 +321,7 @@ def main(): ) args = cmd_parser.parse_args() - dev_repl = run_tests_module.convert_device_shortcut_to_real_device(args.test_instance) + dev_repl = convert_device_shortcut_to_real_device(args.test_instance) test_passed = True try: diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000000000..abbc670c12521 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,358 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# The MIT License (MIT) +# Copyright (c) 2019-2025 Damien P. George + +import inspect +import json +import os +import re +import subprocess +import sys +import tempfile + +# See stackoverflow.com/questions/2632199: __file__ nor sys.argv[0] +# are guaranteed to always work, this one should though. +_BASEPATH = os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: None))) + + +def base_path(*p): + return os.path.abspath(os.path.join(_BASEPATH, *p)).replace("\\", "/") + + +sys.path.append(base_path("../tools")) +import pyboard + +# File with the test results. +_RESULTS_FILE = "_results.json" + +# Maximum time to run a single test, in seconds. +TEST_TIMEOUT = float(os.environ.get("MICROPY_TEST_TIMEOUT", 30)) + +# mpy-cross is only needed if --via-mpy command-line arg is passed +if os.name == "nt": + MPYCROSS = os.getenv("MICROPY_MPYCROSS", base_path("../mpy-cross/build/mpy-cross.exe")) +else: + MPYCROSS = os.getenv("MICROPY_MPYCROSS", base_path("../mpy-cross/build/mpy-cross")) + +test_instance_description = """\ +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. +""" + +test_instance_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 +""" + +test_directory_description = """\ +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. +""" + +# 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): + 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 + return 0 + return -1 + def readinto(self, buf): + buf[:] = memoryview(__buf)[self.off:self.off + len(buf)] + self.off += len(buf) + return len(buf) +class __FS: + def mount(self, readonly, mkfs): + pass + def umount(self): + pass + def chdir(self, path): + pass + def getcwd(self): + return "" + def stat(self, path): + if path == '__injected_test.mpy': + return (0,0,0,0,0,0,0,0,0,0) + else: + raise OSError(2) # ENOENT + def open(self, path, mode): + self.stat(path) + return __File() +vfs.mount(__FS(), '/__vfstest') +os.chdir('/__vfstest') +{import_prologue} +__import__('__injected_test') +""" + + +class PyboardNodeRunner: + def __init__(self): + mjs = os.getenv("MICROPY_MICROPYTHON_MJS") + if mjs is None: + mjs = base_path("../ports/webassembly/build-standard/micropython.mjs") + else: + mjs = os.path.abspath(mjs) + self.micropython_mjs = mjs + + def close(self): + pass + + def run_script_on_remote_target(self, args, test_file, is_special, requires_target_wiring): + cwd = os.path.dirname(test_file) + + # Create system command list. + cmdlist = ["node"] + if test_file.endswith(".py"): + # Run a Python script indirectly via "node micropython.mjs ". + cmdlist.append(self.micropython_mjs) + if args.heapsize is not None: + cmdlist.extend(["-X", "heapsize=" + args.heapsize]) + cmdlist.append(test_file) + else: + # Run a js/mjs script directly with Node, passing in the path to micropython.mjs. + cmdlist.append(test_file) + cmdlist.append(self.micropython_mjs) + + # Run the script. + try: + had_crash = False + output_mupy = subprocess.check_output( + cmdlist, stderr=subprocess.STDOUT, timeout=TEST_TIMEOUT, cwd=cwd + ) + except subprocess.CalledProcessError as er: + had_crash = True + output_mupy = er.output + b"CRASH" + except subprocess.TimeoutExpired as er: + had_crash = True + output_mupy = (er.output or b"") + b"TIMEOUT" + + # Return the results. + return had_crash, output_mupy + + +def rm_f(fname): + if os.path.exists(fname): + os.remove(fname) + + +def normalize_newlines(data): + """Normalize newline variations to \\n. + + Only normalizes actual line endings, not literal \\r characters in strings. + Handles \\r\\r\\n and \\r\\n cases to ensure consistent comparison + across different platforms and terminals. + """ + if isinstance(data, bytes): + # Handle PTY double-newline issue first + data = data.replace(b"\r\r\n", b"\n") + # Then handle standard Windows line endings + data = data.replace(b"\r\n", b"\n") + # Don't convert standalone \r as it might be literal content + return data + + +def set_injected_prologue(prologue): + global _injected_import_hook_code + _injected_import_hook_code = _injected_import_hook_code.replace("{import_prologue}", prologue) + + +def get_results_filename(args): + return os.path.join(args.result_dir, _RESULTS_FILE) + + +def convert_device_shortcut_to_real_device(device): + if device.startswith("port:"): + return device.split(":", 1)[1] + elif device.startswith("a") and device[1:].isdigit(): + return "/dev/ttyACM" + device[1:] + elif device.startswith("u") and device[1:].isdigit(): + return "/dev/ttyUSB" + device[1:] + elif device.startswith("c") and device[1:].isdigit(): + return "COM" + device[1:] + else: + return device + + +def get_test_instance(test_instance, baudrate, user, password): + if test_instance == "unix": + return None + elif test_instance == "webassembly": + return PyboardNodeRunner() + else: + # Assume it's a device path. + port = convert_device_shortcut_to_real_device(test_instance) + + 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 prepare_script_for_target(args, *, script_text=None, force_plain=False): + if force_plain or (not args.via_mpy and args.emit == "bytecode"): + # A plain test to run as-is, no processing needed. + pass + elif args.via_mpy: + tempname = tempfile.mktemp(dir="") + mpy_filename = tempname + ".mpy" + + script_filename = tempname + ".py" + with open(script_filename, "wb") as f: + f.write(script_text) + + try: + subprocess.check_output( + [MPYCROSS] + + args.mpy_cross_flags.split() + + ["-o", mpy_filename, "-X", "emit=" + args.emit, script_filename], + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as er: + return True, b"mpy-cross crash\n" + er.output + + with open(mpy_filename, "rb") as f: + script_text = b"__buf=" + bytes(repr(f.read()), "ascii") + b"\n" + + rm_f(mpy_filename) + rm_f(script_filename) + + script_text += bytes(_injected_import_hook_code, "ascii") + else: + print("error: using emit={} must go via .mpy".format(args.emit)) + sys.exit(1) + + return False, script_text + + +def run_script_on_remote_target(pyb, args, test_file, is_special, requires_target_wiring): + with open(test_file, "rb") as f: + script = f.read() + + # If the test is not a special test, prepend it with a print to indicate that it started. + # If the print does not execute this means that the test did not even start, eg it was + # too large for the target. + prepend_start_test = not is_special + if prepend_start_test: + if script.startswith(b"#"): + script = b"print('START TEST')" + script + else: + script = b"print('START TEST')\n" + script + + had_crash, script = prepare_script_for_target(args, script_text=script, force_plain=is_special) + + if had_crash: + return True, script + + try: + had_crash = False + pyb.enter_raw_repl() + if requires_target_wiring and pyb.target_wiring_script: + pyb.exec_( + "import sys;sys.modules['target_wiring']=__build_class__(lambda:exec(" + + repr(pyb.target_wiring_script) + + "),'target_wiring')" + ) + output_mupy = pyb.exec_(script, timeout=TEST_TIMEOUT) + except pyboard.PyboardError as e: + had_crash = True + if not is_special and e.args[0] == "exception": + if prepend_start_test and e.args[1] == b"" and b"MemoryError" in e.args[2]: + output_mupy = b"SKIP-TOO-LARGE\n" + else: + output_mupy = e.args[1] + e.args[2] + b"CRASH" + else: + output_mupy = bytes(e.args[0], "ascii") + b"\nCRASH" + + if prepend_start_test: + if output_mupy.startswith(b"START TEST\r\n"): + output_mupy = output_mupy.removeprefix(b"START TEST\r\n") + else: + had_crash = True + + return had_crash, output_mupy + + +# Print a summary of the results and save them to a JSON file. +# Returns True if everything succeeded, False otherwise. +def create_test_report(args, test_results, testcase_count=None): + passed_tests = list(r for r in test_results if r[1] == "pass") + skipped_tests = list(r for r in test_results if r[1] == "skip" and r[2] != "too large") + skipped_tests_too_large = list( + r for r in test_results if r[1] == "skip" and r[2] == "too large" + ) + failed_tests = list(r for r in test_results if r[1] == "fail") + + num_tests_performed = len(passed_tests) + len(failed_tests) + + testcase_count_info = "" + if testcase_count is not None: + testcase_count_info = " ({} individual testcases)".format(testcase_count) + print("{} tests performed{}".format(num_tests_performed, testcase_count_info)) + + print("{} tests passed".format(len(passed_tests))) + + if len(skipped_tests) > 0: + print( + "{} tests skipped: {}".format( + len(skipped_tests), " ".join(test[0] for test in skipped_tests) + ) + ) + + if len(skipped_tests_too_large) > 0: + print( + "{} tests skipped because they are too large: {}".format( + len(skipped_tests_too_large), " ".join(test[0] for test in skipped_tests_too_large) + ) + ) + + if len(failed_tests) > 0: + print( + "{} tests failed: {}".format( + len(failed_tests), " ".join(test[0] for test in failed_tests) + ) + ) + + # Serialize regex added by append_filter. + def to_json(obj): + if isinstance(obj, re.Pattern): + return obj.pattern + return obj + + with open(get_results_filename(args), "w") as f: + json.dump( + { + # The arguments passed on the command-line. + "args": vars(args), + # A list of all results of the form [(test, result, reason), ...]. + "results": list(test for test in test_results), + # A list of failed tests. This is deprecated, use the "results" above instead. + "failed_tests": [test[0] for test in failed_tests], + }, + f, + default=to_json, + ) + + # Return True only if all tests succeeded. + return len(failed_tests) == 0 From 0e3cc2910d250b51f2c22ef32c2be73f049696da Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Thu, 16 Oct 2025 15:21:28 -0600 Subject: [PATCH 172/177] mimxrt: Add PSRAM implementation. This commit adds PSRAM support for Teensy 4.1 and other mimxrt boards. It's enabled by default for Teensy 4.1. This implementation is based on the Teensy Arduino core PSRAM code, and Paul Stoffregen has agreed for it to be published here under the MIT license, see https://github.com/micropython/micropython/pull/18288#issuecomment-3806784632 This addresses issue #18281. Signed-off-by: Dryw Wade --- ports/mimxrt/Makefile | 1 + ports/mimxrt/boards/TEENSY41/mpconfigboard.h | 3 + ports/mimxrt/main.c | 18 ++ ports/mimxrt/mpconfigport.h | 9 +- ports/mimxrt/psram.c | 265 +++++++++++++++++++ ports/mimxrt/psram.h | 36 +++ 6 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 ports/mimxrt/psram.c create mode 100644 ports/mimxrt/psram.h diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 08f99d68b53a0..576f6e64ac1ec 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -266,6 +266,7 @@ SRC_C += \ network_lan.c \ pendsv.c \ pin.c \ + psram.c \ sdcard.c \ sdio.c \ systick.c \ diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h index bff319c6d1576..2eeac0dab001b 100644 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h @@ -136,3 +136,6 @@ { IOMUXC_GPIO_B1_11_ENET_RX_ER, 0, 0xB0E9u }, \ { IOMUXC_GPIO_B1_15_ENET_MDIO, 0, 0xB0E9u }, \ { IOMUXC_GPIO_B1_14_ENET_MDC, 0, 0xB0E9u }, + +// Enable PSRAM support +#define MICROPY_HW_ENABLE_PSRAM (1) diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index dcb1ede1670db..9f3d47f8cc245 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -37,6 +37,7 @@ #include "ticks.h" #include "led.h" #include "pendsv.h" +#include "psram.h" #include "modmachine.h" #include "modmimxrt.h" @@ -67,6 +68,10 @@ int main(void) { ticks_init(); pendsv_init(); + #if MICROPY_HW_ENABLE_PSRAM + size_t psram_size = configure_external_ram(); + #endif + #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. @@ -101,7 +106,20 @@ int main(void) { mp_cstack_init_with_top(&_estack, &_estack - &_sstack); + #if MICROPY_HW_ENABLE_PSRAM + if (psram_size) { + #if MICROPY_GC_SPLIT_HEAP + gc_init(&_gc_heap_start, &_gc_heap_end); + 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(&_gc_heap_start, &_gc_heap_end); + } + #else gc_init(&_gc_heap_start, &_gc_heap_end); + #endif mp_init(); #if MICROPY_PY_NETWORK diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 52289549d7cc3..871713e1cb233 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -35,13 +35,20 @@ uint32_t trng_random_u32(void); // Config level #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) +#ifndef MICROPY_HW_ENABLE_PSRAM +#define MICROPY_HW_ENABLE_PSRAM (0) +#endif + // Memory allocation policies -#if MICROPY_HW_SDRAM_AVAIL +#if MICROPY_HW_SDRAM_AVAIL || MICROPY_HW_ENABLE_PSRAM #define MICROPY_GC_STACK_ENTRY_TYPE uint32_t #else #define MICROPY_GC_STACK_ENTRY_TYPE uint16_t #endif #define MICROPY_ALLOC_PATH_MAX (256) +#ifndef MICROPY_GC_SPLIT_HEAP +#define MICROPY_GC_SPLIT_HEAP MICROPY_HW_ENABLE_PSRAM +#endif // MicroPython emitters #define MICROPY_PERSISTENT_CODE_LOAD (1) diff --git a/ports/mimxrt/psram.c b/ports/mimxrt/psram.c new file mode 100644 index 0000000000000..f19d4f1d26557 --- /dev/null +++ b/ports/mimxrt/psram.c @@ -0,0 +1,265 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Paul Stoffregen + * 2026 Dryw Wade + * + * 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 implementation is adapted from here: +// https://github.com/PaulStoffregen/cores/blob/10025393e83ca9f4dc5646643a41cb2f32022ae4/teensy4/startup.c#L421-L615 +#include "py/mphal.h" +#include "fsl_flexspi.h" + +#if MICROPY_HW_ENABLE_PSRAM + +/*! + * @brief Clock divider value. + * + * See https://www.pjrc.com/teensy/IMXRT1060RM_rev3_annotations.pdf (p1010 and p1050) + */ +typedef enum _clock_mux_value { + kCLOCK_Flexspi2Mux_396MHz = 0U, /*!< FLEXSPI2 clock source is PLL2 PFD2. */ + kCLOCK_Flexspi2Mux_720MHz = 1U, /*!< FLEXSPI2 clock source is PLL3 PFD0. */ + kCLOCK_Flexspi2Mux_664_62MHz = 2U, /*!< FLEXSPI2 clock source is PLL3 PFD1. */ + kCLOCK_Flexspi2Mux_528MHz = 3U, /*!< FLEXSPI2 clock source is PLL2 (pll2_main_clk). */ +} clock_mux_value_t; + +static void flexspi2_command(uint32_t index, uint32_t addr, flexspi_port_t port) { + flexspi_transfer_t xfer = { + .deviceAddress = addr, + .port = port, + .cmdType = kFLEXSPI_Command, + .seqIndex = index, + .SeqNumber = 1, + .data = NULL, + .dataSize = 0, + }; + FLEXSPI_TransferBlocking(FLEXSPI2, &xfer); + FLEXSPI_ClearInterruptStatusFlags(FLEXSPI2, kFLEXSPI_IpCommandExecutionDoneFlag); +} + +static uint32_t flexspi2_psram_id(uint32_t addr, flexspi_port_t port) { + uint32_t id = 0; + flexspi_transfer_t xfer = { + .deviceAddress = addr, + .port = port, + .cmdType = kFLEXSPI_Read, + .seqIndex = 3, + .SeqNumber = 1, + .data = &id, + .dataSize = 4, + }; + FLEXSPI_TransferBlocking(FLEXSPI2, &xfer); + FLEXSPI_ClearInterruptStatusFlags(FLEXSPI2, + kFLEXSPI_IpCommandExecutionDoneFlag | kFLEXSPI_IpRxFifoWatermarkAvailableFlag); + return id; +} + +/** + * \return size of PSRAM in MBytes, or 0 if not present + */ +static uint8_t flexspi2_psram_size(uint32_t addr, flexspi_port_t port) { + uint8_t result = 0; // assume we don't have PSRAM at this address + flexspi2_command(0, addr, port); // exit quad mode + flexspi2_command(1, addr, port); // reset enable + flexspi2_command(2, addr, port); // reset (is this really necessary?) + uint32_t id = flexspi2_psram_id(addr, port); + + switch (id & 0xFFFF) + { + default: + break; + + case 0x5D0D: // AP / Ipus / ESP / Lyontek + result = 8; + break; + + case 0x5D9D: // ISSI + switch ((id >> 21) & 0x7) // get size (Datasheet Table 6.2) + { + case 0b011: + result = 8; + break; + case 0b100: + result = 16; + break; + } + break; + } + + return result; +} + +size_t configure_external_ram() { + // initialize pins + IOMUXC->SW_PAD_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_22] = 0x1B0F9; // 100K pullup, strong drive, max speed, hyst + IOMUXC->SW_PAD_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_23] = 0x110F9; // keeper, strong drive, max speed, hyst + IOMUXC->SW_PAD_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_24] = 0x1B0F9; // 100K pullup, strong drive, max speed, hyst + IOMUXC->SW_PAD_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_25] = 0x100F9; // strong drive, max speed, hyst + IOMUXC->SW_PAD_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_26] = 0x170F9; // 47K pullup, strong drive, max speed, hyst + IOMUXC->SW_PAD_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_27] = 0x170F9; // 47K pullup, strong drive, max speed, hyst + IOMUXC->SW_PAD_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_28] = 0x170F9; // 47K pullup, strong drive, max speed, hyst + IOMUXC->SW_PAD_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_29] = 0x170F9; // 47K pullup, strong drive, max speed, hyst + + IOMUXC->SW_MUX_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_22] = 8 | 0x10; // ALT1 = FLEXSPI2_A_SS1_B (Flash) + IOMUXC->SW_MUX_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_23] = 8 | 0x10; // ALT1 = FLEXSPI2_A_DQS + IOMUXC->SW_MUX_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_24] = 8 | 0x10; // ALT1 = FLEXSPI2_A_SS0_B (RAM) + IOMUXC->SW_MUX_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_25] = 8 | 0x10; // ALT1 = FLEXSPI2_A_SCLK + IOMUXC->SW_MUX_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_26] = 8 | 0x10; // ALT1 = FLEXSPI2_A_DATA0 + IOMUXC->SW_MUX_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_27] = 8 | 0x10; // ALT1 = FLEXSPI2_A_DATA1 + IOMUXC->SW_MUX_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_28] = 8 | 0x10; // ALT1 = FLEXSPI2_A_DATA2 + IOMUXC->SW_MUX_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_29] = 8 | 0x10; // ALT1 = FLEXSPI2_A_DATA3 + + IOMUXC->SELECT_INPUT_1[kIOMUXC_FLEXSPI2_IPP_IND_DQS_FA_SELECT_INPUT] = 1; // GPIO_EMC_23 for Mode: ALT8, pg 986 + IOMUXC->SELECT_INPUT_1[kIOMUXC_FLEXSPI2_IPP_IND_IO_FA_BIT0_SELECT_INPUT] = 1; // GPIO_EMC_26 for Mode: ALT8 + IOMUXC->SELECT_INPUT_1[kIOMUXC_FLEXSPI2_IPP_IND_IO_FA_BIT1_SELECT_INPUT] = 1; // GPIO_EMC_27 for Mode: ALT8 + IOMUXC->SELECT_INPUT_1[kIOMUXC_FLEXSPI2_IPP_IND_IO_FA_BIT2_SELECT_INPUT] = 1; // GPIO_EMC_28 for Mode: ALT8 + IOMUXC->SELECT_INPUT_1[kIOMUXC_FLEXSPI2_IPP_IND_IO_FA_BIT3_SELECT_INPUT] = 1; // GPIO_EMC_29 for Mode: ALT8 + IOMUXC->SELECT_INPUT_1[kIOMUXC_FLEXSPI2_IPP_IND_SCK_FA_SELECT_INPUT] = 1; // GPIO_EMC_25 for Mode: ALT8 + + // turn on clock (QSPI flash & PSRAM chips usually spec max clock 100 to 133 MHz) + // CLOCK_SetDiv(kCLOCK_Flexspi2Div, kCLOCK_Flexspi2DivBy6); // 88.0 MHz + // CLOCK_SetMux(kCLOCK_Flexspi2Mux, kCLOCK_Flexspi2Mux_528MHz); // 88.0 MHz + // CLOCK_SetDiv(kCLOCK_Flexspi2Div, kCLOCK_Flexspi2DivBy4); // 99.0 MHz + // CLOCK_SetMux(kCLOCK_Flexspi2Mux, kCLOCK_Flexspi2Mux_396MHz); // 99.0 MHz + // CLOCK_SetDiv(kCLOCK_Flexspi2Div, kCLOCK_Flexspi2DivBy7); // 102.9 MHz + // CLOCK_SetMux(kCLOCK_Flexspi2Mux, kCLOCK_Flexspi2Mux_720MHz); // 102.9 MHz + CLOCK_SetDiv(kCLOCK_Flexspi2Div, kCLOCK_Flexspi2DivBy5); // 105.6 MHz + CLOCK_SetMux(kCLOCK_Flexspi2Mux, kCLOCK_Flexspi2Mux_528MHz); // 105.6 MHz + // CLOCK_SetDiv(kCLOCK_Flexspi2Div, kCLOCK_Flexspi2DivBy6); // 110.8 MHz + // CLOCK_SetMux(kCLOCK_Flexspi2Mux, kCLOCK_Flexspi2Mux_664_62MHz); // 110.8 MHz + // CLOCK_SetDiv(kCLOCK_Flexspi2Div, kCLOCK_Flexspi2DivBy6); // 120.0 MHz + // CLOCK_SetMux(kCLOCK_Flexspi2Mux, kCLOCK_Flexspi2Mux_720MHz); // 120.0 MHz + // CLOCK_SetDiv(kCLOCK_Flexspi2Div, kCLOCK_Flexspi2DivBy4); // 132.0 MHz + // CLOCK_SetMux(kCLOCK_Flexspi2Mux, kCLOCK_Flexspi2Mux_528MHz); // 132.0 MHz + // CLOCK_SetDiv(kCLOCK_Flexspi2Div, kCLOCK_Flexspi2DivBy5); // 144.0 MHz + // CLOCK_SetMux(kCLOCK_Flexspi2Mux, kCLOCK_Flexspi2Mux_720MHz); // 144.0 MHz + // CLOCK_SetDiv(kCLOCK_Flexspi2Div, kCLOCK_Flexspi2DivBy4); // 166.2 MHz + // CLOCK_SetMux(kCLOCK_Flexspi2Mux, kCLOCK_Flexspi2Mux_664_62MHz); // 166.2 MHz + // CLOCK_SetDiv(kCLOCK_Flexspi2Div, kCLOCK_Flexspi2DivBy3); // 176.0 MHz + // CLOCK_SetMux(kCLOCK_Flexspi2Mux, kCLOCK_Flexspi2Mux_528MHz); // 176.0 MHz + + flexspi_config_t flexspi_config = { + .rxSampleClock = kFLEXSPI_ReadSampleClkLoopbackFromDqsPad, + .enableSckFreeRunning = false, + .enableCombination = false, + .enableDoze = false, + .enableHalfSpeedAccess = false, + .enableSckBDiffOpt = false, + .enableSameConfigForAll = false, + .seqTimeoutCycle = 0xFFFF, + .ipGrantTimeoutCycle = 0xFF, + .txWatermark = 0, + .rxWatermark = 0, + .ahbConfig = { + .enableAHBWriteIpTxFifo = false, + .enableAHBWriteIpRxFifo = false, + .ahbGrantTimeoutCycle = 0xFF, + .ahbBusTimeoutCycle = 0xFFFF, + .resumeWaitCycle = 0x20, + .buffer = { + {.priority = 0, .masterIndex = 0, .bufferSize = 512, .enablePrefetch = true}, + {.priority = 0, .masterIndex = 0, .bufferSize = 512, .enablePrefetch = true}, + {.priority = 0, .masterIndex = 0, .bufferSize = 0, .enablePrefetch = false}, + {.priority = 0, .masterIndex = 0, .bufferSize = 0, .enablePrefetch = false}, + }, + .enableClearAHBBufferOpt = false, + .enableReadAddressOpt = false, + .enableAHBPrefetch = false, + .enableAHBBufferable = false, + .enableAHBCachable = false, + }, + }; + FLEXSPI_Init(FLEXSPI2, &flexspi_config); + + FLEXSPI_DisableInterrupts(FLEXSPI2, kFLEXSPI_AllInterruptFlags); + + flexspi_device_config_t flexspi_device_config = { + .flexspiRootClk = 0, + .isSck2Enabled = false, + .flashSize = 1 << 16, // Default value, will be updated later + .CSIntervalUnit = kFLEXSPI_CsIntervalUnit1SckCycle, + .CSInterval = 0, + .CSHoldTime = 1, + .CSSetupTime = 1, + .dataValidTime = 0, + .columnspace = 0, + .enableWordAddress = false, + .AWRSeqIndex = 6, + .AWRSeqNumber = 1, + .ARDSeqIndex = 5, + .ARDSeqNumber = 1, + .AHBWriteWaitUnit = kFLEXSPI_AhbWriteWaitUnit2AhbCycle, + .AHBWriteWaitInterval = 0, + .enableWriteMask = false, + }; + FLEXSPI_SetFlashConfig(FLEXSPI2, &flexspi_device_config, kFLEXSPI_PortA1); + FLEXSPI_SetFlashConfig(FLEXSPI2, &flexspi_device_config, kFLEXSPI_PortA2); + + uint32_t cmd[64] = {0}; + // cmd index 0 = exit QPI mode + cmd[0] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_4PAD, 0xF5, 0, 0, 0); + // cmd index 1 = reset enable + cmd[4] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x66, 0, 0, 0); + // cmd index 2 = reset + cmd[8] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x99, 0, 0, 0); + // cmd index 3 = read ID bytes + cmd[12] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x9F, kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 24); + cmd[13] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 1, 0, 0, 0); + // cmd index 4 = enter QPI mode + cmd[16] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x35, 0, 0, 0); + // cmd index 5 = read QPI + cmd[20] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_4PAD, 0xEB, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 24); + cmd[21] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 6, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 1); + // cmd index 6 = write QPI + cmd[24] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_4PAD, 0x38, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 24); + cmd[25] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 1, 0, 0, 0); + FLEXSPI_UpdateLUT(FLEXSPI2, 0, cmd, 64); + + // Detected PSRAM size in MB + uint8_t external_psram_size = 0; + + // look for the first PSRAM chip + uint8_t size1 = flexspi2_psram_size(0, kFLEXSPI_PortA1); + if (size1 > 0) { + flexspi_device_config.flashSize = size1 << 10; + FLEXSPI_SetFlashConfig(FLEXSPI2, &flexspi_device_config, kFLEXSPI_PortA1); + flexspi2_command(4, 0, kFLEXSPI_PortA1); // enter QPI mode + // look for a second PSRAM chip + uint8_t size2 = flexspi2_psram_size(size1 << 20, kFLEXSPI_PortA2); + external_psram_size = size1 + size2; + if (size2 > 0) { + flexspi_device_config.flashSize = size2 << 10; + FLEXSPI_SetFlashConfig(FLEXSPI2, &flexspi_device_config, kFLEXSPI_PortA2); + flexspi2_command(4, size1 << 20, kFLEXSPI_PortA2); // enter QPI mode + } + } else { + // No PSRAM + external_psram_size = 0; + } + + // Return the size of the PSRAM in bytes + return external_psram_size * 0x100000; +} + +#endif diff --git a/ports/mimxrt/psram.h b/ports/mimxrt/psram.h new file mode 100644 index 0000000000000..e5ecd5aa1643a --- /dev/null +++ b/ports/mimxrt/psram.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Paul Stoffregen + * 2026 Dryw Wade + * + * 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_MIMXRT_PSRAM_H +#define MICROPY_INCLUDED_MIMXRT_PSRAM_H + +#define PSRAM_BASE (0x70000000) + +// Configures external PSRAM if available, returns size in bytes (0 if none). +size_t configure_external_ram(); + +#endif // MICROPY_INCLUDED_MIMXRT_PSRAM_H From b0feb9c7ee54cdf87d69a205fd804d76e65f9bb4 Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Fri, 17 Oct 2025 15:44:37 -0600 Subject: [PATCH 173/177] mimxrt/Makefile: Add CXXFLAGS, and libstdc++ to LDFLAGS. This allows user C modules to be built into the mimxrt port. The change is copied from `samd/Makefile`. Fixes issue #18292. Signed-off-by: Dryw Wade --- ports/mimxrt/Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 576f6e64ac1ec..2c1b41fcde941 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -437,6 +437,15 @@ CFLAGS += \ -Wfloat-conversion \ -Wno-error=unused-parameter +# Flags for optional C++ source code +CXXFLAGS += $(filter-out -std=c99,$(CFLAGS)) + +# TODO make this common -- shouldn't be using these "private" vars from py.mk +ifneq ($(SRC_CXX)$(SRC_USERMOD_CXX)$(SRC_USERMOD_LIB_CXX),) +LIBSTDCPP_FILE_NAME = "$(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a)" +LDFLAGS += -L"$(shell dirname $(LIBSTDCPP_FILE_NAME))" +endif + # Configure respective board flash type # Add hal/flexspi_nor_flash.h or hal/flexspi_hyper_flash.h respectively CFLAGS += -DBOARD_FLASH_OPS_HEADER_H=\"hal/flexspi_$(subst qspi_,,$(FLEXSPI_FLASH_TYPE)).h\" From cf048e0e8ed4ccdd54925d09a9d74cf9d1ec0703 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Mon, 1 Apr 2024 16:13:15 +0800 Subject: [PATCH 174/177] lib/nxp_driver: Update mimxrt SDK to MCUX_2.16.100. Signed-off-by: Andrew Leech --- lib/nxp_driver | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nxp_driver b/lib/nxp_driver index 91b04b34a59f6..a298e8a3cad2d 160000 --- a/lib/nxp_driver +++ b/lib/nxp_driver @@ -1 +1 @@ -Subproject commit 91b04b34a59f6d81661cec6f84611afe6330ce92 +Subproject commit a298e8a3cad2df737de020bbeac4ee2147d189ca From 0e9e66100e99c74caf4aa65f95bc09ba1e77b45c Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 14 Nov 2024 21:55:01 +1100 Subject: [PATCH 175/177] mimxrt/machine_uart: Use a wrapper function to handle IRQ idle support. MicroPython needs to handle the UART idle interrupt, which is not something that the standard SDK provides. This was previously fixed by including in the mimxrt port a modified copy of `fsl_lpuart.c`. This commit changes how that's done by building the unmodified `fsl_lpuart.c` directly from the SDK, and using linker wrapping to override the necessary functions to get at the idle IRQ. As part of this, the code is updated to work with the latest SDK. Signed-off-by: Andrew Leech --- ports/mimxrt/Makefile | 9 +- ports/mimxrt/hal/fsl_lpuart.c | 2013 --------------------------------- ports/mimxrt/machine_uart.c | 72 ++ 3 files changed, 79 insertions(+), 2015 deletions(-) delete mode 100644 ports/mimxrt/hal/fsl_lpuart.c diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 2c1b41fcde941..d2109e2a3be8d 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -137,6 +137,7 @@ SRC_HAL_IMX_C += \ $(MCUX_SDK_DIR)/drivers/lpi2c/fsl_lpi2c.c \ $(MCUX_SDK_DIR)/drivers/lpspi/fsl_lpspi.c \ $(MCUX_SDK_DIR)/drivers/lpspi/fsl_lpspi_edma.c \ + $(MCUX_SDK_DIR)/drivers/lpuart/fsl_lpuart.c \ $(MCUX_SDK_DIR)/drivers/pit/fsl_pit.c \ $(MCUX_SDK_DIR)/drivers/pwm/fsl_pwm.c \ $(MCUX_SDK_DIR)/drivers/sai/fsl_sai.c \ @@ -152,6 +153,10 @@ else SRC_HAL_IMX_C += $(MCU_DIR)/xip/fsl_flexspi_nor_boot.c endif +# UART IRQ wrapper for UART.IRQ_RXIDLE support (see machine_uart.c for implementation details) +# Double wrapping is required because SDK stores function pointers in s_lpuartIsr[] dispatch table +LDFLAGS += --wrap=LPUART_TransferCreateHandle --wrap=LPUART_TransferHandleIRQ + INC_HAL_IMX += \ -I$(TOP)/$(MCU_DIR) \ -I$(TOP)/$(MCU_DIR)/drivers \ @@ -244,7 +249,6 @@ SRC_C += \ eth.c \ fatfs_port.c \ flash.c \ - hal/fsl_lpuart.c \ hal/pwm_backport.c \ help.c \ led.c \ @@ -505,7 +509,8 @@ LDFLAGS += \ --cref \ --gc-sections \ --print-memory-usage \ - -Map=$@.map + -Map=$@.map \ + --wrap=LPUART_TransferCreateHandle --wrap=LPUART_TransferHandleIRQ # LDDEFINES are used for link time adaptation of linker scripts, utilizing # the C preprocessor. Therefore keep LDDEFINES separated from LDFLAGS! diff --git a/ports/mimxrt/hal/fsl_lpuart.c b/ports/mimxrt/hal/fsl_lpuart.c deleted file mode 100644 index 54651c3efbcd4..0000000000000 --- a/ports/mimxrt/hal/fsl_lpuart.c +++ /dev/null @@ -1,2013 +0,0 @@ -/* - * Copyright (c) 2015-2016, Freescale Semiconductor, Inc. - * Copyright 2016-2020 NXP - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "fsl_lpuart.h" - -/******************************************************************************* - * Definitions - ******************************************************************************/ - -/* Component ID definition, used by tools. */ -#ifndef FSL_COMPONENT_ID -#define FSL_COMPONENT_ID "platform.drivers.lpuart" -#endif - -/* LPUART transfer state. */ -enum -{ - kLPUART_TxIdle, /*!< TX idle. */ - kLPUART_TxBusy, /*!< TX busy. */ - kLPUART_RxIdle, /*!< RX idle. */ - kLPUART_RxBusy /*!< RX busy. */ -}; - -/******************************************************************************* - * Prototypes - ******************************************************************************/ -/*! - * @brief Check whether the RX ring buffer is full. - * - * @userData handle LPUART handle pointer. - * @retval true RX ring buffer is full. - * @retval false RX ring buffer is not full. - */ -static bool LPUART_TransferIsRxRingBufferFull(LPUART_Type *base, lpuart_handle_t *handle); - -/*! - * @brief Write to TX register using non-blocking method. - * - * This function writes data to the TX register directly, upper layer must make - * sure the TX register is empty or TX FIFO has empty room before calling this function. - * - * @note This function does not check whether all the data has been sent out to bus, - * so before disable TX, check kLPUART_TransmissionCompleteFlag to ensure the TX is - * finished. - * - * @param base LPUART peripheral base address. - * @param data Start address of the data to write. - * @param length Size of the buffer to be sent. - */ -static void LPUART_WriteNonBlocking(LPUART_Type *base, const uint8_t *data, size_t length); - -/*! - * @brief Read RX register using non-blocking method. - * - * This function reads data from the TX register directly, upper layer must make - * sure the RX register is full or TX FIFO has data before calling this function. - * - * @param base LPUART peripheral base address. - * @param data Start address of the buffer to store the received data. - * @param length Size of the buffer. - */ -static void LPUART_ReadNonBlocking(LPUART_Type *base, uint8_t *data, size_t length); - -/******************************************************************************* - * Variables - ******************************************************************************/ -/* Array of LPUART peripheral base address. */ -static LPUART_Type *const s_lpuartBases[] = LPUART_BASE_PTRS; -/* Array of LPUART handle. */ -void *s_lpuartHandle[ARRAY_SIZE(s_lpuartBases)]; -/* Array of LPUART IRQ number. */ -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -static const IRQn_Type s_lpuartRxIRQ[] = LPUART_RX_IRQS; -const IRQn_Type s_lpuartTxIRQ[] = LPUART_TX_IRQS; -#else -const IRQn_Type s_lpuartIRQ[] = LPUART_RX_TX_IRQS; -#endif -#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) -/* Array of LPUART clock name. */ -static const clock_ip_name_t s_lpuartClock[] = LPUART_CLOCKS; - -#if defined(LPUART_PERIPH_CLOCKS) -/* Array of LPUART functional clock name. */ -static const clock_ip_name_t s_lpuartPeriphClocks[] = LPUART_PERIPH_CLOCKS; -#endif - -#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ - -/* LPUART ISR for transactional APIs. */ -#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) -lpuart_isr_t s_lpuartIsr = (lpuart_isr_t)DefaultISR; -#else -lpuart_isr_t s_lpuartIsr; -#endif - -/******************************************************************************* - * Code - ******************************************************************************/ -/*! - * brief Get the LPUART instance from peripheral base address. - * - * param base LPUART peripheral base address. - * return LPUART instance. - */ -uint32_t LPUART_GetInstance(LPUART_Type *base) { - uint32_t instance; - - /* Find the instance index from base address mappings. */ - for (instance = 0U; instance < ARRAY_SIZE(s_lpuartBases); instance++) { - if (s_lpuartBases[instance] == base) { - break; - } - } - - assert(instance < ARRAY_SIZE(s_lpuartBases)); - - return instance; -} - -/*! - * brief Get the length of received data in RX ring buffer. - * - * userData handle LPUART handle pointer. - * return Length of received data in RX ring buffer. - */ -size_t LPUART_TransferGetRxRingBufferLength(LPUART_Type *base, lpuart_handle_t *handle) { - assert(NULL != handle); - - size_t size; - size_t tmpRxRingBufferSize = handle->rxRingBufferSize; - uint16_t tmpRxRingBufferTail = handle->rxRingBufferTail; - uint16_t tmpRxRingBufferHead = handle->rxRingBufferHead; - - if (tmpRxRingBufferTail > tmpRxRingBufferHead) { - size = ((size_t)tmpRxRingBufferHead + tmpRxRingBufferSize - (size_t)tmpRxRingBufferTail); - } else { - size = ((size_t)tmpRxRingBufferHead - (size_t)tmpRxRingBufferTail); - } - - return size; -} - -static bool LPUART_TransferIsRxRingBufferFull(LPUART_Type *base, lpuart_handle_t *handle) { - assert(NULL != handle); - - bool full; - - if (LPUART_TransferGetRxRingBufferLength(base, handle) == (handle->rxRingBufferSize - 1U)) { - full = true; - } else { - full = false; - } - return full; -} - -static void LPUART_WriteNonBlocking(LPUART_Type *base, const uint8_t *data, size_t length) { - assert(NULL != data); - - size_t i; - - /* The Non Blocking write data API assume user have ensured there is enough space in - peripheral to write. */ - for (i = 0; i < length; i++) { - base->DATA = data[i]; - } -} - -static void LPUART_ReadNonBlocking(LPUART_Type *base, uint8_t *data, size_t length) { - assert(NULL != data); - - size_t i; - #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT - uint32_t ctrl = base->CTRL; - bool isSevenDataBits = (((ctrl & LPUART_CTRL_M7_MASK) != 0U) || - (((ctrl & LPUART_CTRL_M_MASK) == 0U) && ((ctrl & LPUART_CTRL_PE_MASK) != 0U))); - #endif - - /* The Non Blocking read data API assume user have ensured there is enough space in - peripheral to write. */ - for (i = 0; i < length; i++) { - #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT - if (isSevenDataBits) { - data[i] = (uint8_t)(base->DATA & 0x7FU); - } else { - data[i] = (uint8_t)base->DATA; - } - #else - data[i] = (uint8_t)(base->DATA); - #endif - } -} - -/*! - * brief Initializes an LPUART instance with the user configuration structure and the peripheral clock. - * - * This function configures the LPUART module with user-defined settings. Call the LPUART_GetDefaultConfig() function - * to configure the configuration structure and get the default configuration. - * The example below shows how to use this API to configure the LPUART. - * code - * lpuart_config_t lpuartConfig; - * lpuartConfig.baudRate_Bps = 115200U; - * lpuartConfig.parityMode = kLPUART_ParityDisabled; - * lpuartConfig.dataBitsCount = kLPUART_EightDataBits; - * lpuartConfig.isMsb = false; - * lpuartConfig.stopBitCount = kLPUART_OneStopBit; - * lpuartConfig.txFifoWatermark = 0; - * lpuartConfig.rxFifoWatermark = 1; - * LPUART_Init(LPUART1, &lpuartConfig, 20000000U); - * endcode - * - * param base LPUART peripheral base address. - * param config Pointer to a user-defined configuration structure. - * param srcClock_Hz LPUART clock source frequency in HZ. - * retval kStatus_LPUART_BaudrateNotSupport Baudrate is not support in current clock source. - * retval kStatus_Success LPUART initialize succeed - */ -status_t LPUART_Init(LPUART_Type *base, const lpuart_config_t *config, uint32_t srcClock_Hz) { - assert(NULL != config); - assert(0U < config->baudRate_Bps); - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - assert((uint8_t)FSL_FEATURE_LPUART_FIFO_SIZEn(base) >= config->txFifoWatermark); - assert((uint8_t)FSL_FEATURE_LPUART_FIFO_SIZEn(base) >= config->rxFifoWatermark); - #endif - - status_t status = kStatus_Success; - uint32_t temp; - uint16_t sbr, sbrTemp; - uint8_t osr, osrTemp; - uint32_t tempDiff, calculatedBaud, baudDiff; - - /* This LPUART instantiation uses a slightly different baud rate calculation - * The idea is to use the best OSR (over-sampling rate) possible - * Note, OSR is typically hard-set to 16 in other LPUART instantiations - * loop to find the best OSR value possible, one that generates minimum baudDiff - * iterate through the rest of the supported values of OSR */ - - baudDiff = config->baudRate_Bps; - osr = 0U; - sbr = 0U; - for (osrTemp = 4U; osrTemp <= 32U; osrTemp++) { - /* calculate the temporary sbr value */ - sbrTemp = (uint16_t)((srcClock_Hz * 10U / (config->baudRate_Bps * (uint32_t)osrTemp) + 5U) / 10U); - /*set sbrTemp to 1 if the sourceClockInHz can not satisfy the desired baud rate*/ - if (sbrTemp == 0U) { - sbrTemp = 1U; - } - /* Calculate the baud rate based on the temporary OSR and SBR values */ - calculatedBaud = (srcClock_Hz / ((uint32_t)osrTemp * (uint32_t)sbrTemp)); - tempDiff = calculatedBaud > config->baudRate_Bps ? (calculatedBaud - config->baudRate_Bps) : - (config->baudRate_Bps - calculatedBaud); - - if (tempDiff <= baudDiff) { - baudDiff = tempDiff; - osr = osrTemp; /* update and store the best OSR value calculated */ - sbr = sbrTemp; /* update store the best SBR value calculated */ - } - } - - /* Check to see if actual baud rate is within 3% of desired baud rate - * based on the best calculate OSR value */ - if (baudDiff > ((config->baudRate_Bps / 100U) * 3U)) { - /* Unacceptable baud rate difference of more than 3%*/ - status = kStatus_LPUART_BaudrateNotSupport; - } else { - #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) - - uint32_t instance = LPUART_GetInstance(base); - - /* Enable lpuart clock */ - (void)CLOCK_EnableClock(s_lpuartClock[instance]); - #if defined(LPUART_PERIPH_CLOCKS) - (void)CLOCK_EnableClock(s_lpuartPeriphClocks[instance]); - #endif - - #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ - - #if defined(FSL_FEATURE_LPUART_HAS_GLOBAL) && FSL_FEATURE_LPUART_HAS_GLOBAL - /*Reset all internal logic and registers, except the Global Register */ - LPUART_SoftwareReset(base); - #else - /* Disable LPUART TX RX before setting. */ - base->CTRL &= ~(LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK); - #endif - - temp = base->BAUD; - - /* Acceptable baud rate, check if OSR is between 4x and 7x oversampling. - * If so, then "BOTHEDGE" sampling must be turned on */ - if ((osr > 3U) && (osr < 8U)) { - temp |= LPUART_BAUD_BOTHEDGE_MASK; - } - - /* program the osr value (bit value is one less than actual value) */ - temp &= ~LPUART_BAUD_OSR_MASK; - temp |= LPUART_BAUD_OSR((uint32_t)osr - 1UL); - - /* write the sbr value to the BAUD registers */ - temp &= ~LPUART_BAUD_SBR_MASK; - base->BAUD = temp | LPUART_BAUD_SBR(sbr); - - /* Set bit count and parity mode. */ - base->BAUD &= ~LPUART_BAUD_M10_MASK; - - temp = base->CTRL & ~(LPUART_CTRL_PE_MASK | LPUART_CTRL_PT_MASK | LPUART_CTRL_M_MASK | LPUART_CTRL_ILT_MASK | - LPUART_CTRL_IDLECFG_MASK); - - temp |= (uint8_t)config->parityMode | LPUART_CTRL_IDLECFG(config->rxIdleConfig) | - LPUART_CTRL_ILT(config->rxIdleType); - - #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT - if (kLPUART_SevenDataBits == config->dataBitsCount) { - if (kLPUART_ParityDisabled != config->parityMode) { - temp &= ~LPUART_CTRL_M7_MASK; /* Seven data bits and one parity bit */ - } else { - temp |= LPUART_CTRL_M7_MASK; - } - } else - #endif - { - if (kLPUART_ParityDisabled != config->parityMode) { - temp |= LPUART_CTRL_M_MASK; /* Eight data bits and one parity bit */ - } - } - - base->CTRL = temp; - - #if defined(FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT) && FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT - /* set stop bit per char */ - temp = base->BAUD & ~LPUART_BAUD_SBNS_MASK; - base->BAUD = temp | LPUART_BAUD_SBNS((uint8_t)config->stopBitCount); - #endif - - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - /* Set tx/rx WATER watermark - Note: - Take care of the RX FIFO, RX interrupt request only assert when received bytes - equal or more than RX water mark, there is potential issue if RX water - mark larger than 1. - For example, if RX FIFO water mark is 2, upper layer needs 5 bytes and - 5 bytes are received. the last byte will be saved in FIFO but not trigger - RX interrupt because the water mark is 2. - */ - base->WATER = (((uint32_t)(config->rxFifoWatermark) << 16U) | config->txFifoWatermark); - - /* Enable tx/rx FIFO */ - base->FIFO |= (LPUART_FIFO_TXFE_MASK | LPUART_FIFO_RXFE_MASK); - - /* Flush FIFO */ - base->FIFO |= (LPUART_FIFO_TXFLUSH_MASK | LPUART_FIFO_RXFLUSH_MASK); - #endif - - /* Clear all status flags */ - temp = (LPUART_STAT_RXEDGIF_MASK | LPUART_STAT_IDLE_MASK | LPUART_STAT_OR_MASK | LPUART_STAT_NF_MASK | - LPUART_STAT_FE_MASK | LPUART_STAT_PF_MASK); - - #if defined(FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT) && FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT - temp |= LPUART_STAT_LBKDIF_MASK; - #endif - - #if defined(FSL_FEATURE_LPUART_HAS_ADDRESS_MATCHING) && FSL_FEATURE_LPUART_HAS_ADDRESS_MATCHING - temp |= (LPUART_STAT_MA1F_MASK | LPUART_STAT_MA2F_MASK); - #endif - - #if defined(FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT) && FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT - /* Set the CTS configuration/TX CTS source. */ - base->MODIR |= LPUART_MODIR_TXCTSC(config->txCtsConfig) | LPUART_MODIR_TXCTSSRC(config->txCtsSource); - if (true == config->enableRxRTS) { - /* Enable the receiver RTS(request-to-send) function. */ - base->MODIR |= LPUART_MODIR_RXRTSE_MASK; - } - if (true == config->enableTxCTS) { - /* Enable the CTS(clear-to-send) function. */ - base->MODIR |= LPUART_MODIR_TXCTSE_MASK; - } - #endif - - /* Set data bits order. */ - if (true == config->isMsb) { - temp |= LPUART_STAT_MSBF_MASK; - } else { - temp &= ~LPUART_STAT_MSBF_MASK; - } - - base->STAT |= temp; - - /* Enable TX/RX base on configure structure. */ - temp = base->CTRL; - if (true == config->enableTx) { - temp |= LPUART_CTRL_TE_MASK; - } - - if (true == config->enableRx) { - temp |= LPUART_CTRL_RE_MASK; - } - - base->CTRL = temp; - } - - return status; -} -/*! - * brief Deinitializes a LPUART instance. - * - * This function waits for transmit to complete, disables TX and RX, and disables the LPUART clock. - * - * param base LPUART peripheral base address. - */ -void LPUART_Deinit(LPUART_Type *base) { - uint32_t temp; - - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - /* Wait tx FIFO send out*/ - while (0U != ((base->WATER & LPUART_WATER_TXCOUNT_MASK) >> LPUART_WATER_TXWATER_SHIFT)) { - } - #endif - /* Wait last char shift out */ - while (0U == (base->STAT & LPUART_STAT_TC_MASK)) { - } - - /* Clear all status flags */ - temp = (LPUART_STAT_RXEDGIF_MASK | LPUART_STAT_IDLE_MASK | LPUART_STAT_OR_MASK | LPUART_STAT_NF_MASK | - LPUART_STAT_FE_MASK | LPUART_STAT_PF_MASK); - - #if defined(FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT) && FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT - temp |= LPUART_STAT_LBKDIF_MASK; - #endif - - #if defined(FSL_FEATURE_LPUART_HAS_ADDRESS_MATCHING) && FSL_FEATURE_LPUART_HAS_ADDRESS_MATCHING - temp |= (LPUART_STAT_MA1F_MASK | LPUART_STAT_MA2F_MASK); - #endif - - base->STAT |= temp; - - /* Disable the module. */ - base->CTRL = 0U; - - #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) - uint32_t instance = LPUART_GetInstance(base); - - /* Disable lpuart clock */ - (void)CLOCK_DisableClock(s_lpuartClock[instance]); - - #if defined(LPUART_PERIPH_CLOCKS) - (void)CLOCK_DisableClock(s_lpuartPeriphClocks[instance]); - #endif - - #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ -} - -/*! - * brief Gets the default configuration structure. - * - * This function initializes the LPUART configuration structure to a default value. The default - * values are: - * lpuartConfig->baudRate_Bps = 115200U; - * lpuartConfig->parityMode = kLPUART_ParityDisabled; - * lpuartConfig->dataBitsCount = kLPUART_EightDataBits; - * lpuartConfig->isMsb = false; - * lpuartConfig->stopBitCount = kLPUART_OneStopBit; - * lpuartConfig->txFifoWatermark = 0; - * lpuartConfig->rxFifoWatermark = 1; - * lpuartConfig->rxIdleType = kLPUART_IdleTypeStartBit; - * lpuartConfig->rxIdleConfig = kLPUART_IdleCharacter1; - * lpuartConfig->enableTx = false; - * lpuartConfig->enableRx = false; - * - * param config Pointer to a configuration structure. - */ -void LPUART_GetDefaultConfig(lpuart_config_t *config) { - assert(NULL != config); - - /* Initializes the configure structure to zero. */ - (void)memset(config, 0, sizeof(*config)); - - config->baudRate_Bps = 115200U; - config->parityMode = kLPUART_ParityDisabled; - config->dataBitsCount = kLPUART_EightDataBits; - config->isMsb = false; - #if defined(FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT) && FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT - config->stopBitCount = kLPUART_OneStopBit; - #endif - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - config->txFifoWatermark = 0U; - config->rxFifoWatermark = 0U; - #endif - #if defined(FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT) && FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT - config->enableRxRTS = false; - config->enableTxCTS = false; - config->txCtsConfig = kLPUART_CtsSampleAtStart; - config->txCtsSource = kLPUART_CtsSourcePin; - #endif - config->rxIdleType = kLPUART_IdleTypeStartBit; - config->rxIdleConfig = kLPUART_IdleCharacter1; - config->enableTx = false; - config->enableRx = false; -} - -/*! - * brief Sets the LPUART instance baudrate. - * - * This function configures the LPUART module baudrate. This function is used to update - * the LPUART module baudrate after the LPUART module is initialized by the LPUART_Init. - * code - * LPUART_SetBaudRate(LPUART1, 115200U, 20000000U); - * endcode - * - * param base LPUART peripheral base address. - * param baudRate_Bps LPUART baudrate to be set. - * param srcClock_Hz LPUART clock source frequency in HZ. - * retval kStatus_LPUART_BaudrateNotSupport Baudrate is not supported in the current clock source. - * retval kStatus_Success Set baudrate succeeded. - */ -status_t LPUART_SetBaudRate(LPUART_Type *base, uint32_t baudRate_Bps, uint32_t srcClock_Hz) { - assert(0U < baudRate_Bps); - - status_t status = kStatus_Success; - uint32_t temp, oldCtrl; - uint16_t sbr, sbrTemp; - uint8_t osr, osrTemp; - uint32_t tempDiff, calculatedBaud, baudDiff; - - /* This LPUART instantiation uses a slightly different baud rate calculation - * The idea is to use the best OSR (over-sampling rate) possible - * Note, OSR is typically hard-set to 16 in other LPUART instantiations - * loop to find the best OSR value possible, one that generates minimum baudDiff - * iterate through the rest of the supported values of OSR */ - - baudDiff = baudRate_Bps; - osr = 0U; - sbr = 0U; - for (osrTemp = 4U; osrTemp <= 32U; osrTemp++) { - /* calculate the temporary sbr value */ - sbrTemp = (uint16_t)((srcClock_Hz * 10U / (baudRate_Bps * (uint32_t)osrTemp) + 5U) / 10U); - /*set sbrTemp to 1 if the sourceClockInHz can not satisfy the desired baud rate*/ - if (sbrTemp == 0U) { - sbrTemp = 1U; - } - /* Calculate the baud rate based on the temporary OSR and SBR values */ - calculatedBaud = srcClock_Hz / ((uint32_t)osrTemp * (uint32_t)sbrTemp); - - tempDiff = calculatedBaud > baudRate_Bps ? (calculatedBaud - baudRate_Bps) : (baudRate_Bps - calculatedBaud); - - if (tempDiff <= baudDiff) { - baudDiff = tempDiff; - osr = osrTemp; /* update and store the best OSR value calculated */ - sbr = sbrTemp; /* update store the best SBR value calculated */ - } - } - - /* Check to see if actual baud rate is within 3% of desired baud rate - * based on the best calculate OSR value */ - if (baudDiff < (uint32_t)((baudRate_Bps / 100U) * 3U)) { - /* Store CTRL before disable Tx and Rx */ - oldCtrl = base->CTRL; - - /* Disable LPUART TX RX before setting. */ - base->CTRL &= ~(LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK); - - temp = base->BAUD; - - /* Acceptable baud rate, check if OSR is between 4x and 7x oversampling. - * If so, then "BOTHEDGE" sampling must be turned on */ - if ((osr > 3U) && (osr < 8U)) { - temp |= LPUART_BAUD_BOTHEDGE_MASK; - } - - /* program the osr value (bit value is one less than actual value) */ - temp &= ~LPUART_BAUD_OSR_MASK; - temp |= LPUART_BAUD_OSR((uint32_t)osr - 1UL); - - /* write the sbr value to the BAUD registers */ - temp &= ~LPUART_BAUD_SBR_MASK; - base->BAUD = temp | LPUART_BAUD_SBR(sbr); - - /* Restore CTRL. */ - base->CTRL = oldCtrl; - } else { - /* Unacceptable baud rate difference of more than 3%*/ - status = kStatus_LPUART_BaudrateNotSupport; - } - - return status; -} - -/*! - * brief Enable 9-bit data mode for LPUART. - * - * This function set the 9-bit mode for LPUART module. The 9th bit is not used for parity thus can be modified by user. - * - * param base LPUART peripheral base address. - * param enable true to enable, false to disable. - */ -void LPUART_Enable9bitMode(LPUART_Type *base, bool enable) { - assert(base != NULL); - - uint32_t temp = 0U; - - if (enable) { - /* Set LPUART_CTRL_M for 9-bit mode, clear LPUART_CTRL_PE to disable parity. */ - temp = base->CTRL & ~((uint32_t)LPUART_CTRL_PE_MASK | (uint32_t)LPUART_CTRL_M_MASK); - temp |= (uint32_t)LPUART_CTRL_M_MASK; - base->CTRL = temp; - } else { - /* Clear LPUART_CTRL_M. */ - base->CTRL &= ~(uint32_t)LPUART_CTRL_M_MASK; - } - #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT - /* Clear LPUART_CTRL_M7 to disable 7-bit mode. */ - base->CTRL &= ~(uint32_t)LPUART_CTRL_M7_MASK; - #endif - #if defined(FSL_FEATURE_LPUART_HAS_10BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_10BIT_DATA_SUPPORT - /* Clear LPUART_BAUD_M10 to disable 10-bit mode. */ - base->BAUD &= ~(uint32_t)LPUART_BAUD_M10_MASK; - #endif -} - -/*! - * brief Transmit an address frame in 9-bit data mode. - * - * param base LPUART peripheral base address. - * param address LPUART slave address. - */ -void LPUART_SendAddress(LPUART_Type *base, uint8_t address) { - assert(base != NULL); - - uint32_t temp = base->DATA & 0xFFFFFC00UL; - temp |= ((uint32_t)address | (1UL << LPUART_DATA_R8T8_SHIFT)); - base->DATA = temp; -} - -/*! - * brief Enables LPUART interrupts according to a provided mask. - * - * This function enables the LPUART interrupts according to a provided mask. The mask - * is a logical OR of enumeration members. See the ref _lpuart_interrupt_enable. - * This examples shows how to enable TX empty interrupt and RX full interrupt: - * code - * LPUART_EnableInterrupts(LPUART1,kLPUART_TxDataRegEmptyInterruptEnable | kLPUART_RxDataRegFullInterruptEnable); - * endcode - * - * param base LPUART peripheral base address. - * param mask The interrupts to enable. Logical OR of ref _uart_interrupt_enable. - */ -void LPUART_EnableInterrupts(LPUART_Type *base, uint32_t mask) { - /* Only consider the real interrupt enable bits. */ - mask &= (uint32_t)kLPUART_AllInterruptEnable; - - /* Check int enable bits in base->BAUD */ - uint32_t tempReg = base->BAUD; - #if defined(FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT) && FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT - tempReg |= ((mask << 8U) & LPUART_BAUD_LBKDIE_MASK); - /* Clear bit 7 from mask */ - mask &= ~(uint32_t)kLPUART_LinBreakInterruptEnable; - #endif - tempReg |= ((mask << 8U) & LPUART_BAUD_RXEDGIE_MASK); - /* Clear bit 6 from mask */ - mask &= ~(uint32_t)kLPUART_RxActiveEdgeInterruptEnable; - base->BAUD = tempReg; - - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - /* Check int enable bits in base->FIFO */ - base->FIFO = (base->FIFO & ~(LPUART_FIFO_TXOF_MASK | LPUART_FIFO_RXUF_MASK)) | - (mask & (LPUART_FIFO_TXOFE_MASK | LPUART_FIFO_RXUFE_MASK)); - /* Clear bit 9 and bit 8 from mask */ - mask &= ~((uint32_t)kLPUART_TxFifoOverflowInterruptEnable | (uint32_t)kLPUART_RxFifoUnderflowInterruptEnable); - #endif - - /* Check int enable bits in base->CTRL */ - base->CTRL |= mask; -} - -/*! - * brief Disables LPUART interrupts according to a provided mask. - * - * This function disables the LPUART interrupts according to a provided mask. The mask - * is a logical OR of enumeration members. See ref _lpuart_interrupt_enable. - * This example shows how to disable the TX empty interrupt and RX full interrupt: - * code - * LPUART_DisableInterrupts(LPUART1,kLPUART_TxDataRegEmptyInterruptEnable | kLPUART_RxDataRegFullInterruptEnable); - * endcode - * - * param base LPUART peripheral base address. - * param mask The interrupts to disable. Logical OR of ref _lpuart_interrupt_enable. - */ -void LPUART_DisableInterrupts(LPUART_Type *base, uint32_t mask) { - /* Only consider the real interrupt enable bits. */ - mask &= (uint32_t)kLPUART_AllInterruptEnable; - /* Check int enable bits in base->BAUD */ - uint32_t tempReg = base->BAUD; - #if defined(FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT) && FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT - tempReg &= ~((mask << 8U) & LPUART_BAUD_LBKDIE_MASK); - /* Clear bit 7 from mask */ - mask &= ~(uint32_t)kLPUART_LinBreakInterruptEnable; - #endif - tempReg &= ~((mask << 8U) & LPUART_BAUD_RXEDGIE_MASK); - /* Clear bit 6 from mask */ - mask &= ~(uint32_t)kLPUART_RxActiveEdgeInterruptEnable; - base->BAUD = tempReg; - - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - /* Check int enable bits in base->FIFO */ - base->FIFO = (base->FIFO & ~(LPUART_FIFO_TXOF_MASK | LPUART_FIFO_RXUF_MASK)) & - ~(mask & (LPUART_FIFO_TXOFE_MASK | LPUART_FIFO_RXUFE_MASK)); - /* Clear bit 9 and bit 8 from mask */ - mask &= ~((uint32_t)kLPUART_TxFifoOverflowInterruptEnable | (uint32_t)kLPUART_RxFifoUnderflowInterruptEnable); - #endif - - /* Check int enable bits in base->CTRL */ - base->CTRL &= ~mask; -} - -/*! - * brief Gets enabled LPUART interrupts. - * - * This function gets the enabled LPUART interrupts. The enabled interrupts are returned - * as the logical OR value of the enumerators ref _lpuart_interrupt_enable. To check - * a specific interrupt enable status, compare the return value with enumerators - * in ref _lpuart_interrupt_enable. - * For example, to check whether the TX empty interrupt is enabled: - * code - * uint32_t enabledInterrupts = LPUART_GetEnabledInterrupts(LPUART1); - * - * if (kLPUART_TxDataRegEmptyInterruptEnable & enabledInterrupts) - * { - * ... - * } - * endcode - * - * param base LPUART peripheral base address. - * return LPUART interrupt flags which are logical OR of the enumerators in ref _lpuart_interrupt_enable. - */ -uint32_t LPUART_GetEnabledInterrupts(LPUART_Type *base) { - /* Check int enable bits in base->CTRL */ - uint32_t temp = (uint32_t)(base->CTRL & (uint32_t)kLPUART_AllInterruptEnable); - - /* Check int enable bits in base->BAUD */ - temp = (temp & ~(uint32_t)kLPUART_RxActiveEdgeInterruptEnable) | ((base->BAUD & LPUART_BAUD_RXEDGIE_MASK) >> 8U); - #if defined(FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT) && FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT - temp = (temp & ~(uint32_t)kLPUART_LinBreakInterruptEnable) | ((base->BAUD & LPUART_BAUD_LBKDIE_MASK) >> 8U); - #endif - - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - /* Check int enable bits in base->FIFO */ - temp = - (temp & ~((uint32_t)kLPUART_TxFifoOverflowInterruptEnable | (uint32_t)kLPUART_RxFifoUnderflowInterruptEnable)) | - (base->FIFO & (LPUART_FIFO_TXOFE_MASK | LPUART_FIFO_RXUFE_MASK)); - #endif - - return temp; -} - -/*! - * brief Gets LPUART status flags. - * - * This function gets all LPUART status flags. The flags are returned as the logical - * OR value of the enumerators ref _lpuart_flags. To check for a specific status, - * compare the return value with enumerators in the ref _lpuart_flags. - * For example, to check whether the TX is empty: - * code - * if (kLPUART_TxDataRegEmptyFlag & LPUART_GetStatusFlags(LPUART1)) - * { - * ... - * } - * endcode - * - * param base LPUART peripheral base address. - * return LPUART status flags which are ORed by the enumerators in the _lpuart_flags. - */ -uint32_t LPUART_GetStatusFlags(LPUART_Type *base) { - uint32_t temp; - temp = base->STAT; - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - temp |= (base->FIFO & - (LPUART_FIFO_TXEMPT_MASK | LPUART_FIFO_RXEMPT_MASK | LPUART_FIFO_TXOF_MASK | LPUART_FIFO_RXUF_MASK)) >> - 16U; - #endif - /* Only keeps the status bits */ - temp &= (uint32_t)kLPUART_AllFlags; - return temp; -} - -/*! - * brief Clears status flags with a provided mask. - * - * This function clears LPUART status flags with a provided mask. Automatically cleared flags - * can't be cleared by this function. - * Flags that can only cleared or set by hardware are: - * kLPUART_TxDataRegEmptyFlag, kLPUART_TransmissionCompleteFlag, kLPUART_RxDataRegFullFlag, - * kLPUART_RxActiveFlag, kLPUART_NoiseErrorInRxDataRegFlag, kLPUART_ParityErrorInRxDataRegFlag, - * kLPUART_TxFifoEmptyFlag,kLPUART_RxFifoEmptyFlag - * Note: This API should be called when the Tx/Rx is idle, otherwise it takes no effects. - * - * param base LPUART peripheral base address. - * param mask the status flags to be cleared. The user can use the enumerators in the - * _lpuart_status_flag_t to do the OR operation and get the mask. - * return 0 succeed, others failed. - * retval kStatus_LPUART_FlagCannotClearManually The flag can't be cleared by this function but - * it is cleared automatically by hardware. - * retval kStatus_Success Status in the mask are cleared. - */ -status_t LPUART_ClearStatusFlags(LPUART_Type *base, uint32_t mask) { - uint32_t temp; - status_t status; - - /* Only deal with the clearable flags */ - mask &= (uint32_t)kLPUART_AllClearFlags; - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - /* Status bits in FIFO register */ - if ((mask & ((uint32_t)kLPUART_TxFifoOverflowFlag | (uint32_t)kLPUART_RxFifoUnderflowFlag)) != 0U) { - /* Get the FIFO register value and mask the rx/tx FIFO flush bits and the status bits that can be W1C in case - they are written 1 accidentally. */ - temp = (uint32_t)base->FIFO; - temp &= (uint32_t)( - ~(LPUART_FIFO_TXFLUSH_MASK | LPUART_FIFO_RXFLUSH_MASK | LPUART_FIFO_TXOF_MASK | LPUART_FIFO_RXUF_MASK)); - temp |= (mask << 16U) & (LPUART_FIFO_TXOF_MASK | LPUART_FIFO_RXUF_MASK); - base->FIFO = temp; - } - #endif - /* Status bits in STAT register */ - /* First get the STAT register value and mask all the bits that not represent status, then OR with the status bit - * that is to be W1C */ - temp = (base->STAT & 0x3E000000UL) | mask; - base->STAT = temp; - /* If some flags still pending. */ - if (0U != (mask & LPUART_GetStatusFlags(base))) { - status = kStatus_LPUART_FlagCannotClearManually; - } else { - status = kStatus_Success; - } - - return status; -} - -/*! - * brief Writes to the transmitter register using a blocking method. - * - * This function polls the transmitter register, first waits for the register to be empty or TX FIFO to have room, - * and writes data to the transmitter buffer, then waits for the data to be sent out to bus. - * - * param base LPUART peripheral base address. - * param data Start address of the data to write. - * param length Size of the data to write. - * retval kStatus_LPUART_Timeout Transmission timed out and was aborted. - * retval kStatus_Success Successfully wrote all data. - */ -status_t LPUART_WriteBlocking(LPUART_Type *base, const uint8_t *data, size_t length) { - assert(NULL != data); - - const uint8_t *dataAddress = data; - size_t transferSize = length; - - #if UART_RETRY_TIMES - uint32_t waitTimes; - #endif - - while (0U != transferSize) { - #if UART_RETRY_TIMES - waitTimes = UART_RETRY_TIMES; - while ((0U == (base->STAT & LPUART_STAT_TDRE_MASK)) && (0U != --waitTimes)) - #else - while (0U == (base->STAT & LPUART_STAT_TDRE_MASK)) - #endif - { - } - #if UART_RETRY_TIMES - if (0U == waitTimes) { - return kStatus_LPUART_Timeout; - } - #endif - base->DATA = *(dataAddress); - dataAddress++; - transferSize--; - } - /* Ensure all the data in the transmit buffer are sent out to bus. */ - #if UART_RETRY_TIMES - waitTimes = UART_RETRY_TIMES; - while ((0U == (base->STAT & LPUART_STAT_TC_MASK)) && (0U != --waitTimes)) - #else - while (0U == (base->STAT & LPUART_STAT_TC_MASK)) - #endif - { - } - #if UART_RETRY_TIMES - if (0U == waitTimes) { - return kStatus_LPUART_Timeout; - } - #endif - return kStatus_Success; -} - -/*! - * brief Reads the receiver data register using a blocking method. - * - * This function polls the receiver register, waits for the receiver register full or receiver FIFO - * has data, and reads data from the TX register. - * - * param base LPUART peripheral base address. - * param data Start address of the buffer to store the received data. - * param length Size of the buffer. - * retval kStatus_LPUART_RxHardwareOverrun Receiver overrun happened while receiving data. - * retval kStatus_LPUART_NoiseError Noise error happened while receiving data. - * retval kStatus_LPUART_FramingError Framing error happened while receiving data. - * retval kStatus_LPUART_ParityError Parity error happened while receiving data. - * retval kStatus_LPUART_Timeout Transmission timed out and was aborted. - * retval kStatus_Success Successfully received all data. - */ -status_t LPUART_ReadBlocking(LPUART_Type *base, uint8_t *data, size_t length) { - assert(NULL != data); - - status_t status = kStatus_Success; - uint32_t statusFlag; - uint8_t *dataAddress = data; - - #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT - uint32_t ctrl = base->CTRL; - bool isSevenDataBits = (((ctrl & LPUART_CTRL_M7_MASK) != 0U) || - (((ctrl & LPUART_CTRL_M_MASK) == 0U) && ((ctrl & LPUART_CTRL_PE_MASK) != 0U))); - #endif - - #if UART_RETRY_TIMES - uint32_t waitTimes; - #endif - - while (0U != (length--)) { - #if UART_RETRY_TIMES - waitTimes = UART_RETRY_TIMES; - #endif - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - while (0U == ((base->WATER & LPUART_WATER_RXCOUNT_MASK) >> LPUART_WATER_RXCOUNT_SHIFT)) - #else - while (0U == (base->STAT & LPUART_STAT_RDRF_MASK)) - #endif - { - #if UART_RETRY_TIMES - if (0U == --waitTimes) { - status = kStatus_LPUART_Timeout; - break; - } - #endif - statusFlag = LPUART_GetStatusFlags(base); - - if (0U != (statusFlag & (uint32_t)kLPUART_RxOverrunFlag)) { - status = ((kStatus_Success == LPUART_ClearStatusFlags(base, (uint32_t)kLPUART_RxOverrunFlag)) ? - (kStatus_LPUART_RxHardwareOverrun) : - (kStatus_LPUART_FlagCannotClearManually)); - /* Other error flags(FE, NF, and PF) are prevented from setting once OR is set, no need to check other - * error flags*/ - break; - } - - if (0U != (statusFlag & (uint32_t)kLPUART_ParityErrorFlag)) { - status = ((kStatus_Success == LPUART_ClearStatusFlags(base, (uint32_t)kLPUART_ParityErrorFlag)) ? - (kStatus_LPUART_ParityError) : - (kStatus_LPUART_FlagCannotClearManually)); - } - - if (0U != (statusFlag & (uint32_t)kLPUART_FramingErrorFlag)) { - status = ((kStatus_Success == LPUART_ClearStatusFlags(base, (uint32_t)kLPUART_FramingErrorFlag)) ? - (kStatus_LPUART_FramingError) : - (kStatus_LPUART_FlagCannotClearManually)); - } - - if (0U != (statusFlag & (uint32_t)kLPUART_NoiseErrorFlag)) { - status = ((kStatus_Success == LPUART_ClearStatusFlags(base, (uint32_t)kLPUART_NoiseErrorFlag)) ? - (kStatus_LPUART_NoiseError) : - (kStatus_LPUART_FlagCannotClearManually)); - } - if (kStatus_Success != status) { - break; - } - } - - if (kStatus_Success == status) { - #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT - if (isSevenDataBits) { - *(dataAddress) = (uint8_t)(base->DATA & 0x7FU); - dataAddress++; - } else { - *(dataAddress) = (uint8_t)base->DATA; - dataAddress++; - } - #else - *(dataAddress) = (uint8_t)base->DATA; - dataAddress++; - #endif - } else { - break; - } - } - - return status; -} - -/*! - * brief Initializes the LPUART handle. - * - * This function initializes the LPUART handle, which can be used for other LPUART - * transactional APIs. Usually, for a specified LPUART instance, - * call this API once to get the initialized handle. - * - * The LPUART driver supports the "background" receiving, which means that user can set up - * an RX ring buffer optionally. Data received is stored into the ring buffer even when the - * user doesn't call the LPUART_TransferReceiveNonBlocking() API. If there is already data received - * in the ring buffer, the user can get the received data from the ring buffer directly. - * The ring buffer is disabled if passing NULL as p ringBuffer. - * - * param base LPUART peripheral base address. - * param handle LPUART handle pointer. - * param callback Callback function. - * param userData User data. - */ -void LPUART_TransferCreateHandle(LPUART_Type *base, - lpuart_handle_t *handle, - lpuart_transfer_callback_t callback, - void *userData) { - assert(NULL != handle); - - uint32_t instance; - - #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT - uint32_t ctrl = base->CTRL; - bool isSevenDataBits = (((ctrl & LPUART_CTRL_M7_MASK) != 0U) || - (((ctrl & LPUART_CTRL_M_MASK) == 0U) && ((ctrl & LPUART_CTRL_PE_MASK) != 0U))); - #endif - - /* Zero the handle. */ - (void)memset(handle, 0, sizeof(lpuart_handle_t)); - - /* Set the TX/RX state. */ - handle->rxState = (uint8_t)kLPUART_RxIdle; - handle->txState = (uint8_t)kLPUART_TxIdle; - - /* Set the callback and user data. */ - handle->callback = callback; - handle->userData = userData; - - #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT - /* Initial seven data bits flag */ - handle->isSevenDataBits = isSevenDataBits; - #endif - - /* Get instance from peripheral base address. */ - instance = LPUART_GetInstance(base); - - /* Save the handle in global variables to support the double weak mechanism. */ - s_lpuartHandle[instance] = handle; - - s_lpuartIsr = LPUART_TransferHandleIRQ; - -/* Enable interrupt in NVIC. */ - #if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ - (void)EnableIRQ(s_lpuartRxIRQ[instance]); - (void)EnableIRQ(s_lpuartTxIRQ[instance]); - #else - (void)EnableIRQ(s_lpuartIRQ[instance]); - #endif -} - -/*! - * brief Sets up the RX ring buffer. - * - * This function sets up the RX ring buffer to a specific UART handle. - * - * When the RX ring buffer is used, data received is stored into the ring buffer even when - * the user doesn't call the UART_TransferReceiveNonBlocking() API. If there is already data received - * in the ring buffer, the user can get the received data from the ring buffer directly. - * - * note When using RX ring buffer, one byte is reserved for internal use. In other - * words, if p ringBufferSize is 32, then only 31 bytes are used for saving data. - * - * param base LPUART peripheral base address. - * param handle LPUART handle pointer. - * param ringBuffer Start address of ring buffer for background receiving. Pass NULL to disable the ring buffer. - * param ringBufferSize size of the ring buffer. - */ -void LPUART_TransferStartRingBuffer(LPUART_Type *base, - lpuart_handle_t *handle, - uint8_t *ringBuffer, - size_t ringBufferSize) { - assert(NULL != handle); - assert(NULL != ringBuffer); - - /* Setup the ring buffer address */ - handle->rxRingBuffer = ringBuffer; - handle->rxRingBufferSize = ringBufferSize; - handle->rxRingBufferHead = 0U; - handle->rxRingBufferTail = 0U; - - /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. */ - uint32_t irqMask = DisableGlobalIRQ(); - /* Enable the interrupt to accept the data when user need the ring buffer. */ - base->CTRL |= (uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ORIE_MASK); - EnableGlobalIRQ(irqMask); -} - -/*! - * brief Aborts the background transfer and uninstalls the ring buffer. - * - * This function aborts the background transfer and uninstalls the ring buffer. - * - * param base LPUART peripheral base address. - * param handle LPUART handle pointer. - */ -void LPUART_TransferStopRingBuffer(LPUART_Type *base, lpuart_handle_t *handle) { - assert(NULL != handle); - - if (handle->rxState == (uint8_t)kLPUART_RxIdle) { - /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. - */ - uint32_t irqMask = DisableGlobalIRQ(); - base->CTRL &= ~(uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ORIE_MASK); - EnableGlobalIRQ(irqMask); - } - - handle->rxRingBuffer = NULL; - handle->rxRingBufferSize = 0U; - handle->rxRingBufferHead = 0U; - handle->rxRingBufferTail = 0U; -} - -/*! - * brief Transmits a buffer of data using the interrupt method. - * - * This function send data using an interrupt method. This is a non-blocking function, which - * returns directly without waiting for all data written to the transmitter register. When - * all data is written to the TX register in the ISR, the LPUART driver calls the callback - * function and passes the ref kStatus_LPUART_TxIdle as status parameter. - * - * note The kStatus_LPUART_TxIdle is passed to the upper layer when all data are written - * to the TX register. However, there is no check to ensure that all the data sent out. Before disabling the TX, - * check the kLPUART_TransmissionCompleteFlag to ensure that the transmit is finished. - * - * param base LPUART peripheral base address. - * param handle LPUART handle pointer. - * param xfer LPUART transfer structure, see #lpuart_transfer_t. - * retval kStatus_Success Successfully start the data transmission. - * retval kStatus_LPUART_TxBusy Previous transmission still not finished, data not all written to the TX register. - * retval kStatus_InvalidArgument Invalid argument. - */ -status_t LPUART_TransferSendNonBlocking(LPUART_Type *base, lpuart_handle_t *handle, lpuart_transfer_t *xfer) { - assert(NULL != handle); - assert(NULL != xfer); - assert(NULL != xfer->txData); - assert(0U != xfer->dataSize); - - status_t status; - - /* Return error if current TX busy. */ - if ((uint8_t)kLPUART_TxBusy == handle->txState) { - status = kStatus_LPUART_TxBusy; - } else { - handle->txData = xfer->txData; - handle->txDataSize = xfer->dataSize; - handle->txDataSizeAll = xfer->dataSize; - handle->txState = (uint8_t)kLPUART_TxBusy; - - /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. - */ - uint32_t irqMask = DisableGlobalIRQ(); - /* Enable transmitter interrupt. */ - base->CTRL |= (uint32_t)LPUART_CTRL_TIE_MASK; - EnableGlobalIRQ(irqMask); - - status = kStatus_Success; - } - - return status; -} - -/*! - * brief Aborts the interrupt-driven data transmit. - * - * This function aborts the interrupt driven data sending. The user can get the remainBtyes to find out - * how many bytes are not sent out. - * - * param base LPUART peripheral base address. - * param handle LPUART handle pointer. - */ -void LPUART_TransferAbortSend(LPUART_Type *base, lpuart_handle_t *handle) { - assert(NULL != handle); - - /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. */ - uint32_t irqMask = DisableGlobalIRQ(); - base->CTRL &= ~(uint32_t)(LPUART_CTRL_TIE_MASK | LPUART_CTRL_TCIE_MASK); - EnableGlobalIRQ(irqMask); - - handle->txDataSize = 0; - handle->txState = (uint8_t)kLPUART_TxIdle; -} - -/*! - * brief Gets the number of bytes that have been sent out to bus. - * - * This function gets the number of bytes that have been sent out to bus by an interrupt method. - * - * param base LPUART peripheral base address. - * param handle LPUART handle pointer. - * param count Send bytes count. - * retval kStatus_NoTransferInProgress No send in progress. - * retval kStatus_InvalidArgument Parameter is invalid. - * retval kStatus_Success Get successfully through the parameter \p count; - */ -status_t LPUART_TransferGetSendCount(LPUART_Type *base, lpuart_handle_t *handle, uint32_t *count) { - assert(NULL != handle); - assert(NULL != count); - - status_t status = kStatus_Success; - size_t tmptxDataSize = handle->txDataSize; - - if ((uint8_t)kLPUART_TxIdle == handle->txState) { - status = kStatus_NoTransferInProgress; - } else { - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - *count = handle->txDataSizeAll - tmptxDataSize - - ((base->WATER & LPUART_WATER_TXCOUNT_MASK) >> LPUART_WATER_TXCOUNT_SHIFT); - #else - if ((base->STAT & (uint32_t)kLPUART_TxDataRegEmptyFlag) != 0U) { - *count = handle->txDataSizeAll - tmptxDataSize; - } else { - *count = handle->txDataSizeAll - tmptxDataSize - 1U; - } - #endif - } - - return status; -} - -/*! - * brief Receives a buffer of data using the interrupt method. - * - * This function receives data using an interrupt method. This is a non-blocking function - * which returns without waiting to ensure that all data are received. - * If the RX ring buffer is used and not empty, the data in the ring buffer is copied and - * the parameter p receivedBytes shows how many bytes are copied from the ring buffer. - * After copying, if the data in the ring buffer is not enough for read, the receive - * request is saved by the LPUART driver. When the new data arrives, the receive request - * is serviced first. When all data is received, the LPUART driver notifies the upper layer - * through a callback function and passes a status parameter ref kStatus_UART_RxIdle. - * For example, the upper layer needs 10 bytes but there are only 5 bytes in ring buffer. - * The 5 bytes are copied to xfer->data, which returns with the - * parameter p receivedBytes set to 5. For the remaining 5 bytes, the newly arrived data is - * saved from xfer->data[5]. When 5 bytes are received, the LPUART driver notifies the upper layer. - * If the RX ring buffer is not enabled, this function enables the RX and RX interrupt - * to receive data to xfer->data. When all data is received, the upper layer is notified. - * - * param base LPUART peripheral base address. - * param handle LPUART handle pointer. - * param xfer LPUART transfer structure, see #uart_transfer_t. - * param receivedBytes Bytes received from the ring buffer directly. - * retval kStatus_Success Successfully queue the transfer into the transmit queue. - * retval kStatus_LPUART_RxBusy Previous receive request is not finished. - * retval kStatus_InvalidArgument Invalid argument. - */ -status_t LPUART_TransferReceiveNonBlocking(LPUART_Type *base, - lpuart_handle_t *handle, - lpuart_transfer_t *xfer, - size_t *receivedBytes) { - assert(NULL != handle); - assert(NULL != xfer); - assert(NULL != xfer->rxData); - assert(0U != xfer->dataSize); - - uint32_t i; - status_t status; - uint32_t irqMask; - /* How many bytes to copy from ring buffer to user memory. */ - size_t bytesToCopy = 0U; - /* How many bytes to receive. */ - size_t bytesToReceive; - /* How many bytes currently have received. */ - size_t bytesCurrentReceived; - - /* How to get data: - 1. If RX ring buffer is not enabled, then save xfer->data and xfer->dataSize - to lpuart handle, enable interrupt to store received data to xfer->data. When - all data received, trigger callback. - 2. If RX ring buffer is enabled and not empty, get data from ring buffer first. - If there are enough data in ring buffer, copy them to xfer->data and return. - If there are not enough data in ring buffer, copy all of them to xfer->data, - save the xfer->data remained empty space to lpuart handle, receive data - to this empty space and trigger callback when finished. */ - - if ((uint8_t)kLPUART_RxBusy == handle->rxState) { - status = kStatus_LPUART_RxBusy; - } else { - bytesToReceive = xfer->dataSize; - bytesCurrentReceived = 0; - - /* If RX ring buffer is used. */ - if (NULL != handle->rxRingBuffer) { - /* Disable and re-enable the global interrupt to protect the interrupt enable register during - * read-modify-wrte. */ - irqMask = DisableGlobalIRQ(); - /* Disable LPUART RX IRQ, protect ring buffer. */ - base->CTRL &= ~(uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ORIE_MASK); - EnableGlobalIRQ(irqMask); - - /* How many bytes in RX ring buffer currently. */ - bytesToCopy = LPUART_TransferGetRxRingBufferLength(base, handle); - - if (0U != bytesToCopy) { - bytesToCopy = MIN(bytesToReceive, bytesToCopy); - - bytesToReceive -= bytesToCopy; - - /* Copy data from ring buffer to user memory. */ - for (i = 0U; i < bytesToCopy; i++) { - xfer->rxData[bytesCurrentReceived] = handle->rxRingBuffer[handle->rxRingBufferTail]; - bytesCurrentReceived++; - - /* Wrap to 0. Not use modulo (%) because it might be large and slow. */ - if (((uint32_t)handle->rxRingBufferTail + 1U) == handle->rxRingBufferSize) { - handle->rxRingBufferTail = 0U; - } else { - handle->rxRingBufferTail++; - } - } - } - - /* If ring buffer does not have enough data, still need to read more data. */ - if (0U != bytesToReceive) { - /* No data in ring buffer, save the request to LPUART handle. */ - handle->rxData = &xfer->rxData[bytesCurrentReceived]; - handle->rxDataSize = bytesToReceive; - handle->rxDataSizeAll = xfer->dataSize; - handle->rxState = (uint8_t)kLPUART_RxBusy; - } - - /* Disable and re-enable the global interrupt to protect the interrupt enable register during - * read-modify-wrte. */ - irqMask = DisableGlobalIRQ(); - /* Re-enable LPUART RX IRQ. */ - base->CTRL |= (uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ORIE_MASK); - EnableGlobalIRQ(irqMask); - - /* Call user callback since all data are received. */ - if (0U == bytesToReceive) { - if (NULL != handle->callback) { - handle->callback(base, handle, kStatus_LPUART_RxIdle, handle->userData); - } - } - } - /* Ring buffer not used. */ - else { - handle->rxData = &xfer->rxData[bytesCurrentReceived]; - handle->rxDataSize = bytesToReceive; - handle->rxDataSizeAll = bytesToReceive; - handle->rxState = (uint8_t)kLPUART_RxBusy; - - /* Disable and re-enable the global interrupt to protect the interrupt enable register during - * read-modify-wrte. */ - irqMask = DisableGlobalIRQ(); - /* Enable RX interrupt. */ - base->CTRL |= (uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ILIE_MASK | LPUART_CTRL_ORIE_MASK); - EnableGlobalIRQ(irqMask); - } - - /* Return the how many bytes have read. */ - if (NULL != receivedBytes) { - *receivedBytes = bytesCurrentReceived; - } - - status = kStatus_Success; - } - - return status; -} - -/*! - * brief Aborts the interrupt-driven data receiving. - * - * This function aborts the interrupt-driven data receiving. The user can get the remainBytes to find out - * how many bytes not received yet. - * - * param base LPUART peripheral base address. - * param handle LPUART handle pointer. - */ -void LPUART_TransferAbortReceive(LPUART_Type *base, lpuart_handle_t *handle) { - assert(NULL != handle); - - /* Only abort the receive to handle->rxData, the RX ring buffer is still working. */ - if (NULL == handle->rxRingBuffer) { - /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. - */ - uint32_t irqMask = DisableGlobalIRQ(); - /* Disable RX interrupt. */ - base->CTRL &= ~(uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ILIE_MASK | LPUART_CTRL_ORIE_MASK); - EnableGlobalIRQ(irqMask); - } - - handle->rxDataSize = 0U; - handle->rxState = (uint8_t)kLPUART_RxIdle; -} - -/*! - * brief Gets the number of bytes that have been received. - * - * This function gets the number of bytes that have been received. - * - * param base LPUART peripheral base address. - * param handle LPUART handle pointer. - * param count Receive bytes count. - * retval kStatus_NoTransferInProgress No receive in progress. - * retval kStatus_InvalidArgument Parameter is invalid. - * retval kStatus_Success Get successfully through the parameter \p count; - */ -status_t LPUART_TransferGetReceiveCount(LPUART_Type *base, lpuart_handle_t *handle, uint32_t *count) { - assert(NULL != handle); - assert(NULL != count); - - status_t status = kStatus_Success; - size_t tmprxDataSize = handle->rxDataSize; - - if ((uint8_t)kLPUART_RxIdle == handle->rxState) { - status = kStatus_NoTransferInProgress; - } else { - *count = handle->rxDataSizeAll - tmprxDataSize; - } - - return status; -} - -/*! - * brief LPUART IRQ handle function. - * - * This function handles the LPUART transmit and receive IRQ request. - * - * param base LPUART peripheral base address. - * param irqHandle LPUART handle pointer. - */ -void LPUART_TransferHandleIRQ(LPUART_Type *base, void *irqHandle) { - assert(NULL != irqHandle); - - uint8_t count; - uint8_t tempCount; - uint32_t status = LPUART_GetStatusFlags(base); - uint32_t enabledInterrupts = LPUART_GetEnabledInterrupts(base); - uint16_t tpmRxRingBufferHead; - uint32_t tpmData; - uint32_t irqMask; - lpuart_handle_t *handle = (lpuart_handle_t *)irqHandle; - - /* If RX overrun. */ - if ((uint32_t)kLPUART_RxOverrunFlag == ((uint32_t)kLPUART_RxOverrunFlag & status)) { - /* Clear overrun flag, otherwise the RX does not work. */ - base->STAT = ((base->STAT & 0x3FE00000U) | LPUART_STAT_OR_MASK); - - /* Trigger callback. */ - if (NULL != (handle->callback)) { - handle->callback(base, handle, kStatus_LPUART_RxHardwareOverrun, handle->userData); - } - } - /* Receive data register full */ - if ((0U != ((uint32_t)kLPUART_RxDataRegFullFlag & status)) && - (0U != ((uint32_t)kLPUART_RxDataRegFullInterruptEnable & enabledInterrupts))) { - /* Get the size that can be stored into buffer for this interrupt. */ - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - count = ((uint8_t)((base->WATER & LPUART_WATER_RXCOUNT_MASK) >> LPUART_WATER_RXCOUNT_SHIFT)); - #else - count = 1; - #endif - - /* If handle->rxDataSize is not 0, first save data to handle->rxData. */ - while ((0U != handle->rxDataSize) && (0U != count)) { - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - tempCount = (uint8_t)MIN(handle->rxDataSize, count); - #else - tempCount = 1; - #endif - - /* Using non block API to read the data from the registers. */ - LPUART_ReadNonBlocking(base, handle->rxData, tempCount); - handle->rxData = &handle->rxData[tempCount]; - handle->rxDataSize -= tempCount; - count -= tempCount; - - /* If all the data required for upper layer is ready, trigger callback. */ - if (0U == handle->rxDataSize) { - handle->rxState = (uint8_t)kLPUART_RxIdle; - - if (NULL != handle->callback) { - handle->callback(base, handle, kStatus_LPUART_RxIdle, handle->userData); - } - } - } - - /* If use RX ring buffer, receive data to ring buffer. */ - if (NULL != handle->rxRingBuffer) { - while (0U != count--) { - /* If RX ring buffer is full, trigger callback to notify over run. */ - if (LPUART_TransferIsRxRingBufferFull(base, handle)) { - if (NULL != handle->callback) { - handle->callback(base, handle, kStatus_LPUART_RxRingBufferOverrun, handle->userData); - } - } - - /* If ring buffer is still full after callback function, the oldest data is overridden. */ - if (LPUART_TransferIsRxRingBufferFull(base, handle)) { - /* Increase handle->rxRingBufferTail to make room for new data. */ - if (((uint32_t)handle->rxRingBufferTail + 1U) == handle->rxRingBufferSize) { - handle->rxRingBufferTail = 0U; - } else { - handle->rxRingBufferTail++; - } - } - - /* Read data. */ - tpmRxRingBufferHead = handle->rxRingBufferHead; - tpmData = base->DATA; - #if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT - if (handle->isSevenDataBits) { - handle->rxRingBuffer[tpmRxRingBufferHead] = (uint8_t)(tpmData & 0x7FU); - } else { - handle->rxRingBuffer[tpmRxRingBufferHead] = (uint8_t)tpmData; - } - #else - handle->rxRingBuffer[tpmRxRingBufferHead] = (uint8_t)tpmData; - #endif - - /* Increase handle->rxRingBufferHead. */ - if (((uint32_t)handle->rxRingBufferHead + 1U) == handle->rxRingBufferSize) { - handle->rxRingBufferHead = 0U; - } else { - handle->rxRingBufferHead++; - } - } - } - /* If no receive request pending, stop RX interrupt. */ - else if (0U == handle->rxDataSize) { - /* Disable and re-enable the global interrupt to protect the interrupt enable register during - * read-modify-wrte. */ - irqMask = DisableGlobalIRQ(); - base->CTRL &= ~(uint32_t)(LPUART_CTRL_RIE_MASK | LPUART_CTRL_ORIE_MASK | LPUART_CTRL_ILIE_MASK); - EnableGlobalIRQ(irqMask); - } else { - } - } - - /* Send data register empty and the interrupt is enabled. */ - if ((0U != ((uint32_t)kLPUART_TxDataRegEmptyFlag & status)) && - (0U != ((uint32_t)kLPUART_TxDataRegEmptyInterruptEnable & enabledInterrupts))) { -/* Get the bytes that available at this moment. */ - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - count = (uint8_t)FSL_FEATURE_LPUART_FIFO_SIZEn(base) - - (uint8_t)((base->WATER & LPUART_WATER_TXCOUNT_MASK) >> LPUART_WATER_TXCOUNT_SHIFT); - #else - count = 1; - #endif - - while ((0U != handle->txDataSize) && (0U != count)) { - #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO - tempCount = (uint8_t)MIN(handle->txDataSize, count); - #else - tempCount = 1; - #endif - - /* Using non block API to write the data to the registers. */ - LPUART_WriteNonBlocking(base, handle->txData, tempCount); - handle->txData = &handle->txData[tempCount]; - handle->txDataSize -= tempCount; - count -= tempCount; - - /* If all the data are written to data register, notify user with the callback, then TX finished. */ - if (0U == handle->txDataSize) { - /* Disable and re-enable the global interrupt to protect the interrupt enable register during - * read-modify-wrte. */ - irqMask = DisableGlobalIRQ(); - /* Disable TX register empty interrupt and enable transmission completion interrupt. */ - base->CTRL = (base->CTRL & ~LPUART_CTRL_TIE_MASK) | LPUART_CTRL_TCIE_MASK; - EnableGlobalIRQ(irqMask); - } - } - } - - /* Transmission complete and the interrupt is enabled. */ - if ((0U != ((uint32_t)kLPUART_TransmissionCompleteFlag & status)) && - (0U != ((uint32_t)kLPUART_TransmissionCompleteInterruptEnable & enabledInterrupts))) { - /* Set txState to idle only when all data has been sent out to bus. */ - handle->txState = (uint8_t)kLPUART_TxIdle; - - /* Disable and re-enable the global interrupt to protect the interrupt enable register during read-modify-wrte. - */ - irqMask = DisableGlobalIRQ(); - /* Disable transmission complete interrupt. */ - base->CTRL &= ~(uint32_t)LPUART_CTRL_TCIE_MASK; - EnableGlobalIRQ(irqMask); - - /* Trigger callback. */ - if (NULL != handle->callback) { - handle->callback(base, handle, kStatus_LPUART_TxIdle, handle->userData); - } - } - - /* If IDLE flag is set and the IDLE interrupt is enabled. */ - if ((0U != ((uint32_t)kLPUART_IdleLineFlag & status)) && - (0U != ((uint32_t)kLPUART_IdleLineInterruptEnable & enabledInterrupts))) { - /* Clear IDLE flag.*/ - base->STAT |= LPUART_STAT_IDLE_MASK; - if (NULL != handle->callback) { - handle->callback(base, handle, kStatus_LPUART_IdleLineDetected, handle->userData); - } else { - /* Avoid MISRA 15.7 */ - } - } - -} - -/*! - * brief LPUART Error IRQ handle function. - * - * This function handles the LPUART error IRQ request. - * - * param base LPUART peripheral base address. - * param irqHandle LPUART handle pointer. - */ -void LPUART_TransferHandleErrorIRQ(LPUART_Type *base, void *irqHandle) { - /* To be implemented by User. */ -} -#if defined(FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1) && FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1 -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART0_LPUART1_RX_DriverIRQHandler(void); -void LPUART0_LPUART1_RX_DriverIRQHandler(void) { - /* If handle is registered, treat the transfer function is enabled. */ - if (NULL != s_lpuartHandle[0]) { - s_lpuartIsr(LPUART0, s_lpuartHandle[0]); - } - if (NULL != s_lpuartHandle[1]) { - s_lpuartIsr(LPUART1, s_lpuartHandle[1]); - } - SDK_ISR_EXIT_BARRIER; -} -void LPUART0_LPUART1_TX_DriverIRQHandler(void); -void LPUART0_LPUART1_TX_DriverIRQHandler(void) { - /* If handle is registered, treat the transfer function is enabled. */ - if (NULL != s_lpuartHandle[0]) { - s_lpuartIsr(LPUART0, s_lpuartHandle[0]); - } - if (NULL != s_lpuartHandle[1]) { - s_lpuartIsr(LPUART1, s_lpuartHandle[1]); - } - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART0_LPUART1_DriverIRQHandler(void); -void LPUART0_LPUART1_DriverIRQHandler(void) { - /* If handle is registered, treat the transfer function is enabled. */ - if (NULL != s_lpuartHandle[0]) { - s_lpuartIsr(LPUART0, s_lpuartHandle[0]); - } - if (NULL != s_lpuartHandle[1]) { - s_lpuartIsr(LPUART1, s_lpuartHandle[1]); - } - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif - -#if defined(LPUART0) -#if !(defined(FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1) && FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART0_TX_DriverIRQHandler(void); -void LPUART0_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART0, s_lpuartHandle[0]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART0_RX_DriverIRQHandler(void); -void LPUART0_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART0, s_lpuartHandle[0]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART0_DriverIRQHandler(void); -void LPUART0_DriverIRQHandler(void) { - s_lpuartIsr(LPUART0, s_lpuartHandle[0]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif -#endif - -#if defined(LPUART1) -#if !(defined(FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1) && FSL_FEATURE_LPUART_HAS_SHARED_IRQ0_IRQ1) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART1_TX_DriverIRQHandler(void); -void LPUART1_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART1, s_lpuartHandle[1]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART1_RX_DriverIRQHandler(void); -void LPUART1_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART1, s_lpuartHandle[1]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART1_DriverIRQHandler(void); -void LPUART1_DriverIRQHandler(void) { - s_lpuartIsr(LPUART1, s_lpuartHandle[1]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif -#endif - -#if defined(LPUART2) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART2_TX_DriverIRQHandler(void); -void LPUART2_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART2, s_lpuartHandle[2]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART2_RX_DriverIRQHandler(void); -void LPUART2_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART2, s_lpuartHandle[2]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART2_DriverIRQHandler(void); -void LPUART2_DriverIRQHandler(void) { - s_lpuartIsr(LPUART2, s_lpuartHandle[2]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif - -#if defined(LPUART3) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART3_TX_DriverIRQHandler(void); -void LPUART3_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART3, s_lpuartHandle[3]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART3_RX_DriverIRQHandler(void); -void LPUART3_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART3, s_lpuartHandle[3]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART3_DriverIRQHandler(void); -void LPUART3_DriverIRQHandler(void) { - s_lpuartIsr(LPUART3, s_lpuartHandle[3]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif - -#if defined(LPUART4) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART4_TX_DriverIRQHandler(void); -void LPUART4_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART4, s_lpuartHandle[4]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART4_RX_DriverIRQHandler(void); -void LPUART4_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART4, s_lpuartHandle[4]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART4_DriverIRQHandler(void); -void LPUART4_DriverIRQHandler(void) { - s_lpuartIsr(LPUART4, s_lpuartHandle[4]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif - -#if defined(LPUART5) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART5_TX_DriverIRQHandler(void); -void LPUART5_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART5, s_lpuartHandle[5]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART5_RX_DriverIRQHandler(void); -void LPUART5_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART5, s_lpuartHandle[5]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART5_DriverIRQHandler(void); -void LPUART5_DriverIRQHandler(void) { - s_lpuartIsr(LPUART5, s_lpuartHandle[5]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif - -#if defined(LPUART6) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART6_TX_DriverIRQHandler(void); -void LPUART6_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART6, s_lpuartHandle[6]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART6_RX_DriverIRQHandler(void); -void LPUART6_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART6, s_lpuartHandle[6]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART6_DriverIRQHandler(void); -void LPUART6_DriverIRQHandler(void) { - s_lpuartIsr(LPUART6, s_lpuartHandle[6]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif - -#if defined(LPUART7) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART7_TX_DriverIRQHandler(void); -void LPUART7_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART7, s_lpuartHandle[7]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART7_RX_DriverIRQHandler(void); -void LPUART7_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART7, s_lpuartHandle[7]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART7_DriverIRQHandler(void); -void LPUART7_DriverIRQHandler(void) { - s_lpuartIsr(LPUART7, s_lpuartHandle[7]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif - -#if defined(LPUART8) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART8_TX_DriverIRQHandler(void); -void LPUART8_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART8, s_lpuartHandle[8]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART8_RX_DriverIRQHandler(void); -void LPUART8_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART8, s_lpuartHandle[8]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART8_DriverIRQHandler(void); -void LPUART8_DriverIRQHandler(void) { - s_lpuartIsr(LPUART8, s_lpuartHandle[8]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif - -#if defined(LPUART9) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART9_TX_DriverIRQHandler(void); -void LPUART9_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART9, s_lpuartHandle[9]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART9_RX_DriverIRQHandler(void); -void LPUART9_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART9, s_lpuartHandle[9]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART9_DriverIRQHandler(void); -void LPUART9_DriverIRQHandler(void) { - s_lpuartIsr(LPUART9, s_lpuartHandle[9]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif - -#if defined(LPUART10) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART10_TX_DriverIRQHandler(void); -void LPUART10_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART10, s_lpuartHandle[10]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART10_RX_DriverIRQHandler(void); -void LPUART10_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART10, s_lpuartHandle[10]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART10_DriverIRQHandler(void); -void LPUART10_DriverIRQHandler(void) { - s_lpuartIsr(LPUART10, s_lpuartHandle[10]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif - -#if defined(LPUART11) -#if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ -void LPUART11_TX_DriverIRQHandler(void); -void LPUART11_TX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART11, s_lpuartHandle[11]); - SDK_ISR_EXIT_BARRIER; -} -void LPUART11_RX_DriverIRQHandler(void); -void LPUART11_RX_DriverIRQHandler(void) { - s_lpuartIsr(LPUART11, s_lpuartHandle[11]); - SDK_ISR_EXIT_BARRIER; -} -#else -void LPUART11_DriverIRQHandler(void); -void LPUART11_DriverIRQHandler(void) { - s_lpuartIsr(LPUART11, s_lpuartHandle[11]); - SDK_ISR_EXIT_BARRIER; -} -#endif -#endif - -#if defined(CM4_0__LPUART) -void M4_0_LPUART_DriverIRQHandler(void); -void M4_0_LPUART_DriverIRQHandler(void) { - s_lpuartIsr(CM4_0__LPUART, s_lpuartHandle[LPUART_GetInstance(CM4_0__LPUART)]); - SDK_ISR_EXIT_BARRIER; -} -#endif - -#if defined(CM4_1__LPUART) -void M4_1_LPUART_DriverIRQHandler(void); -void M4_1_LPUART_DriverIRQHandler(void) { - s_lpuartIsr(CM4_1__LPUART, s_lpuartHandle[LPUART_GetInstance(CM4_1__LPUART)]); - SDK_ISR_EXIT_BARRIER; -} -#endif - -#if defined(CM4__LPUART) -void M4_LPUART_DriverIRQHandler(void); -void M4_LPUART_DriverIRQHandler(void) { - s_lpuartIsr(CM4__LPUART, s_lpuartHandle[LPUART_GetInstance(CM4__LPUART)]); - SDK_ISR_EXIT_BARRIER; -} -#endif - -#if defined(DMA__LPUART0) -void DMA_UART0_INT_DriverIRQHandler(void); -void DMA_UART0_INT_DriverIRQHandler(void) { - s_lpuartIsr(DMA__LPUART0, s_lpuartHandle[LPUART_GetInstance(DMA__LPUART0)]); - SDK_ISR_EXIT_BARRIER; -} -#endif - -#if defined(DMA__LPUART1) -void DMA_UART1_INT_DriverIRQHandler(void); -void DMA_UART1_INT_DriverIRQHandler(void) { - s_lpuartIsr(DMA__LPUART1, s_lpuartHandle[LPUART_GetInstance(DMA__LPUART1)]); - SDK_ISR_EXIT_BARRIER; -} -#endif - -#if defined(DMA__LPUART2) -void DMA_UART2_INT_DriverIRQHandler(void); -void DMA_UART2_INT_DriverIRQHandler(void) { - s_lpuartIsr(DMA__LPUART2, s_lpuartHandle[LPUART_GetInstance(DMA__LPUART2)]); - SDK_ISR_EXIT_BARRIER; -} -#endif - -#if defined(DMA__LPUART3) -void DMA_UART3_INT_DriverIRQHandler(void); -void DMA_UART3_INT_DriverIRQHandler(void) { - s_lpuartIsr(DMA__LPUART3, s_lpuartHandle[LPUART_GetInstance(DMA__LPUART3)]); - SDK_ISR_EXIT_BARRIER; -} -#endif - -#if defined(DMA__LPUART4) -void DMA_UART4_INT_DriverIRQHandler(void); -void DMA_UART4_INT_DriverIRQHandler(void) { - s_lpuartIsr(DMA__LPUART4, s_lpuartHandle[LPUART_GetInstance(DMA__LPUART4)]); - SDK_ISR_EXIT_BARRIER; -} -#endif - -#if defined(ADMA__LPUART0) -void ADMA_UART0_INT_DriverIRQHandler(void); -void ADMA_UART0_INT_DriverIRQHandler(void) { - s_lpuartIsr(ADMA__LPUART0, s_lpuartHandle[LPUART_GetInstance(ADMA__LPUART0)]); - SDK_ISR_EXIT_BARRIER; -} -#endif - -#if defined(ADMA__LPUART1) -void ADMA_UART1_INT_DriverIRQHandler(void); -void ADMA_UART1_INT_DriverIRQHandler(void) { - s_lpuartIsr(ADMA__LPUART1, s_lpuartHandle[LPUART_GetInstance(ADMA__LPUART1)]); - SDK_ISR_EXIT_BARRIER; -} -#endif - -#if defined(ADMA__LPUART2) -void ADMA_UART2_INT_DriverIRQHandler(void); -void ADMA_UART2_INT_DriverIRQHandler(void) { - s_lpuartIsr(ADMA__LPUART2, s_lpuartHandle[LPUART_GetInstance(ADMA__LPUART2)]); - SDK_ISR_EXIT_BARRIER; -} -#endif - -#if defined(ADMA__LPUART3) -void ADMA_UART3_INT_DriverIRQHandler(void); -void ADMA_UART3_INT_DriverIRQHandler(void) { - s_lpuartIsr(ADMA__LPUART3, s_lpuartHandle[LPUART_GetInstance(ADMA__LPUART3)]); - SDK_ISR_EXIT_BARRIER; -} -#endif diff --git a/ports/mimxrt/machine_uart.c b/ports/mimxrt/machine_uart.c index 107af72297d4f..ab5f9b76aa0c9 100644 --- a/ports/mimxrt/machine_uart.c +++ b/ports/mimxrt/machine_uart.c @@ -638,3 +638,75 @@ static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uint } return ret; } + +// ============================================================================= +// LPUART IRQ Handler Wrapper for UART.IRQ_RXIDLE Support +// ============================================================================= +// +// Problem: SDK 2.16's LPUART_TransferHandleIDLEReady() only invokes the idle line callback +// when rxDataSize != 0. MicroPython uses ring buffer mode where rxDataSize is always 0, +// preventing IRQ_RXIDLE from ever firing. +// +// Solution: Use linker wrapping (see Makefile LDFLAGS) to intercept the IRQ handler and +// handle the idle line interrupt before the SDK processes it. +// +// Why double wrapping is needed: +// - The SDK's LPUART_TransferCreateHandle stores the address of LPUART_TransferHandleIRQ +// into the s_lpuartIsr[] dispatch table (not a direct call). +// - GCC's --wrap flag only intercepts direct function calls, not address-of operations. +// - Therefore we must also wrap CreateHandle to inject our IRQ wrapper's address. +// +// See Makefile: LDFLAGS += --wrap=LPUART_TransferCreateHandle --wrap=LPUART_TransferHandleIRQ +// + +// Linker wrapper declarations - these are provided by --wrap linkage +extern void __real_LPUART_TransferCreateHandle(LPUART_Type *base, lpuart_handle_t *handle, + lpuart_transfer_callback_t callback, void *userData); +extern void __real_LPUART_TransferHandleIRQ(LPUART_Type *base, void *irqHandle); + +// Forward declaration of our IRQ wrapper (defined below) +void __wrap_LPUART_TransferHandleIRQ(LPUART_Type *base, void *irqHandle); + +// SDK's ISR dispatch table - defined in lib/nxp_driver/sdk/drivers/lpuart/fsl_lpuart.c +extern lpuart_isr_t s_lpuartIsr[]; + +// Wrapper for LPUART_TransferCreateHandle to inject our IRQ wrapper into SDK's dispatch table. +// This is called instead of the SDK's function due to --wrap=LPUART_TransferCreateHandle in Makefile. +// After the SDK initializes, we replace s_lpuartIsr[instance] with our wrapper's address. +void __wrap_LPUART_TransferCreateHandle(LPUART_Type *base, lpuart_handle_t *handle, + lpuart_transfer_callback_t callback, void *userData) { + // Call the real SDK function to perform normal initialization + __real_LPUART_TransferCreateHandle(base, handle, callback, userData); + + // Override the ISR dispatch table entry with our wrapper's address + // (SDK stored __real_LPUART_TransferHandleIRQ, we want __wrap_LPUART_TransferHandleIRQ) + uint32_t instance = LPUART_GetInstance(base); + s_lpuartIsr[instance] = __wrap_LPUART_TransferHandleIRQ; +} + +// Wrapper for LPUART_TransferHandleIRQ to handle UART.IRQ_RXIDLE in ring buffer mode. +// This is installed into s_lpuartIsr[] by __wrap_LPUART_TransferCreateHandle above. +// Processes the IDLE line interrupt and invokes the MicroPython callback unconditionally, +// then calls the SDK's handler for remaining interrupt processing. +void __wrap_LPUART_TransferHandleIRQ(LPUART_Type *base, void *irqHandle) { + uint32_t status = LPUART_GetStatusFlags(base); + uint32_t enabledInterrupts = LPUART_GetEnabledInterrupts(base); + lpuart_handle_t *handle = (lpuart_handle_t *)irqHandle; + + // Check if IDLE flag is set and IDLE interrupt is enabled + if ((0U != ((uint32_t)kLPUART_IdleLineFlag & status)) && + (0U != ((uint32_t)kLPUART_IdleLineInterruptEnable & enabledInterrupts))) { + // Clear IDLE flag to prevent SDK's handler from seeing it + // (SDK would disable the interrupt due to rxDataSize == 0) + LPUART_ClearStatusFlags(base, kLPUART_IdleLineFlag); + // Invoke MicroPython's idle handler callback + if (NULL != handle->callback) { + handle->callback(base, handle, kStatus_LPUART_IdleLineDetected, handle->userData); + } else { + /* Avoid MISRA 15.7 */ + } + } + + // Call SDK's handler for all other interrupt processing + __real_LPUART_TransferHandleIRQ(base, irqHandle); +} From 1046b5dc9901945c2fa42eb65cdef2ad75ae0110 Mon Sep 17 00:00:00 2001 From: Dryw Wade Date: Fri, 30 Jan 2026 09:57:36 -0700 Subject: [PATCH 176/177] mimxrt/Makefile: Make board linker scripts configurable. With this change, can simply add `LD_FILES` to a board's `mpconfigboard.mk` to change the linker scripts, such as to change the section sizes, or change the contents for each region. Example usage: LD_FILES = $(BOARD_DIR)/my_board.ld boards/common.ld Signed-off-by: Dryw Wade --- ports/mimxrt/Makefile | 4 ++-- ports/mimxrt/boards/make-flexram-config.py | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index d2109e2a3be8d..9694464b5b828 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -56,7 +56,7 @@ MCUX_SDK_DIR = lib/nxp_driver/sdk MCU_DIR = $(MCUX_SDK_DIR)/devices/$(MCU_SERIES) # Select linker scripts based on MCU_SERIES -LD_FILES = boards/$(MCU_SERIES).ld boards/common.ld +LD_FILES ?= boards/$(MCU_SERIES).ld boards/common.ld # Parameter configurations for generation AF_FILE = boards/$(MCU_SERIES)_af.csv @@ -595,7 +595,7 @@ $(HEADER_BUILD)/qstrdefs.generated.h: $(BOARD_DIR)/mpconfigboard.h $(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) > $@ + -f $(TOP)/$(MCU_DIR)/$(MCU_SERIES)$(MCU_CORE)_features.h $(addprefix -l,$(LD_FILES)) -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 diff --git a/ports/mimxrt/boards/make-flexram-config.py b/ports/mimxrt/boards/make-flexram-config.py index 4a80fb1d24762..f518746c97910 100644 --- a/ports/mimxrt/boards/make-flexram-config.py +++ b/ports/mimxrt/boards/make-flexram-config.py @@ -55,9 +55,14 @@ # Value parser -def mimxrt_default_parser(defines_file, features_file, ld_script): - with open(ld_script, "r") as input_file: - input_str = input_file.read() +def mimxrt_default_parser(defines_file, features_file, ld_scripts): + input_str = "" + if ld_scripts is None: + # Default linker script + ld_scripts = ["boards/MIMXRT1021.ld"] + for ld_script in ld_scripts: + with open(ld_script, "r") as input_file: + input_str += input_file.read() # ocram_match = re.search(ocram_regex, input_str, re.MULTILINE) dtcm_match = re.search(dtcm_regex, input_str, re.MULTILINE) @@ -249,7 +254,9 @@ def main(defines_file, features_file, ld_script, controller): "--ld_file", dest="linker_file", help="Path to the aggregated linker-script", - default="MIMXRT1021.ld", + action="append", + # Can't specify default here when using 'append' action, see + # https://github.com/python/cpython/issues/60603 ) parser.add_argument( "-c", "--controller", dest="controller", help="Controller name", default="MIMXRT1021" From 023a49c55ed5f3ebabab8969013533dfbdfdff33 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 11 Nov 2025 13:54:20 +1100 Subject: [PATCH 177/177] mimxrt/boards/PHYBOARD_RT1170: Add PHYBOARD-RT1170 board support. Adds board support for PHYTEC phyBOARD-RT1170 Development Kit featuring MIMXRT1176 dual-core (Cortex-M7/M4), 64MB SDRAM, 16MB QSPI Flash, dual Gigabit Ethernet (DP83867 RGMII + KSZ8081 RMII), USB 2.0, MIPI-DSI/CSI, audio codec, CAN FD, RS-232, microSD, and M.2 Key E connector. Signed-off-by: Andrew Leech --- .../mimxrt/boards/PHYBOARD_RT1170/board.json | 28 ++ .../mimxrt/boards/PHYBOARD_RT1170/manifest.py | 3 + .../boards/PHYBOARD_RT1170/mpconfigboard.h | 308 ++++++++++++++++++ .../boards/PHYBOARD_RT1170/mpconfigboard.mk | 31 ++ ports/mimxrt/boards/PHYBOARD_RT1170/pins.csv | 127 ++++++++ pyproject.toml | 2 +- 6 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 ports/mimxrt/boards/PHYBOARD_RT1170/board.json create mode 100644 ports/mimxrt/boards/PHYBOARD_RT1170/manifest.py create mode 100644 ports/mimxrt/boards/PHYBOARD_RT1170/mpconfigboard.h create mode 100644 ports/mimxrt/boards/PHYBOARD_RT1170/mpconfigboard.mk create mode 100644 ports/mimxrt/boards/PHYBOARD_RT1170/pins.csv diff --git a/ports/mimxrt/boards/PHYBOARD_RT1170/board.json b/ports/mimxrt/boards/PHYBOARD_RT1170/board.json new file mode 100644 index 0000000000000..6eaafe1f48882 --- /dev/null +++ b/ports/mimxrt/boards/PHYBOARD_RT1170/board.json @@ -0,0 +1,28 @@ +{ + "deploy": [ + "../deploy_mimxrt.md" + ], + "docs": "", + "features": [ + "Audio Codec", + "CAN", + "Camera", + "Display", + "Dual-core", + "Ethernet", + "External Flash", + "External RAM", + "IMU", + "RGB LED", + "USB", + "microSD" + ], + "images": [ + "phyBOARD-RT1170_front.png" + ], + "mcu": "mimxrt", + "product": "phyBOARD-RT1170 Development Kit", + "thumbnail": "", + "url": "https://www.phytec.com/product/phyboard-rt1170-development-kit/", + "vendor": "PHYTEC" +} diff --git a/ports/mimxrt/boards/PHYBOARD_RT1170/manifest.py b/ports/mimxrt/boards/PHYBOARD_RT1170/manifest.py new file mode 100644 index 0000000000000..59866772206a3 --- /dev/null +++ b/ports/mimxrt/boards/PHYBOARD_RT1170/manifest.py @@ -0,0 +1,3 @@ +include("../manifest.py") +require("bundle-networking") +include("$(MPY_DIR)/extmod/asyncio/manifest.py") diff --git a/ports/mimxrt/boards/PHYBOARD_RT1170/mpconfigboard.h b/ports/mimxrt/boards/PHYBOARD_RT1170/mpconfigboard.h new file mode 100644 index 0000000000000..3cb49ab2cc1b5 --- /dev/null +++ b/ports/mimxrt/boards/PHYBOARD_RT1170/mpconfigboard.h @@ -0,0 +1,308 @@ +#define MICROPY_HW_BOARD_NAME "phyBOARD-RT1170 Development Kit" +#define MICROPY_HW_MCU_NAME "MIMXRT1176DVMAA" + +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-phyboard" + +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(bool); \ + mp_handle_pending(true); \ + } while (0); + +// phyBOARD-RT1170 SoM onboard LEDs (red and green from phyCORE SoM) +// Carrier board provides additional RGB LEDs via GPIO +#define MICROPY_HW_LED1_PIN (pin_GPIO_LPSR_07) // SoM Red LED +#define MICROPY_HW_LED2_PIN (pin_GPIO_LPSR_08) // SoM Green LED +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) // LEDs are active low +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) + +// phyBOARD carrier board RGB LEDs +#define MICROPY_HW_LED3_PIN (pin_GPIO_AD_14) // Carrier Red LED +#define MICROPY_HW_LED4_PIN (pin_GPIO_LPSR_13) // Carrier Green LED + +#define MICROPY_HW_NUM_PIN_IRQS (6 * 32) + +// Define mapping hardware UART # to logical UART # +// phyBOARD-RT1170 UART interfaces +// LPUART1 -> 0 (GPIO_AD_24/25) - Primary debug/console (SoM) +// LPUART2 -> 1 (GPIO_DISP_B2_10/11/12/13) - Carrier board +// LPUART3 -> 2 (GPIO_AD_30/31) - General purpose (SoM) +// LPUART5 -> 3 (GPIO_AD_28/29) - Expansion (SoM) +// LPUART6 -> 4 (GPIO_EMC_B1_40/41) - Console UART (FT2232H - Carrier) +// LPUART7 -> 5 (GPIO_DISP_B2_06/07) - General purpose (SoM) +// LPUART8 -> 6 (GPIO_AD_02/03/04/05) - RS-232 (Carrier) + +#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0]) +#define MICROPY_HW_UART_INDEX { 1, 2, 3, 5, 6, 7, 8 } + +#define IOMUX_TABLE_UART \ + { IOMUXC_GPIO_AD_24_LPUART1_TXD }, { IOMUXC_GPIO_AD_25_LPUART1_RXD }, \ + { IOMUXC_GPIO_DISP_B2_10_LPUART2_TXD }, { IOMUXC_GPIO_DISP_B2_11_LPUART2_RXD }, \ + { IOMUXC_GPIO_AD_30_LPUART3_TXD }, { IOMUXC_GPIO_AD_31_LPUART3_RXD }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_28_LPUART5_TXD }, { IOMUXC_GPIO_AD_29_LPUART5_RXD }, \ + { IOMUXC_GPIO_EMC_B1_40_LPUART6_TXD }, { IOMUXC_GPIO_EMC_B1_41_LPUART6_RXD }, \ + { IOMUXC_GPIO_DISP_B2_06_LPUART7_TXD }, { IOMUXC_GPIO_DISP_B2_07_LPUART7_RXD }, \ + { IOMUXC_GPIO_AD_02_LPUART8_TXD }, { IOMUXC_GPIO_AD_03_LPUART8_RXD }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, + +#define IOMUX_TABLE_UART_CTS_RTS \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_DISP_B2_12_LPUART2_CTS_B }, { IOMUXC_GPIO_DISP_B2_13_LPUART2_RTS_B }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_04_LPUART8_CTS_B }, { IOMUXC_GPIO_AD_05_LPUART8_RTS_B }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, + +// Define the mapping hardware SPI # to logical SPI # +// phyCORE SoM basic SPI interfaces available on connector +// LPSPI1 -> 0 (GPIO_AD_28/29/30/31) +// LPSPI2 -> 1 (GPIO_AD_24/25/26/27) + +#define MICROPY_HW_SPI_INDEX { 1, 2 } + +#define IOMUX_TABLE_SPI \ + { IOMUXC_GPIO_AD_28_LPSPI1_SCK }, { IOMUXC_GPIO_AD_29_LPSPI1_PCS0 }, \ + { IOMUXC_GPIO_AD_30_LPSPI1_SOUT }, { IOMUXC_GPIO_AD_31_LPSPI1_SIN }, \ + { IOMUXC_GPIO_AD_24_LPSPI2_SCK }, { IOMUXC_GPIO_AD_25_LPSPI2_PCS0 }, \ + { IOMUXC_GPIO_AD_26_LPSPI2_SOUT }, { IOMUXC_GPIO_AD_27_LPSPI2_SIN }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, + + +#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ + kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } + +#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ + kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } + +// Define the mapping hardware I2C # to logical I2C # +// phyBOARD-RT1170 I2C interfaces +// LPI2C1 -> 0 (GPIO_AD_32/33) - EEPROM (SoM) +// LPI2C2 -> 1 (GPIO_AD_18/19) - EEPROM + Accelerometer (Carrier) +// LPI2C3 -> 2 (GPIO_DISP_B2_10/11) - Reserved (SoM) +// LPI2C5 -> 3 (GPIO_LPSR_08/09, GPIO_AD_26/27) - Audio Codec + Accelerometer (Carrier) + +#define MICROPY_HW_I2C_INDEX { 1, 2, 3, 5 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_AD_32_LPI2C1_SCL }, { IOMUXC_GPIO_AD_33_LPI2C1_SDA }, \ + { IOMUXC_GPIO_AD_18_LPI2C2_SCL }, { IOMUXC_GPIO_AD_19_LPI2C2_SDA }, \ + { IOMUXC_GPIO_DISP_B2_10_LPI2C3_SCL }, { IOMUXC_GPIO_DISP_B2_11_LPI2C3_SDA }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_LPSR_08_LPI2C5_SDA }, { IOMUXC_GPIO_LPSR_09_LPI2C5_SCL }, \ + { 0 }, { 0 }, + +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (1) +#define I2S_CLOCK_MUX { 0, kCLOCK_Root_Sai1, kCLOCK_Root_Sai2, kCLOCK_Root_Sai3, kCLOCK_Root_Sai4 } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx, kDmaRequestMuxSai2Rx, kDmaRequestMuxSai3Rx, kDmaRequestMuxSai4Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx, kDmaRequestMuxSai2Tx, kDmaRequestMuxSai3Tx, kDmaRequestMuxSai4Tx } +#define I2S_WM8960_RX_MODE (1) +#define I2S_AUDIO_PLL_CLOCK (4U) +#define DMAMUX DMAMUX0 + +#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_AD_17, IOMUXC_GPIO_AD_17_SAI1_MCLK), \ + I2S_GPIO(1, SCK, RX, GPIO_AD_19, IOMUXC_GPIO_AD_19_SAI1_RX_BCLK), \ + I2S_GPIO(1, WS, RX, GPIO_AD_18, IOMUXC_GPIO_AD_18_SAI1_RX_SYNC), \ + I2S_GPIO(1, SD, RX, GPIO_AD_20, IOMUXC_GPIO_AD_20_SAI1_RX_DATA00), \ + I2S_GPIO(1, SCK, TX, GPIO_AD_22, IOMUXC_GPIO_AD_22_SAI1_TX_BCLK), \ + I2S_GPIO(1, WS, TX, GPIO_AD_23, IOMUXC_GPIO_AD_23_SAI1_TX_SYNC), \ + I2S_GPIO(1, SD, TX, GPIO_AD_21, IOMUXC_GPIO_AD_21_SAI1_TX_DATA00), \ + } + +// USDHC1 (SDCARD) +#define MICROPY_PY_MACHINE_SDCARD 0 +#if MICROPY_PY_MACHINE_SDCARD +#define USDHC_DUMMY_PIN NULL, 0 +#define MICROPY_USDHC1 \ + { \ + .cmd = {GPIO_SD_B1_00_USDHC1_CMD}, \ + .clk = { GPIO_SD_B1_01_USDHC1_CLK }, \ + .cd_b = { USDHC_DUMMY_PIN }, \ + .data0 = { GPIO_SD_B1_02_USDHC1_DATA0 }, \ + .data1 = { GPIO_SD_B1_03_USDHC1_DATA1 }, \ + .data2 = { GPIO_SD_B1_04_USDHC1_DATA2 }, \ + .data3 = { GPIO_SD_B1_05_USDHC1_DATA3 }, \ + } +#define USDHC_DATA3_PULL_DOWN_ON_BOARD (1) +#endif + +// Network definitions + +// phyBOARD-RT1170 Dual Ethernet configuration +// Port 0: KSZ8081 100Mbps RMII PHY on carrier board (PBA-C-26) +// Port 1: DP83867 1Gbps RGMII PHY on phyCORE SoM + +// Primary Ethernet (100M RMII) - KSZ8081 on carrier board +#define ENET_PHY_ADDRESS (1) // KSZ8081 PHY address 001b +#define ENET_PHY KSZ8081 +#define ENET_PHY_OPS phyksz8081_ops + +// ENET RMII pin configuration - connected to carrier board KSZ8081 +#define IOMUX_TABLE_ENET \ + { IOMUXC_GPIO_DISP_B2_06_ENET_RX_DATA00, 0, 0x06u }, \ + { IOMUXC_GPIO_DISP_B2_07_ENET_RX_DATA01, 0, 0x06u }, \ + { IOMUXC_GPIO_DISP_B2_08_ENET_RX_EN, 0, 0x06u }, \ + { IOMUXC_GPIO_DISP_B2_02_ENET_TX_DATA00, 0, 0x02u }, \ + { IOMUXC_GPIO_DISP_B2_03_ENET_TX_DATA01, 0, 0x02u }, \ + { IOMUXC_GPIO_DISP_B2_04_ENET_TX_EN, 0, 0x06u }, \ + { IOMUXC_GPIO_DISP_B2_05_ENET_REF_CLK, 1, 0x03u }, \ + { IOMUXC_GPIO_DISP_B2_09_ENET_RX_ER, 0, 0x06u }, \ + { IOMUXC_GPIO_AD_33_ENET_MDIO, 0, 0x06u }, \ + { IOMUXC_GPIO_AD_32_ENET_MDC, 0, 0x06u }, + +// Secondary Ethernet (1G RGMII) - DP83867 on phyCORE SoM +#define ENET_1_PHY_ADDRESS (0) +#define ENET_1_PHY DP83867 +#define ENET_1_PHY_OPS phydp83867_ops + +// ENET_1G RGMII pin configuration - full RGMII pins for Gigabit operation +#define IOMUX_TABLE_ENET_1 \ + { IOMUXC_GPIO_DISP_B1_00_ENET_1G_RX_EN, 0, 0x08U }, \ + { IOMUXC_GPIO_DISP_B1_01_ENET_1G_RX_CLK, 0, 0x08U }, \ + { IOMUXC_GPIO_DISP_B1_02_ENET_1G_RX_DATA00, 0, 0x08U }, \ + { IOMUXC_GPIO_DISP_B1_03_ENET_1G_RX_DATA01, 0, 0x08U }, \ + { IOMUXC_GPIO_DISP_B1_04_ENET_1G_RX_DATA02, 0, 0x08U }, \ + { IOMUXC_GPIO_DISP_B1_05_ENET_1G_RX_DATA03, 0, 0x08U }, \ + { IOMUXC_GPIO_DISP_B1_06_ENET_1G_TX_DATA03, 0, 0x0CU }, \ + { IOMUXC_GPIO_DISP_B1_07_ENET_1G_TX_DATA02, 0, 0x0CU }, \ + { IOMUXC_GPIO_DISP_B1_08_ENET_1G_TX_DATA01, 0, 0x0CU }, \ + { IOMUXC_GPIO_DISP_B1_09_ENET_1G_TX_DATA00, 0, 0x0CU }, \ + { IOMUXC_GPIO_DISP_B1_10_ENET_1G_TX_EN, 0, 0x0CU }, \ + { IOMUXC_GPIO_DISP_B1_11_ENET_1G_TX_CLK_IO, 0, 0x0CU }, \ + { IOMUXC_GPIO_EMC_B2_20_ENET_1G_MDIO, 0, 0x06u }, \ + { IOMUXC_GPIO_EMC_B2_19_ENET_1G_MDC, 0, 0x06u }, + + +// --- SEMC --- // +#define MIMXRT_IOMUXC_SEMC_DATA00 IOMUXC_GPIO_EMC_B1_00_SEMC_DATA00 +#define MIMXRT_IOMUXC_SEMC_DATA01 IOMUXC_GPIO_EMC_B1_01_SEMC_DATA01 +#define MIMXRT_IOMUXC_SEMC_DATA02 IOMUXC_GPIO_EMC_B1_02_SEMC_DATA02 +#define MIMXRT_IOMUXC_SEMC_DATA03 IOMUXC_GPIO_EMC_B1_03_SEMC_DATA03 +#define MIMXRT_IOMUXC_SEMC_DATA04 IOMUXC_GPIO_EMC_B1_04_SEMC_DATA04 +#define MIMXRT_IOMUXC_SEMC_DATA05 IOMUXC_GPIO_EMC_B1_05_SEMC_DATA05 +#define MIMXRT_IOMUXC_SEMC_DATA06 IOMUXC_GPIO_EMC_B1_06_SEMC_DATA06 +#define MIMXRT_IOMUXC_SEMC_DATA07 IOMUXC_GPIO_EMC_B1_07_SEMC_DATA07 +#define MIMXRT_IOMUXC_SEMC_DATA08 IOMUXC_GPIO_EMC_B1_30_SEMC_DATA08 +#define MIMXRT_IOMUXC_SEMC_DATA09 IOMUXC_GPIO_EMC_B1_31_SEMC_DATA09 +#define MIMXRT_IOMUXC_SEMC_DATA10 IOMUXC_GPIO_EMC_B1_32_SEMC_DATA10 +#define MIMXRT_IOMUXC_SEMC_DATA11 IOMUXC_GPIO_EMC_B1_33_SEMC_DATA11 +#define MIMXRT_IOMUXC_SEMC_DATA12 IOMUXC_GPIO_EMC_B1_34_SEMC_DATA12 +#define MIMXRT_IOMUXC_SEMC_DATA13 IOMUXC_GPIO_EMC_B1_35_SEMC_DATA13 +#define MIMXRT_IOMUXC_SEMC_DATA14 IOMUXC_GPIO_EMC_B1_36_SEMC_DATA14 +#define MIMXRT_IOMUXC_SEMC_DATA15 IOMUXC_GPIO_EMC_B1_37_SEMC_DATA15 + +#define MIMXRT_IOMUXC_SEMC_ADDR00 IOMUXC_GPIO_EMC_B1_09_SEMC_ADDR00 +#define MIMXRT_IOMUXC_SEMC_ADDR01 IOMUXC_GPIO_EMC_B1_10_SEMC_ADDR01 +#define MIMXRT_IOMUXC_SEMC_ADDR02 IOMUXC_GPIO_EMC_B1_11_SEMC_ADDR02 +#define MIMXRT_IOMUXC_SEMC_ADDR03 IOMUXC_GPIO_EMC_B1_12_SEMC_ADDR03 +#define MIMXRT_IOMUXC_SEMC_ADDR04 IOMUXC_GPIO_EMC_B1_13_SEMC_ADDR04 +#define MIMXRT_IOMUXC_SEMC_ADDR05 IOMUXC_GPIO_EMC_B1_14_SEMC_ADDR05 +#define MIMXRT_IOMUXC_SEMC_ADDR06 IOMUXC_GPIO_EMC_B1_15_SEMC_ADDR06 +#define MIMXRT_IOMUXC_SEMC_ADDR07 IOMUXC_GPIO_EMC_B1_16_SEMC_ADDR07 +#define MIMXRT_IOMUXC_SEMC_ADDR08 IOMUXC_GPIO_EMC_B1_17_SEMC_ADDR08 +#define MIMXRT_IOMUXC_SEMC_ADDR09 IOMUXC_GPIO_EMC_B1_18_SEMC_ADDR09 +#define MIMXRT_IOMUXC_SEMC_ADDR10 IOMUXC_GPIO_EMC_B1_23_SEMC_ADDR10 +#define MIMXRT_IOMUXC_SEMC_ADDR11 IOMUXC_GPIO_EMC_B1_19_SEMC_ADDR11 +#define MIMXRT_IOMUXC_SEMC_ADDR12 IOMUXC_GPIO_EMC_B1_20_SEMC_ADDR12 + +#define MIMXRT_IOMUXC_SEMC_BA0 IOMUXC_GPIO_EMC_B1_21_SEMC_BA0 +#define MIMXRT_IOMUXC_SEMC_BA1 IOMUXC_GPIO_EMC_B1_22_SEMC_BA1 +#define MIMXRT_IOMUXC_SEMC_CAS IOMUXC_GPIO_EMC_B1_24_SEMC_CAS +#define MIMXRT_IOMUXC_SEMC_RAS IOMUXC_GPIO_EMC_B1_25_SEMC_RAS +#define MIMXRT_IOMUXC_SEMC_CLK IOMUXC_GPIO_EMC_B1_26_SEMC_CLK +#define MIMXRT_IOMUXC_SEMC_CKE IOMUXC_GPIO_EMC_B1_27_SEMC_CKE +#define MIMXRT_IOMUXC_SEMC_WE IOMUXC_GPIO_EMC_B1_28_SEMC_WE +#define MIMXRT_IOMUXC_SEMC_DM00 IOMUXC_GPIO_EMC_B1_08_SEMC_DM00 +#define MIMXRT_IOMUXC_SEMC_DM01 IOMUXC_GPIO_EMC_B1_38_SEMC_DM01 +#define MIMXRT_IOMUXC_SEMC_DQS IOMUXC_GPIO_EMC_B1_39_SEMC_DQS + +#define MIMXRT_IOMUXC_SEMC_CS0 IOMUXC_GPIO_EMC_B1_29_SEMC_CS0 + +#define MIMXRT_IOMUXC_SEMC_DATA16 IOMUXC_GPIO_EMC_B2_00_SEMC_DATA16 +#define MIMXRT_IOMUXC_SEMC_DATA17 IOMUXC_GPIO_EMC_B2_01_SEMC_DATA17 +#define MIMXRT_IOMUXC_SEMC_DATA18 IOMUXC_GPIO_EMC_B2_02_SEMC_DATA18 +#define MIMXRT_IOMUXC_SEMC_DATA19 IOMUXC_GPIO_EMC_B2_03_SEMC_DATA19 +#define MIMXRT_IOMUXC_SEMC_DATA20 IOMUXC_GPIO_EMC_B2_04_SEMC_DATA20 +#define MIMXRT_IOMUXC_SEMC_DATA21 IOMUXC_GPIO_EMC_B2_05_SEMC_DATA21 +#define MIMXRT_IOMUXC_SEMC_DATA22 IOMUXC_GPIO_EMC_B2_06_SEMC_DATA22 +#define MIMXRT_IOMUXC_SEMC_DATA23 IOMUXC_GPIO_EMC_B2_07_SEMC_DATA23 +#define MIMXRT_IOMUXC_SEMC_DM02 IOMUXC_GPIO_EMC_B2_08_SEMC_DM02 + +#define MIMXRT_IOMUXC_SEMC_DATA24 IOMUXC_GPIO_EMC_B2_09_SEMC_DATA24 +#define MIMXRT_IOMUXC_SEMC_DATA25 IOMUXC_GPIO_EMC_B2_10_SEMC_DATA25 +#define MIMXRT_IOMUXC_SEMC_DATA26 IOMUXC_GPIO_EMC_B2_11_SEMC_DATA26 +#define MIMXRT_IOMUXC_SEMC_DATA27 IOMUXC_GPIO_EMC_B2_12_SEMC_DATA27 +#define MIMXRT_IOMUXC_SEMC_DATA28 IOMUXC_GPIO_EMC_B2_13_SEMC_DATA28 +#define MIMXRT_IOMUXC_SEMC_DATA29 IOMUXC_GPIO_EMC_B2_14_SEMC_DATA29 +#define MIMXRT_IOMUXC_SEMC_DATA30 IOMUXC_GPIO_EMC_B2_15_SEMC_DATA30 +#define MIMXRT_IOMUXC_SEMC_DATA31 IOMUXC_GPIO_EMC_B2_16_SEMC_DATA31 +#define MIMXRT_IOMUXC_SEMC_DM03 IOMUXC_GPIO_EMC_B2_17_SEMC_DM03 +#define MIMXRT_IOMUXC_SEMC_DQS4 IOMUXC_GPIO_EMC_B2_18_SEMC_DQS4 + +#if MICROPY_PY_MACHINE_I2S +#define MICROPY_BOARD_ROOT_POINTERS \ + struct _machine_i2s_obj_t *machine_i2s_obj[MICROPY_HW_I2S_NUM]; +#endif + +// AUTOGENERATED by update.py from copier +#define MICROPY_HW_USB_CDC_NUM (2) +#define MICROPY_HW_USB_MSC (0) +#define MICROPY_HW_USB_HID (0) + +#define MICROPY_HW_USB_MANUFACTURER_STRING "PHYTEC" +#define MICROPY_HW_USB_PRODUCT_HS_STRING "phyBOARD-RT1170 Development Kit" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "phyBOARD-RT1170 Development Kit" +#define MICROPY_HW_USB_CONFIGURATION_HS_STRING "phyBOARD Config" +#define MICROPY_HW_USB_INTERFACE_HS_STRING "phyBOARD Interface" +#define MICROPY_HW_USB_CONFIGURATION_FS_STRING "phyBOARD Config" +#define MICROPY_HW_USB_INTERFACE_FS_STRING "phyBOARD Interface" + +#define MBOOT_USBD_MANUFACTURER_STRING "PHYTEC" +#define MBOOT_USBD_PRODUCT_STRING "phyBOARD Boot" + +// phyBOARD-RT1170 Development Kit hardware features + +// Onboard EEPROM (M24C32 - 32Kbit) +#define MICROPY_HW_EEPROM_I2C_BUS (0) // On LPI2C1 (SoM) +#define MICROPY_HW_EEPROM_ADDR (0x50) + +// Carrier board peripherals +// Audio Codec: TLV320AIC3110 on LPI2C5 (I2C address: 0x18) +// Accelerometer: ICM-40627 on LPI2C2 (I2C address: 0x6B) +// CAN Interface: CAN3 (GPIO_LPSR_00/01) +// RS-232 Serial: LPUART8 (GPIO_AD_02/03/04/05) +// User Button: GPIO_AD_35 +// RGB LEDs: GPIO_AD_14 (red), GPIO_LPSR_13 (green) +// microSD Card Slot: USDHC1 +// M.2 Connector (Key E): WiFi/Bluetooth modules + +// Basic ADC channels available on connector +#define MICROPY_HW_ADC_NUM_CHANNELS (16) // GPIO_AD domain pins diff --git a/ports/mimxrt/boards/PHYBOARD_RT1170/mpconfigboard.mk b/ports/mimxrt/boards/PHYBOARD_RT1170/mpconfigboard.mk new file mode 100644 index 0000000000000..9cca7a54ab4bb --- /dev/null +++ b/ports/mimxrt/boards/PHYBOARD_RT1170/mpconfigboard.mk @@ -0,0 +1,31 @@ +MCU_SERIES = MIMXRT1176 +MCU_VARIANT = MIMXRT1176DVMAA +MCU_CORE = _cm7 + +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_100MHz +MICROPY_HW_FLASH_QE_CMD = 0x31 +MICROPY_HW_FLASH_QE_ARG = 0x02 +# phyCORE SoM flash timing - use loopback from DQS pad for better signal integrity +MICROPY_HW_FLASH_DQS = kFlexSPIReadSampleClk_LoopbackFromDqsPad + +MICROPY_HW_SDRAM_AVAIL = 1 +MICROPY_HW_SDRAM_SIZE = 0x4000000 # 64MB + +MICROPY_PY_LWIP = 1 +MICROPY_PY_SSL = 1 +MICROPY_SSL_MBEDTLS = 1 +MICROPY_PY_OPENAMP = 1 +MICROPY_PY_OPENAMP_REMOTEPROC = 1 + +FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py + +CFLAGS += -DCPU_MIMXRT1176DVMAA_cm7 \ + -DMIMXRT117x_SERIES \ + -DENET_ENHANCEDBUFFERDESCRIPTOR_MODE=1 \ + -DCPU_HEADER_H='<$(MCU_SERIES)$(MCU_CORE).h>' \ + -DUSB1_BASE=USB_OTG1_BASE \ + -DUSB2_BASE=USB_OTG2_BASE diff --git a/ports/mimxrt/boards/PHYBOARD_RT1170/pins.csv b/ports/mimxrt/boards/PHYBOARD_RT1170/pins.csv new file mode 100644 index 0000000000000..c0222d9b252cc --- /dev/null +++ b/ports/mimxrt/boards/PHYBOARD_RT1170/pins.csv @@ -0,0 +1,127 @@ +# phyBOARD-RT1170 Development Kit Pin Mapping +# phyCORE-i.MX RT1170 SoM + PBA-C-26 Carrier Board + +# LEDs - phyCORE SoM onboard (active low) +LED_SOM_RED,GPIO_LPSR_07 +LED_SOM_GREEN,GPIO_LPSR_08 + +# LEDs - Carrier board RGB LEDs (active low) +LED_CARRIER_RED,GPIO_AD_14 +LED_CARRIER_GREEN,GPIO_LPSR_13 + +# Buttons +USER_BUTTON,GPIO_AD_35 +POWER_BUTTON,GPIO_LPSR_04 +WAKE_BUTTON,GPIO_LPSR_03 +RESET_BUTTON,GPIO_LPSR_02 + +# Primary UART interfaces +UART1_TX,GPIO_AD_24 +UART1_RX,GPIO_AD_25 + +# Additional UART interfaces (Carrier board) +UART2_TX,GPIO_DISP_B2_10 +UART2_RX,GPIO_DISP_B2_11 +UART2_CTS,GPIO_DISP_B2_13 +UART2_RTS,GPIO_DISP_B2_12 + +UART5_TX,GPIO_AD_28 +UART5_RX,GPIO_AD_29 + +UART6_TX,GPIO_EMC_B1_40 +UART6_RX,GPIO_EMC_B1_41 + +UART7_TX,GPIO_DISP_B2_06 +UART7_RX,GPIO_DISP_B2_07 + +UART8_TX_RS232,GPIO_AD_02 +UART8_RX_RS232,GPIO_AD_03 +UART8_CTS,GPIO_AD_04 +UART8_RTS,GPIO_AD_05 + +# I2C interfaces +I2C1_SCL,GPIO_AD_32 +I2C1_SDA,GPIO_AD_33 + +I2C2_SCL,GPIO_AD_18 +I2C2_SDA,GPIO_AD_19 + +I2C3_SCL,GPIO_DISP_B2_10 +I2C3_SDA,GPIO_DISP_B2_11 + +I2C5_SCL,GPIO_LPSR_09 +I2C5_SDA,GPIO_LPSR_08 + +# SPI interface +SPI1_SCK,GPIO_AD_28 +SPI1_CS,GPIO_AD_29 + +# Display interface (MIPI-DSI) +MIPI_DSI_DP0,GPIO_DISP_B1_02 +MIPI_DSI_DN0,GPIO_DISP_B1_03 +MIPI_DSI_DP1,GPIO_DISP_B1_00 +MIPI_DSI_DN1,GPIO_DISP_B1_01 +MIPI_DSI_CKP,GPIO_DISP_B1_05 +MIPI_DSI_CKN,GPIO_DISP_B1_04 + +# Display control pins +LCD_RST_B,GPIO_DISP_B2_14 +LCD_PWR_EN,GPIO_AD_26 +LCD_BACKLIGHT_CTL,GPIO_AD_27 +CTP_INT,GPIO_AD_26 +CTP_RST_B,GPIO_DISP_B2_15 + +# Camera interface (MIPI-CSI) - on SoM connector (via pin definitions) +# Note: MIPI-CSI differential pairs are on SoM - reference schematic for pin assignments + +# Accelerometer (ICM-40627 on LPI2C2) +ACCEL_INT1,GPIO_AD_26 +ACCEL_INT2,GPIO_AD_27 + +# Audio codec (TLV320AIC3110 on LPI2C5 and SAI1) +SAI1_TX_BCLK,GPIO_AD_22 +SAI1_TX_SYNC,GPIO_AD_23 +SAI1_TX_DATA,GPIO_AD_21 +SAI1_RX_DATA,GPIO_AD_20 +SAI1_MCLK,GPIO_AD_17 + +# CAN interface (CAN3) +CAN3_TX,GPIO_LPSR_00 +CAN3_RX,GPIO_LPSR_01 + +# SD Card interface (USDHC1) +SD1_CLK,GPIO_SD_B1_01 +SD1_CMD,GPIO_SD_B1_00 +SD1_DATA0,GPIO_SD_B1_02 +SD1_DATA1,GPIO_SD_B1_03 +SD1_DATA2,GPIO_SD_B1_04 +SD1_DATA3,GPIO_SD_B1_05 + +# Basic ADC channels +ADC1_CH0,GPIO_AD_00 +ADC1_CH1,GPIO_AD_01 +ADC1_CH2,GPIO_AD_02 +ADC1_CH3,GPIO_AD_03 + +# GPIO expansion connector +GPIO_CONN_16,GPIO_AD_16 +GPIO_CONN_17,GPIO_AD_17 +GPIO_CONN_18,GPIO_AD_18 +GPIO_CONN_19,GPIO_AD_19 +GPIO_CONN_20,GPIO_AD_20 +GPIO_CONN_21,GPIO_AD_21 +GPIO_CONN_22,GPIO_AD_22 +GPIO_CONN_23,GPIO_AD_23 +GPIO_CONN_26,GPIO_AD_26 +GPIO_CONN_27,GPIO_AD_27 +GPIO_CONN_LPSR_09,GPIO_LPSR_09 +GPIO_CONN_LPSR_10,GPIO_LPSR_10 +GPIO_CONN_LPSR_11,GPIO_LPSR_11 +GPIO_CONN_LPSR_12,GPIO_LPSR_12 + +# JTAG interface +JTAG_TCK,GPIO_LPSR_14 +JTAG_TMS,GPIO_LPSR_15 +JTAG_TDI,GPIO_LPSR_12 +JTAG_TDO,GPIO_LPSR_11 +JTAG_nTRST,GPIO_LPSR_10 diff --git a/pyproject.toml b/pyproject.toml index 1cf57166e1437..2f71058f60759 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,ser,shft,synopsys,technic,ure,curren" +ignore-words-list = "ans,asend,deques,dout,emac,extint,hsi,iput,mis,notin,numer,ser,shft,som,synopsys,technic,ure,curren" quiet-level = 3 skip = """ */build*,\